Skip to content

Changelog - 2026-10-15 ​

TemporalHelper covers what a dayjs wrapper did ​

Feature Bug Fix Breaking Change

In one line. An app can call TemporalHelper directly instead of keeping its own date wrapper.

typescript
temporal.format({ value: row.paidAt, pattern: 'DD/MM/YYYY', fallback: '--' }); // '--' when paidAt is null
temporal.startOf({ value, unit: TemporalUnits.QUARTER });
temporal.set({ value, hour: 9, minute: 0 });
temporal.isBetween({ value, from, to, inclusivity: '[]' });
temporal.formatDuration({ milliseconds, pattern: 'mm:ss' });
const english = temporal.clone({ locale: 'en-US' });
temporal.fromWallClock({ value: pickedDay }); // the picked day, kept in the helper's zone

What changed ​

AreaNow
nullBug fix. toDate, compare and format threw nothing and answered 1970 for null, and isValid(null) was true. Now null is a 400, and isValid is false
Reading without throwingtryToDate({ value }) answers null; format({ ..., fallback }) writes the fallback for a value that is not a date. The constructor takes a fallback too, used by format and formatIntl when a call passes none
QuarterTemporalUnits.QUARTER for add, startOf, endOf, diff, compare and relative; the Q token writes 1-4
setReplaces wall-clock fields in the zone; the day clamps to the month
ComparisonsisBefore, isAfter, isSame, isSameOrBefore, isSameOrAfter, and isBetween with (), [], [) or (]
DurationsformatDuration takes a clock-face pattern (HH:mm:ss, mm:ss) and maxUnits
Localesclone({ locale, tz }); the constructor's names replaces Intl's month and weekday names and AM/PM marker per locale, and a call in another tag of the same language uses the entry; the dd token writes the narrowest weekday
Date pickersfromWallClock({ value }) moves a picker's host-zone wall clock into the helper's zone; toWallClock feeds it back; TemporalHelper.getHostTz() names the host's zone
addAn hour or smaller takes a fraction: 1.5 hours is 90 minutes. A calendar unit still takes whole numbers only
The zone optionBreaking. timeZone is now tz everywhere on TemporalHelper and ITemporalAdapter: the constructor, every per-call option, getTz(), TemporalHelper.getHostTz(), DEFAULT_TEMPORAL_TZ, and getOffsetMilliseconds({ value, tz }). Intl's own timeZone option is unchanged
StructureITemporalHelper (the contract), AbstractTemporalHelper (the zone machinery) and TemporalHelper (the operations). The internals are classes in scope folders: TemporalCalendar with one shift method, TemporalTokens, TemporalPattern, TemporalLocale, TemporalDuration
diffBehavior change. Counted as dayjs counts: days by the wall clocks, months anchored on the later day of the month. diff(a, b) is always -diff(b, a)

Who is affected ​

  • Every caller that passes a zone. Rename timeZone to tz: new TemporalHelper({ tz: 'Asia/Ho_Chi_Minh' }), format({ value, tz: 'UTC' }), getTimeZone() to getTz(). A custom adapter reads opts.tz.
  • Code that passed null and got 1970. It now gets a 400. Use tryToDate, isValid or format({ fallback }) where a date may be missing.
  • A diff in months or years that ends on a month end. A few month-end cases change by one. Example: from 2026-02-28 back to 2024-02-29 was -23 months and is now -24, as dayjs answers. It matched 2,160 of 2,160 dayjs answers on a set of month-end and day-edge instants.
  • Everyone else. No change.

Files: packages/helpers/src/modules/temporal/helper.ts, packages/helpers/src/modules/temporal/calendar/calendar.ts, packages/helpers/src/modules/temporal/pattern/tokens.ts, packages/helpers/src/modules/temporal/duration/duration.ts