-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathjtime.go
151 lines (124 loc) · 3.02 KB
/
jtime.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package jalali
import (
"fmt"
"time"
)
const (
secInMin = 60
minInHour = 60
secInHour = minInHour * secInMin
hourInDay = 24
minInDay = hourInDay * minInHour
secInDay = secInHour * hourInDay
)
const (
jEpocDay = 10 // Zero based
jEpocMonth = 9 // Dey (Month name is Dey), zero based
jEpocYear = 1348
jEpocDiff = 286 // Epoch day is the 286th day in jalali year, (zero based)
jEpocWDay = time.Thursday
jWeekLen = 7
jEpocLeapBefore = 211 // The year 1348 was not a leap year
jYearLen = 365
)
var (
jMonthLen = [12]int{31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29}
jAccuonthLen = [12]int{0, 31, 62, 93, 124, 155, 186, 216, 246, 276, 306, 336}
)
// secToJTime converts the sec (positive only) to day, hour minute and seconds.
// TODO : handle negative values for old times?
func secToDays(sec int) (day, hour, minute, second int) {
day = sec / secInDay
sec %= secInDay
hour = sec / secInHour
sec %= secInHour
minute = sec / secInMin
second = sec % secInMin
return
}
// TODO: check if instead of error we need to panic, i.e, we generate the day and is
// not from user input
func dayToMonth(days int) (int, int, error) {
if days < 0 || days > 365 {
return 0, 0, fmt.Errorf("the days in the year is out of range [0-365] but is %d", days)
}
var month int
for _, ml := range jMonthLen {
if days > ml {
days -= ml
month++
} else {
break
}
}
return month, days, nil
}
func monthDayToYear(month, days int) (int, error) {
if month < 0 || month > 11 {
return 0, fmt.Errorf("month %d is out of range, it should be [0-11]", month)
}
if days < 0 || days > jMonthLen[month] {
return 0, fmt.Errorf("for month %d day %d is out of range", month, days)
}
ydays := jAccuonthLen[month]
ydays += days
return ydays, nil
}
func dayToWeekday(days int) time.Weekday {
p := (days + int(jEpocWDay)) % jWeekLen
if p < 0 {
p += jWeekLen
}
return time.Weekday(p)
}
func dayToYear(days int) (int, int) {
direction := 1
current := jEpocYear
days += jEpocDiff
if days < 0 {
direction = -1
}
// TODO: Predict and jump to the nearest year, this loop is shit.
for ; ; current += direction {
ln := jYearLen
if IsLeap(current) {
ln++
}
if days >= 0 && days < ln {
break
}
days -= (direction * ln)
}
return current, days
}
//Warning: The result for month and day are zero based, but year is not
func dayToJTime(days int) (year, month, day int, err error) {
var remains int
year, remains = dayToYear(days)
month, day, err = dayToMonth(remains)
if err != nil {
return 0, 0, 0, fmt.Errorf("converting date failed: %w", err)
}
return
}
func timeToJTime(t time.Time) (year, month, day, hour, minute, sec int, err error) {
unix := t.Unix()
days, remains := unix/secInDay, unix%secInDay
if remains < 0 {
days++
remains = -remains
}
year, month, day, err = dayToJTime(int(days))
if err != nil {
return
}
// Month and day are zero based
month++
day++
var d int
d, hour, minute, sec = secToDays(int(remains))
if d != 0 {
panic("invalid")
}
return
}