@@ -455,9 +455,213 @@ impl['iso8601'] = {
455455 }
456456} ;
457457
458- // Note: other built -in calendars than iso8601 are not part of the Temporal
458+ // Note: Built -in calendars other than iso8601 are not part of the Temporal
459459// proposal for ECMA-262. These calendars will be standardized as part of
460- // ECMA-402.
460+ // ECMA-402. Code below here includes an implementation of these calendars order
461+ // to validate the Temporal API and to get feedback. However, non-ISO calendar
462+ // implementation is subject to change because these calendars are
463+ // implementation-defined.
464+ //
465+ // Some ES implementations don't include ECMA 402. For this reason, it's helpful
466+ // to ensure a clean separation between the ISO calendar implementation which is
467+ // a part of ECMA 262 and the non-ISO calendar implementation which requires
468+ // ECMA 402.
469+ //
470+ // To ensure this separation, the implementation is split. The `NonIsoImpl`
471+ // interface is the top-level implementation for all non-ISO calendars. This
472+ // type has the same shape as the ECMA 262-only ISO calendar implementation so
473+ // can use the same callers, tests, etc.
474+ //
475+ // A derived interface `NonIsoImplWithHelper` adds a `helper` property that
476+ // includes the remaining non-ISO implementation properties and methods beyond
477+ // the ISO implementation above. The `helper` property's shape is a base
478+ // singleton object common to all calendars (`HelperSharedImpl`) that's extended
479+ // (interface `HelperPerCalendarImpl`) with implementation that varies for each
480+ // calendar.
481+ //
482+ // Typing of individual methods in the interfaces below uses the `this`
483+ // "parameter" declaration definition, which is a fake parameter (stripped by TS
484+ // during compilation and not visible at runtime) that tells TS what type `this`
485+ // is for a method. For historical reasons, the initial implementation of
486+ // non-ISO calendars mirrored the code style of the previous ISO-only
487+ // implementation which didn't use ES6 classes. Using the `this` parameter is a
488+ // hack to delay converting this file to use ES6 classes until the code was
489+ // fully typed to make a `class` refactoring easier and safer. We'll probably do
490+ // this conversion in the future. (PRs welcome!)
491+
492+ /**
493+ * `NonIsoImpl` - The generic top-level implementation for all non-ISO
494+ * calendars. This type has the same shape as the 262-only ISO calendar
495+ * implementation, which means the `Calendar` class implementation can swap out
496+ * the ISO for non-ISO implementations without changing any `Calendar` code.
497+ */
498+ interface NonIsoImpl {
499+ dateFromFields (
500+ this : NonIsoImplWithHelper ,
501+ fieldsParam : Params [ 'dateFromFields' ] [ 0 ] ,
502+ options : NonNullable < Params [ 'dateFromFields' ] [ 1 ] > ,
503+ calendar : Temporal . Calendar
504+ ) : Temporal . PlainDate ;
505+ yearMonthFromFields (
506+ this : NonIsoImplWithHelper ,
507+ fieldsParam : Params [ 'yearMonthFromFields' ] [ 0 ] ,
508+ options : NonNullable < Params [ 'yearMonthFromFields' ] [ 1 ] > ,
509+ calendar : Temporal . Calendar
510+ ) : Temporal . PlainYearMonth ;
511+ monthDayFromFields (
512+ this : NonIsoImplWithHelper ,
513+ fieldsParam : Params [ 'monthDayFromFields' ] [ 0 ] ,
514+ options : NonNullable < Params [ 'monthDayFromFields' ] [ 1 ] > ,
515+ calendar : Temporal . Calendar
516+ ) : Temporal . PlainMonthDay ;
517+ fields ( fieldsParam : string [ ] ) : Return [ 'fields' ] ;
518+ mergeFields ( fields : Params [ 'mergeFields' ] [ 0 ] , additionalFields : Params [ 'mergeFields' ] [ 1 ] ) : Return [ 'mergeFields' ] ;
519+ dateAdd (
520+ this : NonIsoImplWithHelper ,
521+ date : Temporal . PlainDate ,
522+ years : number ,
523+ months : number ,
524+ weeks : number ,
525+ days : number ,
526+ overflow : Overflow ,
527+ calendar : Temporal . Calendar
528+ ) : Temporal . PlainDate ;
529+ dateUntil (
530+ this : NonIsoImplWithHelper ,
531+ one : Temporal . PlainDate ,
532+ two : Temporal . PlainDate ,
533+ largestUnit : Temporal . DateUnit
534+ ) : {
535+ years : number ;
536+ months : number ;
537+ weeks : number ;
538+ days : number ;
539+ } ;
540+ year ( this : NonIsoImplWithHelper , date : Temporal . PlainDate ) : number ;
541+ month ( this : NonIsoImplWithHelper , date : Temporal . PlainDate ) : number ;
542+ day ( this : NonIsoImplWithHelper , date : Temporal . PlainDate ) : number ;
543+ era ( this : NonIsoImplWithHelper , date : Temporal . PlainDate ) : string | undefined ;
544+ eraYear ( this : NonIsoImplWithHelper , date : Temporal . PlainDate ) : number | undefined ;
545+ monthCode ( this : NonIsoImplWithHelper , date : Temporal . PlainDate ) : string ;
546+ dayOfWeek ( date : Temporal . PlainDate ) : number ;
547+ dayOfYear ( this : NonIsoImplWithHelper , date : Temporal . PlainDate ) : number ;
548+ weekOfYear ( date : Temporal . PlainDate ) : number ;
549+ daysInWeek ( date : Temporal . PlainDate ) : number ;
550+ daysInMonth ( this : NonIsoImplWithHelper , date : Temporal . PlainDate | Temporal . PlainYearMonth ) : number ;
551+ daysInYear ( this : NonIsoImplWithHelper , dateParam : Temporal . PlainDate | Temporal . PlainYearMonth ) : number ;
552+ monthsInYear ( this : NonIsoImplWithHelper , date : Temporal . PlainDate | Temporal . PlainYearMonth ) : number ;
553+ inLeapYear ( this : NonIsoImplWithHelper , dateParam : Temporal . PlainDate | Temporal . PlainYearMonth ) : boolean ;
554+ }
555+
556+ /**
557+ * This type exists solely to ensure a compiler error is shown if a per-calendar
558+ * implementation object doesn't declare a `helper` property. It will go away
559+ * if we migrate to ES6 classes.
560+ *
561+ * The methods of NonIsoImpl all set their `this` to NonIsoImplWithHelper in
562+ * order to avoid having to cast every use of `helper` to exclude `undefined`.
563+ * */
564+ interface NonIsoImplWithHelper extends NonIsoImpl {
565+ helper : HelperPerCalendarImpl ;
566+ }
567+
568+ /** Shape of shared implementation code that applies to all calendars */
569+ interface HelperSharedImpl {
570+ isoToCalendarDate ( isoDate : IsoYMD , cache : OneObjectCache ) : FullCalendarDate ;
571+ validateCalendarDate ( calendarDate : Partial < FullCalendarDate > ) : void ;
572+ adjustCalendarDate (
573+ calendarDate : Partial < FullCalendarDate > ,
574+ cache ?: OneObjectCache ,
575+ overflow ?: Overflow ,
576+ fromLegacyDate ?: boolean
577+ ) : FullCalendarDate ;
578+ regulateMonthDayNaive ( calendarDate : FullCalendarDate , overflow : Overflow , cache : OneObjectCache ) : FullCalendarDate ;
579+ calendarToIsoDate ( date : CalendarDateFields , overflow : Overflow , cache : OneObjectCache ) : IsoYMD ;
580+ temporalToCalendarDate (
581+ date : Temporal . PlainDate | Temporal . PlainMonthDay | Temporal . PlainYearMonth ,
582+ cache : OneObjectCache
583+ ) : FullCalendarDate ;
584+ compareCalendarDates ( date1 : Partial < CalendarYMD > , date2 : Partial < CalendarYMD > ) : 0 | 1 | - 1 ;
585+ regulateDate ( calendarDate : CalendarYMD , overflow : Overflow , cache : OneObjectCache ) : FullCalendarDate ;
586+ addDaysIso ( isoDate : IsoYMD , days : number , cache ?: OneObjectCache ) : IsoYMD ;
587+ addDaysCalendar ( calendarDate : CalendarYMD , days : number , cache : OneObjectCache ) : FullCalendarDate ;
588+ addMonthsCalendar ( calendarDate : CalendarYMD , months : number , overflow : Overflow , cache : OneObjectCache ) : CalendarYMD ;
589+ addCalendar (
590+ calendarDate : CalendarYMD ,
591+ { years, months, weeks, days } : { years ?: number ; months ?: number ; weeks ?: number ; days ?: number } ,
592+ overflow : Overflow ,
593+ cache : OneObjectCache
594+ ) : FullCalendarDate ;
595+ untilCalendar (
596+ calendarOne : FullCalendarDate ,
597+ calendarTwo : FullCalendarDate ,
598+ largestUnit : Temporal . DateUnit ,
599+ cache : OneObjectCache
600+ ) : { years : number ; months : number ; weeks : number ; days : number } ;
601+ daysInMonth ( calendarDate : CalendarYMD , cache : OneObjectCache ) : number ;
602+ daysInPreviousMonth ( calendarDate : CalendarYMD , cache : OneObjectCache ) : number ;
603+ startOfCalendarYear ( calendarDate : CalendarYearOnly ) : CalendarYMD ;
604+ startOfCalendarMonth ( calendarDate : { year : number ; month : number } ) : CalendarYMD ;
605+ calendarDaysUntil ( calendarOne : CalendarYMD , calendarTwo : CalendarYMD , cache : OneObjectCache ) : number ;
606+ isoDaysUntil ( oneIso : IsoYMD , twoIso : IsoYMD ) : number ;
607+ eraLength : 'long' | 'short' | 'narrow' ;
608+ getFormatter ( ) : globalThis . Intl . DateTimeFormat ;
609+ formatter ?: globalThis . Intl . DateTimeFormat ;
610+ hasEra : boolean ;
611+ monthDayFromFields ( fields : Partial < FullCalendarDate > , overflow : Overflow , cache : OneObjectCache ) : IsoYMD ;
612+ }
613+
614+ /** Calendar-specific implementation */
615+ interface HelperPerCalendarImpl extends HelperSharedImpl {
616+ id : string ;
617+ reviseIntlEra ?< T extends Partial < EraAndEraYear > > ( calendarDate : T , isoDate : IsoYMD ) : T ;
618+ constantEra ?: string ;
619+ checkIcuBugs ?( isoDate : IsoYMD ) : void ;
620+ calendarType ?: string ;
621+ monthsInYear ( calendarDate : CalendarYearOnly , cache ?: OneObjectCache ) : number ;
622+ maximumMonthLength ( calendarDate ?: CalendarYM ) : number ;
623+ minimumMonthLength ( calendarDate ?: CalendarYM ) : number ;
624+ estimateIsoDate ( calendarDate : CalendarYMD ) : IsoYMD ;
625+ inLeapYear ( calendarDate : CalendarYearOnly , cache ?: OneObjectCache ) : boolean ;
626+
627+ // Fields below here are only present in some subclasses but not others.
628+ eras ?: Era [ ] ;
629+ anchorEra ?: Era ;
630+ calendarIsVulnerableToJulianBug ?: boolean ;
631+ v8IsVulnerableToJulianBug ?: boolean ;
632+ }
633+
634+ /**
635+ * This type is passed through from Calendar#dateFromFields().
636+ * `monthExtra` is additional information used internally to identify lunisolar leap months.
637+ */
638+ type CalendarDateFields = Params [ 'dateFromFields' ] [ 0 ] & { monthExtra ?: string } ;
639+
640+ /**
641+ * This is a "fully populated" calendar date record. It's only lacking
642+ * `era`/`eraYear` (which may not be present in all calendars) and `monthExtra`
643+ * which is only used in some cases.
644+ */
645+ type FullCalendarDate = {
646+ era ?: string ;
647+ eraYear ?: number ;
648+ year : number ;
649+ month : number ;
650+ monthCode : string ;
651+ day : number ;
652+ monthExtra ?: string ;
653+ } ;
654+
655+ // The types below are various subsets of calendar dates
656+ type CalendarYMD = { year : number ; month : number ; day : number } ;
657+ type CalendarYM = { year : number ; month : number } ;
658+ type CalendarYearOnly = { year : number } ;
659+ type EraAndEraYear = { era : string ; eraYear : number } ;
660+
661+ /** Record representing YMD of an ISO calendar date */
662+ type IsoYMD = { year : number ; month : number ; day : number } ;
663+
664+ type Overflow = Temporal . AssignmentOptions [ 'overflow' ] ;
461665
462666function monthCodeNumberPart ( monthCode : string ) {
463667 if ( ! monthCode . startsWith ( 'M' ) ) {
@@ -1232,6 +1436,27 @@ const nonIsoHelperBase: NonIsoHelperBase = {
12321436 }
12331437} ;
12341438
1439+ interface HebrewMonthInfo {
1440+ [ m : string ] : (
1441+ | {
1442+ leap : undefined ;
1443+ regular : number ;
1444+ }
1445+ | {
1446+ leap : number ;
1447+ regular : undefined ;
1448+ }
1449+ ) & {
1450+ monthCode : string ;
1451+ days :
1452+ | number
1453+ | {
1454+ min : number ;
1455+ max : number ;
1456+ } ;
1457+ } ;
1458+ }
1459+
12351460const helperHebrew : NonIsoHelperBase = ObjectAssign ( { } , nonIsoHelperBase , {
12361461 id : 'hebrew' ,
12371462 calendarType : 'lunisolar' ,
@@ -1428,6 +1653,20 @@ const helperPersian: NonIsoHelperBase = ObjectAssign({}, nonIsoHelperBase, {
14281653 }
14291654} as Partial < NonIsoHelperBase > ) ;
14301655
1656+ interface IndianMonthInfo {
1657+ [ month : number ] : {
1658+ length : number ;
1659+ month : number ;
1660+ day : number ;
1661+ leap ?: {
1662+ length : number ;
1663+ month : number ;
1664+ day : number ;
1665+ } ;
1666+ nextYear ?: true | undefined ;
1667+ } ;
1668+ }
1669+
14311670const helperIndian : NonIsoHelperBase = ObjectAssign ( { } , nonIsoHelperBase , {
14321671 id : 'indian' ,
14331672 calendarType : 'solar' ,
@@ -1958,6 +2197,13 @@ const helperJapanese: NonIsoHelperBase = ObjectAssign(
19582197 } as Partial < NonIsoHelperBase >
19592198) ;
19602199
2200+ interface ChineseMonthInfo {
2201+ [ key : string ] : { monthIndex : number ; daysInMonth : number } ;
2202+ }
2203+ interface ChineseDraftMonthInfo {
2204+ [ key : string ] : { monthIndex : number ; daysInMonth ?: number } ;
2205+ }
2206+
19612207const helperChinese : NonIsoHelperBase = ObjectAssign ( { } , nonIsoHelperBase , {
19622208 id : 'chinese' ,
19632209 calendarType : 'lunisolar' ,
0 commit comments