2023-11-29 07:34:34 +01:00
|
|
|
const parsable_formats = new Map<string, Intl.DateTimeFormat>();
|
2023-11-28 23:43:06 +01:00
|
|
|
|
2023-11-29 07:34:34 +01:00
|
|
|
function get_parsable_format(time_zone: string): Intl.DateTimeFormat {
|
|
|
|
let format = parsable_formats.get(time_zone);
|
2023-11-28 23:43:06 +01:00
|
|
|
if (format === undefined) {
|
|
|
|
format = new Intl.DateTimeFormat("en-US", {
|
2023-11-29 07:34:34 +01:00
|
|
|
year: "numeric",
|
|
|
|
month: "numeric",
|
|
|
|
day: "numeric",
|
|
|
|
hour: "numeric",
|
|
|
|
minute: "numeric",
|
|
|
|
second: "numeric",
|
|
|
|
hourCycle: "h23",
|
2023-11-28 23:43:06 +01:00
|
|
|
timeZone: time_zone,
|
|
|
|
});
|
2023-11-29 07:34:34 +01:00
|
|
|
parsable_formats.set(time_zone, format);
|
2023-11-28 23:43:06 +01:00
|
|
|
}
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get the given time zone's offset in milliseconds at the given date. */
|
|
|
|
export function get_offset(date: number | Date, time_zone: string): number {
|
2023-11-29 07:34:34 +01:00
|
|
|
const parts = Object.fromEntries(
|
|
|
|
get_parsable_format(time_zone)
|
|
|
|
.formatToParts(date)
|
|
|
|
.map((part) => [part.type, part.value]),
|
|
|
|
);
|
|
|
|
return (
|
|
|
|
Date.UTC(
|
|
|
|
Number(parts.year),
|
|
|
|
Number(parts.month) - 1,
|
|
|
|
Number(parts.day),
|
|
|
|
Number(parts.hour),
|
|
|
|
Number(parts.minute),
|
|
|
|
Number(parts.second),
|
|
|
|
) -
|
|
|
|
(Number(date) - (Number(date) % 1000))
|
|
|
|
);
|
2023-11-28 23:43:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Get the start of the day for the given date in the given time zone. */
|
|
|
|
export function start_of_day(date: number | Date, time_zone: string): Date {
|
|
|
|
const offset = get_offset(date, time_zone);
|
|
|
|
let t = Number(date) + offset;
|
|
|
|
t -= t % 86400000;
|
|
|
|
return new Date(t - get_offset(new Date(t - offset), time_zone));
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Get the number of calendar days between the given dates (ignoring times) in
|
|
|
|
* the given time zone. */
|
|
|
|
export function difference_in_calendar_days(
|
|
|
|
left: number | Date,
|
|
|
|
right: number | Date,
|
|
|
|
time_zone: string,
|
|
|
|
): number {
|
|
|
|
return Math.round(
|
|
|
|
(start_of_day(left, time_zone).getTime() - start_of_day(right, time_zone).getTime()) /
|
|
|
|
86400000,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Are the given dates in the same day in the given time zone? */
|
|
|
|
export function is_same_day(left: number | Date, right: number | Date, time_zone: string): boolean {
|
|
|
|
return start_of_day(left, time_zone).getTime() === start_of_day(right, time_zone).getTime();
|
|
|
|
}
|