537 lines
20 KiB
JavaScript
537 lines
20 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.TimezoneOffsetTagnames = exports.defaultVideosToUTC = exports.UnsetZoneName = exports.UnsetZone = exports.UnsetZoneOffsetMinutes = void 0;
|
|
exports.isUTC = isUTC;
|
|
exports.isZoneUnset = isZoneUnset;
|
|
exports.isZoneValid = isZoneValid;
|
|
exports.isZone = isZone;
|
|
exports.normalizeZone = normalizeZone;
|
|
exports.zoneToShortOffset = zoneToShortOffset;
|
|
exports.validTzOffsetMinutes = validTzOffsetMinutes;
|
|
exports.offsetMinutesToZoneName = offsetMinutesToZoneName;
|
|
exports.extractZone = extractZone;
|
|
exports.incrementZone = incrementZone;
|
|
exports.extractTzOffsetFromTags = extractTzOffsetFromTags;
|
|
exports.extractTzOffsetFromDatestamps = extractTzOffsetFromDatestamps;
|
|
exports.extractTzOffsetFromTimeStamp = extractTzOffsetFromTimeStamp;
|
|
exports.inferLikelyOffsetMinutes = inferLikelyOffsetMinutes;
|
|
exports.extractTzOffsetFromUTCOffset = extractTzOffsetFromUTCOffset;
|
|
exports.equivalentZones = equivalentZones;
|
|
exports.getZoneName = getZoneName;
|
|
const luxon_1 = require("luxon");
|
|
const Array_1 = require("./Array");
|
|
const BinaryField_1 = require("./BinaryField");
|
|
const CapturedAtTagNames_1 = require("./CapturedAtTagNames");
|
|
const DefaultExifToolOptions_1 = require("./DefaultExifToolOptions");
|
|
const ExifDate_1 = require("./ExifDate");
|
|
const ExifDateTime_1 = require("./ExifDateTime");
|
|
const ExifTime_1 = require("./ExifTime");
|
|
const Lazy_1 = require("./Lazy");
|
|
const Maybe_1 = require("./Maybe");
|
|
const Number_1 = require("./Number");
|
|
const Object_1 = require("./Object");
|
|
const Pick_1 = require("./Pick");
|
|
const String_1 = require("./String");
|
|
// When should we use "tz" vs "zone"?
|
|
// 1. "tz" refers to the entire database/system - the "tz database" (also called
|
|
// tzdata or zoneinfo)
|
|
// 2. "zone" refers to a specific time zone entry within that database
|
|
// So... we really should be using "zone" everywhere, not "tz".
|
|
// Unique values from
|
|
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones, excluding those
|
|
// that have not been used for at least 50 years.
|
|
const ValidTimezoneOffsets = [
|
|
// "-12:00", // not used for any populated land
|
|
"-11:00",
|
|
// "-10:30", // used by Hawaii 1896-1947
|
|
"-10:00",
|
|
"-09:30",
|
|
"-09:00",
|
|
"-08:30",
|
|
"-08:00",
|
|
"-07:00",
|
|
"-06:00",
|
|
"-05:00",
|
|
"-04:30", // used by Venezuela 1912-1965 and 2007-2016
|
|
"-04:00",
|
|
"-03:30",
|
|
"-03:00",
|
|
"-02:30",
|
|
"-02:00",
|
|
"-01:00",
|
|
// "-00:44", // used by Liberia until 1972
|
|
// "-00:25:21", // Ireland 1880-1916 https://en.wikipedia.org/wiki/UTC%E2%88%9200:25:21
|
|
"+00:00",
|
|
// "+00:20", // used by Netherlands until 1940
|
|
// "+00:30", // used by Switzerland until 1936
|
|
"+01:00",
|
|
// "+01:24", // used by Warsaw until 1915
|
|
// "+01:30", // used by some southern African countries until 1903
|
|
"+02:00",
|
|
// "+02:30", // archaic Moscow time
|
|
"+03:00",
|
|
"+03:30",
|
|
"+04:00",
|
|
"+04:30",
|
|
// "+04:51", // used by Bombay until 1955 https://en.wikipedia.org/wiki/UTC%2B04:51
|
|
"+05:00",
|
|
"+05:30",
|
|
// "+05:40", // used by Nepal until 1920
|
|
"+05:45", // Nepal
|
|
"+06:00",
|
|
"+06:30",
|
|
"+07:00",
|
|
// "+07:20", // used by Singapore and Malaya until 1941
|
|
"+07:30", // used by Mayasia until 1982
|
|
"+08:00",
|
|
"+08:30", // used by North Korea until 2018
|
|
"+08:45", // used by Western Australia, but not in tz database
|
|
"+09:00",
|
|
"+09:30",
|
|
"+09:45", // used by Western Australia, but not in tz database
|
|
"+10:00",
|
|
"+10:30",
|
|
"+11:00",
|
|
"+12:00",
|
|
"+12:45", // New Zealand islands
|
|
"+13:00", // New Zealand and Antarctica
|
|
"+13:45", // New Zealand islands
|
|
"+14:00",
|
|
];
|
|
function offsetToMinutes(offset) {
|
|
const [h, m] = offset.split(":").map(Number);
|
|
// we can't just return `h * 60 + m`: that doesn't work with negative
|
|
// offsets (minutes will be positive but hours will be negative)
|
|
const sign = h < 0 ? -1 : 1;
|
|
return h * 60 + sign * m;
|
|
}
|
|
const ValidOffsetMinutes = (0, Lazy_1.lazy)(() => new Set(ValidTimezoneOffsets.map(offsetToMinutes)));
|
|
/**
|
|
* Zone instances with this offset are a placeholder for being "unset".
|
|
*/
|
|
exports.UnsetZoneOffsetMinutes = -1;
|
|
/**
|
|
* This is a placeholder for dates where the zone is unknown/unset, because
|
|
* Luxon doesn't officially support "unset" zones.
|
|
*/
|
|
exports.UnsetZone = luxon_1.Info.normalizeZone(exports.UnsetZoneOffsetMinutes);
|
|
/**
|
|
* Zone instances with this name are a placeholder for being "unset".
|
|
*/
|
|
exports.UnsetZoneName = exports.UnsetZone.name;
|
|
const Zulus = [
|
|
luxon_1.FixedOffsetZone.utcInstance,
|
|
0,
|
|
-0,
|
|
"UTC",
|
|
"GMT",
|
|
"Z",
|
|
"+0",
|
|
"+00:00",
|
|
"UTC+0",
|
|
"GMT+0",
|
|
"UTC+00:00",
|
|
"GMT+00:00",
|
|
];
|
|
function isUTC(zone) {
|
|
if (zone == null) {
|
|
return false;
|
|
}
|
|
if (typeof zone === "string" || typeof zone === "number") {
|
|
return Zulus.includes(zone);
|
|
}
|
|
if (zone instanceof luxon_1.Zone) {
|
|
return zone.isUniversal && zone.offset(Date.now()) === 0;
|
|
}
|
|
return false;
|
|
}
|
|
function isZoneUnset(zone) {
|
|
return zone.isUniversal && zone.offset(0) === exports.UnsetZoneOffsetMinutes;
|
|
}
|
|
function isZoneValid(zone) {
|
|
return (zone != null &&
|
|
zone.isValid &&
|
|
!isZoneUnset(zone) &&
|
|
Math.abs(zone.offset(Date.now())) < 14 * 60);
|
|
}
|
|
function isZone(zone) {
|
|
return ((0, Object_1.isObject)(zone) && (zone instanceof luxon_1.Zone || zone.constructor.name === "Zone"));
|
|
}
|
|
/**
|
|
* If `tzSource` matches this value, the tags are from a video, and we had to
|
|
* resort to assuming time fields are in UTC.
|
|
* @see https://github.com/photostructure/exiftool-vendored.js/issues/113
|
|
*/
|
|
exports.defaultVideosToUTC = "defaultVideosToUTC";
|
|
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones -- note that
|
|
// "WET" and "W-SU" are full TZs (!!!), and "America/Indiana/Indianapolis" is
|
|
// also a thing.
|
|
const IanaFormatRE = /^\w{2,15}(?:\/\w{3,15}){0,2}$/;
|
|
// Luxon requires fixed-offset zones to look like "UTC+H", "UTC-H",
|
|
// "UTC+H:mm", "UTC-H:mm":
|
|
const FixedFormatRE = /^UTC(?<sign>[+-])(?<hours>\d+)(?::(?<minutes>\d{2}))?$/;
|
|
function parseFixedOffset(str) {
|
|
const match = FixedFormatRE.exec(str)?.groups;
|
|
if (match == null)
|
|
return;
|
|
const h = (0, Number_1.toInt)(match.hours);
|
|
const m = (0, Number_1.toInt)(match.minutes) ?? 0;
|
|
if (h == null || h < 0 || h > 14 || m < 0 || m >= 60)
|
|
return;
|
|
const result = (match.sign === "-" ? -1 : 1) * (h * 60 + m);
|
|
return (ValidOffsetMinutes().has(result) ? result : undefined) ?? undefined;
|
|
}
|
|
/**
|
|
* @param input must be either a number, which is the offset in minutes, or a
|
|
* string in the format "UTC+H" or "UTC+HH:mm"
|
|
*/
|
|
function normalizeZone(input) {
|
|
if (input == null ||
|
|
(0, String_1.blank)(input) ||
|
|
(!(0, Number_1.isNumber)(input) && !(0, String_1.isString)(input) && !isZone(input))) {
|
|
return;
|
|
}
|
|
// wrapped in a try/catch as Luxon.settings.throwOnInvalid may be true:
|
|
try {
|
|
// This test and short-circuit may not be necessary, but it's cheap and
|
|
// explicit:
|
|
if (isUTC(input))
|
|
return luxon_1.FixedOffsetZone.utcInstance;
|
|
let z = input;
|
|
if ((0, String_1.isString)(z)) {
|
|
let s = z;
|
|
z = s = s.replace(/^(?:Zulu|Z|GMT)(?:\b|$)/i, "UTC");
|
|
// We also don't need to tease Info.normalizeZone with obviously
|
|
// non-offset inputs:
|
|
if ((0, String_1.blank)(s))
|
|
return;
|
|
const fixed = parseFixedOffset(s);
|
|
if (fixed != null) {
|
|
return luxon_1.Info.normalizeZone(fixed);
|
|
}
|
|
if (!IanaFormatRE.test(s)) {
|
|
return;
|
|
}
|
|
}
|
|
const result = luxon_1.Info.normalizeZone(z);
|
|
return isZoneValid(result) && result.name !== exports.UnsetZoneName
|
|
? result
|
|
: undefined;
|
|
}
|
|
catch {
|
|
return;
|
|
}
|
|
}
|
|
/**
|
|
* @param ts must be provided if the zone is not a fixed offset
|
|
* @return the zone offset (in "±HH:MM" format) for the given zone, or "" if
|
|
* the zone is invalid
|
|
*/
|
|
function zoneToShortOffset(zone, ts) {
|
|
return normalizeZone(zone)?.formatOffset(ts ?? Date.now(), "short") ?? "";
|
|
}
|
|
function validTzOffsetMinutes(tzOffsetMinutes) {
|
|
return (tzOffsetMinutes != null &&
|
|
(0, Number_1.isNumber)(tzOffsetMinutes) &&
|
|
tzOffsetMinutes !== exports.UnsetZoneOffsetMinutes &&
|
|
ValidOffsetMinutes().has(tzOffsetMinutes));
|
|
}
|
|
/**
|
|
* Returns a "zone name" (used by `luxon`) that encodes the given offset.
|
|
*/
|
|
function offsetMinutesToZoneName(offsetMinutes) {
|
|
if (!validTzOffsetMinutes(offsetMinutes)) {
|
|
return undefined;
|
|
}
|
|
if (offsetMinutes === 0)
|
|
return "UTC";
|
|
const sign = offsetMinutes < 0 ? "-" : "+";
|
|
const absMinutes = Math.abs(offsetMinutes);
|
|
const hours = Math.floor(absMinutes / 60);
|
|
const minutes = Math.abs(absMinutes % 60);
|
|
// luxon now renders simple hour offsets without padding:
|
|
return `UTC${sign}` + hours + (minutes === 0 ? "" : `:${(0, String_1.pad2)(minutes)}`);
|
|
}
|
|
function tzHourToOffset(n) {
|
|
return (0, Number_1.isNumber)(n) && validTzOffsetMinutes(n * 60)
|
|
? offsetMinutesToZoneName(n * 60)
|
|
: undefined;
|
|
}
|
|
// Accept "Z", "UTC+2", "UTC+02", "UTC+2:00", "UTC+02:00", "+2", "+02", and
|
|
// "+02:00". Require the sign (+ or -) and a ":" separator if there are
|
|
// minutes.
|
|
const tzRe = /(?<Z>Z)|((UTC)?(?<sign>[+-])(?<hours>\d\d?)(?::(?<minutes>\d\d))?)$/;
|
|
function extractOffsetFromHours(hourOffset) {
|
|
return (0, Number_1.isNumber)(hourOffset)
|
|
? (0, Maybe_1.map)(tzHourToOffset(hourOffset), (zone) => ({
|
|
zone,
|
|
tz: zone,
|
|
src: "hourOffset",
|
|
}))
|
|
: Array.isArray(hourOffset)
|
|
? extractOffsetFromHours(hourOffset[0])
|
|
: undefined;
|
|
}
|
|
/**
|
|
* Parse a timezone offset and return the offset minutes
|
|
*
|
|
* @param opts.stripTZA If false, do not strip off the timezone abbreviation
|
|
* (TZA) from the value. Defaults to true.
|
|
*
|
|
* @return undefined if the value cannot be parsed as a valid timezone offset
|
|
*/
|
|
function extractZone(value, opts) {
|
|
if (value == null ||
|
|
typeof value === "boolean" ||
|
|
value instanceof BinaryField_1.BinaryField ||
|
|
value instanceof ExifDate_1.ExifDate) {
|
|
return;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
// we only ever care about the first non-null value
|
|
return extractZone(value.find((ea) => ea != null));
|
|
}
|
|
if (value instanceof ExifDateTime_1.ExifDateTime || value instanceof ExifTime_1.ExifTime) {
|
|
return value.zone == null
|
|
? undefined
|
|
: {
|
|
zone: value.zone,
|
|
tz: value.zone,
|
|
src: value.constructor.name + ".zone",
|
|
};
|
|
}
|
|
if ((0, Number_1.isNumber)(value)) {
|
|
return extractOffsetFromHours(value);
|
|
}
|
|
if (typeof value !== "string" || (0, String_1.blank)(value)) {
|
|
// don't accept ExifDate, boolean, BinaryField, ResourceEvent, Struct, or
|
|
// Version instances:
|
|
return;
|
|
}
|
|
{
|
|
// If value is a proper timezone name, this may be easy!
|
|
const z = normalizeZone(value);
|
|
if (z != null) {
|
|
return { zone: z.name, tz: z.name, src: "normalizeZone" };
|
|
}
|
|
}
|
|
let str = value.trim();
|
|
// Some EXIF datetime will "over-specify" and include both the utc offset
|
|
// *and* the "time zone abbreviation"/TZA, like "PST" or "PDT". TZAs are
|
|
// between 2 (AT) and 5 (WEST) characters.
|
|
if (opts?.stripTZA !== false &&
|
|
// We only want to strip off the TZA if the input _doesn't_ end with "UTC"
|
|
// or "Z"
|
|
!/[.\d\s](?:UTC|Z)$/.test(str)) {
|
|
str = str.replace(/\s[a-z]{2,5}$/i, "");
|
|
}
|
|
{
|
|
if ((0, String_1.blank)(str))
|
|
return;
|
|
const z = normalizeZone(str);
|
|
if (z != null) {
|
|
return { zone: z.name, tz: z.name, src: "normalizeZone" };
|
|
}
|
|
}
|
|
const match = tzRe.exec(str);
|
|
const capturedGroups = match?.groups;
|
|
if (match != null && capturedGroups != null) {
|
|
const leftovers = str.slice(0, match.index);
|
|
if (capturedGroups.Z === "Z")
|
|
return {
|
|
zone: "UTC",
|
|
tz: "UTC",
|
|
src: "Z",
|
|
...((0, String_1.blank)(leftovers) ? {} : { leftovers }),
|
|
};
|
|
const offsetMinutes = (capturedGroups.sign === "-" ? -1 : 1) *
|
|
(parseInt(capturedGroups.hours ?? "0") * 60 +
|
|
parseInt(capturedGroups.minutes ?? "0"));
|
|
const zone = offsetMinutesToZoneName(offsetMinutes);
|
|
if (zone != null) {
|
|
return {
|
|
zone,
|
|
tz: zone,
|
|
src: "offsetMinutesToZoneName",
|
|
...((0, String_1.blank)(leftovers) ? {} : { leftovers }),
|
|
};
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
exports.TimezoneOffsetTagnames = [
|
|
"TimeZone",
|
|
// We **don't** look at "OffsetTime", as that is the offset for `ModifyDate`,
|
|
// which is the _file_ modification time.
|
|
// time zone for DateTimeOriginal, "-08:00"
|
|
"OffsetTimeOriginal",
|
|
// time zone for CreateDate, "-08:00"
|
|
"OffsetTimeDigitized",
|
|
// srsly who came up with these wholly inconsistent tag names? _why not just
|
|
// prefix tag names with "Offset"?!11_ SADNESS AND WOE
|
|
// 1 or 2 values: 1. The time zone offset of DateTimeOriginal from GMT in
|
|
// hours, 2. If present, the time zone offset of ModifyDate (which we
|
|
// ignore) @see https://www.exiftool.org/TagNames/EXIF.html
|
|
"TimeZoneOffset", // number | number[] | string
|
|
// We DON'T use "GeolocationTimezone" here, as at this layer in the glue
|
|
// factory we don't have access to the ExifTool option "ignoreZeroZeroLatLon"
|
|
];
|
|
function incrementZone(z, minutes) {
|
|
const norm = normalizeZone(z);
|
|
if (norm == null || true !== norm.isUniversal)
|
|
return;
|
|
const fixed = norm.offset(Date.now()); // < arg doesn't matter, it's universal
|
|
return (0, Number_1.isNumber)(fixed)
|
|
? luxon_1.FixedOffsetZone.instance(fixed + minutes)
|
|
: undefined;
|
|
}
|
|
function extractTzOffsetFromTags(t, opts) {
|
|
const adjustFn = opts?.adjustTimeZoneIfDaylightSavings ??
|
|
DefaultExifToolOptions_1.defaultAdjustTimeZoneIfDaylightSavings;
|
|
for (const tagName of exports.TimezoneOffsetTagnames) {
|
|
const offset = extractZone(t[tagName]);
|
|
if (offset == null)
|
|
continue;
|
|
// UGH. See https://github.com/photostructure/exiftool-vendored.js/issues/215
|
|
const minutes = adjustFn(t, offset.tz);
|
|
if (minutes != null) {
|
|
const adjustedZone = incrementZone(offset.tz, minutes);
|
|
if (adjustedZone != null)
|
|
return {
|
|
zone: adjustedZone.name,
|
|
tz: adjustedZone.name,
|
|
src: tagName + " (adjusted for DaylightSavings)",
|
|
};
|
|
}
|
|
// No fancy adjustments needed, just return the extracted zone:
|
|
return { ...offset, src: tagName };
|
|
}
|
|
return;
|
|
}
|
|
function extractTzOffsetFromDatestamps(t, opts) {
|
|
if (opts?.inferTimezoneFromDatestamps === true) {
|
|
for (const tagName of opts.inferTimezoneFromDatestampTags ?? []) {
|
|
if (t[tagName] != null) {
|
|
const offset = extractZone(t[tagName]);
|
|
// Some applications (looking at you, Google Takeout) will add a
|
|
// spurious "+00:00" timezone offset to random datestamp tags, so
|
|
// ignore UTC offsets here.
|
|
if (offset != null && !isUTC(offset.tz)) {
|
|
return { ...offset, src: tagName };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
function extractTzOffsetFromTimeStamp(t, opts) {
|
|
if (opts?.inferTimezoneFromTimeStamp !== true)
|
|
return;
|
|
const ts = ExifDateTime_1.ExifDateTime.from(t.TimeStamp);
|
|
if (ts == null)
|
|
return;
|
|
for (const tagName of opts.inferTimezoneFromDatestampTags ?? []) {
|
|
const v = t[tagName];
|
|
if (!(0, String_1.isString)(v) && !(v instanceof ExifDateTime_1.ExifDateTime))
|
|
continue;
|
|
const ea = ExifDateTime_1.ExifDateTime.from(v);
|
|
if (ea == null)
|
|
continue;
|
|
if (ea.zone != null) {
|
|
return { zone: ea.zone, tz: ea.zone, src: tagName };
|
|
}
|
|
const deltaMinutes = Math.floor((ea.toEpochSeconds("UTC") - ts.toEpochSeconds()) / 60);
|
|
const likelyOffsetZone = inferLikelyOffsetMinutes(deltaMinutes);
|
|
const zone = offsetMinutesToZoneName(likelyOffsetZone);
|
|
if (zone != null) {
|
|
return {
|
|
zone,
|
|
tz: zone,
|
|
src: "offset between " + tagName + " and TimeStamp",
|
|
};
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
// timezone offsets may be on a 15 minute boundary, but if GPS acquisition is
|
|
// old, this can be spurious. We get less mistakes with a larger multiple, so
|
|
// we're using 30 minutes instead of 15. See
|
|
// https://www.timeanddate.com/time/time-zones-interesting.html
|
|
const LikelyOffsetMinutes = ValidTimezoneOffsets.map(offsetToMinutes);
|
|
function inferLikelyOffsetMinutes(deltaMinutes) {
|
|
const nearest = (0, Array_1.leastBy)(LikelyOffsetMinutes, (ea) => Math.abs(ea - deltaMinutes));
|
|
// Reject timezone offsets more than 30 minutes away from the nearest:
|
|
return Math.abs(nearest - deltaMinutes) < 30 ? nearest : undefined;
|
|
}
|
|
/**
|
|
* Convert blank strings to undefined.
|
|
*/
|
|
function toNotBlank(x) {
|
|
return x == null || (typeof x === "string" && (0, String_1.blank)(x)) ? undefined : x;
|
|
}
|
|
function extractTzOffsetFromUTCOffset(t) {
|
|
const utcSources = {
|
|
...(0, Pick_1.pick)(t, "GPSDateTime", "DateTimeUTC", "SonyDateTime2"),
|
|
GPSDateTimeStamp: (0, Maybe_1.map2)(toNotBlank(t.GPSDateStamp), // Example: "2022:04:13"
|
|
toNotBlank(t.GPSTimeStamp), // Example: "23:59:41.001"
|
|
(a, b) => a + " " + b),
|
|
};
|
|
// We can always assume these are in UTC:
|
|
const utc = (0, Maybe_1.first)([
|
|
"GPSDateTime",
|
|
"DateTimeUTC",
|
|
"GPSDateTimeStamp",
|
|
"SonyDateTime2",
|
|
], (tagName) => {
|
|
const v = utcSources[tagName];
|
|
const edt = v instanceof ExifDateTime_1.ExifDateTime ? v : ExifDateTime_1.ExifDateTime.fromExifStrict(v);
|
|
const s = edt != null && (edt.zone == null || isUTC(edt.zone))
|
|
? edt.setZone("UTC", { keepLocalTime: true })?.toEpochSeconds()
|
|
: undefined;
|
|
return s != null
|
|
? {
|
|
tagName,
|
|
s,
|
|
}
|
|
: undefined;
|
|
});
|
|
if (utc == null)
|
|
return;
|
|
// If we can find any of these without a zone, the timezone should be the
|
|
// offset between this time and the GPS time.
|
|
const dt = (0, Maybe_1.first)(CapturedAtTagNames_1.CapturedAtTagNames, (tagName) => {
|
|
const edt = ExifDateTime_1.ExifDateTime.fromExifStrict(t[tagName]);
|
|
const s = edt != null && edt.zone == null
|
|
? edt.setZone("UTC", { keepLocalTime: true })?.toEpochSeconds()
|
|
: undefined;
|
|
return s != null
|
|
? {
|
|
tagName,
|
|
s,
|
|
}
|
|
: undefined;
|
|
});
|
|
if (dt == null)
|
|
return;
|
|
const diffSeconds = dt.s - utc.s;
|
|
const offsetMinutes = inferLikelyOffsetMinutes(diffSeconds / 60);
|
|
return (0, Maybe_1.map)(offsetMinutesToZoneName(offsetMinutes), (zone) => ({
|
|
zone,
|
|
tz: zone,
|
|
src: `offset between ${dt.tagName} and ${utc.tagName}`,
|
|
}));
|
|
}
|
|
function equivalentZones(a, b) {
|
|
const az = normalizeZone(a);
|
|
const bz = normalizeZone(b);
|
|
return (az != null &&
|
|
bz != null &&
|
|
(az.equals(bz) || az.offset(Date.now()) === bz.offset(Date.now())));
|
|
}
|
|
function getZoneName(args = {}) {
|
|
const result = normalizeZone(args.zone)?.name ??
|
|
normalizeZone(args.zoneName)?.name ??
|
|
offsetMinutesToZoneName(args.tzoffsetMinutes);
|
|
return (0, String_1.blank)(result) || result === exports.UnsetZoneName ? undefined : result;
|
|
}
|
|
//# sourceMappingURL=Timezones.js.map
|