@@ -6,7 +6,12 @@ import { Date as Date_binding } from "./bindings/dom";
66 MILLIS_PER_DAY = 1000 * 60 * 60 * 24 ,
77 MILLIS_PER_HOUR = 1000 * 60 * 60 ,
88 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 ;
1015
1116// ymdFromEpochDays returns values via globals to avoid allocations
1217// @ts -ignore: decorator
@@ -94,7 +99,7 @@ export class Date {
9499 // instead throwing exception.
95100 if ( invalidDate ( epochMillis ) ) throw new RangeError ( E_INVALIDDATE ) ;
96101
97- this . year = ymdFromEpochDays ( i32 ( floorDiv ( epochMillis , MILLIS_PER_DAY ) ) ) ;
102+ this . year = dateFromEpoch ( epochMillis ) ;
98103 this . month = _month ;
99104 this . day = _day ;
100105 }
@@ -107,7 +112,7 @@ export class Date {
107112 if ( invalidDate ( time ) ) throw new RangeError ( E_INVALIDDATE ) ;
108113
109114 this . epochMillis = time ;
110- this . year = ymdFromEpochDays ( i32 ( floorDiv ( time , MILLIS_PER_DAY ) ) ) ;
115+ this . year = dateFromEpoch ( time ) ;
111116 this . month = _month ;
112117 this . day = _day ;
113118
@@ -306,7 +311,7 @@ function epochMillis(
306311
307312// @ts -ignore: decorator
308313@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 ;
310315}
311316
312317// @ts -ignore: decorator
@@ -317,41 +322,42 @@ function epochMillis(
317322
318323function invalidDate ( millis : i64 ) : bool {
319324 // @ts -ignore
320- return ( millis < - 8640000000000000 ) | ( millis > 8640000000000000 ) ;
325+ return ( millis < - MILLIS_LIMIT ) | ( millis > MILLIS_LIMIT ) ;
321326}
322327
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]
336342 return year ;
337343}
338344
339345// http://howardhinnant.github.io/date_algorithms.html#days_from_civil
340346function daysSinceEpoch ( y : i32 , m : i32 , d : i32 ) : i32 {
341347 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]
344350 var doy = < u32 > ( 153 * ( m + ( m > 2 ? - 3 : 9 ) ) + 2 ) / 5 + d - 1 ; // [0, 365]
345351 var doe = yoe * 365 + yoe / 4 - yoe / 100 + doy ; // [0, 146096]
346- return era * 146097 + doe - 719468 ;
352+ return era * 146097 + doe - EPOCH_OFFSET ;
347353}
348354
349355// TomohikoSakamoto algorithm from https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
350356function dayOfWeek ( year : i32 , month : i32 , day : i32 ) : i32 {
351357 const tab = memory . data < u8 > ( [ 0 , 3 , 2 , 5 , 0 , 3 , 5 , 1 , 4 , 6 , 2 , 4 ] ) ;
352358
353359 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 ) ;
355361 month = < i32 > load < u8 > ( tab + month - 1 ) ;
356362 return euclidRem ( year + month + day , 7 ) ;
357363}
0 commit comments