বাংলা solar month-এর দুটি আলাদা “শুরু” আছে। প্রথমটি astronomical—সূর্যের নিরয়ণ longitude ঠিক ০°, ৩০°, ৬০° ইত্যাদি অতিক্রমের মুহূর্ত। দ্বিতীয়টি calendrical—কোন sunrise থেকে নতুন মাসের ১ তারিখ গণনা হবে। প্রথমটি একটি instant; দ্বিতীয়টি sunrise-এ শুরু হওয়া পূর্ণ civil day। এই দুটিকে এক করে ফেললে calendar এক বা দুই দিন সরে যেতে পারে।
বাংলা মাস কেন সৌর মাস?
বাংলা বছরের মাসগুলি সূর্যের বারো নিরয়ণ রাশিতে অবস্থানের সঙ্গে যুক্ত। সূর্য মেষে প্রবেশ করলে বৈশাখ, বৃষে প্রবেশ করলে জ্যৈষ্ঠ—এইভাবে মীনে প্রবেশ পর্যন্ত বারোটি মাস চলে। ফলে মাসের astronomical duration হলো পরপর দুটি solar ingress-এর মধ্যবর্তী interval।
এটি lunar tithi-ভিত্তিক মাস নয়। তিথি মূলত সূর্য ও চন্দ্রের angular elongation থেকে নির্ণীত; কিন্তু বাংলা সৌর মাসের boundary নির্ধারণ করে সূর্যের রাশি পরিবর্তন। তবু Bengal Rule-এর midnight boundary case-এ চলমান তিথির শেষ মুহূর্ত একটি tie-breaking criterion হিসেবে ব্যবহৃত হয়।
সৌর রাশি ও বাংলা মাসের মানচিত্র
| রাশি-প্রবেশ | Longitude boundary | নতুন বাংলা মাস | মাস সংখ্যা |
|---|---|---|---|
| মেষ | ০° | বৈশাখ | ১ |
| বৃষ | ৩০° | জ্যৈষ্ঠ | ২ |
| মিথুন | ৬০° | আষাঢ় | ৩ |
| কর্কট | ৯০° | শ্রাবণ | ৪ |
| সিংহ | ১২০° | ভাদ্র | ৫ |
| কন্যা | ১৫০° | আশ্বিন | ৬ |
| তুলা | ১৮০° | কার্তিক | ৭ |
| বৃশ্চিক | ২১০° | অগ্রহায়ণ | ৮ |
| ধনু | ২৪০° | পৌষ | ৯ |
| মকর | ২৭০° | মাঘ | ১০ |
| কুম্ভ | ৩০০° | ফাল্গুন | ১১ |
| মীন | ৩৩০° | চৈত্র | ১২ |
এখানে longitude অবশ্যই selected calculation profile-এর নিরয়ণ true Sun। Traditional Surya Siddhanta এবং Drik/Lahiri profile একই clock instant-এ সামান্য বা অনেকটা ভিন্ন longitude দিতে পারে; ফলে ingress time এবং কখনো প্রথম দিনের civil date-ও বদলাতে পারে।
সংক্রান্তির exact moment কীভাবে পাওয়া যায়?
একটি daily sample দেখে যে দিন Sun sign বদলেছে সেটিকেই সংক্রান্তির সময় বলা যায় না। Exact ingress-এর জন্য target longitude-এ root solve করতে হয়:
Normalize(TrueSun(t) − TargetLongitude) = 0°
৩৫৯° থেকে ০° crossing-এর সময় সাধারণ subtraction discontinuous হয়। তাই search interval-এ longitude unwrap করে signed function বানাতে হবে। একটি safe process:
- Expected ingress-এর আগে ও পরে True Sun sample করুন;
- Target-এর চারপাশে continuous angular difference তৈরি করুন;
- Opposite signs পাওয়া গেলে bracket স্থির করুন;
- Bisection বা Brent method-এ desired second tolerance পর্যন্ত solve করুন;
- UTC/JD event-কে selected location-এর local time-এ রূপান্তর করুন;
- Raw event ও applied calendar rule দুটিই report-এ রাখুন।
Saṅkrānti time round করে rule প্রয়োগ করবেন না। ১১:৫৯:৫০ PM-কে “12:00 AM” করলে event midnight-এর অন্য পাশে চলে যেতে পারে। Decision raw instant-এ হবে; display rounding পরে।
পঞ্জিকার দিন: সূর্যোদয় থেকে পরের সূর্যোদয়
Modern civil date midnight-এ বদলায়, কিন্তু traditional Panjika day sunrise-এ শুরু হয়ে পরের sunrise পর্যন্ত চলে। শুক্রবার sunrise-এর পরে শুরু হওয়া Panjika day শনিবার sunrise-এর আগে পর্যন্ত একই traditional weekday/day unit হিসেবে বিবেচিত হতে পারে।
= [Sunrise(D), Sunrise(D + 1))
এই interval-এর ভেতরে local midnight একটি internal boundary। তাই event-এর civil DateTime.Date দেখে শুধু AddDays(1) করলেই Bengal Rule বাস্তবায়িত হয় না। প্রথমে event কোন sunrise-to-sunrise interval-এ পড়েছে তা নির্ণয় করতে হবে।
প্রয়োজনীয় চারটি instant:
daySunrise—event-এর Panjika day-এর শুরু;midnight—এই sunrise-এর পরবর্তী local 00:00;nextSunrise—event-day-এর পরের sunrise;followingSunrise—তারও পরের sunrise।
বাংলার সাধারণ সংক্রান্তি-বিধি
Government of India Calendar Reform Committee-এর ঐতিহাসিক survey এবং Sewell–Dikshit-এর The Indian Calendar-এ Bengal Rule একই মূল কাঠামোয় দেওয়া হয়েছে:
| সংক্রান্তির সময় | নতুন মাস শুরু | Software result |
|---|---|---|
| Day sunrise থেকে local midnight-এর আগে | পরের দিন | nextSunrise |
| Local midnight-এর পরে, কিন্তু next sunrise-এর আগে | তার পরের দিন বা “তৃতীয় দিন” | followingSunrise |
“তৃতীয় দিন” কথাটি Panjika day-এর original sunrise label থেকে গণনা করা। ধরুন শুক্রবার sunrise-এর পর শুরু হওয়া দিনে শনিবার ভোর ২টায় সংক্রান্তি হলো। Clock date শনিবার হলেও event এখনও শুক্রবার sunrise থেকে শনিবার sunrise পর্যন্ত Panjika day-এর মধ্যে। সাধারণ নিয়মে ১ তারিখ রবিবার sunrise-এ শুরু হবে।
মধ্যরাত্রির ±২৪ মিনিট: তিথি-পরীক্ষা
সংক্রান্তি যদি midnight-এর খুব কাছাকাছি হয়, শুধু clock-এর আগে/পরে বিচার না করে sunrise-এ চলমান তিথি event পর্যন্ত টিকে আছে কি না দেখা হয়। Critical window:
≤ Saṅkrānti ≤
Local midnight + 24 minutes
এই ৪৮ মিনিটের interval-এ:
- Day sunrise-এ যে তিথি চলছিল তার end instant যদি সংক্রান্তি পর্যন্ত পৌঁছায় বা তার পরে হয়, নতুন মাস
nextSunrise-এ শুরু হবে; - সেই তিথি যদি সংক্রান্তির আগেই শেষ হয়ে যায়, নতুন মাস
followingSunrise-এ শুরু হবে।
→ First day at next sunrise
If SunriseTithiEnd < Saṅkrānti
→ First day at following sunrise
এখানে “sunrise tithi” মানে daySunrise instant-এ current tithi। Saṅkrānti moment-এর tithi আলাদাভাবে lookup করে শুধু নাম তুলনা করা যথেষ্ট নয়; exact tithi-end instant দরকার।
কর্কট ও মকর সংক্রান্তির বিশেষ নিয়ম
Critical ৪৮ মিনিটের window-এ কর্কট ও মকর সংক্রান্তির জন্য sunrise-tithi test প্রয়োগ করা হয় না:
| সংক্রান্তি | নতুন বাংলা মাস | Window-এর ফল |
|---|---|---|
| কর্কট সংক্রান্তি | শ্রাবণ | তিথি না দেখে nextSunrise |
| মকর সংক্রান্তি | মাঘ | তিথি না দেখে followingSunrise |
এই exception শুধু midnight ±২৪ মিনিট window-এ। Window-এর বাইরে সাধারণ sunrise–midnight/after-midnight rule কার্যকর থাকবে। Code-এ Karkaṭa/Makara check-কে global override বানালে দিনের অন্য সময়ের ingress ভুল হয়ে যাবে।
সম্পূর্ণ decision table
| Priority | Condition | ১ তারিখের sunrise | Applied rule |
|---|---|---|---|
| ১ | Within ±24m এবং Karkaṭa | Next | MidnightWindowKarkata |
| ২ | Within ±24m এবং Makara | Following | MidnightWindowMakara |
| ৩ | Within ±24m; sunrise tithi end ≥ ingress | Next | MidnightWindowTithiExtends |
| ৪ | Within ±24m; sunrise tithi end < ingress | Following | MidnightWindowTithiEnded |
| ৫ | Outside window; ingress < midnight | Next | BeforeMidnight |
| ৬ | Outside window; ingress ≥ midnight | Following | AfterMidnight |
Priority order গুরুত্বপূর্ণ। প্রথমে window এবং special saṅkrānti, তারপর tithi test, শেষে general clock rule। Applied-rule code report-এ রাখলে কোনো date কেন next না following sunrise হয়েছে তা বোঝা যায়।
সময়ভিত্তিক উদাহরণ
ধরা যাক daySunrise শুক্রবার ৫:৩০ AM, local midnight শনিবার ১২:০০ AM, nextSunrise শনিবার ৫:৩১ AM এবং followingSunrise রবিবার ৫:৩২ AM।
| Ingress | পরিস্থিতি | ১ তারিখ |
|---|---|---|
| শুক্রবার 4:00 PM | Sunrise–midnight; window-এর বাইরে | শনিবার sunrise |
| শুক্রবার 11:20 PM | Midnight-এর ৪০ মিনিট আগে; window-এর বাইরে | শনিবার sunrise |
| শুক্রবার 11:50 PM | Window-এর মধ্যে | Tithi-end test অনুযায়ী শনিবার বা রবিবার |
| শনিবার 12:10 AM | Window-এর মধ্যে | Tithi-end test অনুযায়ী শনিবার বা রবিবার |
| শনিবার 1:00 AM | Midnight-এর পরে; window-এর বাইরে | রবিবার sunrise |
| শনিবার 4:30 AM | Next sunrise-এর আগে, কিন্তু after-midnight branch | রবিবার sunrise |
Exact midnight, exact window edge এবং exact tithi-end equality software specification-এ স্থির করতে হবে। এই project-এর deterministic policy হলো window endpoints inclusive এবং tithiEnd == sankranti হলে তিথি event পর্যন্ত বিস্তৃত—অতএব next sunrise।
Location, sunrise ও printed Panjika matching
Saṅkrānti astronomical instant পৃথিবীব্যাপী একই UTC event হলেও local clock, midnight এবং sunrise location অনুযায়ী ভিন্ন। ফলে একই ingress Dhaka-তে midnight-এর আগে, New York-এ দুপুরে এবং অন্য কোথাও midnight-এর পরে পড়তে পারে। Location-aware calendar চাইলে rule selected city-এর sunrise ও midnight-এ প্রয়োগ হবে।
কিন্তু কোনো printed Panjika reproduce করতে হলে সেই বইয়ের reference place ব্যবহার করতে হবে। PM Bagchi-র Kolkata-oriented ফল যাচাই করার সময় Kolkata coordinates ও local-time convention pin করুন। User New York-এ থাকলেও printed Kolkata Bengali date অনুসরণ করতে চাইলে calendar-date reference Kolkata থাকবে; শুধু daily sunrise, tithi-end display ও local observance time New York-এ convert করা যেতে পারে।
| Mode | Month-start reference | Daily timings |
|---|---|---|
| Printed Kolkata Panjika | Kolkata fixed | Kolkata |
| Diaspora following Kolkata calendar | Kolkata fixed | User city optional |
| Fully local Panjika | User city | User city |
| Bangladesh civil calendar | Official fixed rules | Bangladesh location |
UI-তে “বাংলা তারিখের reference” এবং “দৈনিক সময়ের location” আলাদা selector দিলে ambiguity কমে। একটিমাত্র location field ব্যবহার করলে footer-এ স্পষ্ট লিখুন calendar date-ও location অনুযায়ী পরিবর্তিত হয়েছে কি না।
ঐতিহাসিক বছর, Julian/Gregorian label ও calendar cutover
বাংলা বছর ১ থেকে historical export-এ মাসের শুরু continuous astronomical day sequence দিয়ে নির্ণয় করতে হবে। Display label Julian বা proleptic Gregorian হতে পারে, কিন্তু ingress এবং sunrise-এর interval Julian Day বা equivalent continuous scale-এ থাকবে।
১৫৮২ বা ১৭৫২-এর calendar transition-এ civil labels বাদ গেছে; সূর্যোদয়ের ধারাবাহিকতা বাদ যায়নি। তাই month length বের করতে displayed day number বা DateTime label subtraction ব্যবহার করলে ১০/১১ দিনের false jump তৈরি হতে পারে।
= Count of sunrise boundaries
from current MonthStartJD to next MonthStartJD
—not subtraction of displayed calendar labels
Historical grid-এ প্রতিটি continuous day record-এর সঙ্গে দুটি label রাখা যায়:
day.InstantId // JD or sequential day key
day.JulianLabel // Historical/Old Style label
day.GregorianLabel // Proleptic Gregorian label
day.BengaliDate // Sunrise-rule result
day.AppliedRule // Month-start audit code
বাংলা বছর ৯৮৯-এর মতো reform-boundary export-এ ২৭–৩৫ দিনের sanity guard যেন label skip-কে physical month length না ভাবে। Guard elapsed sunrise count-এর ওপর হবে।
১৯৪৭-পরবর্তী Modern Fit Saṅkrānti branch
Modern printed Surya Siddhanta Panjika-এর saṅkrānti ও Bengali date matching-এর জন্য project-এ ১৯৪৭ থেকে month-wise dynamic star correction ব্যবহৃত হয়:
31, 20, 23, 48, 51, 39, 33, 39, 43, 32, 30, 34
Month-start pipeline-এ sequence:
- Bengali year/month থেকে approximate Gregorian search year বের করুন;
- Gregorian year ১৯৪৭ বা পরে হলে target month-এর Modern Fit star correction নির্বাচন করুন;
- সেই dedicated solar engine দিয়ে saṅkrānti instant solve করুন;
- Bengal Rule দিয়ে first sunrise নির্ধারণ করুন;
- Daily tithi, nakṣatra, yoga, karaṇa, graha ও eclipse আবার canonical engine-এ গণনা করুন।
C# implementation design
Calculation result শুধু একটি DateTime না হয়ে audit data-সহ model হওয়া উচিত:
public sealed class BengaliMonthStartResult
{
public int BengaliYear { get; init; }
public int MonthNumber { get; init; }
public DateTime SankrantiLocal { get; init; }
public double SankrantiJulianDayUt { get; init; }
public DateTime DaySunrise { get; init; }
public DateTime LocalMidnight { get; init; }
public DateTime NextSunrise { get; init; }
public DateTime FollowingSunrise { get; init; }
public DateTime? SunriseTithiEnd { get; init; }
public DateTime FirstDaySunrise { get; init; }
public string AppliedRule { get; init; }
public string CalculationProfileId { get; init; }
}
Core decision function:
DateTime SelectFirstDaySunrise(
int solarRasi,
DateTime sankranti,
DateTime midnight,
DateTime nextSunrise,
DateTime followingSunrise,
DateTime sunriseTithiEnd,
out string rule)
{
TimeSpan fromMidnight = sankranti - midnight;
bool inWindow = Math.Abs(fromMidnight.TotalMinutes) <= 24.0;
if (inWindow && solarRasi == 3) // Karkaṭa, zero-based
{
rule = "MidnightWindowKarkata";
return nextSunrise;
}
if (inWindow && solarRasi == 9) // Makara, zero-based
{
rule = "MidnightWindowMakara";
return followingSunrise;
}
if (inWindow)
{
if (sunriseTithiEnd >= sankranti)
{
rule = "MidnightWindowTithiExtends";
return nextSunrise;
}
rule = "MidnightWindowTithiEnded";
return followingSunrise;
}
if (sankranti < midnight)
{
rule = "BeforeMidnight";
return nextSunrise;
}
rule = "AfterMidnight";
return followingSunrise;
}
solarRasi zero-based না one-based তা enum দিয়ে স্পষ্ট করুন; magic number এড়ানো ভালো। আরও গুরুত্বপূর্ণ, midnight অবশ্যই event-এর Panjika-day sunrise-এর পরবর্তী midnight—sankranti.Date থেকে অন্ধভাবে বানানো নয়।
Recommended service split:
| Component | দায়িত্ব |
|---|---|
SankrantiFinder | Target solar longitude root solve |
SunriseDayResolver | Event-এর daySunrise, midnight ও দুই পরবর্তী sunrise |
SunriseTithiResolver | Day sunrise-এর current tithi ও exact end |
BengalMonthStartRule | Pure decision function; no rendering |
BengaliCalendarGridService | Start-to-next-start day records |
HistoricalCalendarDisplay | Julian/Gregorian labels only |
Month grid কীভাবে নির্মাণ করবেন?
একটি Bengali month-এর grid বানাতে current month start এবং next month start দুটিই আগে resolve করুন। এরপর continuous sunrise records enumerate করুন:
start = FindStart(bengaliYear, monthNumber, template);
next = FindStart(nextBengaliYear, nextMonthNumber, template);
for (day = start; day.InstantId < next.InstantId; day = NextSunriseDay(day))
{
bengaliDay++;
rows.Add(BuildCalendarDay(day, bengaliDay));
}
Grid cell-এর civil date midnight-based label হতে পারে, কিন্তু Bengali day sunrise-to-sunrise interval। Daily details page-এ “পঞ্জিকা দিনের সীমা: sunrise → next sunrise” দেখালে post-midnight events কেন আগের Panjika day-এ আছে তা user বুঝবেন।
Header-এ প্রথম ও শেষ Gregorian day না দেখিয়ে covered month/year labels দেখানো পরিষ্কার:
একই Bengali month দুই Gregorian month ছুঁলে “April–May 2027 · এপ্রিল–মে ২০২৭” দেখানো যেতে পারে; exact first/last day grid-এর cell-এ থাকবে।
Validation checklist
- True Sun target longitude crossing second-level tolerance-এর মধ্যে;
- ৩৫৯°→০° Mesha ingress wrap;
- Event exact daySunrise;
- Event midnight-এর ২৪ মিনিট ১ second আগে—general before-midnight;
- Event exactly midnight−২৪ মিনিট—window inclusive;
- Event exactly midnight;
- Event exactly midnight+২৪ মিনিট—window inclusive;
- Event ২৪ মিনিট ১ second পরে—general after-midnight;
- Tithi end exactly saṅkrānti—next sunrise;
- Tithi end one second before saṅkrānti—following sunrise;
- Karkaṭa window case ignores tithi and selects next sunrise;
- Makara window case ignores tithi and selects following sunrise;
- Special cases outside window use general rule;
- Kolkata, Dhaka ও New York same UTC ingress, different local context;
- DST transition day-এর invalid/ambiguous local time;
- বাংলা বছর ১ lower-bound search;
- ১৫৮২/১৭৫২ display-label cutover কিন্তু continuous sunrise count;
- বাংলা বছর ৯৮৯ month length guard;
- ১৯৪৬ canonical এবং ১৯৪৭ Modern Fit profile boundary;
- Modern Fit tithi/nakṣatra/yoga/karana/graha অপরিবর্তিত;
- Current month start < next month start;
- Month length elapsed sunrise count-এর গ্রহণযোগ্য সীমায়;
- AppliedRule daily details, research page ও batch export-এ একই।
Printed Panjika validation-এ শুধু ১ বৈশাখ নয়, বারো মাসের ingress ও first-day pair নিন। Midnight window বিরল; সেই কারণে synthetic tests বাধ্যতামূলক, নইলে বহু বছর ঠিক চলার পর edge case-এ error দেখা দেবে।
উপসংহার
বাংলা মাসের প্রথম দিন নির্ণয় দুই ধাপের কাজ। প্রথমে selected astronomical model-এ সূর্যের রাশি-প্রবেশের exact instant বের করতে হয়। তারপর Bengali civil convention সেই instant-কে একটি sunrise-এ শুরু হওয়া ১ তারিখে রূপান্তর করে।
বাংলার সাধারণ নিয়মে sunrise থেকে midnight-এর আগে সংক্রান্তি হলে next sunrise, midnight-এর পরে হলে following sunrise। Midnight-এর ±২৪ মিনিটে sunrise-tithi end বিচার হয়; কিন্তু Karkaṭa next sunrise এবং Makara following sunrise—তিথি পরীক্ষা ছাড়াই। এই rule ordering code, report এবং test—তিন জায়গায় একই থাকতে হবে।
Historical export-এ Julian Day continuity, dual Julian/Gregorian labels এবং sunrise count আলাদা রাখলে calendar-reform year-এ false month-length error এড়ানো যায়। আর ১৯৪৭-পরবর্তী Modern Fit star correction কেবল saṅkrānti ও Bengali solar date branch-এ সীমিত রাখলেই printed calendar matching এবং canonical Panchanga—দুইটি নিরাপদে পাশাপাশি চলতে পারে।
তথ্যসূত্র ও আরও পাঠ
- Government of India—Report of the Calendar Reform Committee, Council of Scientific and Industrial Research, New Delhi, 1955; solar-calendar conventions ও Bengal Rule.
- Robert Sewell ও Śaṅkara Bālakṛṣṇa Dīkṣita—The Indian Calendar, 1896; Bengal midnight rule ও regional solar-month conventions.
- Sewell–Dikshit, Bengal Rule—digitized page.
- M. Yano ও M. Fushimi—Pancanga 3.14 program notes; Ujjain reference এবং location-specific sunrise.
- PM Bagchi printed Panjika comparison archive; saṅkrānti, Bengali first-day and midnight-window validation records.
- সূর্য সিদ্ধান্ত পঞ্জিকা project,
SuryaSiddhantaEngine.cs,BengaliCalendarGridService.csএবং Sankranti Star Correction research table.
মন্তব্য, আলোচনা ও প্রশ্ন