Files
multica/packages/views/common/format-in-time-zone.ts
YYClaw 465546b83b feat(autopilots): redesign the autopilot schedule editor (#5457)
Replace the free-form trigger-config form with a structured schedule
editor built on an orthogonal cron model: separate frequency, time, and
day-of-week/day-of-month dimensions map to and from cron expressions via
a dedicated grammar and mapping layer, with validation and a
human-readable describe() summary. The grammar suite drives the editor
against a combinatorially generated corpus of 51,755 distinct cron
expressions - every token form of every field, crossed - each judged
against a reference robfig/cron v3 parser.

Add a server-side /autopilot/cron-preview endpoint (plus schema and
React Query hook) so the editor shows upcoming run times, and echo
wildcard-carrying cron lists correctly instead of collapsing them.

Supporting pieces: timezone-aware formatting helper, segmented-toggle
and debounced-value utilities, a reworked time-input, and refreshed
en/ja/ko/zh-Hans locale strings.
2026-07-17 16:44:31 +08:00

32 lines
1.3 KiB
TypeScript

/** Render an instant on the schedule's clock, not the reader's: an autopilot
* that says "18:00 (America/Los_Angeles)" must not print its next run as 09:00
* to a reader in UTC+8. The wording — month name, 12- or 24-hour dial — is the
* reader's locale.
*
* Two things can go wrong, and neither may take a page down:
* - a zone this browser's ICU data does not carry (an autopilot saved from a
* newer build, opened in an older packaged desktop one) → local time: wrong by
* an offset, but still a time of day, which a raw ISO string is not;
* - a timestamp that is not a date at all (backend drift) → the string itself.
* Intl throws on an invalid Date, so this is checked before formatting, not
* caught after: a catch would only run the same throwing call again. */
export function formatInTimeZone(
iso: string,
timeZone: string | undefined,
locale: string,
): string {
const at = new Date(iso);
if (Number.isNaN(at.getTime())) return iso;
const options: Intl.DateTimeFormatOptions = {
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
};
try {
return new Intl.DateTimeFormat(locale, { ...options, timeZone }).format(at);
} catch {
return new Intl.DateTimeFormat(locale, options).format(at);
}
}