@@ -7,6 +7,10 @@ import { CALENDAR_ID, ISO_YEAR, ISO_MONTH, ISO_DAY, CreateSlots, GetSlot, HasSlo
7
7
const ArrayIncludes = Array . prototype . includes ;
8
8
const ArrayPrototypePush = Array . prototype . push ;
9
9
const ObjectAssign = Object . assign ;
10
+ const ObjectEntries = Object . entries ;
11
+ const IntlDateTimeFormat = globalThis . Intl . DateTimeFormat ;
12
+ const MathAbs = Math . abs ;
13
+ const MathFloor = Math . floor ;
10
14
11
15
const impl = { } ;
12
16
@@ -427,19 +431,31 @@ function simpleDateDiff(one, two) {
427
431
*/
428
432
const nonIsoHelperBase = {
429
433
// The properties and methods below here should be the same for all lunar/lunisolar calendars.
434
+ getFormatter ( ) {
435
+ // `new Intl.DateTimeFormat()` is amazingly slow and chews up RAM. Per
436
+ // https://bugs.chromium.org/p/v8/issues/detail?id=6528#c4, we cache one
437
+ // DateTimeFormat instance per calendar. Caching is lazy so we only pay for
438
+ // calendars that are used. Note that the nonIsoHelperBase object is spread
439
+ // into each each calendar's implementation before any cache is created, so
440
+ // each calendar gets its own separate cached formatter.
441
+ if ( typeof this . formatter === 'undefined' ) {
442
+ this . formatter = new IntlDateTimeFormat ( `en-US-u-ca-${ this . id } ` , {
443
+ day : 'numeric' ,
444
+ month : 'numeric' ,
445
+ year : 'numeric' ,
446
+ era : this . eraLength ,
447
+ timeZone : 'UTC'
448
+ } ) ;
449
+ }
450
+ return this . formatter ;
451
+ } ,
430
452
isoToCalendarDate ( isoDate , cache ) {
431
453
let { year : isoYear , month : isoMonth , day : isoDay } = isoDate ;
432
454
const key = JSON . stringify ( { func : 'isoToCalendarDate' , isoYear, isoMonth, isoDay, id : this . id } ) ;
433
455
const cached = cache . get ( key ) ;
434
456
if ( cached ) return cached ;
435
457
436
- const dateTimeFormat = new Intl . DateTimeFormat ( `en-US-u-ca-${ this . id } ` , {
437
- day : 'numeric' ,
438
- month : 'numeric' ,
439
- year : 'numeric' ,
440
- era : this . eraLength ,
441
- timeZone : 'UTC'
442
- } ) ;
458
+ const dateTimeFormat = this . getFormatter ( ) ;
443
459
let parts , isoString ;
444
460
try {
445
461
isoString = toUtcIsoDateString ( { isoYear, isoMonth, isoDay } ) ;
@@ -763,7 +779,7 @@ const nonIsoHelperBase = {
763
779
} ,
764
780
addMonthsCalendar ( calendarDate , months , overflow , cache ) {
765
781
const { day } = calendarDate ;
766
- for ( let i = 0 , absMonths = Math . abs ( months ) ; i < absMonths ; i ++ ) {
782
+ for ( let i = 0 , absMonths = MathAbs ( months ) ; i < absMonths ; i ++ ) {
767
783
const days = months < 0 ? - this . daysInPreviousMonth ( calendarDate , cache ) : this . daysInMonth ( calendarDate , cache ) ;
768
784
const isoDate = this . calendarToIsoDate ( calendarDate , 'constrain' , cache ) ;
769
785
const addedIso = this . addDaysIso ( isoDate , days , cache ) ;
@@ -970,7 +986,7 @@ const helperHebrew = ObjectAssign({}, nonIsoHelperBase, {
970
986
minMaxMonthLength ( calendarDate , minOrMax ) {
971
987
const { month, year } = calendarDate ;
972
988
const monthCode = this . getMonthCode ( year , month ) ;
973
- const monthInfo = Object . entries ( this . months ) . find ( ( m ) => m [ 1 ] . monthCode === monthCode ) ;
989
+ const monthInfo = ObjectEntries ( this . months ) . find ( ( m ) => m [ 1 ] . monthCode === monthCode ) ;
974
990
if ( monthInfo === undefined ) throw new RangeError ( `unmatched Hebrew month: ${ month } ` ) ;
975
991
const daysInMonth = monthInfo [ 1 ] . days ;
976
992
return typeof daysInMonth === 'number' ? daysInMonth : daysInMonth [ minOrMax ] ;
@@ -1096,7 +1112,7 @@ const helperIslamic = ObjectAssign({}, nonIsoHelperBase, {
1096
1112
constantEra : 'ah' ,
1097
1113
estimateIsoDate ( calendarDate ) {
1098
1114
const { year } = this . adjustCalendarDate ( calendarDate ) ;
1099
- return { year : Math . floor ( ( year * this . DAYS_PER_ISLAMIC_YEAR ) / this . DAYS_PER_ISO_YEAR ) + 622 , month : 1 , day : 1 } ;
1115
+ return { year : MathFloor ( ( year * this . DAYS_PER_ISLAMIC_YEAR ) / this . DAYS_PER_ISO_YEAR ) + 622 , month : 1 , day : 1 } ;
1100
1116
}
1101
1117
} ) ;
1102
1118
@@ -1587,7 +1603,7 @@ const helperChinese = ObjectAssign({}, nonIsoHelperBase, {
1587
1603
calendarType : 'lunisolar' ,
1588
1604
inLeapYear ( calendarDate , cache ) {
1589
1605
const months = this . getMonthList ( calendarDate . year , cache ) ;
1590
- return Object . entries ( months ) . length === 13 ;
1606
+ return ObjectEntries ( months ) . length === 13 ;
1591
1607
} ,
1592
1608
monthsInYear ( calendarDate , cache ) {
1593
1609
return this . inLeapYear ( calendarDate , cache ) ? 13 : 12 ;
@@ -1601,14 +1617,7 @@ const helperChinese = ObjectAssign({}, nonIsoHelperBase, {
1601
1617
const key = JSON . stringify ( { func : 'getMonthList' , calendarYear, id : this . id } ) ;
1602
1618
const cached = cache . get ( key ) ;
1603
1619
if ( cached ) return cached ;
1604
- const dateTimeFormat = new Intl . DateTimeFormat ( `en-US-u-ca-${ this . id } ` , {
1605
- day : 'numeric' ,
1606
- month : 'numeric' ,
1607
- year : 'numeric' ,
1608
- era : 'short' ,
1609
- timeZone : 'UTC'
1610
- } ) ;
1611
-
1620
+ const dateTimeFormat = this . getFormatter ( ) ;
1612
1621
const getCalendarDate = ( isoYear , daysPastFeb1 ) => {
1613
1622
const isoStringFeb1 = toUtcIsoDateString ( { isoYear, isoMonth : 2 , isoDay : 1 } ) ;
1614
1623
const legacyDate = new Date ( isoStringFeb1 ) ;
@@ -1723,7 +1732,7 @@ const helperChinese = ObjectAssign({}, nonIsoHelperBase, {
1723
1732
}
1724
1733
} else if ( monthCode === undefined ) {
1725
1734
const months = this . getMonthList ( year , cache ) ;
1726
- const monthEntries = Object . entries ( months ) ;
1735
+ const monthEntries = ObjectEntries ( months ) ;
1727
1736
const largestMonth = monthEntries . length ;
1728
1737
if ( overflow === 'reject' ) {
1729
1738
ES . RejectToRange ( month , 1 , largestMonth ) ;
0 commit comments