This commit is contained in:
2026-07-28 02:04:36 +03:30
commit a5aed05b98
31 changed files with 3500 additions and 0 deletions
+109
View File
@@ -0,0 +1,109 @@
package jalali
import (
"fmt"
"time"
)
var MonthNames = [...]string{
"", "Farvardin", "Ordibehesht", "Khordad", "Tir", "Mordad", "Shahrivar",
"Mehr", "Aban", "Azar", "Dey", "Bahman", "Esfand",
}
var MonthNamesFA = [...]string{
"", "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور",
"مهر", "آبان", "آذر", "دی", "بهمن", "اسفند",
}
type Date struct {
Year int
Month int
Day int
}
func (d Date) String() string { return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day) }
func IsLeap(year int) bool {
return ToGregorian(year+1, 1, 1).Sub(ToGregorian(year, 1, 1)) == 366*24*time.Hour
}
func DaysInMonth(year, month int) int {
if month <= 6 {
return 31
}
if month <= 11 {
return 30
}
if IsLeap(year) {
return 30
}
return 29
}
func FromTime(t time.Time) Date {
gy, gm, gd := t.Date()
jy, jm, jd := gregorianToJalali(gy, int(gm), gd)
return Date{jy, jm, jd}
}
func ToGregorian(jy, jm, jd int) time.Time {
gy, gm, gd := jalaliToGregorian(jy, jm, jd)
return time.Date(gy, time.Month(gm), gd, 0, 0, 0, 0, time.Local)
}
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
if gm > 2 {
gy2++
}
days := 355666 + 365*gy + (gy2+3)/4 - (gy2+99)/100 + (gy2+399)/400 + gd + gdm[gm-1]
jy := -1595 + 33*(days/12053)
days %= 12053
jy += 4 * (days / 1461)
days %= 1461
if days > 365 {
jy += (days - 1) / 365
days = (days - 1) % 365
}
if days < 186 {
return jy, 1 + days/31, 1 + days%31
}
return jy, 7 + (days-186)/30, 1 + (days-186)%30
}
func jalaliToGregorian(jy, jm, jd int) (int, int, int) {
jy += 1595
days := -355668 + 365*jy + (jy/33)*8 + (jy%33+3)/4 + jd
if jm < 7 {
days += (jm - 1) * 31
} else {
days += (jm-7)*30 + 186
}
gy := 400 * (days / 146097)
days %= 146097
if days > 36524 {
gy += 100 * ((days - 1) / 36524)
days = (days - 1) % 36524
if days >= 365 {
days++
}
}
gy += 4 * (days / 1461)
days %= 1461
if days > 365 {
gy += (days - 1) / 365
days = (days - 1) % 365
}
gd := days + 1
sal := [...]int{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
if (gy%4 == 0 && gy%100 != 0) || gy%400 == 0 {
sal[2] = 29
}
gm := 1
for gm <= 12 && gd > sal[gm] {
gd -= sal[gm]
gm++
}
return gy, gm, gd
}
+36
View File
@@ -0,0 +1,36 @@
package jalali
import (
"testing"
"time"
)
func TestKnownDates(t *testing.T) {
tests := []struct {
gregorian string
jalali Date
}{
{"2024-03-20", Date{1403, 1, 1}},
{"2025-03-21", Date{1404, 1, 1}},
{"2026-07-28", Date{1405, 5, 6}},
}
for _, tt := range tests {
g, _ := time.Parse("2006-01-02", tt.gregorian)
if got := FromTime(g); got != tt.jalali {
t.Errorf("%s: got %v, want %v", tt.gregorian, got, tt.jalali)
}
back := ToGregorian(tt.jalali.Year, tt.jalali.Month, tt.jalali.Day)
if got := back.Format("2006-01-02"); got != tt.gregorian {
t.Errorf("%v: got %s, want %s", tt.jalali, got, tt.gregorian)
}
}
}
func TestMonthLength(t *testing.T) {
if got := DaysInMonth(1399, 12); got != 30 {
t.Fatalf("leap Esfand: got %d, want 30", got)
}
if got := DaysInMonth(1400, 12); got != 29 {
t.Fatalf("regular Esfand: got %d, want 29", got)
}
}