@northguild/gmt 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1133 -272
- package/dist/calendar/business/businessDaysBetween.d.ts +37 -0
- package/dist/calendar/business/businessDaysBetween.js +89 -0
- package/dist/calendar/business/index.d.ts +7 -0
- package/dist/calendar/business/index.js +10 -0
- package/dist/calendar/business/mergeCalendars.d.ts +33 -0
- package/dist/calendar/business/mergeCalendars.js +68 -0
- package/dist/calendar/business/nextBusinessDay.d.ts +30 -0
- package/dist/calendar/business/nextBusinessDay.js +39 -0
- package/dist/calendar/business/previousBusinessDay.d.ts +30 -0
- package/dist/calendar/business/previousBusinessDay.js +39 -0
- package/dist/calendar/business/rollDate.d.ts +59 -0
- package/dist/calendar/business/rollDate.js +116 -0
- package/dist/calendar/calculate/bucketRange.d.ts +49 -0
- package/dist/calendar/calculate/bucketRange.js +169 -0
- package/dist/calendar/calculate/floorToZone.d.ts +42 -0
- package/dist/calendar/calculate/floorToZone.js +62 -0
- package/dist/calendar/calculate/getFiscalPeriod.d.ts +64 -0
- package/dist/calendar/calculate/getFiscalPeriod.js +96 -0
- package/dist/calendar/calculate/getIsoWeekDate.d.ts +35 -0
- package/dist/calendar/calculate/getIsoWeekDate.js +41 -0
- package/dist/calendar/calculate/getOrdinalDate.d.ts +25 -0
- package/dist/calendar/calculate/getOrdinalDate.js +28 -0
- package/dist/calendar/calculate/getQuarter.d.ts +36 -0
- package/dist/calendar/calculate/getQuarter.js +64 -0
- package/dist/calendar/calculate/index.d.ts +6 -0
- package/dist/calendar/calculate/index.js +6 -0
- package/dist/calendar/index.d.ts +4 -0
- package/dist/calendar/index.js +5 -0
- package/dist/calendar/validate/index.d.ts +4 -0
- package/dist/calendar/validate/index.js +4 -0
- package/dist/calendar/validate/isValidBusinessCalendar.d.ts +31 -0
- package/dist/calendar/validate/isValidBusinessCalendar.js +40 -0
- package/dist/calendar/validate/isValidFiscalPattern.d.ts +25 -0
- package/dist/calendar/validate/isValidFiscalPattern.js +29 -0
- package/dist/calendar/validate/isValidRollConvention.d.ts +28 -0
- package/dist/calendar/validate/isValidRollConvention.js +38 -0
- package/dist/calendar/validate/isValidZoneBucketUnit.d.ts +31 -0
- package/dist/calendar/validate/isValidZoneBucketUnit.js +39 -0
- package/dist/duration/calculate/durationAs.d.ts +27 -3
- package/dist/duration/calculate/durationAs.js +34 -8
- package/dist/duration/calculate/getDurationUnit.d.ts +5 -2
- package/dist/duration/calculate/getDurationUnit.js +7 -3
- package/dist/duration/compare/compareDurations.d.ts +25 -2
- package/dist/duration/compare/compareDurations.js +31 -7
- package/dist/duration/format/formatDuration.d.ts +14 -5
- package/dist/duration/format/formatDuration.js +47 -11
- package/dist/duration/index.d.ts +1 -0
- package/dist/duration/index.js +2 -0
- package/dist/duration/normalize/normalizeDuration.d.ts +33 -6
- package/dist/duration/normalize/normalizeDuration.js +41 -12
- package/dist/duration/parse/parseDuration.js +4 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/instant/convert/classifyLocal.d.ts +50 -0
- package/dist/instant/convert/classifyLocal.js +60 -0
- package/dist/instant/convert/fromOffsetInstant.d.ts +47 -0
- package/dist/instant/convert/fromOffsetInstant.js +91 -0
- package/dist/instant/convert/index.d.ts +4 -0
- package/dist/instant/convert/index.js +4 -0
- package/dist/instant/convert/resolveLocal.d.ts +40 -0
- package/dist/instant/convert/resolveLocal.js +76 -0
- package/dist/instant/convert/toOffsetInstant.d.ts +96 -0
- package/dist/instant/convert/toOffsetInstant.js +162 -0
- package/dist/instant/index.d.ts +2 -0
- package/dist/instant/index.js +3 -0
- package/dist/intermodal/calculate/billingTimeline.d.ts +94 -0
- package/dist/intermodal/calculate/billingTimeline.js +168 -0
- package/dist/intermodal/calculate/chargeableDays.d.ts +98 -0
- package/dist/intermodal/calculate/chargeableDays.js +101 -0
- package/dist/intermodal/calculate/demurrageClock.d.ts +93 -0
- package/dist/intermodal/calculate/demurrageClock.js +140 -0
- package/dist/intermodal/calculate/freeTimeExpiry.d.ts +66 -0
- package/dist/intermodal/calculate/freeTimeExpiry.js +82 -0
- package/dist/intermodal/calculate/index.d.ts +4 -0
- package/dist/intermodal/calculate/index.js +4 -0
- package/dist/intermodal/index.d.ts +2 -0
- package/dist/intermodal/index.js +3 -0
- package/dist/internal/adjustZonedBusinessDays.js +9 -7
- package/dist/internal/businessCalendar.d.ts +46 -0
- package/dist/internal/businessCalendar.js +230 -0
- package/dist/internal/businessDayValues.d.ts +20 -0
- package/dist/internal/businessDayValues.js +54 -0
- package/dist/internal/calendarDatePairPolicy.d.ts +10 -12
- package/dist/internal/calendarDatePairPolicy.js +13 -19
- package/dist/internal/calendarDateString.d.ts +32 -58
- package/dist/internal/calendarDateString.js +51 -93
- package/dist/internal/calendarSystemIds.d.ts +26 -2
- package/dist/internal/calendarSystemIds.js +57 -51
- package/dist/internal/calendarValueOfDate.d.ts +10 -12
- package/dist/internal/calendarValueOfDate.js +22 -20
- package/dist/internal/calendarValueOfZoned.d.ts +14 -17
- package/dist/internal/calendarValueOfZoned.js +25 -25
- package/dist/internal/calendarZonedPairPolicy.d.ts +11 -18
- package/dist/internal/calendarZonedPairPolicy.js +14 -25
- package/dist/internal/calendarZonedString.d.ts +41 -65
- package/dist/internal/calendarZonedString.js +52 -101
- package/dist/internal/compareUnixEpochs.d.ts +14 -0
- package/dist/internal/compareUnixEpochs.js +25 -0
- package/dist/internal/dateTimeUnitHelpers.d.ts +18 -0
- package/dist/internal/dateTimeUnitHelpers.js +27 -0
- package/dist/internal/dateUnitHelpers.d.ts +39 -1
- package/dist/internal/dateUnitHelpers.js +73 -11
- package/dist/internal/defaultFractionalDigits.d.ts +14 -0
- package/dist/internal/defaultFractionalDigits.js +23 -0
- package/dist/internal/differenceRecord.d.ts +32 -0
- package/dist/internal/differenceRecord.js +79 -0
- package/dist/internal/divisionBoundary.d.ts +19 -0
- package/dist/internal/divisionBoundary.js +23 -0
- package/dist/internal/durationUntilString.d.ts +1 -1
- package/dist/internal/durationUntilString.js +10 -2
- package/dist/internal/englishCalendarNames.d.ts +11 -0
- package/dist/internal/englishCalendarNames.js +21 -1
- package/dist/internal/epochNanoseconds.d.ts +27 -0
- package/dist/internal/epochNanoseconds.js +31 -0
- package/dist/internal/fiscalCalendar.d.ts +36 -0
- package/dist/internal/fiscalCalendar.js +86 -0
- package/dist/internal/floorDivide.d.ts +15 -0
- package/dist/internal/floorDivide.js +18 -0
- package/dist/internal/foreignEpochs.d.ts +134 -0
- package/dist/internal/foreignEpochs.js +139 -0
- package/dist/internal/formatCalendarDays.d.ts +28 -0
- package/dist/internal/formatCalendarDays.js +49 -0
- package/dist/internal/formatDateInCalendar.d.ts +14 -39
- package/dist/internal/formatDateInCalendar.js +16 -47
- package/dist/internal/formatRelativeDuration.d.ts +27 -0
- package/dist/internal/formatRelativeDuration.js +45 -0
- package/dist/internal/freeTimeLedger.d.ts +93 -0
- package/dist/internal/freeTimeLedger.js +262 -0
- package/dist/internal/getLocaleFirstDayOfWeek.d.ts +9 -6
- package/dist/internal/getLocaleFirstDayOfWeek.js +9 -10
- package/dist/internal/getLocaleWeekYearBounds.d.ts +17 -9
- package/dist/internal/getLocaleWeekYearBounds.js +45 -26
- package/dist/internal/getLocaleWeekendDays.d.ts +9 -18
- package/dist/internal/getLocaleWeekendDays.js +9 -5
- package/dist/internal/halfOpenIntervals.d.ts +102 -0
- package/dist/internal/halfOpenIntervals.js +180 -0
- package/dist/internal/hasCalendarAnnotation.d.ts +20 -20
- package/dist/internal/hasCalendarAnnotation.js +23 -21
- package/dist/internal/hourDurationString.d.ts +16 -0
- package/dist/internal/hourDurationString.js +47 -0
- package/dist/internal/httpDateFields.d.ts +24 -0
- package/dist/internal/httpDateFields.js +86 -0
- package/dist/internal/index.d.ts +30 -9
- package/dist/internal/index.js +30 -9
- package/dist/internal/instantFormatOptions.d.ts +43 -0
- package/dist/internal/instantFormatOptions.js +99 -0
- package/dist/internal/instantIntervalText.d.ts +20 -0
- package/dist/internal/instantIntervalText.js +25 -0
- package/dist/internal/instantNanoseconds.d.ts +25 -0
- package/dist/internal/instantNanoseconds.js +41 -0
- package/dist/internal/intervalCountHelpers.d.ts +16 -10
- package/dist/internal/intervalCountHelpers.js +1 -31
- package/dist/internal/intervalNanoseconds.d.ts +47 -0
- package/dist/internal/intervalNanoseconds.js +86 -0
- package/dist/internal/isObject.d.ts +26 -0
- package/dist/internal/isObject.js +30 -0
- package/dist/internal/isoStringBody.d.ts +66 -0
- package/dist/internal/isoStringBody.js +105 -0
- package/dist/internal/joinDateTimeConnector.d.ts +1 -1
- package/dist/internal/localeWeekInfo.d.ts +34 -0
- package/dist/internal/localeWeekInfo.js +27 -0
- package/dist/internal/maxPieces.d.ts +41 -0
- package/dist/internal/maxPieces.js +53 -0
- package/dist/internal/measureNearRangeEnd.d.ts +26 -0
- package/dist/internal/measureNearRangeEnd.js +47 -0
- package/dist/internal/monthGridWeekRow.d.ts +4 -1
- package/dist/internal/monthGridWeekRow.js +3 -0
- package/dist/internal/normalizeTimeZone.d.ts +17 -10
- package/dist/internal/normalizeTimeZone.js +25 -15
- package/dist/internal/patternToken.d.ts +2 -1
- package/dist/internal/patternToken.js +10 -4
- package/dist/internal/plainDateAdd.d.ts +20 -0
- package/dist/internal/plainDateAdd.js +42 -0
- package/dist/internal/plainDateUntil.d.ts +21 -0
- package/dist/internal/plainDateUntil.js +48 -0
- package/dist/internal/plainFormatOptions.d.ts +59 -0
- package/dist/internal/plainFormatOptions.js +165 -0
- package/dist/internal/resolveDateTimeUnit.d.ts +4 -1
- package/dist/internal/resolveDateTimeUnit.js +2 -12
- package/dist/internal/resolveDurationRelativeTo.d.ts +28 -22
- package/dist/internal/resolveDurationRelativeTo.js +69 -25
- package/dist/internal/resolveDurationUnit.d.ts +4 -1
- package/dist/internal/resolveDurationUnit.js +2 -11
- package/dist/internal/resolveLocale.d.ts +37 -0
- package/dist/internal/resolveLocale.js +59 -0
- package/dist/internal/resolveManualRoundingOptions.d.ts +23 -0
- package/dist/internal/resolveManualRoundingOptions.js +39 -0
- package/dist/internal/resolveMinimalDaysInFirstWeek.d.ts +19 -0
- package/dist/internal/resolveMinimalDaysInFirstWeek.js +28 -0
- package/dist/internal/resolveOverflow.d.ts +6 -0
- package/dist/internal/resolveOverflow.js +7 -1
- package/dist/internal/resolveReadingTimeZone.d.ts +11 -0
- package/dist/internal/resolveReadingTimeZone.js +18 -0
- package/dist/internal/resolveUnixIntervalPair.d.ts +30 -0
- package/dist/internal/resolveUnixIntervalPair.js +53 -0
- package/dist/internal/resolveWeekStartsOn.d.ts +12 -0
- package/dist/internal/resolveWeekStartsOn.js +18 -0
- package/dist/internal/rfc5322DateTime.d.ts +26 -0
- package/dist/internal/rfc5322DateTime.js +176 -0
- package/dist/internal/splitStep.d.ts +81 -0
- package/dist/internal/splitStep.js +182 -0
- package/dist/internal/startOrEndOfUnix.d.ts +26 -0
- package/dist/internal/startOrEndOfUnix.js +47 -0
- package/dist/{utc/calculate → internal}/startOrEndOfUtc.d.ts +8 -4
- package/dist/{utc/calculate → internal}/startOrEndOfUtc.js +30 -26
- package/dist/internal/startOrEndOfZoned.d.ts +22 -0
- package/dist/internal/startOrEndOfZoned.js +49 -0
- package/dist/internal/temporalCompat/calendarDateArithmetic.d.ts +33 -0
- package/dist/internal/temporalCompat/calendarDateArithmetic.js +199 -0
- package/dist/internal/temporalCompat/calendarDateFromFields.d.ts +36 -0
- package/dist/internal/temporalCompat/calendarDateFromFields.js +120 -0
- package/dist/internal/temporalCompat/calendarFields.d.ts +25 -0
- package/dist/internal/temporalCompat/calendarFields.js +131 -0
- package/dist/internal/temporalCompat/capabilities.d.ts +28 -0
- package/dist/internal/temporalCompat/capabilities.js +74 -0
- package/dist/internal/temporalCompat/fieldSearch.d.ts +21 -0
- package/dist/internal/temporalCompat/fieldSearch.js +83 -0
- package/dist/internal/temporalCompat/fixedDay.d.ts +25 -0
- package/dist/internal/temporalCompat/fixedDay.js +45 -0
- package/dist/internal/temporalCompat/hebrewArithmetic.d.ts +19 -0
- package/dist/internal/temporalCompat/hebrewArithmetic.js +150 -0
- package/dist/internal/temporalCompat/index.d.ts +4 -0
- package/dist/internal/temporalCompat/index.js +5 -0
- package/dist/internal/temporalCompat/indianArithmetic.d.ts +16 -0
- package/dist/internal/temporalCompat/indianArithmetic.js +87 -0
- package/dist/internal/temporalCompat/largeMonthSpan.d.ts +19 -0
- package/dist/internal/temporalCompat/largeMonthSpan.js +199 -0
- package/dist/internal/temporalCompat/nonIsoArithmetic.d.ts +59 -0
- package/dist/internal/temporalCompat/nonIsoArithmetic.js +220 -0
- package/dist/internal/temporalCompat/readArithmeticModel.d.ts +3 -0
- package/dist/internal/temporalCompat/readArithmeticModel.js +284 -0
- package/dist/internal/temporalCompat/repros.d.ts +31 -0
- package/dist/internal/temporalCompat/repros.js +556 -0
- package/dist/internal/toInstantFromUtc.d.ts +10 -0
- package/dist/internal/toInstantFromUtc.js +19 -0
- package/dist/internal/unixEpochValue.d.ts +166 -0
- package/dist/internal/unixEpochValue.js +245 -0
- package/dist/internal/unixFormatReference.d.ts +18 -0
- package/dist/internal/unixFormatReference.js +44 -0
- package/dist/internal/unixZonedDateTime.d.ts +22 -0
- package/dist/internal/unixZonedDateTime.js +32 -0
- package/dist/internal/utcOffsetString.d.ts +39 -0
- package/dist/internal/utcOffsetString.js +75 -0
- package/dist/internal/utcZonedDateTime.d.ts +16 -0
- package/dist/internal/utcZonedDateTime.js +28 -0
- package/dist/internal/wallClockAtOffset.d.ts +22 -0
- package/dist/internal/wallClockAtOffset.js +26 -0
- package/dist/internal/zonedBucket.d.ts +91 -0
- package/dist/internal/zonedBucket.js +441 -0
- package/dist/internal/zonedFormatReference.d.ts +15 -0
- package/dist/internal/zonedFormatReference.js +24 -0
- package/dist/internal/zonedLocalDates.d.ts +15 -0
- package/dist/internal/zonedLocalDates.js +58 -0
- package/dist/internal/zonedNowUnitValue.d.ts +24 -0
- package/dist/internal/zonedNowUnitValue.js +58 -0
- package/dist/internal/zonedWallClock.d.ts +84 -0
- package/dist/internal/zonedWallClock.js +427 -0
- package/dist/internal/zonedWallClockDifference.d.ts +92 -0
- package/dist/internal/zonedWallClockDifference.js +1174 -0
- package/dist/internal/zonedWallClockOperations.d.ts +132 -0
- package/dist/internal/zonedWallClockOperations.js +553 -0
- package/dist/internal/zonelessCalendarDate.d.ts +12 -0
- package/dist/internal/zonelessCalendarDate.js +22 -0
- package/dist/interval/calculate/clampInterval.d.ts +22 -0
- package/dist/interval/calculate/clampInterval.js +31 -0
- package/dist/interval/calculate/index.d.ts +6 -0
- package/dist/interval/calculate/index.js +6 -0
- package/dist/interval/calculate/intersectIntervals.d.ts +23 -0
- package/dist/interval/calculate/intersectIntervals.js +43 -0
- package/dist/interval/calculate/mergeIntervals.d.ts +28 -0
- package/dist/interval/calculate/mergeIntervals.js +44 -0
- package/dist/interval/calculate/splitIntervalAt.d.ts +28 -0
- package/dist/interval/calculate/splitIntervalAt.js +63 -0
- package/dist/interval/calculate/subtractIntervals.d.ts +31 -0
- package/dist/interval/calculate/subtractIntervals.js +64 -0
- package/dist/interval/calculate/sumIntervals.d.ts +26 -0
- package/dist/interval/calculate/sumIntervals.js +40 -0
- package/dist/interval/compare/index.d.ts +2 -0
- package/dist/interval/compare/index.js +2 -0
- package/dist/interval/compare/intervalContains.d.ts +25 -0
- package/dist/interval/compare/intervalContains.js +39 -0
- package/dist/interval/compare/intervalsOverlap.d.ts +28 -0
- package/dist/interval/compare/intervalsOverlap.js +42 -0
- package/dist/interval/index.d.ts +4 -0
- package/dist/interval/index.js +5 -0
- package/dist/interval/validate/index.d.ts +1 -0
- package/dist/interval/validate/index.js +1 -0
- package/dist/interval/validate/isValidInterval.d.ts +29 -0
- package/dist/interval/validate/isValidInterval.js +38 -0
- package/dist/plain/calculate/addBusinessDays.d.ts +26 -9
- package/dist/plain/calculate/addBusinessDays.js +29 -21
- package/dist/plain/calculate/addDate.d.ts +9 -8
- package/dist/plain/calculate/addDate.js +35 -22
- package/dist/plain/calculate/addDateTime.js +24 -10
- package/dist/plain/calculate/addTime.d.ts +5 -8
- package/dist/plain/calculate/addTime.js +23 -16
- package/dist/plain/calculate/clampDate.js +3 -3
- package/dist/plain/calculate/closestDateTo.d.ts +11 -7
- package/dist/plain/calculate/closestDateTo.js +36 -24
- package/dist/plain/calculate/cycleDate.d.ts +1 -1
- package/dist/plain/calculate/cycleDate.js +10 -3
- package/dist/plain/calculate/cycleDateTime.d.ts +1 -1
- package/dist/plain/calculate/cycleDateTime.js +10 -3
- package/dist/plain/calculate/cycleTime.d.ts +6 -8
- package/dist/plain/calculate/cycleTime.js +15 -9
- package/dist/plain/calculate/diffDate.d.ts +31 -14
- package/dist/plain/calculate/diffDate.js +73 -38
- package/dist/plain/calculate/diffDateAsDuration.d.ts +13 -8
- package/dist/plain/calculate/diffDateAsDuration.js +22 -9
- package/dist/plain/calculate/diffDateTime.d.ts +17 -5
- package/dist/plain/calculate/diffDateTime.js +67 -28
- package/dist/plain/calculate/diffDateTimeAsDuration.d.ts +2 -2
- package/dist/plain/calculate/diffDateTimeAsDuration.js +10 -3
- package/dist/plain/calculate/diffTime.d.ts +18 -5
- package/dist/plain/calculate/diffTime.js +68 -27
- package/dist/plain/calculate/endOfDate.d.ts +10 -2
- package/dist/plain/calculate/endOfDate.js +52 -27
- package/dist/plain/calculate/endOfDateTime.d.ts +17 -2
- package/dist/plain/calculate/endOfDateTime.js +126 -103
- package/dist/plain/calculate/endOfTime.d.ts +16 -2
- package/dist/plain/calculate/endOfTime.js +77 -59
- package/dist/plain/calculate/getLargestDateDurationUnit.d.ts +13 -5
- package/dist/plain/calculate/getLargestDateDurationUnit.js +26 -10
- package/dist/plain/calculate/getLargestDateTimeDurationUnit.d.ts +13 -6
- package/dist/plain/calculate/getLargestDateTimeDurationUnit.js +37 -22
- package/dist/plain/calculate/getLargestTimeDurationUnit.d.ts +13 -6
- package/dist/plain/calculate/getLargestTimeDurationUnit.js +33 -18
- package/dist/plain/calculate/getLocaleDayOfWeek.d.ts +8 -4
- package/dist/plain/calculate/getLocaleDayOfWeek.js +8 -3
- package/dist/plain/calculate/getLocaleEndOfWeek.d.ts +8 -4
- package/dist/plain/calculate/getLocaleEndOfWeek.js +8 -3
- package/dist/plain/calculate/getLocaleStartOfWeek.d.ts +8 -4
- package/dist/plain/calculate/getLocaleStartOfWeek.js +8 -3
- package/dist/plain/calculate/getLocaleWeekYear.d.ts +29 -19
- package/dist/plain/calculate/getLocaleWeekYear.js +48 -28
- package/dist/plain/calculate/getWeekNumber.d.ts +21 -2
- package/dist/plain/calculate/getWeekNumber.js +33 -12
- package/dist/plain/calculate/getWeekOfMonth.d.ts +4 -2
- package/dist/plain/calculate/getWeekOfMonth.js +8 -3
- package/dist/plain/calculate/getWeeksInLocaleWeekYear.d.ts +25 -14
- package/dist/plain/calculate/getWeeksInLocaleWeekYear.js +45 -25
- package/dist/plain/calculate/getWeeksInMonth.d.ts +4 -2
- package/dist/plain/calculate/getWeeksInMonth.js +8 -3
- package/dist/plain/calculate/getWeeksInYear.d.ts +5 -4
- package/dist/plain/calculate/getWeeksInYear.js +9 -14
- package/dist/plain/calculate/maxDate.js +19 -12
- package/dist/plain/calculate/maxDateTime.js +19 -12
- package/dist/plain/calculate/maxTime.js +19 -12
- package/dist/plain/calculate/minDate.js +19 -12
- package/dist/plain/calculate/minDateTime.js +19 -12
- package/dist/plain/calculate/minTime.js +19 -12
- package/dist/plain/calculate/nextWeekday.js +5 -0
- package/dist/plain/calculate/previousWeekday.js +5 -0
- package/dist/plain/calculate/roundDate.d.ts +23 -5
- package/dist/plain/calculate/roundDate.js +88 -46
- package/dist/plain/calculate/roundDateTime.d.ts +27 -3
- package/dist/plain/calculate/roundDateTime.js +128 -69
- package/dist/plain/calculate/roundTime.d.ts +8 -1
- package/dist/plain/calculate/roundTime.js +32 -17
- package/dist/plain/calculate/setDate.js +4 -0
- package/dist/plain/calculate/setDateTime.js +4 -0
- package/dist/plain/calculate/setTime.d.ts +1 -1
- package/dist/plain/calculate/setTime.js +5 -1
- package/dist/plain/calculate/sortDateTimes.js +17 -10
- package/dist/plain/calculate/sortDates.js +17 -10
- package/dist/plain/calculate/sortTimes.js +17 -10
- package/dist/plain/calculate/startOfDate.d.ts +7 -2
- package/dist/plain/calculate/startOfDate.js +52 -27
- package/dist/plain/calculate/startOfDateTime.d.ts +5 -2
- package/dist/plain/calculate/startOfDateTime.js +81 -62
- package/dist/plain/calculate/startOfQuarterForDateTime.d.ts +2 -0
- package/dist/plain/calculate/startOfQuarterForDateTime.js +12 -1
- package/dist/plain/calculate/startOfTime.d.ts +15 -2
- package/dist/plain/calculate/startOfTime.js +33 -37
- package/dist/plain/calculate/subtractBusinessDays.d.ts +25 -10
- package/dist/plain/calculate/subtractBusinessDays.js +28 -22
- package/dist/plain/calculate/subtractDate.d.ts +8 -7
- package/dist/plain/calculate/subtractDate.js +35 -21
- package/dist/plain/calculate/subtractDateTime.js +24 -10
- package/dist/plain/calculate/subtractTime.d.ts +5 -8
- package/dist/plain/calculate/subtractTime.js +23 -16
- package/dist/plain/chop/index.d.ts +0 -1
- package/dist/plain/chop/index.js +0 -1
- package/dist/plain/compare/areDateTimesEqualBy.d.ts +10 -5
- package/dist/plain/compare/areDateTimesEqualBy.js +35 -10
- package/dist/plain/compare/areDatesEqualBy.d.ts +10 -3
- package/dist/plain/compare/areDatesEqualBy.js +41 -14
- package/dist/plain/compare/isBetweenDate.js +32 -16
- package/dist/plain/compare/isBetweenDateTime.js +34 -18
- package/dist/plain/compare/isBetweenTime.js +32 -16
- package/dist/plain/compare/isBusinessDay.d.ts +25 -11
- package/dist/plain/compare/isBusinessDay.js +36 -15
- package/dist/plain/compare/isThisUnit.d.ts +13 -4
- package/dist/plain/compare/isThisUnit.js +33 -13
- package/dist/plain/compare/isWeekend.d.ts +8 -4
- package/dist/plain/compare/isWeekend.js +7 -3
- package/dist/plain/convert/convertDateToCalendar.d.ts +34 -33
- package/dist/plain/convert/convertDateToCalendar.js +41 -40
- package/dist/plain/format/formatCalendar.d.ts +12 -6
- package/dist/plain/format/formatCalendar.js +47 -25
- package/dist/plain/format/formatDate.d.ts +21 -4
- package/dist/plain/format/formatDate.js +37 -5
- package/dist/plain/format/formatDateRange.d.ts +17 -4
- package/dist/plain/format/formatDateRange.js +29 -5
- package/dist/plain/format/formatDateTime.d.ts +22 -4
- package/dist/plain/format/formatDateTime.js +31 -5
- package/dist/plain/format/formatDateTimeRange.d.ts +20 -4
- package/dist/plain/format/formatDateTimeRange.js +29 -5
- package/dist/plain/format/formatDateTimeToParts.d.ts +32 -6
- package/dist/plain/format/formatDateTimeToParts.js +40 -14
- package/dist/plain/format/formatDateToParts.d.ts +24 -3
- package/dist/plain/format/formatDateToParts.js +36 -11
- package/dist/plain/format/formatRelativeDate.d.ts +7 -3
- package/dist/plain/format/formatRelativeDate.js +39 -24
- package/dist/plain/format/formatRelativeDateTime.d.ts +15 -6
- package/dist/plain/format/formatRelativeDateTime.js +35 -33
- package/dist/plain/format/formatRelativeTime.d.ts +8 -4
- package/dist/plain/format/formatRelativeTime.js +36 -21
- package/dist/plain/format/formatSql.d.ts +15 -10
- package/dist/plain/format/formatSql.js +20 -11
- package/dist/plain/format/formatTime.d.ts +18 -4
- package/dist/plain/format/formatTime.js +32 -5
- package/dist/plain/get/getNowUnit.d.ts +3 -0
- package/dist/plain/get/getNowUnit.js +14 -29
- package/dist/plain/index.js +1 -1
- package/dist/plain/interval/intervalAbutsDate.d.ts +12 -7
- package/dist/plain/interval/intervalAbutsDate.js +15 -19
- package/dist/plain/interval/intervalAbutsDateTime.d.ts +9 -7
- package/dist/plain/interval/intervalAbutsDateTime.js +17 -23
- package/dist/plain/interval/intervalAbutsTime.d.ts +11 -5
- package/dist/plain/interval/intervalAbutsTime.js +19 -21
- package/dist/plain/interval/intervalContainsDate.d.ts +17 -10
- package/dist/plain/interval/intervalContainsDate.js +20 -15
- package/dist/plain/interval/intervalContainsDateTime.d.ts +16 -11
- package/dist/plain/interval/intervalContainsDateTime.js +24 -20
- package/dist/plain/interval/intervalContainsTime.d.ts +20 -10
- package/dist/plain/interval/intervalContainsTime.js +28 -19
- package/dist/plain/interval/intervalCountDate.d.ts +13 -8
- package/dist/plain/interval/intervalCountDate.js +46 -14
- package/dist/plain/interval/intervalCountDateTime.d.ts +6 -4
- package/dist/plain/interval/intervalCountDateTime.js +63 -18
- package/dist/plain/interval/intervalCountTime.d.ts +3 -4
- package/dist/plain/interval/intervalCountTime.js +9 -9
- package/dist/plain/interval/intervalDifferenceDate.d.ts +13 -6
- package/dist/plain/interval/intervalDifferenceDate.js +19 -32
- package/dist/plain/interval/intervalDifferenceDateTime.d.ts +10 -4
- package/dist/plain/interval/intervalDifferenceDateTime.js +21 -27
- package/dist/plain/interval/intervalDifferenceTime.d.ts +11 -4
- package/dist/plain/interval/intervalDifferenceTime.js +22 -27
- package/dist/plain/interval/intervalDivideEquallyDate.d.ts +16 -4
- package/dist/plain/interval/intervalDivideEquallyDate.js +57 -33
- package/dist/plain/interval/intervalDivideEquallyDateTime.d.ts +16 -4
- package/dist/plain/interval/intervalDivideEquallyDateTime.js +67 -31
- package/dist/plain/interval/intervalDivideEquallyTime.d.ts +16 -4
- package/dist/plain/interval/intervalDivideEquallyTime.js +58 -31
- package/dist/plain/interval/intervalEngulfsDate.d.ts +16 -9
- package/dist/plain/interval/intervalEngulfsDate.js +19 -12
- package/dist/plain/interval/intervalEngulfsDateTime.d.ts +16 -11
- package/dist/plain/interval/intervalEngulfsDateTime.js +24 -18
- package/dist/plain/interval/intervalEngulfsTime.d.ts +18 -8
- package/dist/plain/interval/intervalEngulfsTime.js +26 -15
- package/dist/plain/interval/intervalFromDurationDate.d.ts +5 -4
- package/dist/plain/interval/intervalFromDurationDate.js +11 -8
- package/dist/plain/interval/intervalFromDurationDateTime.js +4 -0
- package/dist/plain/interval/intervalFromDurationTime.d.ts +10 -12
- package/dist/plain/interval/intervalFromDurationTime.js +28 -17
- package/dist/plain/interval/intervalIntersectionDate.d.ts +9 -4
- package/dist/plain/interval/intervalIntersectionDate.js +14 -9
- package/dist/plain/interval/intervalIntersectionDateTime.d.ts +9 -4
- package/dist/plain/interval/intervalIntersectionDateTime.js +19 -13
- package/dist/plain/interval/intervalIntersectionTime.d.ts +7 -3
- package/dist/plain/interval/intervalIntersectionTime.js +17 -12
- package/dist/plain/interval/intervalLengthDate.d.ts +9 -6
- package/dist/plain/interval/intervalLengthDate.js +40 -8
- package/dist/plain/interval/intervalLengthDateTime.js +2 -2
- package/dist/plain/interval/intervalOverlappingDaysDate.d.ts +16 -17
- package/dist/plain/interval/intervalOverlappingDaysDate.js +30 -30
- package/dist/plain/interval/intervalOverlappingDaysDateTime.d.ts +7 -12
- package/dist/plain/interval/intervalOverlappingDaysDateTime.js +24 -24
- package/dist/plain/interval/intervalSplitAtDate.d.ts +10 -5
- package/dist/plain/interval/intervalSplitAtDate.js +48 -36
- package/dist/plain/interval/intervalSplitAtDateTime.d.ts +9 -4
- package/dist/plain/interval/intervalSplitAtDateTime.js +44 -32
- package/dist/plain/interval/intervalSplitAtTime.d.ts +9 -4
- package/dist/plain/interval/intervalSplitAtTime.js +44 -32
- package/dist/plain/interval/intervalUnionDate.d.ts +14 -6
- package/dist/plain/interval/intervalUnionDate.js +19 -11
- package/dist/plain/interval/intervalUnionDateTime.d.ts +12 -5
- package/dist/plain/interval/intervalUnionDateTime.js +22 -14
- package/dist/plain/interval/intervalUnionTime.d.ts +10 -3
- package/dist/plain/interval/intervalUnionTime.js +20 -12
- package/dist/plain/interval/intervalXorAllDate.d.ts +11 -10
- package/dist/plain/interval/intervalXorAllDate.js +37 -59
- package/dist/plain/interval/intervalXorAllDateTime.d.ts +7 -5
- package/dist/plain/interval/intervalXorAllDateTime.js +28 -48
- package/dist/plain/interval/intervalXorAllTime.d.ts +8 -4
- package/dist/plain/interval/intervalXorAllTime.js +29 -47
- package/dist/plain/interval/intervalXorDate.d.ts +10 -9
- package/dist/plain/interval/intervalXorDate.js +19 -54
- package/dist/plain/interval/intervalXorDateTime.d.ts +9 -7
- package/dist/plain/interval/intervalXorDateTime.js +23 -50
- package/dist/plain/interval/intervalXorTime.d.ts +8 -7
- package/dist/plain/interval/intervalXorTime.js +22 -50
- package/dist/plain/interval/intervalsOverlapDate.d.ts +14 -7
- package/dist/plain/interval/intervalsOverlapDate.js +17 -10
- package/dist/plain/interval/intervalsOverlapDateTime.d.ts +12 -7
- package/dist/plain/interval/intervalsOverlapDateTime.js +20 -14
- package/dist/plain/interval/intervalsOverlapTime.d.ts +10 -5
- package/dist/plain/interval/intervalsOverlapTime.js +18 -12
- package/dist/plain/interval/mergeIntervalsDate.d.ts +9 -5
- package/dist/plain/interval/mergeIntervalsDate.js +37 -38
- package/dist/plain/interval/mergeIntervalsDateTime.d.ts +7 -3
- package/dist/plain/interval/mergeIntervalsDateTime.js +31 -32
- package/dist/plain/interval/mergeIntervalsTime.d.ts +7 -3
- package/dist/plain/interval/mergeIntervalsTime.js +31 -31
- package/dist/plain/interval/splitIntervalByUnitDate.d.ts +26 -6
- package/dist/plain/interval/splitIntervalByUnitDate.js +76 -43
- package/dist/plain/interval/splitIntervalByUnitDateTime.d.ts +22 -2
- package/dist/plain/interval/splitIntervalByUnitDateTime.js +65 -39
- package/dist/plain/interval/splitIntervalByUnitTime.d.ts +18 -2
- package/dist/plain/interval/splitIntervalByUnitTime.js +76 -39
- package/dist/plain/interval/validate/isValidDateInterval.d.ts +4 -2
- package/dist/plain/interval/validate/isValidDateInterval.js +4 -2
- package/dist/plain/interval/validate/isValidDateTimeInterval.d.ts +5 -0
- package/dist/plain/interval/validate/isValidDateTimeInterval.js +7 -2
- package/dist/plain/interval/validate/isValidTimeInterval.d.ts +5 -0
- package/dist/plain/interval/validate/isValidTimeInterval.js +8 -18
- package/dist/plain/locale/getLocaleEraNames.d.ts +14 -4
- package/dist/plain/locale/getLocaleEraNames.js +56 -41
- package/dist/plain/locale/getLocaleMeridiems.d.ts +7 -4
- package/dist/plain/locale/getLocaleMeridiems.js +28 -22
- package/dist/plain/locale/getLocaleMonthNames.d.ts +14 -4
- package/dist/plain/locale/getLocaleMonthNames.js +34 -19
- package/dist/plain/locale/getLocaleWeekdayNames.d.ts +14 -7
- package/dist/plain/locale/getLocaleWeekdayNames.js +40 -22
- package/dist/plain/map/mapDatesInRange.d.ts +18 -1
- package/dist/plain/map/mapDatesInRange.js +53 -17
- package/dist/plain/parse/parseDateTimeWithPattern.d.ts +3 -2
- package/dist/plain/parse/parseDateTimeWithPattern.js +30 -24
- package/dist/plain/parse/parseDateWithPattern.d.ts +3 -2
- package/dist/plain/parse/parseDateWithPattern.js +22 -16
- package/dist/plain/parse/parseMicrosecondFromDateTime.d.ts +2 -1
- package/dist/plain/parse/parseMicrosecondFromDateTime.js +2 -1
- package/dist/plain/parse/parseMicrosecondFromTime.d.ts +2 -1
- package/dist/plain/parse/parseMicrosecondFromTime.js +2 -1
- package/dist/plain/parse/parseNanosecondFromDateTime.d.ts +2 -1
- package/dist/plain/parse/parseNanosecondFromDateTime.js +2 -1
- package/dist/plain/parse/parseNanosecondFromTime.d.ts +2 -1
- package/dist/plain/parse/parseNanosecondFromTime.js +2 -1
- package/dist/plain/parse/parseSql.d.ts +15 -12
- package/dist/plain/parse/parseSql.js +16 -13
- package/dist/plain/parse/parseTimeWithPattern.d.ts +3 -2
- package/dist/plain/parse/parseTimeWithPattern.js +27 -21
- package/dist/plain/parse/parseUnitFromDate.d.ts +9 -2
- package/dist/plain/parse/parseUnitFromDate.js +39 -19
- package/dist/plain/parse/parseUnitFromDateTime.d.ts +11 -3
- package/dist/plain/parse/parseUnitFromDateTime.js +54 -28
- package/dist/plain/parse/parseUnitFromTime.d.ts +9 -3
- package/dist/plain/parse/parseUnitFromTime.js +13 -3
- package/dist/plain/parse/parseWeekFromDate.d.ts +5 -0
- package/dist/plain/parse/parseWeekFromDate.js +24 -5
- package/dist/plain/parse/parseWeekFromDateTime.d.ts +5 -0
- package/dist/plain/parse/parseWeekFromDateTime.js +26 -5
- package/dist/plain/validate/isLeapSecond.d.ts +27 -9
- package/dist/plain/validate/isLeapSecond.js +30 -9
- package/dist/plain/validate/isLeapYear.d.ts +7 -1
- package/dist/plain/validate/isLeapYear.js +10 -1
- package/dist/plain/validate/isValidCalendarDate.d.ts +37 -6
- package/dist/plain/validate/isValidCalendarDate.js +37 -6
- package/dist/plain/validate/isValidDate.d.ts +9 -1
- package/dist/plain/validate/isValidDate.js +14 -5
- package/dist/plain/validate/isValidDateRange.d.ts +11 -4
- package/dist/plain/validate/isValidDateRange.js +32 -22
- package/dist/plain/validate/isValidDateTime.d.ts +7 -1
- package/dist/plain/validate/isValidDateTime.js +12 -5
- package/dist/plain/validate/isValidDateTimeRange.d.ts +3 -2
- package/dist/plain/validate/isValidDateTimeRange.js +30 -15
- package/dist/plain/validate/isValidIsoDateLike.d.ts +6 -1
- package/dist/plain/validate/isValidIsoDateLike.js +13 -8
- package/dist/plain/validate/isValidTime.d.ts +6 -1
- package/dist/plain/validate/isValidTime.js +10 -3
- package/dist/plain/validate/isValidTimeRange.d.ts +4 -2
- package/dist/plain/validate/isValidTimeRange.js +31 -27
- package/dist/precision/calculate/index.d.ts +1 -0
- package/dist/precision/calculate/index.js +1 -0
- package/dist/precision/calculate/truncateNanoseconds.d.ts +37 -0
- package/dist/precision/calculate/truncateNanoseconds.js +41 -0
- package/dist/precision/convert/fromDotNetTicks.d.ts +30 -0
- package/dist/precision/convert/fromDotNetTicks.js +39 -0
- package/dist/precision/convert/fromExcelSerial.d.ts +38 -0
- package/dist/precision/convert/fromExcelSerial.js +81 -0
- package/dist/precision/convert/fromFileTime.d.ts +30 -0
- package/dist/precision/convert/fromFileTime.js +39 -0
- package/dist/precision/convert/fromNanoseconds.d.ts +29 -0
- package/dist/precision/convert/fromNanoseconds.js +50 -0
- package/dist/precision/convert/fromNtpTimestamp.d.ts +43 -0
- package/dist/precision/convert/fromNtpTimestamp.js +57 -0
- package/dist/precision/convert/fromPgMicroseconds.d.ts +26 -0
- package/dist/precision/convert/fromPgMicroseconds.js +33 -0
- package/dist/precision/convert/index.d.ts +12 -0
- package/dist/precision/convert/index.js +12 -0
- package/dist/precision/convert/toDotNetTicks.d.ts +29 -0
- package/dist/precision/convert/toDotNetTicks.js +40 -0
- package/dist/precision/convert/toExcelSerial.d.ts +56 -0
- package/dist/precision/convert/toExcelSerial.js +85 -0
- package/dist/precision/convert/toFileTime.d.ts +31 -0
- package/dist/precision/convert/toFileTime.js +43 -0
- package/dist/precision/convert/toNanoseconds.d.ts +37 -0
- package/dist/precision/convert/toNanoseconds.js +40 -0
- package/dist/precision/convert/toNtpTimestamp.d.ts +30 -0
- package/dist/precision/convert/toNtpTimestamp.js +38 -0
- package/dist/precision/convert/toPgMicroseconds.d.ts +29 -0
- package/dist/precision/convert/toPgMicroseconds.js +42 -0
- package/dist/precision/format/formatNanoseconds.d.ts +26 -0
- package/dist/precision/format/formatNanoseconds.js +32 -0
- package/dist/precision/format/index.d.ts +1 -0
- package/dist/precision/format/index.js +1 -0
- package/dist/precision/index.d.ts +6 -0
- package/dist/precision/index.js +7 -0
- package/dist/precision/parse/index.d.ts +1 -0
- package/dist/precision/parse/index.js +1 -0
- package/dist/precision/parse/parseNanoseconds.d.ts +27 -0
- package/dist/precision/parse/parseNanoseconds.js +32 -0
- package/dist/precision/validate/index.d.ts +3 -0
- package/dist/precision/validate/index.js +3 -0
- package/dist/precision/validate/isValidInstant.d.ts +37 -0
- package/dist/precision/validate/isValidInstant.js +40 -0
- package/dist/precision/validate/isValidNanoPattern.d.ts +27 -0
- package/dist/precision/validate/isValidNanoPattern.js +40 -0
- package/dist/precision/validate/isValidNanoseconds.d.ts +25 -0
- package/dist/precision/validate/isValidNanoseconds.js +28 -0
- package/dist/regex/calendar-date.d.ts +25 -14
- package/dist/regex/calendar-date.js +26 -15
- package/dist/regex/calendar-zoned-date-time.d.ts +22 -38
- package/dist/regex/calendar-zoned-date-time.js +23 -39
- package/dist/regex/date-time.d.ts +14 -4
- package/dist/regex/date-time.js +15 -5
- package/dist/regex/date.d.ts +45 -3
- package/dist/regex/date.js +47 -5
- package/dist/regex/http-date.d.ts +10 -6
- package/dist/regex/http-date.js +10 -6
- package/dist/regex/index.d.ts +3 -0
- package/dist/regex/index.js +4 -0
- package/dist/regex/leap-second.d.ts +41 -8
- package/dist/regex/leap-second.js +42 -9
- package/dist/regex/nanoseconds.d.ts +20 -0
- package/dist/regex/nanoseconds.js +20 -0
- package/dist/regex/rfc-2822.d.ts +15 -7
- package/dist/regex/rfc-2822.js +15 -7
- package/dist/regex/rfc-3339.d.ts +8 -1
- package/dist/regex/rfc-3339.js +9 -2
- package/dist/regex/sql-date-time.d.ts +27 -8
- package/dist/regex/sql-date-time.js +28 -9
- package/dist/regex/time-zone-like.d.ts +24 -15
- package/dist/regex/time-zone-like.js +25 -16
- package/dist/regex/time.d.ts +20 -6
- package/dist/regex/time.js +20 -6
- package/dist/regex/unix.d.ts +13 -5
- package/dist/regex/unix.js +13 -5
- package/dist/regex/utc-date-time.d.ts +19 -9
- package/dist/regex/utc-date-time.js +20 -10
- package/dist/regex/utc-offset.d.ts +24 -0
- package/dist/regex/utc-offset.js +24 -0
- package/dist/span/calculate/index.d.ts +3 -0
- package/dist/span/calculate/index.js +3 -0
- package/dist/span/calculate/spanMs.d.ts +52 -0
- package/dist/span/calculate/spanMs.js +80 -0
- package/dist/span/calculate/spanNs.d.ts +43 -0
- package/dist/span/calculate/spanNs.js +51 -0
- package/dist/span/calculate/spanWallClock.d.ts +60 -0
- package/dist/span/calculate/spanWallClock.js +80 -0
- package/dist/span/index.d.ts +3 -0
- package/dist/span/index.js +4 -0
- package/dist/span/validate/index.d.ts +1 -0
- package/dist/span/validate/index.js +1 -0
- package/dist/span/validate/isValidSpan.d.ts +37 -0
- package/dist/span/validate/isValidSpan.js +41 -0
- package/dist/transport/calculate/dwellTime.d.ts +72 -0
- package/dist/transport/calculate/dwellTime.js +126 -0
- package/dist/transport/calculate/index.d.ts +2 -0
- package/dist/transport/calculate/index.js +2 -0
- package/dist/transport/calculate/transitTime.d.ts +53 -0
- package/dist/transport/calculate/transitTime.js +118 -0
- package/dist/transport/convert/etaAtZone.d.ts +36 -0
- package/dist/transport/convert/etaAtZone.js +53 -0
- package/dist/transport/convert/index.d.ts +1 -0
- package/dist/transport/convert/index.js +1 -0
- package/dist/transport/index.d.ts +3 -0
- package/dist/transport/index.js +4 -0
- package/dist/types/business-calendar.d.ts +27 -0
- package/dist/types/business-calendar.js +0 -0
- package/dist/types/calendar-system.d.ts +9 -1
- package/dist/types/fiscal-calendar.d.ts +22 -0
- package/dist/types/fiscal-calendar.js +0 -0
- package/dist/types/free-time-options.d.ts +34 -0
- package/dist/types/free-time-options.js +0 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.js +6 -0
- package/dist/types/interval.d.ts +18 -0
- package/dist/types/interval.js +0 -0
- package/dist/types/now-unit.d.ts +1 -1
- package/dist/types/roll-convention.d.ts +32 -0
- package/dist/types/roll-convention.js +0 -0
- package/dist/types/utc-now-unit.d.ts +1 -1
- package/dist/types/zone-bucket-unit.d.ts +11 -0
- package/dist/types/zone-bucket-unit.js +0 -0
- package/dist/unix/calculate/addUnix.d.ts +10 -4
- package/dist/unix/calculate/addUnix.js +39 -26
- package/dist/unix/calculate/diffUnix.d.ts +38 -11
- package/dist/unix/calculate/diffUnix.js +84 -40
- package/dist/unix/calculate/diffUnixAsDuration.d.ts +17 -7
- package/dist/unix/calculate/diffUnixAsDuration.js +42 -23
- package/dist/unix/calculate/endOfQuarterForUnix.d.ts +15 -11
- package/dist/unix/calculate/endOfQuarterForUnix.js +20 -39
- package/dist/unix/calculate/endOfUnix.d.ts +23 -18
- package/dist/unix/calculate/endOfUnix.js +37 -15
- package/dist/unix/calculate/isBetweenUnix.d.ts +12 -6
- package/dist/unix/calculate/isBetweenUnix.js +34 -23
- package/dist/unix/calculate/maxUnix.d.ts +6 -3
- package/dist/unix/calculate/maxUnix.js +17 -8
- package/dist/unix/calculate/minUnix.d.ts +6 -3
- package/dist/unix/calculate/minUnix.js +17 -8
- package/dist/unix/calculate/roundUnix.d.ts +24 -10
- package/dist/unix/calculate/roundUnix.js +67 -42
- package/dist/unix/calculate/setUnix.d.ts +14 -5
- package/dist/unix/calculate/setUnix.js +48 -29
- package/dist/unix/calculate/sortUnix.d.ts +6 -3
- package/dist/unix/calculate/sortUnix.js +19 -11
- package/dist/unix/calculate/startOfQuarterForUnix.d.ts +17 -12
- package/dist/unix/calculate/startOfQuarterForUnix.js +22 -30
- package/dist/unix/calculate/startOfUnix.d.ts +23 -18
- package/dist/unix/calculate/startOfUnix.js +37 -15
- package/dist/unix/calculate/subtractUnix.d.ts +10 -4
- package/dist/unix/calculate/subtractUnix.js +39 -26
- package/dist/unix/compare/areUnixEqual.d.ts +9 -5
- package/dist/unix/compare/areUnixEqual.js +17 -13
- package/dist/unix/compare/areUnixEqualBy.d.ts +14 -7
- package/dist/unix/compare/areUnixEqualBy.js +14 -7
- package/dist/unix/compare/isAfterUnix.d.ts +8 -4
- package/dist/unix/compare/isAfterUnix.js +16 -12
- package/dist/unix/compare/isBeforeUnix.d.ts +7 -4
- package/dist/unix/compare/isBeforeUnix.js +15 -12
- package/dist/unix/convert/convertUnixToPlainDate.d.ts +17 -9
- package/dist/unix/convert/convertUnixToPlainDate.js +34 -21
- package/dist/unix/convert/convertUnixToPlainDateTime.d.ts +15 -9
- package/dist/unix/convert/convertUnixToPlainDateTime.js +32 -21
- package/dist/unix/convert/convertUnixToPlainTime.d.ts +15 -9
- package/dist/unix/convert/convertUnixToPlainTime.js +32 -21
- package/dist/unix/convert/convertUnixToUtc.d.ts +15 -7
- package/dist/unix/convert/convertUnixToUtc.js +20 -15
- package/dist/unix/convert/convertUnixToZoned.d.ts +17 -10
- package/dist/unix/convert/convertUnixToZoned.js +31 -20
- package/dist/unix/format/formatCalendarUnix.d.ts +22 -19
- package/dist/unix/format/formatCalendarUnix.js +35 -81
- package/dist/unix/format/formatRelativeUnix.d.ts +25 -8
- package/dist/unix/format/formatRelativeUnix.js +46 -93
- package/dist/unix/format/formatUnix.d.ts +26 -4
- package/dist/unix/format/formatUnix.js +48 -34
- package/dist/unix/get/getUnixNow.d.ts +13 -7
- package/dist/unix/get/getUnixNow.js +27 -17
- package/dist/unix/get/getUnixNowUnit.d.ts +5 -2
- package/dist/unix/get/getUnixNowUnit.js +9 -35
- package/dist/unix/index.d.ts +1 -0
- package/dist/unix/index.js +2 -0
- package/dist/unix/interval/intervalAbutsUnix.d.ts +17 -12
- package/dist/unix/interval/intervalAbutsUnix.js +23 -41
- package/dist/unix/interval/intervalContainsUnix.d.ts +17 -7
- package/dist/unix/interval/intervalContainsUnix.js +25 -37
- package/dist/unix/interval/intervalCountUnix.d.ts +30 -13
- package/dist/unix/interval/intervalCountUnix.js +49 -23
- package/dist/unix/interval/intervalDifferenceUnix.d.ts +19 -10
- package/dist/unix/interval/intervalDifferenceUnix.js +25 -43
- package/dist/unix/interval/intervalDivideEquallyUnix.d.ts +24 -10
- package/dist/unix/interval/intervalDivideEquallyUnix.js +57 -36
- package/dist/unix/interval/intervalEngulfsUnix.d.ts +13 -7
- package/dist/unix/interval/intervalEngulfsUnix.js +18 -30
- package/dist/unix/interval/intervalFromDurationUnix.d.ts +10 -6
- package/dist/unix/interval/intervalFromDurationUnix.js +51 -39
- package/dist/unix/interval/intervalIntersectionUnix.d.ts +18 -11
- package/dist/unix/interval/intervalIntersectionUnix.js +25 -46
- package/dist/unix/interval/intervalLengthUnix.d.ts +21 -9
- package/dist/unix/interval/intervalLengthUnix.js +41 -18
- package/dist/unix/interval/intervalOverlappingDaysUnix.d.ts +26 -12
- package/dist/unix/interval/intervalOverlappingDaysUnix.js +67 -63
- package/dist/unix/interval/intervalSplitAtUnix.d.ts +11 -6
- package/dist/unix/interval/intervalSplitAtUnix.js +41 -34
- package/dist/unix/interval/intervalUnionUnix.d.ts +15 -9
- package/dist/unix/interval/intervalUnionUnix.js +20 -42
- package/dist/unix/interval/intervalXorAllUnix.d.ts +8 -6
- package/dist/unix/interval/intervalXorAllUnix.js +21 -56
- package/dist/unix/interval/intervalXorUnix.d.ts +22 -11
- package/dist/unix/interval/intervalXorUnix.js +28 -56
- package/dist/unix/interval/intervalsOverlapUnix.d.ts +16 -10
- package/dist/unix/interval/intervalsOverlapUnix.js +21 -37
- package/dist/unix/interval/mergeIntervalsUnix.d.ts +10 -4
- package/dist/unix/interval/mergeIntervalsUnix.js +25 -36
- package/dist/unix/interval/splitIntervalByUnitUnix.d.ts +38 -8
- package/dist/unix/interval/splitIntervalByUnitUnix.js +72 -56
- package/dist/unix/interval/validate/isValidUnixInterval.d.ts +12 -6
- package/dist/unix/interval/validate/isValidUnixInterval.js +17 -16
- package/dist/unix/parse/index.d.ts +2 -0
- package/dist/unix/parse/index.js +2 -0
- package/dist/unix/parse/parseDateFromUnix.d.ts +10 -8
- package/dist/unix/parse/parseDateFromUnix.js +17 -29
- package/dist/unix/parse/parseDayFromUnix.d.ts +6 -4
- package/dist/unix/parse/parseDayFromUnix.js +18 -5
- package/dist/unix/parse/parseDayOfWeekFromUnix.d.ts +8 -5
- package/dist/unix/parse/parseDayOfWeekFromUnix.js +15 -28
- package/dist/unix/parse/parseHourFromUnix.d.ts +6 -4
- package/dist/unix/parse/parseHourFromUnix.js +18 -5
- package/dist/unix/parse/parseMicrosecondFromUnix.d.ts +4 -2
- package/dist/unix/parse/parseMicrosecondFromUnix.js +16 -3
- package/dist/unix/parse/parseMillisecondFromUnix.d.ts +4 -2
- package/dist/unix/parse/parseMillisecondFromUnix.js +16 -3
- package/dist/unix/parse/parseMinuteFromUnix.d.ts +6 -4
- package/dist/unix/parse/parseMinuteFromUnix.js +18 -5
- package/dist/unix/parse/parseMonthFromUnix.d.ts +6 -4
- package/dist/unix/parse/parseMonthFromUnix.js +18 -5
- package/dist/unix/parse/parseNanosecondFromUnix.d.ts +8 -5
- package/dist/unix/parse/parseNanosecondFromUnix.js +20 -6
- package/dist/unix/parse/parseSecondFromUnix.d.ts +5 -3
- package/dist/unix/parse/parseSecondFromUnix.js +17 -4
- package/dist/unix/parse/parseTimeFromUnix.d.ts +10 -8
- package/dist/unix/parse/parseTimeFromUnix.js +17 -29
- package/dist/unix/parse/parseUnitFromUnix.d.ts +18 -11
- package/dist/unix/parse/parseUnitFromUnix.js +57 -55
- package/dist/unix/parse/parseWeekFromUnix.d.ts +15 -7
- package/dist/unix/parse/parseWeekFromUnix.js +32 -30
- package/dist/unix/parse/parseYearFromUnix.d.ts +5 -3
- package/dist/unix/parse/parseYearFromUnix.js +17 -4
- package/dist/unix/validate/isValidUnixMilliseconds.d.ts +8 -5
- package/dist/unix/validate/isValidUnixMilliseconds.js +10 -9
- package/dist/unix/validate/isValidUnixRange.d.ts +13 -7
- package/dist/unix/validate/isValidUnixRange.js +29 -19
- package/dist/unix/validate/isValidUnixSeconds.d.ts +8 -5
- package/dist/unix/validate/isValidUnixSeconds.js +10 -9
- package/dist/unix/validate/isValidUnixUnit.d.ts +9 -7
- package/dist/unix/validate/isValidUnixUnit.js +6 -8
- package/dist/utc/calculate/addUtc.js +26 -12
- package/dist/utc/calculate/diffUtc.d.ts +22 -7
- package/dist/utc/calculate/diffUtc.js +75 -31
- package/dist/utc/calculate/diffUtcAsDuration.d.ts +2 -2
- package/dist/utc/calculate/diffUtcAsDuration.js +10 -3
- package/dist/utc/calculate/endOfQuarterForUtc.d.ts +1 -1
- package/dist/utc/calculate/endOfQuarterForUtc.js +1 -1
- package/dist/utc/calculate/endOfUtc.d.ts +11 -4
- package/dist/utc/calculate/endOfUtc.js +22 -5
- package/dist/utc/calculate/isBetweenUtc.js +40 -24
- package/dist/utc/calculate/maxUtc.js +19 -12
- package/dist/utc/calculate/minUtc.js +19 -12
- package/dist/utc/calculate/roundUtc.d.ts +16 -2
- package/dist/utc/calculate/roundUtc.js +42 -31
- package/dist/utc/calculate/setUtc.d.ts +4 -8
- package/dist/utc/calculate/setUtc.js +25 -18
- package/dist/utc/calculate/sortUtc.js +17 -10
- package/dist/utc/calculate/startOfQuarterForUtc.d.ts +2 -0
- package/dist/utc/calculate/startOfQuarterForUtc.js +12 -1
- package/dist/utc/calculate/startOfUtc.d.ts +6 -4
- package/dist/utc/calculate/startOfUtc.js +17 -5
- package/dist/utc/calculate/subtractUtc.js +26 -12
- package/dist/utc/chop/chopUtc.d.ts +5 -2
- package/dist/utc/chop/chopUtc.js +8 -4
- package/dist/utc/compare/areUtcEqualBy.d.ts +10 -5
- package/dist/utc/compare/areUtcEqualBy.js +34 -11
- package/dist/utc/convert/convertUtcToPlainDate.js +22 -10
- package/dist/utc/convert/convertUtcToPlainDateTime.js +28 -16
- package/dist/utc/convert/convertUtcToPlainTime.js +22 -10
- package/dist/utc/convert/convertUtcToUnix.d.ts +12 -5
- package/dist/utc/convert/convertUtcToUnix.js +22 -13
- package/dist/utc/format/formatCalendarUtc.d.ts +11 -6
- package/dist/utc/format/formatCalendarUtc.js +32 -41
- package/dist/utc/format/formatHttp.d.ts +9 -3
- package/dist/utc/format/formatHttp.js +14 -12
- package/dist/utc/format/formatRelativeUtc.d.ts +18 -6
- package/dist/utc/format/formatRelativeUtc.js +43 -51
- package/dist/utc/format/formatUtc.d.ts +20 -3
- package/dist/utc/format/formatUtc.js +41 -12
- package/dist/utc/get/getUtcNowUnit.d.ts +3 -0
- package/dist/utc/get/getUtcNowUnit.js +11 -33
- package/dist/utc/index.d.ts +1 -0
- package/dist/utc/index.js +2 -0
- package/dist/utc/interval/intervalAbutsUtc.d.ts +10 -7
- package/dist/utc/interval/intervalAbutsUtc.js +18 -30
- package/dist/utc/interval/intervalContainsUtc.d.ts +19 -11
- package/dist/utc/interval/intervalContainsUtc.js +33 -34
- package/dist/utc/interval/intervalCountUtc.d.ts +3 -4
- package/dist/utc/interval/intervalCountUtc.js +12 -18
- package/dist/utc/interval/intervalDifferenceUtc.d.ts +13 -5
- package/dist/utc/interval/intervalDifferenceUtc.js +25 -51
- package/dist/utc/interval/intervalDivideEquallyUtc.d.ts +16 -3
- package/dist/utc/interval/intervalDivideEquallyUtc.js +54 -34
- package/dist/utc/interval/intervalEngulfsUtc.d.ts +17 -12
- package/dist/utc/interval/intervalEngulfsUtc.js +25 -26
- package/dist/utc/interval/intervalFromDurationUtc.js +5 -0
- package/dist/utc/interval/intervalIntersectionUtc.d.ts +12 -6
- package/dist/utc/interval/intervalIntersectionUtc.js +24 -41
- package/dist/utc/interval/intervalLengthUtc.js +5 -7
- package/dist/utc/interval/intervalOverlappingDaysUtc.d.ts +7 -13
- package/dist/utc/interval/intervalOverlappingDaysUtc.js +25 -36
- package/dist/utc/interval/intervalSplitAtUtc.d.ts +7 -2
- package/dist/utc/interval/intervalSplitAtUtc.js +39 -33
- package/dist/utc/interval/intervalUnionUtc.d.ts +15 -7
- package/dist/utc/interval/intervalUnionUtc.js +31 -42
- package/dist/utc/interval/intervalXorAllUtc.d.ts +7 -4
- package/dist/utc/interval/intervalXorAllUtc.js +30 -52
- package/dist/utc/interval/intervalXorUtc.d.ts +8 -6
- package/dist/utc/interval/intervalXorUtc.js +22 -56
- package/dist/utc/interval/intervalsOverlapUtc.d.ts +15 -9
- package/dist/utc/interval/intervalsOverlapUtc.js +23 -39
- package/dist/utc/interval/mergeIntervalsUtc.d.ts +9 -2
- package/dist/utc/interval/mergeIntervalsUtc.js +23 -36
- package/dist/utc/interval/splitIntervalByUnitUtc.d.ts +23 -2
- package/dist/utc/interval/splitIntervalByUnitUtc.js +65 -44
- package/dist/utc/interval/validate/isValidUtcInterval.d.ts +5 -0
- package/dist/utc/interval/validate/isValidUtcInterval.js +7 -6
- package/dist/utc/parse/parseDateFromUtc.d.ts +7 -1
- package/dist/utc/parse/parseDateFromUtc.js +14 -9
- package/dist/utc/parse/parseDayFromUtc.d.ts +7 -1
- package/dist/utc/parse/parseDayFromUtc.js +14 -8
- package/dist/utc/parse/parseDayOfWeekFromUtc.d.ts +7 -1
- package/dist/utc/parse/parseDayOfWeekFromUtc.js +24 -6
- package/dist/utc/parse/parseHourFromUtc.d.ts +7 -1
- package/dist/utc/parse/parseHourFromUtc.js +14 -8
- package/dist/utc/parse/parseHttp.d.ts +39 -14
- package/dist/utc/parse/parseHttp.js +69 -28
- package/dist/utc/parse/parseMicrosecondFromUtc.d.ts +9 -2
- package/dist/utc/parse/parseMicrosecondFromUtc.js +18 -9
- package/dist/utc/parse/parseMillisecondFromUtc.d.ts +7 -1
- package/dist/utc/parse/parseMillisecondFromUtc.js +16 -8
- package/dist/utc/parse/parseMinuteFromUtc.d.ts +7 -1
- package/dist/utc/parse/parseMinuteFromUtc.js +14 -8
- package/dist/utc/parse/parseMonthFromUtc.d.ts +7 -1
- package/dist/utc/parse/parseMonthFromUtc.js +14 -8
- package/dist/utc/parse/parseNanosecondFromUtc.d.ts +9 -2
- package/dist/utc/parse/parseNanosecondFromUtc.js +18 -9
- package/dist/utc/parse/parseSecondFromUtc.d.ts +7 -1
- package/dist/utc/parse/parseSecondFromUtc.js +14 -8
- package/dist/utc/parse/parseTimeFromUtc.js +21 -10
- package/dist/utc/parse/parseUnitFromUtc.d.ts +18 -7
- package/dist/utc/parse/parseUnitFromUtc.js +58 -31
- package/dist/utc/parse/parseWeekFromUtc.d.ts +16 -3
- package/dist/utc/parse/parseWeekFromUtc.js +38 -10
- package/dist/utc/parse/parseYearFromUtc.d.ts +7 -1
- package/dist/utc/parse/parseYearFromUtc.js +14 -8
- package/dist/utc/validate/isValidUtc.d.ts +8 -1
- package/dist/utc/validate/isValidUtc.js +12 -3
- package/dist/utc/validate/isValidUtcRange.d.ts +3 -2
- package/dist/utc/validate/isValidUtcRange.js +30 -19
- package/dist/zoned/calculate/addZoned.d.ts +40 -27
- package/dist/zoned/calculate/addZoned.js +71 -56
- package/dist/zoned/calculate/addZonedBusinessDays.d.ts +2 -0
- package/dist/zoned/calculate/addZonedBusinessDays.js +5 -2
- package/dist/zoned/calculate/clampZoned.js +4 -3
- package/dist/zoned/calculate/closestZonedTo.d.ts +6 -6
- package/dist/zoned/calculate/closestZonedTo.js +35 -26
- package/dist/zoned/calculate/cycleZoned.d.ts +10 -7
- package/dist/zoned/calculate/cycleZoned.js +19 -11
- package/dist/zoned/calculate/diffZoned.d.ts +39 -17
- package/dist/zoned/calculate/diffZoned.js +90 -45
- package/dist/zoned/calculate/diffZonedAsDuration.d.ts +19 -9
- package/dist/zoned/calculate/diffZonedAsDuration.js +32 -15
- package/dist/zoned/calculate/endOfQuarterForZoned.d.ts +11 -11
- package/dist/zoned/calculate/endOfQuarterForZoned.js +32 -31
- package/dist/zoned/calculate/endOfZoned.d.ts +21 -14
- package/dist/zoned/calculate/endOfZoned.js +25 -111
- package/dist/zoned/calculate/getHoursInZonedDay.d.ts +8 -1
- package/dist/zoned/calculate/getHoursInZonedDay.js +10 -14
- package/dist/zoned/calculate/getLocaleZonedDayOfWeek.d.ts +8 -4
- package/dist/zoned/calculate/getLocaleZonedDayOfWeek.js +9 -6
- package/dist/zoned/calculate/getLocaleZonedEndOfWeek.d.ts +20 -26
- package/dist/zoned/calculate/getLocaleZonedEndOfWeek.js +40 -44
- package/dist/zoned/calculate/getLocaleZonedStartOfWeek.d.ts +16 -24
- package/dist/zoned/calculate/getLocaleZonedStartOfWeek.js +36 -42
- package/dist/zoned/calculate/getQuarterForZoned.js +2 -2
- package/dist/zoned/calculate/maxZoned.js +20 -12
- package/dist/zoned/calculate/minZoned.d.ts +1 -1
- package/dist/zoned/calculate/minZoned.js +21 -13
- package/dist/zoned/calculate/roundZoned.d.ts +18 -4
- package/dist/zoned/calculate/roundZoned.js +58 -33
- package/dist/zoned/calculate/setZoned.d.ts +9 -3
- package/dist/zoned/calculate/setZoned.js +39 -18
- package/dist/zoned/calculate/sortZoned.js +18 -10
- package/dist/zoned/calculate/startOfQuarterForZoned.d.ts +4 -7
- package/dist/zoned/calculate/startOfQuarterForZoned.js +23 -17
- package/dist/zoned/calculate/startOfZoned.d.ts +17 -13
- package/dist/zoned/calculate/startOfZoned.js +21 -103
- package/dist/zoned/calculate/subtractZoned.d.ts +36 -24
- package/dist/zoned/calculate/subtractZoned.js +66 -50
- package/dist/zoned/calculate/subtractZonedBusinessDays.d.ts +2 -0
- package/dist/zoned/calculate/subtractZonedBusinessDays.js +5 -2
- package/dist/zoned/chop/chopZonedDate.js +2 -2
- package/dist/zoned/chop/chopZonedDateTime.js +2 -2
- package/dist/zoned/chop/chopZonedMilliseconds.js +2 -2
- package/dist/zoned/chop/chopZonedSeconds.js +2 -2
- package/dist/zoned/chop/chopZonedTime.js +2 -2
- package/dist/zoned/chop/chopZonedTimezone.js +2 -2
- package/dist/zoned/compare/areZonedEqual.js +3 -2
- package/dist/zoned/compare/areZonedEqualBy.d.ts +15 -2
- package/dist/zoned/compare/areZonedEqualBy.js +45 -12
- package/dist/zoned/compare/isAfterZoned.js +3 -2
- package/dist/zoned/compare/isBeforeZoned.d.ts +1 -1
- package/dist/zoned/compare/isBeforeZoned.js +4 -3
- package/dist/zoned/compare/isBetweenZoned.js +46 -29
- package/dist/zoned/compare/isInDaylightSaving.js +4 -4
- package/dist/zoned/compare/isZonedBusinessDay.d.ts +7 -4
- package/dist/zoned/compare/isZonedBusinessDay.js +9 -6
- package/dist/zoned/compare/isZonedFuture.js +2 -1
- package/dist/zoned/compare/isZonedPast.js +2 -1
- package/dist/zoned/compare/isZonedRelativeDay.js +2 -1
- package/dist/zoned/compare/isZonedThisUnit.d.ts +12 -4
- package/dist/zoned/compare/isZonedThisUnit.js +37 -16
- package/dist/zoned/compare/isZonedWeekend.d.ts +8 -4
- package/dist/zoned/compare/isZonedWeekend.js +9 -6
- package/dist/zoned/convert/convertPlainDateTimeToZoned.d.ts +10 -5
- package/dist/zoned/convert/convertPlainDateTimeToZoned.js +34 -16
- package/dist/zoned/convert/convertZonedToCalendar.d.ts +26 -38
- package/dist/zoned/convert/convertZonedToCalendar.js +33 -48
- package/dist/zoned/convert/convertZonedToPlainDateTime.d.ts +6 -0
- package/dist/zoned/convert/convertZonedToPlainDateTime.js +8 -2
- package/dist/zoned/convert/convertZonedToUnix.d.ts +12 -6
- package/dist/zoned/convert/convertZonedToUnix.js +23 -16
- package/dist/zoned/convert/convertZonedToUtc.js +2 -2
- package/dist/zoned/convert/convertZonedToZoned.js +2 -2
- package/dist/zoned/format/formatCalendarZoned.d.ts +7 -3
- package/dist/zoned/format/formatCalendarZoned.js +37 -39
- package/dist/zoned/format/formatRelativeZoned.d.ts +15 -6
- package/dist/zoned/format/formatRelativeZoned.js +54 -54
- package/dist/zoned/format/formatRfc2822.d.ts +14 -1
- package/dist/zoned/format/formatRfc2822.js +27 -13
- package/dist/zoned/format/formatRfc3339.d.ts +14 -1
- package/dist/zoned/format/formatRfc3339.js +29 -3
- package/dist/zoned/format/formatTimeZoneName.d.ts +3 -2
- package/dist/zoned/format/formatTimeZoneName.js +28 -18
- package/dist/zoned/format/formatZonedDateTime.d.ts +25 -5
- package/dist/zoned/format/formatZonedDateTime.js +40 -9
- package/dist/zoned/format/formatZonedRange.d.ts +24 -6
- package/dist/zoned/format/formatZonedRange.js +61 -28
- package/dist/zoned/format/formatZonedToParts.d.ts +29 -6
- package/dist/zoned/format/formatZonedToParts.js +42 -13
- package/dist/zoned/get/getDstTransitions.d.ts +6 -1
- package/dist/zoned/get/getDstTransitions.js +17 -15
- package/dist/zoned/get/getTimeZoneOffset.d.ts +11 -1
- package/dist/zoned/get/getTimeZoneOffset.js +19 -2
- package/dist/zoned/get/getZonedNow.js +20 -9
- package/dist/zoned/get/getZonedNowUnit.d.ts +8 -4
- package/dist/zoned/get/getZonedNowUnit.js +15 -31
- package/dist/zoned/index.js +1 -1
- package/dist/zoned/interval/intervalAbutsZoned.d.ts +18 -11
- package/dist/zoned/interval/intervalAbutsZoned.js +24 -25
- package/dist/zoned/interval/intervalContainsZoned.d.ts +16 -7
- package/dist/zoned/interval/intervalContainsZoned.js +22 -14
- package/dist/zoned/interval/intervalCountZoned.d.ts +19 -11
- package/dist/zoned/interval/intervalCountZoned.js +32 -27
- package/dist/zoned/interval/intervalDifferenceZoned.d.ts +13 -7
- package/dist/zoned/interval/intervalDifferenceZoned.js +24 -36
- package/dist/zoned/interval/intervalDivideEquallyZoned.d.ts +18 -9
- package/dist/zoned/interval/intervalDivideEquallyZoned.js +67 -46
- package/dist/zoned/interval/intervalEngulfsZoned.d.ts +10 -5
- package/dist/zoned/interval/intervalEngulfsZoned.js +16 -10
- package/dist/zoned/interval/intervalFromDurationZoned.d.ts +25 -11
- package/dist/zoned/interval/intervalFromDurationZoned.js +61 -44
- package/dist/zoned/interval/intervalIntersectionZoned.d.ts +12 -6
- package/dist/zoned/interval/intervalIntersectionZoned.js +24 -18
- package/dist/zoned/interval/intervalLengthZoned.d.ts +15 -6
- package/dist/zoned/interval/intervalLengthZoned.js +23 -9
- package/dist/zoned/interval/intervalOverlappingDaysZoned.d.ts +28 -17
- package/dist/zoned/interval/intervalOverlappingDaysZoned.js +46 -34
- package/dist/zoned/interval/intervalSplitAtZoned.d.ts +6 -3
- package/dist/zoned/interval/intervalSplitAtZoned.js +52 -42
- package/dist/zoned/interval/intervalUnionZoned.d.ts +14 -8
- package/dist/zoned/interval/intervalUnionZoned.js +25 -20
- package/dist/zoned/interval/intervalXorAllZoned.d.ts +9 -7
- package/dist/zoned/interval/intervalXorAllZoned.js +42 -73
- package/dist/zoned/interval/intervalXorZoned.d.ts +20 -12
- package/dist/zoned/interval/intervalXorZoned.js +34 -64
- package/dist/zoned/interval/intervalsOverlapZoned.d.ts +15 -7
- package/dist/zoned/interval/intervalsOverlapZoned.js +21 -12
- package/dist/zoned/interval/mergeIntervalsZoned.d.ts +10 -3
- package/dist/zoned/interval/mergeIntervalsZoned.js +44 -41
- package/dist/zoned/interval/splitIntervalByUnitZoned.d.ts +28 -5
- package/dist/zoned/interval/splitIntervalByUnitZoned.js +72 -39
- package/dist/zoned/interval/validate/isValidCalendarZonedInterval.d.ts +13 -10
- package/dist/zoned/interval/validate/isValidCalendarZonedInterval.js +13 -10
- package/dist/zoned/interval/validate/isValidZonedInterval.d.ts +7 -5
- package/dist/zoned/interval/validate/isValidZonedInterval.js +12 -16
- package/dist/zoned/map/mapZonedDatesInRange.d.ts +17 -1
- package/dist/zoned/map/mapZonedDatesInRange.js +62 -26
- package/dist/zoned/map/mapZonedHoursInDay.d.ts +10 -13
- package/dist/zoned/map/mapZonedHoursInDay.js +13 -21
- package/dist/zoned/parse/getZonedOffset.js +2 -2
- package/dist/zoned/parse/getZonedOffsetAs.d.ts +4 -2
- package/dist/zoned/parse/getZonedOffsetAs.js +10 -5
- package/dist/zoned/parse/parseDateFromZoned.js +2 -2
- package/dist/zoned/parse/parseDateTimeFromZoned.js +2 -2
- package/dist/zoned/parse/parseDayFromZoned.js +2 -2
- package/dist/zoned/parse/parseDayOfWeekFromZoned.js +2 -2
- package/dist/zoned/parse/parseHourFromZoned.js +2 -2
- package/dist/zoned/parse/parseMicrosecondFromZoned.d.ts +2 -1
- package/dist/zoned/parse/parseMicrosecondFromZoned.js +4 -3
- package/dist/zoned/parse/parseMillisecondFromZoned.js +2 -2
- package/dist/zoned/parse/parseMinuteFromZoned.js +2 -2
- package/dist/zoned/parse/parseMonthFromZoned.js +2 -2
- package/dist/zoned/parse/parseNanosecondFromZoned.d.ts +2 -1
- package/dist/zoned/parse/parseNanosecondFromZoned.js +4 -3
- package/dist/zoned/parse/parseRfc2822.d.ts +32 -12
- package/dist/zoned/parse/parseRfc2822.js +47 -45
- package/dist/zoned/parse/parseRfc3339.d.ts +6 -0
- package/dist/zoned/parse/parseRfc3339.js +6 -0
- package/dist/zoned/parse/parseSecondFromZoned.js +2 -2
- package/dist/zoned/parse/parseTimeFromZoned.js +2 -2
- package/dist/zoned/parse/parseTimeZoneFromZoned.js +2 -2
- package/dist/zoned/parse/parseUnitFromZoned.d.ts +14 -5
- package/dist/zoned/parse/parseUnitFromZoned.js +59 -33
- package/dist/zoned/parse/parseWeekFromZoned.d.ts +13 -3
- package/dist/zoned/parse/parseWeekFromZoned.js +34 -10
- package/dist/zoned/parse/parseYearFromZoned.js +2 -2
- package/dist/zoned/validate/hasDaylightSaving.js +3 -3
- package/dist/zoned/validate/isValidCalendarZonedDateTime.d.ts +31 -16
- package/dist/zoned/validate/isValidCalendarZonedDateTime.js +31 -16
- package/dist/zoned/validate/isValidTimeZone.d.ts +15 -2
- package/dist/zoned/validate/isValidTimeZone.js +17 -4
- package/dist/zoned/validate/isValidZonedDateTime.d.ts +14 -7
- package/dist/zoned/validate/isValidZonedDateTime.js +20 -12
- package/dist/zoned/validate/isValidZonedRange.d.ts +9 -2
- package/dist/zoned/validate/isValidZonedRange.js +44 -16
- package/package.json +510 -34
- package/skills/gmt-arithmetic/SKILL.md +197 -16
- package/skills/gmt-basics/SKILL.md +61 -14
- package/skills/gmt-integration/SKILL.md +54 -8
- package/skills/gmt-timezone/SKILL.md +208 -24
- package/dist/internal/advanceBusinessDays.d.ts +0 -2
- package/dist/internal/advanceBusinessDays.js +0 -15
- package/dist/internal/ethiopicFamilyCalendar.d.ts +0 -47
- package/dist/internal/ethiopicFamilyCalendar.js +0 -136
- package/dist/internal/getLocaleMinimalDaysInFirstWeek.d.ts +0 -20
- package/dist/internal/getLocaleMinimalDaysInFirstWeek.js +0 -31
- package/dist/internal/resolveUnixTimeZone.d.ts +0 -20
- package/dist/internal/resolveUnixTimeZone.js +0 -30
- package/dist/unix/calculate/startOrEndOfUnix.d.ts +0 -27
- package/dist/unix/calculate/startOrEndOfUnix.js +0 -140
- package/dist/unix/interval/resolveUnixIntervalPair.d.ts +0 -7
- package/dist/unix/interval/resolveUnixIntervalPair.js +0 -36
package/README.md
CHANGED
|
@@ -11,12 +11,14 @@ Give Me Temporal.
|
|
|
11
11
|
|
|
12
12
|
It wraps `@js-temporal/polyfill` behind a smaller, more opinionated API aimed at the cases application code actually hits: arithmetic, comparison, parsing, formatting, unix conversions, timezone conversion, and validation.
|
|
13
13
|
|
|
14
|
+
Read the [docs](https://gmt-dox.northguild.workers.dev/), or ask us on [Discord](https://discord.gg/TdvQdP3t5a).
|
|
15
|
+
|
|
14
16
|
**Why GMT:**
|
|
15
17
|
|
|
16
18
|
- **100% Temporal, Temporal-first.** GMT is built directly on the TC39 `Temporal` standard (via `@js-temporal/polyfill`) — not a custom, homegrown date/time type system like `@internationalized/date`'s own `CalendarDate`/`ZonedDateTime` classes. No `Date` object anywhere, enforced by 3 dedicated lint packages.
|
|
17
19
|
- **A full replacement for any and all of them.** Luxon, date-fns, Moment.js, and react-aria's `@internationalized/date` don't have parity with each other — GMT covers the combined capabilities of all four in one library, plus what none of them do alone.
|
|
18
|
-
- **~
|
|
19
|
-
- **~
|
|
20
|
+
- **~55× more CI test executions than all four competitors combined**: 1,109,250 from 36,975 tests run in all 10 timezones × 3 Node versions, vs. their combined 20,190.
|
|
21
|
+
- **~96× more test cases than `@internationalized/date`**: 36,975 vs. 386 — Adobe's own library, run at its own commit.
|
|
20
22
|
- **The only one of the five that tests systematically across locales in CI at all.** Zero of the four comparison libraries run a locale-test matrix; GMT mandates all 17 locales on every locale-aware function.
|
|
21
23
|
- **The only one that runs its entire suite under a real `TZ` env var across real-world zones.** Luxon and `@internationalized/date` have no CI timezone matrix; date-fns's zone scope is unclear; Moment.js covers 6 zones but not its full suite.
|
|
22
24
|
- **Explicit DST disambiguation control on both construction _and_ arithmetic** — a control none of the others expose.
|
|
@@ -36,8 +38,8 @@ pnpm add @northguild/gmt
|
|
|
36
38
|
|
|
37
39
|
GMT enforces a strict input/output contract to keep behavior predictable and auditable:
|
|
38
40
|
|
|
39
|
-
- **Explicit inputs only**: Public APIs accept clearly defined shapes — ISO 8601 date/time strings, IANA timezone identifiers, or
|
|
40
|
-
- **Predictable outputs**: Helpers return normalized values (ISO strings, numbers, booleans, or arrays). Invalid input yields typed fallbacks (`""`, `null`, or `
|
|
41
|
+
- **Explicit inputs only**: Public APIs accept clearly defined shapes — ISO 8601 date/time strings, IANA timezone identifiers, or Unix epochs as safe integers or digit strings, in seconds or milliseconds (`{ epochUnit }`). We do not attempt to parse arbitrary or ambiguous date formats.
|
|
42
|
+
- **Predictable outputs**: Helpers return normalized values (ISO strings, numbers, booleans, or arrays). Invalid input yields typed fallbacks (`""`, `null`, `false` or `[]`) instead of throwing.
|
|
41
43
|
- **No fuzzy parsing**: Avoid "throw everything at the wall" patterns found in permissive libraries. If you need permissive parsing, perform it outside of `@northguild/gmt` and then canonicalize to the strict shapes before calling into gmt.
|
|
42
44
|
- **Developer comfort with standards**: The library's goal is to make developers comfortable and deliberate with ISO 8601, IANA timezones, UTC instants, and Unix epochs by keeping APIs small and explicit.
|
|
43
45
|
|
|
@@ -59,7 +61,7 @@ Invalid input fallbacks are consistent across the library:
|
|
|
59
61
|
|
|
60
62
|
## Testing
|
|
61
63
|
|
|
62
|
-
Every function is exercised across **17 locales** and a full IANA timezone matrix. The CI pipeline runs the complete suite in **
|
|
64
|
+
Every function is exercised across **17 locales** and a full IANA timezone matrix. The CI pipeline runs the complete suite in **30 environments** — 3 Node versions (22, 24, 26) × 10 timezones spanning every UTC offset band from Pacific/Niue (−11:00) to Pacific/Apia (+14:00):
|
|
63
65
|
|
|
64
66
|
| Timezone | UTC Offset |
|
|
65
67
|
| ------------------- | --------------- |
|
|
@@ -92,7 +94,7 @@ GMT's test suite balances **thoroughness** against **maintenance burden** by tes
|
|
|
92
94
|
- **Non-string input tables** — functions that guard with `typeof x !== "string"` return the same sentinel for `null`, `undefined`, `123`, `true`, `[]`, and `{}`. We test one representative non-string per argument position rather than all six types × N positions. The collapse is safe because all non-string types hit the identical early-return code path.
|
|
93
95
|
- **Redundant permutations** — adjacent/disjoint/reversed interval cases that produce identical results are not duplicated across every function variant. The `plain/`, `zoned/`, `utc/`, and `unix/` families share the same mathematical behavior; each family gets the minimum set of cases needed to prove correctness.
|
|
94
96
|
|
|
95
|
-
**Result:**
|
|
97
|
+
**Result:** 36,975 tests across 674 files that exercise real behavior differences without redundant permutations. They run in CI as 1,109,250 executions — every one of them × 3 Node versions × 10 timezones.
|
|
96
98
|
|
|
97
99
|
## How GMT is tested, vs. the libraries it targets
|
|
98
100
|
|
|
@@ -108,11 +110,11 @@ GMT is measured directly against react-aria's **`@internationalized/date`**, **L
|
|
|
108
110
|
|
|
109
111
|
| Metric | GMT | `@internationalized/date` | Luxon | date-fns | Moment.js |
|
|
110
112
|
| ------------------------------- | -------------------------------------------------- | ------------------------------ | ------------------------------------ | ----------------------------------------- | -------------------------------- |
|
|
111
|
-
| Test files |
|
|
112
|
-
| Individual test cases | **
|
|
113
|
-
| Effective CI test<br>executions | **
|
|
114
|
-
| CI Node.js matrix | 22, 24
|
|
115
|
-
| CI timezone matrix | **10 zones ×
|
|
113
|
+
| Test files | 674 | 6 | 58 / 60<br>(2 didn't run<br>locally) | 256 | 191<br>(52 core +<br>139 locale) |
|
|
114
|
+
| Individual test cases | **36,975** | 386 | 1,222 | 3,213 | 3,901 |
|
|
115
|
+
| Effective CI test<br>executions | **1,109,250**<br>(36,975 × 3 Node<br>× 10 timezones) | 386<br>(×1 Node) | 4,888<br>(1,222 × 4 Node) | 3,213<br>(×1 Node) | 11,703<br>(3,901 × 3 Node) |
|
|
116
|
+
| CI Node.js matrix | 22, 24, 26 | n/a — tests<br>React 16–canary | 20, 22, 24, 25 | not explicit<br>(`node = "latest"`) | LTS, LTS-1,<br>latest |
|
|
117
|
+
| CI timezone matrix | **10 zones × 3**<br>**Node, full suite** | none found | none found | dedicated workflow,<br>zone scope unclear | 6 zones,<br>partial suite only |
|
|
116
118
|
| Locale test matrix | **17 locales**,<br>every locale fn | none found | none found | none found | none found |
|
|
117
119
|
| Real-browser CI | not yet | yes (Playwright) | not found | yes (Playwright) | not found |
|
|
118
120
|
| Maintenance | active | active | active | active | **maintenance<br>mode** |
|
|
@@ -129,7 +131,7 @@ GMT has **full functional parity** with all four comparison libraries, capabilit
|
|
|
129
131
|
| Interval/range math<br>(contains, overlap, union,<br>intersection, split, set ops) | ✅ Done | Luxon `Interval`,<br>date-fns `areIntervalsOverlapping` |
|
|
130
132
|
| DST disambiguation control<br>on construction _and_ arithmetic | ✅ Done — **differentiator** | None of the others expose<br>this on arithmetic |
|
|
131
133
|
| Locale-aware calendar helpers<br>(weekend, week start/end, day-of-week) | ✅ Done | `@internationalized/date` |
|
|
132
|
-
| Business-day arithmetic,<br>clamp/closest, time rounding
|
|
134
|
+
| Business-day arithmetic with<br>holiday calendars and roll conventions,<br>clamp/closest, time rounding | ✅ Done | `temporal-kit` (arithmetic only) |
|
|
133
135
|
| Interval rounding-out<br>(boundary count, from-duration) | ✅ Done | Luxon |
|
|
134
136
|
| Locale calendar metadata<br>(names, `hasDST`) | ✅ Done | Luxon `Info` |
|
|
135
137
|
| Overlap-day count, relative<br>rounding, DST transitions, hours-in-day | ✅ Done | date-fns, `@internationalized/date` |
|
|
@@ -142,40 +144,48 @@ Specific, sourced claims — not a repeat of the metrics above.
|
|
|
142
144
|
|
|
143
145
|
| Claim | The others |
|
|
144
146
|
| --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
|
145
|
-
| Only GMT runs its **entire** suite in CI<br>under a real `TZ` env var across 10<br>real-world zones ×
|
|
147
|
+
| Only GMT runs its **entire** suite in CI<br>under a real `TZ` env var across 10<br>real-world zones × 3 Node versions<br>(30 full-suite runs) | Luxon/`@internationalized/date`: no<br>CI timezone matrix. date-fns: zone<br>scope unclear. Moment.js: 6 zones,<br>partial suite only |
|
|
146
148
|
| Only GMT enforces a mandatory<br>17-locale test matrix on every<br>locale-aware function | No CI-level or systematic<br>locale-matrix testing found<br>in any of the four |
|
|
147
149
|
| Only GMT exposes explicit DST<br>disambiguation control on both<br>construction _and_ arithmetic | Luxon's docs call this explicitly<br>undefined; `@internationalized/date`<br>only covers construction, not arithmetic |
|
|
148
150
|
| Only GMT is Temporal-native with<br>zero `Date` usage, enforced by<br>3 dedicated lint packages | Luxon, date-fns, and Moment.js all<br>still wrap or depend on `Date` internally |
|
|
149
|
-
| GMT's effective CI test<br>executions exceed all four<br>competitors **combined**<br>by ~
|
|
151
|
+
| GMT's effective CI test<br>executions exceed all four<br>competitors **combined**<br>by ~55× | 1,109,250 vs. 386 + 4,888 + 3,213<br>+ 11,703 = 20,190 |
|
|
150
152
|
|
|
151
153
|
## Package Layout
|
|
152
154
|
|
|
153
|
-
|
|
155
|
+
Every public function, type and regex is a flat named export of the package root, beside
|
|
156
|
+
`Temporal`, `Intl` and `toTemporalInstant` re-exported from `@js-temporal/polyfill`. There are no
|
|
157
|
+
namespace objects: a namespace is a subpath.
|
|
154
158
|
|
|
155
159
|
```typescript
|
|
156
|
-
import {
|
|
157
|
-
Temporal,
|
|
158
|
-
duration,
|
|
159
|
-
plain,
|
|
160
|
-
zoned,
|
|
161
|
-
unix,
|
|
162
|
-
utc,
|
|
163
|
-
regex,
|
|
164
|
-
} from "@northguild/gmt";
|
|
160
|
+
import { addDate, getNow, formatRelativeZoned, Temporal } from "@northguild/gmt";
|
|
165
161
|
```
|
|
166
162
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
163
|
+
The fourteen namespace subpaths:
|
|
164
|
+
|
|
165
|
+
- `@northguild/gmt/calendar`: ISO week and ordinal dates, quarter and fiscal periods, zone-aware bucketing, and business calendars with holiday sets and roll conventions
|
|
166
|
+
- `@northguild/gmt/duration`: ISO 8601 duration string parsing, validation, and arithmetic
|
|
167
|
+
- `@northguild/gmt/instant`: the instant-plus-offset pair, and explicit resolution of zoneless local wall times
|
|
168
|
+
- `@northguild/gmt/interval`: half-open `[start, end)` interval algebra over instants — overlap, intersect, clamp, merge, subtract, split, sum
|
|
169
|
+
- `@northguild/gmt/plain`: timezone-free helpers
|
|
170
|
+
- `@northguild/gmt/precision`: nanosecond (`bigint`) instants, their JSON bridge, storage truncation, and foreign epoch bridges
|
|
171
|
+
- `@northguild/gmt/span`: elapsed and wall-clock durations between two timestamps, as raw numbers
|
|
172
|
+
- `@northguild/gmt/transport`: transit legs as exact elapsed time, arrivals rendered where they land, and dwell measured in local calendar days
|
|
173
|
+
- `@northguild/gmt/intermodal`: free time, demurrage and detention counted in the terminal's local days, with the charged dates behind every count, and the invoice, dispute and resolution deadline chain with every window a caller parameter
|
|
174
|
+
- `@northguild/gmt/zoned`: timezone-aware helpers
|
|
175
|
+
- `@northguild/gmt/unix`: Unix epoch (seconds or milliseconds) helpers
|
|
176
|
+
- `@northguild/gmt/utc`: UTC instant helpers
|
|
177
|
+
- `@northguild/gmt/regex`: low-level regex building blocks
|
|
178
|
+
- `@northguild/gmt/types`: the shared option and unit types
|
|
179
|
+
|
|
180
|
+
Every namespace subpath except `types` also re-exports `Temporal`, `Intl` and `toTemporalInstant`, so `import { Temporal, addZoned } from "@northguild/gmt/zoned"` needs no second import.
|
|
174
181
|
|
|
175
|
-
|
|
182
|
+
Every namespace except `regex` and `types` also exposes its modules as subpaths,
|
|
183
|
+
`@northguild/gmt/<namespace>/<module>`:
|
|
176
184
|
|
|
177
185
|
```typescript
|
|
178
|
-
import {
|
|
186
|
+
import { getZonedNow } from "@northguild/gmt/zoned";
|
|
187
|
+
import { addDateTime, diffDate } from "@northguild/gmt/plain/calculate";
|
|
188
|
+
import type { BusinessCalendar } from "@northguild/gmt/types";
|
|
179
189
|
```
|
|
180
190
|
|
|
181
191
|
## Quick Start
|
|
@@ -192,8 +202,8 @@ import {
|
|
|
192
202
|
isBeforeDateTime,
|
|
193
203
|
} from "@northguild/gmt";
|
|
194
204
|
|
|
195
|
-
addDate("2026-01-01", 90
|
|
196
|
-
// "2026-
|
|
205
|
+
addDate("2026-01-01", { days: 90 });
|
|
206
|
+
// "2026-04-01"
|
|
197
207
|
|
|
198
208
|
addBusinessDays("2024-03-15", 1);
|
|
199
209
|
// "2024-03-18" (skips weekend)
|
|
@@ -201,7 +211,7 @@ addBusinessDays("2024-03-15", 1);
|
|
|
201
211
|
subtractBusinessDays("2024-03-18", 1);
|
|
202
212
|
// "2024-03-15" (skips weekend)
|
|
203
213
|
|
|
204
|
-
diffDateTime("2024-03-17T12:00:00", "2024-03-17T12:30:00", "
|
|
214
|
+
diffDateTime("2024-03-17T12:00:00", "2024-03-17T12:30:00", "minutes");
|
|
205
215
|
// 30
|
|
206
216
|
|
|
207
217
|
areDatesEqual("2026-03-17", "2026-03-17T09:00:00");
|
|
@@ -211,7 +221,7 @@ isBeforeDateTime("2026-03-17T09:00:00", "2026-03-17T10:00:00");
|
|
|
211
221
|
// true
|
|
212
222
|
```
|
|
213
223
|
|
|
214
|
-
`add*`/`subtract*` accept an optional `overflow` (`"constrain"` (default) | `"reject"`) to control out-of-range results (e.g. adding a month to Jan 31), and `diff*` accept optional `smallestUnit`/`roundingIncrement`/`roundingMode` to round the computed difference:
|
|
224
|
+
`add*`/`subtract*` accept an optional `overflow` (`"constrain"` (default) | `"reject"`) to control out-of-range results (e.g. adding a month to Jan 31) — except `addTime`/`subtractTime`, which take no options argument because a clock time wraps (`addTime("23:00:00", { hours: 2 })` is `"01:00:00"`), as Temporal's `PlainTime#add` does — and `diff*` accept optional `smallestUnit`/`roundingIncrement`/`roundingMode` to round the computed difference:
|
|
215
225
|
|
|
216
226
|
```typescript
|
|
217
227
|
import { addDate, diffDate } from "@northguild/gmt";
|
|
@@ -219,7 +229,10 @@ import { addDate, diffDate } from "@northguild/gmt";
|
|
|
219
229
|
addDate("2024-01-31", { months: 1 }, { overflow: "reject" });
|
|
220
230
|
// "" — Feb 31 doesn't exist and overflow: "reject" refuses to clamp it
|
|
221
231
|
|
|
222
|
-
|
|
232
|
+
addDate("2024-02-29", { years: 1 });
|
|
233
|
+
// "2025-02-28" — "constrain" (the default) clamps to the last valid day, as Temporal does
|
|
234
|
+
|
|
235
|
+
diffDate("2023-01-01", "2023-01-10", "weeks", {
|
|
223
236
|
smallestUnit: "week",
|
|
224
237
|
roundingMode: "halfExpand",
|
|
225
238
|
});
|
|
@@ -237,14 +250,20 @@ setDate("2024-01-31", { month: 2 });
|
|
|
237
250
|
setZoned(
|
|
238
251
|
"2024-11-03T01:45:00-05:00[America/New_York]",
|
|
239
252
|
{ minute: 0 },
|
|
240
|
-
{
|
|
241
|
-
disambiguation: "reject",
|
|
242
|
-
},
|
|
253
|
+
{ disambiguation: "reject" },
|
|
243
254
|
);
|
|
244
|
-
// "" — offset defaults to "
|
|
255
|
+
// "2024-11-03T01:00:00-05:00[America/New_York]" — offset defaults to "prefer", as Temporal's
|
|
256
|
+
// ZonedDateTime#with does: the source's -05:00 is still valid, so it is kept
|
|
257
|
+
|
|
258
|
+
setZoned(
|
|
259
|
+
"2024-11-03T01:45:00-05:00[America/New_York]",
|
|
260
|
+
{ minute: 0 },
|
|
261
|
+
{ disambiguation: "reject", offset: "ignore" },
|
|
262
|
+
);
|
|
263
|
+
// "" — offset "ignore" re-resolves the repeated 01:00, and "reject" fires on the fall-back overlap
|
|
245
264
|
```
|
|
246
265
|
|
|
247
|
-
`setZoned`/`setUnix
|
|
266
|
+
`setZoned`/`setUnix` also accept `disambiguation` and `offset` for DST gap/overlap control (`setUtc` takes only `overflow`: a UTC wall clock is never ambiguous) — see [DST Disambiguation](../../docs/dst-disambiguation.md).
|
|
248
267
|
|
|
249
268
|
`cycleDate`/`cycleDateTime`/`cycleTime`/`cycleZoned` adjust a single field and **wrap** at that field's own min/max instead of carrying into the next larger field — the datepicker-segment-editing primitive `add*` can't express, since overflowing into the next field is exactly what `add*` is for:
|
|
250
269
|
|
|
@@ -262,9 +281,9 @@ cycleZoned("2024-03-10T01:30:00-06:00[America/Chicago]", "hour", 1);
|
|
|
262
281
|
// gap; disambiguation ("compatible" by default) resolves it the same way setZoned does
|
|
263
282
|
```
|
|
264
283
|
|
|
265
|
-
`cycleZoned` also accepts `disambiguation` and `offset` (default `offset: "
|
|
284
|
+
`cycleZoned` also accepts `disambiguation` and `offset` (default `offset: "prefer"`) for the same DST gap/overlap control as `setZoned` — see [DST Disambiguation](../../docs/dst-disambiguation.md). `options.round` on any of the four steps to the next multiple of `amount` rather than rounding to the nearest one, matching `@internationalized/date`'s `CycleOptions.round`.
|
|
266
285
|
|
|
267
|
-
`isWeekend`/`isZonedWeekend` check locale-specific weekend days (via `Intl.Locale
|
|
286
|
+
`isWeekend`/`isZonedWeekend` check locale-specific weekend days (via `Intl.Locale#getWeekInfo()`, falling back to `weekInfo`) rather than assuming Saturday/Sunday:
|
|
268
287
|
|
|
269
288
|
```typescript
|
|
270
289
|
import { isWeekend, isZonedWeekend } from "@northguild/gmt";
|
|
@@ -279,7 +298,7 @@ isZonedWeekend("2024-02-04T10:00:00+02:00[Asia/Jerusalem]", "he-IL");
|
|
|
279
298
|
// false (Sunday isn't part of he-IL's weekend)
|
|
280
299
|
```
|
|
281
300
|
|
|
282
|
-
`isBusinessDay`
|
|
301
|
+
`isBusinessDay` is the complement to locale-aware `isWeekend`, and shares its weekend rule with `addBusinessDays`/`subtractBusinessDays`. Called with one argument it uses fixed ISO Monday–Friday business days (Mon=1 … Fri=5), locale-agnostic and with no holidays; pass a `BusinessCalendar` to state the weekend and holidays yourself:
|
|
283
302
|
|
|
284
303
|
```typescript
|
|
285
304
|
import { isBusinessDay } from "@northguild/gmt";
|
|
@@ -289,8 +308,81 @@ isBusinessDay("2024-02-05");
|
|
|
289
308
|
|
|
290
309
|
isBusinessDay("2024-02-10");
|
|
291
310
|
// false (Saturday)
|
|
311
|
+
|
|
312
|
+
isBusinessDay("2024-07-04", {
|
|
313
|
+
weekend: [6, 7],
|
|
314
|
+
holidays: ["2024-07-04"],
|
|
315
|
+
timeZone: "America/New_York",
|
|
316
|
+
});
|
|
317
|
+
// false (a holiday, though it's a Thursday)
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Business calendars and roll conventions
|
|
321
|
+
|
|
322
|
+
A `BusinessCalendar` is `{ weekend: number[], holidays: string[], timeZone: string }`. The weekend is explicit ISO weekday numbers because Saturday–Sunday is not universal — much of the Middle East is Friday–Saturday, and some markets keep a one-day weekend. Holidays are yours to supply: GMT bundles no holiday table on the default import path, because holiday data is jurisdictional and changes annually, sometimes with days of notice. `timeZone` records which locality the calendar describes; the business-day functions take and return local dates and never read it, so a caller holding an instant reduces it with `floorToZone` first.
|
|
323
|
+
|
|
324
|
+
`isBusinessDay`, `addBusinessDays` and `subtractBusinessDays` all take a calendar as an optional trailing argument. The rest of the family requires one — there is no default weekend to fall back on:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import {
|
|
328
|
+
businessDaysBetween,
|
|
329
|
+
nextBusinessDay,
|
|
330
|
+
previousBusinessDay,
|
|
331
|
+
rollDate,
|
|
332
|
+
mergeCalendars,
|
|
333
|
+
isValidBusinessCalendar,
|
|
334
|
+
} from "@northguild/gmt";
|
|
335
|
+
|
|
336
|
+
const nyse = {
|
|
337
|
+
weekend: [6, 7],
|
|
338
|
+
holidays: ["2024-05-31", "2024-07-04"],
|
|
339
|
+
timeZone: "America/New_York",
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
businessDaysBetween("2024-07-01", "2024-07-05", nyse);
|
|
343
|
+
// 3 — start exclusive, end inclusive, and 4 July is a holiday
|
|
344
|
+
|
|
345
|
+
nextBusinessDay("2024-07-03", nyse);
|
|
346
|
+
// "2024-07-05" — strictly after, skipping the holiday
|
|
347
|
+
|
|
348
|
+
previousBusinessDay("2024-07-05", nyse);
|
|
349
|
+
// "2024-07-03"
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
`rollDate` moves a date onto a working day by an explicit convention — `following`, `modifiedFollowing`, `preceding`, `modifiedPreceding`, `endOfMonth` or `none`. `following` and `preceding` are on-or-after and on-or-before, so they leave a working day alone; `nextBusinessDay`/`previousBusinessDay` are the strict neighbours:
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
rollDate("2024-05-31", "following", nyse);
|
|
356
|
+
// "2024-06-03" — forward past the weekend
|
|
357
|
+
|
|
358
|
+
rollDate("2024-05-31", "modifiedFollowing", nyse);
|
|
359
|
+
// "2024-05-30" — backward instead, because forward leaves May
|
|
360
|
+
|
|
361
|
+
rollDate("2024-03-15", "endOfMonth", nyse);
|
|
362
|
+
// "2024-03-29" — March's last working day; March ends on a Sunday
|
|
363
|
+
|
|
364
|
+
rollDate("2024-06-01", "none", nyse);
|
|
365
|
+
// "2024-06-01" — unadjusted, Saturday or not
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
`mergeCalendars` composes jurisdictions: weekend rules and holidays union, so a date survives only if it is a working day in **every** input. That is the two-currency intersection FX settlement needs, and the two-port one an intermodal move needs:
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
const london = { weekend: [6, 7], holidays: ["2024-05-06"], timeZone: "Europe/London" };
|
|
372
|
+
|
|
373
|
+
const both = mergeCalendars([nyse, london]);
|
|
374
|
+
// { weekend: [6, 7], holidays: ["2024-05-06", "2024-05-31", "2024-07-04"], timeZone: "America/New_York" }
|
|
375
|
+
// timeZone is the first calendar's; the merged set spans localities that may disagree
|
|
376
|
+
|
|
377
|
+
isBusinessDay("2024-05-06", both);
|
|
378
|
+
// false — a UK holiday closes the merged calendar too
|
|
379
|
+
|
|
380
|
+
mergeCalendars([]);
|
|
381
|
+
// null
|
|
292
382
|
```
|
|
293
383
|
|
|
384
|
+
`isValidBusinessCalendar` and `isValidRollConvention` narrow a candidate, so a misconfigured calendar or contract term can be told apart from bad date input when a function returns its sentinel.
|
|
385
|
+
|
|
294
386
|
`isRelativeDay`/`isThisUnit`/`isPast`/`isFuture` are now-relative predicates — `isRelativeDay` subsumes `isToday`/`isYesterday`/`isTomorrow`, `isThisUnit` subsumes `isThisWeek`/`isThisMonth`/`isThisYear`. They compare against `getToday()`, so they depend on the **system clock and system timeZone**; the zoned variants (`isZonedRelativeDay`, `isZonedThisUnit`, `isZonedPast`, `isZonedFuture`) resolve "today"/"now" in the value's own timeZone instead, for deterministic results regardless of the host machine's timeZone:
|
|
295
387
|
|
|
296
388
|
```typescript
|
|
@@ -375,8 +467,8 @@ getLocaleDayOfWeek("2024-02-25", "en-US");
|
|
|
375
467
|
getLocaleDayOfWeek("2024-02-26", "fr-FR");
|
|
376
468
|
// 0 (Monday = first day of fr-FR week)
|
|
377
469
|
|
|
378
|
-
getLocaleDayOfWeek("2024-02-24", "
|
|
379
|
-
// 0 (Saturday = first day of
|
|
470
|
+
getLocaleDayOfWeek("2024-02-24", "ar-EG");
|
|
471
|
+
// 0 (Saturday = first day of ar-EG week)
|
|
380
472
|
|
|
381
473
|
getLocaleZonedDayOfWeek("2024-02-25T12:00:00+00:00[UTC]", "en-US");
|
|
382
474
|
// 0
|
|
@@ -447,153 +539,127 @@ Supported tokens include `yyyy`/`MM`/`dd`/`HH`/`mm`/`ss`/`SSS` for fixed-width f
|
|
|
447
539
|
|
|
448
540
|
### Calendar systems
|
|
449
541
|
|
|
450
|
-
|
|
542
|
+
`convertDateToCalendar` expresses a date in another calendar system. GMT writes the standard form, exactly what `Temporal.PlainDate.prototype.toString()` writes: the ISO 8601 date, then an RFC 9557 `[u-ca=<id>]` annotation naming the calendar. The digits stay ISO. The annotation says which calendar the date is presented and computed in (RFC 9557 §3.3), so the string means the same date to GMT, to Temporal and to any other RFC 9557 parser.
|
|
451
543
|
|
|
452
544
|
```typescript
|
|
453
545
|
import { convertDateToCalendar } from "@northguild/gmt";
|
|
454
546
|
|
|
455
547
|
convertDateToCalendar("2024-10-03", "hebrew");
|
|
456
|
-
// "
|
|
548
|
+
// "2024-10-03[u-ca=hebrew]" — Rosh Hashanah 5785, written as its ISO date
|
|
457
549
|
|
|
458
|
-
convertDateToCalendar("
|
|
459
|
-
// "2024-10-03" —
|
|
550
|
+
convertDateToCalendar("2024-10-03[u-ca=hebrew]", "iso8601");
|
|
551
|
+
// "2024-10-03" — iso8601 writes no annotation
|
|
460
552
|
|
|
461
|
-
convertDateToCalendar("2024-10-03", "
|
|
462
|
-
// "
|
|
553
|
+
convertDateToCalendar("2024-10-03", "gregory");
|
|
554
|
+
// "2024-10-03[u-ca=gregory]" — gregory is its own calendar, so it is annotated
|
|
463
555
|
|
|
464
556
|
convertDateToCalendar("invalid", "hebrew");
|
|
465
557
|
// ""
|
|
466
558
|
```
|
|
467
559
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
Hebrew years can run 12 or 13 months (7 leap years per 19-year Metonic cycle insert a 13th month, Adar I, before the regular Adar); `convertDateToCalendar` resolves this the same way Temporal does internally, via ordinal month numbers (`1`-`13`) rather than fixed month names, so no month-counting logic lives in GMT itself.
|
|
560
|
+
`CalendarSystem` is `"iso8601" | "gregory" | "hebrew" | "islamic-civil" | "islamic-tbla" | "islamic-umalqura" | "japanese" | "buddhist" | "roc" | "persian" | "indian" | "ethiopic" | "ethioaa" | "coptic"`: the canonical BCP 47 / CLDR `calendar.xml` ids Temporal uses, in the annotation and as function arguments. An alias or another letter case is canonicalized the way Temporal's `withCalendar` does it (`"ethiopic-amete-alem"` writes `[u-ca=ethioaa]`, `[u-ca=HEBREW]` reads as `hebrew`), and the canonical id is always written. `gregorian`, `taiwan` and `islamic-tabular` are not calendar ids, so they return the sentinel. `chinese`, `dangi`, `islamic` and `islamic-rgsa` are not supported.
|
|
471
561
|
|
|
472
|
-
|
|
562
|
+
Every calendar-accepting function reads annotations the way `Temporal.PlainDate.from` / `Temporal.ZonedDateTime.from` read them. The critical flag is accepted and not written back (`"2024-10-03[!u-ca=hebrew]"` converts to `"2024-10-03[u-ca=hebrew]"`), and an unknown critical annotation is rejected. `;era=` is not RFC 9557 syntax and is rejected. A year is four digits, or a sign and six (`"+275760-09-13[u-ca=hebrew]"`), across Temporal's whole range, `-271821-04-19` to `+275760-09-13`.
|
|
473
563
|
|
|
474
|
-
|
|
475
|
-
- `"islamic-tabular"` — the same style of fixed arithmetic cycle, but a Thursday epoch one day earlier (`1 AH = 622-07-18`); maps to Temporal's `"islamic-tbla"` calendar id internally, though GMT's own string annotation always reads `[u-ca=islamic-tabular]`.
|
|
476
|
-
- `"islamic-umalqura"` — the Saudi civil calendar, based on Umm al-Qura University's own published tables rather than a fixed arithmetic rule. This is **not** approximated by the tabular variant's math — `convertDateToCalendar("2020-02-24", "islamic-umalqura")` returns `"1441-06-30[u-ca=islamic-umalqura]"` while the same input under `"islamic-tabular"` returns `"1441-07-01[u-ca=islamic-tabular]"`, a genuine one-day divergence, not a rounding difference.
|
|
564
|
+
**Calendar fields are read values, never string content.** The Hebrew year 5785, the Japanese era `reiwa` and year 6, and the Umm al-Qura day number are all properties of the date, so GMT never puts them in a string. No standard defines a machine-readable date string with calendar-native digits, and such a string would be ambiguous: `5785-01-01[u-ca=hebrew]` is also a valid RFC 9557 string for ISO year 5785. When you need a field for display, format the date with `Intl.DateTimeFormat` and its `calendar` option, or read it from Temporal, which every GMT entry point re-exports:
|
|
477
565
|
|
|
478
|
-
|
|
566
|
+
```typescript
|
|
567
|
+
import { Temporal } from "@northguild/gmt";
|
|
479
568
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
- `"indian"` — the Indian National Calendar (Saka era, epoch 78 CE). Also not offset-only: its leap-year alignment follows the Gregorian rule rather than an independent cycle — the calendar's first month is 31 days in a Gregorian leap year, 30 otherwise, which is why its Saka-year boundary (`convertDateToCalendar("2024-03-20", "indian")` → `"1945-12-30[u-ca=indian]"`, `convertDateToCalendar("2024-03-21", "indian")` → `"1946-01-01[u-ca=indian]"`) doesn't land on a fixed day-of-year every year.
|
|
484
|
-
- `"japanese"` — era-based: the year resets to `1` at each imperial era change (Meiji, Taishō, Shōwa, Heisei, Reiwa). This is the one calendar where GMT's annotated string carries an era-relative year instead of the calendar's plain native year, and needs its own explanation below.
|
|
569
|
+
Temporal.PlainDate.from("2024-10-03[u-ca=hebrew]").year; // 5785
|
|
570
|
+
Temporal.PlainDate.from("2024-10-03[u-ca=japanese]").eraYear; // 6
|
|
571
|
+
```
|
|
485
572
|
|
|
486
|
-
|
|
573
|
+
The three Islamic variants are different calendars, not spellings of one. On ISO 2020-02-24, `islamic-civil` (Friday epoch) reads day 29 of month 6 of 1441, `islamic-tbla` (the same arithmetic cycle with a Thursday epoch) reads day 1 of month 7, and `islamic-umalqura` (the Saudi civil calendar, from Umm al-Qura University's published tables) reads day 30 of month 6. Month arithmetic follows each calendar's own months, so the same `+1 month` lands on different ISO dates:
|
|
487
574
|
|
|
488
575
|
```typescript
|
|
489
|
-
|
|
490
|
-
// "0006-10-03[u-ca=japanese;era=reiwa]" — year 6 of the Reiwa era, not the proleptic 2024
|
|
576
|
+
import { addDate } from "@northguild/gmt";
|
|
491
577
|
|
|
492
|
-
|
|
493
|
-
// "
|
|
578
|
+
addDate("2020-02-24[u-ca=islamic-umalqura]", { months: 1 });
|
|
579
|
+
// "2020-03-24[u-ca=islamic-umalqura]"
|
|
494
580
|
|
|
495
|
-
|
|
496
|
-
// "
|
|
581
|
+
addDate("2020-02-24[u-ca=islamic-tbla]", { months: 1 });
|
|
582
|
+
// "2020-03-25[u-ca=islamic-tbla]"
|
|
497
583
|
```
|
|
498
584
|
|
|
499
|
-
|
|
585
|
+
`ethiopic` and `coptic` compute in `ethioaa`. The three share months, days and arithmetic and differ only by a constant year offset, and `@js-temporal/polyfill` 0.5.1 throws reading `ethiopic` and `coptic` fields under ICU 78 or later (`Temporal.PlainDate.from("2024-10-03[u-ca=ethiopic]").year` throws there). GMT's results are the same either way, and only the written id differs. Where the polyfill or the runtime's ICU computes a calendar wrongly (Buddhist before 1582, Hebrew years ≤ 0, Indian dates before ISO year 1, dates near either range limit), GMT's arithmetic computes the specified answer instead. Each correction probes the runtime once and stays inactive where the runtime is already right.
|
|
500
586
|
|
|
501
|
-
|
|
587
|
+
#### Calendar-aware interval and duration arithmetic
|
|
502
588
|
|
|
503
|
-
|
|
504
|
-
- `"ethiopic-amete-alem"` — the same calendar, but always counted continuously from the Amete Alem epoch (~5493 BCE), never resetting: `convertDateToCalendar("2024-10-03", "ethiopic-amete-alem")` → `"7517-01-23[u-ca=ethiopic-amete-alem]"`.
|
|
505
|
-
- `"coptic"` — the Coptic Orthodox calendar: same 13-month structure, its own epoch (the Diocletian/Martyrs era, AD 284): `convertDateToCalendar("2024-10-03", "coptic")` → `"1741-01-23[u-ca=coptic]"`.
|
|
589
|
+
A calendar-annotated date feeds `addDate`/`subtractDate`/`diffDate`/`diffDateAsDuration` and every `Date`-suffixed `plain/interval/*` function (`intervalContainsDate`, `intervalCountDate`, `splitIntervalByUnitDate`, and the rest). Calendar units ("add 1 month") resolve in the value's own calendar:
|
|
506
590
|
|
|
507
|
-
|
|
591
|
+
```typescript
|
|
592
|
+
import { addDate, diffDate, diffDateAsDuration, durationAs, intervalCountDate } from "@northguild/gmt";
|
|
508
593
|
|
|
509
|
-
|
|
594
|
+
addDate("2024-02-24[u-ca=hebrew]", { months: 1 });
|
|
595
|
+
// "2024-03-25[u-ca=hebrew]" — 15 Adar I 5784 to 15 Adar II; addDate("2024-02-24", { months: 1 }) is "2024-03-24"
|
|
510
596
|
|
|
511
|
-
|
|
597
|
+
intervalCountDate("2023-09-16[u-ca=hebrew]", "2024-10-03[u-ca=hebrew]", "month");
|
|
598
|
+
// 13 — Hebrew leap year 5784 has 13 months; the same ISO span touches 14 ISO months
|
|
512
599
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
// "5784-07-15[u-ca=hebrew]" — Adar I (a leap-only 30-day month) -> Adar
|
|
600
|
+
diffDate("2024-03-11[u-ca=hebrew]", "2024-04-10[u-ca=hebrew]", "months");
|
|
601
|
+
// 1 — 1 Adar II to 2 Nisan; the bare ISO dates are 0 whole months apart
|
|
516
602
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
"5785-01-01[u-ca=hebrew]",
|
|
520
|
-
"month",
|
|
521
|
-
);
|
|
522
|
-
// 13 — a Hebrew leap year crosses 13 month boundaries, not 12 (ISO's answer for the same span)
|
|
603
|
+
diffDateAsDuration("2024-08-31[u-ca=buddhist]", "2024-09-30[u-ca=buddhist]", "months");
|
|
604
|
+
// "P30D" — not "P1M": a month counts only once the end reaches the same day of the next month
|
|
523
605
|
|
|
524
|
-
|
|
525
|
-
//
|
|
606
|
+
durationAs("P1Y", "days", { relativeTo: "2023-09-16[u-ca=hebrew]" });
|
|
607
|
+
// 383 — Hebrew leap year 5784; relativeTo "2023-09-16" gives 366
|
|
526
608
|
```
|
|
527
609
|
|
|
528
|
-
|
|
610
|
+
Month and year differences follow Temporal's `NonISODateSurpasses` in every calendar, so a span from a month's last day into a shorter month is days, not a month. This matches the ISO calendar (`diffDateAsDuration("2024-08-31", "2024-09-30", "months")` is `"P30D"`), and applies to `diffDate*`, `intervalLength*`, `intervalCount*` and `diffZoned*` alike.
|
|
529
611
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
```
|
|
612
|
+
`utc/` reads a UTC string as `Temporal.Instant.from` does, so a `[u-ca=...]` annotation is ignored there: `isValidUtc("2024-01-01T00:00:00Z[u-ca=hebrew]")` is `true`. `duration/`'s `relativeTo` reads a calendar-annotated string as Temporal's `ParseTemporalRelativeToString` does: zoned when it carries a time zone annotation, otherwise a date.
|
|
613
|
+
|
|
614
|
+
Two values that name different calendars follow Temporal's `CalendarEquals`. A bare ISO string names `iso8601`.
|
|
534
615
|
|
|
535
|
-
|
|
616
|
+
- **Differences return the sentinel** on a mismatch, as Temporal's `until` throws: `diffDate`, `diffDateAsDuration`, `intervalCountDate`, `intervalLengthDate`, `splitIntervalByUnitDate` and `intervalOverlappingDaysDate`.
|
|
617
|
+
- **Ordering accepts mixed calendars**, as `Temporal.PlainDate.compare` has no calendar check: `intervalContainsDate`, `intervalsOverlapDate`, `intervalAbutsDate`, `intervalEngulfsDate` and `isValidDateInterval`.
|
|
618
|
+
- **Functions that return a date value** (`intervalUnionDate`, `intervalIntersectionDate`, `intervalDifferenceDate`, `intervalXorDate`, `intervalXorAllDate`, `mergeIntervalsDate`, `intervalDivideEquallyDate`, `intervalSplitAtDate`) require one shared calendar and return `null`/`[]` on a mismatch, because no calendar can be chosen for the output.
|
|
536
619
|
|
|
537
|
-
|
|
620
|
+
```typescript
|
|
621
|
+
intervalCountDate("2023-09-16[u-ca=hebrew]", "2024-10-03", "month");
|
|
622
|
+
// null — hebrew and iso8601
|
|
623
|
+
|
|
624
|
+
intervalContainsDate("2023-09-16[u-ca=hebrew]", "2024-10-03", "2024-01-01[u-ca=roc]");
|
|
625
|
+
// true — ordering compares the ISO dates
|
|
626
|
+
```
|
|
538
627
|
|
|
539
628
|
#### Calendar-aware zoned datetimes
|
|
540
629
|
|
|
541
|
-
A calendar-annotated `ZonedDateTime` string
|
|
542
|
-
plain grammar above:
|
|
630
|
+
A calendar-annotated `ZonedDateTime` string is the RFC 9557 form `Temporal.ZonedDateTime.prototype.toString()` writes: the time zone annotation first, then the calendar (RFC 9557 §4.1):
|
|
543
631
|
|
|
544
632
|
```
|
|
545
|
-
<
|
|
633
|
+
<date>T<time><offset>[<timeZone>][u-ca=<id>]
|
|
546
634
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
7517-12-30T00:30:00-04:00[u-ca=ethiopic-amete-alem][America/Santiago]
|
|
635
|
+
2024-02-24T14:30:00-05:00[America/New_York][u-ca=hebrew]
|
|
636
|
+
2019-04-30T12:00:00+09:00[Asia/Tokyo][u-ca=japanese]
|
|
550
637
|
```
|
|
551
638
|
|
|
552
|
-
`convertZonedToCalendar` produces it, and `isValidCalendarZonedDateTime` validates it:
|
|
639
|
+
`convertZonedToCalendar` produces it, and `isValidCalendarZonedDateTime` validates it. A calendar annotation before the zone is not RFC 9557 and returns the sentinel:
|
|
553
640
|
|
|
554
641
|
```typescript
|
|
642
|
+
import { addZoned, convertZonedToCalendar } from "@northguild/gmt";
|
|
643
|
+
|
|
555
644
|
convertZonedToCalendar("2024-10-03T14:30:45-04:00[America/New_York]", "hebrew");
|
|
556
|
-
// "
|
|
645
|
+
// "2024-10-03T14:30:45-04:00[America/New_York][u-ca=hebrew]"
|
|
557
646
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
//
|
|
647
|
+
convertZonedToCalendar("2024-10-03T14:30:45-04:00[u-ca=hebrew][America/New_York]", "iso8601");
|
|
648
|
+
// "" — the calendar annotation must follow the zone
|
|
649
|
+
|
|
650
|
+
addZoned("2024-02-24T14:30:00-05:00[America/New_York][u-ca=hebrew]", { months: 1 });
|
|
651
|
+
// "2024-03-25T14:30:00-04:00[America/New_York][u-ca=hebrew]"
|
|
652
|
+
// Adar I -> Adar II AND EST -> EDT, resolved in one call. No ordering of a plain/ calendar
|
|
563
653
|
// operation and a zoned/ conversion produces this: do the calendar step first and DST is
|
|
564
654
|
// applied to an already-resolved wall time; do the zoned step first and there is no calendar
|
|
565
655
|
// left to step in.
|
|
656
|
+
```
|
|
566
657
|
|
|
567
|
-
addZoned
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
> **The `[u-ca=...]` segment comes BEFORE `[timeZone]` — the reverse of RFC 9557.** This is
|
|
574
|
-
> deliberate. GMT's digits are calendar-native (Hebrew year 5784, not ISO year 5784), so the
|
|
575
|
-
> string is never valid RFC 9557 to begin with, and the `;era=` suffix is not valid RFC 9557 at
|
|
576
|
-
> any ordering. Writing it in RFC order is actively dangerous:
|
|
577
|
-
> `Temporal.ZonedDateTime.from("5784-01-01T14:30:00-05:00[America/New_York][u-ca=hebrew]")`
|
|
578
|
-
> _succeeds_, silently reading 5784 as an ISO year — a ~3760-year misparse with no error. GMT's
|
|
579
|
-
> ordering makes that shape uniformly rejected instead.
|
|
580
|
-
|
|
581
|
-
Scope: `addZoned`, `subtractZoned`, `diffZoned`, `diffZonedAsDuration`, `convertZonedToCalendar`,
|
|
582
|
-
and the `zoned/interval/*` family. Everything else in `zoned/` still rejects the annotation —
|
|
583
|
-
`isValidZonedDateTime` is unchanged, so a function that has not opted in fails closed rather than
|
|
584
|
-
silently answering in the wrong calendar. `addZonedBusinessDays`/`subtractZonedBusinessDays` stay
|
|
585
|
-
out by design: day-of-week is ISO-fixed in every supported calendar, so a tag would change nothing
|
|
586
|
-
while implying it might.
|
|
587
|
-
|
|
588
|
-
Mixed-calendar endpoints follow the same split as `plain/`: ordering functions
|
|
589
|
-
(`intervalContainsZoned`, `intervalsOverlapZoned`, `intervalAbutsZoned`, `intervalEngulfsZoned`,
|
|
590
|
-
`intervalOverlappingDaysZoned`, `isValidCalendarZonedInterval`) accept them; the eight
|
|
591
|
-
value-returning set operations require one shared calendar and return their sentinel on a
|
|
592
|
-
mismatch. Measurement functions (`diffZoned`, `diffZonedAsDuration`, `intervalCountZoned`,
|
|
593
|
-
`intervalLengthZoned`, `splitIntervalByUnitZoned`) measure in the endpoints' shared calendar when
|
|
594
|
-
both tags match and fall back to Gregorian otherwise — mandatory here rather than merely
|
|
595
|
-
convenient, because `Temporal.ZonedDateTime.prototype.until` throws across mismatched calendars
|
|
596
|
-
for _every_ unit, including pure time units like hours.
|
|
658
|
+
Scope: `addZoned`, `subtractZoned`, `diffZoned`, `diffZonedAsDuration`, `convertZonedToCalendar`, and the `zoned/interval/*` family. Everything else in `zoned/`, and `isValidZonedDateTime`, accepts `[u-ca=iso8601]` and rejects any other calendar, so a function that has not opted in fails closed rather than silently answering in the wrong calendar. `addZonedBusinessDays`/`subtractZonedBusinessDays` stay out by design: day-of-week is ISO-fixed in every supported calendar, so a tag would change nothing while implying it might.
|
|
659
|
+
|
|
660
|
+
Mixed-calendar endpoints follow the same rules as `plain/`. Ordering functions (`intervalContainsZoned`, `intervalsOverlapZoned`, `intervalAbutsZoned`, `intervalEngulfsZoned`, `isValidCalendarZonedInterval`) accept them. The value-returning set operations require one shared calendar. Differences (`diffZoned`, `diffZonedAsDuration`, `intervalCountZoned`, `intervalLengthZoned`, `splitIntervalByUnitZoned`, `intervalOverlappingDaysZoned`) return their sentinel on a mismatch for every unit, including hours, as `Temporal.ZonedDateTime.prototype.until` throws across calendars.
|
|
661
|
+
|
|
662
|
+
**Migrating strings written before 1.16.0.** Earlier releases wrote the calendar's own year, month and day (`"5785-01-01[u-ca=hebrew]"` for ISO 2024-10-03), a `;era=` suffix, the zoned calendar annotation before the zone, and the ids `gregorian`, `taiwan`, `islamic-tabular` and `ethiopic-amete-alem` (now `iso8601`, `roc`, `islamic-tbla` and `ethioaa`). No converter exists: most old strings are also valid RFC 9557 strings for a different ISO date, and the two readings cannot be told apart. `convertDateToCalendar("5785-01-01[u-ca=hebrew]", "iso8601")` returns `"5785-01-01"`. Regenerate each stored value from its ISO date with `convertDateToCalendar` or `convertZonedToCalendar`.
|
|
597
663
|
|
|
598
664
|
### Durations
|
|
599
665
|
|
|
@@ -684,6 +750,8 @@ diffDateAsDuration("2024-03-10", "2024-04-05", "days");
|
|
|
684
750
|
|
|
685
751
|
### Intervals
|
|
686
752
|
|
|
753
|
+
> **One boundary model.** Every interval function in this section (`intervalsOverlapDate`, `intervalDifferenceUtc`, …) reads an interval as half-open, `[start, end)`: `start` is inside and `end` is not, as in the `interval/` namespace described under [Interval algebra](#interval-algebra) (SQL:2011 closed-open `PERIOD`, RFC 5545's non-inclusive `DTEND`, EWD831). Touching intervals share no instant, so they abut rather than overlap, and no function steps an endpoint by one unit. For a `Date` interval that means `end` is the first day **after** the period: pass `"2024-07-01"`, not `"2024-06-30"`, for the first half of 2024 (`addDate(lastDay, { days: 1 })`). Before 1.16.0 most positional families read intervals as closed `[start, end]`.
|
|
754
|
+
|
|
687
755
|
Interval and range validators are available in two API shapes — **range validators** (matching `isValidDateRange`'s `{ value1, value2, options? }` object-param shape) and **interval validators** (`(start, end)` positional args, `start <= end` always):
|
|
688
756
|
|
|
689
757
|
```typescript
|
|
@@ -734,8 +802,8 @@ isValidZonedRange({
|
|
|
734
802
|
|
|
735
803
|
Interval containment checks (`intervalContains*`) test whether a point or inner interval falls within an outer interval. Each supports two modes via an optional fourth argument:
|
|
736
804
|
|
|
737
|
-
- 3-arg: `intervalContains(start, end, point)` — true when `start <= point
|
|
738
|
-
- 4-arg: `intervalContains(start, end, innerStart, innerEnd)` — true when the inner interval is
|
|
805
|
+
- 3-arg: `intervalContains(start, end, point)` — true when `start <= point < end`
|
|
806
|
+
- 4-arg: `intervalContains(start, end, innerStart, innerEnd)` — true when the inner interval lies within the outer one and overlaps it, so an inner interval may share the outer `end`, but an empty inner interval at `end` is not contained
|
|
739
807
|
|
|
740
808
|
```typescript
|
|
741
809
|
import {
|
|
@@ -751,6 +819,9 @@ import {
|
|
|
751
819
|
intervalContainsDate("2024-01-01", "2024-12-31", "2024-06-15");
|
|
752
820
|
// true
|
|
753
821
|
|
|
822
|
+
intervalContainsDate("2024-01-01", "2024-12-31", "2024-12-31");
|
|
823
|
+
// false (the end is excluded)
|
|
824
|
+
|
|
754
825
|
intervalContainsTime("09:00:00", "17:00:00", "12:00:00");
|
|
755
826
|
// true
|
|
756
827
|
|
|
@@ -779,9 +850,9 @@ intervalContainsTime("09:00:00", "17:00:00", "10:00:00", "16:00:00");
|
|
|
779
850
|
// true
|
|
780
851
|
```
|
|
781
852
|
|
|
782
|
-
All interval containment checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals,
|
|
853
|
+
All interval containment checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, or an epoch outside the `unix/` grammar: not a safe integer or a digit string).
|
|
783
854
|
|
|
784
|
-
`intervalsOverlap*` checks whether two intervals share any
|
|
855
|
+
`intervalsOverlap*` checks whether two intervals share any instant. Returns `false` when they are disjoint. Intervals that touch (one's end equals the other's start) share no instant, so they do **not** overlap, exactly as `intervalsOverlap` in `interval/`:
|
|
785
856
|
|
|
786
857
|
```typescript
|
|
787
858
|
import {
|
|
@@ -797,23 +868,23 @@ intervalsOverlapDate("2024-01-01", "2024-06-30", "2024-04-01", "2024-12-31");
|
|
|
797
868
|
// true
|
|
798
869
|
|
|
799
870
|
intervalsOverlapDate("2024-01-01", "2024-06-30", "2024-06-30", "2024-12-31");
|
|
800
|
-
// false (
|
|
871
|
+
// false (touching — 2024-06-30 is outside the first interval)
|
|
801
872
|
|
|
802
873
|
intervalsOverlapUnix(0, 1700000000, 1000000, 2000000);
|
|
803
874
|
// true
|
|
804
875
|
|
|
805
876
|
intervalsOverlapUtc(
|
|
806
|
-
"2024-01-
|
|
807
|
-
"2024-
|
|
808
|
-
"2024-
|
|
809
|
-
"2024-
|
|
877
|
+
"2024-01-01T09:00:00Z",
|
|
878
|
+
"2024-01-01T17:00:00Z",
|
|
879
|
+
"2024-01-01T17:00:00Z",
|
|
880
|
+
"2024-01-01T18:00:00Z",
|
|
810
881
|
);
|
|
811
|
-
//
|
|
882
|
+
// false (touching at 17:00)
|
|
812
883
|
```
|
|
813
884
|
|
|
814
|
-
All overlap checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals,
|
|
885
|
+
All overlap checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, or an epoch outside the `unix/` grammar: not a safe integer or a digit string).
|
|
815
886
|
|
|
816
|
-
`intervalIntersection*` returns the overlapping span of two intervals, or `null` when they do not overlap.
|
|
887
|
+
`intervalIntersection*` returns the overlapping span of two intervals, or `null` when they do not overlap. Touching intervals share no instant, so their intersection is `null`:
|
|
817
888
|
|
|
818
889
|
```typescript
|
|
819
890
|
import {
|
|
@@ -827,43 +898,35 @@ import {
|
|
|
827
898
|
|
|
828
899
|
intervalIntersectionDate(
|
|
829
900
|
"2024-01-01",
|
|
830
|
-
"2024-
|
|
901
|
+
"2024-07-01",
|
|
831
902
|
"2024-04-01",
|
|
832
|
-
"
|
|
903
|
+
"2025-01-01",
|
|
833
904
|
);
|
|
834
|
-
// { start: "2024-04-01", end: "2024-
|
|
905
|
+
// { start: "2024-04-01", end: "2024-07-01" }
|
|
835
906
|
|
|
836
907
|
intervalIntersectionDate(
|
|
837
908
|
"2024-01-01",
|
|
838
|
-
"2024-06-30",
|
|
839
|
-
"2024-06-30",
|
|
840
|
-
"2024-12-31",
|
|
841
|
-
);
|
|
842
|
-
// { start: "2024-06-30", end: "2024-06-30" } (adjacent, shares one instant)
|
|
843
|
-
|
|
844
|
-
intervalIntersectionDate(
|
|
845
|
-
"2024-01-01",
|
|
846
|
-
"2024-06-30",
|
|
847
909
|
"2024-07-01",
|
|
848
|
-
"2024-
|
|
910
|
+
"2024-07-01",
|
|
911
|
+
"2025-01-01",
|
|
849
912
|
);
|
|
850
|
-
// null (
|
|
913
|
+
// null (touching, no shared day)
|
|
851
914
|
|
|
852
915
|
intervalIntersectionUnix(0, 1700000000, 1000000, 2000000);
|
|
853
|
-
// { start: 1000000, end:
|
|
916
|
+
// { start: 1000000, end: 2000000 } (B lies inside A)
|
|
854
917
|
|
|
855
918
|
intervalIntersectionUtc(
|
|
856
919
|
"2024-01-01T00:00:00Z",
|
|
857
|
-
"2024-
|
|
920
|
+
"2024-07-01T00:00:00Z",
|
|
858
921
|
"2024-04-01T00:00:00Z",
|
|
859
|
-
"
|
|
922
|
+
"2025-01-01T00:00:00Z",
|
|
860
923
|
);
|
|
861
|
-
// { start: "2024-04-01T00:00:00Z", end: "2024-
|
|
924
|
+
// { start: "2024-04-01T00:00:00Z", end: "2024-07-01T00:00:00Z" }
|
|
862
925
|
```
|
|
863
926
|
|
|
864
|
-
All intersection functions return `null` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals,
|
|
927
|
+
All intersection functions return `null` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, or an epoch outside the `unix/` grammar: not a safe integer or a digit string).
|
|
865
928
|
|
|
866
|
-
`intervalOverlappingDays*` returns how many distinct calendar dates two intervals share — the numeric counterpart to `intervalIntersection*`'s span.
|
|
929
|
+
`intervalOverlappingDays*` returns how many distinct calendar dates two intervals share — the numeric counterpart to `intervalIntersection*`'s span. It counts the calendar dates that hold at least one instant of the half-open intersection `[max(aStart, bStart), min(aEnd, bEnd))`, so an empty intersection counts `0`: `intervalOverlappingDaysDate("2024-01-01", "2024-01-02", "2024-01-01", "2024-01-02")` is `1`, and touching intervals are `0`. There is no `Time` variant — `PlainTime` has no calendar, so a day count is undefined for it:
|
|
867
930
|
|
|
868
931
|
```typescript
|
|
869
932
|
import {
|
|
@@ -876,44 +939,42 @@ import {
|
|
|
876
939
|
|
|
877
940
|
intervalOverlappingDaysDate(
|
|
878
941
|
"2024-01-01",
|
|
879
|
-
"2024-
|
|
942
|
+
"2024-07-01",
|
|
880
943
|
"2024-04-01",
|
|
881
|
-
"
|
|
944
|
+
"2025-01-01",
|
|
882
945
|
);
|
|
883
|
-
// 91
|
|
946
|
+
// 91 (2024-04-01 through 2024-06-30)
|
|
884
947
|
|
|
885
948
|
intervalOverlappingDaysDate(
|
|
886
949
|
"2024-01-01",
|
|
887
|
-
"2024-
|
|
888
|
-
"2024-
|
|
889
|
-
"
|
|
950
|
+
"2024-07-01",
|
|
951
|
+
"2024-07-01",
|
|
952
|
+
"2025-01-01",
|
|
890
953
|
);
|
|
891
|
-
//
|
|
954
|
+
// 0 (touching, no shared date)
|
|
892
955
|
|
|
893
|
-
|
|
894
|
-
"2024-01-
|
|
895
|
-
"2024-
|
|
896
|
-
"2024-
|
|
897
|
-
"2024-
|
|
956
|
+
intervalOverlappingDaysUtc(
|
|
957
|
+
"2024-01-17T12:00:00Z",
|
|
958
|
+
"2024-01-19T00:00:00Z",
|
|
959
|
+
"2024-01-10T00:00:00Z",
|
|
960
|
+
"2024-01-18T06:00:00Z",
|
|
898
961
|
);
|
|
899
|
-
//
|
|
962
|
+
// 2 (18 hours of overlap touch 17 and 18 January)
|
|
900
963
|
|
|
901
|
-
intervalOverlappingDaysUnix(0, 172800000, 86400000, 259200000
|
|
902
|
-
|
|
903
|
-
});
|
|
904
|
-
// 2
|
|
964
|
+
intervalOverlappingDaysUnix(0, 172800000, 86400000, 259200000);
|
|
965
|
+
// 1 (the intersection [86400000, 172800000) is 1970-01-02 in UTC)
|
|
905
966
|
```
|
|
906
967
|
|
|
907
|
-
Returns `0` when the intervals do not overlap (a well-defined answer, not invalid input) and `null` on invalid input, including an inverted interval (`start > end`). `intervalOverlappingDaysZoned` and `intervalOverlappingDaysUnix` count days in `aStart`'s time zone (`intervalOverlappingDaysUnix` defaults to
|
|
968
|
+
Returns `0` when the intervals do not overlap (a well-defined answer, not invalid input) and `null` on invalid input, including an inverted interval (`start > end`). `intervalOverlappingDaysZoned` and `intervalOverlappingDaysUnix` count days in `aStart`'s time zone (`intervalOverlappingDaysUnix` defaults to UTC; pass `{ timeZone }`, or `"local"` for the system zone) — the same rule `intervalCountZoned`/`intervalCountUnix` use — so `intervalOverlappingDaysZoned` is **not commutative** when the two intervals carry different zones: swapping the arguments can change the answer. Both count the distinct local dates the overlap touches, so a date the zone skipped (`Pacific/Apia`, 2011-12-30) is not counted, and a fall-back that sends the clock back into the previous date counts that date too.
|
|
908
969
|
|
|
909
|
-
|
|
970
|
+
It counts calendar dates, not elapsed days: the 18-hour overlap above touches two dates. For the elapsed length of the overlap, compose `intervalIntersection*` with `intervalLength*`:
|
|
910
971
|
|
|
911
972
|
```typescript
|
|
912
|
-
const span =
|
|
913
|
-
span ?
|
|
973
|
+
const span = intervalIntersectionUtc(aStart, aEnd, bStart, bEnd);
|
|
974
|
+
span ? intervalLengthUtc(span.start, span.end, "day") : 0; // 0.75 for the overlap above
|
|
914
975
|
```
|
|
915
976
|
|
|
916
|
-
`intervalUnion*` returns the combined span of two overlapping or
|
|
977
|
+
`intervalUnion*` returns the combined span of two overlapping or touching intervals, or `null` when a gap separates them. Touching intervals (one's end equals the other's start) join:
|
|
917
978
|
|
|
918
979
|
```typescript
|
|
919
980
|
import {
|
|
@@ -925,28 +986,28 @@ import {
|
|
|
925
986
|
intervalUnionZoned,
|
|
926
987
|
} from "@northguild/gmt";
|
|
927
988
|
|
|
928
|
-
intervalUnionDate("2024-01-01", "2024-
|
|
929
|
-
// { start: "2024-01-01", end: "
|
|
989
|
+
intervalUnionDate("2024-01-01", "2024-07-01", "2024-04-01", "2025-01-01");
|
|
990
|
+
// { start: "2024-01-01", end: "2025-01-01" }
|
|
930
991
|
|
|
931
|
-
intervalUnionDate("2024-01-01", "2024-
|
|
932
|
-
// { start: "2024-01-01", end: "
|
|
992
|
+
intervalUnionDate("2024-01-01", "2024-07-01", "2024-07-01", "2025-01-01");
|
|
993
|
+
// { start: "2024-01-01", end: "2025-01-01" } (touching, joined)
|
|
933
994
|
|
|
934
995
|
intervalUnionDate("2024-01-01", "2024-06-30", "2024-07-01", "2024-12-31");
|
|
935
|
-
// null (
|
|
996
|
+
// null (2024-06-30 lies in neither interval)
|
|
936
997
|
|
|
937
998
|
intervalUnionUnix(0, 1700000000, 1000000, 2000000);
|
|
938
999
|
// { start: 0, end: 1700000000 }
|
|
939
1000
|
|
|
940
1001
|
intervalUnionUtc(
|
|
941
1002
|
"2024-01-01T00:00:00Z",
|
|
942
|
-
"2024-
|
|
1003
|
+
"2024-07-01T00:00:00Z",
|
|
943
1004
|
"2024-04-01T00:00:00Z",
|
|
944
|
-
"
|
|
1005
|
+
"2025-01-01T00:00:00Z",
|
|
945
1006
|
);
|
|
946
|
-
// { start: "2024-01-01T00:00:00Z", end: "
|
|
1007
|
+
// { start: "2024-01-01T00:00:00Z", end: "2025-01-01T00:00:00Z" }
|
|
947
1008
|
```
|
|
948
1009
|
|
|
949
|
-
All union functions return `null` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals,
|
|
1010
|
+
All union functions return `null` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, or an epoch outside the `unix/` grammar: not a safe integer or a digit string).
|
|
950
1011
|
|
|
951
1012
|
`intervalDifference*` returns the portion(s) of interval A not covered by interval B, as an array of `{ start, end }` records:
|
|
952
1013
|
|
|
@@ -961,24 +1022,26 @@ import {
|
|
|
961
1022
|
} from "@northguild/gmt";
|
|
962
1023
|
|
|
963
1024
|
intervalDifferenceDate("2024-01-01", "2024-12-31", "2024-06-01", "2024-07-01");
|
|
964
|
-
// [{ start: "2024-01-01", end: "2024-
|
|
1025
|
+
// [{ start: "2024-01-01", end: "2024-06-01" }, { start: "2024-07-01", end: "2024-12-31" }]
|
|
965
1026
|
|
|
966
1027
|
intervalDifferenceDate("2024-01-01", "2024-12-31", "2024-01-01", "2024-12-31");
|
|
967
1028
|
// [] (B fully covers A)
|
|
968
1029
|
|
|
969
1030
|
intervalDifferenceUnix(0, 1700000000, 1000000, 2000000);
|
|
970
|
-
// [{ start: 0, end:
|
|
1031
|
+
// [{ start: 0, end: 1000000 }, { start: 2000000, end: 1700000000 }]
|
|
971
1032
|
|
|
972
1033
|
intervalDifferenceUtc(
|
|
973
|
-
"2024-01-
|
|
974
|
-
"2024-
|
|
975
|
-
"2024-
|
|
976
|
-
"2024-
|
|
1034
|
+
"2024-01-01T09:00:00Z",
|
|
1035
|
+
"2024-01-01T17:00:00Z",
|
|
1036
|
+
"2024-01-01T12:00:00Z",
|
|
1037
|
+
"2024-01-01T13:00:00Z",
|
|
977
1038
|
);
|
|
978
|
-
// [{ start: "2024-01-
|
|
1039
|
+
// [{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T12:00:00Z" }, { start: "2024-01-01T13:00:00Z", end: "2024-01-01T17:00:00Z" }]
|
|
979
1040
|
```
|
|
980
1041
|
|
|
981
|
-
|
|
1042
|
+
Each piece ends exactly where B starts and resumes exactly where B ends, with no one-unit step, so the pieces and B together cover A once. `intervalDifferenceUtc` returns the same pieces as `subtractIntervals`.
|
|
1043
|
+
|
|
1044
|
+
All difference functions return `[]` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, or an epoch outside the `unix/` grammar: not a safe integer or a digit string).
|
|
982
1045
|
|
|
983
1046
|
`intervalXor*` returns the symmetric difference of two intervals — the portions covered by exactly one of them, not both — as an array of `{ start, end }` records:
|
|
984
1047
|
|
|
@@ -992,27 +1055,27 @@ import {
|
|
|
992
1055
|
intervalXorZoned,
|
|
993
1056
|
} from "@northguild/gmt";
|
|
994
1057
|
|
|
995
|
-
intervalXorDate("2024-01-01", "2024-
|
|
996
|
-
// [{ start: "2024-01-01", end: "2024-
|
|
1058
|
+
intervalXorDate("2024-01-01", "2024-07-01", "2024-04-01", "2025-01-01");
|
|
1059
|
+
// [{ start: "2024-01-01", end: "2024-04-01" }, { start: "2024-07-01", end: "2025-01-01" }]
|
|
997
1060
|
|
|
998
|
-
intervalXorDate("2024-01-01", "2024-
|
|
999
|
-
// [{ start: "2024-01-01", end: "
|
|
1061
|
+
intervalXorDate("2024-01-01", "2024-07-01", "2024-07-01", "2025-01-01");
|
|
1062
|
+
// [{ start: "2024-01-01", end: "2025-01-01" }] (touching intervals share no date, so the runs join)
|
|
1000
1063
|
|
|
1001
1064
|
intervalXorUnix(0, 1700000000, 1000000, 2000000);
|
|
1002
|
-
// [{ start: 0, end:
|
|
1065
|
+
// [{ start: 0, end: 1000000 }, { start: 2000000, end: 1700000000 }]
|
|
1003
1066
|
|
|
1004
1067
|
intervalXorUtc(
|
|
1005
|
-
"2024-01-
|
|
1006
|
-
"2024-
|
|
1007
|
-
"2024-
|
|
1008
|
-
"2024-
|
|
1068
|
+
"2024-01-01T09:00:00Z",
|
|
1069
|
+
"2024-01-01T13:00:00Z",
|
|
1070
|
+
"2024-01-01T12:00:00Z",
|
|
1071
|
+
"2024-01-01T17:00:00Z",
|
|
1009
1072
|
);
|
|
1010
|
-
// [{ start: "2024-01-
|
|
1073
|
+
// [{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T12:00:00Z" }, { start: "2024-01-01T13:00:00Z", end: "2024-01-01T17:00:00Z" }]
|
|
1011
1074
|
```
|
|
1012
1075
|
|
|
1013
|
-
All xor functions return `[]` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals,
|
|
1076
|
+
All xor functions return `[]` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, or an epoch outside the `unix/` grammar: not a safe integer or a digit string).
|
|
1014
1077
|
|
|
1015
|
-
`intervalAbuts*` checks whether two intervals are exactly adjacent
|
|
1078
|
+
`intervalAbuts*` checks whether two intervals are exactly adjacent, in either order: one interval's `end` equals the other's `start` (Allen's "meets"). The two then share no instant and leave no gap. Intervals separated by any gap, even one nanosecond or one epoch unit, do not abut, and an empty interval abuts nothing:
|
|
1016
1079
|
|
|
1017
1080
|
```typescript
|
|
1018
1081
|
import {
|
|
@@ -1024,30 +1087,30 @@ import {
|
|
|
1024
1087
|
intervalAbutsZoned,
|
|
1025
1088
|
} from "@northguild/gmt";
|
|
1026
1089
|
|
|
1027
|
-
intervalAbutsDate("2024-01-01", "2024-
|
|
1028
|
-
// true
|
|
1090
|
+
intervalAbutsDate("2024-01-01", "2024-07-01", "2024-07-01", "2025-01-01");
|
|
1091
|
+
// true (the first interval ends where the second starts)
|
|
1029
1092
|
|
|
1030
|
-
intervalAbutsDate("2024-01-01", "2024-06-
|
|
1031
|
-
// false (
|
|
1093
|
+
intervalAbutsDate("2024-01-01", "2024-06-30", "2024-07-01", "2024-12-31");
|
|
1094
|
+
// false (2024-06-30 lies in neither interval)
|
|
1032
1095
|
|
|
1033
|
-
intervalAbutsDate("2024-01-01", "2024-
|
|
1096
|
+
intervalAbutsDate("2024-01-01", "2024-07-01", "2024-04-01", "2025-01-01");
|
|
1034
1097
|
// false (overlap)
|
|
1035
1098
|
|
|
1036
1099
|
intervalAbutsUnix(0, 1000000, 1000000, 2000000);
|
|
1037
1100
|
// true
|
|
1038
1101
|
|
|
1039
1102
|
intervalAbutsUtc(
|
|
1040
|
-
"2024-01-
|
|
1041
|
-
"2024-
|
|
1042
|
-
"2024-
|
|
1043
|
-
"2024-
|
|
1103
|
+
"2024-01-01T09:00:00Z",
|
|
1104
|
+
"2024-01-01T12:00:00Z",
|
|
1105
|
+
"2024-01-01T12:00:00.000000001Z",
|
|
1106
|
+
"2024-01-01T17:00:00Z",
|
|
1044
1107
|
);
|
|
1045
|
-
//
|
|
1108
|
+
// false (1 ns apart)
|
|
1046
1109
|
```
|
|
1047
1110
|
|
|
1048
|
-
All abuts checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals,
|
|
1111
|
+
All abuts checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, or an epoch outside the `unix/` grammar: not a safe integer or a digit string).
|
|
1049
1112
|
|
|
1050
|
-
`intervalEngulfs*` checks whether interval B
|
|
1113
|
+
`intervalEngulfs*` checks whether interval B lies within interval A and overlaps it — B may share A's `start` or `end`, but an empty B at A's `end` is not engulfed. Equivalent to the 4-argument `intervalContains*` mode:
|
|
1051
1114
|
|
|
1052
1115
|
```typescript
|
|
1053
1116
|
import {
|
|
@@ -1080,7 +1143,7 @@ intervalEngulfsUtc(
|
|
|
1080
1143
|
// true
|
|
1081
1144
|
```
|
|
1082
1145
|
|
|
1083
|
-
All engulfs checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals,
|
|
1146
|
+
All engulfs checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, or an epoch outside the `unix/` grammar: not a safe integer or a digit string).
|
|
1084
1147
|
|
|
1085
1148
|
`splitIntervalByUnit*` splits an interval into sub-intervals of `amount × unit`, returning an array of `{ start, end }` records. The final sub-interval is trimmed so its `end` never exceeds the original `end`:
|
|
1086
1149
|
|
|
@@ -1109,7 +1172,26 @@ splitIntervalByUnitUnix(0, 86400000, "hour", 6);
|
|
|
1109
1172
|
// [{ start: 0, end: 21600000 }, { start: 21600000, end: 43200000 }, { start: 43200000, end: 64800000 }, { start: 64800000, end: 86400000 }]
|
|
1110
1173
|
```
|
|
1111
1174
|
|
|
1112
|
-
All split functions return `[]` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, non-positive amount, unsupported unit, or a unit that has no effect on the target type).
|
|
1175
|
+
All split functions return `[]` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, non-positive amount, unsupported unit, or a unit that has no effect on the target type). A zero-length interval returns itself as the one piece (`[{ start, end }]`) for a supported unit, and `[]` for an unsupported one: `splitIntervalByUnitDate("2024-01-01", "2024-01-01", "hour", 1)` is `[]`.
|
|
1176
|
+
|
|
1177
|
+
`splitIntervalByUnit*`, `intervalDivideEqually*`, `mapDatesInRange` and `mapZonedDatesInRange` build one array element per piece, so each takes an optional `{ maxPieces }` (a positive safe integer, default `1_000_000`) and returns `[]` when the result would be longer. A split stops as soon as the piece past the limit is due, so a huge range answers in bounded time instead of exhausting the heap. Pass a larger `maxPieces` when a long range legitimately needs it:
|
|
1178
|
+
|
|
1179
|
+
```typescript
|
|
1180
|
+
import { mapDatesInRange, splitIntervalByUnitDate } from "@northguild/gmt";
|
|
1181
|
+
|
|
1182
|
+
splitIntervalByUnitDate("2024-01-01", "2024-01-10", "day", 2, { maxPieces: 4 });
|
|
1183
|
+
// [] — 5 slices exceed the limit
|
|
1184
|
+
|
|
1185
|
+
mapDatesInRange("0001-01-01", "9999-12-31", 1);
|
|
1186
|
+
// [] — 3,652,059 dates exceed the default
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
Each boundary is computed from `start` (`start + k × amount`, as Temporal and Luxon's `Interval.splitBy` do), never by stepping from the previous boundary, so month-end starts don't drift. A yearly split from February 29 likewise returns to February 29 in leap years:
|
|
1190
|
+
|
|
1191
|
+
```typescript
|
|
1192
|
+
splitIntervalByUnitDate("2024-01-31", "2024-05-01", "month", 1);
|
|
1193
|
+
// [{ start: "2024-01-31", end: "2024-02-29" }, { start: "2024-02-29", end: "2024-03-31" }, { start: "2024-03-31", end: "2024-04-30" }, { start: "2024-04-30", end: "2024-05-01" }]
|
|
1194
|
+
```
|
|
1113
1195
|
|
|
1114
1196
|
`intervalCount*` returns how many calendar-unit boundaries an interval crosses — the number of units the half-open interval `[start, end)` touches. This is distinct from `diff*`, which measures exact elapsed duration: an interval from 23:59 to 00:01 is two minutes long but touches two days:
|
|
1115
1197
|
|
|
@@ -1146,7 +1228,9 @@ intervalCountUnix(0, 86400000, "hour");
|
|
|
1146
1228
|
// 24
|
|
1147
1229
|
```
|
|
1148
1230
|
|
|
1149
|
-
|
|
1231
|
+
A zero-length interval counts `0` in every unit — the empty `[start, start)` holds no instant, so `intervalCountDate("2024-01-15", "2024-01-15", "month")` is `0`. Weeks start on Monday (ISO 8601), singular and plural units are interchangeable (`"day"` and `"days"`), and `intervalCountUnix` counts calendar boundaries in `options.timeZone`, UTC by default (`"local"` for the system zone), like `addUnix`. All count functions return `null` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, unsupported unit, or a unit that has no effect on the target type).
|
|
1232
|
+
|
|
1233
|
+
`intervalCountZoned`, `intervalCountUnix` and `intervalCountUtc` count the same local buckets `bucketRange` returns. A bucket shorter than its unit still counts once — a 20-minute range straddling 04:00 on `Pacific/Chatham`'s spring-forward day counts 2 hours, because its 03:00 hour lasts only 15 minutes — and a day the zone deleted counts not at all (`Pacific/Apia`'s 30 December 2011).
|
|
1150
1234
|
|
|
1151
1235
|
`intervalLength*` is `intervalCount*`'s exact-duration counterpart — it answers "how long is this interval" as a real, possibly fractional number, rather than "how many boundaries does it touch":
|
|
1152
1236
|
|
|
@@ -1174,7 +1258,7 @@ intervalLengthZoned(
|
|
|
1174
1258
|
// 23 (spring-forward local day is 23 real hours)
|
|
1175
1259
|
```
|
|
1176
1260
|
|
|
1177
|
-
`intervalLength*` uses `Temporal.Duration.prototype.total`, so calendar units (month, year) resolve against the interval's own `start` rather than truncating, and zoned/unix/utc variants are DST-aware the same way `intervalCount*` is. Returns `0` for a zero-length interval, and `null` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, unsupported unit).
|
|
1261
|
+
`intervalLength*` uses `Temporal.Duration.prototype.total`, so calendar units (month, year) resolve against the interval's own `start` rather than truncating, and zoned/unix/utc variants are DST-aware the same way `intervalCount*` is. Returns `0` for a zero-length interval, and `null` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, unsupported unit). `intervalLengthUnix` and `splitIntervalByUnitUnix` take `{ epochUnit, timeZone }` like `intervalCountUnix` (milliseconds and UTC by default): `intervalLengthUnix(0, 86400, "day", { epochUnit: "seconds" })` is `1`.
|
|
1178
1262
|
|
|
1179
1263
|
`intervalDivideEqually*` splits an interval into `n` equal-length sub-intervals, and `intervalSplitAt*` splits an interval at arbitrary points instead of by count:
|
|
1180
1264
|
|
|
@@ -1191,7 +1275,7 @@ intervalSplitAtDate("2024-01-01", "2024-01-10", ["2024-01-07", "2024-01-03"]);
|
|
|
1191
1275
|
// [{ start: "2024-01-01", end: "2024-01-03" }, { start: "2024-01-03", end: "2024-01-07" }, { start: "2024-01-07", end: "2024-01-10" }]
|
|
1192
1276
|
```
|
|
1193
1277
|
|
|
1194
|
-
`n` must be a positive integer (`[]` otherwise)
|
|
1278
|
+
`n` must be a positive integer (`[]` otherwise), and `n` above `maxPieces` also returns `[]`; `n === 1` returns the original interval unchanged, and a zero-length interval returns `n` identical zero-length sub-intervals. Each internal boundary is `start + round((end − start) · i / n)`, an exact half rounding up — in whole days for `PlainDate`, nanoseconds for the other string variants and the arguments' own epoch unit for `intervalDivideEquallyUnix` — so the split is exact when the span divides evenly by `n`, within half a unit otherwise, and the pieces tile the range with no gap or overshoot (`intervalDivideEquallyZoned` splits DST-crossing intervals by real elapsed time, not local clock time). `intervalSplitAt*` sorts its `points` internally — they need not be pre-sorted — and drops points outside the interval or exactly on `start` or `end`, since those cannot introduce a new sub-interval. Each piece's `end` is the next piece's `start`, so under `[start, end)` the pieces partition the interval; an empty or all-dropped `points` array returns `[{ start, end }]` unsplit.
|
|
1195
1279
|
|
|
1196
1280
|
`mergeIntervals*` and `intervalXorAll*` are the list-form generalizations of `intervalUnion*` and `intervalXor*`, which are pairwise only — each takes a single array of `{ start, end }` records instead of two flat intervals:
|
|
1197
1281
|
|
|
@@ -1209,10 +1293,10 @@ intervalXorAllDate([
|
|
|
1209
1293
|
{ start: "2024-01-05", end: "2024-01-15" },
|
|
1210
1294
|
{ start: "2024-01-08", end: "2024-01-20" },
|
|
1211
1295
|
]);
|
|
1212
|
-
// [{ start: "2024-01-01", end: "2024-01-
|
|
1296
|
+
// [{ start: "2024-01-01", end: "2024-01-05" }, { start: "2024-01-08", end: "2024-01-10" }, { start: "2024-01-15", end: "2024-01-20" }]
|
|
1213
1297
|
```
|
|
1214
1298
|
|
|
1215
|
-
`mergeIntervals*` collapses overlapping or
|
|
1299
|
+
`mergeIntervals*` collapses overlapping or touching intervals (one's end equals the next one's start) into the minimum sorted set that does not overlap. `intervalXorAll*` returns the maximal runs covered by an odd number of the input intervals, sorted by start — for exactly two intervals the result is identical to the pairwise `intervalXor*`, and two identical intervals cancel out to `[]`. All four return `[]` for an empty list or on invalid input.
|
|
1216
1300
|
|
|
1217
1301
|
`intervalFromDuration*` constructs an interval from a single point plus an ISO 8601 duration, anchored at either end — Luxon's `Interval.after`/`Interval.before` as one function with an `anchor` param instead of two:
|
|
1218
1302
|
|
|
@@ -1243,16 +1327,16 @@ intervalFromDurationTime("12:00:00", "P1D", "start");
|
|
|
1243
1327
|
// null (PlainTime has no calendar — a date-unit duration needs a relativeTo it can't supply)
|
|
1244
1328
|
```
|
|
1245
1329
|
|
|
1246
|
-
Calendar units (years/months/weeks) resolve against `value` itself, so no separate `relativeTo` is needed — except for `intervalFromDurationTime`, which returns `null` for a `duration` with a nonzero years/months/weeks/days component, since `PlainTime` has no calendar to resolve it against. `intervalFromDurationZoned` accepts the same `disambiguation`/`
|
|
1330
|
+
Calendar units (years/months/weeks) resolve against `value` itself, so no separate `relativeTo` is needed — except for `intervalFromDurationTime`, which returns `null` for a `duration` with a nonzero years/months/weeks/days component, since `PlainTime` has no calendar to resolve it against. `intervalFromDurationZoned` accepts the same `disambiguation`/`overflow` options as `addZoned`; `intervalFromDurationUnix` accepts `addUnix`'s `epochUnit`/`timeZone`/`overflow` options; `intervalFromDurationTime` takes no options argument, like `addTime`. A negative `duration` that inverts the computed span, or an `overflow: "reject"` result, returns `null` — same sentinel as any other invalid input.
|
|
1247
1331
|
|
|
1248
|
-
All validators return `false` on invalid input (wrong type, malformed strings, leap seconds, mixed kinds for plain interval validators,
|
|
1332
|
+
All validators return `false` on invalid input (wrong type, malformed strings, leap seconds, mixed kinds for plain interval validators, or an epoch outside the `unix/` grammar: not a safe integer or a digit string).
|
|
1249
1333
|
|
|
1250
1334
|
### Zoned operations
|
|
1251
1335
|
|
|
1252
1336
|
```typescript
|
|
1253
1337
|
import { addZoned, formatZonedDateTime } from "@northguild/gmt";
|
|
1254
1338
|
|
|
1255
|
-
addZoned("2026-03-07T23:00:00-05:00[America/New_York]", 2
|
|
1339
|
+
addZoned("2026-03-07T23:00:00-05:00[America/New_York]", { hours: 2 });
|
|
1256
1340
|
// "2026-03-08T01:00:00-05:00[America/New_York]"
|
|
1257
1341
|
|
|
1258
1342
|
formatZonedDateTime("2024-03-17T14:30:45+00:00[UTC]", "en-US", {
|
|
@@ -1274,23 +1358,31 @@ convertPlainDateTimeToZoned("2024-03-10T02:30:00", "America/New_York", {
|
|
|
1274
1358
|
// "" — no such local time exists
|
|
1275
1359
|
```
|
|
1276
1360
|
|
|
1277
|
-
The `startOfZoned`/`endOfZoned`/`startOfQuarterForZoned`/`endOfQuarterForZoned`/`
|
|
1361
|
+
The `startOfZoned`/`endOfZoned`/`startOfQuarterForZoned`/`endOfQuarterForZoned`/`getLocaleZonedStartOfWeek`/`getLocaleZonedEndOfWeek` family (and their `unix/` counterparts) always return the real boundary of the unit that contains the input, in its zone: a start is never after the input and an end never before it, even when the wall-clock boundary was skipped or repeated. `Pacific/Chatham`'s 03:00 hour on its spring-forward starts at 03:45, New York's repeated 1 a.m. is its own hour, and a midnight repeated on the same date (America/Havana, 2024-11-03) stays one 25-hour day:
|
|
1278
1362
|
|
|
1279
1363
|
```typescript
|
|
1280
1364
|
import { startOfZoned } from "@northguild/gmt";
|
|
1281
1365
|
|
|
1282
1366
|
// 2024-11-03T01:45:00-05:00 is the SECOND, repeated 1am of the fall-back overlap in America/New_York.
|
|
1283
|
-
|
|
1367
|
+
startOfZoned("2024-11-03T01:45:00-05:00[America/New_York]", "hour");
|
|
1368
|
+
// "2024-11-03T01:00:00-05:00[America/New_York]" — the real start of the repeated hour
|
|
1369
|
+
```
|
|
1284
1370
|
|
|
1285
|
-
|
|
1286
|
-
// "" — offset defaults to "ignore", so disambiguation actually fires and "reject" throws
|
|
1371
|
+
These boundary functions follow TC39's `startOfDay()`, which takes no resolution options: they take no `disambiguation` or `offset` options, and neither does `mapZonedHoursInDay`. Resolution options belong on functions that set wall-clock fields. `getHoursInZonedDay` and `mapZonedHoursInDay` measure the input's calendar date exactly as TC39's `hoursInDay` does, which differs from `startOfZoned(…, "day")` only where a fall-back re-enters the previous date (America/Goose_Bay, 2010-11-07).
|
|
1287
1372
|
|
|
1288
|
-
|
|
1289
|
-
// "2024-11-03T01:00:00-05:00[America/New_York]" — offset:"prefer" keeps the source's
|
|
1290
|
-
// still-valid -05:00 offset, so disambiguation is never consulted and "reject" never fires
|
|
1291
|
-
```
|
|
1373
|
+
`convertPlainDateTimeToZoned`, `addZoned`, `subtractZoned` and `intervalFromDurationZoned` take no `offset` option: they resolve a plain date-time that has no UTC offset for it to act on.
|
|
1292
1374
|
|
|
1293
|
-
`
|
|
1375
|
+
`addZoned`, `subtractZoned` and `intervalFromDurationZoned` follow TC39's AddZonedDateTime: the date part of the duration (years to days) moves the wall clock, and the time part (hours and smaller) is added in exact time. `disambiguation` resolves the wall clock the date part lands on — in a spring-forward gap `"compatible"` and `"later"` move it forward, `"earlier"` back, and `"reject"` returns the sentinel; in a fall-back overlap it picks the occurrence — and never re-resolves the time part, so adding 10 minutes is always 10 real minutes. Before 1.16.0 a gap landing always moved forward. `diffZoned`, `diffZonedAsDuration` and `intervalLengthZoned` follow DifferenceZonedDateTime: days, weeks, months and years are counted on the zone's wall clock, and hours and smaller in exact time. Two values in different zones share no wall clock, so a calendar unit returns the sentinel; convert one end with `convertZonedToZoned` first:
|
|
1376
|
+
|
|
1377
|
+
```typescript
|
|
1378
|
+
import { diffZoned } from "@northguild/gmt";
|
|
1379
|
+
|
|
1380
|
+
diffZoned("2024-03-09T12:00:00-05:00[America/New_York]", "2024-03-10T12:00:00-04:00[America/New_York]", "days");
|
|
1381
|
+
// 1 — noon to noon across a 23-hour day
|
|
1382
|
+
|
|
1383
|
+
diffZoned("2024-01-01T00:00:00-05:00[America/New_York]", "2024-01-03T00:00:00+01:00[Europe/Paris]", "days");
|
|
1384
|
+
// null — calendar unit across two time zones
|
|
1385
|
+
```
|
|
1294
1386
|
|
|
1295
1387
|
`hasDaylightSaving` reports whether an IANA timezone observes daylight saving time at all:
|
|
1296
1388
|
|
|
@@ -1431,7 +1523,7 @@ closestZonedTo("2024-03-15T12:00:00[America/New_York]", [
|
|
|
1431
1523
|
// "2024-03-18T00:00:00-04:00[America/New_York]"
|
|
1432
1524
|
```
|
|
1433
1525
|
|
|
1434
|
-
See [`docs/dst-disambiguation.md`](../../docs/dst-disambiguation.md) for the full explanation, including
|
|
1526
|
+
See [`docs/dst-disambiguation.md`](../../docs/dst-disambiguation.md) for the full explanation, including where `overflow` is and is not exposed.
|
|
1435
1527
|
|
|
1436
1528
|
### Formatting
|
|
1437
1529
|
|
|
@@ -1466,24 +1558,36 @@ formatRelativeDate("2026-01-15");
|
|
|
1466
1558
|
// e.g. "3 months ago"
|
|
1467
1559
|
|
|
1468
1560
|
formatRelativeTime("14:30:00", "en-US", { style: "short" });
|
|
1469
|
-
// e.g. "2
|
|
1561
|
+
// e.g. "2 hr. ago"
|
|
1470
1562
|
|
|
1471
1563
|
formatRelativeDateTime("2026-03-17T09:00:00", "en-GB", {
|
|
1472
1564
|
style: "long",
|
|
1473
1565
|
numeric: "always",
|
|
1474
1566
|
});
|
|
1475
|
-
// e.g. "
|
|
1567
|
+
// e.g. "in 3 hours"
|
|
1476
1568
|
|
|
1477
1569
|
// Zoned relative formatting — reference can be a ZonedDateTime, UTC string, or unix epoch.
|
|
1478
1570
|
formatRelativeZoned("2026-03-08T01:00:00-05:00[America/New_York]", "en-US");
|
|
1479
1571
|
// e.g. "tomorrow"
|
|
1480
1572
|
|
|
1481
|
-
|
|
1482
|
-
//
|
|
1573
|
+
// Auto-picked units run from second through year, with formatRelativeDate's thresholds;
|
|
1574
|
+
// pass largestUnit to cap them.
|
|
1575
|
+
formatRelativeUtc("2024-03-17T14:30:45Z", "en-US", {
|
|
1576
|
+
reference: "2026-03-17T14:30:45Z",
|
|
1577
|
+
});
|
|
1578
|
+
// "2 years ago"
|
|
1579
|
+
formatRelativeUtc("2024-03-17T14:30:45Z", "en-US", {
|
|
1580
|
+
reference: "2026-03-17T14:30:45Z",
|
|
1581
|
+
largestUnit: "day",
|
|
1582
|
+
});
|
|
1583
|
+
// "730 days ago"
|
|
1483
1584
|
|
|
1484
1585
|
// Unix epoch relative formatting.
|
|
1485
|
-
formatRelativeUnix(1710685845000, "en-US", {
|
|
1486
|
-
|
|
1586
|
+
formatRelativeUnix(1710685845000, "en-US", {
|
|
1587
|
+
epochUnit: "milliseconds",
|
|
1588
|
+
reference: 1805358645000,
|
|
1589
|
+
});
|
|
1590
|
+
// "3 years ago"
|
|
1487
1591
|
|
|
1488
1592
|
// roundingMethod ("floor" | "ceil" | "round", default "round") controls how the
|
|
1489
1593
|
// computed distance rounds to the display unit — every formatRelative* function accepts it.
|
|
@@ -1514,13 +1618,13 @@ formatCalendarZoned("2026-03-16T14:30:00-04:00[America/New_York]", "de-DE", {
|
|
|
1514
1618
|
// formatZonedRange (same parameter order and option shape), for a
|
|
1515
1619
|
// locale-elided range between two timezone-free values.
|
|
1516
1620
|
formatDateRange("2024-02-03", "2024-02-05", "en-US", { dateStyle: "long" });
|
|
1517
|
-
// "February 3
|
|
1621
|
+
// "February 3 - 5, 2024"
|
|
1518
1622
|
|
|
1519
1623
|
formatDateTimeRange("2024-02-03T09:00:00", "2024-02-03T17:00:00", "en-US", {
|
|
1520
1624
|
dateStyle: "long",
|
|
1521
1625
|
timeStyle: "short",
|
|
1522
1626
|
});
|
|
1523
|
-
// "February 3, 2024, 9:00 AM
|
|
1627
|
+
// "February 3, 2024, 9:00 AM - 5:00 PM"
|
|
1524
1628
|
|
|
1525
1629
|
// formatDateToParts / formatDateTimeToParts / formatZonedToParts return the
|
|
1526
1630
|
// locale-ordered Array<{ type, value }> parts behind the strings above,
|
|
@@ -1541,7 +1645,7 @@ formatDateToParts("2024-03-15", "fr-FR");
|
|
|
1541
1645
|
formatZonedToParts("2024-03-15T14:30:00-04:00[America/New_York]", "en-US", {
|
|
1542
1646
|
timeZoneName: "longOffset",
|
|
1543
1647
|
});
|
|
1544
|
-
// includes { type: "timeZoneName", value: "GMT-
|
|
1648
|
+
// includes { type: "timeZoneName", value: "GMT-04:00" }
|
|
1545
1649
|
```
|
|
1546
1650
|
|
|
1547
1651
|
### Named machine formats
|
|
@@ -1569,17 +1673,29 @@ formatRfc2822("2024-03-15T14:30:00-04:00[America/New_York]");
|
|
|
1569
1673
|
parseRfc2822("Fri, 15 Mar 2024 14:30:00 -0400");
|
|
1570
1674
|
// "2024-03-15T14:30:00-04:00[-04:00]"
|
|
1571
1675
|
|
|
1572
|
-
//
|
|
1676
|
+
// A receiver reads RFC 5322's full and obsolete syntax: comments, any case,
|
|
1677
|
+
// two-digit years and zone names. A day name that contradicts the date is "".
|
|
1678
|
+
parseRfc2822("fri,15 Mar 24 14:30 -0400 (EDT)");
|
|
1679
|
+
// "2024-03-15T14:30:00-04:00[-04:00]"
|
|
1680
|
+
parseRfc2822("Sat, 15 Mar 2024 14:30:00 -0400");
|
|
1681
|
+
// "" — 15 March 2024 was a Friday
|
|
1682
|
+
|
|
1683
|
+
// HTTP headers (RFC 9110 IMF-fixdate) — Last-Modified, Date, Expires.
|
|
1573
1684
|
formatHttp("2024-03-15T14:30:00Z");
|
|
1574
1685
|
// "Fri, 15 Mar 2024 14:30:00 GMT"
|
|
1575
1686
|
parseHttp("Fri, 15 Mar 2024 14:30:00 GMT");
|
|
1576
1687
|
// "2024-03-15T14:30:00Z"
|
|
1688
|
+
// parseHttp also reads the obsolete rfc850-date and asctime-date forms.
|
|
1689
|
+
parseHttp("Sun Nov 6 08:49:37 1994");
|
|
1690
|
+
// "1994-11-06T08:49:37Z"
|
|
1577
1691
|
|
|
1578
1692
|
// ANSI SQL / ODBC datetime literals (DATETIME/TIMESTAMP columns, no tz).
|
|
1579
1693
|
formatSql("2024-03-15T14:30:00");
|
|
1580
1694
|
// "2024-03-15 14:30:00"
|
|
1581
1695
|
parseSql("2024-03-15 14:30:00");
|
|
1582
1696
|
// "2024-03-15T14:30:00"
|
|
1697
|
+
parseSql("2024-03-15 14:30");
|
|
1698
|
+
// "" — the SQL literal grammar requires seconds, and a year of 0001–9999
|
|
1583
1699
|
|
|
1584
1700
|
// Strict RFC 3339 — strips the bracketed IANA zone GMT's own zoned strings
|
|
1585
1701
|
// carry, which RFC 3339 does not permit.
|
|
@@ -1589,21 +1705,759 @@ parseRfc3339("2024-03-15T14:30:00-04:00");
|
|
|
1589
1705
|
// "2024-03-15T14:30:00-04:00[-04:00]"
|
|
1590
1706
|
```
|
|
1591
1707
|
|
|
1708
|
+
The formatters return `""` for a value their grammar cannot express, such as a year outside `0000`–`9999` for RFC 3339 and HTTP-date or outside `0001`–`9999` for SQL, rather than writing a string no conforming parser accepts. Use Temporal's own `toString()` when you need any year.
|
|
1709
|
+
|
|
1592
1710
|
### Unix and UTC helpers
|
|
1593
1711
|
|
|
1594
1712
|
```typescript
|
|
1595
1713
|
import { getUnixNow, getUtcNow, convertUnixToPlainDate } from "@northguild/gmt";
|
|
1596
1714
|
|
|
1597
|
-
getUnixNow(
|
|
1598
|
-
// 1710685845000
|
|
1715
|
+
getUnixNow();
|
|
1716
|
+
// 1710685845000 (milliseconds; getUnixNow({ epochUnit: "seconds" }) is 1710685845)
|
|
1599
1717
|
|
|
1600
1718
|
getUtcNow();
|
|
1601
1719
|
// "2026-03-18T11:42:33.123Z"
|
|
1602
1720
|
|
|
1603
|
-
convertUnixToPlainDate(
|
|
1721
|
+
convertUnixToPlainDate(1710685845000, { timeZone: "UTC" });
|
|
1604
1722
|
// "2024-03-17"
|
|
1605
1723
|
```
|
|
1606
1724
|
|
|
1725
|
+
### Nanosecond precision
|
|
1726
|
+
|
|
1727
|
+
`convertZonedToUnix` and friends return `number` milliseconds. Systems that record
|
|
1728
|
+
telemetry, trades or sensor readings need nanoseconds, and a `number` cannot hold them:
|
|
1729
|
+
integers are exact only to `2^53 − 1` ≈ 9.0 × 10^15, and nanoseconds since the epoch
|
|
1730
|
+
passed that in April 1970. (`Date.now() * 1e6` is not a nanosecond timestamp — it is a
|
|
1731
|
+
millisecond timestamp with three zeroes appended.) The `precision/` namespace works in
|
|
1732
|
+
`bigint`:
|
|
1733
|
+
|
|
1734
|
+
```typescript
|
|
1735
|
+
import {
|
|
1736
|
+
toNanoseconds,
|
|
1737
|
+
fromNanoseconds,
|
|
1738
|
+
formatNanoseconds,
|
|
1739
|
+
parseNanoseconds,
|
|
1740
|
+
truncateNanoseconds,
|
|
1741
|
+
} from "@northguild/gmt";
|
|
1742
|
+
|
|
1743
|
+
toNanoseconds("2024-03-10T12:00:00.123456789Z");
|
|
1744
|
+
// 1710072000123456789n
|
|
1745
|
+
|
|
1746
|
+
fromNanoseconds(1710072000123456789n);
|
|
1747
|
+
// "2024-03-10T12:00:00.123456789Z"
|
|
1748
|
+
|
|
1749
|
+
fromNanoseconds(1710072000123456789n, "America/New_York");
|
|
1750
|
+
// "2024-03-10T08:00:00.123456789-04:00[America/New_York]"
|
|
1751
|
+
```
|
|
1752
|
+
|
|
1753
|
+
`JSON.stringify` throws a `TypeError` on a `bigint`, so the value crosses a wire as a
|
|
1754
|
+
canonical decimal string:
|
|
1755
|
+
|
|
1756
|
+
```typescript
|
|
1757
|
+
JSON.stringify({ observedAt: formatNanoseconds(1710072000123456789n) });
|
|
1758
|
+
// '{"observedAt":"1710072000123456789"}'
|
|
1759
|
+
|
|
1760
|
+
parseNanoseconds("1710072000123456789");
|
|
1761
|
+
// 1710072000123456789n
|
|
1762
|
+
```
|
|
1763
|
+
|
|
1764
|
+
Most storage engines cannot hold nanoseconds — PostgreSQL `timestamptz` and MySQL
|
|
1765
|
+
`DATETIME(6)` hold microseconds — so a naive write-then-read loses the last digits
|
|
1766
|
+
silently. Truncate to the target precision first, and the round-trip is exact:
|
|
1767
|
+
|
|
1768
|
+
```typescript
|
|
1769
|
+
truncateNanoseconds(1710072000123456789n, "us");
|
|
1770
|
+
// 1710072000123456000n — safe to write to a microsecond column
|
|
1771
|
+
|
|
1772
|
+
truncateNanoseconds(1710072000123456789n, "ms");
|
|
1773
|
+
// 1710072000123000000n
|
|
1774
|
+
```
|
|
1775
|
+
|
|
1776
|
+
Truncation floors toward negative infinity, so pre-1970 values truncate the same way
|
|
1777
|
+
post-1970 ones do — `truncateNanoseconds(-1500n, "us")` is `-2000n`, not `-1000n`.
|
|
1778
|
+
Rounding toward zero would make the result jump direction either side of the epoch.
|
|
1779
|
+
|
|
1780
|
+
`0n` is both the epoch and the invalid-input sentinel, so `precision/validate` exists to
|
|
1781
|
+
tell them apart. Each predicate accepts exactly what its partner parses:
|
|
1782
|
+
|
|
1783
|
+
```typescript
|
|
1784
|
+
import {
|
|
1785
|
+
isValidInstant,
|
|
1786
|
+
isValidNanoseconds,
|
|
1787
|
+
isValidNanoPattern,
|
|
1788
|
+
} from "@northguild/gmt";
|
|
1789
|
+
|
|
1790
|
+
isValidInstant("1970-01-01T00:00:00Z"); // true — toNanoseconds returns 0n, the epoch
|
|
1791
|
+
isValidInstant("garbage"); // false — toNanoseconds returns 0n, the sentinel
|
|
1792
|
+
|
|
1793
|
+
isValidNanoPattern("0"); // true — parseNanoseconds returns 0n, the epoch
|
|
1794
|
+
isValidNanoPattern("1e18"); // false
|
|
1795
|
+
|
|
1796
|
+
isValidNanoseconds(0n); // true — in range for truncateNanoseconds / fromNanoseconds
|
|
1797
|
+
isValidNanoseconds(0); // false — a number cannot carry a nanosecond timestamp
|
|
1798
|
+
```
|
|
1799
|
+
|
|
1800
|
+
Reach for `isValidInstant` rather than `isValidUtc`: the latter gates on GMT's stricter
|
|
1801
|
+
`<date>T<time>Z` shape and rejects the offsets and bracketed zones `toNanoseconds`
|
|
1802
|
+
accepts, so validating with it discards valid input.
|
|
1803
|
+
|
|
1804
|
+
Every `precision/` function returns a sentinel (`""` for strings, `0n` for bigints) on
|
|
1805
|
+
invalid input, and accepts only values inside the range `Temporal.Instant` can represent
|
|
1806
|
+
(±8_640_000_000_000_000_000_000n). `0n` is also the epoch itself, so validate the input
|
|
1807
|
+
first when the two must be told apart. Leap-second-aware time scales (TAI, GPS) are not
|
|
1808
|
+
part of this namespace — these are plain instant conversions.
|
|
1809
|
+
|
|
1810
|
+
### Foreign epoch bridges
|
|
1811
|
+
|
|
1812
|
+
Other systems do not count from 1970. NTP counts from 1900 and rolls over in 2036, Windows
|
|
1813
|
+
`FILETIME` counts 100-nanosecond intervals from 1601, .NET ticks count the same interval
|
|
1814
|
+
from year 1, Excel uses a day serial with a deliberate 1900 leap-year bug, and PostgreSQL
|
|
1815
|
+
stores microseconds from 2000-01-01. Every integration re-derives these constants, usually
|
|
1816
|
+
wrongly:
|
|
1817
|
+
|
|
1818
|
+
```typescript
|
|
1819
|
+
import {
|
|
1820
|
+
toNtpTimestamp,
|
|
1821
|
+
fromNtpTimestamp,
|
|
1822
|
+
toFileTime,
|
|
1823
|
+
fromFileTime,
|
|
1824
|
+
toDotNetTicks,
|
|
1825
|
+
fromDotNetTicks,
|
|
1826
|
+
toExcelSerial,
|
|
1827
|
+
fromExcelSerial,
|
|
1828
|
+
toPgMicroseconds,
|
|
1829
|
+
fromPgMicroseconds,
|
|
1830
|
+
} from "@northguild/gmt";
|
|
1831
|
+
|
|
1832
|
+
toFileTime("1970-01-01T00:00:00Z");
|
|
1833
|
+
// 116444736000000000n — 100 ns intervals since 1601-01-01
|
|
1834
|
+
|
|
1835
|
+
toDotNetTicks("1970-01-01T00:00:00Z");
|
|
1836
|
+
// 621355968000000000n — same unit, counted from 0001-01-01
|
|
1837
|
+
|
|
1838
|
+
toPgMicroseconds("2024-03-10T12:00:00Z");
|
|
1839
|
+
// 763387200000000n — microseconds since 2000-01-01
|
|
1840
|
+
```
|
|
1841
|
+
|
|
1842
|
+
**NTP timestamps are era-ambiguous by design.** The seconds field is unsigned 32-bit, so it
|
|
1843
|
+
wraps every ~136.19 years and the era is not part of the value. `fromNtpTimestamp` takes it
|
|
1844
|
+
as an explicit argument:
|
|
1845
|
+
|
|
1846
|
+
```typescript
|
|
1847
|
+
toNtpTimestamp("2036-02-07T06:28:15Z");
|
|
1848
|
+
// 18446744069414584320n — the last second of era 0
|
|
1849
|
+
|
|
1850
|
+
toNtpTimestamp("2036-02-07T06:28:16Z");
|
|
1851
|
+
// 0n — era 1 begins, and the seconds field wraps
|
|
1852
|
+
|
|
1853
|
+
fromNtpTimestamp(0n);
|
|
1854
|
+
// "1900-01-01T00:00:00Z"
|
|
1855
|
+
|
|
1856
|
+
fromNtpTimestamp(0n, 1);
|
|
1857
|
+
// "2036-02-07T06:28:16Z" — the same value, one era on
|
|
1858
|
+
```
|
|
1859
|
+
|
|
1860
|
+
**Excel's serial 60 is the phantom 29 February 1900**, a date that never existed — 1900 was
|
|
1861
|
+
not a leap year. Lotus 1-2-3 got it wrong and Excel keeps the bug for compatibility, so the
|
|
1862
|
+
serial day `[60, 61)` is a hole rather than a date. "Fixing" it would put GMT one day out
|
|
1863
|
+
from Excel for every date before March 1900:
|
|
1864
|
+
|
|
1865
|
+
```typescript
|
|
1866
|
+
fromExcelSerial(59);
|
|
1867
|
+
// "1900-02-28T00:00:00Z"
|
|
1868
|
+
|
|
1869
|
+
fromExcelSerial(60);
|
|
1870
|
+
// "" — the phantom 1900-02-29
|
|
1871
|
+
|
|
1872
|
+
fromExcelSerial(61);
|
|
1873
|
+
// "1900-03-01T00:00:00Z"
|
|
1874
|
+
|
|
1875
|
+
toExcelSerial("2024-03-10T12:00:00Z");
|
|
1876
|
+
// 45361.5 — days, with the time of day as the fraction
|
|
1877
|
+
|
|
1878
|
+
toExcelSerial("2024-03-10T12:00:00Z", { system: "1904" });
|
|
1879
|
+
// 43899.5 — the legacy Mac system, exactly 1462 days lower
|
|
1880
|
+
```
|
|
1881
|
+
|
|
1882
|
+
Each bridge accepts only what its target format can actually hold, and returns the
|
|
1883
|
+
namespace's sentinel otherwise — `toFileTime` on a pre-1601 instant is `0n`, not a negative
|
|
1884
|
+
tick count, and `toExcelSerial` past 9999-12-31 is `null`, not an out-of-range serial.
|
|
1885
|
+
PostgreSQL's range is the one that surprises: its timestamp domain starts at 4713 BC, which
|
|
1886
|
+
is 40× later than the earliest instant Temporal can represent, so an in-range Temporal
|
|
1887
|
+
instant is not automatically an in-range PostgreSQL one. Where
|
|
1888
|
+
the target unit is coarser than a nanosecond the conversion floors toward negative infinity,
|
|
1889
|
+
so a round trip is exact to that unit: 100 ns for `FILETIME` and .NET ticks, 1 µs for
|
|
1890
|
+
PostgreSQL, 1 ms for Excel. NTP is the exception in the other direction — its 2^-32 s unit is
|
|
1891
|
+
finer than a nanosecond, so `fromNtpTimestamp` rounds to the nearest nanosecond and the round
|
|
1892
|
+
trip is exact.
|
|
1893
|
+
### Spans
|
|
1894
|
+
|
|
1895
|
+
`diffZoned` measures in calendar units and returns a `Duration`. Profiling, tracing and
|
|
1896
|
+
telemetry want a raw number, and the `span/` namespace gives one:
|
|
1897
|
+
|
|
1898
|
+
```typescript
|
|
1899
|
+
import { spanMs, spanNs, spanWallClock } from "@northguild/gmt";
|
|
1900
|
+
|
|
1901
|
+
spanMs("2024-03-10T12:00:00Z", "2024-03-10T12:00:01Z");
|
|
1902
|
+
// 1000
|
|
1903
|
+
|
|
1904
|
+
spanMs("2024-03-10T12:00:00Z", "2024-03-10T12:00:00.123456789Z");
|
|
1905
|
+
// 123.456789 — fractional, like performance.now()
|
|
1906
|
+
|
|
1907
|
+
spanNs("2024-03-10T12:00:00.123456789Z", "2024-03-10T12:00:00.123456790Z");
|
|
1908
|
+
// 1n
|
|
1909
|
+
```
|
|
1910
|
+
|
|
1911
|
+
Both are signed — `spanMs(b, a)` is exactly `-spanMs(a, b)` — and both measure **exact
|
|
1912
|
+
elapsed time**. That is not the same question as calendar distance, and conflating the two
|
|
1913
|
+
is the most common span bug there is. A wall-clock day containing a DST transition is 23 or
|
|
1914
|
+
25 hours long (24.5 in `Australia/Lord_Howe`), so:
|
|
1915
|
+
|
|
1916
|
+
```typescript
|
|
1917
|
+
const start = "2024-03-09T12:00:00-05:00[America/New_York]";
|
|
1918
|
+
const end = "2024-03-10T12:00:00-04:00[America/New_York]";
|
|
1919
|
+
|
|
1920
|
+
spanMs(start, end);
|
|
1921
|
+
// 82800000 — 23 hours actually elapsed
|
|
1922
|
+
|
|
1923
|
+
spanWallClock(start, end, "hours");
|
|
1924
|
+
// 24 — the clock face advanced a full day
|
|
1925
|
+
|
|
1926
|
+
spanWallClock(start, end, "days");
|
|
1927
|
+
// 1
|
|
1928
|
+
```
|
|
1929
|
+
|
|
1930
|
+
`spanWallClock` reads each endpoint's own local wall clock — straight off the string, never
|
|
1931
|
+
via an instant, so DST disambiguation cannot distort it and a local time that never occurred
|
|
1932
|
+
is measured as written. The two endpoints need not share a zone: a flight leaving New York at 23:00 and landing in Berlin at 11:00 the next local day
|
|
1933
|
+
is 12 wall-clock hours and 7 elapsed hours. It truncates toward zero, and it is not a count
|
|
1934
|
+
of midnights crossed.
|
|
1935
|
+
|
|
1936
|
+
`isValidSpan(start, end)` answers "will these two produce a span?" — true exactly when
|
|
1937
|
+
`spanMs` and `spanNs` will return a value. It is symmetric, since a reversed pair is a
|
|
1938
|
+
negative span rather than an invalid one. `spanWallClock` has a different input grammar and
|
|
1939
|
+
is covered by `isValidZonedDateTime`.
|
|
1940
|
+
|
|
1941
|
+
Three limits, all deliberate:
|
|
1942
|
+
|
|
1943
|
+
- **`0` and `0n` are valid spans**, so invalid input returns `null` from all three rather
|
|
1944
|
+
than the zero the rest of the library uses for numbers. `null` and not `NaN`: it is the
|
|
1945
|
+
sentinel every other number-returning function in GMT uses, and the only one
|
|
1946
|
+
`strictNullChecks` forces a caller to handle — a `NaN` types as plain `number` and
|
|
1947
|
+
propagates silently through arithmetic.
|
|
1948
|
+
- **`spanMs` returns `null` past `Number.MAX_SAFE_INTEGER` milliseconds** (±285,000 years,
|
|
1949
|
+
which two instants at opposite ends of `Temporal.Instant`'s range exceed). A
|
|
1950
|
+
sub-millisecond fraction counts toward that ceiling, so what comes back is always a safe
|
|
1951
|
+
integer or below. Use `spanNs` there; its result is a duration, not an instant, and can be
|
|
1952
|
+
twice the epoch-nanosecond range.
|
|
1953
|
+
- **Leap seconds are not counted.** UTC repeats a second rather than numbering a 61st one,
|
|
1954
|
+
so a span across one is a second short of the physical elapsed time; against a smeared
|
|
1955
|
+
clock (Google, AWS, Meta) the error is up to a second spread over the smear window.
|
|
1956
|
+
|
|
1957
|
+
### Offset-preserving instants and local resolution
|
|
1958
|
+
|
|
1959
|
+
An instant orders events globally; the UTC offset in force where the event happened renders
|
|
1960
|
+
it as the human on the ground saw it. Neither derives from the other, which is why GS1 EPCIS
|
|
1961
|
+
2.0 requires both fields (`eventTime` plus `eventTimeZoneOffset`), UN/EDIFACT DTM has
|
|
1962
|
+
qualifiers `303`/`304` for the pair, and DICOM appends `&ZZXX` to a `DT` value. The `instant/`
|
|
1963
|
+
namespace is that pair:
|
|
1964
|
+
|
|
1965
|
+
```typescript
|
|
1966
|
+
import { fromOffsetInstant, toOffsetInstant } from "@northguild/gmt";
|
|
1967
|
+
|
|
1968
|
+
toOffsetInstant("2024-07-15T12:00:00-04:00[America/New_York]");
|
|
1969
|
+
// { instant: "2024-07-15T16:00:00Z", offset: "-04:00", timeZone: "America/New_York" }
|
|
1970
|
+
|
|
1971
|
+
// Most feeds send no zone at all — the offset alone is what happened.
|
|
1972
|
+
toOffsetInstant("2024-07-15T12:00:00-04:00");
|
|
1973
|
+
// { instant: "2024-07-15T16:00:00Z", offset: "-04:00" }
|
|
1974
|
+
|
|
1975
|
+
// A UTC-only feed plus a zone you know from somewhere else.
|
|
1976
|
+
toOffsetInstant("2024-07-15T16:00:00Z", "America/New_York");
|
|
1977
|
+
// { instant: "2024-07-15T16:00:00Z", offset: "-04:00", timeZone: "America/New_York" }
|
|
1978
|
+
|
|
1979
|
+
fromOffsetInstant({ instant: "2024-07-15T16:00:00Z", offset: "-04:00" });
|
|
1980
|
+
// "2024-07-15T12:00:00-04:00"
|
|
1981
|
+
```
|
|
1982
|
+
|
|
1983
|
+
**An offset is not a zone.** `-05:00` does not identify `America/New_York` — it is every zone
|
|
1984
|
+
sitting at `-05:00` that day, and it says nothing about what that zone will do next spring.
|
|
1985
|
+
Store the zone for anything still to be scheduled; store the offset for anything that already
|
|
1986
|
+
happened. `timeZone` is optional because most feeds do not send one, and a string whose
|
|
1987
|
+
offset contradicts its own bracketed zone returns `null` rather than a guess.
|
|
1988
|
+
|
|
1989
|
+
The other half of the namespace is the reverse problem: a wall time that arrives with no
|
|
1990
|
+
offset at all ("gate-out 08:00"). Resolving one needs a zone the sender did not send, plus a
|
|
1991
|
+
policy for the two days a year the mapping is not one-to-one:
|
|
1992
|
+
|
|
1993
|
+
```typescript
|
|
1994
|
+
import { classifyLocal, resolveLocal } from "@northguild/gmt";
|
|
1995
|
+
|
|
1996
|
+
classifyLocal("2024-11-03T01:30:00", "America/New_York"); // "ambiguous"
|
|
1997
|
+
classifyLocal("2024-03-10T02:30:00", "America/New_York"); // "nonexistent"
|
|
1998
|
+
classifyLocal("2024-07-15T12:00:00", "America/New_York"); // "unique"
|
|
1999
|
+
|
|
2000
|
+
resolveLocal("2024-11-03T01:30:00", "America/New_York");
|
|
2001
|
+
// "2024-11-03T05:30:00Z" — the default "compatible" takes the first 01:30
|
|
2002
|
+
|
|
2003
|
+
resolveLocal("2024-11-03T01:30:00", "America/New_York", { disambiguation: "later" });
|
|
2004
|
+
// "2024-11-03T06:30:00Z" — the same wall clock, an hour of real time later
|
|
2005
|
+
|
|
2006
|
+
resolveLocal("2024-11-03T01:30:00", "America/New_York", { disambiguation: "reject" });
|
|
2007
|
+
// "" — ambiguous, and not guessed
|
|
2008
|
+
```
|
|
2009
|
+
|
|
2010
|
+
`classifyLocal` exists so code can branch **before** a policy is applied. A demurrage clock, a
|
|
2011
|
+
medication window or a duty limit should refuse an ambiguous wall time, or route it to a
|
|
2012
|
+
human — not silently accept whichever of two instants an hour apart a default handed it. See
|
|
2013
|
+
[DST Disambiguation](../../docs/dst-disambiguation.md) for the four strategies in full.
|
|
2014
|
+
|
|
2015
|
+
Two notes on the boundaries of this namespace:
|
|
2016
|
+
|
|
2017
|
+
- **`resolveLocal` returns an instant; `convertPlainDateTimeToZoned` returns a zoned string.**
|
|
2018
|
+
Same underlying resolution, different output and different precision — `resolveLocal` is
|
|
2019
|
+
exact to the nanosecond, while `convertPlainDateTimeToZoned` defaults to milliseconds.
|
|
2020
|
+
Neither takes an `offset` option. Reach for whichever shape you need next.
|
|
2021
|
+
- **`offset` is `±HH:MM`, except where it is not.** A handful of zones did not run on a whole
|
|
2022
|
+
minute before 1972 — `Africa/Monrovia` really was `-00:44:30` — and those report
|
|
2023
|
+
`±HH:MM:SS`. Rounding them would put the pair thirty seconds from the event it describes.
|
|
2024
|
+
|
|
2025
|
+
### Calendar boundaries and zone-aware buckets
|
|
2026
|
+
|
|
2027
|
+
Two absences with outsized consequences, and they turn out to be the same absence twice: a
|
|
2028
|
+
calendar boundary is not a fixed number of hours, and it is not in UTC.
|
|
2029
|
+
|
|
2030
|
+
**Week and period identifiers.** Vessel schedules are published by week number and retail runs
|
|
2031
|
+
on 52/53-week fiscal calendars. Both come back as whole identifiers, not as fields to combine
|
|
2032
|
+
by hand — a week number without its week-numbering year is ambiguous at both ends of a year:
|
|
2033
|
+
|
|
2034
|
+
```typescript
|
|
2035
|
+
import { getFiscalPeriod, getIsoWeekDate, getOrdinalDate, getQuarter } from "@northguild/gmt";
|
|
2036
|
+
|
|
2037
|
+
getIsoWeekDate("2027-01-01"); // { year: 2026, week: 53, weekday: 5 } — week-year 2026, not 2027
|
|
2038
|
+
getOrdinalDate("2024-12-31"); // { year: 2024, dayOfYear: 366 }
|
|
2039
|
+
getQuarter("2024-03-31", { fiscalYearStartMonth: 4 }); // { year: 2023, quarter: 4 }
|
|
2040
|
+
|
|
2041
|
+
// The NRF retail calendar, stated as its published rule: "the Saturday nearest to January 31".
|
|
2042
|
+
const nrf = { pattern: "4-5-4", yearEndsOn: "2026-01-31" } as const;
|
|
2043
|
+
|
|
2044
|
+
getFiscalPeriod("2024-06-15", nrf); // { year: 2024, period: 5, week: 19 }
|
|
2045
|
+
getFiscalPeriod("2024-01-28", nrf); // { year: 2023, period: 12, week: 53 } — a 53-week year
|
|
2046
|
+
```
|
|
2047
|
+
|
|
2048
|
+
52 × 7 is 364 days, so a 53rd week is inserted every five or six years and lands in the final
|
|
2049
|
+
period. `yearEndsOn` states the *rule*, by example — years end on that date's weekday, nearest
|
|
2050
|
+
that date's month and day — not one year's end, because GMT bundles no fiscal calendar. There
|
|
2051
|
+
is no single correct retail calendar to bundle.
|
|
2052
|
+
|
|
2053
|
+
**Zone-aware bucketing.** "Group by day in `America/New_York`" over UTC timestamps is the most
|
|
2054
|
+
common observability bug there is, and the same operation decides how many chargeable days a
|
|
2055
|
+
container accrued, because free time is counted in terminal-local calendar days:
|
|
2056
|
+
|
|
2057
|
+
```typescript
|
|
2058
|
+
import { bucketRange, floorToZone } from "@northguild/gmt";
|
|
2059
|
+
|
|
2060
|
+
floorToZone("2024-06-15T03:00:00Z", "day", "America/New_York"); // "2024-06-14T04:00:00Z"
|
|
2061
|
+
floorToZone("2024-06-15T03:00:00Z", "day", "UTC"); // "2024-06-15T00:00:00Z"
|
|
2062
|
+
```
|
|
2063
|
+
|
|
2064
|
+
Same instant, different calendar day — 03:00 UTC on 15 June is still 14 June in New York, and
|
|
2065
|
+
flooring it to the UTC day is wrong for most of the world for most of the day. The zone is an
|
|
2066
|
+
argument because GMT has no ambient one and must not acquire one.
|
|
2067
|
+
|
|
2068
|
+
`bucketRange` returns the boundaries spanning a range, and **the buckets are deliberately not
|
|
2069
|
+
uniform in length**:
|
|
2070
|
+
|
|
2071
|
+
```typescript
|
|
2072
|
+
bucketRange("2024-03-09T05:00:00Z", "2024-03-12T04:00:00Z", "day", "America/New_York");
|
|
2073
|
+
// ["2024-03-09T05:00:00Z", "2024-03-10T05:00:00Z", "2024-03-11T04:00:00Z"]
|
|
2074
|
+
// 24h, then 23h — the middle day springs forward
|
|
2075
|
+
|
|
2076
|
+
bucketRange("2024-11-02T04:00:00Z", "2024-11-05T05:00:00Z", "day", "America/New_York");
|
|
2077
|
+
// ["2024-11-02T04:00:00Z", "2024-11-03T04:00:00Z", "2024-11-04T05:00:00Z"]
|
|
2078
|
+
// 24h, then 25h — the middle day falls back
|
|
2079
|
+
```
|
|
2080
|
+
|
|
2081
|
+
Forcing 24 hours here is what makes a daily aggregate drift an hour twice a year. The same
|
|
2082
|
+
honesty applies to boundaries that do not exist: the calendar day Samoa deleted crossing the
|
|
2083
|
+
date line is absent from the list, a local day whose midnight is skipped by a spring-forward
|
|
2084
|
+
starts at 01:00, and in a zone that falls back by half an hour (`Australia/Lord_Howe`) the
|
|
2085
|
+
local 01:00 hour bucket is genuinely 90 minutes long.
|
|
2086
|
+
|
|
2087
|
+
For a zoned or epoch value, `startOfZoned`/`startOfUnix` with no options return the same real
|
|
2088
|
+
boundary. Do not floor with `roundZoned`/`roundUnix` and `roundingMode: "trunc"`: they follow
|
|
2089
|
+
TC39 `ZonedDateTime.round`, which rounds the wall clock and re-resolves it in the zone, so
|
|
2090
|
+
`"2024-09-29T03:50:00+13:45[Pacific/Chatham]"` truncated to the hour gives 04:00 — after the
|
|
2091
|
+
input.
|
|
2092
|
+
|
|
2093
|
+
A pattern or a bucket unit the namespace does not recognise returns the same sentinel an
|
|
2094
|
+
unusable date or instant does, so both unions have a type guard to narrow one that arrived as
|
|
2095
|
+
a bare string — from config, an env var, a form:
|
|
2096
|
+
|
|
2097
|
+
```typescript
|
|
2098
|
+
import { isValidFiscalPattern, isValidZoneBucketUnit } from "@northguild/gmt";
|
|
2099
|
+
|
|
2100
|
+
isValidFiscalPattern("4-5-4"); // true — the NRF retail calendar
|
|
2101
|
+
isValidFiscalPattern("4-5-5"); // false — the shape a 53-week year's last quarter takes
|
|
2102
|
+
isValidZoneBucketUnit("day"); // true
|
|
2103
|
+
isValidZoneBucketUnit("days"); // true — singular or plural, as floorToZone accepts
|
|
2104
|
+
isValidZoneBucketUnit("year"); // false — the year question is getQuarter's
|
|
2105
|
+
```
|
|
2106
|
+
|
|
2107
|
+
### Interval algebra
|
|
2108
|
+
|
|
2109
|
+
Four realms need the same operation and none of them could express it: sum the parts of an
|
|
2110
|
+
interval that fall inside a set of allowed windows. Laytime counts only the hours a charter
|
|
2111
|
+
clause allows, driver hours split a duty period around mandatory rest, demurrage free time
|
|
2112
|
+
counts only working days, and a trading window counts only continuous-session time. The
|
|
2113
|
+
`interval/` namespace is that one shared primitive, with one boundary rule:
|
|
2114
|
+
|
|
2115
|
+
**Half-open `[start, end)`.** An instant `t` is inside when `start ≤ t < end`. That is SQL:2011's
|
|
2116
|
+
closed-open `PERIOD`, RFC 5545's non-inclusive `DTEND` and Dijkstra's EWD831. A container gated
|
|
2117
|
+
out at exactly 17:00 has not used another day, and two consecutive shifts never double-count the
|
|
2118
|
+
instant they meet.
|
|
2119
|
+
|
|
2120
|
+
```typescript
|
|
2121
|
+
import {
|
|
2122
|
+
clampInterval,
|
|
2123
|
+
intersectIntervals,
|
|
2124
|
+
intervalContains,
|
|
2125
|
+
intervalsOverlap,
|
|
2126
|
+
isValidInterval,
|
|
2127
|
+
mergeIntervals,
|
|
2128
|
+
splitIntervalAt,
|
|
2129
|
+
subtractIntervals,
|
|
2130
|
+
sumIntervals,
|
|
2131
|
+
} from "@northguild/gmt";
|
|
2132
|
+
|
|
2133
|
+
const shift = { start: "2024-01-01T09:00:00Z", end: "2024-01-01T17:00:00Z" };
|
|
2134
|
+
|
|
2135
|
+
intervalContains(shift, "2024-01-01T09:00:00Z"); // true — start is inside
|
|
2136
|
+
intervalContains(shift, "2024-01-01T17:00:00Z"); // false — end is not
|
|
2137
|
+
|
|
2138
|
+
intervalsOverlap(shift, { start: "2024-01-01T17:00:00Z", end: "2024-01-01T18:00:00Z" });
|
|
2139
|
+
// false — touching intervals share no instant
|
|
2140
|
+
intersectIntervals(shift, { start: "2024-01-01T12:00:00Z", end: "2024-01-01T18:00:00Z" });
|
|
2141
|
+
// { start: "2024-01-01T12:00:00Z", end: "2024-01-01T17:00:00Z" }
|
|
2142
|
+
clampInterval({ start: "2024-01-01T08:00:00Z", end: "2024-01-01T12:00:00Z" }, shift);
|
|
2143
|
+
// { start: "2024-01-01T09:00:00Z", end: "2024-01-01T12:00:00Z" }
|
|
2144
|
+
|
|
2145
|
+
mergeIntervals([
|
|
2146
|
+
{ start: "2024-01-01T13:00:00Z", end: "2024-01-01T17:00:00Z" },
|
|
2147
|
+
{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T12:00:00Z" },
|
|
2148
|
+
{ start: "2024-01-01T12:00:00Z", end: "2024-01-01T13:00:00Z" },
|
|
2149
|
+
]);
|
|
2150
|
+
// [{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T17:00:00Z" }] — sorted, touching runs joined
|
|
2151
|
+
|
|
2152
|
+
const worked = subtractIntervals(shift, [{ start: "2024-01-01T12:00:00Z", end: "2024-01-01T13:00:00Z" }]);
|
|
2153
|
+
// [{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T12:00:00Z" },
|
|
2154
|
+
// { start: "2024-01-01T13:00:00Z", end: "2024-01-01T17:00:00Z" }]
|
|
2155
|
+
sumIntervals(worked); // "PT7H"
|
|
2156
|
+
|
|
2157
|
+
splitIntervalAt(shift, ["2024-01-01T15:00:00Z", "2024-01-01T11:00:00Z"]);
|
|
2158
|
+
// [{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T11:00:00Z" },
|
|
2159
|
+
// { start: "2024-01-01T11:00:00Z", end: "2024-01-01T15:00:00Z" },
|
|
2160
|
+
// { start: "2024-01-01T15:00:00Z", end: "2024-01-01T17:00:00Z" }] — pieces share no instant
|
|
2161
|
+
|
|
2162
|
+
isValidInterval({ start: "2024-01-01T17:00:00Z", end: "2024-01-01T09:00:00Z" }); // false — inverted
|
|
2163
|
+
```
|
|
2164
|
+
|
|
2165
|
+
- **Endpoints are instants.** An offset (`Z` or `±HH:MM`) is required and a `[Zone]` annotation is
|
|
2166
|
+
optional. As with a TC39 `Temporal.Instant`, endpoints compare by epoch nanoseconds, so two of
|
|
2167
|
+
them may name different zones. Leap seconds are rejected, and a `[u-ca=…]` annotation is ignored, as `Temporal.Instant.from` ignores it. For local-calendar
|
|
2168
|
+
edges, build them with `floorToZone` or `bucketRange` first.
|
|
2169
|
+
- **Outputs are the caller's own strings**, never re-serialised. When two strings spell the same
|
|
2170
|
+
instant, the first argument's spelling wins. No spec covers the tie, so this is a GMT rule.
|
|
2171
|
+
- **`start === end` is a valid empty interval.** It contains no instant, overlaps only an interval
|
|
2172
|
+
it lies strictly inside, and adds nothing to a sum. An inverted interval returns the sentinel:
|
|
2173
|
+
`false`, `null`, `[]` or `""`.
|
|
2174
|
+
- **`sumIntervals` is covered time.** It returns the length of the union, with overlaps counted
|
|
2175
|
+
once, as an exact duration with hours as the largest unit. That is exactly what TC39
|
|
2176
|
+
`Instant.prototype.until(…, { largestUnit: "hour" })` produces, because an instant has no
|
|
2177
|
+
calendar that says how long a day is. So `PT49H30M` is never written `P2DT1H30M`, a New York
|
|
2178
|
+
spring-forward day is `PT23H`, and `sumIntervals([])` is `"PT0S"`. The sum is exact past
|
|
2179
|
+
2^53 nanoseconds.
|
|
2180
|
+
- **`[]` means two things.** From `mergeIntervals`, `subtractIntervals` and `splitIntervalAt` it
|
|
2181
|
+
is both a legitimate result (a fully covered subtraction) and the invalid-input sentinel.
|
|
2182
|
+
`isValidInterval` tells them apart.
|
|
2183
|
+
|
|
2184
|
+
### Transport legs and dwell
|
|
2185
|
+
|
|
2186
|
+
Every transport mode shares three operations: add a leg's duration to a departure, show the
|
|
2187
|
+
arrival in the zone where it lands, and measure how long something sat at a terminal, berth,
|
|
2188
|
+
gate or yard. Every mode also gets the same two things wrong: a leg across a DST transition is a
|
|
2189
|
+
fixed number of *elapsed* hours, and dwell is charged in *local calendar days*, not hours.
|
|
2190
|
+
|
|
2191
|
+
```typescript
|
|
2192
|
+
import { dwellTime, etaAtZone, transitTime } from "@northguild/gmt";
|
|
2193
|
+
|
|
2194
|
+
// A leg is exact time in the departure's own zone. Four elapsed hours across the
|
|
2195
|
+
// spring-forward: the wall clock reads 04:00, not 03:00.
|
|
2196
|
+
transitTime("2024-03-09T23:00:00-05:00[America/New_York]", "PT4H");
|
|
2197
|
+
// "2024-03-10T04:00:00-04:00[America/New_York]"
|
|
2198
|
+
|
|
2199
|
+
// A day is 24 exact hours, not "the same wall time tomorrow".
|
|
2200
|
+
transitTime("2024-03-09T12:00:00-05:00[America/New_York]", "P1D");
|
|
2201
|
+
// "2024-03-10T13:00:00-04:00[America/New_York]"
|
|
2202
|
+
|
|
2203
|
+
// The departure's form is preserved: Z stays Z, an offset stays an offset.
|
|
2204
|
+
transitTime("2024-06-15T10:00:00Z", "PT36H"); // "2024-06-16T22:00:00Z"
|
|
2205
|
+
transitTime("2024-06-15T10:00:00+09:00", "PT1H"); // "2024-06-15T11:00:00+09:00"
|
|
2206
|
+
transitTime("2024-06-15T10:00:00+05:30:15", "PT1H"); // "2024-06-15T11:00:00+05:30:15"
|
|
2207
|
+
|
|
2208
|
+
// Calendar units need a reference point; a zone that contradicts its offset is rejected.
|
|
2209
|
+
transitTime("2024-06-15T10:00:00Z", "P1M"); // ""
|
|
2210
|
+
transitTime("2024-06-15T10:00:00-05:00[America/New_York]", "PT2H"); // "" (June is -04:00)
|
|
2211
|
+
|
|
2212
|
+
// Render a moment where it will be read. No disambiguation arises: on a fall-back night
|
|
2213
|
+
// two arrivals an hour apart print the same wall time, and the offset tells them apart.
|
|
2214
|
+
etaAtZone("2024-06-15T12:30:00Z", "Asia/Tokyo"); // "2024-06-15T21:30:00+09:00[Asia/Tokyo]"
|
|
2215
|
+
etaAtZone("2024-11-03T05:30:00Z", "America/New_York"); // "2024-11-03T01:30:00-04:00[America/New_York]"
|
|
2216
|
+
etaAtZone("2024-11-03T06:30:00Z", "America/New_York"); // "2024-11-03T01:30:00-05:00[America/New_York]"
|
|
2217
|
+
|
|
2218
|
+
// Dwell: exact duration, zone-local entry and exit, and the local calendar days touched.
|
|
2219
|
+
dwellTime("2024-06-15T22:30:00Z", "2024-06-16T01:00:00Z", "Europe/London");
|
|
2220
|
+
// { duration: "PT2H30M",
|
|
2221
|
+
// enter: "2024-06-15T23:30:00+01:00[Europe/London]",
|
|
2222
|
+
// exit: "2024-06-16T02:00:00+01:00[Europe/London]",
|
|
2223
|
+
// calendarDays: 2 }
|
|
2224
|
+
|
|
2225
|
+
// The same two instants, one fewer day: the zone decides.
|
|
2226
|
+
dwellTime("2024-06-15T22:30:00Z", "2024-06-16T01:00:00Z", "Europe/Amsterdam");
|
|
2227
|
+
// { duration: "PT2H30M",
|
|
2228
|
+
// enter: "2024-06-16T00:30:00+02:00[Europe/Amsterdam]",
|
|
2229
|
+
// exit: "2024-06-16T03:00:00+02:00[Europe/Amsterdam]",
|
|
2230
|
+
// calendarDays: 1 }
|
|
2231
|
+
|
|
2232
|
+
// The zone comes from targetZone or from the entry's bracketed zone. Bare instants with
|
|
2233
|
+
// neither return null: an offset is not a place, and a day count needs one.
|
|
2234
|
+
dwellTime("2024-06-15T22:30:00+01:00[Europe/London]", "2024-06-16T02:00:00+01:00[Europe/London]");
|
|
2235
|
+
// { duration: "PT3H30M",
|
|
2236
|
+
// enter: "2024-06-15T22:30:00+01:00[Europe/London]",
|
|
2237
|
+
// exit: "2024-06-16T02:00:00+01:00[Europe/London]",
|
|
2238
|
+
// calendarDays: 2 }
|
|
2239
|
+
dwellTime("2024-06-15T22:30:00Z", "2024-06-16T01:00:00Z"); // null
|
|
2240
|
+
|
|
2241
|
+
// Inverted intervals return null.
|
|
2242
|
+
dwellTime("2024-06-16T01:00:00Z", "2024-06-15T22:30:00Z", "Europe/London"); // null
|
|
2243
|
+
```
|
|
2244
|
+
|
|
2245
|
+
- **`transitTime` adds exact time.** Hours, minutes, seconds and fractions are elapsed time and a
|
|
2246
|
+
`D` component is 24 hours exactly, so the arrival wall clock reflects any DST shift in between.
|
|
2247
|
+
Years, months and weeks return `""`: no leg takes "a month" without a reference point. A
|
|
2248
|
+
negative duration moves backwards, which is how a departure is recovered from an arrival.
|
|
2249
|
+
- **`etaAtZone` renders a moment.** The input is an instant (`Z`, an offset, or an offset with a
|
|
2250
|
+
bracketed zone), and only the instant is read; `targetZone` is where it is rendered. The zone
|
|
2251
|
+
is the caller's fact: GMT does not resolve a port, airport or station code to a timezone. A
|
|
2252
|
+
fixed offset such as `"+09:00"` is accepted as a zone, as `isValidTimeZone` accepts it.
|
|
2253
|
+
`dwellTime` accepts one as `targetZone` too, and counts days in that offset, which observes
|
|
2254
|
+
no DST.
|
|
2255
|
+
- **`dwellTime.calendarDays` counts local dates, not hours.** It is the number of distinct
|
|
2256
|
+
local dates the half-open interval `[entry, exit)` touches: same date is `1`, across one
|
|
2257
|
+
local midnight is `2`, and an exit exactly at local midnight does not touch the new day. It
|
|
2258
|
+
is counted by walking the zone's real transitions, so a 23- or 25-hour local day is one day,
|
|
2259
|
+
a date the zone deleted (`Pacific/Apia`, 2011-12-30) is never touched, and a date the clock
|
|
2260
|
+
falls back into (`America/Goose_Bay`, 2010-11-07) is counted once. `duration` is the exact elapsed time with hours as the largest unit, as
|
|
2261
|
+
`sumIntervals` reports it.
|
|
2262
|
+
|
|
2263
|
+
### Free time and demurrage
|
|
2264
|
+
|
|
2265
|
+
Free time is the money calculation in container logistics, and none of its terms is a fact about
|
|
2266
|
+
the port or fixed by a world standard. How many days are free, whether the day of discharge is
|
|
2267
|
+
free day one, how free days and charged days are counted, and which clock a charge runs on are set
|
|
2268
|
+
by the carrier's tariff and the service contract. DCSA defines what demurrage, detention and
|
|
2269
|
+
storage are, but not how their days are counted. So every term is a parameter, no counting term has a default, and what comes back is the free-time window and the specific dates charged.
|
|
2270
|
+
|
|
2271
|
+
```typescript
|
|
2272
|
+
import { chargeableDays, demurrageClock, freeTimeExpiry } from "@northguild/gmt";
|
|
2273
|
+
|
|
2274
|
+
const tariff = { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" };
|
|
2275
|
+
|
|
2276
|
+
// A Friday afternoon discharge in New York with three free calendar days: the weekend is
|
|
2277
|
+
// consumed, and free time ends at Monday 00:00 local.
|
|
2278
|
+
freeTimeExpiry("2024-06-14T19:00:00Z", 3, tariff);
|
|
2279
|
+
// { freeTimeStart: "2024-06-14", lastFreeDay: "2024-06-16", expiresAt: "2024-06-17T04:00:00Z" }
|
|
2280
|
+
|
|
2281
|
+
// The same tariff read the other way: one more day. firstDay has no default.
|
|
2282
|
+
freeTimeExpiry("2024-06-14T19:00:00Z", 3, { ...tariff, firstDay: "nextDay" });
|
|
2283
|
+
// { freeTimeStart: "2024-06-15", lastFreeDay: "2024-06-17", expiresAt: "2024-06-18T04:00:00Z" }
|
|
2284
|
+
freeTimeExpiry("2024-06-14T19:00:00Z", 3, { basis: "calendar", timeZone: "America/New_York" }); // null
|
|
2285
|
+
|
|
2286
|
+
// Working days skip the weekend, and a terminal holiday moves expiry a day.
|
|
2287
|
+
const terminal = { weekend: [6, 7], holidays: [], timeZone: "America/New_York" };
|
|
2288
|
+
freeTimeExpiry("2024-06-14T19:00:00Z", 3, { ...tariff, basis: "working", calendar: terminal });
|
|
2289
|
+
// { freeTimeStart: "2024-06-14", lastFreeDay: "2024-06-18", expiresAt: "2024-06-19T04:00:00Z" }
|
|
2290
|
+
freeTimeExpiry("2024-06-14T19:00:00Z", 3, { ...tariff, basis: "working", calendar: { ...terminal, holidays: ["2024-06-17"] } });
|
|
2291
|
+
// { freeTimeStart: "2024-06-14", lastFreeDay: "2024-06-19", expiresAt: "2024-06-20T04:00:00Z" }
|
|
2292
|
+
|
|
2293
|
+
// Expiry is half-open: out exactly as free time ends is free, one second later is a charged day,
|
|
2294
|
+
// and the date is listed.
|
|
2295
|
+
chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:00Z", 3, tariff);
|
|
2296
|
+
// { freeDaysUsed: 3, chargeableDays: 0, expiresAt: "2024-06-17T04:00:00Z",
|
|
2297
|
+
// chargedDates: [], byTier: [{ from: 1, to: null, days: 0 }] }
|
|
2298
|
+
chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:01Z", 3, tariff);
|
|
2299
|
+
// { freeDaysUsed: 3, chargeableDays: 1, expiresAt: "2024-06-17T04:00:00Z",
|
|
2300
|
+
// chargedDates: ["2024-06-17"], byTier: [{ from: 1, to: null, days: 1 }] }
|
|
2301
|
+
|
|
2302
|
+
// Charged days are counted on their own basis. Where a tariff grants free time in working days,
|
|
2303
|
+
// the days after it are mostly charged as calendar days; some tariffs charge working days only.
|
|
2304
|
+
const juneteenth = { ...terminal, holidays: ["2024-06-19"] };
|
|
2305
|
+
chargeableDays("2024-06-14T19:00:00Z", "2024-06-24T15:00:00Z", 3, { ...tariff, basis: "working", calendar: juneteenth });
|
|
2306
|
+
// { freeDaysUsed: 3, chargeableDays: 6, expiresAt: "2024-06-19T04:00:00Z",
|
|
2307
|
+
// chargedDates: ["2024-06-19", "2024-06-20", "2024-06-21", "2024-06-22", "2024-06-23", "2024-06-24"],
|
|
2308
|
+
// byTier: [{ from: 1, to: null, days: 6 }] }
|
|
2309
|
+
chargeableDays("2024-06-14T19:00:00Z", "2024-06-24T15:00:00Z", 3, { ...tariff, basis: "working", chargeBasis: "working", calendar: juneteenth });
|
|
2310
|
+
// { freeDaysUsed: 3, chargeableDays: 3, expiresAt: "2024-06-19T04:00:00Z",
|
|
2311
|
+
// chargedDates: ["2024-06-20", "2024-06-21", "2024-06-24"],
|
|
2312
|
+
// byTier: [{ from: 1, to: null, days: 3 }] }
|
|
2313
|
+
|
|
2314
|
+
// Tiers are day bands, not rates: twelve charged days in bands of 5, 5 and the rest.
|
|
2315
|
+
chargeableDays("2024-06-14T19:00:00Z", "2024-06-28T15:00:00Z", 3, { ...tariff, tiers: [5, 10] });
|
|
2316
|
+
// { freeDaysUsed: 3, chargeableDays: 12, expiresAt: "2024-06-17T04:00:00Z",
|
|
2317
|
+
// chargedDates: ["2024-06-17", "2024-06-18", "2024-06-19", "2024-06-20", "2024-06-21",
|
|
2318
|
+
// "2024-06-22", "2024-06-23", "2024-06-24", "2024-06-25", "2024-06-26",
|
|
2319
|
+
// "2024-06-27", "2024-06-28"],
|
|
2320
|
+
// byTier: [{ from: 1, to: 5, days: 5 }, { from: 6, to: 10, days: 5 }, { from: 11, to: null, days: 2 }] }
|
|
2321
|
+
|
|
2322
|
+
// Which two events a charge runs between, on the import or the export leg.
|
|
2323
|
+
const imported = [
|
|
2324
|
+
{ type: "discharged", at: "2024-06-14T19:00:00Z" },
|
|
2325
|
+
{ type: "available", at: "2024-06-15T12:00:00Z" },
|
|
2326
|
+
{ type: "gatedOut", at: "2024-06-20T14:30:00Z" },
|
|
2327
|
+
{ type: "emptyReturned", at: "2024-06-27T09:00:00Z" },
|
|
2328
|
+
];
|
|
2329
|
+
demurrageClock(imported, "demurrage", { direction: "import" }); // { start: "2024-06-14T19:00:00Z", end: "2024-06-20T14:30:00Z" }
|
|
2330
|
+
demurrageClock(imported, "detention", { direction: "import" }); // { start: "2024-06-20T14:30:00Z", end: "2024-06-27T09:00:00Z" }
|
|
2331
|
+
demurrageClock(imported, "combined", { direction: "import" }); // { start: "2024-06-14T19:00:00Z", end: "2024-06-27T09:00:00Z" }
|
|
2332
|
+
demurrageClock(imported, "demurrage", { direction: "import", startEvent: "available" }); // { start: "2024-06-15T12:00:00Z", end: "2024-06-20T14:30:00Z" }
|
|
2333
|
+
const exported = [
|
|
2334
|
+
{ type: "emptyReleased", at: "2024-07-01T08:00:00Z" },
|
|
2335
|
+
{ type: "gatedIn", at: "2024-07-05T16:00:00Z" },
|
|
2336
|
+
{ type: "loaded", at: "2024-07-09T03:00:00Z" },
|
|
2337
|
+
];
|
|
2338
|
+
demurrageClock(exported, "demurrage", { direction: "export" }); // { start: "2024-07-05T16:00:00Z", end: "2024-07-09T03:00:00Z" }
|
|
2339
|
+
demurrageClock(exported, "detention", { direction: "export" }); // { start: "2024-07-01T08:00:00Z", end: "2024-07-05T16:00:00Z" }
|
|
2340
|
+
demurrageClock([{ type: "discharged", at: "2024-06-14T19:00:00Z" }], "demurrage", { direction: "import" }); // null (no gate-out yet)
|
|
2341
|
+
```
|
|
2342
|
+
|
|
2343
|
+
- **Days are the terminal's local days.** `clockStart` and `clockEnd` are instants; their local
|
|
2344
|
+
dates in `options.timeZone` are what is counted, over the zone's real day boundaries, the same
|
|
2345
|
+
ones `dwellTime` and `floorToZone` find. A 23- or 25-hour day is one day, a date the zone deleted
|
|
2346
|
+
is never a free or charged day, and a date the clock falls back into counts once. On the calendar
|
|
2347
|
+
basis with `firstDay: "eventDay"`, `freeDaysUsed + chargeableDays` is exactly
|
|
2348
|
+
`dwellTime(...).calendarDays` for the same dwell, except that a fall-back re-entering the day
|
|
2349
|
+
before the event day (Goose Bay, 7 November 2010) is a date `dwellTime` counts and a tariff
|
|
2350
|
+
never does.
|
|
2351
|
+
- **`firstDay` has no default.** `"eventDay"` makes the event day free day one; `"nextDay"` starts
|
|
2352
|
+
free time the following counted day. The two differ by a full day of charges, so omitting it
|
|
2353
|
+
returns `null`.
|
|
2354
|
+
- **`basis` counts free days; `chargeBasis` counts charged days.** `"calendar"` counts every local
|
|
2355
|
+
day; `"working"` counts only the working days of `options.calendar`, a `BusinessCalendar` (its
|
|
2356
|
+
weekend and holidays; its `timeZone` is not read), and returns `null` without one. Many tariffs
|
|
2357
|
+
count both in calendar days. Where a tariff grants free time in working days, the days after it
|
|
2358
|
+
are mostly charged as calendar days; some tariffs charge working days only. Neither has a default.
|
|
2359
|
+
"Calendar days excluding bank holidays" is `"working"` with an empty `weekend`.
|
|
2360
|
+
- **Expiry is half-open.** `expiresAt` is the first instant of the local day after `lastFreeDay`, as
|
|
2361
|
+
a UTC instant. A gate-out at exactly `expiresAt` is not a chargeable day; one nanosecond later is.
|
|
2362
|
+
- **`chargedDates` makes the count auditable.** It is the list a carrier's day-numbered tariff grid
|
|
2363
|
+
is applied to, and the dates an itemised invoice can list.
|
|
2364
|
+
- **Tiers are day bands, not rates.** `tiers: [5, 10]` names days 1–5, 6–10 and 11 onward; `byTier`
|
|
2365
|
+
says how many charged days fell in each band, empty bands included, so a rate table applies by
|
|
2366
|
+
index. Omitted, it is the single open band. GMT computes days, never money.
|
|
2367
|
+
- **`freeDays: 0`** is a tariff with no free time: `chargeableDays` charges every counted day from day
|
|
2368
|
+
one, and `freeTimeExpiry` returns `null` because there is no last free day to name.
|
|
2369
|
+
- **`demurrageClock` selects the pair, per leg.** DCSA's glossary puts demurrage inside the terminal
|
|
2370
|
+
or depot and detention outside it; the carriers' published tariffs agree on the events. Import: demurrage and
|
|
2371
|
+
storage run from `startEvent` (`"discharged"` by default, or `"available"`) to `gatedOut`, detention
|
|
2372
|
+
from `gatedOut` to `emptyReturned`, and the combined clock from `startEvent` to `emptyReturned`.
|
|
2373
|
+
Export: demurrage and storage run from `gatedIn` to `loaded`, detention from `emptyReleased` to
|
|
2374
|
+
`gatedIn`, and the combined clock from `emptyReleased` to `loaded`. `direction` has no default. The
|
|
2375
|
+
event names follow DCSA's Track & Trace equipment events (DISC, GTOT, GTIN, LOAD). A required event
|
|
2376
|
+
that is missing or present twice, or an end before its start, returns `null`, and the result is an
|
|
2377
|
+
`Interval` in the caller's own strings, ready for `chargeableDays(start, end, …)`.
|
|
2378
|
+
|
|
2379
|
+
### Billing deadlines
|
|
2380
|
+
|
|
2381
|
+
A billing regime or service contract can set up to three windows around a demurrage or
|
|
2382
|
+
detention invoice: one to issue it, counted from an anchor date; one to dispute it, counted from
|
|
2383
|
+
issuance; and one to resolve the dispute, counted from the request, unless the parties
|
|
2384
|
+
agree a date instead. The number of days in each is the caller's fact; GMT carries none of them. The arithmetic
|
|
2385
|
+
is the function's: each deadline is a date, counted in calendar days from a date, with the anchor
|
|
2386
|
+
as day zero and the deadline day itself inside the window.
|
|
2387
|
+
|
|
2388
|
+
```typescript
|
|
2389
|
+
import { billingTimeline, convertUtcToPlainDate } from "@northguild/gmt";
|
|
2390
|
+
|
|
2391
|
+
const windows = { issueDays: 30, disputeDays: 30, resolutionDays: 30 };
|
|
2392
|
+
|
|
2393
|
+
// Charges last accrued on 1 March. Day 30 is the last day by the deadline; day 31 is not.
|
|
2394
|
+
billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-31" }, windows);
|
|
2395
|
+
// { invoiceDeadline: "2026-03-31", issuedByDeadline: true, disputeDeadline: "2026-04-30",
|
|
2396
|
+
// requestedByDeadline: null, resolutionDeadline: null }
|
|
2397
|
+
billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-04-01" }, windows);
|
|
2398
|
+
// { invoiceDeadline: "2026-03-31", issuedByDeadline: false, disputeDeadline: "2026-05-01",
|
|
2399
|
+
// requestedByDeadline: null, resolutionDeadline: null }
|
|
2400
|
+
|
|
2401
|
+
// No invoice yet: a forecast. Only the invoice deadline is known.
|
|
2402
|
+
billingTimeline({ anchorOn: "2026-03-01" }, windows);
|
|
2403
|
+
// { invoiceDeadline: "2026-03-31", issuedByDeadline: null, disputeDeadline: null,
|
|
2404
|
+
// requestedByDeadline: null, resolutionDeadline: null }
|
|
2405
|
+
|
|
2406
|
+
// A dispute received on the dispute deadline, and one received a day later with a resolution
|
|
2407
|
+
// date the parties agreed, which replaces the computed one.
|
|
2408
|
+
billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-20", requestReceivedOn: "2026-04-19" }, windows);
|
|
2409
|
+
// { invoiceDeadline: "2026-03-31", issuedByDeadline: true, disputeDeadline: "2026-04-19",
|
|
2410
|
+
// requestedByDeadline: true, resolutionDeadline: "2026-05-19" }
|
|
2411
|
+
billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-20", requestReceivedOn: "2026-04-20" }, { ...windows, agreedResolutionOn: "2026-06-01" });
|
|
2412
|
+
// { invoiceDeadline: "2026-03-31", issuedByDeadline: true, disputeDeadline: "2026-04-19",
|
|
2413
|
+
// requestedByDeadline: false, resolutionDeadline: "2026-06-01" }
|
|
2414
|
+
|
|
2415
|
+
// A re-bill is anchored on the invoice it received (issued 10 March), not on the charge.
|
|
2416
|
+
billingTimeline({ anchorOn: "2026-03-10", invoiceIssuedOn: "2026-04-05" }, windows);
|
|
2417
|
+
// { invoiceDeadline: "2026-04-09", issuedByDeadline: true, disputeDeadline: "2026-05-05",
|
|
2418
|
+
// requestedByDeadline: null, resolutionDeadline: null }
|
|
2419
|
+
|
|
2420
|
+
// A 14/14/45 service contract through the same chain: the numbers are the caller's.
|
|
2421
|
+
billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-05", requestReceivedOn: "2026-03-18" }, { issueDays: 14, disputeDays: 14, resolutionDays: 45 });
|
|
2422
|
+
// { invoiceDeadline: "2026-03-15", issuedByDeadline: true, disputeDeadline: "2026-03-19",
|
|
2423
|
+
// requestedByDeadline: true, resolutionDeadline: "2026-05-02" }
|
|
2424
|
+
|
|
2425
|
+
// Windows have no defaults.
|
|
2426
|
+
billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-31" }, { disputeDays: 30, resolutionDays: 30 }); // null
|
|
2427
|
+
|
|
2428
|
+
// Deadlines are dates. Reduce an instant to the billing party's local date first.
|
|
2429
|
+
convertUtcToPlainDate("2024-06-17T04:00:01Z", { timeZone: "America/New_York" }); // "2024-06-17"
|
|
2430
|
+
```
|
|
2431
|
+
|
|
2432
|
+
- **Day zero is the anchor and the deadline is `anchor + days`** on the ISO calendar
|
|
2433
|
+
(`Temporal.PlainDate.add`); a date is by the deadline when it is on or before it
|
|
2434
|
+
(`Temporal.PlainDate.compare(date, deadline) <= 0`). That is the function's stated contract, not
|
|
2435
|
+
a reading of any rule; a regime that counts differently passes a different number. Thirty days
|
|
2436
|
+
across a leap day, a month end, a year end or a DST change is thirty dates, never thirty times
|
|
2437
|
+
24 hours: `2028-01-30` plus 30 is `2028-02-29`.
|
|
2438
|
+
- **Deadlines are dates, never instants.** Reduce an instant to the billing party's local date
|
|
2439
|
+
first with `convertUtcToPlainDate(instant, { timeZone })`; the function does not guess a zone,
|
|
2440
|
+
and a date-time or an instant passed as a date returns `null`.
|
|
2441
|
+
- **Windows have no defaults.** Each is a safe integer of at least `0`; a missing, negative or
|
|
2442
|
+
non-integer window returns `null`, because a silently defaulted window is a wrong deadline. The
|
|
2443
|
+
same rule as `firstDay`.
|
|
2444
|
+
- **The anchor is whatever date the caller counts from.** The last date a charge accrued is
|
|
2445
|
+
`chargeableDays(...).chargedDates.at(-1)`. A party re-billing a charge it was itself billed
|
|
2446
|
+
passes the issuance date of the invoice it received; the chain is the same.
|
|
2447
|
+
- **The chain fills in as its dates exist.** With only `anchorOn` the result is a forecast:
|
|
2448
|
+
`invoiceDeadline` is set and every other field is `null`. An invoice date sets
|
|
2449
|
+
`issuedByDeadline` and `disputeDeadline`; a request date sets `requestedByDeadline` and
|
|
2450
|
+
`resolutionDeadline`. A request without an invoice, or dated before it, is a data error and
|
|
2451
|
+
returns `null`; an invoice dated before the anchor is allowed, because an invoice may be issued
|
|
2452
|
+
while charges still accrue.
|
|
2453
|
+
- **An agreed date replaces the computed resolution deadline.** `agreedResolutionOn` must be a
|
|
2454
|
+
valid date on or after `requestReceivedOn`, else `null`; it is validated even when no request
|
|
2455
|
+
exists, but cannot act without one. Every emitted date is bare ISO, so an input annotation such
|
|
2456
|
+
as `[u-ca=iso8601]` does not reach the output.
|
|
2457
|
+
- **GMT computes dates, not liability.** `issuedByDeadline` and `requestedByDeadline` compare
|
|
2458
|
+
dates and say nothing else. Whether a charge is payable, whether a dispute must be heard, and
|
|
2459
|
+
any consequence are the consumer's, the sibling of "GMT computes days, never money".
|
|
2460
|
+
|
|
1607
2461
|
## API Surface
|
|
1608
2462
|
|
|
1609
2463
|
For the complete API listing, see the namespace documentation on GitHub:
|
|
@@ -1612,6 +2466,13 @@ For the complete API listing, see the namespace documentation on GitHub:
|
|
|
1612
2466
|
- [Plain API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/plain) — timezone-free operations
|
|
1613
2467
|
- [Zoned API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/zoned) — IANA timezone-aware operations
|
|
1614
2468
|
- [Unix API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/unix) — Unix epoch utilities
|
|
2469
|
+
- [Precision API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/precision) — nanosecond instants, JSON transport, storage truncation, NTP / FILETIME / .NET ticks / Excel / PostgreSQL epoch bridges
|
|
2470
|
+
- [Span API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/span) — elapsed and wall-clock durations as raw numbers
|
|
2471
|
+
- [Calendar API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/calendar) — ISO week and ordinal dates, quarter and fiscal periods, zone-aware bucketing, and business calendars with holiday sets and roll conventions
|
|
2472
|
+
- [Interval API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/interval) — half-open interval algebra over instants: overlap, intersect, clamp, merge, subtract, split, sum
|
|
2473
|
+
- [Transport API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/transport) — transit legs as exact elapsed time, arrivals rendered in the zone where they land, dwell in local calendar days
|
|
2474
|
+
- [Intermodal API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/intermodal) — free time, demurrage and detention: which clock, which start day, calendar or working days, and the charged dates an itemised invoice can list, plus the deadline chain around the invoice with every window a caller parameter
|
|
2475
|
+
- [Instant API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/instant) — the instant-plus-offset pair, and explicit local-time resolution
|
|
1615
2476
|
- [UTC API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/utc) — UTC instant utilities
|
|
1616
2477
|
- [Regex API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/regex) — composable regex patterns
|
|
1617
2478
|
|
|
@@ -1647,7 +2508,7 @@ When working with `@northguild/gmt`, follow these rules:
|
|
|
1647
2508
|
3. **Invalid input returns a sentinel, never throws.** `""` for strings, `null` for numbers, `false` for booleans, `[]` for arrays.
|
|
1648
2509
|
4. **Wrap all Temporal calls in `try-catch`.** `.from()`, `.add()`, `.since()`, etc. throw `RangeError` on bad input.
|
|
1649
2510
|
5. **Keep `plain/` and `zoned/` strictly separate.** Never mix `PlainDateTime` and `ZonedDateTime`.
|
|
1650
|
-
6. **Full locale matrix for any locale-aware function.** 17 locales, explicit rows, `
|
|
2511
|
+
6. **Full locale matrix for any locale-aware function.** 17 locales, explicit rows, `expectOneOfIcu`/`expectDateTimeEqual` (from `src/test/icuVariants.ts`) where CLDR wording differs.
|
|
1651
2512
|
7. **Use pre-built mocks for error-path tests.** See `packages/gmt/src/test/mocks`.
|
|
1652
2513
|
8. **JSDoc with `@example` on every public function.** Cover valid, invalid, and edge-case inputs.
|
|
1653
2514
|
|