Files
native/src/lib/vacation-dates.ts
T

55 lines
2.2 KiB
TypeScript

// Date helpers for the vacation responder. RFC 8621 `VacationResponse.fromDate`
// / `toDate` are UTCDate values ("2026-03-01T09:00:00Z"); the user types a
// local wall-clock time. Mirrors the webmail, which feeds a datetime-local
// input through `new Date(local).toISOString()` and displays with
// `utcToLocalDatetime`.
const pad = (n: number) => String(n).padStart(2, '0');
/** Accepts "YYYY-MM-DD", "YYYY-MM-DD HH:MM", "YYYY-MM-DDTHH:MM[:SS]". */
export function parseLocalInput(input: string): Date | null {
const trimmed = input.trim();
if (!trimmed) return null;
const m = /^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?$/.exec(trimmed);
if (!m) return null;
const [, y, mo, d, hh = '0', mm = '0', ss = '0'] = m;
const date = new Date(Number(y), Number(mo) - 1, Number(d), Number(hh), Number(mm), Number(ss));
// Reject overflowed fields such as 2026-13-45.
if (
date.getFullYear() !== Number(y) ||
date.getMonth() !== Number(mo) - 1 ||
date.getDate() !== Number(d) ||
date.getHours() !== Number(hh) ||
date.getMinutes() !== Number(mm)
) {
return null;
}
return date;
}
/** Local wall-clock input -> JMAP UTCDate ISO string (or null when empty). */
export function localInputToUtcIso(input: string): string | null {
const date = parseLocalInput(input);
return date ? date.toISOString() : null;
}
/** Whether a non-empty input is syntactically valid. Empty is valid (unset). */
export function isValidLocalInput(input: string): boolean {
return !input.trim() || parseLocalInput(input) !== null;
}
/** JMAP UTCDate -> "YYYY-MM-DD HH:MM" in the device's local time zone. */
export function utcIsoToLocalInput(iso: string | null | undefined): string {
if (!iso) return '';
const d = new Date(iso);
if (Number.isNaN(d.getTime())) return '';
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
/** Normalise a stored date for change detection (null when unset/invalid). */
export function normalizeUtcIso(iso: string | null | undefined): string | null {
if (!iso) return null;
const d = new Date(iso);
return Number.isNaN(d.getTime()) ? null : d.toISOString();
}