| 1 | import time |
| 2 | |
| 3 | fn test_parse() { |
| 4 | s := '2018-01-27 12:48:34' |
| 5 | t := time.parse(s) or { |
| 6 | assert false, '> failing format: ${s} | err: ${err}' |
| 7 | return |
| 8 | } |
| 9 | assert t.year == 2018 && t.month == 1 && t.day == 27 && t.hour == 12 && t.minute == 48 |
| 10 | && t.second == 34 |
| 11 | assert t.unix() == 1517057314 |
| 12 | } |
| 13 | |
| 14 | fn test_parse_invalid() { |
| 15 | if x := time.parse('Invalid time string') { |
| 16 | assert false |
| 17 | } |
| 18 | assert true |
| 19 | |
| 20 | if x := time.parse('2020-02-02 02.20.02') { |
| 21 | assert false |
| 22 | } |
| 23 | assert true |
| 24 | } |
| 25 | |
| 26 | fn test_parse_rfc2822() { |
| 27 | s1 := 'Thu, 12 Dec 2019 06:07:45 GMT' |
| 28 | t1 := time.parse_rfc2822(s1) or { |
| 29 | assert false, '> failing format: ${s1} | err: ${err}' |
| 30 | return |
| 31 | } |
| 32 | assert t1.year == 2019 && t1.month == 12 && t1.day == 12 && t1.hour == 6 && t1.minute == 7 |
| 33 | && t1.second == 45 |
| 34 | assert t1.unix() == 1576130865 |
| 35 | s2 := 'Thu 12 Dec 2019 06:07:45 +0800' |
| 36 | t2 := time.parse_rfc2822(s2) or { |
| 37 | assert false, '> failing format: ${s2} | err: ${err}' |
| 38 | return |
| 39 | } |
| 40 | assert t2.year == 2019 && t2.month == 12 && t2.day == 12 && t2.hour == 6 && t2.minute == 7 |
| 41 | && t2.second == 45 |
| 42 | assert t2.unix() == 1576130865 |
| 43 | } |
| 44 | |
| 45 | fn test_parse_rfc2822_invalid() { |
| 46 | s3 := 'Thu 12 Foo 2019 06:07:45 +0800' |
| 47 | time.parse_rfc2822(s3) or { |
| 48 | assert true |
| 49 | return |
| 50 | } |
| 51 | assert false |
| 52 | } |
| 53 | |
| 54 | fn test_parse_iso8601() { |
| 55 | formats := [ |
| 56 | '2020-06-05T15:38:06Z', |
| 57 | '2020-06-05T15:38:06.015959Z', |
| 58 | '2020-06-05T15:38:06.015959+00:00', |
| 59 | '2020-06-05T15:38:06.015959+02:00', |
| 60 | '2020-06-05T15:38:06.015959-02:00', |
| 61 | '2020-11-05T15:38:06.015959Z', |
| 62 | ] |
| 63 | times := [ |
| 64 | [2020, 6, 5, 15, 38, 6, 0], |
| 65 | [2020, 6, 5, 15, 38, 6, 15959000], |
| 66 | [2020, 6, 5, 15, 38, 6, 15959000], |
| 67 | [2020, 6, 5, 13, 38, 6, 15959000], |
| 68 | [2020, 6, 5, 17, 38, 6, 15959000], |
| 69 | [2020, 11, 5, 15, 38, 6, 15959000], |
| 70 | ] |
| 71 | for i, format in formats { |
| 72 | t := time.parse_iso8601(format) or { |
| 73 | assert false, '>>> failing format: ${format} | err: ${err}' |
| 74 | continue |
| 75 | } |
| 76 | year := times[i][0] |
| 77 | assert t.year == year |
| 78 | month := times[i][1] |
| 79 | assert t.month == month |
| 80 | day := times[i][2] |
| 81 | assert t.day == day |
| 82 | hour := times[i][3] |
| 83 | assert t.hour == hour |
| 84 | minute := times[i][4] |
| 85 | assert t.minute == minute |
| 86 | second := times[i][5] |
| 87 | assert t.second == second |
| 88 | nanosecond := times[i][6] |
| 89 | assert t.nanosecond == nanosecond |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | fn test_parse_iso8601_local() { |
| 94 | format := '2020-06-05T15:38:06.015959' |
| 95 | t := time.parse_iso8601(format) or { |
| 96 | assert false, '> failing format: ${format} | err: ${err}' |
| 97 | return |
| 98 | } |
| 99 | assert t.year == 2020 |
| 100 | assert t.month == 6 |
| 101 | assert t.day == 5 |
| 102 | assert t.hour == 15 |
| 103 | assert t.minute == 38 |
| 104 | assert t.second == 6 |
| 105 | assert t.nanosecond == 15959_000 |
| 106 | } |
| 107 | |
| 108 | fn test_parse_iso8601_invalid() { |
| 109 | formats := [ |
| 110 | '', |
| 111 | '2020-06-05X15:38:06.015959Z', |
| 112 | '2020-06-05T15:38:06.015959X', |
| 113 | '2020-06-05T15:38:06.015959+0000', |
| 114 | '2020-06-05T', |
| 115 | '2020-06-05Z', |
| 116 | '2020-06-05+00:00', |
| 117 | '15:38:06', |
| 118 | '2020-06-32T15:38:06.015959', |
| 119 | '2020-13-13T15:38:06.015959', |
| 120 | ] |
| 121 | for format in formats { |
| 122 | time.parse_iso8601(format) or { |
| 123 | assert true |
| 124 | continue |
| 125 | } |
| 126 | assert false |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | fn test_parse_iso8601_date_only() { |
| 131 | format := '2020-06-05' |
| 132 | t := time.parse_iso8601(format) or { |
| 133 | assert false, '> failing format: ${format} | err: ${err}' |
| 134 | return |
| 135 | } |
| 136 | assert t.year == 2020 |
| 137 | assert t.month == 6 |
| 138 | assert t.day == 5 |
| 139 | assert t.hour == 0 |
| 140 | assert t.minute == 0 |
| 141 | assert t.second == 0 |
| 142 | assert t.nanosecond == 0 |
| 143 | } |
| 144 | |
| 145 | fn check_invalid_date(s string) { |
| 146 | if date := time.parse(s) { |
| 147 | assert false, 'invalid date: "${s}" => "${date}"' |
| 148 | } |
| 149 | assert true |
| 150 | } |
| 151 | |
| 152 | fn invalid_rfc3339(s string) string { |
| 153 | if date := time.parse_rfc3339(s) { |
| 154 | assert false, 'invalid date: "${s}" => "${date}"' |
| 155 | } else { |
| 156 | assert true |
| 157 | return err.str() |
| 158 | } |
| 159 | return '' |
| 160 | } |
| 161 | |
| 162 | fn test_invalid_dates_should_error_during_parse() { |
| 163 | check_invalid_date('-99999-12-20 00:00:00') |
| 164 | check_invalid_date('99999-12-20 00:00:00') |
| 165 | |
| 166 | check_invalid_date('2008-00-20 00:00:00') |
| 167 | check_invalid_date('2008-25-20 00:00:00') |
| 168 | |
| 169 | check_invalid_date('2008-12-00 00:00:00') |
| 170 | check_invalid_date('2008-12-32 00:00:00') |
| 171 | |
| 172 | check_invalid_date('2008-12-01 30:00:00') |
| 173 | check_invalid_date('2008-12-01 00:60:00') |
| 174 | check_invalid_date('2008-12-01 00:01:60') |
| 175 | } |
| 176 | |
| 177 | fn test_parse_rfc3339() { |
| 178 | pairs := [ |
| 179 | ['2015-01-06T15:47:32.080254511Z', '2015-01-06 15:47:32.080254'], |
| 180 | ['2015-01-06T15:47:32.072697474Z', '2015-01-06 15:47:32.072697'], |
| 181 | ['2015-01-06T15:47:32.1234Z', '2015-01-06 15:47:32.123400'], |
| 182 | ['2015-01-06T15:47:32.001234Z', '2015-01-06 15:47:32.001234'], |
| 183 | ['2015-01-06T15:47:32Z', '2015-01-06 15:47:32.000000'], |
| 184 | ['2015-01-06T15:47:32+00:00', '2015-01-06 15:47:32.000000'], |
| 185 | ['2015-01-06T15:47:32-00:00', '2015-01-06 15:47:32.000000'], |
| 186 | ['2015-01-06T15:47:32-01:00', '2015-01-06 16:47:32.000000'], |
| 187 | ['2015-01-06T15:47:32+01:00', '2015-01-06 14:47:32.000000'], |
| 188 | ['2015-01-06T15:47:32-01:10', '2015-01-06 16:57:32.000000'], |
| 189 | ['2015-01-06T15:47:32+01:10', '2015-01-06 14:37:32.000000'], |
| 190 | ['2015-01-06T15:47:32.1234-00:00', '2015-01-06 15:47:32.123400'], |
| 191 | ['2015-01-06T15:47:32.1234+01:00', '2015-01-06 14:47:32.123400'], |
| 192 | ['2015-01-06T15:47:32.1234-01:00', '2015-01-06 16:47:32.123400'], |
| 193 | ['2015-01-06T22:59:59-00:10', '2015-01-06 23:09:59.000000'], |
| 194 | ['1979-05-27T07:32:00-08:00', '1979-05-27 15:32:00.000000'], |
| 195 | ['2024-10-19T22:47:08-00:00', '2024-10-19 22:47:08.000000'], |
| 196 | ['2024-10-19T22:47:08.9+00:00', '2024-10-19 22:47:08.900000'], |
| 197 | ['2024-10-20T01:47:08+03:00', '2024-10-19 22:47:08.000000'], |
| 198 | ['2024-10-20T01:47:08.981+03:00', '2024-10-19 22:47:08.981000'], |
| 199 | ['1979-05-27t07:32:00z', '1979-05-27 07:32:00.000000'], |
| 200 | ['1979-05-27 07:32:00Z', '1979-05-27 07:32:00.000000'], |
| 201 | ['1979-05-27T00:32:00.999999Z', '1979-05-27 00:32:00.999999'], |
| 202 | ] |
| 203 | for pair in pairs { |
| 204 | input, expected := pair[0], pair[1] |
| 205 | res := time.parse_rfc3339(input) or { |
| 206 | assert false, '>>> failing input: ${input} | err: ${err}' |
| 207 | return |
| 208 | } |
| 209 | output := res.format_ss_micro() |
| 210 | assert expected == output |
| 211 | } |
| 212 | assert invalid_rfc3339('22:47:08Z') == 'missing date part of RFC 3339' |
| 213 | assert invalid_rfc3339('01:47:08.981+03:00') == 'missing date part of RFC 3339' |
| 214 | assert invalid_rfc3339('2006-01-00T00:00:00Z') == 'date error: invalid day 0' |
| 215 | assert invalid_rfc3339('2006-01-32T00:00:00Z') == 'date error: invalid day 32' |
| 216 | assert invalid_rfc3339('2006-01-88T00:00:00Z') == 'date error: invalid day 88' |
| 217 | assert invalid_rfc3339('2006-00-01T00:00:00Z') == 'date error: invalid month 0' |
| 218 | assert invalid_rfc3339('2006-13-01T00:00:00Z') == 'date error: invalid month 13' |
| 219 | assert invalid_rfc3339('2006-77-01T00:00:00Z') == 'date error: invalid month 77' |
| 220 | assert invalid_rfc3339('2006-01-01T24:47:08Z') == 'invalid hour: 24' |
| 221 | assert invalid_rfc3339('2006-01-01T99:47:08Z') == 'invalid hour: 99' |
| 222 | assert invalid_rfc3339('2006-01-01T23:60:08Z') == 'invalid minute: 60' |
| 223 | assert invalid_rfc3339('2006-01-01T23:99:08Z') == 'invalid minute: 99' |
| 224 | assert invalid_rfc3339('2006-01-01T23:59:60Z') == 'invalid second: 60' |
| 225 | assert invalid_rfc3339('2006-01-01T23:59:99Z') == 'invalid second: 99' |
| 226 | assert invalid_rfc3339('1979-05-27X07:32:00Z') == 'invalid date-time separator:X' |
| 227 | assert invalid_rfc3339('1979-05-27T07:32:00') == 'timezone error: datetime string is too short' |
| 228 | assert invalid_rfc3339('1979-05-27') == 'date-time too short to parse' |
| 229 | assert invalid_rfc3339('1979-05-27T00:32:00.999999') == 'timezone error: expected "Z" or "z" or "+" or "-" in position 26, not "9"' |
| 230 | assert invalid_rfc3339('1979-05-27T00:32:00.') == 'timezone error: expected "Z" or "z" at the end of the string' |
| 231 | } |
| 232 | |
| 233 | fn test_parse_rfc3339_offset() { |
| 234 | pairs := [ |
| 235 | ['2024-01-15T14:00:00+01:35', '2024-01-15 12:25:00'], |
| 236 | ['2024-01-15T14:10:00+01:40', '2024-01-15 12:30:00'], |
| 237 | ['2024-01-15T14:00:00+10:55', '2024-01-15 03:05:00'], |
| 238 | ] |
| 239 | for pair in pairs { |
| 240 | input, expected := pair[0], pair[1] |
| 241 | res := time.parse_rfc3339(input)! |
| 242 | assert res.str() == expected |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | fn test_parse_rfc2616() { |
| 247 | mut r := time.parse_rfc2616('Wed, 06 Nov 2024 08:49:37 GMT')! |
| 248 | assert r.unix() == 1730882977 |
| 249 | |
| 250 | r = time.parse_rfc2616('Wednesday, 06-Nov-24 08:49:37 GMT')! |
| 251 | assert r.unix() == 1730882977 |
| 252 | |
| 253 | r = time.parse_rfc2616('Wed Nov 6 08:49:37 2024')! |
| 254 | assert r.unix() == 1730882977 |
| 255 | |
| 256 | r = time.parse_rfc2616('Thu, 19 Feb 2026 11:07:09 GMT')! |
| 257 | assert r.unix() == 1771499229 |
| 258 | |
| 259 | r = time.parse_rfc2616('Tuesday, 19-Feb-26 11:07:09 GMT')! |
| 260 | assert r.unix() == 1771499229 |
| 261 | |
| 262 | r = time.parse_rfc2616('Thu Feb 19 11:07:09 2026')! |
| 263 | assert r.unix() == 1771499229 |
| 264 | |
| 265 | // This should map to 1994, not 2094. |
| 266 | r = time.parse_rfc2616('Tuesday, 06-Nov-94 11:07:09 GMT')! |
| 267 | assert r.unix() == 784120029 |
| 268 | |
| 269 | // This should map to 2020, not 1920. |
| 270 | r = time.parse_rfc2616('Friday, 06-Nov-20 08:49:37 GMT')! |
| 271 | assert r.unix() == 1604652577 |
| 272 | } |
| 273 | |
| 274 | fn test_parse_http_header_string() { |
| 275 | t := time.now() |
| 276 | header := t.http_header_string() |
| 277 | back_time := time.parse_http_header_string(header)! |
| 278 | assert t.str() == back_time.str() |
| 279 | } |
| 280 | |
| 281 | fn test_ad_second_to_parse_result_in_2001() { |
| 282 | now_tm := time.parse('2001-01-01 04:00:00')! |
| 283 | future_tm := now_tm.add_seconds(60) |
| 284 | assert future_tm.str() == '2001-01-01 04:01:00' |
| 285 | assert now_tm.unix() < future_tm.unix() |
| 286 | } |
| 287 | |
| 288 | fn test_ad_second_to_parse_result_pre_2001() { |
| 289 | now_tm := time.parse('2000-01-01 04:00:00')! |
| 290 | future_tm := now_tm.add_seconds(60) |
| 291 | assert future_tm.str() == '2000-01-01 04:01:00' |
| 292 | assert now_tm.unix() < future_tm.unix() |
| 293 | } |
| 294 | |
| 295 | fn test_parse_format() { |
| 296 | mut s := '2018-01-27 12:48:34' |
| 297 | mut t := time.parse_format(s, 'YYYY-MM-DD HH:mm:ss') or { |
| 298 | assert false, '> failing format: ${s} | err: ${err}' |
| 299 | return |
| 300 | } |
| 301 | assert t.year == 2018 && t.month == 1 && t.day == 27 && t.hour == 12 && t.minute == 48 |
| 302 | && t.second == 34 |
| 303 | |
| 304 | s = '2018-November-27 12:48:20' |
| 305 | t = time.parse_format(s, 'YYYY-MMMM-DD HH:mm:ss') or { |
| 306 | assert false, '> failing format: ${s} | err: ${err}' |
| 307 | return |
| 308 | } |
| 309 | assert t.year == 2018 && t.month == 11 && t.day == 27 && t.hour == 12 && t.minute == 48 |
| 310 | && t.second == 20 |
| 311 | |
| 312 | s = '18-1-2 0:8:2' |
| 313 | t = time.parse_format(s, 'YY-M-D H:m:s') or { |
| 314 | assert false, '> failing format: ${s} | err: ${err}' |
| 315 | return |
| 316 | } |
| 317 | assert t.year == 2018 && t.month == 1 && t.day == 2 && t.hour == 0 && t.minute == 8 |
| 318 | && t.second == 2 |
| 319 | |
| 320 | // This should always fail, because we test if M and D allow for a 01 value which they shouldn't |
| 321 | s = '2018-01-02 1:8:2' |
| 322 | t = time.parse_format(s, 'YYYY-M-D H:m:s') or { return } |
| 323 | |
| 324 | assert false, '> failing for datetime: ${s}, the datetime string should not have passed the format "YYYY-M-D H:m:s"' |
| 325 | } |
| 326 | |