@burglekitt/gmt
Version:
Temporal-based date and time utilities with timezone support and polyfill integration
63 lines (62 loc) • 2.69 kB
JavaScript
import { Temporal } from "@js-temporal/polyfill";
import { isValidDateTimeUnit } from "../../plain/index.js";
import { getSystemTimeZone } from "../../zoned/get/index.js";
import { isValidTimeZone } from "../../zoned/validate/index.js";
/**
* Round a Unix timestamp to the specified unit.
*
* - Converts to ZonedDateTime, rounds, converts back to epoch.
* - Supports: "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond".
* - Date units ("year", "month", "week") are not supported by the Temporal polyfill's ZonedDateTime.round() — they return null.
* - Returns null for invalid input.
*
* @param value Unix timestamp (number)
* @param options Rounding options: smallestUnit, optional roundingIncrement, roundingMode, epochUnit, timeZone
* @returns Rounded Unix epoch number, or null on invalid input
*
* @example roundUnix(1706659200000, { smallestUnit: "hour" }) // 1706662800000 (rounded up to next hour)
* @example roundUnix(1706659200000, { smallestUnit: "day", epochUnit: "seconds" }) // 1706640000 (start of day in seconds)
* @example roundUnix(1706659200000, { smallestUnit: "hour", roundingIncrement: 2 }) // 1706662800000 (rounded to nearest 2-hour mark)
* @example roundUnix(-86400000, { smallestUnit: "day" }) // -86400000 (start of day for negative timestamp)
* @example roundUnix("invalid", { smallestUnit: "hour" }) // null
* @example roundUnix(NaN, { smallestUnit: "hour" }) // null
*/
export function roundUnix(value, options) {
const { smallestUnit, roundingIncrement, roundingMode, epochUnit = "milliseconds", timeZone = getSystemTimeZone(), } = options;
if (!timeZone ||
!isValidDateTimeUnit(smallestUnit) ||
!isValidTimeZone(timeZone)) {
return null;
}
if (!Number.isFinite(value) || !Number.isInteger(value)) {
return null;
}
// Polyfill limitation: year/month/week not supported for ZonedDateTime.round()
const supportedUnits = [
"day",
"hour",
"minute",
"second",
"millisecond",
"microsecond",
"nanosecond",
];
if (!supportedUnits.includes(smallestUnit)) {
return null;
}
try {
let epochMs = epochUnit === "seconds" ? value * 1000 : value;
const instant = Temporal.Instant.fromEpochMilliseconds(epochMs);
const source = instant.toZonedDateTimeISO(timeZone);
const result = source.round({
smallestUnit,
roundingIncrement,
roundingMode,
});
epochMs = result.epochMilliseconds;
return epochUnit === "seconds" ? Math.floor(epochMs / 1000) : epochMs;
}
catch {
return null;
}
}