“আজ বৃহস্পতিবার”—এই বাক্যটি সাধারণ ঘড়ি ও পঞ্জিকা দুই ক্ষেত্রেই বলা যায়, কিন্তু day boundary একই নয়। Civil calendar-এ দিন রাত ১২টায় বদলায়; প্রথাগত Panchanga day স্থানীয় সূর্যোদয়ে শুরু হয়ে পরের স্থানীয় সূর্যোদয়ে শেষ হয়। একটি production app-এ এই দুই পরিচয় পাশাপাশি রাখতে হবে—একটিকে আরেকটির বদলে ব্যবহার করলে বিশেষ করে মধ্যরাত থেকে সূর্যোদয়ের আগের output ভুল হবে।
একই গণনায় চার রকম day boundary
| ধারণা | Boundary | কাজ |
|---|---|---|
| Civil day | স্থানীয় midnight থেকে পরের midnight | Gregorian/Julian date, অফিসিয়াল timestamp, URL input |
| Panchanga/Vedic day | স্থানীয় sunrise থেকে পরের sunrise | বার, sunrise-state, দৈনিক তিথি–নক্ষত্র–যোগ–করণ ও উৎসব |
| Julian Day | Astronomical continuous count; পূর্ণসংখ্যা boundary noon-এ | Ephemeris, root solving, historical continuity |
| Event interval | দুটি exact astronomical instant | তিথি/নক্ষত্র/করণ/যোগের শুরু–শেষ |
এগুলোকে একটি DateTime field-এ গুঁজে দিলে অর্থ হারায়। Internal calculation-এ absolute instant বা Julian Day, display-এ local civil date, আর daily report-এ sunrise-anchored span আলাদা object হিসেবে রাখা নিরাপদ।
বার বা Vāra কী?
বার—সংস্কৃতে Vāra—পঞ্চাঙ্গের পাঁচ অঙ্গের একটি। এই পরিচয় সূর্যোদয়-anchored day span-এর সঙ্গে যুক্ত। যে স্থানীয় civil date-এর সূর্যোদয় span-টি শুরু করেছে, সেই date-এর weekday-ই span-এর বার।
Vāra(D) = weekday of local civil date D
Half-open interval [start, end) ব্যবহার জরুরি: ঠিক প্রথম sunrise instant বর্তমান day-এর অন্তর্ভুক্ত; ঠিক পরের sunrise instant পরবর্তী day-এর অন্তর্ভুক্ত। এতে shared boundary দুই দিনে duplicate হয় না।
সাত বারের stable mapping
| .NET DayOfWeek | Key | বাংলা | সংস্কৃত নাম |
|---|---|---|---|
| Sunday = 0 | ravivara | রবিবার | রবি/আদিত্যবার |
| Monday = 1 | somavara | সোমবার | সোমবার |
| Tuesday = 2 | mangalavara | মঙ্গলবার | মঙ্গলবার |
| Wednesday = 3 | budhavara | বুধবার | বুধবার |
| Thursday = 4 | guruvara | বৃহস্পতিবার | গুরু/বৃহস্পতিবার |
| Friday = 5 | shukravara | শুক্রবার | শুক্রবার |
| Saturday = 6 | shanivara | শনিবার | শনিবার |
Database ও API-তে বাংলা display text-এর বদলে stable key রাখুন। ভবিষ্যতে Assamese বা English language file যোগ হলেও calculation key বদলাবে না।
সূর্যোদয় থেকে পরের সূর্যোদয়
ধরা যাক কোনো শহরে বৃহস্পতিবার sunrise 05:45 এবং শুক্রবার sunrise 05:46। তাহলে:
শুক্রবার 05:46 ≤ শুক্রবার < শনিবারের sunrise
বৃহস্পতিবার রাত ১১টা এবং শুক্রবার ভোর ৩টা—দুটিই একই sunrise-to-sunrise Panchanga day-তে পড়ে। Civil date আলাদা হলেও বার বদলায়নি। শুক্রবারের local sunrise-এ বার শুক্রবার হবে।
Pre-sunrise instant কেন আগের পঞ্জিকা দিবস?
Live calculation-এর জন্য local timestamp-কে আজকের sunrise-এর সঙ্গে তুলনা করতে হবে:
- instant ≥ আজকের sunrise: anchor date আজ;
- instant < আজকের sunrise: anchor date গতকাল;
- instant = আগামী sunrise: পরের Panchanga day শুরু।
এখানে “গতকাল” মানে local civil date minus one। এরপর সেই anchor date-এর sunrise এবং তার পরের civil date-এর sunrise স্বাধীনভাবে গণনা করতে হবে।
Date report mode ও live mode এক নয়
| Mode | Input | Anchor | উদাহরণ |
|---|---|---|---|
| Date report | City + civil date D | সরাসরি D | ৮ অক্টোবরের দৈনিক পঞ্জিকা = ৮ অক্টোবর sunrise → ৯ অক্টোবর sunrise |
| Live now | City + exact instant | আজ বা আগের date, sunrise comparison-এর পরে | ৯ অক্টোবর 03:00; sunrise 05:50 হলে anchor ৮ অক্টোবর |
Date report-এ user একটি civil date-এর page চান; সেখানে span সবসময় সেই date-এর sunrise থেকে শুরু। Live mode-এ user এখনকার Panchanga state চান; তাই sunrise comparison বাধ্যতামূলক। একই method-এ দুই অর্থ মেশালে cache key, URL ও header অস্পষ্ট হয়।
একই UTC instant, কিন্তু ভিন্ন Panchanga day
সূর্যোদয় স্থানীয়। একই UTC instant কলকাতা, ঢাকা ও নিউ ইয়র্কে:
- ভিন্ন local civil date বা clock time হতে পারে;
- কোনো শহরে sunrise পেরিয়ে গেছে, অন্য শহরে এখনও হয়নি;
- তাই bar ও Panchanga day anchor-ও আলাদা হতে পারে।
প্রতিটি request-এ latitude, longitude এবং time-zone identifier একসঙ্গে রাখতে হবে। শুধু offset যেমন +05:30 ভবিষ্যৎ/অতীত DST rule প্রকাশ করে না; শুধু city name-ও নির্ভরযোগ্য coordinate নয়।
“সূর্যোদয়” কোন definition?
সূর্যোদয়ের একাধিক computational convention আছে। উদাহরণ:
| Profile | ধারণা | ফল |
|---|---|---|
| Upper limb + refraction | সূর্যের upper edge দৃশ্যমান; standard refraction ও semidiameter ধরা | সাধারণ almanac sunrise-এর কাছাকাছি |
| Sun center + standard refraction | Center-এর apparent crossing | Upper-limb profile থেকে সময় আলাদা |
| Center + no refraction | Geometric center horizon crossing | Research/engine-specific output |
Rashtriya Panchang-এর official explanation upper limb এবং refraction-ভিত্তিক convention বর্ণনা করে; U.S. Naval Observatory-এর standard computational sunrise-এ Sun center-এর zenith distance 90.8333° ব্যবহৃত হয়—যা semidiameter ও typical refraction অন্তর্ভুক্ত করে। আপনার app যদি Swiss Ephemeris-এর center/no-refraction profile ব্যবহার করে, সেটি ভুল বলে গোপনে বদলাবেন না; বরং profile name স্পষ্ট দেখান এবং তুলনার সময় একই definition ব্যবহার করুন।
sunriseProfile = "sun-center-no-refraction"
// অথবা
sunriseProfile = "upper-limb-standard-refraction"
DST-তে next sunrise কখনোই শুধু +24 hours নয়
New York-এর মতো অঞ্চলে daylight-saving transition-এর রাতে local clock এক ঘণ্টা এগিয়ে বা পিছিয়ে যায়। ফলে:
// ভুল
nextSunrise = sunrise.AddHours(24);
// সঠিক ধারণা
sunrise = CalculateSunrise(localDate, location);
nextSunrise = CalculateSunrise(localDate.AddDays(1), location);
প্রতিটি sunrise-কে নিজ civil date, coordinate ও time-zone rule দিয়ে independently গণনা করুন। Absolute UTC interval DST transition-এর কাছে ২৩ বা ২৫ ঘণ্টার আশেপাশে হতে পারে; তার সঙ্গে seasonal sunrise shift-ও যোগ হয়। এটি error নয়।
DateTimeOffset absolute instant-এর জন্য উপযোগী, কিন্তু একা future/historical zone rule ধারণ করে না। Zone conversion-এর জন্য TimeZoneInfo বা আপনার versioned time-zone provider রাখুন।
তিথি–নক্ষত্র–করণ–যোগ কোন দিনে যাবে?
Day span তৈরি হলে event assignment সহজ:
SunriseUtc ≤ e.InstantUtc < NextSunriseUtc
এই span-এর মধ্যে শেষ হওয়া সব তিথি, নক্ষত্র, করণ ও যোগ chronological order-এ দেখান। Midnight-এর পরে কিন্তু next sunrise-এর আগে event হলে “নিশা” বা extended-hour notation ব্যবহার করা যায়; day পরিবর্তন করবেন না। Official Rashtriya Panchang-এ midnight-এর পরের event time ২৫, ২৬ ইত্যাদি ঘণ্টায় প্রকাশের convention-ও ব্যাখ্যা করা আছে।
কোনো element পরের sunrise-এর আগে শেষ না হলে আপনার format অনুযায়ী অহোরাত্র বা “পরবর্তী সূর্যোদয়ের পর” দেখানো যেতে পারে। Exact next-sunrise boundary পরের day-এর event—বর্তমান day-এ duplicate নয়।
Julian/Gregorian date বদলালেও weekday sequence ভাঙে না
একই astronomical instant-কে proleptic Gregorian ও Julian calendar-এ দুই আলাদা date label দেওয়া যায়। ১৫৮২ সালের reform-এ কিছু civil date label বাদ পড়েছিল; কিন্তু absolute day ও weekday sequence থেকে কোনো দিন হারায়নি। তাই historical output-এ:
- core chronology Julian Day/Julian Day Number-এ রাখুন;
- Julian ও Gregorian label presentation layer-এ তৈরি করুন;
- একই sunrise instant-এর দুই label-এর weekday অবশ্যই একই হবে;
- calendar system বদলে weekday পুনর্গণনায় artificial jump তৈরি করবেন না।
৫৯৪ খ্রিস্টাব্দের মতো early historical year-এ .NET DateTime দিয়ে সব intermediate civil conversion করা ঝুঁকিপূর্ণ। Astronomy ও weekday core-এ JD/JDN, আর display-এর জন্য tested Julian/Gregorian converter ব্যবহার করুন। Historical time zone-ও আধুনিক national offset ধরে নেওয়া উচিত নয়; প্রয়োজনে fixed LMT/research profile প্রকাশ করুন।
সূর্যোদয় না হলে কী হবে?
উচ্চ অক্ষাংশে কোনো civil date-এ sunrise নাও ঘটতে পারে। Production code-এর ভুল fallback হলো silently midnight-কে sunrise ধরা। সঠিক আচরণ:
NoSunriseবাUnsupportedLatitudestatus ফেরত দিন;- UI-তে location/date-সহ পরিষ্কার ব্যাখ্যা দিন;
- কোনো ধর্মীয় polar-day convention নিলে authority ও algorithm প্রকাশ করুন;
- normal-latitude cache-এর সঙ্গে fallback result মেশাবেন না।
C# 5-compatible data model
public enum VaraKind
{
Ravivara = 0,
Somavara = 1,
Mangalavara = 2,
Budhavara = 3,
Guruvara = 4,
Shukravara = 5,
Shanivara = 6
}
public sealed class PanchangaDaySpan
{
public DateTime LocalAnchorDate { get; set; }
public DateTimeOffset SunriseUtc { get; set; }
public DateTimeOffset NextSunriseUtc { get; set; }
public DateTimeOffset LocalSunrise { get; set; }
public DateTimeOffset LocalNextSunrise { get; set; }
public VaraKind Vara { get; set; }
public string TimeZoneId { get; set; }
public string SunriseProfile { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public interface ISunriseCalculator
{
DateTimeOffset CalculateSunriseUtc(
DateTime localCivilDate,
DesktopLocation location);
}
LocalAnchorDate-এর Kind Unspecified রাখা ভালো—এটি একটি calendar label, machine-local instant নয়। Sunrise calculator absolute UTC instant ফেরত দেবে।
Civil date থেকে day span তৈরি
public sealed class PanchangaDayResolver
{
readonly ISunriseCalculator sunriseCalculator;
public PanchangaDayResolver(ISunriseCalculator sunriseCalculator)
{
if (sunriseCalculator == null)
throw new ArgumentNullException("sunriseCalculator");
this.sunriseCalculator = sunriseCalculator;
}
public PanchangaDaySpan BuildForCivilDate(
DateTime localDate,
DesktopLocation location)
{
DateTime anchor = DateTime.SpecifyKind(
localDate.Date,
DateTimeKind.Unspecified);
DateTimeOffset sunriseUtc =
sunriseCalculator.CalculateSunriseUtc(anchor, location)
.ToUniversalTime();
DateTimeOffset nextSunriseUtc =
sunriseCalculator.CalculateSunriseUtc(
anchor.AddDays(1), location).ToUniversalTime();
if (nextSunriseUtc <= sunriseUtc)
throw new InvalidOperationException(
"Next sunrise must be after sunrise.");
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(
location.TimeZoneId);
return new PanchangaDaySpan
{
LocalAnchorDate = anchor,
SunriseUtc = sunriseUtc,
NextSunriseUtc = nextSunriseUtc,
LocalSunrise = TimeZoneInfo.ConvertTime(sunriseUtc, zone),
LocalNextSunrise = TimeZoneInfo.ConvertTime(nextSunriseUtc, zone),
Vara = (VaraKind)(int)anchor.DayOfWeek,
TimeZoneId = location.TimeZoneId,
SunriseProfile = location.SunriseProfile,
Latitude = location.Latitude,
Longitude = location.Longitude
};
}
}
.NET-এর DayOfWeek Sunday=0 থেকে Saturday=6; উপরের VaraKind একই order-এ হওয়ায় cast নিরাপদ। যদি enum order বদলান, explicit switch ব্যবহার করুন।
Exact instant থেকে live Panchanga day
public PanchangaDaySpan ResolveForInstant(
DateTimeOffset instantUtc,
DesktopLocation location)
{
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(
location.TimeZoneId);
DateTimeOffset utc = instantUtc.ToUniversalTime();
DateTimeOffset local = TimeZoneInfo.ConvertTime(utc, zone);
DateTime localDate = DateTime.SpecifyKind(
local.Date,
DateTimeKind.Unspecified);
DateTimeOffset todaySunriseUtc =
sunriseCalculator.CalculateSunriseUtc(
localDate, location).ToUniversalTime();
DateTime anchorDate = utc < todaySunriseUtc
? localDate.AddDays(-1)
: localDate;
return BuildForCivilDate(anchorDate, location);
}
Comparison সবসময় absolute UTC instant-এ করা হয়েছে। Local wall time compare করলে ambiguous DST hour এবং offset mismatch-এর ঝুঁকি বাড়ে।
বারের localized display
static string VaraNameBn(VaraKind value)
{
switch (value)
{
case VaraKind.Ravivara: return "রবিবার";
case VaraKind.Somavara: return "সোমবার";
case VaraKind.Mangalavara: return "মঙ্গলবার";
case VaraKind.Budhavara: return "বুধবার";
case VaraKind.Guruvara: return "বৃহস্পতিবার";
case VaraKind.Shukravara: return "শুক্রবার";
case VaraKind.Shanivara: return "শনিবার";
default: throw new ArgumentOutOfRangeException("value");
}
}
JD/JDN থেকে historical weekday
Modern DateTime range বা calendar behavior-এর উপর নির্ভর না করে JDN থেকে weekday:
static int Mod(int value, int modulus)
{
int result = value % modulus;
return result < 0 ? result + modulus : result;
}
static VaraKind VaraFromLocalJulianDay(
double julianDayUt,
double utcOffsetSeconds)
{
double localJd = julianDayUt + utcOffsetSeconds / 86400.0;
long localJdn = (long)Math.Floor(localJd + 0.5);
int sundayZero = Mod((int)((localJdn + 1L) % 7L), 7);
return (VaraKind)sundayZero;
}
এই helper local civil weekday label দেয়; Panchanga bar-এর জন্য sunrise anchor instant/date ব্যবহার করুন। Historical offset অবশ্যই chosen research profile থেকে আসবে। Modern TimeZoneInfo data বহু শতাব্দী আগে নির্ভুল local mean time দেয়—এমন অনুমান করবেন না।
API ও XML output
{
"requestedCivilDate": "2026-10-09",
"panchangaAnchorDate": "2026-10-08",
"vara": {
"key": "guruvara",
"nameBn": "বৃহস্পতিবার"
},
"sunrise": "2026-10-08T05:54:21+06:00",
"nextSunrise": "2026-10-09T05:54:43+06:00",
"timeZoneId": "Bangladesh Standard Time",
"latitude": 23.8103,
"longitude": 90.4125,
"sunriseProfile": "sun-center-no-refraction",
"assignmentRule": "sunrise-inclusive-next-sunrise-exclusive"
}
উপরেরটি live pre-sunrise request-এর format example; সময়গুলো test vector নয়। Date-only endpoint হলে requestedCivilDate ও panchangaAnchorDate একই হবে।
<panchanga-day anchor-date="2026-10-08"
vara-key="guruvara"
vara-bn="বৃহস্পতিবার"
timezone="Bangladesh Standard Time"
sunrise-profile="sun-center-no-refraction">
<sunrise local="2026-10-08T05:54:21+06:00" />
<next-sunrise local="2026-10-09T05:54:43+06:00" />
<assignment>[sunrise, next-sunrise)</assignment>
</panchanga-day>
Cache key-তে অন্তত anchor date, rounded/exact coordinates, time-zone ID, sunrise profile এবং astronomy version রাখুন। শুধু civil date + city label যথেষ্ট নয়।
সাধারণ implementation error
| ভুল | কী সমস্যা হয় | সংশোধন |
|---|---|---|
| Midnight-এ Panchanga day বদলানো | Pre-sunrise bar ও festival ভুল | Local sunrise comparison |
Now.DayOfWeek সরাসরি ব্যবহার | ভোরের live report এক দিন এগিয়ে | Anchor date resolve করে weekday |
| Next sunrise = sunrise + 24h | DST ও seasonal shift ভুল | পরের civil date independently calculate |
| Local wall-time compare | DST ambiguous time | UTC instant compare |
| Offset-ই time zone | Historical/future DST হারায় | Zone ID + versioned rules |
| Location ও zone mismatch | Sunrise local time ভুল | Validated location profile |
| Sunrise profile গোপন | Printed Panchanga mismatch ব্যাখ্যাতীত | Definition/version publish |
| Exact next sunrise current day-এ | Boundary duplicate | Half-open interval |
| Julian/Gregorian gap-এ weekday reset | Historical sequence ভাঙে | Continuous JD/JDN core |
| No sunrise হলে midnight fallback | অপ্রকাশিত invented result | Explicit status/policy |
Validation checklist
- Exact sunrise instant নতুন Panchanga day;
- Sunrise-এর এক second আগে আগের day;
- Sunrise-এর এক second পরে নতুন day;
- Exact next sunrise current span থেকে excluded;
- Local midnight-এ bar অপরিবর্তিত, যদি sunrise না হয়;
- Pre-sunrise local time আগের anchor date;
- Post-sunrise local time বর্তমান anchor date;
- Date report-এ requested date-ই anchor;
- Live mode-এ sunrise comparison;
- Sunday=0 enum mapping;
- সাতটি stable key language-independent;
- Kolkata fixed-zone normal day;
- Dhaka fixed-zone normal day;
- New York DST spring-forward;
- New York DST fall-back;
- Next sunrise independently calculated;
- Span duration ২৪ ঘণ্টা hard-code নয়;
- Same UTC instant, Dhaka বনাম New York;
- Latitude/longitude-zone mismatch rejected;
- Invalid zone ID handled;
- Sunrise calculator no-result handled;
- Upper-limb/refraction profile;
- Center/no-refraction profile;
- Profile change cache invalidates;
- Event at sunrise included;
- Event inside span included;
- Event at next sunrise excluded;
- Post-midnight event একই Panchanga day;
- Multiple Karana transitions chronological;
- No duplicate shared-boundary event;
- “অহোরাত্র” path;
- Julian/Gregorian dual label একই JD;
- Dual label-এ একই weekday;
- ১৫৮২ reform continuity;
- ১৭৫২ regional reform label test;
- ৫৯৪ খ্রিস্টাব্দ JD/JDN weekday;
- Historical fixed-offset profile;
- Negative/proleptic date modulo test;
- UTC comparison around offset change;
- API ও HTML একই anchor date;
- XML ও printed report একই bar;
- URL date semantics documented;
- Cache key-তে coordinate/zone/profile;
- Polar no-sunrise explicit error;
- Regression against trusted printed Panchanga।
উপসংহার
Civil date রাত ১২টায় বদলায়, কিন্তু Panchanga day স্থানীয় sunrise-এ বদলায়। তাই bar নির্ণয়ের আগে প্রশ্নটি পরিষ্কার করতে হবে: user কি একটি civil date-এর report চেয়েছেন, নাকি exact instant-এর live Panchanga state?
Date report-এর span হলো [Sunrise(D), Sunrise(D+1)) এবং bar হলো D-এর weekday। Live mode-এ local date-এর sunrise-এর সঙ্গে current UTC instant compare করে anchor date আজ বা গতকাল নির্বাচন করতে হয়। পরের sunrise কখনোই কেবল ২৪ ঘণ্টা যোগ করে বানানো উচিত নয়—বিশেষ করে DST region-এ।
Continuous JD/JDN core, explicit sunrise profile, validated location/time zone এবং half-open event interval একসঙ্গে ব্যবহার করলে Daily Details, print report, API, XML ও historical dual-calendar output একই নিয়মে স্থিতিশীল থাকে।
তথ্যসূত্র ও আরও পাঠ
- Positional Astronomy Centre—Rashtriya Panchang Explanation; ভারতীয় sunrise-day convention, Panchanga elements, post-midnight time এবং sunrise definition.
- Rashtriya Panchang—Daily Panchanga; weekday, sunrise ও daily elements-এর official presentation.
- U.S. Naval Observatory—Rise, Set and Twilight Definitions; standard computational sunrise-এর geometric definition.
- Nachum Dershowitz ও Edward M. Reingold—Indian Calendrical Calculations; critical time, sunrise, Julian day ও Indian calendar framework.
- Microsoft Learn—TimeZoneInfo.ConvertTime; .NET time-zone conversion reference.
- Microsoft Learn—DateTimeOffset; offset-bearing instant model.
- সূর্য সিদ্ধান্ত পঞ্জিকা project specification; sunrise-to-sunrise daily scan, Julian/Gregorian dual display ও location-specific rendering.
মন্তব্য, আলোচনা ও প্রশ্ন