UNPKG

@burglekitt/gmt

Version:

Temporal-based date and time utilities with timezone support and polyfill integration

86 lines (85 loc) 3.45 kB
import { Temporal } from "@js-temporal/polyfill"; import { getSystemTimeZone } from "../../zoned/get/index.js"; import { isValidTimeZone } from "../../zoned/validate/index.js"; import { resolveDurationUnit } from "../../internal/index.js"; /** * Split a Unix epoch interval into sub-intervals of `amount × unit`. * * - Returns an array of `{ start, end }` records that tile the interval. * - The final sub-interval is trimmed so its `end` never exceeds the original `end`. * - Returns `[{ start, end }]` when `start === end` (zero-length interval). * - Returns `[]` on invalid input (non-finite/non-integer start/end, unsupported unit, * non-positive amount, or invalid timeZone). * * Uses the system timeZone for calendar-unit arithmetic (consistent with `addUnix`). * * @param start Unix epoch value (seconds or milliseconds) — interval start * @param end Unix epoch value (seconds or milliseconds) — interval end * @param unit duration unit string — any `DateTimeDurationUnit` * @param amount positive number of units per step * @returns array of `{ start, end }` records, or [] on invalid input * * @example splitIntervalByUnitUnix(0, 86400000, "hour", 6) // [{ start: 0, end: 21600000 }, { start: 21600000, end: 43200000 }, { start: 43200000, end: 64800000 }, { start: 64800000, end: 86400000 }] * @example splitIntervalByUnitUnix(0, 3600000, "hour", 1) // [{ start: 0, end: 3600000 }] * @example splitIntervalByUnitUnix(0, 0, "hour", 1) // [{ start: 0, end: 0 }] * @example splitIntervalByUnitUnix(0, 86400000, "hour", 0) // [] * @example splitIntervalByUnitUnix("invalid", 86400000, "hour", 1) // [] */ export function splitIntervalByUnitUnix(start, end, unit, amount) { if (typeof start !== "number" && typeof start !== "string") { return []; } if (typeof end !== "number" && typeof end !== "string") { return []; } const startMs = typeof start === "number" ? start : Number(start); const endMs = typeof end === "number" ? end : Number(end); if (!Number.isFinite(startMs) || !Number.isInteger(startMs)) { return []; } if (!Number.isFinite(endMs) || !Number.isInteger(endMs)) { return []; } if (startMs > endMs) { return []; } if (typeof unit !== "string") { return []; } const resolvedUnit = resolveDurationUnit(unit); if (!resolvedUnit) { return []; } if (!Number.isFinite(amount) || amount <= 0) { return []; } try { const timeZone = getSystemTimeZone(); if (!timeZone || !isValidTimeZone(timeZone)) { return []; } if (startMs === endMs) { return [{ start: startMs, end: endMs }]; } const result = []; for (let currentMs = startMs; currentMs < endMs;) { const instant = Temporal.Instant.fromEpochMilliseconds(currentMs); const zoned = instant.toZonedDateTimeISO(timeZone); const next = zoned.add({ [resolvedUnit]: amount }).toInstant(); const nextMs = next.epochMilliseconds; if (nextMs === currentMs) { return []; } const sliceEndMs = nextMs > endMs ? endMs : nextMs; result.push({ start: currentMs, end: sliceEndMs, }); currentMs = nextMs; } return result; } catch { return []; } }