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
This commit is contained in:
2026-06-24 09:48:39 +03:30
parent 9d123316e6
commit b900710196

View File

@@ -59,78 +59,75 @@ func monthLenHijri(year, month int) int {
return 29 return 29
} }
func gregorianToJalali(gy, gm, gd int) (int, int, int) { // JDN for March 21, 622 CE (Jalali epoch: 1 Farvardin 1)
gdm := []int{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334} const jalaliEpochJDN = 1948320
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
jy := 979 func gregorianToJDN(gy, gm, gd int) int {
for { a := (14 - gm) / 12
jyDays := 365 y := gy + 4800 - a
if isJalaliLeap(jy) { m := gm + 12*a - 3
jyDays = 366 return gd + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045
} }
if gd2 < jyDays {
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 {
yearDays := 365
if isJalaliLeap(jy) {
yearDays = 366
}
if days < yearDays {
break break
} }
gd2 -= jyDays days -= yearDays
jy++ jy++
} }
jm := 1 jm := 1
for _, d := range jalaliMonthDays { for mm := 0; mm < 12; mm++ {
if gd2 < d { md := monthLenJalali(jy, mm+1)
if days < md {
break break
} }
gd2 -= d days -= md
jm++ jm++
} }
return jy, jm, gd2 + 1 jd := days + 1
return jy, jm, jd
} }
func jalaliToGregorian(jy, jm, jd int) (int, int, int) { func jalaliToGregorian(jy, jm, jd int) (int, int, int) {
jy2 := jy - 979 days := 0
days := jy2 * 365 for y := 1; y < jy; y++ {
for y := 979; y < jy; y++ {
if isJalaliLeap(y) { if isJalaliLeap(y) {
days++ days += 366
} else {
days += 365
} }
} }
for m := 1; m < jm; m++ {
jdm := []int{0, 31, 62, 93, 124, 155, 186, 216, 246, 276, 306, 336} days += monthLenJalali(jy, m)
days += jdm[jm-1] + jd - 1
days += 226899
gy := 1
for {
gyDays := 365
if isGregorianLeap(gy) {
gyDays = 366
} }
if days < gyDays { days += jd - 1
break jdn := jalaliEpochJDN + days
} return jdnToGregorian(jdn)
days -= gyDays
gy++
}
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
} }
func hijriToGregorian(hy, hm, hd int) (int, int, int) { 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) { func gregorianToHijri(gy, gm, gd int) (int, int, int) {
a := (14 - gm) / 12 jdn := gregorianToJDN(gy, gm, gd)
y := gy + 4800 - a
m := gm + 12*a - 3
jdn := gd + (153*m+2)/5 + 365*y + y/4 - y/100 + y/400 - 32045
days := jdn - 1948440 days := jdn - 1948440
hy := 1 hy := 1
@@ -259,7 +252,6 @@ func buildCalendarMonth(cal string, year, month int) calMonth {
totalDays = monthLenGreg(year, month) totalDays = monthLenGreg(year, month)
dow := dayOfWeekGregorian(year, month, 1) dow := dayOfWeekGregorian(year, month, 1)
// Convert Sunday=0 to Monday=0 alignment
startOffset := (dow + 6) % 7 startOffset := (dow + 6) % 7
var days []calDay var days []calDay