@northguild/gmt 1.14.2 → 1.16.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.
Files changed (1163) hide show
  1. package/README.md +887 -277
  2. package/dist/calendar/business/businessDaysBetween.d.ts +37 -0
  3. package/dist/calendar/business/businessDaysBetween.js +89 -0
  4. package/dist/calendar/business/index.d.ts +7 -0
  5. package/dist/calendar/business/index.js +10 -0
  6. package/dist/calendar/business/mergeCalendars.d.ts +33 -0
  7. package/dist/calendar/business/mergeCalendars.js +68 -0
  8. package/dist/calendar/business/nextBusinessDay.d.ts +30 -0
  9. package/dist/calendar/business/nextBusinessDay.js +39 -0
  10. package/dist/calendar/business/previousBusinessDay.d.ts +30 -0
  11. package/dist/calendar/business/previousBusinessDay.js +39 -0
  12. package/dist/calendar/business/rollDate.d.ts +59 -0
  13. package/dist/calendar/business/rollDate.js +116 -0
  14. package/dist/calendar/calculate/bucketRange.d.ts +49 -0
  15. package/dist/calendar/calculate/bucketRange.js +169 -0
  16. package/dist/calendar/calculate/floorToZone.d.ts +42 -0
  17. package/dist/calendar/calculate/floorToZone.js +62 -0
  18. package/dist/calendar/calculate/getFiscalPeriod.d.ts +64 -0
  19. package/dist/calendar/calculate/getFiscalPeriod.js +96 -0
  20. package/dist/calendar/calculate/getIsoWeekDate.d.ts +35 -0
  21. package/dist/calendar/calculate/getIsoWeekDate.js +41 -0
  22. package/dist/calendar/calculate/getOrdinalDate.d.ts +25 -0
  23. package/dist/calendar/calculate/getOrdinalDate.js +28 -0
  24. package/dist/calendar/calculate/getQuarter.d.ts +36 -0
  25. package/dist/calendar/calculate/getQuarter.js +64 -0
  26. package/dist/calendar/calculate/index.d.ts +6 -0
  27. package/dist/calendar/calculate/index.js +6 -0
  28. package/dist/calendar/index.d.ts +4 -0
  29. package/dist/calendar/index.js +5 -0
  30. package/dist/calendar/validate/index.d.ts +4 -0
  31. package/dist/calendar/validate/index.js +4 -0
  32. package/dist/calendar/validate/isValidBusinessCalendar.d.ts +31 -0
  33. package/dist/calendar/validate/isValidBusinessCalendar.js +40 -0
  34. package/dist/calendar/validate/isValidFiscalPattern.d.ts +25 -0
  35. package/dist/calendar/validate/isValidFiscalPattern.js +29 -0
  36. package/dist/calendar/validate/isValidRollConvention.d.ts +28 -0
  37. package/dist/calendar/validate/isValidRollConvention.js +38 -0
  38. package/dist/calendar/validate/isValidZoneBucketUnit.d.ts +31 -0
  39. package/dist/calendar/validate/isValidZoneBucketUnit.js +39 -0
  40. package/dist/duration/calculate/durationAs.d.ts +27 -3
  41. package/dist/duration/calculate/durationAs.js +34 -8
  42. package/dist/duration/calculate/getDurationUnit.d.ts +5 -2
  43. package/dist/duration/calculate/getDurationUnit.js +7 -3
  44. package/dist/duration/compare/compareDurations.d.ts +25 -2
  45. package/dist/duration/compare/compareDurations.js +31 -7
  46. package/dist/duration/format/formatDuration.d.ts +14 -5
  47. package/dist/duration/format/formatDuration.js +47 -11
  48. package/dist/duration/index.d.ts +1 -0
  49. package/dist/duration/index.js +2 -0
  50. package/dist/duration/normalize/normalizeDuration.d.ts +33 -6
  51. package/dist/duration/normalize/normalizeDuration.js +41 -12
  52. package/dist/duration/parse/parseDuration.js +4 -0
  53. package/dist/index.d.ts +6 -0
  54. package/dist/index.js +6 -0
  55. package/dist/instant/convert/classifyLocal.d.ts +50 -0
  56. package/dist/instant/convert/classifyLocal.js +60 -0
  57. package/dist/instant/convert/fromOffsetInstant.d.ts +47 -0
  58. package/dist/instant/convert/fromOffsetInstant.js +99 -0
  59. package/dist/instant/convert/index.d.ts +4 -0
  60. package/dist/instant/convert/index.js +4 -0
  61. package/dist/instant/convert/resolveLocal.d.ts +40 -0
  62. package/dist/instant/convert/resolveLocal.js +76 -0
  63. package/dist/instant/convert/toOffsetInstant.d.ts +73 -0
  64. package/dist/instant/convert/toOffsetInstant.js +185 -0
  65. package/dist/instant/index.d.ts +2 -0
  66. package/dist/instant/index.js +3 -0
  67. package/dist/internal/adjustZonedBusinessDays.js +9 -7
  68. package/dist/internal/businessCalendar.d.ts +46 -0
  69. package/dist/internal/businessCalendar.js +230 -0
  70. package/dist/internal/businessDayValues.d.ts +20 -0
  71. package/dist/internal/businessDayValues.js +54 -0
  72. package/dist/internal/calendarDatePairPolicy.d.ts +10 -12
  73. package/dist/internal/calendarDatePairPolicy.js +13 -19
  74. package/dist/internal/calendarDateString.d.ts +32 -58
  75. package/dist/internal/calendarDateString.js +51 -93
  76. package/dist/internal/calendarSystemIds.d.ts +26 -2
  77. package/dist/internal/calendarSystemIds.js +57 -51
  78. package/dist/internal/calendarValueOfDate.d.ts +10 -12
  79. package/dist/internal/calendarValueOfDate.js +22 -20
  80. package/dist/internal/calendarValueOfZoned.d.ts +14 -17
  81. package/dist/internal/calendarValueOfZoned.js +25 -25
  82. package/dist/internal/calendarZonedPairPolicy.d.ts +11 -18
  83. package/dist/internal/calendarZonedPairPolicy.js +14 -25
  84. package/dist/internal/calendarZonedString.d.ts +41 -65
  85. package/dist/internal/calendarZonedString.js +52 -101
  86. package/dist/internal/compareUnixEpochs.d.ts +14 -0
  87. package/dist/internal/compareUnixEpochs.js +25 -0
  88. package/dist/internal/dateTimeUnitHelpers.d.ts +18 -0
  89. package/dist/internal/dateTimeUnitHelpers.js +27 -0
  90. package/dist/internal/dateUnitHelpers.d.ts +39 -1
  91. package/dist/internal/dateUnitHelpers.js +73 -11
  92. package/dist/internal/defaultFractionalDigits.d.ts +14 -0
  93. package/dist/internal/defaultFractionalDigits.js +23 -0
  94. package/dist/internal/differenceRecord.d.ts +32 -0
  95. package/dist/internal/differenceRecord.js +79 -0
  96. package/dist/internal/divisionBoundary.d.ts +19 -0
  97. package/dist/internal/divisionBoundary.js +23 -0
  98. package/dist/internal/durationUntilString.d.ts +1 -1
  99. package/dist/internal/durationUntilString.js +10 -2
  100. package/dist/internal/englishCalendarNames.d.ts +11 -0
  101. package/dist/internal/englishCalendarNames.js +21 -1
  102. package/dist/internal/epochNanoseconds.d.ts +27 -0
  103. package/dist/internal/epochNanoseconds.js +31 -0
  104. package/dist/internal/fiscalCalendar.d.ts +36 -0
  105. package/dist/internal/fiscalCalendar.js +86 -0
  106. package/dist/internal/floorDivide.d.ts +15 -0
  107. package/dist/internal/floorDivide.js +18 -0
  108. package/dist/internal/foreignEpochs.d.ts +134 -0
  109. package/dist/internal/foreignEpochs.js +139 -0
  110. package/dist/internal/formatCalendarDays.d.ts +28 -0
  111. package/dist/internal/formatCalendarDays.js +49 -0
  112. package/dist/internal/formatDateInCalendar.d.ts +14 -39
  113. package/dist/internal/formatDateInCalendar.js +16 -47
  114. package/dist/internal/formatRelativeDuration.d.ts +27 -0
  115. package/dist/internal/formatRelativeDuration.js +45 -0
  116. package/dist/internal/getLocaleFirstDayOfWeek.d.ts +9 -6
  117. package/dist/internal/getLocaleFirstDayOfWeek.js +9 -10
  118. package/dist/internal/getLocaleWeekYearBounds.d.ts +17 -9
  119. package/dist/internal/getLocaleWeekYearBounds.js +45 -26
  120. package/dist/internal/getLocaleWeekendDays.d.ts +9 -18
  121. package/dist/internal/getLocaleWeekendDays.js +9 -5
  122. package/dist/internal/halfOpenIntervals.d.ts +102 -0
  123. package/dist/internal/halfOpenIntervals.js +180 -0
  124. package/dist/internal/hasCalendarAnnotation.d.ts +20 -20
  125. package/dist/internal/hasCalendarAnnotation.js +23 -21
  126. package/dist/internal/hourDurationString.d.ts +16 -0
  127. package/dist/internal/hourDurationString.js +47 -0
  128. package/dist/internal/httpDateFields.d.ts +24 -0
  129. package/dist/internal/httpDateFields.js +86 -0
  130. package/dist/internal/index.d.ts +28 -9
  131. package/dist/internal/index.js +28 -9
  132. package/dist/internal/instantFormatOptions.d.ts +43 -0
  133. package/dist/internal/instantFormatOptions.js +99 -0
  134. package/dist/internal/instantIntervalText.d.ts +20 -0
  135. package/dist/internal/instantIntervalText.js +25 -0
  136. package/dist/internal/instantNanoseconds.d.ts +25 -0
  137. package/dist/internal/instantNanoseconds.js +41 -0
  138. package/dist/internal/intervalCountHelpers.d.ts +16 -10
  139. package/dist/internal/intervalCountHelpers.js +1 -31
  140. package/dist/internal/intervalNanoseconds.d.ts +47 -0
  141. package/dist/internal/intervalNanoseconds.js +86 -0
  142. package/dist/internal/isObject.d.ts +26 -0
  143. package/dist/internal/isObject.js +30 -0
  144. package/dist/internal/isoStringBody.d.ts +40 -0
  145. package/dist/internal/isoStringBody.js +85 -0
  146. package/dist/internal/joinDateTimeConnector.d.ts +1 -1
  147. package/dist/internal/localeWeekInfo.d.ts +34 -0
  148. package/dist/internal/localeWeekInfo.js +27 -0
  149. package/dist/internal/maxPieces.d.ts +41 -0
  150. package/dist/internal/maxPieces.js +53 -0
  151. package/dist/internal/measureNearRangeEnd.d.ts +26 -0
  152. package/dist/internal/measureNearRangeEnd.js +47 -0
  153. package/dist/internal/monthGridWeekRow.d.ts +4 -1
  154. package/dist/internal/monthGridWeekRow.js +3 -0
  155. package/dist/internal/normalizeTimeZone.d.ts +17 -10
  156. package/dist/internal/normalizeTimeZone.js +25 -15
  157. package/dist/internal/patternToken.d.ts +2 -1
  158. package/dist/internal/patternToken.js +10 -4
  159. package/dist/internal/plainDateAdd.d.ts +20 -0
  160. package/dist/internal/plainDateAdd.js +42 -0
  161. package/dist/internal/plainDateUntil.d.ts +21 -0
  162. package/dist/internal/plainDateUntil.js +48 -0
  163. package/dist/internal/plainFormatOptions.d.ts +59 -0
  164. package/dist/internal/plainFormatOptions.js +165 -0
  165. package/dist/internal/resolveDateTimeUnit.d.ts +4 -1
  166. package/dist/internal/resolveDateTimeUnit.js +2 -12
  167. package/dist/internal/resolveDurationRelativeTo.d.ts +28 -22
  168. package/dist/internal/resolveDurationRelativeTo.js +69 -25
  169. package/dist/internal/resolveDurationUnit.d.ts +4 -1
  170. package/dist/internal/resolveDurationUnit.js +2 -11
  171. package/dist/internal/resolveLocale.d.ts +37 -0
  172. package/dist/internal/resolveLocale.js +59 -0
  173. package/dist/internal/resolveManualRoundingOptions.d.ts +23 -0
  174. package/dist/internal/resolveManualRoundingOptions.js +39 -0
  175. package/dist/internal/resolveMinimalDaysInFirstWeek.d.ts +19 -0
  176. package/dist/internal/resolveMinimalDaysInFirstWeek.js +28 -0
  177. package/dist/internal/resolveOverflow.d.ts +6 -0
  178. package/dist/internal/resolveOverflow.js +7 -1
  179. package/dist/internal/resolveReadingTimeZone.d.ts +11 -0
  180. package/dist/internal/resolveReadingTimeZone.js +18 -0
  181. package/dist/internal/resolveUnixIntervalPair.d.ts +30 -0
  182. package/dist/internal/resolveUnixIntervalPair.js +53 -0
  183. package/dist/internal/resolveWeekStartsOn.d.ts +12 -0
  184. package/dist/internal/resolveWeekStartsOn.js +18 -0
  185. package/dist/internal/rfc5322DateTime.d.ts +26 -0
  186. package/dist/internal/rfc5322DateTime.js +176 -0
  187. package/dist/internal/splitStep.d.ts +81 -0
  188. package/dist/internal/splitStep.js +182 -0
  189. package/dist/internal/startOrEndOfUnix.d.ts +26 -0
  190. package/dist/internal/startOrEndOfUnix.js +47 -0
  191. package/dist/internal/startOrEndOfUtc.d.ts +27 -0
  192. package/dist/{utc/calculate → internal}/startOrEndOfUtc.js +45 -24
  193. package/dist/internal/startOrEndOfZoned.d.ts +22 -0
  194. package/dist/internal/startOrEndOfZoned.js +49 -0
  195. package/dist/internal/temporalCompat/calendarDateArithmetic.d.ts +33 -0
  196. package/dist/internal/temporalCompat/calendarDateArithmetic.js +199 -0
  197. package/dist/internal/temporalCompat/calendarDateFromFields.d.ts +36 -0
  198. package/dist/internal/temporalCompat/calendarDateFromFields.js +120 -0
  199. package/dist/internal/temporalCompat/calendarFields.d.ts +25 -0
  200. package/dist/internal/temporalCompat/calendarFields.js +131 -0
  201. package/dist/internal/temporalCompat/capabilities.d.ts +28 -0
  202. package/dist/internal/temporalCompat/capabilities.js +74 -0
  203. package/dist/internal/temporalCompat/fieldSearch.d.ts +21 -0
  204. package/dist/internal/temporalCompat/fieldSearch.js +83 -0
  205. package/dist/internal/temporalCompat/fixedDay.d.ts +25 -0
  206. package/dist/internal/temporalCompat/fixedDay.js +45 -0
  207. package/dist/internal/temporalCompat/hebrewArithmetic.d.ts +19 -0
  208. package/dist/internal/temporalCompat/hebrewArithmetic.js +150 -0
  209. package/dist/internal/temporalCompat/index.d.ts +4 -0
  210. package/dist/internal/temporalCompat/index.js +5 -0
  211. package/dist/internal/temporalCompat/indianArithmetic.d.ts +16 -0
  212. package/dist/internal/temporalCompat/indianArithmetic.js +87 -0
  213. package/dist/internal/temporalCompat/largeMonthSpan.d.ts +19 -0
  214. package/dist/internal/temporalCompat/largeMonthSpan.js +199 -0
  215. package/dist/internal/temporalCompat/nonIsoArithmetic.d.ts +59 -0
  216. package/dist/internal/temporalCompat/nonIsoArithmetic.js +220 -0
  217. package/dist/internal/temporalCompat/readArithmeticModel.d.ts +3 -0
  218. package/dist/internal/temporalCompat/readArithmeticModel.js +284 -0
  219. package/dist/internal/temporalCompat/repros.d.ts +31 -0
  220. package/dist/internal/temporalCompat/repros.js +556 -0
  221. package/dist/internal/toInstantFromUtc.d.ts +10 -0
  222. package/dist/internal/toInstantFromUtc.js +19 -0
  223. package/dist/internal/unixEpochValue.d.ts +166 -0
  224. package/dist/internal/unixEpochValue.js +245 -0
  225. package/dist/internal/unixFormatReference.d.ts +18 -0
  226. package/dist/internal/unixFormatReference.js +44 -0
  227. package/dist/internal/unixZonedDateTime.d.ts +22 -0
  228. package/dist/internal/unixZonedDateTime.js +32 -0
  229. package/dist/internal/utcOffsetString.d.ts +39 -0
  230. package/dist/internal/utcOffsetString.js +75 -0
  231. package/dist/internal/utcZonedDateTime.d.ts +16 -0
  232. package/dist/internal/utcZonedDateTime.js +28 -0
  233. package/dist/internal/zonedBucket.d.ts +91 -0
  234. package/dist/internal/zonedBucket.js +441 -0
  235. package/dist/internal/zonedFormatReference.d.ts +15 -0
  236. package/dist/internal/zonedFormatReference.js +24 -0
  237. package/dist/internal/zonedLocalDates.d.ts +15 -0
  238. package/dist/internal/zonedLocalDates.js +58 -0
  239. package/dist/internal/zonedNowUnitValue.d.ts +24 -0
  240. package/dist/internal/zonedNowUnitValue.js +58 -0
  241. package/dist/internal/zonedWallClock.d.ts +84 -0
  242. package/dist/internal/zonedWallClock.js +427 -0
  243. package/dist/internal/zonedWallClockDifference.d.ts +92 -0
  244. package/dist/internal/zonedWallClockDifference.js +1173 -0
  245. package/dist/internal/zonedWallClockOperations.d.ts +132 -0
  246. package/dist/internal/zonedWallClockOperations.js +553 -0
  247. package/dist/internal/zonelessCalendarDate.d.ts +12 -0
  248. package/dist/internal/zonelessCalendarDate.js +22 -0
  249. package/dist/interval/calculate/clampInterval.d.ts +22 -0
  250. package/dist/interval/calculate/clampInterval.js +31 -0
  251. package/dist/interval/calculate/index.d.ts +6 -0
  252. package/dist/interval/calculate/index.js +6 -0
  253. package/dist/interval/calculate/intersectIntervals.d.ts +23 -0
  254. package/dist/interval/calculate/intersectIntervals.js +43 -0
  255. package/dist/interval/calculate/mergeIntervals.d.ts +28 -0
  256. package/dist/interval/calculate/mergeIntervals.js +44 -0
  257. package/dist/interval/calculate/splitIntervalAt.d.ts +28 -0
  258. package/dist/interval/calculate/splitIntervalAt.js +63 -0
  259. package/dist/interval/calculate/subtractIntervals.d.ts +31 -0
  260. package/dist/interval/calculate/subtractIntervals.js +64 -0
  261. package/dist/interval/calculate/sumIntervals.d.ts +26 -0
  262. package/dist/interval/calculate/sumIntervals.js +40 -0
  263. package/dist/interval/compare/index.d.ts +2 -0
  264. package/dist/interval/compare/index.js +2 -0
  265. package/dist/interval/compare/intervalContains.d.ts +25 -0
  266. package/dist/interval/compare/intervalContains.js +39 -0
  267. package/dist/interval/compare/intervalsOverlap.d.ts +28 -0
  268. package/dist/interval/compare/intervalsOverlap.js +42 -0
  269. package/dist/interval/index.d.ts +4 -0
  270. package/dist/interval/index.js +5 -0
  271. package/dist/interval/validate/index.d.ts +1 -0
  272. package/dist/interval/validate/index.js +1 -0
  273. package/dist/interval/validate/isValidInterval.d.ts +29 -0
  274. package/dist/interval/validate/isValidInterval.js +38 -0
  275. package/dist/plain/calculate/addBusinessDays.d.ts +26 -9
  276. package/dist/plain/calculate/addBusinessDays.js +29 -21
  277. package/dist/plain/calculate/addDate.d.ts +9 -8
  278. package/dist/plain/calculate/addDate.js +35 -22
  279. package/dist/plain/calculate/addDateTime.js +24 -10
  280. package/dist/plain/calculate/addTime.d.ts +5 -8
  281. package/dist/plain/calculate/addTime.js +23 -16
  282. package/dist/plain/calculate/clampDate.js +3 -3
  283. package/dist/plain/calculate/closestDateTo.d.ts +11 -7
  284. package/dist/plain/calculate/closestDateTo.js +36 -24
  285. package/dist/plain/calculate/cycleDate.d.ts +1 -1
  286. package/dist/plain/calculate/cycleDate.js +10 -3
  287. package/dist/plain/calculate/cycleDateTime.d.ts +1 -1
  288. package/dist/plain/calculate/cycleDateTime.js +10 -3
  289. package/dist/plain/calculate/cycleTime.d.ts +6 -8
  290. package/dist/plain/calculate/cycleTime.js +15 -9
  291. package/dist/plain/calculate/diffDate.d.ts +31 -14
  292. package/dist/plain/calculate/diffDate.js +73 -38
  293. package/dist/plain/calculate/diffDateAsDuration.d.ts +13 -8
  294. package/dist/plain/calculate/diffDateAsDuration.js +23 -10
  295. package/dist/plain/calculate/diffDateTime.d.ts +17 -5
  296. package/dist/plain/calculate/diffDateTime.js +67 -28
  297. package/dist/plain/calculate/diffDateTimeAsDuration.d.ts +2 -2
  298. package/dist/plain/calculate/diffDateTimeAsDuration.js +10 -3
  299. package/dist/plain/calculate/diffTime.d.ts +18 -5
  300. package/dist/plain/calculate/diffTime.js +68 -27
  301. package/dist/plain/calculate/endOfDate.d.ts +10 -2
  302. package/dist/plain/calculate/endOfDate.js +52 -27
  303. package/dist/plain/calculate/endOfDateTime.d.ts +17 -2
  304. package/dist/plain/calculate/endOfDateTime.js +126 -103
  305. package/dist/plain/calculate/endOfTime.d.ts +16 -2
  306. package/dist/plain/calculate/endOfTime.js +77 -59
  307. package/dist/plain/calculate/getLargestDateDurationUnit.d.ts +13 -5
  308. package/dist/plain/calculate/getLargestDateDurationUnit.js +26 -10
  309. package/dist/plain/calculate/getLargestDateTimeDurationUnit.d.ts +13 -6
  310. package/dist/plain/calculate/getLargestDateTimeDurationUnit.js +37 -22
  311. package/dist/plain/calculate/getLargestTimeDurationUnit.d.ts +13 -6
  312. package/dist/plain/calculate/getLargestTimeDurationUnit.js +33 -18
  313. package/dist/plain/calculate/getLocaleDayOfWeek.d.ts +8 -4
  314. package/dist/plain/calculate/getLocaleDayOfWeek.js +8 -3
  315. package/dist/plain/calculate/getLocaleEndOfWeek.d.ts +8 -4
  316. package/dist/plain/calculate/getLocaleEndOfWeek.js +8 -3
  317. package/dist/plain/calculate/getLocaleStartOfWeek.d.ts +8 -4
  318. package/dist/plain/calculate/getLocaleStartOfWeek.js +8 -3
  319. package/dist/plain/calculate/getLocaleWeekYear.d.ts +29 -19
  320. package/dist/plain/calculate/getLocaleWeekYear.js +48 -28
  321. package/dist/plain/calculate/getWeekNumber.d.ts +21 -2
  322. package/dist/plain/calculate/getWeekNumber.js +33 -12
  323. package/dist/plain/calculate/getWeekOfMonth.d.ts +4 -2
  324. package/dist/plain/calculate/getWeekOfMonth.js +8 -3
  325. package/dist/plain/calculate/getWeeksInLocaleWeekYear.d.ts +25 -14
  326. package/dist/plain/calculate/getWeeksInLocaleWeekYear.js +45 -25
  327. package/dist/plain/calculate/getWeeksInMonth.d.ts +4 -2
  328. package/dist/plain/calculate/getWeeksInMonth.js +8 -3
  329. package/dist/plain/calculate/getWeeksInYear.d.ts +5 -4
  330. package/dist/plain/calculate/getWeeksInYear.js +9 -14
  331. package/dist/plain/calculate/index.d.ts +2 -1
  332. package/dist/plain/calculate/index.js +2 -1
  333. package/dist/plain/calculate/maxDate.js +19 -12
  334. package/dist/plain/calculate/maxDateTime.js +19 -12
  335. package/dist/plain/calculate/maxTime.js +19 -12
  336. package/dist/plain/calculate/minDate.js +19 -12
  337. package/dist/plain/calculate/minDateTime.js +19 -12
  338. package/dist/plain/calculate/minTime.js +19 -12
  339. package/dist/plain/calculate/nextWeekday.js +5 -0
  340. package/dist/plain/calculate/previousWeekday.js +5 -0
  341. package/dist/plain/calculate/roundDate.d.ts +23 -5
  342. package/dist/plain/calculate/roundDate.js +88 -46
  343. package/dist/plain/calculate/roundDateTime.d.ts +27 -3
  344. package/dist/plain/calculate/roundDateTime.js +128 -69
  345. package/dist/plain/calculate/roundTime.d.ts +8 -1
  346. package/dist/plain/calculate/roundTime.js +32 -17
  347. package/dist/plain/calculate/setDate.js +4 -0
  348. package/dist/plain/calculate/setDateTime.js +4 -0
  349. package/dist/plain/calculate/setTime.d.ts +1 -1
  350. package/dist/plain/calculate/setTime.js +5 -1
  351. package/dist/plain/calculate/sortDateTimes.js +17 -10
  352. package/dist/plain/calculate/sortDates.js +17 -10
  353. package/dist/plain/calculate/sortTimes.js +17 -10
  354. package/dist/plain/calculate/startOfDate.d.ts +7 -2
  355. package/dist/plain/calculate/startOfDate.js +52 -27
  356. package/dist/plain/calculate/startOfDateTime.d.ts +5 -2
  357. package/dist/plain/calculate/startOfDateTime.js +81 -62
  358. package/dist/plain/calculate/startOfQuarterForDateTime.d.ts +2 -0
  359. package/dist/plain/calculate/startOfQuarterForDateTime.js +12 -1
  360. package/dist/plain/calculate/startOfTime.d.ts +15 -2
  361. package/dist/plain/calculate/startOfTime.js +33 -37
  362. package/dist/plain/calculate/subtractBusinessDays.d.ts +25 -10
  363. package/dist/plain/calculate/subtractBusinessDays.js +28 -22
  364. package/dist/plain/calculate/subtractDate.d.ts +8 -7
  365. package/dist/plain/calculate/subtractDate.js +35 -21
  366. package/dist/plain/calculate/subtractDateTime.js +24 -10
  367. package/dist/plain/calculate/subtractTime.d.ts +5 -8
  368. package/dist/plain/calculate/subtractTime.js +23 -16
  369. package/dist/plain/calculate/{weekOfYear.d.ts → weekOfYearForDate.d.ts} +0 -14
  370. package/dist/plain/calculate/weekOfYearForDate.js +28 -0
  371. package/dist/plain/calculate/weekOfYearForDateTime.d.ts +14 -0
  372. package/dist/plain/calculate/{weekOfYear.js → weekOfYearForDateTime.js} +1 -27
  373. package/dist/plain/chop/index.d.ts +0 -1
  374. package/dist/plain/chop/index.js +0 -1
  375. package/dist/plain/compare/areDateTimesEqualBy.d.ts +10 -5
  376. package/dist/plain/compare/areDateTimesEqualBy.js +35 -10
  377. package/dist/plain/compare/areDatesEqualBy.d.ts +10 -3
  378. package/dist/plain/compare/areDatesEqualBy.js +41 -14
  379. package/dist/plain/compare/isBetweenDate.js +32 -16
  380. package/dist/plain/compare/isBetweenDateTime.js +34 -18
  381. package/dist/plain/compare/isBetweenTime.js +32 -16
  382. package/dist/plain/compare/isBusinessDay.d.ts +25 -11
  383. package/dist/plain/compare/isBusinessDay.js +36 -15
  384. package/dist/plain/compare/isThisUnit.d.ts +13 -4
  385. package/dist/plain/compare/isThisUnit.js +33 -13
  386. package/dist/plain/compare/isWeekend.d.ts +8 -4
  387. package/dist/plain/compare/isWeekend.js +7 -3
  388. package/dist/plain/convert/convertDateToCalendar.d.ts +34 -33
  389. package/dist/plain/convert/convertDateToCalendar.js +41 -40
  390. package/dist/plain/format/formatCalendar.d.ts +14 -8
  391. package/dist/plain/format/formatCalendar.js +47 -25
  392. package/dist/plain/format/formatDate.d.ts +22 -4
  393. package/dist/plain/format/formatDate.js +37 -5
  394. package/dist/plain/format/formatDateRange.d.ts +18 -4
  395. package/dist/plain/format/formatDateRange.js +29 -5
  396. package/dist/plain/format/formatDateTime.d.ts +23 -4
  397. package/dist/plain/format/formatDateTime.js +31 -5
  398. package/dist/plain/format/formatDateTimeRange.d.ts +21 -4
  399. package/dist/plain/format/formatDateTimeRange.js +29 -5
  400. package/dist/plain/format/formatDateTimeToParts.d.ts +33 -6
  401. package/dist/plain/format/formatDateTimeToParts.js +40 -14
  402. package/dist/plain/format/formatDateToParts.d.ts +25 -3
  403. package/dist/plain/format/formatDateToParts.js +36 -11
  404. package/dist/plain/format/formatRelativeDate.d.ts +10 -17
  405. package/dist/plain/format/formatRelativeDate.js +39 -24
  406. package/dist/plain/format/formatRelativeDateTime.d.ts +18 -20
  407. package/dist/plain/format/formatRelativeDateTime.js +35 -33
  408. package/dist/plain/format/formatRelativeTime.d.ts +11 -18
  409. package/dist/plain/format/formatRelativeTime.js +36 -21
  410. package/dist/plain/format/formatSql.d.ts +15 -10
  411. package/dist/plain/format/formatSql.js +20 -11
  412. package/dist/plain/format/formatTime.d.ts +19 -4
  413. package/dist/plain/format/formatTime.js +32 -5
  414. package/dist/plain/get/getNowUnit.d.ts +5 -3
  415. package/dist/plain/get/getNowUnit.js +14 -29
  416. package/dist/plain/index.js +1 -1
  417. package/dist/plain/interval/intervalAbutsDate.d.ts +12 -7
  418. package/dist/plain/interval/intervalAbutsDate.js +15 -19
  419. package/dist/plain/interval/intervalAbutsDateTime.d.ts +9 -7
  420. package/dist/plain/interval/intervalAbutsDateTime.js +17 -23
  421. package/dist/plain/interval/intervalAbutsTime.d.ts +11 -5
  422. package/dist/plain/interval/intervalAbutsTime.js +19 -21
  423. package/dist/plain/interval/intervalContainsDate.d.ts +17 -10
  424. package/dist/plain/interval/intervalContainsDate.js +20 -15
  425. package/dist/plain/interval/intervalContainsDateTime.d.ts +16 -11
  426. package/dist/plain/interval/intervalContainsDateTime.js +24 -20
  427. package/dist/plain/interval/intervalContainsTime.d.ts +20 -10
  428. package/dist/plain/interval/intervalContainsTime.js +28 -19
  429. package/dist/plain/interval/intervalCountDate.d.ts +13 -8
  430. package/dist/plain/interval/intervalCountDate.js +46 -14
  431. package/dist/plain/interval/intervalCountDateTime.d.ts +6 -4
  432. package/dist/plain/interval/intervalCountDateTime.js +63 -18
  433. package/dist/plain/interval/intervalCountTime.d.ts +3 -4
  434. package/dist/plain/interval/intervalCountTime.js +9 -9
  435. package/dist/plain/interval/intervalDifferenceDate.d.ts +13 -6
  436. package/dist/plain/interval/intervalDifferenceDate.js +19 -32
  437. package/dist/plain/interval/intervalDifferenceDateTime.d.ts +10 -4
  438. package/dist/plain/interval/intervalDifferenceDateTime.js +21 -27
  439. package/dist/plain/interval/intervalDifferenceTime.d.ts +11 -4
  440. package/dist/plain/interval/intervalDifferenceTime.js +22 -27
  441. package/dist/plain/interval/intervalDivideEquallyDate.d.ts +16 -4
  442. package/dist/plain/interval/intervalDivideEquallyDate.js +57 -33
  443. package/dist/plain/interval/intervalDivideEquallyDateTime.d.ts +16 -4
  444. package/dist/plain/interval/intervalDivideEquallyDateTime.js +67 -31
  445. package/dist/plain/interval/intervalDivideEquallyTime.d.ts +16 -4
  446. package/dist/plain/interval/intervalDivideEquallyTime.js +58 -31
  447. package/dist/plain/interval/intervalEngulfsDate.d.ts +16 -9
  448. package/dist/plain/interval/intervalEngulfsDate.js +19 -12
  449. package/dist/plain/interval/intervalEngulfsDateTime.d.ts +16 -11
  450. package/dist/plain/interval/intervalEngulfsDateTime.js +24 -18
  451. package/dist/plain/interval/intervalEngulfsTime.d.ts +18 -8
  452. package/dist/plain/interval/intervalEngulfsTime.js +26 -15
  453. package/dist/plain/interval/intervalFromDurationDate.d.ts +5 -4
  454. package/dist/plain/interval/intervalFromDurationDate.js +11 -8
  455. package/dist/plain/interval/intervalFromDurationDateTime.js +4 -0
  456. package/dist/plain/interval/intervalFromDurationTime.d.ts +10 -12
  457. package/dist/plain/interval/intervalFromDurationTime.js +28 -17
  458. package/dist/plain/interval/intervalIntersectionDate.d.ts +9 -4
  459. package/dist/plain/interval/intervalIntersectionDate.js +14 -9
  460. package/dist/plain/interval/intervalIntersectionDateTime.d.ts +9 -4
  461. package/dist/plain/interval/intervalIntersectionDateTime.js +19 -13
  462. package/dist/plain/interval/intervalIntersectionTime.d.ts +7 -3
  463. package/dist/plain/interval/intervalIntersectionTime.js +17 -12
  464. package/dist/plain/interval/intervalLengthDate.d.ts +9 -6
  465. package/dist/plain/interval/intervalLengthDate.js +40 -8
  466. package/dist/plain/interval/intervalLengthDateTime.js +2 -2
  467. package/dist/plain/interval/intervalOverlappingDaysDate.d.ts +16 -17
  468. package/dist/plain/interval/intervalOverlappingDaysDate.js +30 -30
  469. package/dist/plain/interval/intervalOverlappingDaysDateTime.d.ts +7 -12
  470. package/dist/plain/interval/intervalOverlappingDaysDateTime.js +24 -24
  471. package/dist/plain/interval/intervalSplitAtDate.d.ts +10 -5
  472. package/dist/plain/interval/intervalSplitAtDate.js +48 -36
  473. package/dist/plain/interval/intervalSplitAtDateTime.d.ts +9 -4
  474. package/dist/plain/interval/intervalSplitAtDateTime.js +44 -32
  475. package/dist/plain/interval/intervalSplitAtTime.d.ts +9 -4
  476. package/dist/plain/interval/intervalSplitAtTime.js +44 -32
  477. package/dist/plain/interval/intervalUnionDate.d.ts +14 -6
  478. package/dist/plain/interval/intervalUnionDate.js +19 -11
  479. package/dist/plain/interval/intervalUnionDateTime.d.ts +12 -5
  480. package/dist/plain/interval/intervalUnionDateTime.js +22 -14
  481. package/dist/plain/interval/intervalUnionTime.d.ts +10 -3
  482. package/dist/plain/interval/intervalUnionTime.js +20 -12
  483. package/dist/plain/interval/intervalXorAllDate.d.ts +11 -10
  484. package/dist/plain/interval/intervalXorAllDate.js +37 -59
  485. package/dist/plain/interval/intervalXorAllDateTime.d.ts +7 -5
  486. package/dist/plain/interval/intervalXorAllDateTime.js +28 -48
  487. package/dist/plain/interval/intervalXorAllTime.d.ts +8 -4
  488. package/dist/plain/interval/intervalXorAllTime.js +29 -47
  489. package/dist/plain/interval/intervalXorDate.d.ts +10 -9
  490. package/dist/plain/interval/intervalXorDate.js +19 -54
  491. package/dist/plain/interval/intervalXorDateTime.d.ts +9 -7
  492. package/dist/plain/interval/intervalXorDateTime.js +23 -50
  493. package/dist/plain/interval/intervalXorTime.d.ts +8 -7
  494. package/dist/plain/interval/intervalXorTime.js +22 -50
  495. package/dist/plain/interval/intervalsOverlapDate.d.ts +14 -7
  496. package/dist/plain/interval/intervalsOverlapDate.js +17 -10
  497. package/dist/plain/interval/intervalsOverlapDateTime.d.ts +12 -7
  498. package/dist/plain/interval/intervalsOverlapDateTime.js +20 -14
  499. package/dist/plain/interval/intervalsOverlapTime.d.ts +10 -5
  500. package/dist/plain/interval/intervalsOverlapTime.js +18 -12
  501. package/dist/plain/interval/mergeIntervalsDate.d.ts +9 -5
  502. package/dist/plain/interval/mergeIntervalsDate.js +37 -38
  503. package/dist/plain/interval/mergeIntervalsDateTime.d.ts +7 -3
  504. package/dist/plain/interval/mergeIntervalsDateTime.js +31 -32
  505. package/dist/plain/interval/mergeIntervalsTime.d.ts +7 -3
  506. package/dist/plain/interval/mergeIntervalsTime.js +31 -31
  507. package/dist/plain/interval/splitIntervalByUnitDate.d.ts +26 -6
  508. package/dist/plain/interval/splitIntervalByUnitDate.js +76 -43
  509. package/dist/plain/interval/splitIntervalByUnitDateTime.d.ts +22 -2
  510. package/dist/plain/interval/splitIntervalByUnitDateTime.js +65 -39
  511. package/dist/plain/interval/splitIntervalByUnitTime.d.ts +18 -2
  512. package/dist/plain/interval/splitIntervalByUnitTime.js +76 -39
  513. package/dist/plain/interval/validate/isValidDateInterval.d.ts +4 -2
  514. package/dist/plain/interval/validate/isValidDateInterval.js +4 -2
  515. package/dist/plain/interval/validate/isValidDateTimeInterval.d.ts +5 -0
  516. package/dist/plain/interval/validate/isValidDateTimeInterval.js +7 -2
  517. package/dist/plain/interval/validate/isValidTimeInterval.d.ts +5 -0
  518. package/dist/plain/interval/validate/isValidTimeInterval.js +8 -18
  519. package/dist/plain/locale/getLocaleEraNames.d.ts +14 -4
  520. package/dist/plain/locale/getLocaleEraNames.js +56 -41
  521. package/dist/plain/locale/getLocaleMeridiems.d.ts +7 -4
  522. package/dist/plain/locale/getLocaleMeridiems.js +28 -22
  523. package/dist/plain/locale/getLocaleMonthNames.d.ts +14 -4
  524. package/dist/plain/locale/getLocaleMonthNames.js +34 -19
  525. package/dist/plain/locale/getLocaleWeekdayNames.d.ts +14 -7
  526. package/dist/plain/locale/getLocaleWeekdayNames.js +40 -22
  527. package/dist/plain/map/mapDatesInRange.d.ts +18 -1
  528. package/dist/plain/map/mapDatesInRange.js +53 -17
  529. package/dist/plain/parse/parseDateTimeWithPattern.d.ts +3 -2
  530. package/dist/plain/parse/parseDateTimeWithPattern.js +30 -24
  531. package/dist/plain/parse/parseDateWithPattern.d.ts +3 -2
  532. package/dist/plain/parse/parseDateWithPattern.js +22 -16
  533. package/dist/plain/parse/parseMicrosecondFromDateTime.d.ts +2 -1
  534. package/dist/plain/parse/parseMicrosecondFromDateTime.js +2 -1
  535. package/dist/plain/parse/parseMicrosecondFromTime.d.ts +2 -1
  536. package/dist/plain/parse/parseMicrosecondFromTime.js +2 -1
  537. package/dist/plain/parse/parseNanosecondFromDateTime.d.ts +2 -1
  538. package/dist/plain/parse/parseNanosecondFromDateTime.js +2 -1
  539. package/dist/plain/parse/parseNanosecondFromTime.d.ts +2 -1
  540. package/dist/plain/parse/parseNanosecondFromTime.js +2 -1
  541. package/dist/plain/parse/parseSql.d.ts +15 -12
  542. package/dist/plain/parse/parseSql.js +16 -13
  543. package/dist/plain/parse/parseTimeWithPattern.d.ts +3 -2
  544. package/dist/plain/parse/parseTimeWithPattern.js +27 -21
  545. package/dist/plain/parse/parseUnitFromDate.d.ts +9 -2
  546. package/dist/plain/parse/parseUnitFromDate.js +39 -19
  547. package/dist/plain/parse/parseUnitFromDateTime.d.ts +11 -3
  548. package/dist/plain/parse/parseUnitFromDateTime.js +54 -28
  549. package/dist/plain/parse/parseUnitFromTime.d.ts +9 -3
  550. package/dist/plain/parse/parseUnitFromTime.js +13 -3
  551. package/dist/plain/parse/parseWeekFromDate.d.ts +5 -0
  552. package/dist/plain/parse/parseWeekFromDate.js +24 -5
  553. package/dist/plain/parse/parseWeekFromDateTime.d.ts +5 -0
  554. package/dist/plain/parse/parseWeekFromDateTime.js +26 -5
  555. package/dist/plain/validate/isLeapSecond.d.ts +27 -9
  556. package/dist/plain/validate/isLeapSecond.js +30 -9
  557. package/dist/plain/validate/isLeapYear.d.ts +7 -1
  558. package/dist/plain/validate/isLeapYear.js +10 -1
  559. package/dist/plain/validate/isValidCalendarDate.d.ts +37 -6
  560. package/dist/plain/validate/isValidCalendarDate.js +37 -6
  561. package/dist/plain/validate/isValidDate.d.ts +9 -1
  562. package/dist/plain/validate/isValidDate.js +14 -5
  563. package/dist/plain/validate/isValidDateRange.d.ts +11 -4
  564. package/dist/plain/validate/isValidDateRange.js +32 -22
  565. package/dist/plain/validate/isValidDateTime.d.ts +7 -1
  566. package/dist/plain/validate/isValidDateTime.js +12 -5
  567. package/dist/plain/validate/isValidDateTimeRange.d.ts +3 -2
  568. package/dist/plain/validate/isValidDateTimeRange.js +30 -15
  569. package/dist/plain/validate/isValidIsoDateLike.d.ts +6 -1
  570. package/dist/plain/validate/isValidIsoDateLike.js +13 -8
  571. package/dist/plain/validate/isValidTime.d.ts +6 -1
  572. package/dist/plain/validate/isValidTime.js +10 -3
  573. package/dist/plain/validate/isValidTimeRange.d.ts +4 -2
  574. package/dist/plain/validate/isValidTimeRange.js +31 -27
  575. package/dist/precision/calculate/index.d.ts +1 -0
  576. package/dist/precision/calculate/index.js +1 -0
  577. package/dist/precision/calculate/truncateNanoseconds.d.ts +37 -0
  578. package/dist/precision/calculate/truncateNanoseconds.js +41 -0
  579. package/dist/precision/convert/fromDotNetTicks.d.ts +30 -0
  580. package/dist/precision/convert/fromDotNetTicks.js +39 -0
  581. package/dist/precision/convert/fromExcelSerial.d.ts +38 -0
  582. package/dist/precision/convert/fromExcelSerial.js +81 -0
  583. package/dist/precision/convert/fromFileTime.d.ts +30 -0
  584. package/dist/precision/convert/fromFileTime.js +39 -0
  585. package/dist/precision/convert/fromNanoseconds.d.ts +29 -0
  586. package/dist/precision/convert/fromNanoseconds.js +50 -0
  587. package/dist/precision/convert/fromNtpTimestamp.d.ts +43 -0
  588. package/dist/precision/convert/fromNtpTimestamp.js +57 -0
  589. package/dist/precision/convert/fromPgMicroseconds.d.ts +26 -0
  590. package/dist/precision/convert/fromPgMicroseconds.js +33 -0
  591. package/dist/precision/convert/index.d.ts +12 -0
  592. package/dist/precision/convert/index.js +12 -0
  593. package/dist/precision/convert/toDotNetTicks.d.ts +29 -0
  594. package/dist/precision/convert/toDotNetTicks.js +40 -0
  595. package/dist/precision/convert/toExcelSerial.d.ts +56 -0
  596. package/dist/precision/convert/toExcelSerial.js +85 -0
  597. package/dist/precision/convert/toFileTime.d.ts +31 -0
  598. package/dist/precision/convert/toFileTime.js +43 -0
  599. package/dist/precision/convert/toNanoseconds.d.ts +37 -0
  600. package/dist/precision/convert/toNanoseconds.js +40 -0
  601. package/dist/precision/convert/toNtpTimestamp.d.ts +30 -0
  602. package/dist/precision/convert/toNtpTimestamp.js +38 -0
  603. package/dist/precision/convert/toPgMicroseconds.d.ts +29 -0
  604. package/dist/precision/convert/toPgMicroseconds.js +42 -0
  605. package/dist/precision/format/formatNanoseconds.d.ts +26 -0
  606. package/dist/precision/format/formatNanoseconds.js +32 -0
  607. package/dist/precision/format/index.d.ts +1 -0
  608. package/dist/precision/format/index.js +1 -0
  609. package/dist/precision/index.d.ts +6 -0
  610. package/dist/precision/index.js +7 -0
  611. package/dist/precision/parse/index.d.ts +1 -0
  612. package/dist/precision/parse/index.js +1 -0
  613. package/dist/precision/parse/parseNanoseconds.d.ts +27 -0
  614. package/dist/precision/parse/parseNanoseconds.js +32 -0
  615. package/dist/precision/validate/index.d.ts +3 -0
  616. package/dist/precision/validate/index.js +3 -0
  617. package/dist/precision/validate/isValidInstant.d.ts +37 -0
  618. package/dist/precision/validate/isValidInstant.js +40 -0
  619. package/dist/precision/validate/isValidNanoPattern.d.ts +27 -0
  620. package/dist/precision/validate/isValidNanoPattern.js +40 -0
  621. package/dist/precision/validate/isValidNanoseconds.d.ts +25 -0
  622. package/dist/precision/validate/isValidNanoseconds.js +28 -0
  623. package/dist/regex/calendar-date.d.ts +30 -0
  624. package/dist/regex/calendar-date.js +31 -11
  625. package/dist/regex/calendar-zoned-date-time.d.ts +29 -0
  626. package/dist/regex/calendar-zoned-date-time.js +30 -41
  627. package/dist/regex/date-time.d.ts +28 -0
  628. package/dist/regex/date-time.js +29 -2
  629. package/dist/regex/date.d.ts +58 -0
  630. package/dist/regex/date.js +60 -3
  631. package/dist/regex/http-date.d.ts +19 -0
  632. package/dist/regex/http-date.js +19 -6
  633. package/dist/regex/index.d.ts +3 -0
  634. package/dist/regex/index.js +4 -0
  635. package/dist/regex/leap-second.d.ts +45 -0
  636. package/dist/regex/leap-second.js +46 -2
  637. package/dist/regex/nanoseconds.d.ts +20 -0
  638. package/dist/regex/nanoseconds.js +20 -0
  639. package/dist/regex/rfc-2822.d.ts +26 -0
  640. package/dist/regex/rfc-2822.js +26 -8
  641. package/dist/regex/rfc-3339.d.ts +26 -0
  642. package/dist/regex/rfc-3339.js +27 -7
  643. package/dist/regex/sql-date-time.d.ts +33 -0
  644. package/dist/regex/sql-date-time.js +34 -7
  645. package/dist/regex/time-zone-like.d.ts +30 -0
  646. package/dist/regex/time-zone-like.js +31 -1
  647. package/dist/regex/time.d.ts +73 -0
  648. package/dist/regex/time.js +73 -3
  649. package/dist/regex/unix.d.ts +29 -0
  650. package/dist/regex/unix.js +29 -0
  651. package/dist/regex/utc-date-time.d.ts +30 -0
  652. package/dist/regex/utc-date-time.js +31 -2
  653. package/dist/regex/utc-offset.d.ts +24 -0
  654. package/dist/regex/utc-offset.js +24 -0
  655. package/dist/span/calculate/index.d.ts +3 -0
  656. package/dist/span/calculate/index.js +3 -0
  657. package/dist/span/calculate/spanMs.d.ts +52 -0
  658. package/dist/span/calculate/spanMs.js +80 -0
  659. package/dist/span/calculate/spanNs.d.ts +43 -0
  660. package/dist/span/calculate/spanNs.js +51 -0
  661. package/dist/span/calculate/spanWallClock.d.ts +60 -0
  662. package/dist/span/calculate/spanWallClock.js +80 -0
  663. package/dist/span/index.d.ts +3 -0
  664. package/dist/span/index.js +4 -0
  665. package/dist/span/validate/index.d.ts +1 -0
  666. package/dist/span/validate/index.js +1 -0
  667. package/dist/span/validate/isValidSpan.d.ts +37 -0
  668. package/dist/span/validate/isValidSpan.js +41 -0
  669. package/dist/types/business-calendar.d.ts +27 -0
  670. package/dist/types/business-calendar.js +0 -0
  671. package/dist/types/calendar-options.d.ts +11 -0
  672. package/dist/types/calendar-options.js +0 -0
  673. package/dist/types/calendar-system.d.ts +9 -1
  674. package/dist/types/datetime-format-options.d.ts +6 -0
  675. package/dist/types/datetime-format-options.js +0 -0
  676. package/dist/types/duration-unit.d.ts +5 -0
  677. package/dist/types/duration-unit.js +0 -0
  678. package/dist/types/fiscal-calendar.d.ts +22 -0
  679. package/dist/types/fiscal-calendar.js +0 -0
  680. package/dist/types/index.d.ts +17 -0
  681. package/dist/types/index.js +16 -0
  682. package/dist/types/interval.d.ts +18 -0
  683. package/dist/types/interval.js +0 -0
  684. package/dist/types/now-unit.d.ts +5 -0
  685. package/dist/types/now-unit.js +0 -0
  686. package/dist/types/relative-date-time-unit.d.ts +6 -0
  687. package/dist/types/relative-date-time-unit.js +0 -0
  688. package/dist/types/relative-date-unit.d.ts +5 -0
  689. package/dist/types/relative-date-unit.js +0 -0
  690. package/dist/types/relative-time-options.d.ts +18 -0
  691. package/dist/types/relative-time-options.js +0 -0
  692. package/dist/types/relative-time-unit.d.ts +5 -0
  693. package/dist/types/relative-time-unit.js +0 -0
  694. package/dist/types/relative-unit.d.ts +6 -0
  695. package/dist/types/relative-unit.js +0 -0
  696. package/dist/types/roll-convention.d.ts +32 -0
  697. package/dist/types/roll-convention.js +0 -0
  698. package/dist/types/unix-now-unit.d.ts +5 -0
  699. package/dist/types/unix-now-unit.js +0 -0
  700. package/dist/types/utc-now-unit.d.ts +5 -0
  701. package/dist/types/utc-now-unit.js +0 -0
  702. package/dist/types/zone-bucket-unit.d.ts +11 -0
  703. package/dist/types/zone-bucket-unit.js +0 -0
  704. package/dist/unix/calculate/addUnix.d.ts +10 -4
  705. package/dist/unix/calculate/addUnix.js +39 -26
  706. package/dist/unix/calculate/diffUnix.d.ts +38 -11
  707. package/dist/unix/calculate/diffUnix.js +84 -40
  708. package/dist/unix/calculate/diffUnixAsDuration.d.ts +17 -7
  709. package/dist/unix/calculate/diffUnixAsDuration.js +42 -23
  710. package/dist/unix/calculate/endOfQuarterForUnix.d.ts +15 -11
  711. package/dist/unix/calculate/endOfQuarterForUnix.js +20 -39
  712. package/dist/unix/calculate/endOfUnix.d.ts +23 -18
  713. package/dist/unix/calculate/endOfUnix.js +37 -15
  714. package/dist/unix/calculate/isBetweenUnix.d.ts +12 -6
  715. package/dist/unix/calculate/isBetweenUnix.js +34 -23
  716. package/dist/unix/calculate/maxUnix.d.ts +6 -3
  717. package/dist/unix/calculate/maxUnix.js +17 -8
  718. package/dist/unix/calculate/minUnix.d.ts +6 -3
  719. package/dist/unix/calculate/minUnix.js +17 -8
  720. package/dist/unix/calculate/roundUnix.d.ts +24 -10
  721. package/dist/unix/calculate/roundUnix.js +67 -42
  722. package/dist/unix/calculate/setUnix.d.ts +14 -5
  723. package/dist/unix/calculate/setUnix.js +48 -29
  724. package/dist/unix/calculate/sortUnix.d.ts +6 -3
  725. package/dist/unix/calculate/sortUnix.js +19 -11
  726. package/dist/unix/calculate/startOfQuarterForUnix.d.ts +17 -12
  727. package/dist/unix/calculate/startOfQuarterForUnix.js +22 -30
  728. package/dist/unix/calculate/startOfUnix.d.ts +23 -18
  729. package/dist/unix/calculate/startOfUnix.js +37 -15
  730. package/dist/unix/calculate/subtractUnix.d.ts +10 -4
  731. package/dist/unix/calculate/subtractUnix.js +39 -26
  732. package/dist/unix/compare/areUnixEqual.d.ts +9 -5
  733. package/dist/unix/compare/areUnixEqual.js +17 -13
  734. package/dist/unix/compare/areUnixEqualBy.d.ts +14 -7
  735. package/dist/unix/compare/areUnixEqualBy.js +14 -7
  736. package/dist/unix/compare/isAfterUnix.d.ts +8 -4
  737. package/dist/unix/compare/isAfterUnix.js +16 -12
  738. package/dist/unix/compare/isBeforeUnix.d.ts +7 -4
  739. package/dist/unix/compare/isBeforeUnix.js +15 -12
  740. package/dist/unix/convert/convertUnixToPlainDate.d.ts +17 -9
  741. package/dist/unix/convert/convertUnixToPlainDate.js +34 -21
  742. package/dist/unix/convert/convertUnixToPlainDateTime.d.ts +15 -9
  743. package/dist/unix/convert/convertUnixToPlainDateTime.js +32 -21
  744. package/dist/unix/convert/convertUnixToPlainTime.d.ts +15 -9
  745. package/dist/unix/convert/convertUnixToPlainTime.js +32 -21
  746. package/dist/unix/convert/convertUnixToUtc.d.ts +15 -7
  747. package/dist/unix/convert/convertUnixToUtc.js +20 -15
  748. package/dist/unix/convert/convertUnixToZoned.d.ts +17 -10
  749. package/dist/unix/convert/convertUnixToZoned.js +31 -20
  750. package/dist/unix/format/formatCalendarUnix.d.ts +35 -6
  751. package/dist/unix/format/formatCalendarUnix.js +35 -81
  752. package/dist/unix/format/formatRelativeUnix.d.ts +26 -16
  753. package/dist/unix/format/formatRelativeUnix.js +46 -93
  754. package/dist/unix/format/formatUnix.d.ts +43 -2
  755. package/dist/unix/format/formatUnix.js +65 -32
  756. package/dist/unix/get/getUnixNow.d.ts +13 -7
  757. package/dist/unix/get/getUnixNow.js +27 -17
  758. package/dist/unix/get/getUnixNowUnit.d.ts +7 -5
  759. package/dist/unix/get/getUnixNowUnit.js +9 -35
  760. package/dist/unix/index.d.ts +1 -0
  761. package/dist/unix/index.js +2 -0
  762. package/dist/unix/interval/intervalAbutsUnix.d.ts +17 -12
  763. package/dist/unix/interval/intervalAbutsUnix.js +23 -41
  764. package/dist/unix/interval/intervalContainsUnix.d.ts +17 -7
  765. package/dist/unix/interval/intervalContainsUnix.js +25 -37
  766. package/dist/unix/interval/intervalCountUnix.d.ts +30 -13
  767. package/dist/unix/interval/intervalCountUnix.js +49 -23
  768. package/dist/unix/interval/intervalDifferenceUnix.d.ts +19 -10
  769. package/dist/unix/interval/intervalDifferenceUnix.js +25 -43
  770. package/dist/unix/interval/intervalDivideEquallyUnix.d.ts +24 -10
  771. package/dist/unix/interval/intervalDivideEquallyUnix.js +57 -36
  772. package/dist/unix/interval/intervalEngulfsUnix.d.ts +13 -7
  773. package/dist/unix/interval/intervalEngulfsUnix.js +18 -30
  774. package/dist/unix/interval/intervalFromDurationUnix.d.ts +10 -6
  775. package/dist/unix/interval/intervalFromDurationUnix.js +51 -39
  776. package/dist/unix/interval/intervalIntersectionUnix.d.ts +18 -11
  777. package/dist/unix/interval/intervalIntersectionUnix.js +25 -46
  778. package/dist/unix/interval/intervalLengthUnix.d.ts +21 -9
  779. package/dist/unix/interval/intervalLengthUnix.js +41 -18
  780. package/dist/unix/interval/intervalOverlappingDaysUnix.d.ts +26 -12
  781. package/dist/unix/interval/intervalOverlappingDaysUnix.js +67 -63
  782. package/dist/unix/interval/intervalSplitAtUnix.d.ts +11 -6
  783. package/dist/unix/interval/intervalSplitAtUnix.js +41 -34
  784. package/dist/unix/interval/intervalUnionUnix.d.ts +15 -9
  785. package/dist/unix/interval/intervalUnionUnix.js +20 -42
  786. package/dist/unix/interval/intervalXorAllUnix.d.ts +8 -6
  787. package/dist/unix/interval/intervalXorAllUnix.js +21 -56
  788. package/dist/unix/interval/intervalXorUnix.d.ts +22 -11
  789. package/dist/unix/interval/intervalXorUnix.js +28 -56
  790. package/dist/unix/interval/intervalsOverlapUnix.d.ts +16 -10
  791. package/dist/unix/interval/intervalsOverlapUnix.js +21 -37
  792. package/dist/unix/interval/mergeIntervalsUnix.d.ts +10 -4
  793. package/dist/unix/interval/mergeIntervalsUnix.js +25 -36
  794. package/dist/unix/interval/splitIntervalByUnitUnix.d.ts +38 -8
  795. package/dist/unix/interval/splitIntervalByUnitUnix.js +72 -56
  796. package/dist/unix/interval/validate/isValidUnixInterval.d.ts +12 -6
  797. package/dist/unix/interval/validate/isValidUnixInterval.js +17 -16
  798. package/dist/unix/parse/index.d.ts +2 -0
  799. package/dist/unix/parse/index.js +2 -0
  800. package/dist/unix/parse/parseDateFromUnix.d.ts +10 -8
  801. package/dist/unix/parse/parseDateFromUnix.js +17 -29
  802. package/dist/unix/parse/parseDayFromUnix.d.ts +6 -4
  803. package/dist/unix/parse/parseDayFromUnix.js +18 -5
  804. package/dist/unix/parse/parseDayOfWeekFromUnix.d.ts +8 -5
  805. package/dist/unix/parse/parseDayOfWeekFromUnix.js +15 -28
  806. package/dist/unix/parse/parseHourFromUnix.d.ts +6 -4
  807. package/dist/unix/parse/parseHourFromUnix.js +18 -5
  808. package/dist/unix/parse/parseMicrosecondFromUnix.d.ts +4 -2
  809. package/dist/unix/parse/parseMicrosecondFromUnix.js +16 -3
  810. package/dist/unix/parse/parseMillisecondFromUnix.d.ts +4 -2
  811. package/dist/unix/parse/parseMillisecondFromUnix.js +16 -3
  812. package/dist/unix/parse/parseMinuteFromUnix.d.ts +6 -4
  813. package/dist/unix/parse/parseMinuteFromUnix.js +18 -5
  814. package/dist/unix/parse/parseMonthFromUnix.d.ts +6 -4
  815. package/dist/unix/parse/parseMonthFromUnix.js +18 -5
  816. package/dist/unix/parse/parseNanosecondFromUnix.d.ts +8 -5
  817. package/dist/unix/parse/parseNanosecondFromUnix.js +20 -6
  818. package/dist/unix/parse/parseSecondFromUnix.d.ts +5 -3
  819. package/dist/unix/parse/parseSecondFromUnix.js +17 -4
  820. package/dist/unix/parse/parseTimeFromUnix.d.ts +10 -8
  821. package/dist/unix/parse/parseTimeFromUnix.js +17 -29
  822. package/dist/unix/parse/parseUnitFromUnix.d.ts +40 -9
  823. package/dist/unix/parse/parseUnitFromUnix.js +57 -55
  824. package/dist/unix/parse/parseWeekFromUnix.d.ts +15 -7
  825. package/dist/unix/parse/parseWeekFromUnix.js +32 -30
  826. package/dist/unix/parse/parseYearFromUnix.d.ts +5 -3
  827. package/dist/unix/parse/parseYearFromUnix.js +17 -4
  828. package/dist/unix/validate/isValidUnixMilliseconds.d.ts +8 -5
  829. package/dist/unix/validate/isValidUnixMilliseconds.js +10 -9
  830. package/dist/unix/validate/isValidUnixRange.d.ts +13 -7
  831. package/dist/unix/validate/isValidUnixRange.js +29 -19
  832. package/dist/unix/validate/isValidUnixSeconds.d.ts +8 -5
  833. package/dist/unix/validate/isValidUnixSeconds.js +10 -9
  834. package/dist/unix/validate/isValidUnixUnit.d.ts +22 -5
  835. package/dist/unix/validate/isValidUnixUnit.js +6 -8
  836. package/dist/utc/calculate/addUtc.js +26 -12
  837. package/dist/utc/calculate/diffUtc.d.ts +22 -7
  838. package/dist/utc/calculate/diffUtc.js +75 -31
  839. package/dist/utc/calculate/diffUtcAsDuration.d.ts +2 -2
  840. package/dist/utc/calculate/diffUtcAsDuration.js +10 -3
  841. package/dist/utc/calculate/endOfQuarterForUtc.d.ts +1 -1
  842. package/dist/utc/calculate/endOfQuarterForUtc.js +1 -1
  843. package/dist/utc/calculate/endOfUtc.d.ts +11 -4
  844. package/dist/utc/calculate/endOfUtc.js +22 -5
  845. package/dist/utc/calculate/isBetweenUtc.js +40 -24
  846. package/dist/utc/calculate/maxUtc.js +19 -12
  847. package/dist/utc/calculate/minUtc.js +19 -12
  848. package/dist/utc/calculate/roundUtc.d.ts +16 -2
  849. package/dist/utc/calculate/roundUtc.js +42 -31
  850. package/dist/utc/calculate/setUtc.d.ts +4 -8
  851. package/dist/utc/calculate/setUtc.js +25 -18
  852. package/dist/utc/calculate/sortUtc.js +17 -10
  853. package/dist/utc/calculate/startOfQuarterForUtc.d.ts +2 -0
  854. package/dist/utc/calculate/startOfQuarterForUtc.js +12 -1
  855. package/dist/utc/calculate/startOfUtc.d.ts +6 -4
  856. package/dist/utc/calculate/startOfUtc.js +17 -5
  857. package/dist/utc/calculate/subtractUtc.js +26 -12
  858. package/dist/utc/chop/chopUtc.d.ts +5 -2
  859. package/dist/utc/chop/chopUtc.js +8 -4
  860. package/dist/utc/compare/areUtcEqualBy.d.ts +10 -5
  861. package/dist/utc/compare/areUtcEqualBy.js +34 -11
  862. package/dist/utc/convert/convertUtcToPlainDate.js +22 -10
  863. package/dist/utc/convert/convertUtcToPlainDateTime.js +28 -16
  864. package/dist/utc/convert/convertUtcToPlainTime.js +22 -10
  865. package/dist/utc/convert/convertUtcToUnix.d.ts +12 -5
  866. package/dist/utc/convert/convertUtcToUnix.js +22 -13
  867. package/dist/utc/format/formatCalendarUtc.d.ts +13 -8
  868. package/dist/utc/format/formatCalendarUtc.js +32 -41
  869. package/dist/utc/format/formatHttp.d.ts +9 -3
  870. package/dist/utc/format/formatHttp.js +14 -12
  871. package/dist/utc/format/formatRelativeUtc.d.ts +20 -18
  872. package/dist/utc/format/formatRelativeUtc.js +43 -51
  873. package/dist/utc/format/formatUtc.d.ts +51 -1
  874. package/dist/utc/format/formatUtc.js +57 -11
  875. package/dist/utc/get/getUtcNowUnit.d.ts +5 -3
  876. package/dist/utc/get/getUtcNowUnit.js +11 -33
  877. package/dist/utc/index.d.ts +1 -0
  878. package/dist/utc/index.js +2 -0
  879. package/dist/utc/interval/intervalAbutsUtc.d.ts +10 -7
  880. package/dist/utc/interval/intervalAbutsUtc.js +18 -30
  881. package/dist/utc/interval/intervalContainsUtc.d.ts +19 -11
  882. package/dist/utc/interval/intervalContainsUtc.js +33 -34
  883. package/dist/utc/interval/intervalCountUtc.d.ts +3 -4
  884. package/dist/utc/interval/intervalCountUtc.js +12 -18
  885. package/dist/utc/interval/intervalDifferenceUtc.d.ts +13 -5
  886. package/dist/utc/interval/intervalDifferenceUtc.js +25 -51
  887. package/dist/utc/interval/intervalDivideEquallyUtc.d.ts +16 -3
  888. package/dist/utc/interval/intervalDivideEquallyUtc.js +54 -34
  889. package/dist/utc/interval/intervalEngulfsUtc.d.ts +17 -12
  890. package/dist/utc/interval/intervalEngulfsUtc.js +25 -26
  891. package/dist/utc/interval/intervalFromDurationUtc.js +5 -0
  892. package/dist/utc/interval/intervalIntersectionUtc.d.ts +12 -6
  893. package/dist/utc/interval/intervalIntersectionUtc.js +24 -41
  894. package/dist/utc/interval/intervalLengthUtc.js +5 -7
  895. package/dist/utc/interval/intervalOverlappingDaysUtc.d.ts +7 -13
  896. package/dist/utc/interval/intervalOverlappingDaysUtc.js +25 -36
  897. package/dist/utc/interval/intervalSplitAtUtc.d.ts +7 -2
  898. package/dist/utc/interval/intervalSplitAtUtc.js +39 -33
  899. package/dist/utc/interval/intervalUnionUtc.d.ts +15 -7
  900. package/dist/utc/interval/intervalUnionUtc.js +31 -42
  901. package/dist/utc/interval/intervalXorAllUtc.d.ts +7 -4
  902. package/dist/utc/interval/intervalXorAllUtc.js +30 -52
  903. package/dist/utc/interval/intervalXorUtc.d.ts +8 -6
  904. package/dist/utc/interval/intervalXorUtc.js +22 -56
  905. package/dist/utc/interval/intervalsOverlapUtc.d.ts +15 -9
  906. package/dist/utc/interval/intervalsOverlapUtc.js +23 -39
  907. package/dist/utc/interval/mergeIntervalsUtc.d.ts +9 -2
  908. package/dist/utc/interval/mergeIntervalsUtc.js +23 -36
  909. package/dist/utc/interval/splitIntervalByUnitUtc.d.ts +23 -2
  910. package/dist/utc/interval/splitIntervalByUnitUtc.js +65 -44
  911. package/dist/utc/interval/validate/isValidUtcInterval.d.ts +5 -0
  912. package/dist/utc/interval/validate/isValidUtcInterval.js +7 -6
  913. package/dist/utc/parse/parseDateFromUtc.d.ts +7 -1
  914. package/dist/utc/parse/parseDateFromUtc.js +14 -9
  915. package/dist/utc/parse/parseDayFromUtc.d.ts +7 -1
  916. package/dist/utc/parse/parseDayFromUtc.js +14 -8
  917. package/dist/utc/parse/parseDayOfWeekFromUtc.d.ts +7 -1
  918. package/dist/utc/parse/parseDayOfWeekFromUtc.js +24 -6
  919. package/dist/utc/parse/parseHourFromUtc.d.ts +7 -1
  920. package/dist/utc/parse/parseHourFromUtc.js +14 -8
  921. package/dist/utc/parse/parseHttp.d.ts +39 -14
  922. package/dist/utc/parse/parseHttp.js +69 -28
  923. package/dist/utc/parse/parseMicrosecondFromUtc.d.ts +9 -2
  924. package/dist/utc/parse/parseMicrosecondFromUtc.js +18 -9
  925. package/dist/utc/parse/parseMillisecondFromUtc.d.ts +7 -1
  926. package/dist/utc/parse/parseMillisecondFromUtc.js +16 -8
  927. package/dist/utc/parse/parseMinuteFromUtc.d.ts +7 -1
  928. package/dist/utc/parse/parseMinuteFromUtc.js +14 -8
  929. package/dist/utc/parse/parseMonthFromUtc.d.ts +7 -1
  930. package/dist/utc/parse/parseMonthFromUtc.js +14 -8
  931. package/dist/utc/parse/parseNanosecondFromUtc.d.ts +9 -2
  932. package/dist/utc/parse/parseNanosecondFromUtc.js +18 -9
  933. package/dist/utc/parse/parseSecondFromUtc.d.ts +7 -1
  934. package/dist/utc/parse/parseSecondFromUtc.js +14 -8
  935. package/dist/utc/parse/parseTimeFromUtc.js +21 -10
  936. package/dist/utc/parse/parseUnitFromUtc.d.ts +39 -5
  937. package/dist/utc/parse/parseUnitFromUtc.js +58 -31
  938. package/dist/utc/parse/parseWeekFromUtc.d.ts +16 -3
  939. package/dist/utc/parse/parseWeekFromUtc.js +38 -10
  940. package/dist/utc/parse/parseYearFromUtc.d.ts +7 -1
  941. package/dist/utc/parse/parseYearFromUtc.js +14 -8
  942. package/dist/utc/validate/isValidUtc.d.ts +8 -1
  943. package/dist/utc/validate/isValidUtc.js +12 -3
  944. package/dist/utc/validate/isValidUtcRange.d.ts +3 -2
  945. package/dist/utc/validate/isValidUtcRange.js +30 -19
  946. package/dist/zoned/calculate/addZoned.d.ts +40 -27
  947. package/dist/zoned/calculate/addZoned.js +71 -56
  948. package/dist/zoned/calculate/addZonedBusinessDays.d.ts +2 -0
  949. package/dist/zoned/calculate/addZonedBusinessDays.js +5 -2
  950. package/dist/zoned/calculate/clampZoned.js +4 -3
  951. package/dist/zoned/calculate/closestZonedTo.d.ts +6 -6
  952. package/dist/zoned/calculate/closestZonedTo.js +35 -26
  953. package/dist/zoned/calculate/cycleZoned.d.ts +10 -7
  954. package/dist/zoned/calculate/cycleZoned.js +19 -11
  955. package/dist/zoned/calculate/diffZoned.d.ts +39 -17
  956. package/dist/zoned/calculate/diffZoned.js +90 -45
  957. package/dist/zoned/calculate/diffZonedAsDuration.d.ts +19 -9
  958. package/dist/zoned/calculate/diffZonedAsDuration.js +32 -15
  959. package/dist/zoned/calculate/endOfQuarterForZoned.d.ts +11 -11
  960. package/dist/zoned/calculate/endOfQuarterForZoned.js +32 -31
  961. package/dist/zoned/calculate/endOfZoned.d.ts +21 -14
  962. package/dist/zoned/calculate/endOfZoned.js +25 -111
  963. package/dist/zoned/calculate/getHoursInZonedDay.d.ts +8 -1
  964. package/dist/zoned/calculate/getHoursInZonedDay.js +10 -14
  965. package/dist/zoned/calculate/getLocaleZonedDayOfWeek.d.ts +8 -4
  966. package/dist/zoned/calculate/getLocaleZonedDayOfWeek.js +9 -6
  967. package/dist/zoned/calculate/getLocaleZonedEndOfWeek.d.ts +20 -26
  968. package/dist/zoned/calculate/getLocaleZonedEndOfWeek.js +40 -44
  969. package/dist/zoned/calculate/getLocaleZonedStartOfWeek.d.ts +16 -24
  970. package/dist/zoned/calculate/getLocaleZonedStartOfWeek.js +36 -42
  971. package/dist/zoned/calculate/getQuarterForZoned.js +2 -2
  972. package/dist/zoned/calculate/maxZoned.js +20 -12
  973. package/dist/zoned/calculate/minZoned.d.ts +1 -1
  974. package/dist/zoned/calculate/minZoned.js +21 -13
  975. package/dist/zoned/calculate/roundZoned.d.ts +18 -4
  976. package/dist/zoned/calculate/roundZoned.js +58 -33
  977. package/dist/zoned/calculate/setZoned.d.ts +9 -3
  978. package/dist/zoned/calculate/setZoned.js +39 -18
  979. package/dist/zoned/calculate/sortZoned.js +18 -10
  980. package/dist/zoned/calculate/startOfQuarterForZoned.d.ts +4 -7
  981. package/dist/zoned/calculate/startOfQuarterForZoned.js +23 -17
  982. package/dist/zoned/calculate/startOfZoned.d.ts +17 -13
  983. package/dist/zoned/calculate/startOfZoned.js +21 -103
  984. package/dist/zoned/calculate/subtractZoned.d.ts +36 -24
  985. package/dist/zoned/calculate/subtractZoned.js +66 -50
  986. package/dist/zoned/calculate/subtractZonedBusinessDays.d.ts +2 -0
  987. package/dist/zoned/calculate/subtractZonedBusinessDays.js +5 -2
  988. package/dist/zoned/chop/chopZonedDate.js +2 -2
  989. package/dist/zoned/chop/chopZonedDateTime.js +2 -2
  990. package/dist/zoned/chop/chopZonedMilliseconds.js +2 -2
  991. package/dist/zoned/chop/chopZonedSeconds.js +2 -2
  992. package/dist/zoned/chop/chopZonedTime.js +2 -2
  993. package/dist/zoned/chop/chopZonedTimezone.js +2 -2
  994. package/dist/zoned/compare/areZonedEqual.js +3 -2
  995. package/dist/zoned/compare/areZonedEqualBy.d.ts +15 -2
  996. package/dist/zoned/compare/areZonedEqualBy.js +45 -12
  997. package/dist/zoned/compare/isAfterZoned.js +3 -2
  998. package/dist/zoned/compare/isBeforeZoned.d.ts +1 -1
  999. package/dist/zoned/compare/isBeforeZoned.js +4 -3
  1000. package/dist/zoned/compare/isBetweenZoned.js +46 -29
  1001. package/dist/zoned/compare/isInDaylightSaving.js +4 -4
  1002. package/dist/zoned/compare/isZonedBusinessDay.d.ts +7 -4
  1003. package/dist/zoned/compare/isZonedBusinessDay.js +9 -6
  1004. package/dist/zoned/compare/isZonedFuture.js +2 -1
  1005. package/dist/zoned/compare/isZonedPast.js +2 -1
  1006. package/dist/zoned/compare/isZonedRelativeDay.js +2 -1
  1007. package/dist/zoned/compare/isZonedThisUnit.d.ts +12 -4
  1008. package/dist/zoned/compare/isZonedThisUnit.js +37 -16
  1009. package/dist/zoned/compare/isZonedWeekend.d.ts +8 -4
  1010. package/dist/zoned/compare/isZonedWeekend.js +9 -6
  1011. package/dist/zoned/convert/convertPlainDateTimeToZoned.d.ts +10 -5
  1012. package/dist/zoned/convert/convertPlainDateTimeToZoned.js +34 -16
  1013. package/dist/zoned/convert/convertZonedToCalendar.d.ts +26 -38
  1014. package/dist/zoned/convert/convertZonedToCalendar.js +33 -48
  1015. package/dist/zoned/convert/convertZonedToPlainDateTime.d.ts +6 -0
  1016. package/dist/zoned/convert/convertZonedToPlainDateTime.js +8 -2
  1017. package/dist/zoned/convert/convertZonedToUnix.d.ts +12 -6
  1018. package/dist/zoned/convert/convertZonedToUnix.js +23 -16
  1019. package/dist/zoned/convert/convertZonedToUtc.js +2 -2
  1020. package/dist/zoned/convert/convertZonedToZoned.js +2 -2
  1021. package/dist/zoned/format/formatCalendarZoned.d.ts +7 -3
  1022. package/dist/zoned/format/formatCalendarZoned.js +37 -39
  1023. package/dist/zoned/format/formatRelativeZoned.d.ts +16 -14
  1024. package/dist/zoned/format/formatRelativeZoned.js +54 -54
  1025. package/dist/zoned/format/formatRfc2822.d.ts +14 -1
  1026. package/dist/zoned/format/formatRfc2822.js +27 -13
  1027. package/dist/zoned/format/formatRfc3339.d.ts +14 -1
  1028. package/dist/zoned/format/formatRfc3339.js +29 -3
  1029. package/dist/zoned/format/formatTimeZoneName.d.ts +18 -2
  1030. package/dist/zoned/format/formatTimeZoneName.js +28 -18
  1031. package/dist/zoned/format/formatZonedDateTime.d.ts +26 -5
  1032. package/dist/zoned/format/formatZonedDateTime.js +40 -9
  1033. package/dist/zoned/format/formatZonedRange.d.ts +25 -6
  1034. package/dist/zoned/format/formatZonedRange.js +61 -28
  1035. package/dist/zoned/format/formatZonedToParts.d.ts +30 -6
  1036. package/dist/zoned/format/formatZonedToParts.js +42 -13
  1037. package/dist/zoned/get/getDstTransitions.d.ts +24 -3
  1038. package/dist/zoned/get/getDstTransitions.js +17 -15
  1039. package/dist/zoned/get/getTimeZoneOffset.d.ts +11 -1
  1040. package/dist/zoned/get/getTimeZoneOffset.js +19 -2
  1041. package/dist/zoned/get/getZonedNow.js +20 -9
  1042. package/dist/zoned/get/getZonedNowUnit.d.ts +30 -2
  1043. package/dist/zoned/get/getZonedNowUnit.js +15 -31
  1044. package/dist/zoned/index.js +1 -1
  1045. package/dist/zoned/interval/intervalAbutsZoned.d.ts +18 -11
  1046. package/dist/zoned/interval/intervalAbutsZoned.js +24 -25
  1047. package/dist/zoned/interval/intervalContainsZoned.d.ts +16 -7
  1048. package/dist/zoned/interval/intervalContainsZoned.js +22 -14
  1049. package/dist/zoned/interval/intervalCountZoned.d.ts +19 -11
  1050. package/dist/zoned/interval/intervalCountZoned.js +32 -27
  1051. package/dist/zoned/interval/intervalDifferenceZoned.d.ts +14 -8
  1052. package/dist/zoned/interval/intervalDifferenceZoned.js +25 -37
  1053. package/dist/zoned/interval/intervalDivideEquallyZoned.d.ts +18 -9
  1054. package/dist/zoned/interval/intervalDivideEquallyZoned.js +67 -46
  1055. package/dist/zoned/interval/intervalEngulfsZoned.d.ts +10 -5
  1056. package/dist/zoned/interval/intervalEngulfsZoned.js +16 -10
  1057. package/dist/zoned/interval/intervalFromDurationZoned.d.ts +25 -11
  1058. package/dist/zoned/interval/intervalFromDurationZoned.js +61 -44
  1059. package/dist/zoned/interval/intervalIntersectionZoned.d.ts +12 -6
  1060. package/dist/zoned/interval/intervalIntersectionZoned.js +24 -18
  1061. package/dist/zoned/interval/intervalLengthZoned.d.ts +15 -6
  1062. package/dist/zoned/interval/intervalLengthZoned.js +23 -9
  1063. package/dist/zoned/interval/intervalOverlappingDaysZoned.d.ts +28 -17
  1064. package/dist/zoned/interval/intervalOverlappingDaysZoned.js +46 -34
  1065. package/dist/zoned/interval/intervalSplitAtZoned.d.ts +6 -3
  1066. package/dist/zoned/interval/intervalSplitAtZoned.js +52 -42
  1067. package/dist/zoned/interval/intervalUnionZoned.d.ts +14 -8
  1068. package/dist/zoned/interval/intervalUnionZoned.js +25 -20
  1069. package/dist/zoned/interval/intervalXorAllZoned.d.ts +9 -7
  1070. package/dist/zoned/interval/intervalXorAllZoned.js +42 -73
  1071. package/dist/zoned/interval/intervalXorZoned.d.ts +20 -10
  1072. package/dist/zoned/interval/intervalXorZoned.js +34 -62
  1073. package/dist/zoned/interval/intervalsOverlapZoned.d.ts +15 -7
  1074. package/dist/zoned/interval/intervalsOverlapZoned.js +21 -12
  1075. package/dist/zoned/interval/mergeIntervalsZoned.d.ts +10 -3
  1076. package/dist/zoned/interval/mergeIntervalsZoned.js +44 -41
  1077. package/dist/zoned/interval/splitIntervalByUnitZoned.d.ts +28 -5
  1078. package/dist/zoned/interval/splitIntervalByUnitZoned.js +72 -39
  1079. package/dist/zoned/interval/validate/isValidCalendarZonedInterval.d.ts +13 -10
  1080. package/dist/zoned/interval/validate/isValidCalendarZonedInterval.js +13 -10
  1081. package/dist/zoned/interval/validate/isValidZonedInterval.d.ts +7 -5
  1082. package/dist/zoned/interval/validate/isValidZonedInterval.js +12 -16
  1083. package/dist/zoned/map/mapZonedDatesInRange.d.ts +17 -1
  1084. package/dist/zoned/map/mapZonedDatesInRange.js +62 -26
  1085. package/dist/zoned/map/mapZonedHoursInDay.d.ts +10 -13
  1086. package/dist/zoned/map/mapZonedHoursInDay.js +13 -21
  1087. package/dist/zoned/parse/getZonedOffset.js +2 -2
  1088. package/dist/zoned/parse/getZonedOffsetAs.d.ts +15 -2
  1089. package/dist/zoned/parse/getZonedOffsetAs.js +10 -5
  1090. package/dist/zoned/parse/parseDateFromZoned.js +2 -2
  1091. package/dist/zoned/parse/parseDateTimeFromZoned.js +2 -2
  1092. package/dist/zoned/parse/parseDayFromZoned.js +2 -2
  1093. package/dist/zoned/parse/parseDayOfWeekFromZoned.js +2 -2
  1094. package/dist/zoned/parse/parseHourFromZoned.js +2 -2
  1095. package/dist/zoned/parse/parseMicrosecondFromZoned.d.ts +2 -1
  1096. package/dist/zoned/parse/parseMicrosecondFromZoned.js +4 -3
  1097. package/dist/zoned/parse/parseMillisecondFromZoned.js +2 -2
  1098. package/dist/zoned/parse/parseMinuteFromZoned.js +2 -2
  1099. package/dist/zoned/parse/parseMonthFromZoned.js +2 -2
  1100. package/dist/zoned/parse/parseNanosecondFromZoned.d.ts +2 -1
  1101. package/dist/zoned/parse/parseNanosecondFromZoned.js +4 -3
  1102. package/dist/zoned/parse/parseRfc2822.d.ts +32 -12
  1103. package/dist/zoned/parse/parseRfc2822.js +47 -45
  1104. package/dist/zoned/parse/parseRfc3339.d.ts +6 -0
  1105. package/dist/zoned/parse/parseRfc3339.js +6 -0
  1106. package/dist/zoned/parse/parseSecondFromZoned.js +2 -2
  1107. package/dist/zoned/parse/parseTimeFromZoned.js +2 -2
  1108. package/dist/zoned/parse/parseTimeZoneFromZoned.js +2 -2
  1109. package/dist/zoned/parse/parseUnitFromZoned.d.ts +36 -3
  1110. package/dist/zoned/parse/parseUnitFromZoned.js +59 -33
  1111. package/dist/zoned/parse/parseWeekFromZoned.d.ts +13 -3
  1112. package/dist/zoned/parse/parseWeekFromZoned.js +34 -10
  1113. package/dist/zoned/parse/parseYearFromZoned.js +2 -2
  1114. package/dist/zoned/validate/hasDaylightSaving.js +3 -3
  1115. package/dist/zoned/validate/isValidCalendarZonedDateTime.d.ts +31 -16
  1116. package/dist/zoned/validate/isValidCalendarZonedDateTime.js +31 -16
  1117. package/dist/zoned/validate/isValidTimeZone.d.ts +15 -2
  1118. package/dist/zoned/validate/isValidTimeZone.js +17 -4
  1119. package/dist/zoned/validate/isValidZonedDateTime.d.ts +14 -7
  1120. package/dist/zoned/validate/isValidZonedDateTime.js +20 -12
  1121. package/dist/zoned/validate/isValidZonedRange.d.ts +9 -2
  1122. package/dist/zoned/validate/isValidZonedRange.js +44 -16
  1123. package/package.json +487 -28
  1124. package/skills/gmt-arithmetic/SKILL.md +264 -0
  1125. package/skills/gmt-basics/SKILL.md +127 -0
  1126. package/skills/gmt-integration/SKILL.md +119 -0
  1127. package/skills/gmt-timezone/SKILL.md +197 -0
  1128. package/dist/internal/advanceBusinessDays.d.ts +0 -2
  1129. package/dist/internal/advanceBusinessDays.js +0 -15
  1130. package/dist/internal/ethiopicFamilyCalendar.d.ts +0 -47
  1131. package/dist/internal/ethiopicFamilyCalendar.js +0 -136
  1132. package/dist/internal/getLocaleMinimalDaysInFirstWeek.d.ts +0 -20
  1133. package/dist/internal/getLocaleMinimalDaysInFirstWeek.js +0 -31
  1134. package/dist/internal/resolveUnixTimeZone.d.ts +0 -20
  1135. package/dist/internal/resolveUnixTimeZone.js +0 -30
  1136. package/dist/unix/calculate/startOrEndOfUnix.d.ts +0 -9
  1137. package/dist/unix/calculate/startOrEndOfUnix.js +0 -122
  1138. package/dist/unix/interval/resolveUnixIntervalPair.d.ts +0 -7
  1139. package/dist/unix/interval/resolveUnixIntervalPair.js +0 -36
  1140. package/dist/utc/calculate/startOrEndOfUtc.d.ts +0 -6
  1141. package/skills/_artifacts/domain_map.yaml +0 -547
  1142. package/skills/_artifacts/skill_spec.md +0 -67
  1143. package/skills/_artifacts/skill_tree.yaml +0 -278
  1144. package/skills/api-expansion-workflow/SKILL.md +0 -52
  1145. package/skills/app-integration/SKILL.md +0 -38
  1146. package/skills/calculate-dates/SKILL.md +0 -432
  1147. package/skills/compare-dates/SKILL.md +0 -412
  1148. package/skills/convert-types/SKILL.md +0 -305
  1149. package/skills/durations/SKILL.md +0 -418
  1150. package/skills/format-date-time/SKILL.md +0 -486
  1151. package/skills/format-date-time/references/format-api.md +0 -34
  1152. package/skills/format-relative-time/SKILL.md +0 -223
  1153. package/skills/get-current/SKILL.md +0 -229
  1154. package/skills/interval-ops/SKILL.md +0 -432
  1155. package/skills/issue-creation/SKILL.md +0 -118
  1156. package/skills/lint-package-suggestion/SKILL.md +0 -37
  1157. package/skills/new-method-implementation/SKILL.md +0 -71
  1158. package/skills/parse-date-time/SKILL.md +0 -349
  1159. package/skills/pr-contribution/SKILL.md +0 -180
  1160. package/skills/unit-test-generation/SKILL.md +0 -56
  1161. package/skills/validate-dates/SKILL.md +0 -438
  1162. package/skills/zoned-date-ops/SKILL.md +0 -422
  1163. package/skills/zoned-date-ops/references/timezones.md +0 -13
package/README.md CHANGED
@@ -11,7 +11,18 @@ 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
- **Status:** pre-alpha. Expect API movement while the surface is still being filled out.
14
+ Read the [docs](https://gmt-dox.northguild.workers.dev/), or ask us on [Discord](https://discord.gg/TdvQdP3t5a).
15
+
16
+ **Why GMT:**
17
+
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.
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.
20
+ - **~54× more CI test executions than all four competitors combined**: 1,090,440 from 36,348 tests run in all 10 timezones × 3 Node versions, vs. their combined 20,190.
21
+ - **~94× more test cases than `@internationalized/date`**: 36,348 vs. 386 — Adobe's own library, run at its own commit.
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.
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.
24
+ - **Explicit DST disambiguation control on both construction _and_ arithmetic** — a control none of the others expose.
25
+ - **The only actively-maintained one that's Temporal-native.** Moment.js is officially in maintenance mode; Luxon, date-fns, and `@internationalized/date` are still active but all still depend on `Date` internally.
15
26
 
16
27
  ## Install
17
28
 
@@ -27,8 +38,8 @@ pnpm add @northguild/gmt
27
38
 
28
39
  GMT enforces a strict input/output contract to keep behavior predictable and auditable:
29
40
 
30
- - **Explicit inputs only**: Public APIs accept clearly defined shapes — ISO 8601 date/time strings, IANA timezone identifiers, or numeric Unix epoch values (explicitly seconds or milliseconds). We do not attempt to parse arbitrary or ambiguous date formats.
31
- - **Predictable outputs**: Helpers return normalized values (ISO strings, numbers, booleans, or arrays). Invalid input yields typed fallbacks (`""`, `null`, or `false`) instead of throwing.
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.
32
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.
33
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.
34
45
 
@@ -50,7 +61,7 @@ Invalid input fallbacks are consistent across the library:
50
61
 
51
62
  ## Testing
52
63
 
53
- Every function is exercised across **17 locales** and a full IANA timezone matrix. The CI pipeline runs the complete suite in **20 environments** — 2 Node versions (22, 24) × 10 timezones spanning every UTC offset band from Pacific/Niue (−11:00) to Pacific/Apia (+13:00):
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):
54
65
 
55
66
  | Timezone | UTC Offset |
56
67
  | ------------------- | --------------- |
@@ -83,15 +94,15 @@ GMT's test suite balances **thoroughness** against **maintenance burden** by tes
83
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.
84
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.
85
96
 
86
- **Result:** 15,611 tests across 527 files that exercise real behavior differences without redundant permutations. The suite runs in CI as 312,220 executions (15,611 × 2 Node versions × 10 timezones).
97
+ **Result:** 36,348 tests across 663 files that exercise real behavior differences without redundant permutations. They run in CI as 1,090,440 executions — every one of them × 3 Node versions × 10 timezones.
87
98
 
88
99
  ## How GMT is tested, vs. the libraries it targets
89
100
 
90
- GMT's roadmap (see [context/roadmap](https://github.com/northguild/gmt/tree/main/context/roadmap)) is explicitly scoped against react-aria's **`@internationalized/date`**, **Luxon**, **date-fns**, and **Moment.js** — the same four libraries compared below. All numbers were verified **2026-08-22** against the exact package versions/commits below — nothing is estimated. Re-verify before citing these numbers elsewhere; library surfaces and CI configs move.
101
+ GMT is measured directly against react-aria's **`@internationalized/date`**, **Luxon**, **date-fns**, and **Moment.js** — the same four libraries compared below. All numbers were verified **2026-08-22** against the exact package versions/commits below — nothing is estimated. Re-verify before citing these numbers elsewhere; library surfaces and CI configs move.
91
102
 
92
103
  | Library | Version tested |
93
104
  | ------------------------- | --------------------------------------- |
94
- | GMT (`@northguild/gmt`) | 1.12.0 |
105
+ | GMT (`@northguild/gmt`) | 1.14.2 |
95
106
  | `@internationalized/date` | 3.12.3 (`adobe/react-spectrum@5d191ab`) |
96
107
  | Luxon | 3.7.2 (`moment/luxon@f427515`) |
97
108
  | date-fns | 4.4.0 (`date-fns/date-fns@a0a3922`) |
@@ -99,20 +110,20 @@ GMT's roadmap (see [context/roadmap](https://github.com/northguild/gmt/tree/main
99
110
 
100
111
  | Metric | GMT | `@internationalized/date` | Luxon | date-fns | Moment.js |
101
112
  | ------------------------------- | -------------------------------------------------- | ------------------------------ | ------------------------------------ | ----------------------------------------- | -------------------------------- |
102
- | Test files | 527 | 6 | 58 / 60<br>(2 didn't run<br>locally) | 256 | 191<br>(52 core +<br>139 locale) |
103
- | Individual test cases | **15,611** | 386 | 1,222 | 3,213 | 3,901 |
104
- | Effective CI test<br>executions | **312,220**<br>(15,611 × 2 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) |
105
- | CI Node.js matrix | 22, 24 | n/a — tests<br>React 16–canary | 20, 22, 24, 25 | not explicit<br>(`node = "latest"`) | LTS, LTS-1,<br>latest |
106
- | CI timezone matrix | **10 zones × 2**<br>**Node, full suite** | none found | none found | dedicated workflow,<br>zone scope unclear | 6 zones,<br>partial suite only |
113
+ | Test files | 663 | 6 | 58 / 60<br>(2 didn't run<br>locally) | 256 | 191<br>(52 core +<br>139 locale) |
114
+ | Individual test cases | **36,348** | 386 | 1,222 | 3,213 | 3,901 |
115
+ | Effective CI test<br>executions | **1,090,440**<br>(36,348 × 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 |
107
118
  | Locale test matrix | **17 locales**,<br>every locale fn | none found | none found | none found | none found |
108
119
  | Real-browser CI | not yet | yes (Playwright) | not found | yes (Playwright) | not found |
109
120
  | Maintenance | active | active | active | active | **maintenance<br>mode** |
110
121
 
111
122
  <sub>Methodology: "Test files" and the CI/maintenance rows come from each project's public CI configuration and repository file listing. "Individual test cases" for GMT, Luxon, date-fns, and Moment.js were obtained by actually cloning the repo at the commit above, installing dependencies, running the project's own test command (`vitest run` / `jest` / `node scripts/test.js`), and reading that runner's own final summary — not grepped from source. `@internationalized/date` was run by cloning `adobe/react-spectrum` at `5d191ab`, installing dependencies, and executing `npx jest packages/@internationalized/date/tests/`, yielding 386 passing tests. Luxon (39 failures) and date-fns (46 failures) had environment-dependent local failures that don't affect the total count: Luxon's suite assumes its CI container's local time zone is `America/New_York`; date-fns's experimental native-`Temporal` code path needs a global `Temporal` Node doesn't yet provide natively. Moment.js passed cleanly (0 failed) on Node 24. Sources: [GMT](https://github.com/northguild/gmt/blob/main/.github/workflows/ci.yml) · [`@internationalized/date`](https://github.com/adobe/react-spectrum/blob/main/.circleci/config.yml) · [Luxon](https://github.com/moment/luxon/blob/master/.github/workflows/test.yml) · [date-fns](https://github.com/date-fns/date-fns/tree/main/.github/workflows) · [Moment.js](https://github.com/moment/moment/tree/develop/.github/workflows).</sub>
112
123
 
113
- ### Functionality parity progress
124
+ ### Feature parity
114
125
 
115
- GMT's roadmap tracks parity against the same four libraries story-by-story, with each gap sourced against the specific competitor function it closes — see [context/roadmap](https://github.com/northguild/gmt/tree/main/context/roadmap) for the full, source-verified audit trail. This is a live snapshot, not a finished-parity claim: ✅ shipped, 🟡 in progress, ⏳ backlog and not yet scheduled.
126
+ GMT has **full functional parity** with all four comparison libraries, capability for capability — with several areas where GMT goes further than any of them.
116
127
 
117
128
  | Capability | Status | Also has it |
118
129
  | -------------------------------------------------------------------------------------------- | ---------------------------- | ------------------------------------------------------------------------ |
@@ -120,55 +131,59 @@ GMT's roadmap tracks parity against the same four libraries story-by-story, with
120
131
  | Interval/range math<br>(contains, overlap, union,<br>intersection, split, set ops) | ✅ Done | Luxon `Interval`,<br>date-fns `areIntervalsOverlapping` |
121
132
  | DST disambiguation control<br>on construction _and_ arithmetic | ✅ Done — **differentiator** | None of the others expose<br>this on arithmetic |
122
133
  | Locale-aware calendar helpers<br>(weekend, week start/end, day-of-week) | ✅ Done | `@internationalized/date` |
123
- | Business-day arithmetic,<br>clamp/closest, time rounding | ✅ Done | `temporal-kit` |
134
+ | Business-day arithmetic with<br>holiday calendars and roll conventions,<br>clamp/closest, time rounding | ✅ Done | `temporal-kit` (arithmetic only) |
124
135
  | Interval rounding-out<br>(boundary count, from-duration) | ✅ Done | Luxon |
125
136
  | Locale calendar metadata<br>(names, `hasDST`) | ✅ Done | Luxon `Info` |
126
137
  | Overlap-day count, relative<br>rounding, DST transitions, hours-in-day | ✅ Done | date-fns, `@internationalized/date` |
127
138
  | Field setters, token-pattern<br>parsing, named machine formats,<br>calendar-style formatting | ✅ Done | Luxon `.set()`,<br>`toRFC2822`/`toHTTP`/`toSQL`,<br>Moment `.calendar()` |
128
139
  | Non-Gregorian calendar systems<br>(conversion + calendar-aware<br>interval/duration math) | ✅ Done | `@internationalized/date`'s<br>`toCalendar` |
129
140
 
130
- <sub>Status reflects [context/roadmap/tracker.md](https://github.com/northguild/gmt/tree/main/context/roadmap/tracker.md) as of this writing.</sub>
131
-
132
141
  ### Where GMT stands alone
133
142
 
134
143
  Specific, sourced claims — not a repeat of the metrics above.
135
144
 
136
145
  | Claim | The others |
137
146
  | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
138
- | Only GMT runs its **entire** suite in CI<br>under a real `TZ` env var across 10<br>real-world zones × 2 Node versions<br>(20 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 |
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 |
139
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 |
140
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 |
141
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 |
142
- | GMT's effective CI test<br>executions exceed all four<br>competitors **combined**<br>by ~15× | 312,220 vs. 386 + 4,888 + 3,213<br>+ 11,703 = 20,190 |
151
+ | GMT's effective CI test<br>executions exceed all four<br>competitors **combined**<br>by ~54× | 1,090,440 vs. 386 + 4,888 + 3,213<br>+ 11,703 = 20,190 |
143
152
 
144
153
  ## Package Layout
145
154
 
146
- The package exports seven top-level namespaces:
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.
147
158
 
148
159
  ```typescript
149
- import {
150
- Temporal,
151
- duration,
152
- plain,
153
- zoned,
154
- unix,
155
- utc,
156
- regex,
157
- } from "@northguild/gmt";
160
+ import { addDate, getNow, formatRelativeZoned, Temporal } from "@northguild/gmt";
158
161
  ```
159
162
 
160
- - `Temporal`: re-exported from `@js-temporal/polyfill`
161
- - `duration`: ISO 8601 duration string parsing, validation, and arithmetic
162
- - `plain`: timezone-free helpers
163
- - `zoned`: timezone-aware helpers
164
- - `unix`: Unix epoch (seconds or milliseconds) helpers
165
- - `utc`: UTC instant helpers
166
- - `regex`: low-level regex building blocks
163
+ The twelve namespace subpaths:
167
164
 
168
- You can also import subpaths directly:
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/zoned`: timezone-aware helpers
173
+ - `@northguild/gmt/unix`: Unix epoch (seconds or milliseconds) helpers
174
+ - `@northguild/gmt/utc`: UTC instant helpers
175
+ - `@northguild/gmt/regex`: low-level regex building blocks
176
+ - `@northguild/gmt/types`: the shared option and unit types
177
+
178
+ Every namespace subpath except `types` also re-exports `Temporal`, `Intl` and `toTemporalInstant`, so `import { Temporal, addZoned } from "@northguild/gmt/zoned"` needs no second import.
179
+
180
+ Every namespace except `regex` and `types` also exposes its modules as subpaths,
181
+ `@northguild/gmt/<namespace>/<module>`:
169
182
 
170
183
  ```typescript
171
- import { addDate, getNow, formatRelativeZoned } from "@northguild/gmt";
184
+ import { getZonedNow } from "@northguild/gmt/zoned";
185
+ import { addDateTime, diffDate } from "@northguild/gmt/plain/calculate";
186
+ import type { BusinessCalendar } from "@northguild/gmt/types";
172
187
  ```
173
188
 
174
189
  ## Quick Start
@@ -185,8 +200,8 @@ import {
185
200
  isBeforeDateTime,
186
201
  } from "@northguild/gmt";
187
202
 
188
- addDate("2026-01-01", 90, "day");
189
- // "2026-03-32" is impossible, so Temporal normalizes correctly -> "2026-04-01"
203
+ addDate("2026-01-01", { days: 90 });
204
+ // "2026-04-01"
190
205
 
191
206
  addBusinessDays("2024-03-15", 1);
192
207
  // "2024-03-18" (skips weekend)
@@ -194,7 +209,7 @@ addBusinessDays("2024-03-15", 1);
194
209
  subtractBusinessDays("2024-03-18", 1);
195
210
  // "2024-03-15" (skips weekend)
196
211
 
197
- diffDateTime("2024-03-17T12:00:00", "2024-03-17T12:30:00", "minute");
212
+ diffDateTime("2024-03-17T12:00:00", "2024-03-17T12:30:00", "minutes");
198
213
  // 30
199
214
 
200
215
  areDatesEqual("2026-03-17", "2026-03-17T09:00:00");
@@ -204,7 +219,7 @@ isBeforeDateTime("2026-03-17T09:00:00", "2026-03-17T10:00:00");
204
219
  // true
205
220
  ```
206
221
 
207
- `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:
222
+ `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:
208
223
 
209
224
  ```typescript
210
225
  import { addDate, diffDate } from "@northguild/gmt";
@@ -212,7 +227,10 @@ import { addDate, diffDate } from "@northguild/gmt";
212
227
  addDate("2024-01-31", { months: 1 }, { overflow: "reject" });
213
228
  // "" — Feb 31 doesn't exist and overflow: "reject" refuses to clamp it
214
229
 
215
- diffDate("2023-01-01", "2023-01-10", "week", {
230
+ addDate("2024-02-29", { years: 1 });
231
+ // "2025-02-28" — "constrain" (the default) clamps to the last valid day, as Temporal does
232
+
233
+ diffDate("2023-01-01", "2023-01-10", "weeks", {
216
234
  smallestUnit: "week",
217
235
  roundingMode: "halfExpand",
218
236
  });
@@ -230,14 +248,20 @@ setDate("2024-01-31", { month: 2 });
230
248
  setZoned(
231
249
  "2024-11-03T01:45:00-05:00[America/New_York]",
232
250
  { minute: 0 },
233
- {
234
- disambiguation: "reject",
235
- },
251
+ { disambiguation: "reject" },
236
252
  );
237
- // "" — offset defaults to "ignore" so disambiguation actually fires on this fall-back overlap
253
+ // "2024-11-03T01:00:00-05:00[America/New_York]" — offset defaults to "prefer", as Temporal's
254
+ // ZonedDateTime#with does: the source's -05:00 is still valid, so it is kept
255
+
256
+ setZoned(
257
+ "2024-11-03T01:45:00-05:00[America/New_York]",
258
+ { minute: 0 },
259
+ { disambiguation: "reject", offset: "ignore" },
260
+ );
261
+ // "" — offset "ignore" re-resolves the repeated 01:00, and "reject" fires on the fall-back overlap
238
262
  ```
239
263
 
240
- `setZoned`/`setUnix`/`setUtc` also accept `disambiguation` and `offset` for DST gap/overlap control — see [DST Disambiguation](../../docs/dst-disambiguation.md).
264
+ `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).
241
265
 
242
266
  `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:
243
267
 
@@ -255,9 +279,9 @@ cycleZoned("2024-03-10T01:30:00-06:00[America/Chicago]", "hour", 1);
255
279
  // gap; disambiguation ("compatible" by default) resolves it the same way setZoned does
256
280
  ```
257
281
 
258
- `cycleZoned` also accepts `disambiguation` and `offset` (default `offset: "ignore"`) 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`.
282
+ `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`.
259
283
 
260
- `isWeekend`/`isZonedWeekend` check locale-specific weekend days (via `Intl.Locale`'s `weekInfo`) rather than assuming Saturday/Sunday:
284
+ `isWeekend`/`isZonedWeekend` check locale-specific weekend days (via `Intl.Locale#getWeekInfo()`, falling back to `weekInfo`) rather than assuming Saturday/Sunday:
261
285
 
262
286
  ```typescript
263
287
  import { isWeekend, isZonedWeekend } from "@northguild/gmt";
@@ -272,7 +296,7 @@ isZonedWeekend("2024-02-04T10:00:00+02:00[Asia/Jerusalem]", "he-IL");
272
296
  // false (Sunday isn't part of he-IL's weekend)
273
297
  ```
274
298
 
275
- `isBusinessDay` returns true for fixed ISO Monday–Friday business days (Mon=1 … Fri=5), locale-agnostic and with no holiday calendar. It's the complement to locale-aware `isWeekend` and matches the boundary that `addBusinessDays`/`subtractBusinessDays` use:
299
+ `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:
276
300
 
277
301
  ```typescript
278
302
  import { isBusinessDay } from "@northguild/gmt";
@@ -282,8 +306,81 @@ isBusinessDay("2024-02-05");
282
306
 
283
307
  isBusinessDay("2024-02-10");
284
308
  // false (Saturday)
309
+
310
+ isBusinessDay("2024-07-04", {
311
+ weekend: [6, 7],
312
+ holidays: ["2024-07-04"],
313
+ timeZone: "America/New_York",
314
+ });
315
+ // false (a holiday, though it's a Thursday)
316
+ ```
317
+
318
+ ### Business calendars and roll conventions
319
+
320
+ 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.
321
+
322
+ `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:
323
+
324
+ ```typescript
325
+ import {
326
+ businessDaysBetween,
327
+ nextBusinessDay,
328
+ previousBusinessDay,
329
+ rollDate,
330
+ mergeCalendars,
331
+ isValidBusinessCalendar,
332
+ } from "@northguild/gmt";
333
+
334
+ const nyse = {
335
+ weekend: [6, 7],
336
+ holidays: ["2024-05-31", "2024-07-04"],
337
+ timeZone: "America/New_York",
338
+ };
339
+
340
+ businessDaysBetween("2024-07-01", "2024-07-05", nyse);
341
+ // 3 — start exclusive, end inclusive, and 4 July is a holiday
342
+
343
+ nextBusinessDay("2024-07-03", nyse);
344
+ // "2024-07-05" — strictly after, skipping the holiday
345
+
346
+ previousBusinessDay("2024-07-05", nyse);
347
+ // "2024-07-03"
348
+ ```
349
+
350
+ `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:
351
+
352
+ ```typescript
353
+ rollDate("2024-05-31", "following", nyse);
354
+ // "2024-06-03" — forward past the weekend
355
+
356
+ rollDate("2024-05-31", "modifiedFollowing", nyse);
357
+ // "2024-05-30" — backward instead, because forward leaves May
358
+
359
+ rollDate("2024-03-15", "endOfMonth", nyse);
360
+ // "2024-03-29" — March's last working day; March ends on a Sunday
361
+
362
+ rollDate("2024-06-01", "none", nyse);
363
+ // "2024-06-01" — unadjusted, Saturday or not
285
364
  ```
286
365
 
366
+ `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:
367
+
368
+ ```typescript
369
+ const london = { weekend: [6, 7], holidays: ["2024-05-06"], timeZone: "Europe/London" };
370
+
371
+ const both = mergeCalendars([nyse, london]);
372
+ // { weekend: [6, 7], holidays: ["2024-05-06", "2024-05-31", "2024-07-04"], timeZone: "America/New_York" }
373
+ // timeZone is the first calendar's; the merged set spans localities that may disagree
374
+
375
+ isBusinessDay("2024-05-06", both);
376
+ // false — a UK holiday closes the merged calendar too
377
+
378
+ mergeCalendars([]);
379
+ // null
380
+ ```
381
+
382
+ `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.
383
+
287
384
  `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:
288
385
 
289
386
  ```typescript
@@ -368,8 +465,8 @@ getLocaleDayOfWeek("2024-02-25", "en-US");
368
465
  getLocaleDayOfWeek("2024-02-26", "fr-FR");
369
466
  // 0 (Monday = first day of fr-FR week)
370
467
 
371
- getLocaleDayOfWeek("2024-02-24", "he-IL");
372
- // 0 (Saturday = first day of he-IL week)
468
+ getLocaleDayOfWeek("2024-02-24", "ar-EG");
469
+ // 0 (Saturday = first day of ar-EG week)
373
470
 
374
471
  getLocaleZonedDayOfWeek("2024-02-25T12:00:00+00:00[UTC]", "en-US");
375
472
  // 0
@@ -440,153 +537,127 @@ Supported tokens include `yyyy`/`MM`/`dd`/`HH`/`mm`/`ss`/`SSS` for fixed-width f
440
537
 
441
538
  ### Calendar systems
442
539
 
443
- GMT's `CalendarSystem` type (`"gregorian" | "hebrew" | "islamic-civil" | "islamic-tabular" | "islamic-umalqura" | "japanese" | "buddhist" | "taiwan" | "persian" | "indian" | "ethiopic" | "ethiopic-amete-alem" | "coptic"`, extended by later stories) and `convertDateToCalendar` express a date in a non-Gregorian calendar system, built almost entirely on Temporal's native calendar support — no bundled leap-year tables or ported arithmetic, with one deliberate exception (the Ethiopic family, covered below).
540
+ `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.
444
541
 
445
542
  ```typescript
446
543
  import { convertDateToCalendar } from "@northguild/gmt";
447
544
 
448
545
  convertDateToCalendar("2024-10-03", "hebrew");
449
- // "5785-01-01[u-ca=hebrew]" — Rosh Hashanah 5785
546
+ // "2024-10-03[u-ca=hebrew]" — Rosh Hashanah 5785, written as its ISO date
450
547
 
451
- convertDateToCalendar("5785-01-01[u-ca=hebrew]", "gregorian");
452
- // "2024-10-03" — round-trips back
548
+ convertDateToCalendar("2024-10-03[u-ca=hebrew]", "iso8601");
549
+ // "2024-10-03" — iso8601 writes no annotation
453
550
 
454
- convertDateToCalendar("2024-10-03", "islamic-umalqura");
455
- // "1446-03-30[u-ca=islamic-umalqura]" — Saudi Umm al-Qura calendar
551
+ convertDateToCalendar("2024-10-03", "gregory");
552
+ // "2024-10-03[u-ca=gregory]" — gregory is its own calendar, so it is annotated
456
553
 
457
554
  convertDateToCalendar("invalid", "hebrew");
458
555
  // ""
459
556
  ```
460
557
 
461
- The output string shape is the key design decision here, and it deliberately **diverges from Temporal's own** `[u-ca=...]` convention. Temporal's `Temporal.PlainDate.prototype.toString()` always keeps the ISO/proleptic-Gregorian year-month-day digits and only tags the calendar (`"2024-10-03[u-ca=hebrew]"` — still literally October 3rd's Gregorian digits). That hides the calendar's own fields behind calendar-aware accessors, which GMT's string-only contract has no place for. GMT's annotated string instead carries the **calendar-native** year/month/day — Hebrew year 5785, not 2024 — so the calendar-system concept is visible directly in the string, not just in an object property. A plain, unannotated ISO string is always treated as (and always produced for) the `"gregorian"` calendar, so every existing GMT function keeps working unchanged.
558
+ `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.
462
559
 
463
- 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
+ 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`.
464
561
 
465
- Three Islamic (Hijri) calendar variants are supported, and they are **not interchangeable** — each resolves the same Gregorian date to different calendar-native digits:
562
+ **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:
466
563
 
467
- - `"islamic-civil"` — a fixed 30-year leap-year cycle, Friday epoch (`1 AH = 622-07-19`).
468
- - `"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]`.
469
- - `"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.
470
-
471
- Five era-based solar calendars round out the set. Unlike Hebrew and the Islamic variants, none of these needed new leap-year logic — each is either Gregorian-shaped with a different year numbering layered on top, or a distinct-but-simple solar calendar, so they were materially less work than Hebrew or Islamic:
564
+ ```typescript
565
+ import { Temporal } from "@northguild/gmt";
472
566
 
473
- - `"buddhist"` — Gregorian day/month structure, a fixed `+543` year offset (`convertDateToCalendar("2024-10-03", "buddhist")` → `"2567-10-03[u-ca=buddhist]"`). One continuous era, no reset.
474
- - `"taiwan"` — Gregorian day/month structure, year numbering reset at 1912 (the Republic of China's founding): `2024` → `"0113-10-03[u-ca=taiwan]"` (`2024 - 1911`). Dates before 1912 count backward through Temporal's inverse era instead of going negative in the way a plain offset would — `convertDateToCalendar("1911-12-31", "taiwan")` is `"0000-12-31[u-ca=taiwan]"`, not `"-0001-12-31"`.
475
- - `"persian"` — a genuinely distinct solar calendar (not Gregorian-derived): own month lengths (6 months of 31 days, 5 of 30, a 29/30-day 12th month) and its own 33-year leap-year cycle (a year is leap when `(25 × year + 11) mod 33 < 8`), verified against `@internationalized/date`'s `PersianCalendar.ts` rather than assumed from the offset-only calendars above.
476
- - `"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.
477
- - `"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.
567
+ Temporal.PlainDate.from("2024-10-03[u-ca=hebrew]").year; // 5785
568
+ Temporal.PlainDate.from("2024-10-03[u-ca=japanese]").eraYear; // 6
569
+ ```
478
570
 
479
- **Why `"japanese"` gets a different string shape.** Every other supported calendar's plain `year` field is exactly what belongs in GMT's annotated string — Hebrew year 5785, Taiwan year 113, and so on are each already the single number that identifies the year. Japanese is the exception: Temporal's `.year` for the `"japanese"` calendar stays **proleptic** across era changes (it doesn't reset — `1912-07-30`, the first day of Taishō, still reports `.year === 1912`, not `1`), because Temporal's `.year` is designed to be a stable sort key, not a display value. Using it directly would silently contradict the calendar's entire reason for existing. So `convertDateToCalendar` uses Temporal's `.eraYear` (the field that _does_ reset) paired with the era name, tagged onto the annotation as `;era=<name>`:
571
+ 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:
480
572
 
481
573
  ```typescript
482
- convertDateToCalendar("2024-10-03", "japanese");
483
- // "0006-10-03[u-ca=japanese;era=reiwa]" — year 6 of the Reiwa era, not the proleptic 2024
574
+ import { addDate } from "@northguild/gmt";
484
575
 
485
- convertDateToCalendar("1912-07-30", "japanese");
486
- // "0001-07-30[u-ca=japanese;era=taisho]" — the first day of Taishō reads year 1
576
+ addDate("2020-02-24[u-ca=islamic-umalqura]", { months: 1 });
577
+ // "2020-03-24[u-ca=islamic-umalqura]"
487
578
 
488
- convertDateToCalendar("0001-07-30[u-ca=japanese;era=taisho]", "gregorian");
489
- // "1912-07-30" — round-trips back through the era + eraYear pair
579
+ addDate("2020-02-24[u-ca=islamic-tbla]", { months: 1 });
580
+ // "2020-03-25[u-ca=islamic-tbla]"
490
581
  ```
491
582
 
492
- `@internationalized/date` documents pre-Meiji (before 1868-10-23) dates as unsupported for its Japanese calendar. GMT does not replicate that restriction: since `convertDateToCalendar` is built entirely on Temporal's own calendar support rather than a ported implementation, and Temporal resolves those dates correctly under a synthetic `"japanese"` era (with an ISO-aligned `eraYear`), rejecting them would mean writing new validation solely to reproduce another library's gap rather than an actual GMT limitation. `convertDateToCalendar("1800-01-01", "japanese")` returns `"1800-01-01[u-ca=japanese;era=japanese]"` rather than `""`.
583
+ `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.
493
584
 
494
- Three Ethiopic-family calendars round out the set, all sharing one 13-month structure (12 months of 30 days, plus a short Pagume/Nasie 13th month of 5 days, or 6 in a leap year) but differing in epoch:
585
+ #### Calendar-aware interval and duration arithmetic
495
586
 
496
- - `"ethiopic"` — the modern Ethiopian calendar, era-based like `"japanese"`: it resets to the Amete Mihret ("Year of Mercy"/Incarnation) era at its own epoch (~AD 8), so `convertDateToCalendar("2024-10-03", "ethiopic")` returns `"2017-01-23[u-ca=ethiopic;era=ethiopic]"` — year 2017, not a 5-digit proleptic number. Dates before that epoch resolve under the Amete Alem ("Year of the World") era instead: `convertDateToCalendar("0001-01-01", "ethiopic")` → `"5493-05-08[u-ca=ethiopic;era=ethioaa]"`.
497
- - `"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]"`.
498
- - `"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]"`.
587
+ 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:
499
588
 
500
- **Why this family isn't built on Temporal's native `"ethiopic"`/`"coptic"` calendar ids, unlike every other calendar above.** `@js-temporal/polyfill`'s implementation of these two calendars resolves year/era by formatting the date through `Intl.DateTimeFormat` and matching the result against a hardcoded era-name table — unlike calendars with a fixed year-offset from ISO (Buddhist, Taiwan, and Ethiopic Amete Alem, which the polyfill computes with pure arithmetic and never touches `Intl` for). CLDR's era-name output for these two calendars changed between ICU versions: under ICU ≥ 78 (the version Node 22 and 24 both bundle — this is an ICU-version boundary, not a Node-major one), every read _or_ write of Temporal's `"ethiopic"`/`"coptic"` calendar ids throws a `RangeError` (`Era am (ISO year …) was not matched by any era`) — confirmed directly, not a hypothetical. `"ethiopic-amete-alem"` (Temporal's `"ethioaa"` id) is unaffected, since it has no era at all and is resolved with pure arithmetic. GMT routes around the bug rather than inheriting it: month/day are identical across all three calendars (they share one annual cycle), so `convertDateToCalendar` reads/writes them via the safe `"ethioaa"` calendar and computes each calendar's own displayed year (+ era, for `"ethiopic"`) with GMT-owned arithmetic — ported from the same `EthiopicHelper`/`CopticHelper` epoch constants `@js-temporal/polyfill` itself uses internally, just evaluated in GMT's code instead of through the ICU-dependent path. See `internal/ethiopicFamilyCalendar.ts` for the implementation.
589
+ ```typescript
590
+ import { addDate, diffDate, diffDateAsDuration, durationAs, intervalCountDate } from "@northguild/gmt";
501
591
 
502
- #### Calendar-aware interval and duration arithmetic
592
+ addDate("2024-02-24[u-ca=hebrew]", { months: 1 });
593
+ // "2024-03-25[u-ca=hebrew]" — 15 Adar I 5784 to 15 Adar II; addDate("2024-02-24", { months: 1 }) is "2024-03-24"
503
594
 
504
- `convertDateToCalendar`'s output feeds directly back into `addDate`/`subtractDate`/`diffDate`/`diffDateAsDuration` and every `Date`-suffixed `plain/interval/*` function (`intervalContainsDate`, `intervalCountDate`, `splitIntervalByUnitDate`, and the rest) — calendar-unit arithmetic ("add 1 month") resolves in the value's own calendar rather than being rejected or silently treated as Gregorian:
595
+ intervalCountDate("2023-09-16[u-ca=hebrew]", "2024-10-03[u-ca=hebrew]", "month");
596
+ // 13 — Hebrew leap year 5784 has 13 months; the same ISO span touches 14 ISO months
505
597
 
506
- ```typescript
507
- addDate("5784-06-15[u-ca=hebrew]", { months: 1 });
508
- // "5784-07-15[u-ca=hebrew]" — Adar I (a leap-only 30-day month) -> Adar
598
+ diffDate("2024-03-11[u-ca=hebrew]", "2024-04-10[u-ca=hebrew]", "months");
599
+ // 1 — 1 Adar II to 2 Nisan; the bare ISO dates are 0 whole months apart
509
600
 
510
- intervalCountDate(
511
- "5784-01-01[u-ca=hebrew]",
512
- "5785-01-01[u-ca=hebrew]",
513
- "month",
514
- );
515
- // 13 — a Hebrew leap year crosses 13 month boundaries, not 12 (ISO's answer for the same span)
601
+ diffDateAsDuration("2024-08-31[u-ca=buddhist]", "2024-09-30[u-ca=buddhist]", "months");
602
+ // "P30D" — not "P1M": a month counts only once the end reaches the same day of the next month
516
603
 
517
- diffDate("5784-06-15[u-ca=hebrew]", "5784-07-15[u-ca=hebrew]", "months");
518
- // 1 — measured in the shared calendar when both endpoints carry the same tag
604
+ durationAs("P1Y", "days", { relativeTo: "2023-09-16[u-ca=hebrew]" });
605
+ // 383 — Hebrew leap year 5784; relativeTo "2023-09-16" gives 366
519
606
  ```
520
607
 
521
- `utc/` and `unix/` reject a `[u-ca=...]` calendar annotation outright, and `duration/`'s `relativeTo` option accepts GMT's calendar-annotated string (not Temporal's own differently-shaped `[u-ca=...]` convention) when a calendar-aware anchor is needed:
608
+ 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.
522
609
 
523
- ```typescript
524
- durationAs("P1Y", "days", { relativeTo: "5784-06-15[u-ca=hebrew]" });
525
- // 385 — a Hebrew leap year, not the 366 a Gregorian P1Y would total
526
- ```
610
+ `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.
611
+
612
+ Two values that name different calendars follow Temporal's `CalendarEquals`. A bare ISO string names `iso8601`.
613
+
614
+ - **Differences return the sentinel** on a mismatch, as Temporal's `until` throws: `diffDate`, `diffDateAsDuration`, `intervalCountDate`, `intervalLengthDate`, `splitIntervalByUnitDate` and `intervalOverlappingDaysDate`.
615
+ - **Ordering accepts mixed calendars**, as `Temporal.PlainDate.compare` has no calendar check: `intervalContainsDate`, `intervalsOverlapDate`, `intervalAbutsDate`, `intervalEngulfsDate` and `isValidDateInterval`.
616
+ - **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.
527
617
 
528
- `zoned/` has its own calendar-annotated grammar as of E7 (issue #152) — see "Calendar-aware zoned datetimes" below.
618
+ ```typescript
619
+ intervalCountDate("2023-09-16[u-ca=hebrew]", "2024-10-03", "month");
620
+ // null — hebrew and iso8601
529
621
 
530
- Interval functions that only compare or diff absolute instants (`intervalContainsDate`, `intervalsOverlapDate`, `intervalAbutsDate`, `intervalEngulfsDate`, `isValidDateInterval`, `intervalOverlappingDaysDate`) accept endpoints tagged with _different_ calendars, since ordering and day-counting don't depend on which calendar a date is expressed in. Functions that return a date _value_ (`intervalUnionDate`, `intervalIntersectionDate`, `intervalDifferenceDate`, `intervalXorDate`, `intervalXorAllDate`, `mergeIntervalsDate`, `intervalDivideEquallyDate`, `intervalSplitAtDate`) require every argument to share one calendar and return their sentinel (`null`/`[]`) on a mismatch, since there's no principled way to pick which calendar the output should be expressed in. See `context/roadmap/issues/E.md`'s "E5 outcome" section for the full per-function audit, including the negatives ("no change needed, verified why") this scope boundary implies — `*DateTime`/`*Time` variants, `unix/`, and `utc/` were all confirmed unaffected rather than assumed to be.
622
+ intervalContainsDate("2023-09-16[u-ca=hebrew]", "2024-10-03", "2024-01-01[u-ca=roc]");
623
+ // true — ordering compares the ISO dates
624
+ ```
531
625
 
532
626
  #### Calendar-aware zoned datetimes
533
627
 
534
- A calendar-annotated `ZonedDateTime` string adds a time, a UTC offset and an IANA zone to the
535
- plain grammar above:
628
+ 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):
536
629
 
537
630
  ```
538
- <calendar-native-date>T<time><offset>[u-ca=<id>[;era=<era>]][<timeZone>]
631
+ <date>T<time><offset>[<timeZone>][u-ca=<id>]
539
632
 
540
- 5784-06-15T14:30:00-05:00[u-ca=hebrew][America/New_York]
541
- 0031-04-30T12:00:00+09:00[u-ca=japanese;era=heisei][Asia/Tokyo]
542
- 7517-12-30T00:30:00-04:00[u-ca=ethiopic-amete-alem][America/Santiago]
633
+ 2024-02-24T14:30:00-05:00[America/New_York][u-ca=hebrew]
634
+ 2019-04-30T12:00:00+09:00[Asia/Tokyo][u-ca=japanese]
543
635
  ```
544
636
 
545
- `convertZonedToCalendar` produces it, and `isValidCalendarZonedDateTime` validates it:
637
+ `convertZonedToCalendar` produces it, and `isValidCalendarZonedDateTime` validates it. A calendar annotation before the zone is not RFC 9557 and returns the sentinel:
546
638
 
547
639
  ```typescript
640
+ import { addZoned, convertZonedToCalendar } from "@northguild/gmt";
641
+
548
642
  convertZonedToCalendar("2024-10-03T14:30:45-04:00[America/New_York]", "hebrew");
549
- // "5785-01-01T14:30:45-04:00[u-ca=hebrew][America/New_York]"
643
+ // "2024-10-03T14:30:45-04:00[America/New_York][u-ca=hebrew]"
550
644
 
551
- addZoned("5784-06-15T14:30:00-05:00[u-ca=hebrew][America/New_York]", {
552
- months: 1,
553
- });
554
- // "5784-07-15T14:30:00-04:00[u-ca=hebrew][America/New_York]"
555
- // Adar I -> Adar AND EST -> EDT, resolved in one call. No ordering of a plain/ calendar
645
+ convertZonedToCalendar("2024-10-03T14:30:45-04:00[u-ca=hebrew][America/New_York]", "iso8601");
646
+ // "" — the calendar annotation must follow the zone
647
+
648
+ addZoned("2024-02-24T14:30:00-05:00[America/New_York][u-ca=hebrew]", { months: 1 });
649
+ // "2024-03-25T14:30:00-04:00[America/New_York][u-ca=hebrew]"
650
+ // Adar I -> Adar II AND EST -> EDT, resolved in one call. No ordering of a plain/ calendar
556
651
  // operation and a zoned/ conversion produces this: do the calendar step first and DST is
557
652
  // applied to an already-resolved wall time; do the zoned step first and there is no calendar
558
653
  // left to step in.
654
+ ```
559
655
 
560
- addZoned("0031-04-30T12:00:00+09:00[u-ca=japanese;era=heisei][Asia/Tokyo]", {
561
- days: 1,
562
- });
563
- // "0001-05-01T12:00:00+09:00[u-ca=japanese;era=reiwa][Asia/Tokyo]" — era re-derived, never copied
564
- ```
565
-
566
- > **The `[u-ca=...]` segment comes BEFORE `[timeZone]` — the reverse of RFC 9557.** This is
567
- > deliberate. GMT's digits are calendar-native (Hebrew year 5784, not ISO year 5784), so the
568
- > string is never valid RFC 9557 to begin with, and the `;era=` suffix is not valid RFC 9557 at
569
- > any ordering. Writing it in RFC order is actively dangerous:
570
- > `Temporal.ZonedDateTime.from("5784-01-01T14:30:00-05:00[America/New_York][u-ca=hebrew]")`
571
- > _succeeds_, silently reading 5784 as an ISO year — a ~3760-year misparse with no error. GMT's
572
- > ordering makes that shape uniformly rejected instead.
573
-
574
- Scope: `addZoned`, `subtractZoned`, `diffZoned`, `diffZonedAsDuration`, `convertZonedToCalendar`,
575
- and the `zoned/interval/*` family. Everything else in `zoned/` still rejects the annotation —
576
- `isValidZonedDateTime` is unchanged, so a function that has not opted in fails closed rather than
577
- silently answering in the wrong calendar. `addZonedBusinessDays`/`subtractZonedBusinessDays` stay
578
- out by design: day-of-week is ISO-fixed in every supported calendar, so a tag would change nothing
579
- while implying it might.
580
-
581
- Mixed-calendar endpoints follow the same split as `plain/`: ordering functions
582
- (`intervalContainsZoned`, `intervalsOverlapZoned`, `intervalAbutsZoned`, `intervalEngulfsZoned`,
583
- `intervalOverlappingDaysZoned`, `isValidCalendarZonedInterval`) accept them; the eight
584
- value-returning set operations require one shared calendar and return their sentinel on a
585
- mismatch. Measurement functions (`diffZoned`, `diffZonedAsDuration`, `intervalCountZoned`,
586
- `intervalLengthZoned`, `splitIntervalByUnitZoned`) measure in the endpoints' shared calendar when
587
- both tags match and fall back to Gregorian otherwise — mandatory here rather than merely
588
- convenient, because `Temporal.ZonedDateTime.prototype.until` throws across mismatched calendars
589
- for _every_ unit, including pure time units like hours.
656
+ 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.
657
+
658
+ 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.
659
+
660
+ **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`.
590
661
 
591
662
  ### Durations
592
663
 
@@ -677,6 +748,8 @@ diffDateAsDuration("2024-03-10", "2024-04-05", "days");
677
748
 
678
749
  ### Intervals
679
750
 
751
+ > **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]`.
752
+
680
753
  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):
681
754
 
682
755
  ```typescript
@@ -727,8 +800,8 @@ isValidZonedRange({
727
800
 
728
801
  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:
729
802
 
730
- - 3-arg: `intervalContains(start, end, point)` — true when `start <= point <= end`
731
- - 4-arg: `intervalContains(start, end, innerStart, innerEnd)` — true when the inner interval is fully contained
803
+ - 3-arg: `intervalContains(start, end, point)` — true when `start <= point < end`
804
+ - 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
732
805
 
733
806
  ```typescript
734
807
  import {
@@ -744,6 +817,9 @@ import {
744
817
  intervalContainsDate("2024-01-01", "2024-12-31", "2024-06-15");
745
818
  // true
746
819
 
820
+ intervalContainsDate("2024-01-01", "2024-12-31", "2024-12-31");
821
+ // false (the end is excluded)
822
+
747
823
  intervalContainsTime("09:00:00", "17:00:00", "12:00:00");
748
824
  // true
749
825
 
@@ -772,9 +848,9 @@ intervalContainsTime("09:00:00", "17:00:00", "10:00:00", "16:00:00");
772
848
  // true
773
849
  ```
774
850
 
775
- All interval containment checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, non-finite values for Unix).
851
+ 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).
776
852
 
777
- `intervalsOverlap*` checks whether two intervals share any time. Returns `true` when intervals overlap, `false` when they are disjoint or only adjacent (touching at a single instant with no shared time):
853
+ `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/`:
778
854
 
779
855
  ```typescript
780
856
  import {
@@ -790,23 +866,23 @@ intervalsOverlapDate("2024-01-01", "2024-06-30", "2024-04-01", "2024-12-31");
790
866
  // true
791
867
 
792
868
  intervalsOverlapDate("2024-01-01", "2024-06-30", "2024-06-30", "2024-12-31");
793
- // false (adjacent, no shared time)
869
+ // false (touching — 2024-06-30 is outside the first interval)
794
870
 
795
871
  intervalsOverlapUnix(0, 1700000000, 1000000, 2000000);
796
872
  // true
797
873
 
798
874
  intervalsOverlapUtc(
799
- "2024-01-01T00:00:00Z",
800
- "2024-06-30T23:59:59Z",
801
- "2024-04-01T00:00:00Z",
802
- "2024-12-31T23:59:59Z",
875
+ "2024-01-01T09:00:00Z",
876
+ "2024-01-01T17:00:00Z",
877
+ "2024-01-01T17:00:00Z",
878
+ "2024-01-01T18:00:00Z",
803
879
  );
804
- // true
880
+ // false (touching at 17:00)
805
881
  ```
806
882
 
807
- All overlap checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, non-finite values for Unix).
883
+ 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).
808
884
 
809
- `intervalIntersection*` returns the overlapping span of two intervals, or `null` when they do not overlap. Adjacent intervals (sharing one instant) count as overlapping and return a single-point span:
885
+ `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`:
810
886
 
811
887
  ```typescript
812
888
  import {
@@ -820,43 +896,35 @@ import {
820
896
 
821
897
  intervalIntersectionDate(
822
898
  "2024-01-01",
823
- "2024-06-30",
899
+ "2024-07-01",
824
900
  "2024-04-01",
825
- "2024-12-31",
901
+ "2025-01-01",
826
902
  );
827
- // { start: "2024-04-01", end: "2024-06-30" }
903
+ // { start: "2024-04-01", end: "2024-07-01" }
828
904
 
829
905
  intervalIntersectionDate(
830
906
  "2024-01-01",
831
- "2024-06-30",
832
- "2024-06-30",
833
- "2024-12-31",
834
- );
835
- // { start: "2024-06-30", end: "2024-06-30" } (adjacent, shares one instant)
836
-
837
- intervalIntersectionDate(
838
- "2024-01-01",
839
- "2024-06-30",
840
907
  "2024-07-01",
841
- "2024-12-31",
908
+ "2024-07-01",
909
+ "2025-01-01",
842
910
  );
843
- // null (disjoint)
911
+ // null (touching, no shared day)
844
912
 
845
913
  intervalIntersectionUnix(0, 1700000000, 1000000, 2000000);
846
- // { start: 1000000, end: 1700000000 }
914
+ // { start: 1000000, end: 2000000 } (B lies inside A)
847
915
 
848
916
  intervalIntersectionUtc(
849
917
  "2024-01-01T00:00:00Z",
850
- "2024-06-30T23:59:59Z",
918
+ "2024-07-01T00:00:00Z",
851
919
  "2024-04-01T00:00:00Z",
852
- "2024-12-31T23:59:59Z",
920
+ "2025-01-01T00:00:00Z",
853
921
  );
854
- // { start: "2024-04-01T00:00:00Z", end: "2024-06-30T23:59:59Z" }
922
+ // { start: "2024-04-01T00:00:00Z", end: "2024-07-01T00:00:00Z" }
855
923
  ```
856
924
 
857
- All intersection functions return `null` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, non-finite values for Unix).
925
+ 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).
858
926
 
859
- `intervalOverlappingDays*` returns how many distinct calendar dates two intervals share — the numeric counterpart to `intervalIntersection*`'s span. Counting is inclusive of both endpoints of the closed intersection `[max(aStart, bStart), min(aEnd, bEnd)]`, so `intervalOverlappingDaysDate("2024-01-01", "2024-01-01", "2024-01-01", "2024-01-01")` is `1`, not `0`. There is no `Time` variant — `PlainTime` has no calendar, so a day count is undefined for it:
927
+ `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:
860
928
 
861
929
  ```typescript
862
930
  import {
@@ -869,44 +937,42 @@ import {
869
937
 
870
938
  intervalOverlappingDaysDate(
871
939
  "2024-01-01",
872
- "2024-06-30",
940
+ "2024-07-01",
873
941
  "2024-04-01",
874
- "2024-12-31",
942
+ "2025-01-01",
875
943
  );
876
- // 91
944
+ // 91 (2024-04-01 through 2024-06-30)
877
945
 
878
946
  intervalOverlappingDaysDate(
879
947
  "2024-01-01",
880
- "2024-06-30",
881
- "2024-06-30",
882
- "2024-12-31",
948
+ "2024-07-01",
949
+ "2024-07-01",
950
+ "2025-01-01",
883
951
  );
884
- // 1 (adjacent, shares one date)
952
+ // 0 (touching, no shared date)
885
953
 
886
- intervalOverlappingDaysDate(
887
- "2024-01-01",
888
- "2024-06-30",
889
- "2024-07-01",
890
- "2024-12-31",
954
+ intervalOverlappingDaysUtc(
955
+ "2024-01-17T12:00:00Z",
956
+ "2024-01-19T00:00:00Z",
957
+ "2024-01-10T00:00:00Z",
958
+ "2024-01-18T06:00:00Z",
891
959
  );
892
- // 0 (disjoint)
960
+ // 2 (18 hours of overlap touch 17 and 18 January)
893
961
 
894
- intervalOverlappingDaysUnix(0, 172800000, 86400000, 259200000, {
895
- timeZone: "UTC",
896
- });
897
- // 2
962
+ intervalOverlappingDaysUnix(0, 172800000, 86400000, 259200000);
963
+ // 1 (the intersection [86400000, 172800000) is 1970-01-02 in UTC)
898
964
  ```
899
965
 
900
- 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 the system time zone, overridable via `{ timeZone }`) — 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.
966
+ 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.
901
967
 
902
- This deliberately diverges from date-fns's `getOverlappingDaysInIntervals`, which rounds up elapsed 24-hour periods instead of counting calendar dates — its own doc example (`Jan 10–20` vs `Jan 17–21`) returns `3` there and `4` here. To reproduce date-fns's number, compose `intervalIntersection*` with `intervalCount*`:
968
+ 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*`:
903
969
 
904
970
  ```typescript
905
- const span = intervalIntersectionDate(aStart, aEnd, bStart, bEnd);
906
- span ? intervalCountDate(span.start, span.end, "day") : 0; // date-fns semantics
971
+ const span = intervalIntersectionUtc(aStart, aEnd, bStart, bEnd);
972
+ span ? intervalLengthUtc(span.start, span.end, "day") : 0; // 0.75 for the overlap above
907
973
  ```
908
974
 
909
- `intervalUnion*` returns the combined span of two overlapping or adjacent intervals, or `null` when they are disjoint with a gap. Adjacent intervals (sharing one instant) count as mergeable:
975
+ `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:
910
976
 
911
977
  ```typescript
912
978
  import {
@@ -918,28 +984,28 @@ import {
918
984
  intervalUnionZoned,
919
985
  } from "@northguild/gmt";
920
986
 
921
- intervalUnionDate("2024-01-01", "2024-06-30", "2024-04-01", "2024-12-31");
922
- // { start: "2024-01-01", end: "2024-12-31" }
987
+ intervalUnionDate("2024-01-01", "2024-07-01", "2024-04-01", "2025-01-01");
988
+ // { start: "2024-01-01", end: "2025-01-01" }
923
989
 
924
- intervalUnionDate("2024-01-01", "2024-06-30", "2024-06-30", "2024-12-31");
925
- // { start: "2024-01-01", end: "2024-12-31" } (adjacent, merged)
990
+ intervalUnionDate("2024-01-01", "2024-07-01", "2024-07-01", "2025-01-01");
991
+ // { start: "2024-01-01", end: "2025-01-01" } (touching, joined)
926
992
 
927
993
  intervalUnionDate("2024-01-01", "2024-06-30", "2024-07-01", "2024-12-31");
928
- // null (disjoint with a gap)
994
+ // null (2024-06-30 lies in neither interval)
929
995
 
930
996
  intervalUnionUnix(0, 1700000000, 1000000, 2000000);
931
997
  // { start: 0, end: 1700000000 }
932
998
 
933
999
  intervalUnionUtc(
934
1000
  "2024-01-01T00:00:00Z",
935
- "2024-06-30T23:59:59Z",
1001
+ "2024-07-01T00:00:00Z",
936
1002
  "2024-04-01T00:00:00Z",
937
- "2024-12-31T23:59:59Z",
1003
+ "2025-01-01T00:00:00Z",
938
1004
  );
939
- // { start: "2024-01-01T00:00:00Z", end: "2024-12-31T23:59:59Z" }
1005
+ // { start: "2024-01-01T00:00:00Z", end: "2025-01-01T00:00:00Z" }
940
1006
  ```
941
1007
 
942
- All union functions return `null` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, non-finite values for Unix).
1008
+ 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).
943
1009
 
944
1010
  `intervalDifference*` returns the portion(s) of interval A not covered by interval B, as an array of `{ start, end }` records:
945
1011
 
@@ -954,24 +1020,26 @@ import {
954
1020
  } from "@northguild/gmt";
955
1021
 
956
1022
  intervalDifferenceDate("2024-01-01", "2024-12-31", "2024-06-01", "2024-07-01");
957
- // [{ start: "2024-01-01", end: "2024-05-31" }, { start: "2024-07-02", end: "2024-12-31" }]
1023
+ // [{ start: "2024-01-01", end: "2024-06-01" }, { start: "2024-07-01", end: "2024-12-31" }]
958
1024
 
959
1025
  intervalDifferenceDate("2024-01-01", "2024-12-31", "2024-01-01", "2024-12-31");
960
1026
  // [] (B fully covers A)
961
1027
 
962
1028
  intervalDifferenceUnix(0, 1700000000, 1000000, 2000000);
963
- // [{ start: 0, end: 999999 }, { start: 2000001, end: 1700000000 }]
1029
+ // [{ start: 0, end: 1000000 }, { start: 2000000, end: 1700000000 }]
964
1030
 
965
1031
  intervalDifferenceUtc(
966
- "2024-01-01T00:00:00Z",
967
- "2024-12-31T23:59:59Z",
968
- "2024-06-01T00:00:00Z",
969
- "2024-07-01T00:00:00Z",
1032
+ "2024-01-01T09:00:00Z",
1033
+ "2024-01-01T17:00:00Z",
1034
+ "2024-01-01T12:00:00Z",
1035
+ "2024-01-01T13:00:00Z",
970
1036
  );
971
- // [{ start: "2024-01-01T00:00:00Z", end: "2024-05-31T23:59:59Z" }, { start: "2024-07-02T00:00:00Z", end: "2024-12-31T23:59:59Z" }]
1037
+ // [{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T12:00:00Z" }, { start: "2024-01-01T13:00:00Z", end: "2024-01-01T17:00:00Z" }]
972
1038
  ```
973
1039
 
974
- All difference functions return `[]` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, non-finite values for Unix).
1040
+ 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`.
1041
+
1042
+ 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).
975
1043
 
976
1044
  `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:
977
1045
 
@@ -985,27 +1053,27 @@ import {
985
1053
  intervalXorZoned,
986
1054
  } from "@northguild/gmt";
987
1055
 
988
- intervalXorDate("2024-01-01", "2024-06-30", "2024-04-01", "2024-12-31");
989
- // [{ start: "2024-01-01", end: "2024-03-31" }, { start: "2024-07-01", end: "2024-12-31" }]
1056
+ intervalXorDate("2024-01-01", "2024-07-01", "2024-04-01", "2025-01-01");
1057
+ // [{ start: "2024-01-01", end: "2024-04-01" }, { start: "2024-07-01", end: "2025-01-01" }]
990
1058
 
991
- intervalXorDate("2024-01-01", "2024-06-30", "2024-06-30", "2024-12-31");
992
- // [{ start: "2024-01-01", end: "2024-06-29" }, { start: "2024-07-01", end: "2024-12-31" }] (adjacent)
1059
+ intervalXorDate("2024-01-01", "2024-07-01", "2024-07-01", "2025-01-01");
1060
+ // [{ start: "2024-01-01", end: "2025-01-01" }] (touching intervals share no date, so the runs join)
993
1061
 
994
1062
  intervalXorUnix(0, 1700000000, 1000000, 2000000);
995
- // [{ start: 0, end: 999999 }, { start: 2000001, end: 1700000000 }]
1063
+ // [{ start: 0, end: 1000000 }, { start: 2000000, end: 1700000000 }]
996
1064
 
997
1065
  intervalXorUtc(
998
- "2024-01-01T00:00:00Z",
999
- "2024-06-30T23:59:59Z",
1000
- "2024-04-01T00:00:00Z",
1001
- "2024-12-31T23:59:59Z",
1066
+ "2024-01-01T09:00:00Z",
1067
+ "2024-01-01T13:00:00Z",
1068
+ "2024-01-01T12:00:00Z",
1069
+ "2024-01-01T17:00:00Z",
1002
1070
  );
1003
- // [{ start: "2024-01-01T00:00:00Z", end: "2024-03-31T23:59:59Z" }, { start: "2024-07-01T00:00:00Z", end: "2024-12-31T23:59:59Z" }]
1071
+ // [{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T12:00:00Z" }, { start: "2024-01-01T13:00:00Z", end: "2024-01-01T17:00:00Z" }]
1004
1072
  ```
1005
1073
 
1006
- All xor functions return `[]` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, non-finite values for Unix).
1074
+ 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).
1007
1075
 
1008
- `intervalAbuts*` checks whether two intervals are exactly adjacent — one's end equals the other's start with zero gap and zero overlap:
1076
+ `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:
1009
1077
 
1010
1078
  ```typescript
1011
1079
  import {
@@ -1017,30 +1085,30 @@ import {
1017
1085
  intervalAbutsZoned,
1018
1086
  } from "@northguild/gmt";
1019
1087
 
1020
- intervalAbutsDate("2024-01-01", "2024-06-30", "2024-06-30", "2024-12-31");
1021
- // true
1088
+ intervalAbutsDate("2024-01-01", "2024-07-01", "2024-07-01", "2025-01-01");
1089
+ // true (the first interval ends where the second starts)
1022
1090
 
1023
- intervalAbutsDate("2024-01-01", "2024-06-29", "2024-06-30", "2024-12-31");
1024
- // false (one-day gap)
1091
+ intervalAbutsDate("2024-01-01", "2024-06-30", "2024-07-01", "2024-12-31");
1092
+ // false (2024-06-30 lies in neither interval)
1025
1093
 
1026
- intervalAbutsDate("2024-01-01", "2024-06-30", "2024-04-01", "2024-12-31");
1094
+ intervalAbutsDate("2024-01-01", "2024-07-01", "2024-04-01", "2025-01-01");
1027
1095
  // false (overlap)
1028
1096
 
1029
1097
  intervalAbutsUnix(0, 1000000, 1000000, 2000000);
1030
1098
  // true
1031
1099
 
1032
1100
  intervalAbutsUtc(
1033
- "2024-01-01T00:00:00Z",
1034
- "2024-06-30T23:59:59Z",
1035
- "2024-06-30T23:59:59Z",
1036
- "2024-12-31T23:59:59Z",
1101
+ "2024-01-01T09:00:00Z",
1102
+ "2024-01-01T12:00:00Z",
1103
+ "2024-01-01T12:00:00.000000001Z",
1104
+ "2024-01-01T17:00:00Z",
1037
1105
  );
1038
- // true
1106
+ // false (1 ns apart)
1039
1107
  ```
1040
1108
 
1041
- All abuts checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, non-finite values for Unix).
1109
+ 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).
1042
1110
 
1043
- `intervalEngulfs*` checks whether interval B is fully contained within interval A — every instant of B falls within A. Equivalent to the 4-argument `intervalContains*` mode:
1111
+ `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:
1044
1112
 
1045
1113
  ```typescript
1046
1114
  import {
@@ -1073,7 +1141,7 @@ intervalEngulfsUtc(
1073
1141
  // true
1074
1142
  ```
1075
1143
 
1076
- All engulfs checks return `false` on invalid input (wrong type, malformed strings, leap seconds, inverted intervals, non-finite values for Unix).
1144
+ 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).
1077
1145
 
1078
1146
  `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`:
1079
1147
 
@@ -1102,7 +1170,26 @@ splitIntervalByUnitUnix(0, 86400000, "hour", 6);
1102
1170
  // [{ start: 0, end: 21600000 }, { start: 21600000, end: 43200000 }, { start: 43200000, end: 64800000 }, { start: 64800000, end: 86400000 }]
1103
1171
  ```
1104
1172
 
1105
- 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).
1173
+ 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 `[]`.
1174
+
1175
+ `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:
1176
+
1177
+ ```typescript
1178
+ import { mapDatesInRange, splitIntervalByUnitDate } from "@northguild/gmt";
1179
+
1180
+ splitIntervalByUnitDate("2024-01-01", "2024-01-10", "day", 2, { maxPieces: 4 });
1181
+ // [] — 5 slices exceed the limit
1182
+
1183
+ mapDatesInRange("0001-01-01", "9999-12-31", 1);
1184
+ // [] — 3,652,059 dates exceed the default
1185
+ ```
1186
+
1187
+ 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:
1188
+
1189
+ ```typescript
1190
+ splitIntervalByUnitDate("2024-01-31", "2024-05-01", "month", 1);
1191
+ // [{ 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" }]
1192
+ ```
1106
1193
 
1107
1194
  `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:
1108
1195
 
@@ -1139,7 +1226,9 @@ intervalCountUnix(0, 86400000, "hour");
1139
1226
  // 24
1140
1227
  ```
1141
1228
 
1142
- Zero-length intervals count `1` when they sit mid-unit and `0` when they sit exactly on a unit boundary — `intervalCountDate("2024-01-15", "2024-01-15", "month")` is `1`, while `intervalCountDate("2024-01-01", "2024-01-01", "month")` is `0`. Weeks start on Monday (ISO 8601), singular and plural units are interchangeable (`"day"` and `"days"`), and `intervalCountUnix` uses the system timeZone for calendar boundaries (consistent with `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).
1229
+ 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).
1230
+
1231
+ `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).
1143
1232
 
1144
1233
  `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":
1145
1234
 
@@ -1167,7 +1256,7 @@ intervalLengthZoned(
1167
1256
  // 23 (spring-forward local day is 23 real hours)
1168
1257
  ```
1169
1258
 
1170
- `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).
1259
+ `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`.
1171
1260
 
1172
1261
  `intervalDivideEqually*` splits an interval into `n` equal-length sub-intervals, and `intervalSplitAt*` splits an interval at arbitrary points instead of by count:
1173
1262
 
@@ -1184,7 +1273,7 @@ intervalSplitAtDate("2024-01-01", "2024-01-10", ["2024-01-07", "2024-01-03"]);
1184
1273
  // [{ start: "2024-01-01", end: "2024-01-03" }, { start: "2024-01-03", end: "2024-01-07" }, { start: "2024-01-07", end: "2024-01-10" }]
1185
1274
  ```
1186
1275
 
1187
- `n` must be a positive integer (`[]` otherwise); `n === 1` returns the original interval unchanged, and a zero-length interval returns `n` identical zero-length sub-intervals. Non-fractional types (`PlainDate`) round internal boundaries to the nearest whole unit; every other variant is exact, computed from total elapsed nanoseconds (`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 `[start, end]` or exactly on a boundary, since those cannot introduce a new sub-interval; an empty or all-dropped `points` array returns `[{ start, end }]` unsplit.
1276
+ `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.
1188
1277
 
1189
1278
  `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:
1190
1279
 
@@ -1202,10 +1291,10 @@ intervalXorAllDate([
1202
1291
  { start: "2024-01-05", end: "2024-01-15" },
1203
1292
  { start: "2024-01-08", end: "2024-01-20" },
1204
1293
  ]);
1205
- // [{ start: "2024-01-01", end: "2024-01-04" }, { start: "2024-01-08", end: "2024-01-10" }, { start: "2024-01-16", end: "2024-01-20" }]
1294
+ // [{ start: "2024-01-01", end: "2024-01-05" }, { start: "2024-01-08", end: "2024-01-10" }, { start: "2024-01-15", end: "2024-01-20" }]
1206
1295
  ```
1207
1296
 
1208
- `mergeIntervals*` collapses overlapping or adjacent intervals (shared endpoint) into the minimum non-overlapping set. `intervalXorAll*` returns the set covered by an odd number of the input intervals — 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.
1297
+ `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.
1209
1298
 
1210
1299
  `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:
1211
1300
 
@@ -1236,16 +1325,16 @@ intervalFromDurationTime("12:00:00", "P1D", "start");
1236
1325
  // null (PlainTime has no calendar — a date-unit duration needs a relativeTo it can't supply)
1237
1326
  ```
1238
1327
 
1239
- 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`/`offset`/`overflow` options as `addZoned`; `intervalFromDurationUnix` accepts `addUnix`'s `epochUnit`/`timeZone`/`overflow` options. A negative `duration` that inverts the computed span, or an `overflow: "reject"` result, returns `null` — same sentinel as any other invalid input.
1328
+ 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.
1240
1329
 
1241
- All validators return `false` on invalid input (wrong type, malformed strings, leap seconds, mixed kinds for plain interval validators, non-finite values for Unix).
1330
+ 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).
1242
1331
 
1243
1332
  ### Zoned operations
1244
1333
 
1245
1334
  ```typescript
1246
1335
  import { addZoned, formatZonedDateTime } from "@northguild/gmt";
1247
1336
 
1248
- addZoned("2026-03-07T23:00:00-05:00[America/New_York]", 2, "hour");
1337
+ addZoned("2026-03-07T23:00:00-05:00[America/New_York]", { hours: 2 });
1249
1338
  // "2026-03-08T01:00:00-05:00[America/New_York]"
1250
1339
 
1251
1340
  formatZonedDateTime("2024-03-17T14:30:45+00:00[UTC]", "en-US", {
@@ -1267,23 +1356,31 @@ convertPlainDateTimeToZoned("2024-03-10T02:30:00", "America/New_York", {
1267
1356
  // "" — no such local time exists
1268
1357
  ```
1269
1358
 
1270
- The `startOfZoned`/`endOfZoned`/`startOfQuarterForZoned`/`endOfQuarterForZoned`/`mapZonedHoursInDay` family (and their `unix/` counterparts) also accept `disambiguation`, plus an `offset` option (`"prefer"` | `"use"` | `"ignore"` (default) | `"reject"`) that controls whether the source's existing UTC offset is kept when computing the new boundary. **`offset` must stay at its default (`"ignore"`) for `disambiguation` to take effect** — Temporal's own default (`"prefer"`) keeps the source offset whenever still valid, which silently makes `disambiguation` a no-op:
1359
+ 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:
1271
1360
 
1272
1361
  ```typescript
1273
1362
  import { startOfZoned } from "@northguild/gmt";
1274
1363
 
1275
1364
  // 2024-11-03T01:45:00-05:00 is the SECOND, repeated 1am of the fall-back overlap in America/New_York.
1276
- const source = "2024-11-03T01:45:00-05:00[America/New_York]";
1365
+ startOfZoned("2024-11-03T01:45:00-05:00[America/New_York]", "hour");
1366
+ // "2024-11-03T01:00:00-05:00[America/New_York]" — the real start of the repeated hour
1367
+ ```
1277
1368
 
1278
- startOfZoned(source, "hour", { disambiguation: "reject" });
1279
- // "" — offset defaults to "ignore", so disambiguation actually fires and "reject" throws
1369
+ 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).
1280
1370
 
1281
- startOfZoned(source, "hour", { disambiguation: "reject", offset: "prefer" });
1282
- // "2024-11-03T01:00:00-05:00[America/New_York]" — offset:"prefer" keeps the source's
1283
- // still-valid -05:00 offset, so disambiguation is never consulted and "reject" never fires
1284
- ```
1371
+ `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.
1372
+
1373
+ `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:
1285
1374
 
1286
- `convertPlainDateTimeToZoned` and `addZoned`/`subtractZoned` also accept `offset` for API consistency, but it's permanently inert on both — their construction path never has a stored offset for it to act on.
1375
+ ```typescript
1376
+ import { diffZoned } from "@northguild/gmt";
1377
+
1378
+ diffZoned("2024-03-09T12:00:00-05:00[America/New_York]", "2024-03-10T12:00:00-04:00[America/New_York]", "days");
1379
+ // 1 — noon to noon across a 23-hour day
1380
+
1381
+ diffZoned("2024-01-01T00:00:00-05:00[America/New_York]", "2024-01-03T00:00:00+01:00[Europe/Paris]", "days");
1382
+ // null — calendar unit across two time zones
1383
+ ```
1287
1384
 
1288
1385
  `hasDaylightSaving` reports whether an IANA timezone observes daylight saving time at all:
1289
1386
 
@@ -1424,7 +1521,7 @@ closestZonedTo("2024-03-15T12:00:00[America/New_York]", [
1424
1521
  // "2024-03-18T00:00:00-04:00[America/New_York]"
1425
1522
  ```
1426
1523
 
1427
- See [`docs/dst-disambiguation.md`](../../docs/dst-disambiguation.md) for the full explanation, including why `overflow` was deliberately left off the public API.
1524
+ See [`docs/dst-disambiguation.md`](../../docs/dst-disambiguation.md) for the full explanation, including where `overflow` is and is not exposed.
1428
1525
 
1429
1526
  ### Formatting
1430
1527
 
@@ -1459,24 +1556,36 @@ formatRelativeDate("2026-01-15");
1459
1556
  // e.g. "3 months ago"
1460
1557
 
1461
1558
  formatRelativeTime("14:30:00", "en-US", { style: "short" });
1462
- // e.g. "2 hours ago"
1559
+ // e.g. "2 hr. ago"
1463
1560
 
1464
1561
  formatRelativeDateTime("2026-03-17T09:00:00", "en-GB", {
1465
1562
  style: "long",
1466
1563
  numeric: "always",
1467
1564
  });
1468
- // e.g. "17 March, 2026 at 09:00"
1565
+ // e.g. "in 3 hours"
1469
1566
 
1470
1567
  // Zoned relative formatting — reference can be a ZonedDateTime, UTC string, or unix epoch.
1471
1568
  formatRelativeZoned("2026-03-08T01:00:00-05:00[America/New_York]", "en-US");
1472
1569
  // e.g. "tomorrow"
1473
1570
 
1474
- formatRelativeUtc("2024-03-17T14:30:45+00:00[UTC]", "en-US");
1475
- // e.g. "2 years ago"
1571
+ // Auto-picked units run from second through year, with formatRelativeDate's thresholds;
1572
+ // pass largestUnit to cap them.
1573
+ formatRelativeUtc("2024-03-17T14:30:45Z", "en-US", {
1574
+ reference: "2026-03-17T14:30:45Z",
1575
+ });
1576
+ // "2 years ago"
1577
+ formatRelativeUtc("2024-03-17T14:30:45Z", "en-US", {
1578
+ reference: "2026-03-17T14:30:45Z",
1579
+ largestUnit: "day",
1580
+ });
1581
+ // "730 days ago"
1476
1582
 
1477
1583
  // Unix epoch relative formatting.
1478
- formatRelativeUnix(1710685845000, "en-US", { epochUnit: "milliseconds" });
1479
- // e.g. "3 years ago"
1584
+ formatRelativeUnix(1710685845000, "en-US", {
1585
+ epochUnit: "milliseconds",
1586
+ reference: 1805358645000,
1587
+ });
1588
+ // "3 years ago"
1480
1589
 
1481
1590
  // roundingMethod ("floor" | "ceil" | "round", default "round") controls how the
1482
1591
  // computed distance rounds to the display unit — every formatRelative* function accepts it.
@@ -1507,13 +1616,13 @@ formatCalendarZoned("2026-03-16T14:30:00-04:00[America/New_York]", "de-DE", {
1507
1616
  // formatZonedRange (same parameter order and option shape), for a
1508
1617
  // locale-elided range between two timezone-free values.
1509
1618
  formatDateRange("2024-02-03", "2024-02-05", "en-US", { dateStyle: "long" });
1510
- // "February 3 – 5, 2024"
1619
+ // "February 3 - 5, 2024"
1511
1620
 
1512
1621
  formatDateTimeRange("2024-02-03T09:00:00", "2024-02-03T17:00:00", "en-US", {
1513
1622
  dateStyle: "long",
1514
1623
  timeStyle: "short",
1515
1624
  });
1516
- // "February 3, 2024, 9:00 AM – 5:00 PM"
1625
+ // "February 3, 2024, 9:00 AM - 5:00 PM"
1517
1626
 
1518
1627
  // formatDateToParts / formatDateTimeToParts / formatZonedToParts return the
1519
1628
  // locale-ordered Array<{ type, value }> parts behind the strings above,
@@ -1534,7 +1643,7 @@ formatDateToParts("2024-03-15", "fr-FR");
1534
1643
  formatZonedToParts("2024-03-15T14:30:00-04:00[America/New_York]", "en-US", {
1535
1644
  timeZoneName: "longOffset",
1536
1645
  });
1537
- // includes { type: "timeZoneName", value: "GMT-4" }
1646
+ // includes { type: "timeZoneName", value: "GMT-04:00" }
1538
1647
  ```
1539
1648
 
1540
1649
  ### Named machine formats
@@ -1562,17 +1671,29 @@ formatRfc2822("2024-03-15T14:30:00-04:00[America/New_York]");
1562
1671
  parseRfc2822("Fri, 15 Mar 2024 14:30:00 -0400");
1563
1672
  // "2024-03-15T14:30:00-04:00[-04:00]"
1564
1673
 
1565
- // HTTP headers (RFC 7231 IMF-fixdate) — Last-Modified, Date, Expires.
1674
+ // A receiver reads RFC 5322's full and obsolete syntax: comments, any case,
1675
+ // two-digit years and zone names. A day name that contradicts the date is "".
1676
+ parseRfc2822("fri,15 Mar 24 14:30 -0400 (EDT)");
1677
+ // "2024-03-15T14:30:00-04:00[-04:00]"
1678
+ parseRfc2822("Sat, 15 Mar 2024 14:30:00 -0400");
1679
+ // "" — 15 March 2024 was a Friday
1680
+
1681
+ // HTTP headers (RFC 9110 IMF-fixdate) — Last-Modified, Date, Expires.
1566
1682
  formatHttp("2024-03-15T14:30:00Z");
1567
1683
  // "Fri, 15 Mar 2024 14:30:00 GMT"
1568
1684
  parseHttp("Fri, 15 Mar 2024 14:30:00 GMT");
1569
1685
  // "2024-03-15T14:30:00Z"
1686
+ // parseHttp also reads the obsolete rfc850-date and asctime-date forms.
1687
+ parseHttp("Sun Nov 6 08:49:37 1994");
1688
+ // "1994-11-06T08:49:37Z"
1570
1689
 
1571
1690
  // ANSI SQL / ODBC datetime literals (DATETIME/TIMESTAMP columns, no tz).
1572
1691
  formatSql("2024-03-15T14:30:00");
1573
1692
  // "2024-03-15 14:30:00"
1574
1693
  parseSql("2024-03-15 14:30:00");
1575
1694
  // "2024-03-15T14:30:00"
1695
+ parseSql("2024-03-15 14:30");
1696
+ // "" — the SQL literal grammar requires seconds, and a year of 0001–9999
1576
1697
 
1577
1698
  // Strict RFC 3339 — strips the bracketed IANA zone GMT's own zoned strings
1578
1699
  // carry, which RFC 3339 does not permit.
@@ -1582,21 +1703,482 @@ parseRfc3339("2024-03-15T14:30:00-04:00");
1582
1703
  // "2024-03-15T14:30:00-04:00[-04:00]"
1583
1704
  ```
1584
1705
 
1706
+ 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.
1707
+
1585
1708
  ### Unix and UTC helpers
1586
1709
 
1587
1710
  ```typescript
1588
1711
  import { getUnixNow, getUtcNow, convertUnixToPlainDate } from "@northguild/gmt";
1589
1712
 
1590
- getUnixNow("milliseconds");
1591
- // 1710685845000
1713
+ getUnixNow();
1714
+ // 1710685845000 (milliseconds; getUnixNow({ epochUnit: "seconds" }) is 1710685845)
1592
1715
 
1593
1716
  getUtcNow();
1594
1717
  // "2026-03-18T11:42:33.123Z"
1595
1718
 
1596
- convertUnixToPlainDate(1710685845);
1719
+ convertUnixToPlainDate(1710685845000, { timeZone: "UTC" });
1597
1720
  // "2024-03-17"
1598
1721
  ```
1599
1722
 
1723
+ ### Nanosecond precision
1724
+
1725
+ `convertZonedToUnix` and friends return `number` milliseconds. Systems that record
1726
+ telemetry, trades or sensor readings need nanoseconds, and a `number` cannot hold them:
1727
+ integers are exact only to `2^53 − 1` ≈ 9.0 × 10^15, and nanoseconds since the epoch
1728
+ passed that in April 1970. (`Date.now() * 1e6` is not a nanosecond timestamp — it is a
1729
+ millisecond timestamp with three zeroes appended.) The `precision/` namespace works in
1730
+ `bigint`:
1731
+
1732
+ ```typescript
1733
+ import {
1734
+ toNanoseconds,
1735
+ fromNanoseconds,
1736
+ formatNanoseconds,
1737
+ parseNanoseconds,
1738
+ truncateNanoseconds,
1739
+ } from "@northguild/gmt";
1740
+
1741
+ toNanoseconds("2024-03-10T12:00:00.123456789Z");
1742
+ // 1710072000123456789n
1743
+
1744
+ fromNanoseconds(1710072000123456789n);
1745
+ // "2024-03-10T12:00:00.123456789Z"
1746
+
1747
+ fromNanoseconds(1710072000123456789n, "America/New_York");
1748
+ // "2024-03-10T08:00:00.123456789-04:00[America/New_York]"
1749
+ ```
1750
+
1751
+ `JSON.stringify` throws a `TypeError` on a `bigint`, so the value crosses a wire as a
1752
+ canonical decimal string:
1753
+
1754
+ ```typescript
1755
+ JSON.stringify({ observedAt: formatNanoseconds(1710072000123456789n) });
1756
+ // '{"observedAt":"1710072000123456789"}'
1757
+
1758
+ parseNanoseconds("1710072000123456789");
1759
+ // 1710072000123456789n
1760
+ ```
1761
+
1762
+ Most storage engines cannot hold nanoseconds — PostgreSQL `timestamptz` and MySQL
1763
+ `DATETIME(6)` hold microseconds — so a naive write-then-read loses the last digits
1764
+ silently. Truncate to the target precision first, and the round-trip is exact:
1765
+
1766
+ ```typescript
1767
+ truncateNanoseconds(1710072000123456789n, "us");
1768
+ // 1710072000123456000n — safe to write to a microsecond column
1769
+
1770
+ truncateNanoseconds(1710072000123456789n, "ms");
1771
+ // 1710072000123000000n
1772
+ ```
1773
+
1774
+ Truncation floors toward negative infinity, so pre-1970 values truncate the same way
1775
+ post-1970 ones do — `truncateNanoseconds(-1500n, "us")` is `-2000n`, not `-1000n`.
1776
+ Rounding toward zero would make the result jump direction either side of the epoch.
1777
+
1778
+ `0n` is both the epoch and the invalid-input sentinel, so `precision/validate` exists to
1779
+ tell them apart. Each predicate accepts exactly what its partner parses:
1780
+
1781
+ ```typescript
1782
+ import {
1783
+ isValidInstant,
1784
+ isValidNanoseconds,
1785
+ isValidNanoPattern,
1786
+ } from "@northguild/gmt";
1787
+
1788
+ isValidInstant("1970-01-01T00:00:00Z"); // true — toNanoseconds returns 0n, the epoch
1789
+ isValidInstant("garbage"); // false — toNanoseconds returns 0n, the sentinel
1790
+
1791
+ isValidNanoPattern("0"); // true — parseNanoseconds returns 0n, the epoch
1792
+ isValidNanoPattern("1e18"); // false
1793
+
1794
+ isValidNanoseconds(0n); // true — in range for truncateNanoseconds / fromNanoseconds
1795
+ isValidNanoseconds(0); // false — a number cannot carry a nanosecond timestamp
1796
+ ```
1797
+
1798
+ Reach for `isValidInstant` rather than `isValidUtc`: the latter gates on GMT's stricter
1799
+ `<date>T<time>Z` shape and rejects the offsets and bracketed zones `toNanoseconds`
1800
+ accepts, so validating with it discards valid input.
1801
+
1802
+ Every `precision/` function returns a sentinel (`""` for strings, `0n` for bigints) on
1803
+ invalid input, and accepts only values inside the range `Temporal.Instant` can represent
1804
+ (±8_640_000_000_000_000_000_000n). `0n` is also the epoch itself, so validate the input
1805
+ first when the two must be told apart. Leap-second-aware time scales (TAI, GPS) are not
1806
+ part of this namespace — these are plain instant conversions.
1807
+
1808
+ ### Foreign epoch bridges
1809
+
1810
+ Other systems do not count from 1970. NTP counts from 1900 and rolls over in 2036, Windows
1811
+ `FILETIME` counts 100-nanosecond intervals from 1601, .NET ticks count the same interval
1812
+ from year 1, Excel uses a day serial with a deliberate 1900 leap-year bug, and PostgreSQL
1813
+ stores microseconds from 2000-01-01. Every integration re-derives these constants, usually
1814
+ wrongly:
1815
+
1816
+ ```typescript
1817
+ import {
1818
+ toNtpTimestamp,
1819
+ fromNtpTimestamp,
1820
+ toFileTime,
1821
+ fromFileTime,
1822
+ toDotNetTicks,
1823
+ fromDotNetTicks,
1824
+ toExcelSerial,
1825
+ fromExcelSerial,
1826
+ toPgMicroseconds,
1827
+ fromPgMicroseconds,
1828
+ } from "@northguild/gmt";
1829
+
1830
+ toFileTime("1970-01-01T00:00:00Z");
1831
+ // 116444736000000000n — 100 ns intervals since 1601-01-01
1832
+
1833
+ toDotNetTicks("1970-01-01T00:00:00Z");
1834
+ // 621355968000000000n — same unit, counted from 0001-01-01
1835
+
1836
+ toPgMicroseconds("2024-03-10T12:00:00Z");
1837
+ // 763387200000000n — microseconds since 2000-01-01
1838
+ ```
1839
+
1840
+ **NTP timestamps are era-ambiguous by design.** The seconds field is unsigned 32-bit, so it
1841
+ wraps every ~136.19 years and the era is not part of the value. `fromNtpTimestamp` takes it
1842
+ as an explicit argument:
1843
+
1844
+ ```typescript
1845
+ toNtpTimestamp("2036-02-07T06:28:15Z");
1846
+ // 18446744069414584320n — the last second of era 0
1847
+
1848
+ toNtpTimestamp("2036-02-07T06:28:16Z");
1849
+ // 0n — era 1 begins, and the seconds field wraps
1850
+
1851
+ fromNtpTimestamp(0n);
1852
+ // "1900-01-01T00:00:00Z"
1853
+
1854
+ fromNtpTimestamp(0n, 1);
1855
+ // "2036-02-07T06:28:16Z" — the same value, one era on
1856
+ ```
1857
+
1858
+ **Excel's serial 60 is the phantom 29 February 1900**, a date that never existed — 1900 was
1859
+ not a leap year. Lotus 1-2-3 got it wrong and Excel keeps the bug for compatibility, so the
1860
+ serial day `[60, 61)` is a hole rather than a date. "Fixing" it would put GMT one day out
1861
+ from Excel for every date before March 1900:
1862
+
1863
+ ```typescript
1864
+ fromExcelSerial(59);
1865
+ // "1900-02-28T00:00:00Z"
1866
+
1867
+ fromExcelSerial(60);
1868
+ // "" — the phantom 1900-02-29
1869
+
1870
+ fromExcelSerial(61);
1871
+ // "1900-03-01T00:00:00Z"
1872
+
1873
+ toExcelSerial("2024-03-10T12:00:00Z");
1874
+ // 45361.5 — days, with the time of day as the fraction
1875
+
1876
+ toExcelSerial("2024-03-10T12:00:00Z", { system: "1904" });
1877
+ // 43899.5 — the legacy Mac system, exactly 1462 days lower
1878
+ ```
1879
+
1880
+ Each bridge accepts only what its target format can actually hold, and returns the
1881
+ namespace's sentinel otherwise — `toFileTime` on a pre-1601 instant is `0n`, not a negative
1882
+ tick count, and `toExcelSerial` past 9999-12-31 is `null`, not an out-of-range serial.
1883
+ PostgreSQL's range is the one that surprises: its timestamp domain starts at 4713 BC, which
1884
+ is 40× later than the earliest instant Temporal can represent, so an in-range Temporal
1885
+ instant is not automatically an in-range PostgreSQL one. Where
1886
+ the target unit is coarser than a nanosecond the conversion floors toward negative infinity,
1887
+ so a round trip is exact to that unit: 100 ns for `FILETIME` and .NET ticks, 1 µs for
1888
+ PostgreSQL, 1 ms for Excel. NTP is the exception in the other direction — its 2^-32 s unit is
1889
+ finer than a nanosecond, so `fromNtpTimestamp` rounds to the nearest nanosecond and the round
1890
+ trip is exact.
1891
+ ### Spans
1892
+
1893
+ `diffZoned` measures in calendar units and returns a `Duration`. Profiling, tracing and
1894
+ telemetry want a raw number, and the `span/` namespace gives one:
1895
+
1896
+ ```typescript
1897
+ import { spanMs, spanNs, spanWallClock } from "@northguild/gmt";
1898
+
1899
+ spanMs("2024-03-10T12:00:00Z", "2024-03-10T12:00:01Z");
1900
+ // 1000
1901
+
1902
+ spanMs("2024-03-10T12:00:00Z", "2024-03-10T12:00:00.123456789Z");
1903
+ // 123.456789 — fractional, like performance.now()
1904
+
1905
+ spanNs("2024-03-10T12:00:00.123456789Z", "2024-03-10T12:00:00.123456790Z");
1906
+ // 1n
1907
+ ```
1908
+
1909
+ Both are signed — `spanMs(b, a)` is exactly `-spanMs(a, b)` — and both measure **exact
1910
+ elapsed time**. That is not the same question as calendar distance, and conflating the two
1911
+ is the most common span bug there is. A wall-clock day containing a DST transition is 23 or
1912
+ 25 hours long (24.5 in `Australia/Lord_Howe`), so:
1913
+
1914
+ ```typescript
1915
+ const start = "2024-03-09T12:00:00-05:00[America/New_York]";
1916
+ const end = "2024-03-10T12:00:00-04:00[America/New_York]";
1917
+
1918
+ spanMs(start, end);
1919
+ // 82800000 — 23 hours actually elapsed
1920
+
1921
+ spanWallClock(start, end, "hours");
1922
+ // 24 — the clock face advanced a full day
1923
+
1924
+ spanWallClock(start, end, "days");
1925
+ // 1
1926
+ ```
1927
+
1928
+ `spanWallClock` reads each endpoint's own local wall clock — straight off the string, never
1929
+ via an instant, so DST disambiguation cannot distort it and a local time that never occurred
1930
+ 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
1931
+ is 12 wall-clock hours and 7 elapsed hours. It truncates toward zero, and it is not a count
1932
+ of midnights crossed.
1933
+
1934
+ `isValidSpan(start, end)` answers "will these two produce a span?" — true exactly when
1935
+ `spanMs` and `spanNs` will return a value. It is symmetric, since a reversed pair is a
1936
+ negative span rather than an invalid one. `spanWallClock` has a different input grammar and
1937
+ is covered by `isValidZonedDateTime`.
1938
+
1939
+ Three limits, all deliberate:
1940
+
1941
+ - **`0` and `0n` are valid spans**, so invalid input returns `null` from all three rather
1942
+ than the zero the rest of the library uses for numbers. `null` and not `NaN`: it is the
1943
+ sentinel every other number-returning function in GMT uses, and the only one
1944
+ `strictNullChecks` forces a caller to handle — a `NaN` types as plain `number` and
1945
+ propagates silently through arithmetic.
1946
+ - **`spanMs` returns `null` past `Number.MAX_SAFE_INTEGER` milliseconds** (±285,000 years,
1947
+ which two instants at opposite ends of `Temporal.Instant`'s range exceed). A
1948
+ sub-millisecond fraction counts toward that ceiling, so what comes back is always a safe
1949
+ integer or below. Use `spanNs` there; its result is a duration, not an instant, and can be
1950
+ twice the epoch-nanosecond range.
1951
+ - **Leap seconds are not counted.** UTC repeats a second rather than numbering a 61st one,
1952
+ so a span across one is a second short of the physical elapsed time; against a smeared
1953
+ clock (Google, AWS, Meta) the error is up to a second spread over the smear window.
1954
+
1955
+ ### Offset-preserving instants and local resolution
1956
+
1957
+ An instant orders events globally; the UTC offset in force where the event happened renders
1958
+ it as the human on the ground saw it. Neither derives from the other, which is why GS1 EPCIS
1959
+ 2.0 requires both fields (`eventTime` plus `eventTimeZoneOffset`), UN/EDIFACT DTM has
1960
+ qualifiers `303`/`304` for the pair, and DICOM appends `&ZZXX` to a `DT` value. The `instant/`
1961
+ namespace is that pair:
1962
+
1963
+ ```typescript
1964
+ import { fromOffsetInstant, toOffsetInstant } from "@northguild/gmt";
1965
+
1966
+ toOffsetInstant("2024-07-15T12:00:00-04:00[America/New_York]");
1967
+ // { instant: "2024-07-15T16:00:00Z", offset: "-04:00", timeZone: "America/New_York" }
1968
+
1969
+ // Most feeds send no zone at all — the offset alone is what happened.
1970
+ toOffsetInstant("2024-07-15T12:00:00-04:00");
1971
+ // { instant: "2024-07-15T16:00:00Z", offset: "-04:00" }
1972
+
1973
+ // A UTC-only feed plus a zone you know from somewhere else.
1974
+ toOffsetInstant("2024-07-15T16:00:00Z", "America/New_York");
1975
+ // { instant: "2024-07-15T16:00:00Z", offset: "-04:00", timeZone: "America/New_York" }
1976
+
1977
+ fromOffsetInstant({ instant: "2024-07-15T16:00:00Z", offset: "-04:00" });
1978
+ // "2024-07-15T12:00:00-04:00"
1979
+ ```
1980
+
1981
+ **An offset is not a zone.** `-05:00` does not identify `America/New_York` — it is every zone
1982
+ sitting at `-05:00` that day, and it says nothing about what that zone will do next spring.
1983
+ Store the zone for anything still to be scheduled; store the offset for anything that already
1984
+ happened. `timeZone` is optional because most feeds do not send one, and a string whose
1985
+ offset contradicts its own bracketed zone returns `null` rather than a guess.
1986
+
1987
+ The other half of the namespace is the reverse problem: a wall time that arrives with no
1988
+ offset at all ("gate-out 08:00"). Resolving one needs a zone the sender did not send, plus a
1989
+ policy for the two days a year the mapping is not one-to-one:
1990
+
1991
+ ```typescript
1992
+ import { classifyLocal, resolveLocal } from "@northguild/gmt";
1993
+
1994
+ classifyLocal("2024-11-03T01:30:00", "America/New_York"); // "ambiguous"
1995
+ classifyLocal("2024-03-10T02:30:00", "America/New_York"); // "nonexistent"
1996
+ classifyLocal("2024-07-15T12:00:00", "America/New_York"); // "unique"
1997
+
1998
+ resolveLocal("2024-11-03T01:30:00", "America/New_York");
1999
+ // "2024-11-03T05:30:00Z" — the default "compatible" takes the first 01:30
2000
+
2001
+ resolveLocal("2024-11-03T01:30:00", "America/New_York", { disambiguation: "later" });
2002
+ // "2024-11-03T06:30:00Z" — the same wall clock, an hour of real time later
2003
+
2004
+ resolveLocal("2024-11-03T01:30:00", "America/New_York", { disambiguation: "reject" });
2005
+ // "" — ambiguous, and not guessed
2006
+ ```
2007
+
2008
+ `classifyLocal` exists so code can branch **before** a policy is applied. A demurrage clock, a
2009
+ medication window or a duty limit should refuse an ambiguous wall time, or route it to a
2010
+ human — not silently accept whichever of two instants an hour apart a default handed it. See
2011
+ [DST Disambiguation](../../docs/dst-disambiguation.md) for the four strategies in full.
2012
+
2013
+ Two notes on the boundaries of this namespace:
2014
+
2015
+ - **`resolveLocal` returns an instant; `convertPlainDateTimeToZoned` returns a zoned string.**
2016
+ Same underlying resolution, different output and different precision — `resolveLocal` is
2017
+ exact to the nanosecond, while `convertPlainDateTimeToZoned` defaults to milliseconds.
2018
+ Neither takes an `offset` option. Reach for whichever shape you need next.
2019
+ - **`offset` is `±HH:MM`, except where it is not.** A handful of zones did not run on a whole
2020
+ minute before 1972 — `Africa/Monrovia` really was `-00:44:30` — and those report
2021
+ `±HH:MM:SS`. Rounding them would put the pair thirty seconds from the event it describes.
2022
+
2023
+ ### Calendar boundaries and zone-aware buckets
2024
+
2025
+ Two absences with outsized consequences, and they turn out to be the same absence twice: a
2026
+ calendar boundary is not a fixed number of hours, and it is not in UTC.
2027
+
2028
+ **Week and period identifiers.** Vessel schedules are published by week number and retail runs
2029
+ on 52/53-week fiscal calendars. Both come back as whole identifiers, not as fields to combine
2030
+ by hand — a week number without its week-numbering year is ambiguous at both ends of a year:
2031
+
2032
+ ```typescript
2033
+ import { getFiscalPeriod, getIsoWeekDate, getOrdinalDate, getQuarter } from "@northguild/gmt";
2034
+
2035
+ getIsoWeekDate("2027-01-01"); // { year: 2026, week: 53, weekday: 5 } — week-year 2026, not 2027
2036
+ getOrdinalDate("2024-12-31"); // { year: 2024, dayOfYear: 366 }
2037
+ getQuarter("2024-03-31", { fiscalYearStartMonth: 4 }); // { year: 2023, quarter: 4 }
2038
+
2039
+ // The NRF retail calendar, stated as its published rule: "the Saturday nearest to January 31".
2040
+ const nrf = { pattern: "4-5-4", yearEndsOn: "2026-01-31" } as const;
2041
+
2042
+ getFiscalPeriod("2024-06-15", nrf); // { year: 2024, period: 5, week: 19 }
2043
+ getFiscalPeriod("2024-01-28", nrf); // { year: 2023, period: 12, week: 53 } — a 53-week year
2044
+ ```
2045
+
2046
+ 52 × 7 is 364 days, so a 53rd week is inserted every five or six years and lands in the final
2047
+ period. `yearEndsOn` states the *rule*, by example — years end on that date's weekday, nearest
2048
+ that date's month and day — not one year's end, because GMT bundles no fiscal calendar. There
2049
+ is no single correct retail calendar to bundle.
2050
+
2051
+ **Zone-aware bucketing.** "Group by day in `America/New_York`" over UTC timestamps is the most
2052
+ common observability bug there is, and the same operation decides how many chargeable days a
2053
+ container accrued, because free time is counted in terminal-local calendar days:
2054
+
2055
+ ```typescript
2056
+ import { bucketRange, floorToZone } from "@northguild/gmt";
2057
+
2058
+ floorToZone("2024-06-15T03:00:00Z", "day", "America/New_York"); // "2024-06-14T04:00:00Z"
2059
+ floorToZone("2024-06-15T03:00:00Z", "day", "UTC"); // "2024-06-15T00:00:00Z"
2060
+ ```
2061
+
2062
+ Same instant, different calendar day — 03:00 UTC on 15 June is still 14 June in New York, and
2063
+ flooring it to the UTC day is wrong for most of the world for most of the day. The zone is an
2064
+ argument because GMT has no ambient one and must not acquire one.
2065
+
2066
+ `bucketRange` returns the boundaries spanning a range, and **the buckets are deliberately not
2067
+ uniform in length**:
2068
+
2069
+ ```typescript
2070
+ bucketRange("2024-03-09T05:00:00Z", "2024-03-12T04:00:00Z", "day", "America/New_York");
2071
+ // ["2024-03-09T05:00:00Z", "2024-03-10T05:00:00Z", "2024-03-11T04:00:00Z"]
2072
+ // 24h, then 23h — the middle day springs forward
2073
+
2074
+ bucketRange("2024-11-02T04:00:00Z", "2024-11-05T05:00:00Z", "day", "America/New_York");
2075
+ // ["2024-11-02T04:00:00Z", "2024-11-03T04:00:00Z", "2024-11-04T05:00:00Z"]
2076
+ // 24h, then 25h — the middle day falls back
2077
+ ```
2078
+
2079
+ Forcing 24 hours here is what makes a daily aggregate drift an hour twice a year. The same
2080
+ honesty applies to boundaries that do not exist: the calendar day Samoa deleted crossing the
2081
+ date line is absent from the list, a local day whose midnight is skipped by a spring-forward
2082
+ starts at 01:00, and in a zone that falls back by half an hour (`Australia/Lord_Howe`) the
2083
+ local 01:00 hour bucket is genuinely 90 minutes long.
2084
+
2085
+ For a zoned or epoch value, `startOfZoned`/`startOfUnix` with no options return the same real
2086
+ boundary. Do not floor with `roundZoned`/`roundUnix` and `roundingMode: "trunc"`: they follow
2087
+ TC39 `ZonedDateTime.round`, which rounds the wall clock and re-resolves it in the zone, so
2088
+ `"2024-09-29T03:50:00+13:45[Pacific/Chatham]"` truncated to the hour gives 04:00 — after the
2089
+ input.
2090
+
2091
+ A pattern or a bucket unit the namespace does not recognise returns the same sentinel an
2092
+ unusable date or instant does, so both unions have a type guard to narrow one that arrived as
2093
+ a bare string — from config, an env var, a form:
2094
+
2095
+ ```typescript
2096
+ import { isValidFiscalPattern, isValidZoneBucketUnit } from "@northguild/gmt";
2097
+
2098
+ isValidFiscalPattern("4-5-4"); // true — the NRF retail calendar
2099
+ isValidFiscalPattern("4-5-5"); // false — the shape a 53-week year's last quarter takes
2100
+ isValidZoneBucketUnit("day"); // true
2101
+ isValidZoneBucketUnit("days"); // true — singular or plural, as floorToZone accepts
2102
+ isValidZoneBucketUnit("year"); // false — the year question is getQuarter's
2103
+ ```
2104
+
2105
+ ### Interval algebra
2106
+
2107
+ Four realms need the same operation and none of them could express it: sum the parts of an
2108
+ interval that fall inside a set of allowed windows. Laytime counts only the hours a charter
2109
+ clause allows, driver hours split a duty period around mandatory rest, demurrage free time
2110
+ counts only working days, and a trading window counts only continuous-session time. The
2111
+ `interval/` namespace is that one shared primitive, with one boundary rule:
2112
+
2113
+ **Half-open `[start, end)`.** An instant `t` is inside when `start ≤ t < end`. That is SQL:2011's
2114
+ closed-open `PERIOD`, RFC 5545's non-inclusive `DTEND` and Dijkstra's EWD831. A container gated
2115
+ out at exactly 17:00 has not used another day, and two consecutive shifts never double-count the
2116
+ instant they meet.
2117
+
2118
+ ```typescript
2119
+ import {
2120
+ clampInterval,
2121
+ intersectIntervals,
2122
+ intervalContains,
2123
+ intervalsOverlap,
2124
+ isValidInterval,
2125
+ mergeIntervals,
2126
+ splitIntervalAt,
2127
+ subtractIntervals,
2128
+ sumIntervals,
2129
+ } from "@northguild/gmt";
2130
+
2131
+ const shift = { start: "2024-01-01T09:00:00Z", end: "2024-01-01T17:00:00Z" };
2132
+
2133
+ intervalContains(shift, "2024-01-01T09:00:00Z"); // true — start is inside
2134
+ intervalContains(shift, "2024-01-01T17:00:00Z"); // false — end is not
2135
+
2136
+ intervalsOverlap(shift, { start: "2024-01-01T17:00:00Z", end: "2024-01-01T18:00:00Z" });
2137
+ // false — touching intervals share no instant
2138
+ intersectIntervals(shift, { start: "2024-01-01T12:00:00Z", end: "2024-01-01T18:00:00Z" });
2139
+ // { start: "2024-01-01T12:00:00Z", end: "2024-01-01T17:00:00Z" }
2140
+ clampInterval({ start: "2024-01-01T08:00:00Z", end: "2024-01-01T12:00:00Z" }, shift);
2141
+ // { start: "2024-01-01T09:00:00Z", end: "2024-01-01T12:00:00Z" }
2142
+
2143
+ mergeIntervals([
2144
+ { start: "2024-01-01T13:00:00Z", end: "2024-01-01T17:00:00Z" },
2145
+ { start: "2024-01-01T09:00:00Z", end: "2024-01-01T12:00:00Z" },
2146
+ { start: "2024-01-01T12:00:00Z", end: "2024-01-01T13:00:00Z" },
2147
+ ]);
2148
+ // [{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T17:00:00Z" }] — sorted, touching runs joined
2149
+
2150
+ const worked = subtractIntervals(shift, [{ start: "2024-01-01T12:00:00Z", end: "2024-01-01T13:00:00Z" }]);
2151
+ // [{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T12:00:00Z" },
2152
+ // { start: "2024-01-01T13:00:00Z", end: "2024-01-01T17:00:00Z" }]
2153
+ sumIntervals(worked); // "PT7H"
2154
+
2155
+ splitIntervalAt(shift, ["2024-01-01T15:00:00Z", "2024-01-01T11:00:00Z"]);
2156
+ // [{ start: "2024-01-01T09:00:00Z", end: "2024-01-01T11:00:00Z" },
2157
+ // { start: "2024-01-01T11:00:00Z", end: "2024-01-01T15:00:00Z" },
2158
+ // { start: "2024-01-01T15:00:00Z", end: "2024-01-01T17:00:00Z" }] — pieces share no instant
2159
+
2160
+ isValidInterval({ start: "2024-01-01T17:00:00Z", end: "2024-01-01T09:00:00Z" }); // false — inverted
2161
+ ```
2162
+
2163
+ - **Endpoints are instants.** An offset (`Z` or `±HH:MM`) is required and a `[Zone]` annotation is
2164
+ optional. As with a TC39 `Temporal.Instant`, endpoints compare by epoch nanoseconds, so two of
2165
+ 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
2166
+ edges, build them with `floorToZone` or `bucketRange` first.
2167
+ - **Outputs are the caller's own strings**, never re-serialised. When two strings spell the same
2168
+ instant, the first argument's spelling wins. No spec covers the tie, so this is a GMT rule.
2169
+ - **`start === end` is a valid empty interval.** It contains no instant, overlaps only an interval
2170
+ it lies strictly inside, and adds nothing to a sum. An inverted interval returns the sentinel:
2171
+ `false`, `null`, `[]` or `""`.
2172
+ - **`sumIntervals` is covered time.** It returns the length of the union, with overlaps counted
2173
+ once, as an exact duration with hours as the largest unit. That is exactly what TC39
2174
+ `Instant.prototype.until(…, { largestUnit: "hour" })` produces, because an instant has no
2175
+ calendar that says how long a day is. So `PT49H30M` is never written `P2DT1H30M`, a New York
2176
+ spring-forward day is `PT23H`, and `sumIntervals([])` is `"PT0S"`. The sum is exact past
2177
+ 2^53 nanoseconds.
2178
+ - **`[]` means two things.** From `mergeIntervals`, `subtractIntervals` and `splitIntervalAt` it
2179
+ is both a legitimate result (a fully covered subtraction) and the invalid-input sentinel.
2180
+ `isValidInterval` tells them apart.
2181
+
1600
2182
  ## API Surface
1601
2183
 
1602
2184
  For the complete API listing, see the namespace documentation on GitHub:
@@ -1605,9 +2187,37 @@ For the complete API listing, see the namespace documentation on GitHub:
1605
2187
  - [Plain API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/plain) — timezone-free operations
1606
2188
  - [Zoned API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/zoned) — IANA timezone-aware operations
1607
2189
  - [Unix API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/unix) — Unix epoch utilities
2190
+ - [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
2191
+ - [Span API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/span) — elapsed and wall-clock durations as raw numbers
2192
+ - [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
2193
+ - [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
2194
+ - [Instant API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/instant) — the instant-plus-offset pair, and explicit local-time resolution
1608
2195
  - [UTC API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/utc) — UTC instant utilities
1609
2196
  - [Regex API](https://github.com/northguild/gmt/tree/main/packages/gmt/src/regex) — composable regex patterns
1610
2197
 
2198
+ ## AI Agent Skills
2199
+
2200
+ `@northguild/gmt` ships four lightweight TanStack Intent skill files
2201
+ (`skills/gmt-basics/`, `gmt-arithmetic/`, `gmt-timezone/`,
2202
+ `gmt-integration/`). Each is a ~30–60 line routing pointer that carries core
2203
+ rules and common pitfalls, then directs the agent to **this README** and the
2204
+ source JSDoc for full API signatures and code examples.
2205
+
2206
+ | Skill | Task area | Load with |
2207
+ | --- | --- | --- |
2208
+ | `gmt-basics` | Get current values, parse, format, relative time, compare, validate | `intent load @northguild/gmt#gmt-basics` |
2209
+ | `gmt-arithmetic` | Add/subtract, diff, durations, interval range math | `intent load @northguild/gmt#gmt-arithmetic` |
2210
+ | `gmt-timezone` | Zoned arithmetic, formatting, DST disambiguation | `intent load @northguild/gmt#gmt-timezone` |
2211
+ | `gmt-integration` | Cache keys, router/query params, lint package selection | `intent load @northguild/gmt#gmt-integration` |
2212
+
2213
+ Contributor skills (issue-creation, pr-contribution, new-method-implementation,
2214
+ unit-test-generation, api-expansion-workflow) live under
2215
+ `skills/contributor/` and are excluded from the npm tarball via
2216
+ `.npmignore` — load them only when contributing to the library itself.
2217
+
2218
+ See the [dox site's Skills guide](https://gmt-dox.northguild.workers.dev/guides/integration/skills/)
2219
+ for full install and discovery instructions.
2220
+
1611
2221
  ## Agent Prompt
1612
2222
 
1613
2223
  When working with `@northguild/gmt`, follow these rules:
@@ -1617,7 +2227,7 @@ When working with `@northguild/gmt`, follow these rules:
1617
2227
  3. **Invalid input returns a sentinel, never throws.** `""` for strings, `null` for numbers, `false` for booleans, `[]` for arrays.
1618
2228
  4. **Wrap all Temporal calls in `try-catch`.** `.from()`, `.add()`, `.since()`, etc. throw `RangeError` on bad input.
1619
2229
  5. **Keep `plain/` and `zoned/` strictly separate.** Never mix `PlainDateTime` and `ZonedDateTime`.
1620
- 6. **Full locale matrix for any locale-aware function.** 17 locales, explicit rows, `hasFullIcu` ternaries where output differs.
2230
+ 6. **Full locale matrix for any locale-aware function.** 17 locales, explicit rows, `expectOneOfIcu`/`expectDateTimeEqual` (from `src/test/icuVariants.ts`) where CLDR wording differs.
1621
2231
  7. **Use pre-built mocks for error-path tests.** See `packages/gmt/src/test/mocks`.
1622
2232
  8. **JSDoc with `@example` on every public function.** Cover valid, invalid, and edge-case inputs.
1623
2233