41 lines
1.5 KiB
JavaScript
41 lines
1.5 KiB
JavaScript
const offsetFormatCache = {};
|
|
const offsetCache = {};
|
|
|
|
/**
|
|
* The function extracts UTC offset in minutes from the given date in specified
|
|
* time zone.
|
|
*
|
|
* Unlike `Date.prototype.getTimezoneOffset`, this function returns the value
|
|
* mirrored to the sign of the offset in the time zone. For Asia/Singapore
|
|
* (UTC+8), `tzOffset` returns 480, while `getTimezoneOffset` returns -480.
|
|
*
|
|
* @param timeZone - Time zone name (IANA or UTC offset)
|
|
* @param date - Date to check the offset for
|
|
*
|
|
* @returns UTC offset in minutes
|
|
*/
|
|
export function tzOffset(timeZone, date) {
|
|
try {
|
|
const format = offsetFormatCache[timeZone] ||= new Intl.DateTimeFormat("en-GB", {
|
|
timeZone,
|
|
hour: "numeric",
|
|
timeZoneName: "longOffset"
|
|
}).format;
|
|
const offsetStr = format(date).split('GMT')[1] || '';
|
|
if (offsetStr in offsetCache) return offsetCache[offsetStr];
|
|
return calcOffset(offsetStr, offsetStr.split(":"));
|
|
} catch {
|
|
// Fallback to manual parsing if the runtime doesn't support ±HH:MM/±HHMM/±HH
|
|
// See: https://github.com/nodejs/node/issues/53419
|
|
if (timeZone in offsetCache) return offsetCache[timeZone];
|
|
const captures = timeZone?.match(offsetRe);
|
|
if (captures) return calcOffset(timeZone, captures.slice(1));
|
|
return NaN;
|
|
}
|
|
}
|
|
const offsetRe = /([+-]\d\d):?(\d\d)?/;
|
|
function calcOffset(cacheStr, values) {
|
|
const hours = +values[0];
|
|
const minutes = +(values[1] || 0);
|
|
return offsetCache[cacheStr] = hours > 0 ? hours * 60 + minutes : hours * 60 - minutes;
|
|
} |