একটি civil date-কে “পূর্ণিমা” বলা, astronomical Full Moon instant বের করা এবং পূর্ণিমা তিথির শুরু–শেষ নির্ণয়—তিনটি related কিন্তু আলাদা কাজ। একইভাবে New Moon instant অমাবস্যা তিথির মাঝখানে নয়; এটি অমাবস্যার শেষ boundary। এই distinction না রাখলে daily page, festival rule, XML export ও historical report-এ এক দিনের ভুল খুব সহজে তৈরি হয়।
মূল রাশি: সূর্য–চন্দ্রের elongation
একটি সময়ে geocentric ecliptic longitude ধরা যাক:
λSun = সূর্যের geocentric ecliptic longitude
E = Normalize360( λMoon − λSun )
E হলো চন্দ্র সূর্যের তুলনায় zodiac বরাবর কত degree এগিয়ে। এটি ০° থেকে ৩৬০° পর্যন্ত চলে, তারপর আবার ০°-এ wrap করে।
| Elongation | Astronomical relation | Panchanga significance |
|---|---|---|
| ০° | Conjunction / astronomical New Moon | অমাবস্যা শেষ; শুক্ল প্রতিপদ শুরু |
| ৯০° | First Quarter | শুক্ল অষ্টমীর ভিতরের একটি instant |
| ১৮০° | Opposition / astronomical Full Moon | পূর্ণিমা শেষ; কৃষ্ণ প্রতিপদ শুরু |
| ২৭০° | Last Quarter | কৃষ্ণ অষ্টমীর ভিতরের একটি instant |
| ৩৬০° = ০° | পরবর্তী conjunction | পরবর্তী lunar cycle শুরু |
৩৬০° ভাগে ৩০ তিথি
একটি পূর্ণ phase cycle ৩৬০°। ত্রিশটি তিথির প্রতিটি ১২° elongation interval:
TithiIndex = floor(E ÷ ১২°) + ১
TithiIndex ১ থেকে ৩০। প্রথম ১৫টি শুক্লপক্ষ; পরের ১৫টি কৃষ্ণপক্ষ। একটি তিথি ১২° angular interval—২৪ ঘণ্টার civil day নয়। সূর্য ও চন্দ্রের relative speed পরিবর্তিত হওয়ায় তিথির সময়কালও পরিবর্তিত হয়।
| Global index | Elongation interval | পক্ষ ও তিথি |
|---|---|---|
| ১ | [০°, ১২°) | শুক্ল প্রতিপদ |
| ২ | [১২°, ২৪°) | শুক্ল দ্বিতীয়া |
| ৮ | [৮৪°, ৯৬°) | শুক্ল অষ্টমী |
| ১৫ | [১৬৮°, ১৮০°) | পূর্ণিমা |
| ১৬ | [১৮০°, ১৯২°) | কৃষ্ণ প্রতিপদ |
| ২৩ | [২৬৪°, ২৭৬°) | কৃষ্ণ অষ্টমী |
| ৩০ | [৩৪৮°, ৩৬০°) | অমাবস্যা |
Half-open interval ব্যবহারে exact boundary একবারই count হয়। যেমন E = 180° হলে পূর্ণিমা শেষ—state হবে কৃষ্ণ প্রতিপদ।
শুক্লপক্ষ ও কৃষ্ণপক্ষের exact boundary
পক্ষ হলো পনেরো তিথির group:
১৮০° ≤ E < ৩৬০° → কৃষ্ণপক্ষ
শুক্লপক্ষে Moon–Sun elongation ০° থেকে ১৮০°-এর দিকে বাড়ে; সাধারণভাবে চাঁদের illuminated portion বাড়তে দেখা যায়। কৃষ্ণপক্ষে elongation ১৮০° থেকে ৩৬০°/০°-এর দিকে যায়; illuminated portion কমে। কিন্তু panchanga classification দৃশ্যমান আকার দেখে নয়—longitude calculation দেখে।
Astronomical Full Moon আর পূর্ণিমা তিথি এক নয়
পূর্ণিমা তিথি শুরু হয় elongation ১৬৮° অতিক্রম করলে এবং শেষ হয় ১৮০°-এ। Astronomical Full Moon হলো ১৮০°-এর exact instant। তাই:
Full Moon instant = E ঠিক ১৮০°
১৮০° থেকে = কৃষ্ণ প্রতিপদ
কথ্য ভাষায় “আজ পূর্ণিমা” বলতে পূর্ণিমা তিথিযুক্ত দিন বোঝানো হয়। Astronomy table-এ “Full Moon 22:14 UTC” মানে exact opposition instant। দুইটি একই field-এ রাখবেন না।
Astronomical New Moon আর অমাবস্যা তিথি এক নয়
অমাবস্যা তিথি elongation ৩৪৮° থেকে ৩৬০° পর্যন্ত। Exact conjunction বা New Moon instant ৩৬০°/০°-এ, যেখানে অমাবস্যা শেষ এবং শুক্ল প্রতিপদ শুরু:
New Moon instant = E ঠিক ৩৬০°/০°
০° থেকে = শুক্ল প্রতিপদ
এ কারণে astronomical New Moon-এর civil date এবং sunrise-based “অমাবস্যা দিন” কখনও আলাদা date-এ পড়তে পারে। এটি error নয়; একদিকে exact instant, অন্যদিকে local calendrical day assignment।
First/Last Quarter কেন তিথির boundary নয়?
Western principal lunar phases ০°, ৯০°, ১৮০° ও ২৭০° elongation-এ সংজ্ঞায়িত। কিন্তু তিথির boundary প্রতি ১২°। ৯০° বা ২৭০° সংখ্যা ১২ দিয়ে নিঃশেষে বিভাজ্য নয়; তাই:
- ৯০° পড়ে শুক্ল অষ্টমীর [৮৪°, ৯৬°) interval-এর মাঝামাঝি;
- ২৭০° পড়ে কৃষ্ণ অষ্টমীর [২৬৪°, ২৭৬°) interval-এর মাঝামাঝি;
- শুধু ০° ও ১৮০° একই সঙ্গে principal phase এবং pakṣa boundary।
সুতরাং quarter-phase API ব্যবহার করে সরাসরি অষ্টমীর শুরু/শেষ বের করা যাবে না। ৮৪° ও ৯৬° roots আলাদাভাবে solve করতে হবে।
দৃশ্যমান চাঁদ, conjunction এবং পঞ্জিকার তিথি
Astronomical New Moon-এর সময় সূর্য ও চন্দ্রের ecliptic longitude সমান। কিন্তু first crescent visibility পরে ঘটে এবং atmosphere, horizon, latitude, elongation, Moon altitude ও observer condition-এর উপর নির্ভর করে। Hindu tithi calculation সাধারণত crescent sighting-এর অপেক্ষা করে না; mathematical elongation ব্যবহার করে।
এই distinction Hijri calculation-এর ক্ষেত্রেও গুরুত্বপূর্ণ। Islamic observational month-start model-এ conjunction-এর পর crescent visibility বিবেচিত হতে পারে; Hindu তিথি boundary প্রতি ১২° mathematical phase interval। একই “চাঁদের মাস” শব্দ ব্যবহার হলেও algorithm এক নয়।
তিথিতে নিরয়ণ না সায়ন longitude?
তিথি হলো সূর্য ও চন্দ্রের longitude difference। একই ayanāṃśa দুই longitude থেকে বিয়োগ করলে difference-এ তা algebraically cancel করে:
= λMoon − λSun
তাই একই coordinate convention-এ নেওয়া tropical বা sidereal longitude থেকে elongation সাধারণত একই হয়। কিন্তু এক body-তে apparent, অন্যটিতে mean; একটিতে geocentric, অন্যটিতে topocentric; অথবা দুই epoch/model mix করলে cancel হবে না।
Project profile-এ অন্তত এই metadata রাখুন:
- Solar/lunar engine ও version;
- geocentric না topocentric;
- true/apparent/mean longitude convention;
- time scale—UT, TT এবং ΔT policy;
- root-solver tolerance।
তিথির interval global, পঞ্জিকার দিন local
Chosen geocentric model-এ একটি তিথির start/end absolute instant পৃথিবীর সব স্থানের জন্য একই। কিন্তু সেই interval কোন local sunrise-কে স্পর্শ করছে তা শহরভেদে বদলে যায়। ফলে ঢাকা ও নিউ ইয়র্কে:
- একই তিথির UTC start/end একই থাকবে;
- local clock time ও civil date আলাদা হবে;
- sunrise-এ বর্তমান তিথি আলাদা হতে পারে;
- festival civil day-ও rule অনুযায়ী আলাদা হতে পারে।
তাই তিথিকে timezone দিয়ে পুনর্গণনা নয়; absolute event localize করুন, তারপর sunrise/ritual-moment rule প্রয়োগ করুন।
সূর্যোদয়-ভিত্তিক daily assignment
একটি traditional Panchanga day সাধারণত local sunrise থেকে পরের local sunrise পর্যন্ত। Daily heading-এ যে তিথি দেখানো হয়, তা অধিকাংশ ক্ষেত্রে প্রথম sunrise-এ বর্তমান তিথি। কিন্তু day-এর ভিতরে এক বা একাধিক transition ঘটতে পারে।
| পরিস্থিতি | Daily result |
|---|---|
| একই তিথি sunrise থেকে next sunrise পর্যন্ত | “অহোরাত্র” বা full-day indication |
| তিথি day-এর ভিতরে শেষ | বর্তমান তিথি + end time + পরে পরবর্তী তিথি |
| দুই boundary sunrise interval-এর ভিতরে | মধ্যবর্তী তিথি কোনো sunrise-এ না থাকলে তিথিক্ষয় display |
| এক তিথি দুই consecutive sunrise-এ | তিথিবৃদ্ধি; দুই civil day-এ একই sunrise tithi |
Calendar grid-এ শুধু sunrise tithi দেখালেও daily details ও XML-এ sunrise-to-next-sunrise সব transition রাখা উচিত। এতে “পরে” line এবং festival interval ঠিক থাকে।
পূর্ণিমা–অমাবস্যার observance date কীভাবে বাছবেন?
পূর্ণিমা বা অমাবস্যা তিথি যে civil day-এ আছে, সব observance স্বয়ংক্রিয়ভাবে সেই দিনেই হবে—এমন নয়। Rule-এর reference moment ভিন্ন হতে পারে:
- sunrise prevalence;
- প্রদোষ/সন্ধ্যা prevalence;
- midnight prevalence;
- পূর্ণ তিথি interval-এর overlap;
- দুই দিনে থাকলে pūrvaviddha/paraviddha preference;
- স্থানীয় বা sampradāya-specific rule।
Engine architecture-এ প্রথমে pure interval calculate করুন। আলাদা ObservanceRule সেই intervalকে sunrise, sunset, midnight বা অন্য ritual window-এর সঙ্গে intersect করবে। Daily label-কে festival decision হিসেবে reuse করবেন না।
Calculation pipeline
- Requested date-এর আগে–পরে পর্যাপ্ত Julian Day window নিন;
- একই astronomical profile-এ সূর্য ও চন্দ্র longitude calculate করুন;
E = Normalize360(Moon − Sun);- Current global tithi index নির্ণয় করুন;
- Next multiple of ১২° target করুন;
- Coarse stepping দিয়ে target crossing bracket করুন;
- Bisection/Brent method দিয়ে exact boundary solve করুন;
- Sunrise-to-next-sunrise interval-এর সব boundary collect করুন;
- Absolute instants requested time zone-এ convert করুন;
- Daily label ও observance policy আলাদা ধাপে প্রয়োগ করুন।
Midnight-এ state sample করে ২৪ ঘণ্টা যোগ দিয়ে end time অনুমান করলে চলবে না। Relative angular speed constant নয়; interpolation কেবল safe bracket তৈরিতে সাহায্য করতে পারে, final result root solver থেকে আসবে।
৩৬০° wrap-এর মধ্যেও exact boundary solve
Raw normalized elongation 359.9° থেকে 0.1° হলে সংখ্যা কমেছে মনে হয়, যদিও phase এগিয়েছে 0.2°। তাই scan-এর সময় phase unwrap করুন। Anchor instant-এর elongation থেকে continuous progress:
static double Normalize360(double value)
{
value %= 360.0;
if (value < 0.0) value += 360.0;
return value;
}
static double ForwardDifference(double later, double earlier)
{
return Normalize360(later - earlier);
}
একটি ছোট bracket—যেমন কয়েক ঘণ্টা—এর মধ্যে Moon–Sun relative motion forward থাকে। Bracket-এর start phase basePhase ধরে target পর্যন্ত continuous distance compare করা যায়। খুব বড় interval এক ধাপে নেবেন না; coarse scan ৩–৬ ঘণ্টার step ব্যবহার করতে পারে।
C# 5-compatible তিথি state
public enum PakshaKind
{
Shukla,
Krishna
}
public sealed class TithiState
{
public int GlobalIndex { get; set; } // 1..30
public int PakshaIndex { get; set; } // 1..15
public PakshaKind Paksha { get; set; }
public double ElongationDegrees { get; set; }
public double StartBoundaryDegrees { get; set; }
public double EndBoundaryDegrees { get; set; }
}
static TithiState StateAt(double moonLongitude, double sunLongitude)
{
double e = Normalize360(moonLongitude - sunLongitude);
int global = (int)Math.Floor(e / 12.0) + 1;
// Floating-point protection at the upper wrap.
if (global > 30) global = 30;
bool shukla = global <= 15;
int pakshaIndex = shukla ? global : global - 15;
return new TithiState
{
GlobalIndex = global,
PakshaIndex = pakshaIndex,
Paksha = shukla ? PakshaKind.Shukla : PakshaKind.Krishna,
ElongationDegrees = e,
StartBoundaryDegrees = (global - 1) * 12.0,
EndBoundaryDegrees = global == 30 ? 360.0 : global * 12.0
};
}
Exact boundary-তে floating-point value 179.999999999 বা 180.000000001 হতে পারে। State display-এর আগে arbitrary rounding না করে solver tolerance এবং a consistent boundary policy ব্যবহার করুন।
C# bisection boundary solver
নিচের code ধরে নিচ্ছে PhaseProgress(jd, anchorJd) anchor-এর পর continuous, unwrapped elongation progress ফেরত দেয় এবং targetProgress bracket-এর মধ্যে আছে:
public sealed class TithiBoundary
{
public int EndingGlobalTithi { get; set; }
public int StartingGlobalTithi { get; set; }
public double JulianDayUt { get; set; }
public DateTimeOffset InstantUtc { get; set; }
}
static double SolveByBisection(
double leftJd,
double rightJd,
double targetProgress,
Func<double, double> phaseProgress,
double toleranceDays)
{
double leftValue = phaseProgress(leftJd) - targetProgress;
double rightValue = phaseProgress(rightJd) - targetProgress;
if (leftValue > 0.0 || rightValue < 0.0)
throw new ArgumentException("Boundary is not bracketed.");
while (rightJd - leftJd > toleranceDays)
{
double middleJd = (leftJd + rightJd) / 2.0;
double middleValue = phaseProgress(middleJd) - targetProgress;
if (middleValue < 0.0)
leftJd = middleJd;
else
rightJd = middleJd;
}
return (leftJd + rightJd) / 2.0;
}
এক সেকেন্ড tolerance চাইলে day unit-এ 1.0 / 86400.0 ব্যবহার করা যায়। Ephemeris input/output precision কম হলে solver tolerance ছোট করলেই accuracy বাড়বে না—source model-এর বাস্তব precision report করুন।
এক sunrise থেকে পরের sunrise: সব event সংগ্রহ
Daily report-এর জন্য algorithm:
public sealed class VedicDayTithiReport
{
public DateTimeOffset Sunrise { get; set; }
public DateTimeOffset NextSunrise { get; set; }
public TithiState TithiAtSunrise { get; set; }
public IList<TithiBoundary> Boundaries { get; set; }
}
// Pseudocode-shaped C# 5 flow:
// 1. sunriseUtc এবং nextSunriseUtc -> JD
// 2. StateAt(sunrise) calculate
// 3. current tithi-এর end target থেকে শুরু
// 4. যতক্ষণ solved boundary < nextSunrise:
// report.Boundaries.Add(boundary)
// target += 12 degrees
// 5. local time zone-এ render
Boundary list count:
- ০ হলে sunrise tithi next sunrise পর্যন্ত—“অহোরাত্র”;
- ১ হলে একটি end time;
- ২ হলে day-এর ভিতরে একটি সম্পূর্ণ intermediate tithi এসেছে ও শেষ হয়েছে;
- unexpected বেশি হলে input window, solver বা ephemeris যাচাই করুন।
API ও XML output
API-তে label-এর পাশাপাশি interval evidence দিন:
{
"vedicDay": {
"sunrise": "2026-10-08T05:54:10+06:00",
"nextSunrise": "2026-10-09T05:54:32+06:00"
},
"tithiAtSunrise": {
"globalIndex": 28,
"paksha": "krishna",
"pakshaIndex": 13,
"nameBn": "ত্রয়োদশী",
"elongationDegrees": 329.4172
},
"boundaries": [
{
"endingTithi": 28,
"startingTithi": 29,
"julianDayUt": 2461322.198765,
"localTime": "2026-10-08T22:14:13+06:00"
}
],
"profile": "surya-siddhanta-v3"
}
XML:
<tithi-day sunrise="2026-10-08T05:54:10+06:00"
next-sunrise="2026-10-09T05:54:32+06:00"
profile="surya-siddhanta-v3">
<at-sunrise global-index="28"
paksha="krishna"
paksha-index="13"
elongation="329.4172">ত্রয়োদশী</at-sunrise>
<boundary ending="28" starting="29"
jd-ut="2461322.198765"
local="2026-10-08T22:14:13+06:00" />
</tithi-day>
সংখ্যাগুলো output-format illustration; production ephemeris result নয়। API consumer যেন example-কে test vector হিসেবে ব্যবহার না করে।
সাধারণ calculation error
| ভুল | ফল | সংশোধন |
|---|---|---|
| Moon phase image দেখে তিথি ধরা | Boundary time ভুল | Longitude difference ব্যবহার |
| Full Moon instant-কে পূর্ণিমার শুরু ধরা | তিথি প্রায় এক interval সরে যায় | পূর্ণিমা [১৬৮°,১৮০°) |
| New Moon instant-কে অমাবস্যার শুরু ধরা | অমাবস্যা/প্রতিপদ উল্টে যায় | অমাবস্যা [৩৪৮°,৩৬০°) |
| 359°→0° raw subtraction | Phase পিছিয়েছে মনে হয় | Unwrap/Normalize360 |
| Midnight-only sample | End time ও skipped tithi হারায় | Root solve + sunrise interval scan |
| UTC date-কে local festival date ধরা | Location অনুযায়ী এক দিন ভুল | Absolute instant localize, তারপর rule |
| Daily tithi = observance decision | প্রদোষ/মধ্যরাত্রির উৎসব ভুল | Separate ObservanceRule |
| Surya Siddhanta Moon + Swiss Sun mix | Unversioned hybrid result | Explicit compatible calculation profile |
Validation checklist
Normalize360(-1) = 359;Normalize360(361) = 1;- Elongation 0° → শুক্ল প্রতিপদ;
- 11.999° → শুক্ল প্রতিপদ;
- 12° → শুক্ল দ্বিতীয়া;
- 167.999° → শুক্ল চতুর্দশী;
- 168° → পূর্ণিমা;
- 179.999° → পূর্ণিমা;
- 180° → কৃষ্ণ প্রতিপদ;
- 347.999° → কৃষ্ণ চতুর্দশী;
- 348° → অমাবস্যা;
- 359.999° → অমাবস্যা;
- 360° normalized → শুক্ল প্রতিপদ;
- 90° → শুক্ল অষ্টমী, boundary নয়;
- 270° → কৃষ্ণ অষ্টমী, boundary নয়;
- পূর্ণিমা interval [168°,180°);
- অমাবস্যা interval [348°,360°);
- NewMoonInstant = AmavasyaEnd;
- FullMoonInstant = PurnimaEnd;
- 359°→1° unwrap forward;
- Root bracket sign/progress verified;
- Bisection result requested tolerance-এর মধ্যে;
- Boundary start inclusive, end exclusive;
- Exact boundary duplicate event নয়;
- Consecutive tithi boundary gap positive;
- Sunrise state direct longitude থেকে;
- Sunrise-to-next-sunrise zero-boundary case;
- এক-boundary case;
- দুই-boundary বা tithikshaya case;
- Same tithi at consecutive sunrise বা vriddhi case;
- “অহোরাত্র” only when next sunrise পর্যন্ত boundary নেই;
- Post-midnight local time “নিশা” formatting;
- UTC instant ঢাকা/কলকাতা/নিউ ইয়র্কে correct conversion;
- Time zone বদলালে absolute JD বদলায় না;
- Festival rule sunrise state থেকে আলাদা;
- Pradosha rule sunset window test;
- Midnight observance test;
- Same engine/profile for Sun and Moon;
- Apparent/true convention metadata;
- UT/TT ও ΔT policy versioned;
- Surya Siddhanta regression case;
- Drik/Swiss Ephemeris regression case;
- Printed Panchanga end-time comparison;
- API ও XML একই boundary list দেয়;
- Example values production test data হিসেবে marked নয়।
উপসংহার
পক্ষ ও তিথির ভিত্তি হলো সূর্য–চন্দ্রের geocentric longitude difference। ৩৬০° phase cycle-এর প্রতি ১২° একটি তিথি; ০°–১৮০° শুক্লপক্ষ এবং ১৮০°–৩৬০° কৃষ্ণপক্ষ। পূর্ণিমা তিথি [১৬৮°,১৮০°), অমাবস্যা [৩৪৮°,৩৬০°)। Exact Full Moon ১৮০°-এ পূর্ণিমার শেষ, আর exact New Moon ০°-এ অমাবস্যার শেষ।
Astronomical interval location-independent হলেও কোন local sunrise-এ কোন তিথি চলছে তা location ও time zone অনুযায়ী বদলে যায়। তাই event calculation এবং civil-day assignment আলাদা layer হওয়া উচিত। একইভাবে পূর্ণিমা/অমাবস্যার observance selection-কে daily label-এর সঙ্গে এক করে ফেলবেন না।
Production engine-এ consistent longitude profile, 360° unwrap, exact root solver, half-open boundary এবং sunrise-to-next-sunrise event scan ব্যবহার করলে daily details, festival rules, print report, API ও XML একই auditable result দিতে পারে।
তথ্যসূত্র ও আরও পাঠ
- U.S. Naval Observatory—Phases of the Moon and Percent Illuminated; principal phase-এর ০°, ৯০°, ১৮০° ও ২৭০° apparent ecliptic-longitude definition.
- U.S. Naval Observatory—Astronomical Glossary; conjunction, principal lunar phases ও synodic period terminology.
- U.S. Naval Observatory—Crescent Moon Visibility; astronomical New Moon এবং first visible crescent-এর distinction.
- Nachum Dershowitz ও Edward M. Reingold—Indian Calendrical Calculations; tithi, sunrise assignment, lunisolar calendar এবং observance-time framework.
- Vinod K. Mishra—Calendars of India; ভারতীয় calendrical elements-এর astronomical terminology.
- Positional Astronomy Centre—Rashtriya Panchang; tithi, nirayana longitude, solar/lunar events ও official almanac context.
- সূর্য সিদ্ধান্ত পঞ্জিকা project specification; sunrise-based Vedic day, Surya Siddhanta/Drik profile separation এবং Bengali rendering rules.
মন্তব্য, আলোচনা ও প্রশ্ন