From b900710196c0689122ffbab799134d19a35a96b9 Mon Sep 17 00:00:00 2001 From: db123 Date: Wed, 24 Jun 2026 09:48:39 +0330 Subject: [PATCH] fix: correct Jalali calendar year conversion Complete rewrite of gregorianToJalali and jalaliToGregorian using proper JDN arithmetic. Removed broken epoch offset (226899 / year 979) and replaced with jalaliEpochJDN = 1948320 (March 21, 622 CE). Verified: 2026-06-24 -> Jalali 1405-04-03 (correct) Nowruz 1405 verified as 2026-03-21 --- internal/bot/dateutil.go | 106 ++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 57 deletions(-) diff --git a/internal/bot/dateutil.go b/internal/bot/dateutil.go index b1b6816..9eba40c 100644 --- a/internal/bot/dateutil.go +++ b/internal/bot/dateutil.go @@ -59,78 +59,75 @@ func monthLenHijri(year, month int) int { return 29 } -func gregorianToJalali(gy, gm, gd int) (int, int, int) { - gdm := []int{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334} - gy2 := gy - 1 - gd2 := gdm[gm-1] + gd - if gm > 2 && isGregorianLeap(gy) { - gd2++ - } - gd2 += gy2*365 + gy2/4 - gy2/100 + gy2/400 - gd2 -= 226899 +// JDN for March 21, 622 CE (Jalali epoch: 1 Farvardin 1) +const jalaliEpochJDN = 1948320 - jy := 979 +func gregorianToJDN(gy, gm, gd int) int { + a := (14 - gm) / 12 + y := gy + 4800 - a + m := gm + 12*a - 3 + return gd + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045 +} + +func jdnToGregorian(jdn int) (int, int, int) { + f := jdn + 1401 + ((4*jdn+274277)/146097)*3/4 - 38 + e := 4*f + 3 + g := (e % 1461) / 4 + h := 5*g + 2 + day := (h%153)/5 + 1 + month := ((h/153 + 2) % 12) + 1 + year := e/1461 - 4716 + (14-month)/12 + return year, month, day +} + +func gregorianToJalali(gy, gm, gd int) (int, int, int) { + jdn := gregorianToJDN(gy, gm, gd) + days := jdn - jalaliEpochJDN + if days < 0 { + return 0, 0, 0 + } + + jy := 1 for { - jyDays := 365 + yearDays := 365 if isJalaliLeap(jy) { - jyDays = 366 + yearDays = 366 } - if gd2 < jyDays { + if days < yearDays { break } - gd2 -= jyDays + days -= yearDays jy++ } jm := 1 - for _, d := range jalaliMonthDays { - if gd2 < d { + for mm := 0; mm < 12; mm++ { + md := monthLenJalali(jy, mm+1) + if days < md { break } - gd2 -= d + days -= md jm++ } - return jy, jm, gd2 + 1 + jd := days + 1 + return jy, jm, jd } func jalaliToGregorian(jy, jm, jd int) (int, int, int) { - jy2 := jy - 979 - days := jy2 * 365 - for y := 979; y < jy; y++ { + days := 0 + for y := 1; y < jy; y++ { if isJalaliLeap(y) { - days++ + days += 366 + } else { + days += 365 } } - - jdm := []int{0, 31, 62, 93, 124, 155, 186, 216, 246, 276, 306, 336} - days += jdm[jm-1] + jd - 1 - days += 226899 - - gy := 1 - for { - gyDays := 365 - if isGregorianLeap(gy) { - gyDays = 366 - } - if days < gyDays { - break - } - days -= gyDays - gy++ + for m := 1; m < jm; m++ { + days += monthLenJalali(jy, m) } - - gm := 1 - for i, d := range gregMonthDays { - if gm == 2 && isGregorianLeap(gy) { - d = 29 - } - if days < d { - break - } - days -= d - gm = i + 2 - } - return gy, gm, days + 1 + days += jd - 1 + jdn := jalaliEpochJDN + days + return jdnToGregorian(jdn) } func hijriToGregorian(hy, hm, hd int) (int, int, int) { @@ -153,11 +150,7 @@ func hijriToGregorian(hy, hm, hd int) (int, int, int) { } func gregorianToHijri(gy, gm, gd int) (int, int, int) { - a := (14 - gm) / 12 - y := gy + 4800 - a - m := gm + 12*a - 3 - jdn := gd + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045 - + jdn := gregorianToJDN(gy, gm, gd) days := jdn - 1948440 hy := 1 @@ -259,7 +252,6 @@ func buildCalendarMonth(cal string, year, month int) calMonth { totalDays = monthLenGreg(year, month) dow := dayOfWeekGregorian(year, month, 1) - // Convert Sunday=0 to Monday=0 alignment startOffset := (dow + 6) % 7 var days []calDay