Skip to content

Changelog - 2026-10-14 ​

TemporalHelper runs in every browser and speaks a locale ​

Feature

In one line. A browser app can drop dayjs: TemporalHelper now covers names, relative time and comparisons, without needing Temporal.

typescript
import { TemporalHelper, TemporalUnits } from '@venizia/ignis-helpers/temporal';

const temporal = new TemporalHelper({ timeZone: 'Asia/Ho_Chi_Minh', locale: 'vi-VN' });

temporal.format({ value: order.createdAt, pattern: 'dddd, D MMMM YYYY hh:mm A' }); // 'Thứ Bảy, 19 Tháng 9 2026 12:00 CH'
temporal.relative({ value: order.createdAt }); // '3 giờ trước'
temporal.relative({ value: order.createdAt, unit: TemporalUnits.DAY, numeric: 'auto' }); // 'Hôm qua'
temporal.diff({ value: end, other: start, unit: TemporalUnits.MONTH }); // whole months
temporal.tryParse({ value: input }); // a Date, or null
temporal.formatDuration({ milliseconds: 91_800_000 }); // '1d1h30m'

What changed ​

Before, the default adapter needed the runtime's Temporal, which not every browser ships, and the pattern dialect wrote digits only.

A browser-safe adapter. IntlTemporalAdapter reads offsets through Intl.DateTimeFormat alone. It matched NativeTemporalAdapter on 240,000 random instants across 12 zones, years 1 to 9999. A helper built without an adapter uses Temporal where the runtime has it and IntlTemporalAdapter everywhere else.

Writing in a locale. The constructor takes locale (default en-US); every call can pass its own.

NewWhat it writes
MMMM / MMMMonth name, long / short
dddd / dddWeekday name, long / short
hh / h, A / a12-hour clock and its AM/PM marker
L / LLThe date in the locale's numeric order / its long form
formatIntl({ value, options })Any Intl.DateTimeFormat options, in the helper's zone

These tokens write only. parse stays numeric and strict, and a parse pattern holding one throws a 500.

New methods.

MethodAnswers
relative({ value, base, unit, numeric })3 hours ago, yesterday, through Intl.RelativeTimeFormat. With a unit, it counts the unit's boundaries in the zone
diff({ value, other, unit })Whole units, truncated. The inverse of add: diff(add(x, n), x) is n, outside a zone that skipped a whole day
compare({ value, other, unit })-1, 0 or 1, at a unit's granularity in the zone
tryParse(...) / isValid({ value })null / false where parse would throw a 400
durationParts({ milliseconds }) / formatDuration(...){ days, hours, ... } / 1d1h30m, or the locale's unit words

Who is affected ​

  • A browser app on a runtime without Temporal. new TemporalHelper() used to throw; it now works through Intl. Nothing to change.
  • A pattern with MMM, MMMM or dddd. It used to throw an "unknown token" 500 when formatting; it now writes the name. As a parse pattern it still throws.
  • Everyone else. No change. Existing tokens, units, strict parsing and the 1-9999 year range behave as before.

Files: packages/helpers/src/modules/temporal/adapters/intl.ts, packages/helpers/src/modules/temporal/locale/locale.ts, packages/helpers/src/modules/temporal/duration/duration.ts, packages/helpers/src/modules/temporal/helper.ts, packages/helpers/src/modules/temporal/pattern/tokens.ts