@@ -6,7 +6,12 @@ import { Date as Date_binding } from "./bindings/dom";
6
6
MILLIS_PER_DAY = 1000 * 60 * 60 * 24 ,
7
7
MILLIS_PER_HOUR = 1000 * 60 * 60 ,
8
8
MILLIS_PER_MINUTE = 1000 * 60 ,
9
- MILLIS_PER_SECOND = 1000 ;
9
+ MILLIS_PER_SECOND = 1000 ,
10
+
11
+ YEARS_PER_EPOCH = 400 ,
12
+ DAYS_PER_EPOCH = 146097 ,
13
+ EPOCH_OFFSET = 719468 , // Jan 1, 1970
14
+ MILLIS_LIMIT = 8640000000000000 ;
10
15
11
16
// ymdFromEpochDays returns values via globals to avoid allocations
12
17
// @ts -ignore: decorator
@@ -94,7 +99,7 @@ export class Date {
94
99
// instead throwing exception.
95
100
if ( invalidDate ( epochMillis ) ) throw new RangeError ( E_INVALIDDATE ) ;
96
101
97
- this . year = ymdFromEpochDays ( i32 ( floorDiv ( epochMillis , MILLIS_PER_DAY ) ) ) ;
102
+ this . year = dateFromEpoch ( epochMillis ) ;
98
103
this . month = _month ;
99
104
this . day = _day ;
100
105
}
@@ -107,7 +112,7 @@ export class Date {
107
112
if ( invalidDate ( time ) ) throw new RangeError ( E_INVALIDDATE ) ;
108
113
109
114
this . epochMillis = time ;
110
- this . year = ymdFromEpochDays ( i32 ( floorDiv ( time , MILLIS_PER_DAY ) ) ) ;
115
+ this . year = dateFromEpoch ( time ) ;
111
116
this . month = _month ;
112
117
this . day = _day ;
113
118
@@ -306,7 +311,7 @@ function epochMillis(
306
311
307
312
// @ts -ignore: decorator
308
313
@inline function floorDiv < T extends number > ( a : T , b : T ) : T {
309
- return ( a >= 0 ? a : a - b + 1 ) / b as T ;
314
+ return ( a - ( a < 0 ? b - 1 : 0 ) ) / b as T ;
310
315
}
311
316
312
317
// @ts -ignore: decorator
@@ -317,41 +322,42 @@ function epochMillis(
317
322
318
323
function invalidDate ( millis : i64 ) : bool {
319
324
// @ts -ignore
320
- return ( millis < - 8640000000000000 ) | ( millis > 8640000000000000 ) ;
325
+ return ( millis < - MILLIS_LIMIT ) | ( millis > MILLIS_LIMIT ) ;
321
326
}
322
327
323
- // see: http://howardhinnant.github.io/date_algorithms.html#civil_from_days
324
- function ymdFromEpochDays ( z : i32 ) : i32 {
325
- z += 719468 ;
326
- var era = < u32 > floorDiv ( z , 146097 ) ;
327
- var doe = < u32 > z - era * 146097 ; // [0, 146096]
328
- var yoe = ( doe - doe / 1460 + doe / 36524 - doe / 146096 ) / 365 ; // [0, 399]
329
- var year = yoe + era * 400 ;
330
- var doy = doe - ( 365 * yoe + yoe / 4 - yoe / 100 ) ; // [0, 365]
331
- var mo = ( 5 * doy + 2 ) / 153 ; // [0, 11]
332
- _day = doy - ( 153 * mo + 2 ) / 5 + 1 ; // [1, 31]
333
- mo += mo < 10 ? 3 : - 9 ; // [1, 12]
334
- _month = mo ;
335
- year += u32 ( mo <= 2 ) ;
328
+ // Based on "Euclidean Affine Functions and Applications to Calendar Algorithms"
329
+ // Paper: https://arxiv.org/pdf/2102.06959.pdf
330
+ function dateFromEpoch ( ms : i64 ) : i32 {
331
+ var da = ( < i32 > floorDiv ( ms , MILLIS_PER_DAY ) * 4 + EPOCH_OFFSET * 4 ) | 3 ;
332
+ var q0 = floorDiv ( da , DAYS_PER_EPOCH ) ; // [0, 146096]
333
+ var r1 = < u32 > da - q0 * DAYS_PER_EPOCH ;
334
+ var u1 = u64 ( r1 | 3 ) * 2939745 ;
335
+ var dm1 = < u32 > u1 / 11758980 ;
336
+ var n1 = 2141 * dm1 + 197913 ;
337
+ var year = 100 * q0 + i32 ( u1 >>> 32 ) ;
338
+ var mo = n1 >>> 16 ;
339
+ _day = ( n1 & 0xFFFF ) / 2141 + 1 ; // [1, 31]
340
+ if ( dm1 >= 306 ) { mo -= 12 ; ++ year ; }
341
+ _month = mo ; // [1, 12]
336
342
return year ;
337
343
}
338
344
339
345
// http://howardhinnant.github.io/date_algorithms.html#days_from_civil
340
346
function daysSinceEpoch ( y : i32 , m : i32 , d : i32 ) : i32 {
341
347
y -= i32 ( m <= 2 ) ;
342
- var era = < u32 > floorDiv ( y , 400 ) ;
343
- var yoe = < u32 > y - era * 400 ; // [0, 399]
348
+ var era = < u32 > floorDiv ( y , YEARS_PER_EPOCH ) ;
349
+ var yoe = < u32 > y - era * YEARS_PER_EPOCH ; // [0, 399]
344
350
var doy = < u32 > ( 153 * ( m + ( m > 2 ? - 3 : 9 ) ) + 2 ) / 5 + d - 1 ; // [0, 365]
345
351
var doe = yoe * 365 + yoe / 4 - yoe / 100 + doy ; // [0, 146096]
346
- return era * 146097 + doe - 719468 ;
352
+ return era * 146097 + doe - EPOCH_OFFSET ;
347
353
}
348
354
349
355
// TomohikoSakamoto algorithm from https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
350
356
function dayOfWeek ( year : i32 , month : i32 , day : i32 ) : i32 {
351
357
const tab = memory . data < u8 > ( [ 0 , 3 , 2 , 5 , 0 , 3 , 5 , 1 , 4 , 6 , 2 , 4 ] ) ;
352
358
353
359
year -= i32 ( month < 3 ) ;
354
- year += floorDiv ( year , 4 ) - floorDiv ( year , 100 ) + floorDiv ( year , 400 ) ;
360
+ year += floorDiv ( year , 4 ) - floorDiv ( year , 100 ) + floorDiv ( year , YEARS_PER_EPOCH ) ;
355
361
month = < i32 > load < u8 > ( tab + month - 1 ) ;
356
362
return euclidRem ( year + month + day , 7 ) ;
357
363
}
0 commit comments