-
Notifications
You must be signed in to change notification settings - Fork 11
/
InternalDateTime.roc
215 lines (185 loc) · 6.57 KB
/
InternalDateTime.roc
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
interface InternalDateTime
exposes [
DateTime,
toIso8601Str,
epochMillisToDateTime,
]
imports []
DateTime : { year : I128, month : I128, day : I128, hours : I128, minutes : I128, seconds : I128 }
toIso8601Str : DateTime -> Str
toIso8601Str = \{ year, month, day, hours, minutes, seconds } ->
yearStr = yearWithPaddedZeros year
monthStr = monthWithPaddedZeros month
dayStr = dayWithPaddedZeros day
hourStr = hoursWithPaddedZeros hours
minuteStr = minutesWithPaddedZeros minutes
secondsStr = secondsWithPaddedZeros seconds
"\(yearStr)-\(monthStr)-\(dayStr)T\(hourStr):\(minuteStr):\(secondsStr)Z"
yearWithPaddedZeros : I128 -> Str
yearWithPaddedZeros = \year ->
yearStr = Num.toStr year
if year < 10 then
"000\(yearStr)"
else if year < 100 then
"00\(yearStr)"
else if year < 1000 then
"0\(yearStr)"
else
yearStr
monthWithPaddedZeros : I128 -> Str
monthWithPaddedZeros = \month ->
monthStr = Num.toStr month
if month < 10 then
"0\(monthStr)"
else
monthStr
dayWithPaddedZeros : I128 -> Str
dayWithPaddedZeros = monthWithPaddedZeros
hoursWithPaddedZeros : I128 -> Str
hoursWithPaddedZeros = monthWithPaddedZeros
minutesWithPaddedZeros : I128 -> Str
minutesWithPaddedZeros = monthWithPaddedZeros
secondsWithPaddedZeros : I128 -> Str
secondsWithPaddedZeros = monthWithPaddedZeros
isLeapYear : I128 -> Bool
isLeapYear = \year ->
(year % 4 == 0)
&& # divided evenly by 4 unless...
(
(year % 100 != 0)
|| # divided by 100 not a leap year
(year % 400 == 0) # expecpt when also divisible by 400
)
expect isLeapYear 2000
expect isLeapYear 2012
expect !(isLeapYear 1900)
expect !(isLeapYear 2015)
expect List.map [2023, 1988, 1992, 1996] isLeapYear == [Bool.false, Bool.true, Bool.true, Bool.true]
expect List.map [1700, 1800, 1900, 2100, 2200, 2300, 2500, 2600] isLeapYear == [Bool.false, Bool.false, Bool.false, Bool.false, Bool.false, Bool.false, Bool.false, Bool.false]
daysInMonth : I128, I128 -> I128
daysInMonth = \year, month ->
if List.contains [1, 3, 5, 7, 8, 10, 12] month then
31
else if List.contains [4, 6, 9, 11] month then
30
else if month == 2 then
(if isLeapYear year then 29 else 28)
else
0
expect daysInMonth 2023 1 == 31 # January
expect daysInMonth 2023 2 == 28 # February
expect daysInMonth 1996 2 == 29 # February in a leap year
expect daysInMonth 2023 3 == 31 # March
expect daysInMonth 2023 4 == 30 # April
expect daysInMonth 2023 5 == 31 # May
expect daysInMonth 2023 6 == 30 # June
expect daysInMonth 2023 7 == 31 # July
expect daysInMonth 2023 8 == 31 # August
expect daysInMonth 2023 9 == 30 # September
expect daysInMonth 2023 10 == 31 # October
expect daysInMonth 2023 11 == 30 # November
expect daysInMonth 2023 12 == 31 # December
epochMillisToDateTime : I128 -> DateTime
epochMillisToDateTime = \millis ->
seconds = millis // 1000
minutes = seconds // 60
hours = minutes // 60
day = 1 + hours // 24
month = 1
year = 1970
epochMillisToDateTimeHelp {
year,
month,
day,
hours: hours % 24,
minutes: minutes % 60,
seconds: seconds % 60,
}
epochMillisToDateTimeHelp : DateTime -> DateTime
epochMillisToDateTimeHelp = \current ->
countDaysInMonth = daysInMonth current.year current.month
countDaysInPrevMonth =
if current.month == 1 then daysInMonth (current.year - 1) 12
else daysInMonth current.year (current.month - 1)
if current.day < 1 then
epochMillisToDateTimeHelp {
current &
year: if current.month == 1 then current.year - 1 else current.year,
month: if current.month == 1 then 12 else current.month - 1,
day: current.day + countDaysInPrevMonth,
}
else if current.hours < 0 then
epochMillisToDateTimeHelp {
current &
day: current.day - 1,
hours: current.hours + 24
}
else if current.minutes < 0 then
epochMillisToDateTimeHelp {
current &
hours: current.hours - 1,
minutes: current.minutes + 60,
}
else if current.seconds < 0 then
epochMillisToDateTimeHelp {
current &
minutes: current.minutes - 1,
seconds: current.seconds + 60,
}
else if current.day > countDaysInMonth then
epochMillisToDateTimeHelp {
current &
year: if current.month == 12 then current.year + 1 else current.year,
month: if current.month == 12 then 1 else current.month + 1,
day: current.day - countDaysInMonth,
}
else
current
# test 1000 ms before epoch
expect
str = -1000 |> epochMillisToDateTime |> toIso8601Str
str == "1969-12-31T23:59:59Z"
# test 1 hour, 1 minute, 1 second before epoch
expect
str = (-3600 * 1000 - 60 * 1000 - 1000) |> epochMillisToDateTime |> toIso8601Str
str == "1969-12-31T22:58:59Z"
# test 1 month before epoch
expect
str = (-1 * 31 * 24 * 60 * 60 * 1000) |> epochMillisToDateTime |> toIso8601Str
str == "1969-12-01T00:00:00Z"
# test 1 year before epoch
expect
str = (-1 * 365 * 24 * 60 * 60 * 1000) |> epochMillisToDateTime |> toIso8601Str
str == "1969-01-01T00:00:00Z"
# test 1st leap year before epoch
expect
str = (-1 * (365 + 366) * 24 * 60 * 60 * 1000) |> epochMillisToDateTime |> toIso8601Str
str == "1968-01-01T00:00:00Z"
# test last day of 1st year after epoch
expect
str = (364 * 24 * 60 * 60 * 1000) |> epochMillisToDateTime |> toIso8601Str
str == "1970-12-31T00:00:00Z"
# test last day of 1st month after epoch
expect
str = (30 * 24 * 60 * 60 * 1000) |> epochMillisToDateTime |> toIso8601Str
str == "1970-01-31T00:00:00Z"
# test 1_700_005_179_053 ms past epoch
expect
str = 1_700_005_179_053 |> epochMillisToDateTime |> toIso8601Str
str == "2023-11-14T23:39:39Z"
# test 1000 ms past epoch
expect
str = 1_000 |> epochMillisToDateTime |> toIso8601Str
str == "1970-01-01T00:00:01Z"
# test 1_000_000 ms past epoch
expect
str = 1_000_000 |> epochMillisToDateTime |> toIso8601Str
str == "1970-01-01T00:16:40Z"
# test 1_000_000_000 ms past epoch
expect
str = 1_000_000_000 |> epochMillisToDateTime |> toIso8601Str
str == "1970-01-12T13:46:40Z"
# test 1_600_005_179_000 ms past epoch
expect
str = 1_600_005_179_000 |> epochMillisToDateTime |> toIso8601Str
str == "2020-09-13T13:52:59Z"