@northguild/gmt 1.16.0 → 1.18.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 +742 -21
- package/dist/calendar/calculate/bucketRange.d.ts +3 -3
- package/dist/calendar/calculate/bucketRange.js +6 -6
- package/dist/calendar/calculate/floorToZone.d.ts +5 -3
- package/dist/calendar/calculate/floorToZone.js +6 -5
- package/dist/calendar/calculate/getFiscalPeriod.d.ts +21 -8
- package/dist/calendar/calculate/getFiscalPeriod.js +9 -8
- package/dist/calendar/calculate/getIsoWeekDate.d.ts +10 -5
- package/dist/calendar/calculate/getIsoWeekDate.js +1 -5
- package/dist/calendar/calculate/getOrdinalDate.d.ts +10 -3
- package/dist/calendar/calculate/getOrdinalDate.js +2 -3
- package/dist/calendar/calculate/getQuarter.d.ts +21 -9
- package/dist/calendar/calculate/getQuarter.js +8 -12
- package/dist/calendar/hours/addOperatingTime.d.ts +53 -0
- package/dist/calendar/hours/addOperatingTime.js +59 -0
- package/dist/calendar/hours/index.d.ts +7 -0
- package/dist/calendar/hours/index.js +7 -0
- package/dist/calendar/hours/isOpenAt.d.ts +39 -0
- package/dist/calendar/hours/isOpenAt.js +44 -0
- package/dist/calendar/hours/nextCloseAt.d.ts +49 -0
- package/dist/calendar/hours/nextCloseAt.js +51 -0
- package/dist/calendar/hours/nextOpenAt.d.ts +45 -0
- package/dist/calendar/hours/nextOpenAt.js +47 -0
- package/dist/calendar/hours/operatingIntervals.d.ts +43 -0
- package/dist/calendar/hours/operatingIntervals.js +52 -0
- package/dist/calendar/hours/operatingTimeBetween.d.ts +40 -0
- package/dist/calendar/hours/operatingTimeBetween.js +49 -0
- package/dist/calendar/hours/recurringWindows.d.ts +61 -0
- package/dist/calendar/hours/recurringWindows.js +80 -0
- package/dist/calendar/index.d.ts +1 -0
- package/dist/calendar/index.js +1 -0
- package/dist/duration/calculate/durationAs.d.ts +13 -8
- package/dist/duration/calculate/durationAs.js +4 -8
- package/dist/duration/compare/compareDurations.d.ts +12 -6
- package/dist/duration/compare/compareDurations.js +3 -6
- package/dist/duration/format/formatDuration.d.ts +16 -4
- package/dist/duration/format/formatDuration.js +4 -5
- package/dist/duration/normalize/normalizeDuration.d.ts +39 -14
- package/dist/duration/normalize/normalizeDuration.js +6 -15
- package/dist/duration/parse/parseDuration.d.ts +21 -3
- package/dist/duration/parse/parseDuration.js +1 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/instant/convert/classifyLocal.d.ts +1 -1
- package/dist/instant/convert/classifyLocal.js +1 -1
- package/dist/instant/convert/fromOffsetInstant.d.ts +3 -1
- package/dist/instant/convert/fromOffsetInstant.js +6 -12
- package/dist/instant/convert/resolveLocal.d.ts +12 -7
- package/dist/instant/convert/resolveLocal.js +6 -10
- package/dist/instant/convert/toOffsetInstant.d.ts +37 -1
- package/dist/instant/convert/toOffsetInstant.js +12 -28
- package/dist/intermodal/calculate/billingTimeline.d.ts +114 -0
- package/dist/intermodal/calculate/billingTimeline.js +168 -0
- package/dist/intermodal/calculate/chargeableDays.d.ts +100 -0
- package/dist/intermodal/calculate/chargeableDays.js +96 -0
- package/dist/intermodal/calculate/demurrageClock.d.ts +102 -0
- package/dist/intermodal/calculate/demurrageClock.js +140 -0
- package/dist/intermodal/calculate/freeTimeExpiry.d.ts +67 -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/businessCalendar.d.ts +5 -0
- package/dist/internal/businessCalendar.js +5 -1
- package/dist/internal/cycleFieldValue.d.ts +6 -4
- package/dist/internal/cycleFieldValue.js +6 -4
- package/dist/internal/daylightTime.d.ts +33 -0
- package/dist/internal/daylightTime.js +164 -0
- package/dist/internal/durationUntilString.js +4 -3
- package/dist/internal/englishCalendarNames.js +1 -2
- package/dist/internal/epochNanoseconds.d.ts +2 -0
- package/dist/internal/epochNanoseconds.js +2 -0
- package/dist/internal/exactDuration.d.ts +37 -0
- package/dist/internal/exactDuration.js +64 -0
- package/dist/internal/exactMoment.d.ts +72 -0
- package/dist/internal/exactMoment.js +114 -0
- package/dist/internal/fiscalCalendar.d.ts +4 -3
- package/dist/internal/fiscalCalendar.js +4 -3
- package/dist/internal/formatCalendarDays.d.ts +2 -2
- package/dist/internal/formatRelativeDuration.d.ts +21 -1
- package/dist/internal/formatRelativeDuration.js +47 -7
- package/dist/internal/freeTimeLedger.d.ts +93 -0
- package/dist/internal/freeTimeLedger.js +262 -0
- package/dist/internal/index.d.ts +10 -3
- package/dist/internal/index.js +10 -3
- package/dist/internal/instantFormatOptions.js +2 -1
- package/dist/internal/instantNanoseconds.d.ts +75 -9
- package/dist/internal/instantNanoseconds.js +192 -11
- package/dist/internal/isoStringBody.d.ts +26 -0
- package/dist/internal/isoStringBody.js +22 -2
- package/dist/internal/joinDateTimeConnector.d.ts +1 -1
- package/dist/internal/monthGridWeekRow.d.ts +2 -1
- package/dist/internal/monthGridWeekRow.js +2 -1
- package/dist/internal/normalizeTimeZone.d.ts +2 -2
- package/dist/internal/normalizeTimeZone.js +2 -2
- package/dist/internal/operatingSchedule.d.ts +129 -0
- package/dist/internal/operatingSchedule.js +668 -0
- package/dist/internal/optionOrDefault.d.ts +18 -0
- package/dist/internal/optionOrDefault.js +20 -0
- package/dist/internal/patternToken.d.ts +2 -2
- package/dist/internal/patternToken.js +28 -13
- package/dist/internal/plainDateUntil.js +10 -6
- package/dist/internal/punctuality.d.ts +38 -0
- package/dist/internal/punctuality.js +59 -0
- package/dist/internal/readDateTimeFormatOptions.d.ts +30 -0
- package/dist/internal/readDateTimeFormatOptions.js +93 -0
- package/dist/internal/resolveDurationRelativeTo.d.ts +4 -0
- package/dist/internal/resolveDurationRelativeTo.js +11 -1
- package/dist/internal/resolveRelativeRounding.d.ts +17 -6
- package/dist/internal/resolveRelativeRounding.js +27 -7
- package/dist/internal/resolveRelativeUnit.d.ts +30 -0
- package/dist/internal/resolveRelativeUnit.js +40 -0
- package/dist/internal/splitStep.d.ts +24 -1
- package/dist/internal/splitStep.js +34 -2
- package/dist/internal/startOrEndOfZoned.js +2 -3
- package/dist/internal/temporalCompat/capabilities.d.ts +26 -0
- package/dist/internal/temporalCompat/capabilities.js +35 -3
- package/dist/internal/temporalCompat/fieldSearch.js +1 -1
- package/dist/internal/temporalCompat/index.d.ts +1 -1
- package/dist/internal/temporalCompat/index.js +1 -1
- package/dist/internal/temporalCompat/repros.d.ts +1 -1
- package/dist/internal/temporalCompat/repros.js +157 -0
- package/dist/internal/timestampEvents.d.ts +32 -0
- package/dist/internal/timestampEvents.js +76 -0
- package/dist/internal/utcZonedDateTime.d.ts +5 -2
- package/dist/internal/utcZonedDateTime.js +8 -5
- package/dist/internal/wallClockAtOffset.d.ts +22 -0
- package/dist/internal/wallClockAtOffset.js +26 -0
- package/dist/internal/zonedBucket.js +8 -6
- package/dist/internal/zonedLocalDates.js +1 -1
- package/dist/internal/zonedTransitionSearch.d.ts +27 -0
- package/dist/internal/zonedTransitionSearch.js +218 -0
- package/dist/internal/zonedWallClock.d.ts +21 -7
- package/dist/internal/zonedWallClock.js +87 -18
- package/dist/internal/zonedWallClockDifference.js +5 -4
- package/dist/internal/zonedWallClockOperations.d.ts +26 -13
- package/dist/internal/zonedWallClockOperations.js +87 -34
- package/dist/interval/validate/isValidInterval.d.ts +6 -3
- package/dist/interval/validate/isValidInterval.js +6 -3
- package/dist/plain/calculate/addDate.d.ts +11 -8
- package/dist/plain/calculate/addDate.js +5 -8
- package/dist/plain/calculate/addDateTime.d.ts +7 -4
- package/dist/plain/calculate/addDateTime.js +1 -4
- package/dist/plain/calculate/cycleDate.d.ts +16 -7
- package/dist/plain/calculate/cycleDate.js +3 -7
- package/dist/plain/calculate/cycleDateTime.d.ts +14 -4
- package/dist/plain/calculate/cycleDateTime.js +1 -4
- package/dist/plain/calculate/cycleTime.d.ts +10 -5
- package/dist/plain/calculate/cycleTime.js +3 -5
- package/dist/plain/calculate/diffDate.d.ts +1 -1
- package/dist/plain/calculate/diffDate.js +1 -1
- package/dist/plain/calculate/diffDateAsDuration.d.ts +1 -1
- package/dist/plain/calculate/diffDateAsDuration.js +1 -1
- package/dist/plain/calculate/diffDateTime.d.ts +1 -1
- package/dist/plain/calculate/diffDateTime.js +1 -1
- package/dist/plain/calculate/diffDateTimeAsDuration.d.ts +1 -1
- package/dist/plain/calculate/diffDateTimeAsDuration.js +1 -1
- package/dist/plain/calculate/diffTime.d.ts +1 -1
- package/dist/plain/calculate/diffTime.js +1 -1
- package/dist/plain/calculate/endOfDate.d.ts +8 -3
- package/dist/plain/calculate/endOfDate.js +1 -3
- package/dist/plain/calculate/endOfDateTime.d.ts +15 -5
- package/dist/plain/calculate/endOfDateTime.js +1 -5
- package/dist/plain/calculate/endOfTime.d.ts +8 -2
- package/dist/plain/calculate/endOfTime.js +1 -2
- package/dist/plain/calculate/getLocaleWeekYear.d.ts +16 -9
- package/dist/plain/calculate/getLocaleWeekYear.js +9 -9
- package/dist/plain/calculate/getWeekOfMonth.d.ts +7 -4
- package/dist/plain/calculate/getWeekOfMonth.js +7 -4
- package/dist/plain/calculate/getWeeksInLocaleWeekYear.d.ts +13 -6
- package/dist/plain/calculate/getWeeksInLocaleWeekYear.js +6 -6
- package/dist/plain/calculate/getWeeksInMonth.d.ts +5 -2
- package/dist/plain/calculate/getWeeksInMonth.js +5 -2
- package/dist/plain/calculate/nextWeekday.d.ts +7 -3
- package/dist/plain/calculate/nextWeekday.js +1 -3
- package/dist/plain/calculate/previousWeekday.d.ts +7 -3
- package/dist/plain/calculate/previousWeekday.js +1 -3
- package/dist/plain/calculate/roundDate.d.ts +20 -7
- package/dist/plain/calculate/roundDate.js +3 -10
- package/dist/plain/calculate/roundDateTime.d.ts +22 -8
- package/dist/plain/calculate/roundDateTime.js +3 -11
- package/dist/plain/calculate/roundTime.d.ts +20 -4
- package/dist/plain/calculate/roundTime.js +3 -7
- package/dist/plain/calculate/setDate.d.ts +7 -5
- package/dist/plain/calculate/setDate.js +1 -5
- package/dist/plain/calculate/setDateTime.d.ts +8 -5
- package/dist/plain/calculate/setDateTime.js +1 -5
- package/dist/plain/calculate/setTime.d.ts +10 -6
- package/dist/plain/calculate/setTime.js +4 -6
- package/dist/plain/calculate/startOfDate.d.ts +8 -2
- package/dist/plain/calculate/startOfDate.js +1 -2
- package/dist/plain/calculate/startOfDateTime.d.ts +15 -2
- package/dist/plain/calculate/startOfDateTime.js +1 -2
- package/dist/plain/calculate/startOfTime.d.ts +8 -1
- package/dist/plain/calculate/startOfTime.js +1 -1
- package/dist/plain/calculate/subtractDate.d.ts +7 -4
- package/dist/plain/calculate/subtractDate.js +1 -4
- package/dist/plain/calculate/subtractDateTime.d.ts +7 -4
- package/dist/plain/calculate/subtractDateTime.js +1 -4
- package/dist/plain/compare/areDateTimesEqualBy.d.ts +13 -5
- package/dist/plain/compare/areDateTimesEqualBy.js +6 -5
- package/dist/plain/compare/areDatesEqualBy.d.ts +15 -6
- package/dist/plain/compare/areDatesEqualBy.js +8 -6
- package/dist/plain/compare/isBetweenDate.d.ts +13 -2
- package/dist/plain/compare/isBetweenDate.js +4 -4
- package/dist/plain/compare/isBetweenDateTime.d.ts +13 -2
- package/dist/plain/compare/isBetweenDateTime.js +4 -4
- package/dist/plain/compare/isBetweenTime.d.ts +13 -2
- package/dist/plain/compare/isBetweenTime.js +4 -4
- package/dist/plain/compare/isRelativeDay.d.ts +1 -1
- package/dist/plain/compare/isRelativeDay.js +1 -1
- package/dist/plain/compare/isThisUnit.d.ts +1 -1
- package/dist/plain/compare/isThisUnit.js +1 -1
- package/dist/plain/format/formatCalendar.d.ts +21 -14
- package/dist/plain/format/formatCalendar.js +21 -16
- package/dist/plain/format/formatDate.js +5 -3
- package/dist/plain/format/formatDateRange.js +4 -2
- package/dist/plain/format/formatDateTime.js +4 -2
- package/dist/plain/format/formatDateTimeRange.js +4 -2
- package/dist/plain/format/formatDateTimeToParts.d.ts +15 -4
- package/dist/plain/format/formatDateTimeToParts.js +10 -6
- package/dist/plain/format/formatDateToParts.d.ts +13 -2
- package/dist/plain/format/formatDateToParts.js +8 -4
- package/dist/plain/format/formatRelativeDate.d.ts +16 -3
- package/dist/plain/format/formatRelativeDate.js +26 -15
- package/dist/plain/format/formatRelativeDateTime.d.ts +16 -3
- package/dist/plain/format/formatRelativeDateTime.js +8 -5
- package/dist/plain/format/formatRelativeTime.d.ts +16 -3
- package/dist/plain/format/formatRelativeTime.js +21 -13
- package/dist/plain/format/formatTime.js +4 -2
- package/dist/plain/interval/intervalDifferenceDate.d.ts +9 -0
- package/dist/plain/interval/intervalDifferenceDateTime.d.ts +9 -0
- package/dist/plain/interval/intervalDifferenceTime.d.ts +9 -0
- package/dist/plain/interval/intervalDivideEquallyDate.d.ts +19 -4
- package/dist/plain/interval/intervalDivideEquallyDate.js +3 -4
- package/dist/plain/interval/intervalDivideEquallyDateTime.d.ts +19 -4
- package/dist/plain/interval/intervalDivideEquallyDateTime.js +3 -4
- package/dist/plain/interval/intervalDivideEquallyTime.d.ts +19 -4
- package/dist/plain/interval/intervalDivideEquallyTime.js +3 -4
- package/dist/plain/interval/intervalFromDurationDate.d.ts +16 -3
- package/dist/plain/interval/intervalFromDurationDate.js +1 -3
- package/dist/plain/interval/intervalFromDurationDateTime.d.ts +16 -3
- package/dist/plain/interval/intervalFromDurationDateTime.js +1 -3
- package/dist/plain/interval/intervalFromDurationTime.d.ts +9 -0
- package/dist/plain/interval/intervalIntersectionDate.d.ts +9 -0
- package/dist/plain/interval/intervalIntersectionDateTime.d.ts +9 -0
- package/dist/plain/interval/intervalIntersectionTime.d.ts +9 -0
- package/dist/plain/interval/intervalSplitAtDate.d.ts +11 -0
- package/dist/plain/interval/intervalSplitAtDate.js +2 -0
- package/dist/plain/interval/intervalSplitAtDateTime.d.ts +11 -0
- package/dist/plain/interval/intervalSplitAtDateTime.js +2 -0
- package/dist/plain/interval/intervalSplitAtTime.d.ts +11 -0
- package/dist/plain/interval/intervalSplitAtTime.js +2 -0
- package/dist/plain/interval/intervalUnionDate.d.ts +9 -0
- package/dist/plain/interval/intervalUnionDateTime.d.ts +9 -0
- package/dist/plain/interval/intervalUnionTime.d.ts +9 -0
- package/dist/plain/interval/intervalXorAllDate.d.ts +9 -0
- package/dist/plain/interval/intervalXorAllDateTime.d.ts +9 -0
- package/dist/plain/interval/intervalXorAllTime.d.ts +9 -0
- package/dist/plain/interval/intervalXorDate.d.ts +9 -0
- package/dist/plain/interval/intervalXorDateTime.d.ts +9 -0
- package/dist/plain/interval/intervalXorTime.d.ts +9 -0
- package/dist/plain/interval/mergeIntervalsDate.d.ts +9 -0
- package/dist/plain/interval/mergeIntervalsDateTime.d.ts +9 -0
- package/dist/plain/interval/mergeIntervalsTime.d.ts +9 -0
- package/dist/plain/interval/splitIntervalByUnitDate.d.ts +23 -7
- package/dist/plain/interval/splitIntervalByUnitDate.js +7 -7
- package/dist/plain/interval/splitIntervalByUnitDateTime.d.ts +22 -6
- package/dist/plain/interval/splitIntervalByUnitDateTime.js +6 -6
- package/dist/plain/interval/splitIntervalByUnitTime.d.ts +17 -4
- package/dist/plain/interval/splitIntervalByUnitTime.js +1 -4
- package/dist/plain/map/mapDatesInRange.d.ts +8 -4
- package/dist/plain/map/mapDatesInRange.js +1 -4
- package/dist/plain/parse/parseDateTimeWithPattern.d.ts +10 -7
- package/dist/plain/parse/parseDateTimeWithPattern.js +10 -7
- package/dist/plain/parse/parseDateWithPattern.d.ts +10 -7
- package/dist/plain/parse/parseDateWithPattern.js +10 -7
- package/dist/plain/parse/parseTimeWithPattern.d.ts +10 -7
- package/dist/plain/parse/parseTimeWithPattern.js +10 -7
- package/dist/plain/parse/parseUnitFromDate.d.ts +11 -3
- package/dist/plain/parse/parseUnitFromDate.js +3 -3
- package/dist/plain/parse/parseUnitFromDateTime.d.ts +11 -3
- package/dist/plain/parse/parseUnitFromDateTime.js +3 -3
- package/dist/plain/parse/parseWeekFromDate.d.ts +10 -6
- package/dist/plain/parse/parseWeekFromDate.js +2 -6
- package/dist/plain/parse/parseWeekFromDateTime.d.ts +10 -6
- package/dist/plain/parse/parseWeekFromDateTime.js +2 -6
- package/dist/plain/validate/isValidDateRange.d.ts +19 -4
- package/dist/plain/validate/isValidDateRange.js +1 -4
- package/dist/plain/validate/isValidDateTimeRange.d.ts +20 -5
- package/dist/plain/validate/isValidDateTimeRange.js +2 -5
- package/dist/plain/validate/isValidTimeRange.d.ts +20 -5
- package/dist/plain/validate/isValidTimeRange.js +2 -5
- package/dist/precision/convert/fromExcelSerial.d.ts +10 -4
- package/dist/precision/convert/fromExcelSerial.js +4 -5
- package/dist/precision/convert/fromNanoseconds.d.ts +1 -1
- package/dist/precision/convert/fromNanoseconds.js +1 -1
- package/dist/precision/convert/toExcelSerial.d.ts +9 -3
- package/dist/precision/convert/toExcelSerial.js +3 -4
- package/dist/precision/convert/toNanoseconds.d.ts +11 -6
- package/dist/precision/convert/toNanoseconds.js +11 -6
- package/dist/precision/validate/isValidInstant.d.ts +12 -3
- package/dist/precision/validate/isValidInstant.js +12 -3
- package/dist/span/calculate/spanMs.d.ts +10 -4
- package/dist/span/calculate/spanMs.js +10 -4
- package/dist/span/calculate/spanNs.d.ts +10 -4
- package/dist/span/calculate/spanNs.js +10 -4
- package/dist/span/validate/isValidSpan.d.ts +8 -3
- package/dist/span/validate/isValidSpan.js +8 -3
- package/dist/transport/calculate/bestAvailable.d.ts +47 -0
- package/dist/transport/calculate/bestAvailable.js +76 -0
- package/dist/transport/calculate/crossingTime.d.ts +58 -0
- package/dist/transport/calculate/crossingTime.js +76 -0
- package/dist/transport/calculate/cutoffAt.d.ts +90 -0
- package/dist/transport/calculate/cutoffAt.js +209 -0
- package/dist/transport/calculate/cutoffSchedule.d.ts +70 -0
- package/dist/transport/calculate/cutoffSchedule.js +71 -0
- package/dist/transport/calculate/dwellTime.d.ts +81 -0
- package/dist/transport/calculate/dwellTime.js +130 -0
- package/dist/transport/calculate/estimateDrift.d.ts +65 -0
- package/dist/transport/calculate/estimateDrift.js +75 -0
- package/dist/transport/calculate/index.d.ts +10 -0
- package/dist/transport/calculate/index.js +10 -0
- package/dist/transport/calculate/nextDeparture.d.ts +78 -0
- package/dist/transport/calculate/nextDeparture.js +131 -0
- package/dist/transport/calculate/punctualityRate.d.ts +42 -0
- package/dist/transport/calculate/punctualityRate.js +59 -0
- package/dist/transport/calculate/scheduleDelivery.d.ts +150 -0
- package/dist/transport/calculate/scheduleDelivery.js +300 -0
- package/dist/transport/calculate/transitTime.d.ts +71 -0
- package/dist/transport/calculate/transitTime.js +101 -0
- package/dist/transport/compare/classifyPunctuality.d.ts +46 -0
- package/dist/transport/compare/classifyPunctuality.js +64 -0
- package/dist/transport/compare/index.d.ts +4 -0
- package/dist/transport/compare/index.js +4 -0
- package/dist/transport/compare/isPastCutoff.d.ts +33 -0
- package/dist/transport/compare/isPastCutoff.js +48 -0
- package/dist/transport/compare/scheduleDeviation.d.ts +37 -0
- package/dist/transport/compare/scheduleDeviation.js +53 -0
- package/dist/transport/compare/timeToCutoff.d.ts +31 -0
- package/dist/transport/compare/timeToCutoff.js +47 -0
- package/dist/transport/convert/etaAtZone.d.ts +43 -0
- package/dist/transport/convert/etaAtZone.js +58 -0
- package/dist/transport/convert/index.d.ts +1 -0
- package/dist/transport/convert/index.js +1 -0
- package/dist/transport/index.d.ts +4 -0
- package/dist/transport/index.js +5 -0
- package/dist/types/business-calendar.d.ts +19 -14
- package/dist/types/calendar-options.d.ts +15 -3
- package/dist/types/date-cycle-field.d.ts +5 -0
- package/dist/types/date-duration-unit.d.ts +4 -0
- package/dist/types/date-time-cycle-field.d.ts +4 -0
- package/dist/types/date-time-duration-units.d.ts +4 -0
- package/dist/types/date-time-unit.d.ts +4 -0
- package/dist/types/date-unit.d.ts +4 -0
- package/dist/types/disambiguation.d.ts +8 -0
- package/dist/types/duration-relative-to.d.ts +8 -0
- package/dist/types/duration-string-options.d.ts +28 -0
- package/dist/types/fiscal-calendar.d.ts +1 -1
- package/dist/types/fractional-digit.d.ts +5 -0
- package/dist/types/free-time-options.d.ts +44 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.js +3 -1
- package/dist/types/interval.d.ts +8 -1
- package/dist/types/offset.d.ts +9 -0
- package/dist/types/operating-schedule.d.ts +71 -0
- package/dist/types/operating-schedule.js +0 -0
- package/dist/types/overflow.d.ts +6 -0
- package/dist/types/relative-rounding-method.d.ts +6 -0
- package/dist/types/relative-time-options.d.ts +33 -5
- package/dist/types/rounding-options.d.ts +27 -0
- package/dist/types/time-cycle-field.d.ts +4 -0
- package/dist/types/time-duration-unit.d.ts +4 -0
- package/dist/types/time-unit.d.ts +4 -0
- package/dist/types/transport-timestamps.d.ts +54 -0
- package/dist/types/transport-timestamps.js +0 -0
- package/dist/unix/calculate/addUnix.d.ts +19 -5
- package/dist/unix/calculate/addUnix.js +1 -5
- package/dist/unix/calculate/diffUnix.d.ts +13 -3
- package/dist/unix/calculate/diffUnix.js +1 -3
- package/dist/unix/calculate/diffUnixAsDuration.d.ts +13 -3
- package/dist/unix/calculate/diffUnixAsDuration.js +1 -3
- package/dist/unix/calculate/endOfQuarterForUnix.d.ts +13 -3
- package/dist/unix/calculate/endOfQuarterForUnix.js +1 -3
- package/dist/unix/calculate/endOfUnix.d.ts +20 -4
- package/dist/unix/calculate/endOfUnix.js +1 -4
- package/dist/unix/calculate/isBetweenUnix.d.ts +26 -4
- package/dist/unix/calculate/isBetweenUnix.js +4 -6
- package/dist/unix/calculate/roundUnix.d.ts +31 -3
- package/dist/unix/calculate/roundUnix.js +3 -6
- package/dist/unix/calculate/setUnix.d.ts +35 -9
- package/dist/unix/calculate/setUnix.js +4 -13
- package/dist/unix/calculate/startOfQuarterForUnix.d.ts +13 -3
- package/dist/unix/calculate/startOfQuarterForUnix.js +1 -3
- package/dist/unix/calculate/startOfUnix.d.ts +20 -4
- package/dist/unix/calculate/startOfUnix.js +1 -4
- package/dist/unix/calculate/subtractUnix.d.ts +19 -5
- package/dist/unix/calculate/subtractUnix.js +1 -5
- package/dist/unix/compare/areUnixEqual.d.ts +7 -1
- package/dist/unix/compare/areUnixEqual.js +1 -1
- package/dist/unix/compare/areUnixEqualBy.d.ts +24 -7
- package/dist/unix/compare/areUnixEqualBy.js +13 -9
- package/dist/unix/compare/isAfterUnix.d.ts +7 -1
- package/dist/unix/compare/isAfterUnix.js +1 -1
- package/dist/unix/compare/isBeforeUnix.d.ts +7 -1
- package/dist/unix/compare/isBeforeUnix.js +1 -1
- package/dist/unix/convert/convertUnixToPlainDate.d.ts +14 -4
- package/dist/unix/convert/convertUnixToPlainDate.js +2 -4
- package/dist/unix/convert/convertUnixToPlainDateTime.d.ts +14 -4
- package/dist/unix/convert/convertUnixToPlainDateTime.js +2 -4
- package/dist/unix/convert/convertUnixToPlainTime.d.ts +14 -4
- package/dist/unix/convert/convertUnixToPlainTime.js +2 -4
- package/dist/unix/convert/convertUnixToUtc.d.ts +9 -4
- package/dist/unix/convert/convertUnixToUtc.js +3 -4
- package/dist/unix/convert/convertUnixToZoned.d.ts +10 -5
- package/dist/unix/convert/convertUnixToZoned.js +4 -5
- package/dist/unix/format/formatCalendarUnix.d.ts +27 -19
- package/dist/unix/format/formatCalendarUnix.js +4 -5
- package/dist/unix/format/formatRelativeUnix.d.ts +54 -5
- package/dist/unix/format/formatRelativeUnix.js +3 -5
- package/dist/unix/format/formatUnix.d.ts +24 -6
- package/dist/unix/format/formatUnix.js +8 -7
- package/dist/unix/get/getUnixNow.d.ts +8 -3
- package/dist/unix/get/getUnixNow.js +1 -3
- package/dist/unix/interval/intervalCountUnix.d.ts +14 -5
- package/dist/unix/interval/intervalCountUnix.js +2 -5
- package/dist/unix/interval/intervalDifferenceUnix.d.ts +6 -0
- package/dist/unix/interval/intervalDivideEquallyUnix.d.ts +18 -4
- package/dist/unix/interval/intervalDivideEquallyUnix.js +5 -4
- package/dist/unix/interval/intervalFromDurationUnix.d.ts +33 -5
- package/dist/unix/interval/intervalFromDurationUnix.js +6 -5
- package/dist/unix/interval/intervalIntersectionUnix.d.ts +6 -0
- package/dist/unix/interval/intervalLengthUnix.d.ts +16 -6
- package/dist/unix/interval/intervalLengthUnix.js +4 -6
- package/dist/unix/interval/intervalOverlappingDaysUnix.d.ts +13 -3
- package/dist/unix/interval/intervalOverlappingDaysUnix.js +1 -3
- package/dist/unix/interval/intervalSplitAtUnix.d.ts +8 -0
- package/dist/unix/interval/intervalSplitAtUnix.js +2 -0
- package/dist/unix/interval/intervalUnionUnix.d.ts +6 -0
- package/dist/unix/interval/intervalXorAllUnix.d.ts +6 -0
- package/dist/unix/interval/intervalXorUnix.d.ts +6 -0
- package/dist/unix/interval/mergeIntervalsUnix.d.ts +6 -0
- package/dist/unix/interval/splitIntervalByUnitUnix.d.ts +42 -11
- package/dist/unix/interval/splitIntervalByUnitUnix.js +21 -16
- package/dist/unix/parse/parseDateFromUnix.d.ts +13 -2
- package/dist/unix/parse/parseDateFromUnix.js +1 -2
- package/dist/unix/parse/parseDayFromUnix.d.ts +14 -2
- package/dist/unix/parse/parseDayFromUnix.js +2 -2
- package/dist/unix/parse/parseDayOfWeekFromUnix.d.ts +13 -1
- package/dist/unix/parse/parseDayOfWeekFromUnix.js +1 -1
- package/dist/unix/parse/parseHourFromUnix.d.ts +14 -2
- package/dist/unix/parse/parseHourFromUnix.js +2 -2
- package/dist/unix/parse/parseMicrosecondFromUnix.d.ts +14 -2
- package/dist/unix/parse/parseMicrosecondFromUnix.js +2 -2
- package/dist/unix/parse/parseMillisecondFromUnix.d.ts +14 -2
- package/dist/unix/parse/parseMillisecondFromUnix.js +2 -2
- package/dist/unix/parse/parseMinuteFromUnix.d.ts +14 -2
- package/dist/unix/parse/parseMinuteFromUnix.js +2 -2
- package/dist/unix/parse/parseMonthFromUnix.d.ts +14 -2
- package/dist/unix/parse/parseMonthFromUnix.js +2 -2
- package/dist/unix/parse/parseNanosecondFromUnix.d.ts +14 -2
- package/dist/unix/parse/parseNanosecondFromUnix.js +2 -2
- package/dist/unix/parse/parseSecondFromUnix.d.ts +14 -2
- package/dist/unix/parse/parseSecondFromUnix.js +2 -2
- package/dist/unix/parse/parseTimeFromUnix.d.ts +13 -2
- package/dist/unix/parse/parseTimeFromUnix.js +1 -2
- package/dist/unix/parse/parseUnitFromUnix.d.ts +22 -3
- package/dist/unix/parse/parseUnitFromUnix.js +2 -3
- package/dist/unix/parse/parseWeekFromUnix.d.ts +23 -7
- package/dist/unix/parse/parseWeekFromUnix.js +3 -7
- package/dist/unix/parse/parseYearFromUnix.d.ts +14 -2
- package/dist/unix/parse/parseYearFromUnix.js +2 -2
- package/dist/unix/validate/isValidUnixRange.d.ts +20 -4
- package/dist/unix/validate/isValidUnixRange.js +1 -4
- package/dist/utc/calculate/addUtc.d.ts +7 -4
- package/dist/utc/calculate/addUtc.js +1 -4
- package/dist/utc/calculate/diffUtc.d.ts +1 -1
- package/dist/utc/calculate/diffUtc.js +1 -1
- package/dist/utc/calculate/diffUtcAsDuration.d.ts +1 -1
- package/dist/utc/calculate/diffUtcAsDuration.js +1 -1
- package/dist/utc/calculate/endOfUtc.d.ts +16 -3
- package/dist/utc/calculate/endOfUtc.js +2 -3
- package/dist/utc/calculate/isBetweenUtc.d.ts +13 -2
- package/dist/utc/calculate/isBetweenUtc.js +4 -4
- package/dist/utc/calculate/roundUtc.d.ts +26 -9
- package/dist/utc/calculate/roundUtc.js +3 -12
- package/dist/utc/calculate/setUtc.d.ts +7 -4
- package/dist/utc/calculate/setUtc.js +1 -4
- package/dist/utc/calculate/startOfUtc.d.ts +15 -2
- package/dist/utc/calculate/startOfUtc.js +1 -2
- package/dist/utc/calculate/subtractUtc.d.ts +7 -4
- package/dist/utc/calculate/subtractUtc.js +1 -4
- package/dist/utc/compare/areUtcEqualBy.d.ts +9 -3
- package/dist/utc/compare/areUtcEqualBy.js +2 -3
- package/dist/utc/convert/convertUtcToPlainDate.d.ts +8 -3
- package/dist/utc/convert/convertUtcToPlainDate.js +5 -6
- package/dist/utc/convert/convertUtcToPlainDateTime.d.ts +8 -3
- package/dist/utc/convert/convertUtcToPlainDateTime.js +5 -6
- package/dist/utc/convert/convertUtcToPlainTime.d.ts +8 -3
- package/dist/utc/convert/convertUtcToPlainTime.js +5 -6
- package/dist/utc/convert/convertUtcToUnix.d.ts +9 -4
- package/dist/utc/convert/convertUtcToUnix.js +3 -4
- package/dist/utc/convert/convertUtcToZoned.d.ts +1 -1
- package/dist/utc/convert/convertUtcToZoned.js +1 -1
- package/dist/utc/format/formatCalendarUtc.d.ts +19 -8
- package/dist/utc/format/formatCalendarUtc.js +8 -6
- package/dist/utc/format/formatHttp.d.ts +1 -1
- package/dist/utc/format/formatHttp.js +1 -1
- package/dist/utc/format/formatRelativeUtc.d.ts +30 -10
- package/dist/utc/format/formatRelativeUtc.js +9 -11
- package/dist/utc/format/formatUtc.d.ts +17 -15
- package/dist/utc/format/formatUtc.js +8 -8
- package/dist/utc/interval/intervalDifferenceUtc.d.ts +6 -0
- package/dist/utc/interval/intervalDivideEquallyUtc.d.ts +18 -4
- package/dist/utc/interval/intervalDivideEquallyUtc.js +5 -4
- package/dist/utc/interval/intervalFromDurationUtc.d.ts +13 -3
- package/dist/utc/interval/intervalFromDurationUtc.js +1 -3
- package/dist/utc/interval/intervalIntersectionUtc.d.ts +6 -0
- package/dist/utc/interval/intervalSplitAtUtc.d.ts +8 -0
- package/dist/utc/interval/intervalSplitAtUtc.js +2 -0
- package/dist/utc/interval/intervalUnionUtc.d.ts +6 -0
- package/dist/utc/interval/intervalXorAllUtc.d.ts +6 -0
- package/dist/utc/interval/intervalXorUtc.d.ts +6 -0
- package/dist/utc/interval/mergeIntervalsUtc.d.ts +6 -0
- package/dist/utc/interval/splitIntervalByUnitUtc.d.ts +19 -6
- package/dist/utc/interval/splitIntervalByUnitUtc.js +6 -6
- package/dist/utc/parse/parseDateFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseDateFromUtc.js +1 -3
- package/dist/utc/parse/parseDayFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseDayFromUtc.js +1 -3
- package/dist/utc/parse/parseDayOfWeekFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseDayOfWeekFromUtc.js +4 -6
- package/dist/utc/parse/parseHourFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseHourFromUtc.js +1 -3
- package/dist/utc/parse/parseHttp.d.ts +1 -1
- package/dist/utc/parse/parseHttp.js +1 -1
- package/dist/utc/parse/parseMicrosecondFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseMicrosecondFromUtc.js +1 -3
- package/dist/utc/parse/parseMillisecondFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseMillisecondFromUtc.js +1 -3
- package/dist/utc/parse/parseMinuteFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseMinuteFromUtc.js +1 -3
- package/dist/utc/parse/parseMonthFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseMonthFromUtc.js +1 -3
- package/dist/utc/parse/parseNanosecondFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseNanosecondFromUtc.js +1 -3
- package/dist/utc/parse/parseSecondFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseSecondFromUtc.js +1 -3
- package/dist/utc/parse/parseTimeFromUtc.d.ts +8 -3
- package/dist/utc/parse/parseTimeFromUtc.js +5 -6
- package/dist/utc/parse/parseUnitFromUtc.d.ts +17 -5
- package/dist/utc/parse/parseUnitFromUtc.js +6 -8
- package/dist/utc/parse/parseWeekFromUtc.d.ts +18 -10
- package/dist/utc/parse/parseWeekFromUtc.js +7 -13
- package/dist/utc/parse/parseYearFromUtc.d.ts +7 -3
- package/dist/utc/parse/parseYearFromUtc.js +1 -3
- package/dist/utc/validate/isValidUtcRange.d.ts +16 -5
- package/dist/utc/validate/isValidUtcRange.js +2 -5
- package/dist/zoned/calculate/addZoned.d.ts +20 -10
- package/dist/zoned/calculate/addZoned.js +6 -13
- package/dist/zoned/calculate/cycleZoned.d.ts +33 -8
- package/dist/zoned/calculate/cycleZoned.js +1 -8
- package/dist/zoned/calculate/diffZoned.d.ts +1 -1
- package/dist/zoned/calculate/diffZoned.js +1 -1
- package/dist/zoned/calculate/diffZonedAsDuration.d.ts +1 -1
- package/dist/zoned/calculate/diffZonedAsDuration.js +1 -1
- package/dist/zoned/calculate/endOfQuarterForZoned.d.ts +9 -2
- package/dist/zoned/calculate/endOfQuarterForZoned.js +4 -5
- package/dist/zoned/calculate/endOfZoned.d.ts +16 -3
- package/dist/zoned/calculate/endOfZoned.js +2 -3
- package/dist/zoned/calculate/getLocaleZonedEndOfWeek.d.ts +10 -3
- package/dist/zoned/calculate/getLocaleZonedEndOfWeek.js +5 -6
- package/dist/zoned/calculate/getLocaleZonedStartOfWeek.d.ts +7 -1
- package/dist/zoned/calculate/getLocaleZonedStartOfWeek.js +3 -4
- package/dist/zoned/calculate/roundZoned.d.ts +21 -4
- package/dist/zoned/calculate/roundZoned.js +3 -7
- package/dist/zoned/calculate/setZoned.d.ts +25 -8
- package/dist/zoned/calculate/setZoned.js +4 -12
- package/dist/zoned/calculate/startOfQuarterForZoned.d.ts +7 -2
- package/dist/zoned/calculate/startOfQuarterForZoned.js +3 -5
- package/dist/zoned/calculate/startOfZoned.d.ts +15 -3
- package/dist/zoned/calculate/startOfZoned.js +1 -3
- package/dist/zoned/calculate/subtractZoned.d.ts +20 -10
- package/dist/zoned/calculate/subtractZoned.js +6 -13
- package/dist/zoned/compare/areZonedEqualBy.d.ts +14 -5
- package/dist/zoned/compare/areZonedEqualBy.js +7 -5
- package/dist/zoned/compare/isBetweenZoned.d.ts +13 -1
- package/dist/zoned/compare/isBetweenZoned.js +4 -3
- package/dist/zoned/compare/isInDaylightSaving.d.ts +38 -11
- package/dist/zoned/compare/isInDaylightSaving.js +40 -44
- package/dist/zoned/convert/convertPlainDateTimeToZoned.d.ts +18 -5
- package/dist/zoned/convert/convertPlainDateTimeToZoned.js +6 -11
- package/dist/zoned/convert/convertZonedToUnix.d.ts +9 -4
- package/dist/zoned/convert/convertZonedToUnix.js +2 -4
- package/dist/zoned/convert/convertZonedToZoned.d.ts +1 -1
- package/dist/zoned/convert/convertZonedToZoned.js +1 -1
- package/dist/zoned/format/formatCalendarZoned.d.ts +18 -12
- package/dist/zoned/format/formatCalendarZoned.js +13 -9
- package/dist/zoned/format/formatRelativeZoned.d.ts +41 -12
- package/dist/zoned/format/formatRelativeZoned.js +7 -4
- package/dist/zoned/format/formatRfc2822.d.ts +2 -2
- package/dist/zoned/format/formatRfc2822.js +2 -2
- package/dist/zoned/format/formatRfc3339.d.ts +2 -3
- package/dist/zoned/format/formatRfc3339.js +2 -3
- package/dist/zoned/format/formatTimeZoneName.d.ts +12 -3
- package/dist/zoned/format/formatTimeZoneName.js +5 -4
- package/dist/zoned/format/formatZonedDateTime.js +2 -1
- package/dist/zoned/format/formatZonedRange.js +2 -1
- package/dist/zoned/format/formatZonedToParts.d.ts +15 -4
- package/dist/zoned/format/formatZonedToParts.js +8 -5
- package/dist/zoned/get/getDstTransitions.d.ts +9 -10
- package/dist/zoned/get/getDstTransitions.js +12 -3
- package/dist/zoned/get/getTimeZoneOffset.d.ts +4 -1
- package/dist/zoned/get/getTimeZoneOffset.js +6 -3
- package/dist/zoned/get/getZonedNow.d.ts +13 -1
- package/dist/zoned/get/getZonedNow.js +12 -3
- package/dist/zoned/interval/intervalDifferenceZoned.d.ts +9 -0
- package/dist/zoned/interval/intervalDivideEquallyZoned.d.ts +22 -4
- package/dist/zoned/interval/intervalDivideEquallyZoned.js +6 -4
- package/dist/zoned/interval/intervalFromDurationZoned.d.ts +29 -10
- package/dist/zoned/interval/intervalFromDurationZoned.js +6 -13
- package/dist/zoned/interval/intervalIntersectionZoned.d.ts +9 -0
- package/dist/zoned/interval/intervalSplitAtZoned.d.ts +11 -0
- package/dist/zoned/interval/intervalSplitAtZoned.js +2 -0
- package/dist/zoned/interval/intervalUnionZoned.d.ts +9 -0
- package/dist/zoned/interval/intervalXorAllZoned.d.ts +9 -0
- package/dist/zoned/interval/intervalXorZoned.d.ts +9 -0
- package/dist/zoned/interval/mergeIntervalsZoned.d.ts +9 -0
- package/dist/zoned/interval/splitIntervalByUnitZoned.d.ts +27 -6
- package/dist/zoned/interval/splitIntervalByUnitZoned.js +11 -6
- package/dist/zoned/map/mapZonedDatesInRange.d.ts +9 -4
- package/dist/zoned/map/mapZonedDatesInRange.js +2 -4
- package/dist/zoned/parse/parseRfc2822.d.ts +1 -1
- package/dist/zoned/parse/parseRfc2822.js +1 -1
- package/dist/zoned/parse/parseUnitFromZoned.d.ts +9 -3
- package/dist/zoned/parse/parseUnitFromZoned.js +1 -3
- package/dist/zoned/parse/parseWeekFromZoned.d.ts +10 -3
- package/dist/zoned/parse/parseWeekFromZoned.js +2 -3
- package/dist/zoned/validate/hasDaylightSaving.d.ts +42 -11
- package/dist/zoned/validate/hasDaylightSaving.js +47 -31
- package/dist/zoned/validate/isValidZonedDateTime.d.ts +13 -0
- package/dist/zoned/validate/isValidZonedDateTime.js +14 -1
- package/dist/zoned/validate/isValidZonedRange.d.ts +15 -4
- package/dist/zoned/validate/isValidZonedRange.js +1 -4
- package/package.json +53 -5
- package/skills/gmt-arithmetic/SKILL.md +1 -1
- package/skills/gmt-basics/SKILL.md +10 -1
- package/skills/gmt-integration/SKILL.md +10 -5
- package/skills/gmt-timezone/SKILL.md +210 -14
- package/dist/internal/resolveReadingTimeZone.d.ts +0 -11
- package/dist/internal/resolveReadingTimeZone.js +0 -18
- package/dist/types/duration-unit.d.ts +0 -5
- /package/dist/types/{duration-unit.js → free-time-options.js} +0 -0
|
@@ -1,32 +1,9 @@
|
|
|
1
1
|
import { Temporal } from "@js-temporal/polyfill";
|
|
2
|
-
import { formatUtcOffset, isoStringBody, parseInstantNanoseconds, zonedDateTimeFrom, } from "../../internal/index.js";
|
|
2
|
+
import { formatUtcOffset, isoStringBody, parseInstantNanoseconds, TIME_ZONE_ANNOTATION, zonedDateTimeFrom, } from "../../internal/index.js";
|
|
3
3
|
import { utcOffset } from "../../regex/index.js";
|
|
4
4
|
import { isValidTimeZone } from "../../zoned/validate/index.js";
|
|
5
|
-
/**
|
|
6
|
-
* An absolute instant paired with the local UTC offset in force where the event happened.
|
|
7
|
-
*
|
|
8
|
-
* @remarks Members:
|
|
9
|
-
*
|
|
10
|
-
* | Member | Type | Description |
|
|
11
|
-
* | --- | --- | --- |
|
|
12
|
-
* | `instant` | `string` | UTC instant, ISO 8601 ending in `Z` (e.g. `2024-07-15T16:00:00Z`). Orders events globally. |
|
|
13
|
-
* | `offset` | `string` | UTC offset in force where the event happened, `±HH:MM` (or `±HH:MM:SS` for a pre-1972 zone that did not run on a whole minute). Renders the event as the human on the ground saw it. |
|
|
14
|
-
* | `timeZone` | `string \| undefined` | IANA zone identifier, when one is known. Absent from most feeds, so absent from most pairs. |
|
|
15
|
-
*
|
|
16
|
-
* Neither `instant` nor `offset` derives from the other, which is why both are stored. It is
|
|
17
|
-
* the shape GS1 EPCIS 2.0 (`eventTime` + a required `eventTimeZoneOffset`), UN/EDIFACT DTM
|
|
18
|
-
* (qualifier `303`/`304`) and DICOM (`DT` + `&ZZXX`) all exchange.
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* import { OffsetInstant } from "@northguild/gmt/instant";
|
|
22
|
-
* const pair: OffsetInstant = {
|
|
23
|
-
* instant: "2024-07-15T16:00:00Z",
|
|
24
|
-
* offset: "-04:00",
|
|
25
|
-
* timeZone: "America/New_York",
|
|
26
|
-
* };
|
|
27
|
-
*/
|
|
28
5
|
/** A leading RFC 9557 time zone annotation: `[zone]` or `[!zone]`, with no `=`. */
|
|
29
|
-
const timeZoneAnnotation =
|
|
6
|
+
const timeZoneAnnotation = new RegExp(`^${TIME_ZONE_ANNOTATION}`);
|
|
30
7
|
/**
|
|
31
8
|
* Split an ISO 8601 instant string into the instant-plus-offset pair the world exchanges.
|
|
32
9
|
*
|
|
@@ -69,10 +46,15 @@ const timeZoneAnnotation = /^\[!?[^\]=]+\]/;
|
|
|
69
46
|
* rounding it would put the pair 30 seconds from the event it describes. Such a zone's
|
|
70
47
|
* *string* still carries the rounded `-00:45`, because RFC 9557 caps a written offset at
|
|
71
48
|
* minutes; the bracketed zone is what resolves it, so the instant stays exact.
|
|
49
|
+
* - A bracketed zone is checked as `Temporal.ZonedDateTime.from` checks it, which also rejects
|
|
50
|
+
* a local date of −271821-04-19. A zone west of Greenwich shows that date for the first hours
|
|
51
|
+
* of the instant range, so a zoned string written there
|
|
52
|
+
* (`-271821-04-19T23:59:00-00:01[Europe/London]`) returns null, although `isValidInstant`
|
|
53
|
+
* accepts it. Pass the instant in `Z` form with `timeZone` instead.
|
|
72
54
|
* - Returns null on invalid input, not a zero-offset pair — `+00:00` is a real offset.
|
|
73
55
|
*
|
|
74
56
|
* @param value ISO 8601 instant string, with an offset designator and optionally a bracketed zone
|
|
75
|
-
* @param timeZone optional IANA
|
|
57
|
+
* @param timeZone optional IANA name or UTC offset to read the offset in
|
|
76
58
|
* @returns `{ instant, offset, timeZone? }`, or null on invalid input
|
|
77
59
|
*
|
|
78
60
|
* @example toOffsetInstant("2024-07-15T12:00:00-04:00[America/New_York]") // { instant: "2024-07-15T16:00:00Z", offset: "-04:00", timeZone: "America/New_York" }
|
|
@@ -84,6 +66,8 @@ const timeZoneAnnotation = /^\[!?[^\]=]+\]/;
|
|
|
84
66
|
* @example toOffsetInstant("1969-12-31T23:15:30-00:45[Africa/Monrovia]") // { instant: "1970-01-01T00:00:00Z", offset: "-00:44:30", timeZone: "Africa/Monrovia" } — the zone, not the rounded offset, fixes the instant
|
|
85
67
|
* @example toOffsetInstant("2024-07-15T12:00:00-04:00[-04:00]") // { instant: "2024-07-15T16:00:00Z", offset: "-04:00" } — a bracketed offset is not a zone, so no timeZone field
|
|
86
68
|
* @example toOffsetInstant("2024-07-15T12:00:00-05:00[America/New_York]") // null (offset contradicts the bracketed zone)
|
|
69
|
+
* @example toOffsetInstant("-271821-04-19T23:59:00-00:01[Europe/London]") // null (a bracketed zone on a local date before Temporal's date range)
|
|
70
|
+
* @example toOffsetInstant("-271821-04-20T00:00:15Z", "Europe/London") // { instant: "-271821-04-20T00:00:15Z", offset: "-00:01:15", timeZone: "Europe/London" }
|
|
87
71
|
* @example toOffsetInstant("2024-07-15T12:00:00-04:00[foo=bar]") // { instant: "2024-07-15T16:00:00Z", offset: "-04:00" } — an elective annotation is ignored
|
|
88
72
|
* @example toOffsetInstant("2024-07-15T12:00:00-04:00[!foo=bar]") // null (an unknown critical annotation)
|
|
89
73
|
* @example toOffsetInstant("2024-07-15T12:00:00") // null (no offset designator)
|
|
@@ -103,8 +87,8 @@ export function toOffsetInstant(value, timeZone) {
|
|
|
103
87
|
return null;
|
|
104
88
|
}
|
|
105
89
|
try {
|
|
106
|
-
// `parseInstantNanoseconds`
|
|
107
|
-
//
|
|
90
|
+
// `parseInstantNanoseconds` does not validate the bracket, so a self-contradictory string
|
|
91
|
+
// parses fine as an instant. `ZonedDateTime.from` is what checks
|
|
108
92
|
// the two agree, and it runs even when `timeZone` overrides the bracketed zone.
|
|
109
93
|
// Only a time zone annotation makes the string zoned: it is the first annotation, and the
|
|
110
94
|
// only kind with no `=` (RFC 9557 §4.1). A calendar or elective annotation alone leaves an
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/** The dates a billing timeline is counted from: the anchor, and the invoice and request once they exist. */
|
|
2
|
+
export interface BillingDates {
|
|
3
|
+
/**
|
|
4
|
+
* The date the issue window is counted from, as an ISO date, such as the date the charge was
|
|
5
|
+
* last incurred or, for a re-bill, the issuance date of the invoice received.
|
|
6
|
+
*/
|
|
7
|
+
anchorOn: string;
|
|
8
|
+
/**
|
|
9
|
+
* The date the invoice was issued, once it has been, as an ISO date.
|
|
10
|
+
*
|
|
11
|
+
* @defaultValue None. `issuedByDeadline` and `disputeDeadline` are `null`.
|
|
12
|
+
*/
|
|
13
|
+
invoiceIssuedOn?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The date the dispute request was received, once it has been, as an ISO date. It needs
|
|
16
|
+
* `invoiceIssuedOn` and may not precede it.
|
|
17
|
+
*
|
|
18
|
+
* @defaultValue None. `requestedByDeadline` and `resolutionDeadline` are `null`.
|
|
19
|
+
*/
|
|
20
|
+
requestReceivedOn?: string;
|
|
21
|
+
}
|
|
22
|
+
/** The three windows the caller supplies, in calendar days, none defaulted, and an optional agreed resolution date. */
|
|
23
|
+
export interface BillingWindows {
|
|
24
|
+
/** The days from `anchorOn` within which the invoice must be issued. */
|
|
25
|
+
issueDays: number;
|
|
26
|
+
/** The days from `invoiceIssuedOn` within which a dispute may be raised. */
|
|
27
|
+
disputeDays: number;
|
|
28
|
+
/** The days from `requestReceivedOn` within which the dispute must be resolved. */
|
|
29
|
+
resolutionDays: number;
|
|
30
|
+
/**
|
|
31
|
+
* A resolution date the parties agreed instead, as an ISO date on or after `requestReceivedOn`;
|
|
32
|
+
* it replaces the computed one.
|
|
33
|
+
*
|
|
34
|
+
* @defaultValue None. `resolutionDeadline` is `requestReceivedOn + resolutionDays`.
|
|
35
|
+
*/
|
|
36
|
+
agreedResolutionOn?: string;
|
|
37
|
+
}
|
|
38
|
+
/** What `billingTimeline` returns: each deadline as an ISO date, and each comparison once its date exists. */
|
|
39
|
+
export interface BillingDeadlines {
|
|
40
|
+
/** The last date the invoice may be issued: `anchorOn + issueDays`. */
|
|
41
|
+
invoiceDeadline: string;
|
|
42
|
+
/** Whether `invoiceIssuedOn` is on or before `invoiceDeadline`; `null` until an invoice date exists. */
|
|
43
|
+
issuedByDeadline: boolean | null;
|
|
44
|
+
/**
|
|
45
|
+
* The last date a dispute may be raised: `invoiceIssuedOn + disputeDays`; `null` until an invoice
|
|
46
|
+
* date exists.
|
|
47
|
+
*/
|
|
48
|
+
disputeDeadline: string | null;
|
|
49
|
+
/** Whether `requestReceivedOn` is on or before `disputeDeadline`; `null` until a request date exists. */
|
|
50
|
+
requestedByDeadline: boolean | null;
|
|
51
|
+
/**
|
|
52
|
+
* The last date the dispute must be resolved: `requestReceivedOn + resolutionDays`, or
|
|
53
|
+
* `agreedResolutionOn`; `null` until a request date exists.
|
|
54
|
+
*/
|
|
55
|
+
resolutionDeadline: string | null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Lay out the deadline chain around a demurrage or detention invoice: the last date it may be
|
|
59
|
+
* issued, the last date it may be disputed, and the last date the dispute must be resolved.
|
|
60
|
+
*
|
|
61
|
+
* A billing regime or service contract can set up to three windows around an invoice: one to
|
|
62
|
+
* issue it, one to dispute it, and one to resolve the dispute. The number of days in each is the
|
|
63
|
+
* caller's fact; GMT carries none of them. The arithmetic is this function's: each deadline is a
|
|
64
|
+
* date, counted in calendar days from a date, with the anchor as day zero and the deadline day
|
|
65
|
+
* itself inside the window.
|
|
66
|
+
*
|
|
67
|
+
* - **Day zero is the anchor and the deadline is `anchor + days`** on the ISO 8601 calendar
|
|
68
|
+
* (`Temporal.PlainDate.add`); a date is "by the deadline" when
|
|
69
|
+
* `Temporal.PlainDate.compare(date, deadline) <= 0`. That is this function's stated contract,
|
|
70
|
+
* not a reading of any rule; a regime that counts differently passes a different number.
|
|
71
|
+
* - **Deadlines are dates, never instants.** Reduce an instant to the billing party's local date
|
|
72
|
+
* first with `convertUtcToPlainDate(instant, { timeZone })`; this function does not guess a
|
|
73
|
+
* zone. `anchorOn` is whatever date the caller counts from, such as the last charged date
|
|
74
|
+
* (`chargeableDays(...).chargedDates.at(-1)`) or, for a party re-billing a charge it was
|
|
75
|
+
* itself billed, the issuance date of the invoice it received.
|
|
76
|
+
* - **Windows have no defaults.** Each is a safe integer of at least `0`; a missing, negative or
|
|
77
|
+
* non-integer window returns `null`, because a silently defaulted window is a wrong deadline.
|
|
78
|
+
* - **The chain fills in as its dates exist.** With only `anchorOn` the result is a forecast:
|
|
79
|
+
* `invoiceDeadline` is set and every other field is `null`. An invoice date sets
|
|
80
|
+
* `issuedByDeadline` and `disputeDeadline`; a request date sets `requestedByDeadline` and
|
|
81
|
+
* `resolutionDeadline`. An explicit `undefined` is an omission; any other value must be a
|
|
82
|
+
* valid ISO date.
|
|
83
|
+
* - **The booleans compare dates and say nothing else.** Whether a charge is payable, whether a
|
|
84
|
+
* dispute must be heard, and any consequence are the consumer's. GMT computes dates, not
|
|
85
|
+
* liability.
|
|
86
|
+
* - **`agreedResolutionOn` replaces the computed `resolutionDeadline`** when given. It must be a
|
|
87
|
+
* valid date on or after `requestReceivedOn`, else `null`; it is validated even when no
|
|
88
|
+
* request exists, but cannot act without one.
|
|
89
|
+
* - **A request cannot precede the invoice it disputes.** `requestReceivedOn` without
|
|
90
|
+
* `invoiceIssuedOn`, or earlier than it, is a data error and returns `null`. An invoice earlier
|
|
91
|
+
* than the anchor is allowed: an invoice may be issued while charges still accrue.
|
|
92
|
+
* - Every emitted date is canonical bare ISO (`Temporal.PlainDate#toString()`), so an input
|
|
93
|
+
* annotation such as `[u-ca=iso8601]` does not reach the output, as `addDate` behaves.
|
|
94
|
+
* - Returns `null` when `dates` or `windows` is not an object, `anchorOn` or a given optional
|
|
95
|
+
* date is not a valid ISO date, a window is missing or not a safe integer of at least `0`, a
|
|
96
|
+
* request exists without an invoice or before it, `agreedResolutionOn` is before the request,
|
|
97
|
+
* or a deadline would leave Temporal's range.
|
|
98
|
+
*
|
|
99
|
+
* @param dates The anchor date, and the invoice and request dates once they exist
|
|
100
|
+
* @param windows The three calendar-day windows, none defaulted, and an agreed resolution date
|
|
101
|
+
* @returns { invoiceDeadline, issuedByDeadline, disputeDeadline, requestedByDeadline, resolutionDeadline }, or null on invalid input
|
|
102
|
+
*
|
|
103
|
+
* @example billingTimeline({ anchorOn: "2026-03-01" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: null, disputeDeadline: null, requestedByDeadline: null, resolutionDeadline: null } (no invoice yet: a forecast)
|
|
104
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-31" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: true, disputeDeadline: "2026-04-30", requestedByDeadline: null, resolutionDeadline: null } (day 30 is the last day by the deadline)
|
|
105
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-04-01" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: false, disputeDeadline: "2026-05-01", requestedByDeadline: null, resolutionDeadline: null } (day 31)
|
|
106
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-20", requestReceivedOn: "2026-04-19" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: true, disputeDeadline: "2026-04-19", requestedByDeadline: true, resolutionDeadline: "2026-05-19" }
|
|
107
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-20", requestReceivedOn: "2026-04-20" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30, agreedResolutionOn: "2026-06-01" }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: true, disputeDeadline: "2026-04-19", requestedByDeadline: false, resolutionDeadline: "2026-06-01" } (an agreed date replaces the computed one)
|
|
108
|
+
* @example billingTimeline({ anchorOn: "2026-03-10", invoiceIssuedOn: "2026-04-05" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-04-09", issuedByDeadline: true, disputeDeadline: "2026-05-05", requestedByDeadline: null, resolutionDeadline: null } (a re-bill anchored on the invoice it received, not on the charge)
|
|
109
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-05", requestReceivedOn: "2026-03-18" }, { issueDays: 14, disputeDays: 14, resolutionDays: 45 }) // { invoiceDeadline: "2026-03-15", issuedByDeadline: true, disputeDeadline: "2026-03-19", requestedByDeadline: true, resolutionDeadline: "2026-05-02" } (a service contract's own windows)
|
|
110
|
+
* @example billingTimeline({ anchorOn: "2028-01-30" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2028-02-29", issuedByDeadline: null, disputeDeadline: null, requestedByDeadline: null, resolutionDeadline: null } (calendar days across a leap day)
|
|
111
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-31" }, { disputeDays: 30, resolutionDays: 30 }) // null (issueDays has no default)
|
|
112
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", requestReceivedOn: "2026-04-19" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // null (a request cannot precede the invoice it disputes)
|
|
113
|
+
*/
|
|
114
|
+
export declare function billingTimeline(dates: BillingDates, windows: BillingWindows): BillingDeadlines | null;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
2
|
+
import { isObject } from "../../internal/index.js";
|
|
3
|
+
import { isValidDate } from "../../plain/validate/isValidDate.js";
|
|
4
|
+
/**
|
|
5
|
+
* Lay out the deadline chain around a demurrage or detention invoice: the last date it may be
|
|
6
|
+
* issued, the last date it may be disputed, and the last date the dispute must be resolved.
|
|
7
|
+
*
|
|
8
|
+
* A billing regime or service contract can set up to three windows around an invoice: one to
|
|
9
|
+
* issue it, one to dispute it, and one to resolve the dispute. The number of days in each is the
|
|
10
|
+
* caller's fact; GMT carries none of them. The arithmetic is this function's: each deadline is a
|
|
11
|
+
* date, counted in calendar days from a date, with the anchor as day zero and the deadline day
|
|
12
|
+
* itself inside the window.
|
|
13
|
+
*
|
|
14
|
+
* - **Day zero is the anchor and the deadline is `anchor + days`** on the ISO 8601 calendar
|
|
15
|
+
* (`Temporal.PlainDate.add`); a date is "by the deadline" when
|
|
16
|
+
* `Temporal.PlainDate.compare(date, deadline) <= 0`. That is this function's stated contract,
|
|
17
|
+
* not a reading of any rule; a regime that counts differently passes a different number.
|
|
18
|
+
* - **Deadlines are dates, never instants.** Reduce an instant to the billing party's local date
|
|
19
|
+
* first with `convertUtcToPlainDate(instant, { timeZone })`; this function does not guess a
|
|
20
|
+
* zone. `anchorOn` is whatever date the caller counts from, such as the last charged date
|
|
21
|
+
* (`chargeableDays(...).chargedDates.at(-1)`) or, for a party re-billing a charge it was
|
|
22
|
+
* itself billed, the issuance date of the invoice it received.
|
|
23
|
+
* - **Windows have no defaults.** Each is a safe integer of at least `0`; a missing, negative or
|
|
24
|
+
* non-integer window returns `null`, because a silently defaulted window is a wrong deadline.
|
|
25
|
+
* - **The chain fills in as its dates exist.** With only `anchorOn` the result is a forecast:
|
|
26
|
+
* `invoiceDeadline` is set and every other field is `null`. An invoice date sets
|
|
27
|
+
* `issuedByDeadline` and `disputeDeadline`; a request date sets `requestedByDeadline` and
|
|
28
|
+
* `resolutionDeadline`. An explicit `undefined` is an omission; any other value must be a
|
|
29
|
+
* valid ISO date.
|
|
30
|
+
* - **The booleans compare dates and say nothing else.** Whether a charge is payable, whether a
|
|
31
|
+
* dispute must be heard, and any consequence are the consumer's. GMT computes dates, not
|
|
32
|
+
* liability.
|
|
33
|
+
* - **`agreedResolutionOn` replaces the computed `resolutionDeadline`** when given. It must be a
|
|
34
|
+
* valid date on or after `requestReceivedOn`, else `null`; it is validated even when no
|
|
35
|
+
* request exists, but cannot act without one.
|
|
36
|
+
* - **A request cannot precede the invoice it disputes.** `requestReceivedOn` without
|
|
37
|
+
* `invoiceIssuedOn`, or earlier than it, is a data error and returns `null`. An invoice earlier
|
|
38
|
+
* than the anchor is allowed: an invoice may be issued while charges still accrue.
|
|
39
|
+
* - Every emitted date is canonical bare ISO (`Temporal.PlainDate#toString()`), so an input
|
|
40
|
+
* annotation such as `[u-ca=iso8601]` does not reach the output, as `addDate` behaves.
|
|
41
|
+
* - Returns `null` when `dates` or `windows` is not an object, `anchorOn` or a given optional
|
|
42
|
+
* date is not a valid ISO date, a window is missing or not a safe integer of at least `0`, a
|
|
43
|
+
* request exists without an invoice or before it, `agreedResolutionOn` is before the request,
|
|
44
|
+
* or a deadline would leave Temporal's range.
|
|
45
|
+
*
|
|
46
|
+
* @param dates The anchor date, and the invoice and request dates once they exist
|
|
47
|
+
* @param windows The three calendar-day windows, none defaulted, and an agreed resolution date
|
|
48
|
+
* @returns { invoiceDeadline, issuedByDeadline, disputeDeadline, requestedByDeadline, resolutionDeadline }, or null on invalid input
|
|
49
|
+
*
|
|
50
|
+
* @example billingTimeline({ anchorOn: "2026-03-01" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: null, disputeDeadline: null, requestedByDeadline: null, resolutionDeadline: null } (no invoice yet: a forecast)
|
|
51
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-31" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: true, disputeDeadline: "2026-04-30", requestedByDeadline: null, resolutionDeadline: null } (day 30 is the last day by the deadline)
|
|
52
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-04-01" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: false, disputeDeadline: "2026-05-01", requestedByDeadline: null, resolutionDeadline: null } (day 31)
|
|
53
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-20", requestReceivedOn: "2026-04-19" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: true, disputeDeadline: "2026-04-19", requestedByDeadline: true, resolutionDeadline: "2026-05-19" }
|
|
54
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-20", requestReceivedOn: "2026-04-20" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30, agreedResolutionOn: "2026-06-01" }) // { invoiceDeadline: "2026-03-31", issuedByDeadline: true, disputeDeadline: "2026-04-19", requestedByDeadline: false, resolutionDeadline: "2026-06-01" } (an agreed date replaces the computed one)
|
|
55
|
+
* @example billingTimeline({ anchorOn: "2026-03-10", invoiceIssuedOn: "2026-04-05" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2026-04-09", issuedByDeadline: true, disputeDeadline: "2026-05-05", requestedByDeadline: null, resolutionDeadline: null } (a re-bill anchored on the invoice it received, not on the charge)
|
|
56
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-05", requestReceivedOn: "2026-03-18" }, { issueDays: 14, disputeDays: 14, resolutionDays: 45 }) // { invoiceDeadline: "2026-03-15", issuedByDeadline: true, disputeDeadline: "2026-03-19", requestedByDeadline: true, resolutionDeadline: "2026-05-02" } (a service contract's own windows)
|
|
57
|
+
* @example billingTimeline({ anchorOn: "2028-01-30" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // { invoiceDeadline: "2028-02-29", issuedByDeadline: null, disputeDeadline: null, requestedByDeadline: null, resolutionDeadline: null } (calendar days across a leap day)
|
|
58
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", invoiceIssuedOn: "2026-03-31" }, { disputeDays: 30, resolutionDays: 30 }) // null (issueDays has no default)
|
|
59
|
+
* @example billingTimeline({ anchorOn: "2026-03-01", requestReceivedOn: "2026-04-19" }, { issueDays: 30, disputeDays: 30, resolutionDays: 30 }) // null (a request cannot precede the invoice it disputes)
|
|
60
|
+
*/
|
|
61
|
+
export function billingTimeline(dates, windows) {
|
|
62
|
+
try {
|
|
63
|
+
return layOutDeadlines(dates, windows);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Never throws (Core Rule 3): a hostile
|
|
67
|
+
// argument is invalid input, not an exception.
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** A window is a whole number of calendar days, zero or more. */
|
|
72
|
+
function isWindow(value) {
|
|
73
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
74
|
+
}
|
|
75
|
+
/** An optional date is absent (`undefined`) or a valid ISO date; anything else is invalid. */
|
|
76
|
+
function optionalDate(value) {
|
|
77
|
+
if (value === undefined) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return typeof value === "string" && isValidDate(value) ? value : null;
|
|
81
|
+
}
|
|
82
|
+
/** `date + days` on the ISO calendar, canonical. Throws `RangeError` past Temporal's range. */
|
|
83
|
+
function plusDays(date, days) {
|
|
84
|
+
return Temporal.PlainDate.from(date).add({ days }).toString();
|
|
85
|
+
}
|
|
86
|
+
/** Whether `date` is on or before `deadline`. */
|
|
87
|
+
function byDeadline(date, deadline) {
|
|
88
|
+
return (Temporal.PlainDate.compare(Temporal.PlainDate.from(date), Temporal.PlainDate.from(deadline)) <= 0);
|
|
89
|
+
}
|
|
90
|
+
/** The dates, each a valid ISO date, with no request before its invoice; `null` otherwise. */
|
|
91
|
+
function readDates(dates) {
|
|
92
|
+
const { anchorOn } = dates;
|
|
93
|
+
if (typeof anchorOn !== "string" || !isValidDate(anchorOn)) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const invoiceIssuedOn = optionalDate(dates.invoiceIssuedOn);
|
|
97
|
+
const requestReceivedOn = optionalDate(dates.requestReceivedOn);
|
|
98
|
+
if (invoiceIssuedOn === null || requestReceivedOn === null) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
// A request cannot precede the invoice it disputes.
|
|
102
|
+
if (requestReceivedOn !== undefined &&
|
|
103
|
+
(invoiceIssuedOn === undefined ||
|
|
104
|
+
!byDeadline(invoiceIssuedOn, requestReceivedOn))) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return { anchorOn, invoiceIssuedOn, requestReceivedOn };
|
|
108
|
+
}
|
|
109
|
+
/** The three windows, each present and valid, and the agreed date if given; `null` otherwise. */
|
|
110
|
+
function readWindows(windows) {
|
|
111
|
+
const { issueDays, disputeDays, resolutionDays } = windows;
|
|
112
|
+
if (!isWindow(issueDays) ||
|
|
113
|
+
!isWindow(disputeDays) ||
|
|
114
|
+
!isWindow(resolutionDays)) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const agreedResolutionOn = optionalDate(windows.agreedResolutionOn);
|
|
118
|
+
if (agreedResolutionOn === null) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
return { issueDays, disputeDays, resolutionDays, agreedResolutionOn };
|
|
122
|
+
}
|
|
123
|
+
/** The agreed date when given, else `requestReceivedOn + resolutionDays`; `null` with no request. */
|
|
124
|
+
function resolutionDeadline(requestReceivedOn, windows) {
|
|
125
|
+
if (requestReceivedOn === undefined) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return windows.agreedResolutionOn === undefined
|
|
129
|
+
? plusDays(requestReceivedOn, windows.resolutionDays)
|
|
130
|
+
: Temporal.PlainDate.from(windows.agreedResolutionOn).toString();
|
|
131
|
+
}
|
|
132
|
+
/** Whether `date` is on or before `deadline`; `null` until both exist. */
|
|
133
|
+
function onOrBefore(date, deadline) {
|
|
134
|
+
return date === undefined || deadline === null
|
|
135
|
+
? null
|
|
136
|
+
: byDeadline(date, deadline);
|
|
137
|
+
}
|
|
138
|
+
/** An agreed resolution date cannot precede the request it resolves. */
|
|
139
|
+
function agreedAfterRequest(requestReceivedOn, agreedResolutionOn) {
|
|
140
|
+
return (requestReceivedOn === undefined ||
|
|
141
|
+
agreedResolutionOn === undefined ||
|
|
142
|
+
byDeadline(requestReceivedOn, agreedResolutionOn));
|
|
143
|
+
}
|
|
144
|
+
/** `billingTimeline` without the guard; every read of its arguments happens here. */
|
|
145
|
+
function layOutDeadlines(dates, windows) {
|
|
146
|
+
if (!isObject(dates) || !isObject(windows)) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const read = readDates(dates);
|
|
150
|
+
const counts = readWindows(windows);
|
|
151
|
+
if (read === null ||
|
|
152
|
+
counts === null ||
|
|
153
|
+
!agreedAfterRequest(read.requestReceivedOn, counts.agreedResolutionOn)) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const { anchorOn, invoiceIssuedOn, requestReceivedOn } = read;
|
|
157
|
+
const invoiceDeadline = plusDays(anchorOn, counts.issueDays);
|
|
158
|
+
const disputeDeadline = invoiceIssuedOn === undefined
|
|
159
|
+
? null
|
|
160
|
+
: plusDays(invoiceIssuedOn, counts.disputeDays);
|
|
161
|
+
return {
|
|
162
|
+
invoiceDeadline,
|
|
163
|
+
issuedByDeadline: onOrBefore(invoiceIssuedOn, invoiceDeadline),
|
|
164
|
+
disputeDeadline,
|
|
165
|
+
requestedByDeadline: onOrBefore(requestReceivedOn, disputeDeadline),
|
|
166
|
+
resolutionDeadline: resolutionDeadline(requestReceivedOn, counts),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { FreeTimeBasis, FreeTimeOptions } from "../../types/index.js";
|
|
2
|
+
/** A band of chargeable-day ordinals and how many of the charged days fell in it. */
|
|
3
|
+
export interface TierBand {
|
|
4
|
+
/** The first chargeable-day ordinal of the band, from 1. */
|
|
5
|
+
from: number;
|
|
6
|
+
/** The last ordinal of the band, or `null` for the open final band. */
|
|
7
|
+
to: number | null;
|
|
8
|
+
/** The number of charged days that fell in the band. */
|
|
9
|
+
days: number;
|
|
10
|
+
}
|
|
11
|
+
/** What `chargeableDays` returns: the day count an invoice bills, and the dates that back it. */
|
|
12
|
+
export interface FreeTimeCharges {
|
|
13
|
+
/** The number of free days the dwell touched, at most the tariff's `freeDays`. */
|
|
14
|
+
freeDaysUsed: number;
|
|
15
|
+
/** The number of counted days on or after `expiresAt` the dwell touched. */
|
|
16
|
+
chargeableDays: number;
|
|
17
|
+
/** The instant free time ended, as a UTC instant, as `freeTimeExpiry` reports it. */
|
|
18
|
+
expiresAt: string;
|
|
19
|
+
/**
|
|
20
|
+
* The local dates charged, ascending. Carriers' day-numbered tariff grids and progressive tiers
|
|
21
|
+
* are applied to this list, and its entries are the dates an itemised invoice can list.
|
|
22
|
+
*/
|
|
23
|
+
chargedDates: string[];
|
|
24
|
+
/** The charged days split into the `tiers` bands; one open band when no tiers were given. */
|
|
25
|
+
byTier: TierBand[];
|
|
26
|
+
}
|
|
27
|
+
/** `FreeTimeOptions` plus how charged days are counted, and the optional tier boundaries. */
|
|
28
|
+
export type FreeTimeChargeOptions = FreeTimeOptions & {
|
|
29
|
+
/**
|
|
30
|
+
* How days after expiry are charged: `"calendar"` charges every local day, `"working"` only the
|
|
31
|
+
* working days of `calendar`. No default; see `chargeableDays`.
|
|
32
|
+
*/
|
|
33
|
+
chargeBasis: FreeTimeBasis;
|
|
34
|
+
/**
|
|
35
|
+
* The last chargeable-day ordinal of each band, as strictly ascending positive whole numbers:
|
|
36
|
+
* `[5, 10]` is days 1–5, 6–10 and 11 onward. They are day bands, not rates. Every band is
|
|
37
|
+
* returned, an empty one with `days: 0`, and the last is always open.
|
|
38
|
+
*
|
|
39
|
+
* @defaultValue None. `byTier` is the single open band `{ from: 1, to: null }`.
|
|
40
|
+
*/
|
|
41
|
+
tiers?: number[];
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Count the days a container is charged for between the start of its clock and its exit,
|
|
45
|
+
* and list the dates behind the count.
|
|
46
|
+
*
|
|
47
|
+
* Lays out free time with the same walk as `freeTimeExpiry`, then counts the days on or after
|
|
48
|
+
* the expiry that the half-open dwell `[clockStart, clockEnd)` touched. The result is the
|
|
49
|
+
* auditable form of a demurrage or detention charge: `chargedDates` lists the days behind the
|
|
50
|
+
* count, which is the list a day-numbered tariff grid is applied to, and the dates an itemised
|
|
51
|
+
* invoice can list. No regulation or industry standard fixes how the days are counted; every term
|
|
52
|
+
* below is the tariff's. GMT computes days, never money: `byTier` says how many days fell in each
|
|
53
|
+
* band so the caller can apply its rates.
|
|
54
|
+
*
|
|
55
|
+
* - **Half-open at the exit.** A day is charged when it started before `clockEnd`, so a
|
|
56
|
+
* gate-out at exactly `expiresAt` is `0` chargeable days and one nanosecond later is `1`. A
|
|
57
|
+
* zero-length dwell touches the day it sits on, as `dwellTime` counts. On the calendar basis
|
|
58
|
+
* with `firstDay: "eventDay"`, `freeDaysUsed + chargeableDays` equals `dwellTime(...).calendarDays`,
|
|
59
|
+
* with one exception: a fall-back that re-enters the day before the event day (`America/Goose_Bay`,
|
|
60
|
+
* 00:01 on 7 November 2010 back into the 6th) is a date `dwellTime` counts as touched, and a
|
|
61
|
+
* tariff never counts a date before the event day.
|
|
62
|
+
* - **The free-time terms are `freeTimeExpiry`'s**: `basis`, `timeZone`, `firstDay` and
|
|
63
|
+
* `calendar` mean the same, and `firstDay` has no default.
|
|
64
|
+
* - **`chargeBasis` says how days after expiry are charged, and has no default.** Many
|
|
65
|
+
* tariffs count free time and charges alike in calendar days. Where a tariff grants free time in
|
|
66
|
+
* working days, the days after it are mostly charged as calendar days, weekends and holidays
|
|
67
|
+
* included (Hapag-Lloyd's tables: "Rate per Calendar day"; ACL: "Once free time expires ...
|
|
68
|
+
* charged on calendar days"): `basis: "working", chargeBasis: "calendar"`. Some tariffs charge
|
|
69
|
+
* working days only, for free time and charges alike, so neither counts a day the gate is
|
|
70
|
+
* closed or a holiday: both `"working"`.
|
|
71
|
+
* Either `"working"` needs `calendar`. The two differ by every closed day after expiry, so
|
|
72
|
+
* neither is assumed.
|
|
73
|
+
* - **`freeDays` may be `0`** for a tariff with no free time: `expiresAt` is the start of day one,
|
|
74
|
+
* the first day counted on `basis` (under `"nextDay"`, the first after the event day), and days
|
|
75
|
+
* before it are neither free nor charged.
|
|
76
|
+
* - Returns `null` when either instant is invalid, `clockEnd` is before `clockStart` (an
|
|
77
|
+
* inverted dwell is a data error), `freeDays` is not a whole number of at least `0`, the
|
|
78
|
+
* options fail as they do for `freeTimeExpiry`, `tiers` has a hole, an unsafe integer or does not ascend, or the walk would pass
|
|
79
|
+
* 10,000 local days.
|
|
80
|
+
*
|
|
81
|
+
* @param clockStart ISO 8601 instant string of the event that starts the clock
|
|
82
|
+
* @param clockEnd ISO 8601 instant string of the event that stops it (gate-out or empty return)
|
|
83
|
+
* @param freeDays whole number of free days the tariff allows, 0 or more
|
|
84
|
+
* @param options The tariff's counting terms for free time and for charged days, and the tier bands
|
|
85
|
+
* @returns the free days used, the chargeable days, the expiry, the charged dates and the days per tier band, or null on invalid input
|
|
86
|
+
*
|
|
87
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:00Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // { freeDaysUsed: 3, chargeableDays: 0, expiresAt: "2024-06-17T04:00:00Z", chargedDates: [], byTier: [{ from: 1, to: null, days: 0 }] } (gate-out exactly at expiry is free)
|
|
88
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:01Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // { freeDaysUsed: 3, chargeableDays: 1, expiresAt: "2024-06-17T04:00:00Z", chargedDates: ["2024-06-17"], byTier: [{ from: 1, to: null, days: 1 }] } (one second later is a charged day)
|
|
89
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:01Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "nextDay" }) // { freeDaysUsed: 3, chargeableDays: 0, expiresAt: "2024-06-18T04:00:00Z", chargedDates: [], byTier: [{ from: 1, to: null, days: 0 }] } (the other start-day convention: still free)
|
|
90
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-28T15:00:00Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay", tiers: [5, 10] }) // { freeDaysUsed: 3, chargeableDays: 12, expiresAt: "2024-06-17T04:00:00Z", chargedDates: ["2024-06-17", "2024-06-18", "2024-06-19", "2024-06-20", "2024-06-21", "2024-06-22", "2024-06-23", "2024-06-24", "2024-06-25", "2024-06-26", "2024-06-27", "2024-06-28"], byTier: [{ from: 1, to: 5, days: 5 }, { from: 6, to: 10, days: 5 }, { from: 11, to: null, days: 2 }] }
|
|
91
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-24T15:00:00Z", 3, { basis: "working", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay", calendar: { weekend: [6, 7], holidays: ["2024-06-19"], timeZone: "America/New_York" } }) // { freeDaysUsed: 3, chargeableDays: 6, expiresAt: "2024-06-19T04:00:00Z", chargedDates: ["2024-06-19", "2024-06-20", "2024-06-21", "2024-06-22", "2024-06-23", "2024-06-24"], byTier: [{ from: 1, to: null, days: 6 }] } (free days in working days, every calendar day after expiry charged)
|
|
92
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-24T15:00:00Z", 3, { basis: "working", chargeBasis: "working", timeZone: "America/New_York", firstDay: "eventDay", calendar: { weekend: [6, 7], holidays: ["2024-06-19"], timeZone: "America/New_York" } }) // { freeDaysUsed: 3, chargeableDays: 3, expiresAt: "2024-06-19T04:00:00Z", chargedDates: ["2024-06-20", "2024-06-21", "2024-06-24"], byTier: [{ from: 1, to: null, days: 3 }] } (working days throughout: closed days after expiry are not charged)
|
|
93
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-15T15:00:00Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // { freeDaysUsed: 2, chargeableDays: 0, expiresAt: "2024-06-17T04:00:00Z", chargedDates: [], byTier: [{ from: 1, to: null, days: 0 }] } (out on the second free day)
|
|
94
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-16T15:00:00Z", 0, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // { freeDaysUsed: 0, chargeableDays: 3, expiresAt: "2024-06-14T04:00:00Z", chargedDates: ["2024-06-14", "2024-06-15", "2024-06-16"], byTier: [{ from: 1, to: null, days: 3 }] } (no free time: every day is charged)
|
|
95
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-28T15:00:00Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay", tiers: [10, 5] }) // null (tiers must ascend)
|
|
96
|
+
* @example chargeableDays("2024-06-17T04:00:01Z", "2024-06-14T19:00:00Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // null (exit before the clock started)
|
|
97
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:01Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York" }) // null (firstDay has no default)
|
|
98
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:01Z", 3, { basis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // null (chargeBasis has no default)
|
|
99
|
+
*/
|
|
100
|
+
export declare function chargeableDays(clockStart: string, clockEnd: string, freeDays: number, options: FreeTimeChargeOptions): FreeTimeCharges | null;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
2
|
+
import { bandsByTier, instantFrom, parseFreeDays, parseFreeTimeTerms, parseTiers, walkFreeTime, } from "../../internal/index.js";
|
|
3
|
+
import { isValidInstant } from "../../precision/validate/isValidInstant.js";
|
|
4
|
+
/**
|
|
5
|
+
* Count the days a container is charged for between the start of its clock and its exit,
|
|
6
|
+
* and list the dates behind the count.
|
|
7
|
+
*
|
|
8
|
+
* Lays out free time with the same walk as `freeTimeExpiry`, then counts the days on or after
|
|
9
|
+
* the expiry that the half-open dwell `[clockStart, clockEnd)` touched. The result is the
|
|
10
|
+
* auditable form of a demurrage or detention charge: `chargedDates` lists the days behind the
|
|
11
|
+
* count, which is the list a day-numbered tariff grid is applied to, and the dates an itemised
|
|
12
|
+
* invoice can list. No regulation or industry standard fixes how the days are counted; every term
|
|
13
|
+
* below is the tariff's. GMT computes days, never money: `byTier` says how many days fell in each
|
|
14
|
+
* band so the caller can apply its rates.
|
|
15
|
+
*
|
|
16
|
+
* - **Half-open at the exit.** A day is charged when it started before `clockEnd`, so a
|
|
17
|
+
* gate-out at exactly `expiresAt` is `0` chargeable days and one nanosecond later is `1`. A
|
|
18
|
+
* zero-length dwell touches the day it sits on, as `dwellTime` counts. On the calendar basis
|
|
19
|
+
* with `firstDay: "eventDay"`, `freeDaysUsed + chargeableDays` equals `dwellTime(...).calendarDays`,
|
|
20
|
+
* with one exception: a fall-back that re-enters the day before the event day (`America/Goose_Bay`,
|
|
21
|
+
* 00:01 on 7 November 2010 back into the 6th) is a date `dwellTime` counts as touched, and a
|
|
22
|
+
* tariff never counts a date before the event day.
|
|
23
|
+
* - **The free-time terms are `freeTimeExpiry`'s**: `basis`, `timeZone`, `firstDay` and
|
|
24
|
+
* `calendar` mean the same, and `firstDay` has no default.
|
|
25
|
+
* - **`chargeBasis` says how days after expiry are charged, and has no default.** Many
|
|
26
|
+
* tariffs count free time and charges alike in calendar days. Where a tariff grants free time in
|
|
27
|
+
* working days, the days after it are mostly charged as calendar days, weekends and holidays
|
|
28
|
+
* included (Hapag-Lloyd's tables: "Rate per Calendar day"; ACL: "Once free time expires ...
|
|
29
|
+
* charged on calendar days"): `basis: "working", chargeBasis: "calendar"`. Some tariffs charge
|
|
30
|
+
* working days only, for free time and charges alike, so neither counts a day the gate is
|
|
31
|
+
* closed or a holiday: both `"working"`.
|
|
32
|
+
* Either `"working"` needs `calendar`. The two differ by every closed day after expiry, so
|
|
33
|
+
* neither is assumed.
|
|
34
|
+
* - **`freeDays` may be `0`** for a tariff with no free time: `expiresAt` is the start of day one,
|
|
35
|
+
* the first day counted on `basis` (under `"nextDay"`, the first after the event day), and days
|
|
36
|
+
* before it are neither free nor charged.
|
|
37
|
+
* - Returns `null` when either instant is invalid, `clockEnd` is before `clockStart` (an
|
|
38
|
+
* inverted dwell is a data error), `freeDays` is not a whole number of at least `0`, the
|
|
39
|
+
* options fail as they do for `freeTimeExpiry`, `tiers` has a hole, an unsafe integer or does not ascend, or the walk would pass
|
|
40
|
+
* 10,000 local days.
|
|
41
|
+
*
|
|
42
|
+
* @param clockStart ISO 8601 instant string of the event that starts the clock
|
|
43
|
+
* @param clockEnd ISO 8601 instant string of the event that stops it (gate-out or empty return)
|
|
44
|
+
* @param freeDays whole number of free days the tariff allows, 0 or more
|
|
45
|
+
* @param options The tariff's counting terms for free time and for charged days, and the tier bands
|
|
46
|
+
* @returns the free days used, the chargeable days, the expiry, the charged dates and the days per tier band, or null on invalid input
|
|
47
|
+
*
|
|
48
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:00Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // { freeDaysUsed: 3, chargeableDays: 0, expiresAt: "2024-06-17T04:00:00Z", chargedDates: [], byTier: [{ from: 1, to: null, days: 0 }] } (gate-out exactly at expiry is free)
|
|
49
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:01Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // { freeDaysUsed: 3, chargeableDays: 1, expiresAt: "2024-06-17T04:00:00Z", chargedDates: ["2024-06-17"], byTier: [{ from: 1, to: null, days: 1 }] } (one second later is a charged day)
|
|
50
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:01Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "nextDay" }) // { freeDaysUsed: 3, chargeableDays: 0, expiresAt: "2024-06-18T04:00:00Z", chargedDates: [], byTier: [{ from: 1, to: null, days: 0 }] } (the other start-day convention: still free)
|
|
51
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-28T15:00:00Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay", tiers: [5, 10] }) // { freeDaysUsed: 3, chargeableDays: 12, expiresAt: "2024-06-17T04:00:00Z", chargedDates: ["2024-06-17", "2024-06-18", "2024-06-19", "2024-06-20", "2024-06-21", "2024-06-22", "2024-06-23", "2024-06-24", "2024-06-25", "2024-06-26", "2024-06-27", "2024-06-28"], byTier: [{ from: 1, to: 5, days: 5 }, { from: 6, to: 10, days: 5 }, { from: 11, to: null, days: 2 }] }
|
|
52
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-24T15:00:00Z", 3, { basis: "working", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay", calendar: { weekend: [6, 7], holidays: ["2024-06-19"], timeZone: "America/New_York" } }) // { freeDaysUsed: 3, chargeableDays: 6, expiresAt: "2024-06-19T04:00:00Z", chargedDates: ["2024-06-19", "2024-06-20", "2024-06-21", "2024-06-22", "2024-06-23", "2024-06-24"], byTier: [{ from: 1, to: null, days: 6 }] } (free days in working days, every calendar day after expiry charged)
|
|
53
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-24T15:00:00Z", 3, { basis: "working", chargeBasis: "working", timeZone: "America/New_York", firstDay: "eventDay", calendar: { weekend: [6, 7], holidays: ["2024-06-19"], timeZone: "America/New_York" } }) // { freeDaysUsed: 3, chargeableDays: 3, expiresAt: "2024-06-19T04:00:00Z", chargedDates: ["2024-06-20", "2024-06-21", "2024-06-24"], byTier: [{ from: 1, to: null, days: 3 }] } (working days throughout: closed days after expiry are not charged)
|
|
54
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-15T15:00:00Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // { freeDaysUsed: 2, chargeableDays: 0, expiresAt: "2024-06-17T04:00:00Z", chargedDates: [], byTier: [{ from: 1, to: null, days: 0 }] } (out on the second free day)
|
|
55
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-16T15:00:00Z", 0, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // { freeDaysUsed: 0, chargeableDays: 3, expiresAt: "2024-06-14T04:00:00Z", chargedDates: ["2024-06-14", "2024-06-15", "2024-06-16"], byTier: [{ from: 1, to: null, days: 3 }] } (no free time: every day is charged)
|
|
56
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-28T15:00:00Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay", tiers: [10, 5] }) // null (tiers must ascend)
|
|
57
|
+
* @example chargeableDays("2024-06-17T04:00:01Z", "2024-06-14T19:00:00Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // null (exit before the clock started)
|
|
58
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:01Z", 3, { basis: "calendar", chargeBasis: "calendar", timeZone: "America/New_York" }) // null (firstDay has no default)
|
|
59
|
+
* @example chargeableDays("2024-06-14T19:00:00Z", "2024-06-17T04:00:01Z", 3, { basis: "calendar", timeZone: "America/New_York", firstDay: "eventDay" }) // null (chargeBasis has no default)
|
|
60
|
+
*/
|
|
61
|
+
export function chargeableDays(clockStart, clockEnd, freeDays, options) {
|
|
62
|
+
try {
|
|
63
|
+
const terms = parseFreeTimeTerms(options, true);
|
|
64
|
+
const allowed = parseFreeDays(freeDays, 0);
|
|
65
|
+
// Read after the terms, so a non-object `options` is never read from.
|
|
66
|
+
const tiers = terms === null ? null : parseTiers(options.tiers);
|
|
67
|
+
if (!isValidInstant(clockStart) ||
|
|
68
|
+
!isValidInstant(clockEnd) ||
|
|
69
|
+
terms === null ||
|
|
70
|
+
allowed === null ||
|
|
71
|
+
tiers === null) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const start = instantFrom(clockStart).toZonedDateTimeISO(terms.timeZone);
|
|
75
|
+
const end = instantFrom(clockEnd).toZonedDateTimeISO(terms.timeZone);
|
|
76
|
+
if (Temporal.ZonedDateTime.compare(start, end) > 0) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const ledger = walkFreeTime(start, allowed, terms, end);
|
|
80
|
+
if (ledger === null) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
freeDaysUsed: ledger.used,
|
|
85
|
+
chargeableDays: ledger.charged.length,
|
|
86
|
+
expiresAt: ledger.expiresAt.toInstant().toString(),
|
|
87
|
+
chargedDates: ledger.charged.map((day) => day.date),
|
|
88
|
+
byTier: bandsByTier(tiers, ledger.charged.length),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Never throws (Core Rule 3): a hostile
|
|
93
|
+
// argument is invalid input, not an exception.
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|