UNPKG

@burglekitt/gmt

Version:

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

67 lines (66 loc) 3.22 kB
import { Temporal } from "@js-temporal/polyfill"; import { isValidUnixEpochPair, resolveUnixTimeZone, } from "../../internal/resolveUnixTimeZone.js"; import { getLargestDateTimeDurationUnit } from "../../plain/calculate/getLargestDateTimeDurationUnit.js"; import { isValidDateTimeDurationUnit } from "../../plain/validate/index.js"; /** * Return the difference between two Unix timestamps measured in the given unit. * * - Uses Temporal.Instant.until() to calculate the difference. * - Supports single unit or array of units. * - Returns null for invalid input. * * `smallestUnit`, `roundingIncrement`, and `roundingMode` control optional rounding of the result, * per Temporal's DifferenceOptions — e.g. `{ smallestUnit: "hour", roundingMode: "halfExpand" }` * rounds the difference to the nearest hour before extracting the requested unit. * - When `units` is an array, `smallestUnit` must not be coarser than the largest unit in the * array (e.g. `["day", "hour"]` with `smallestUnit: "week"`) — this combination is rejected by * Temporal and returns null, same as other invalid input. * * @param value1 first Unix timestamp * @param value2 second Unix timestamp * @param units DateTimeDurationUnit | DateTimeDurationUnit[] to measure the difference * @param options optional: epochUnit ("seconds" | "milliseconds"), timeZone (IANA), smallestUnit, roundingIncrement, roundingMode (Temporal.DifferenceOptions rounding controls) * @returns numeric difference in the requested unit, or null on invalid input * * @example diffUnix(1706745600000, 1706659200000, "day") // 1 * @example diffUnix(1706745600, 1706659200, "day", { epochUnit: "seconds" }) // 1 * @example diffUnix(0, -86400000, "day") // 1 (Jan 1 1970 - Dec 31 1969 = 1 day) */ export function diffUnix(value1, value2, units, options) { const epochUnit = options?.epochUnit ?? "milliseconds"; const timeZone = resolveUnixTimeZone(options?.timeZone); if (!timeZone) return null; const isSingleUnit = !Array.isArray(units); const validUnits = isSingleUnit ? isValidDateTimeDurationUnit(units) : units.every(isValidDateTimeDurationUnit); if (!validUnits) { return null; } if (!isValidUnixEpochPair(value1, value2)) { return null; } try { const instant1 = Temporal.Instant.fromEpochMilliseconds(epochUnit === "seconds" ? value1 * 1000 : value1); const instant2 = Temporal.Instant.fromEpochMilliseconds(epochUnit === "seconds" ? value2 * 1000 : value2); const zdt1 = instant1.toZonedDateTimeISO(timeZone); const zdt2 = instant2.toZonedDateTimeISO(timeZone); const duration = zdt1.until(zdt2, { largestUnit: isSingleUnit ? units : getLargestDateTimeDurationUnit(units), smallestUnit: options?.smallestUnit, roundingIncrement: options?.roundingIncrement, roundingMode: options?.roundingMode, }); if (isSingleUnit) { return duration[units] ?? 0; } return units.reduce((result, unit) => { result[unit] = duration[unit] ?? 0; return result; }, {}); } catch { return null; } }