exiftool-vendored
Advanced tools
Comparing version 23.0.0 to 23.1.0
@@ -29,2 +29,17 @@ # Changelog/Versioning | ||
### v23.1.0 | ||
- 🌱 ExifTool upgraded to [v12.67](https://exiftool.org/history.html#v12.67) | ||
- ✨ `ExifTime` now parses and stores timezone offsets if available. This resolves [issue | ||
#157](https://github.com/photostructure/exiftool-vendored.js/issues/157). | ||
- 📦 `ExifDateTime`, `ExifTime`, and `ExifDate` are now [only allowed to try | ||
to parse keys that includes `date` or | ||
`time`](https://github.com/photostructure/exiftool-vendored.js/blob/ed7bf9eaea9b1d8ad234fb907953568219fc5bdb/src/ReadTask.ts#L389), | ||
which avoids incorrect parsing of tags like `MonthDayCreated` (which looks | ||
like `12:19`) | ||
- 📦 Upgrade all deps, but only `devDependencies` were impacted. | ||
### v23.0.0 | ||
@@ -43,4 +58,6 @@ | ||
- 💔 `backfillTimezones` now defaults to `true`. Although this is likely to be | ||
what people expect, know that there are edge and corner cases that you | ||
should be aware of. | ||
what people expect, but know that this makes the assumption that all encoded | ||
times without an explicit offset share the same tz, which may not be correct | ||
(say, if you edit the image in a different timezone from when it was | ||
captured). | ||
@@ -47,0 +64,0 @@ - 💔 If `backfillTimezones` is set to `false`, `ExifDateTime` will no longer |
@@ -11,3 +11,2 @@ "use strict"; | ||
catch (err) { | ||
// console.log("retryOnReject caught error", { err, retries, maxRetries }) | ||
if (retries < maxRetries) { | ||
@@ -14,0 +13,0 @@ retries++; |
@@ -6,3 +6,4 @@ import { DateTime } from "luxon"; | ||
import { Maybe } from "./Maybe"; | ||
export declare function validDateTime(dt: DateTime): boolean; | ||
export declare function validDateTime(dt: Maybe<DateTime>): dt is DateTime; | ||
export declare const SecondMs = 1000; | ||
export declare const MinuteMs: number; | ||
@@ -9,0 +10,0 @@ export declare const HourMs: number; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.hms = exports.toExifString = exports.dateTimeToExif = exports.isDateOrTime = exports.DayMs = exports.HourMs = exports.MinuteMs = exports.validDateTime = void 0; | ||
exports.hms = exports.toExifString = exports.dateTimeToExif = exports.isDateOrTime = exports.DayMs = exports.HourMs = exports.MinuteMs = exports.SecondMs = exports.validDateTime = void 0; | ||
const luxon_1 = require("luxon"); | ||
@@ -12,3 +12,4 @@ const ExifDate_1 = require("./ExifDate"); | ||
exports.validDateTime = validDateTime; | ||
exports.MinuteMs = 60 * 1000; | ||
exports.SecondMs = 1000; | ||
exports.MinuteMs = 60 * exports.SecondMs; | ||
exports.HourMs = 60 * exports.MinuteMs; | ||
@@ -15,0 +16,0 @@ exports.DayMs = 24 * exports.HourMs; |
@@ -32,3 +32,2 @@ import { DateTime, DateTimeJSOptions, DurationLike, ToISOTimeOptions, Zone, ZoneOptions } from "luxon"; | ||
static fromEXIF(text: string, defaultZone?: Maybe<string>): Maybe<ExifDateTime>; | ||
private static fromPatterns; | ||
/** | ||
@@ -70,10 +69,13 @@ * Parse the given date-time string, EXIF-formatted. | ||
}): ExifDateTime; | ||
readonly zone: Maybe<string>; | ||
constructor(year: number, month: number, day: number, hour: number, minute: number, second: number, millisecond?: number | undefined, tzoffsetMinutes?: number | undefined, rawValue?: string | undefined, zoneName?: string | undefined, inferredZone?: boolean | undefined); | ||
get millis(): number | undefined; | ||
get hasZone(): boolean; | ||
get zone(): Maybe<string>; | ||
setZone(zone?: string | Zone, opts?: ZoneOptions): Maybe<ExifDateTime>; | ||
get unsetMilliseconds(): boolean; | ||
setZone(zone: string | Zone, opts?: ZoneOptions & { | ||
inferredZone?: boolean; | ||
}): Maybe<ExifDateTime>; | ||
/** | ||
* CAUTION: This instance will inherit the system timezone if this instance | ||
* has an unset zone | ||
* has an unset zone (as Luxon doesn't support "unset" timezones) | ||
*/ | ||
@@ -80,0 +82,0 @@ toDateTime(): DateTime; |
@@ -13,3 +13,3 @@ "use strict"; | ||
}; | ||
var _ExifDateTime_dt; | ||
var _a, _ExifDateTime_fromPatterns, _ExifDateTime_looseExifFormats, _ExifDateTime_dt; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -22,8 +22,4 @@ exports.ExifDateTime = void 0; | ||
const String_1 = require("./String"); | ||
const TimeParsing_1 = require("./TimeParsing"); | ||
const Timezones_1 = require("./Timezones"); | ||
const TimeFmts = [ | ||
{ fmt: "HH:mm:ss.u", unsetMilliseconds: false }, | ||
{ fmt: "HH:mm:ss", unsetMilliseconds: true }, | ||
{ fmt: "HH:mm", unsetMilliseconds: true }, | ||
]; | ||
/** | ||
@@ -34,6 +30,6 @@ * Encodes an ExifDateTime with an optional tz offset in minutes. | ||
static from(exifOrIso, defaultZone) { | ||
var _a, _b; | ||
var _b, _c; | ||
return ( | ||
// in order of strictness: | ||
(_b = (_a = this.fromExifStrict(exifOrIso, defaultZone)) !== null && _a !== void 0 ? _a : this.fromISO(exifOrIso, defaultZone)) !== null && _b !== void 0 ? _b : this.fromExifLoose(exifOrIso, defaultZone)); | ||
(_c = (_b = this.fromExifStrict(exifOrIso, defaultZone)) !== null && _b !== void 0 ? _b : this.fromISO(exifOrIso, defaultZone)) !== null && _c !== void 0 ? _c : this.fromExifLoose(exifOrIso, defaultZone)); | ||
} | ||
@@ -45,20 +41,6 @@ static fromISO(iso, defaultZone) { | ||
// so we have to do this ourselves: | ||
const patterns = []; | ||
for (const z of [ | ||
{ fmt: "ZZ", zone: undefined, inferredZone: false }, | ||
{ fmt: "'Z'", zone: "UTC", inferredZone: false }, | ||
{ fmt: "", zone: defaultZone, inferredZone: true }, | ||
]) { | ||
for (const sep of ["'T'", " "]) { | ||
for (const timeFmt of TimeFmts) { | ||
patterns.push({ | ||
fmt: `y-M-d${sep}${timeFmt.fmt}${z.fmt}`, | ||
zone: z.zone, | ||
unsetMilliseconds: timeFmt.unsetMilliseconds, | ||
inferredZone: z.inferredZone, | ||
}); | ||
} | ||
} | ||
} | ||
return this.fromPatterns(iso, patterns); | ||
return __classPrivateFieldGet(this, _a, "m", _ExifDateTime_fromPatterns).call(this, iso, (0, TimeParsing_1.timeFormats)({ | ||
formatPrefixes: ["y-MM-dd'T'", "y-MM-dd ", "y-M-d "], | ||
defaultZone, | ||
})); | ||
} | ||
@@ -76,3 +58,3 @@ /** | ||
static fromEXIF(text, defaultZone) { | ||
var _a; | ||
var _b; | ||
if ((0, String_1.blank)(text)) | ||
@@ -82,46 +64,4 @@ return undefined; | ||
// .fromExifStrict() uses .fromISO() as a backstop | ||
(_a = this.fromExifStrict(text, defaultZone)) !== null && _a !== void 0 ? _a : this.fromExifLoose(text, defaultZone)); | ||
(_b = this.fromExifStrict(text, defaultZone)) !== null && _b !== void 0 ? _b : this.fromExifLoose(text, defaultZone)); | ||
} | ||
static fromPatterns(text, fmts) { | ||
var _a, _b; | ||
const s = (0, String_1.toS)(text).trim(); | ||
const inputs = [s]; | ||
// Some EXIF datetime will "over-specify" and include both the utc offset | ||
// *and* the "time zone abbreviation", like PST or PDT. | ||
// TZAs are between 2 (AT) and 5 (WEST) characters. | ||
// Unfortunately, luxon doesn't support regex. | ||
// We only want to strip off the TZA if it isn't "UTC" or "Z" | ||
const zuluSuffix = s.match(/[.\d\s](UTC|Z)$/); | ||
if (null == zuluSuffix) { | ||
const noTza = s.replace(/ [a-z]{2,5}$/i, ""); | ||
if (noTza !== s) | ||
inputs.push(noTza); | ||
} | ||
for (const input of inputs) { | ||
for (const ea of fmts) { | ||
const dt = luxon_1.DateTime.fromFormat(input, ea.fmt, { | ||
setZone: true, | ||
zone: (_a = ea.zone) !== null && _a !== void 0 ? _a : Timezones_1.UnsetZone, | ||
}); | ||
if (dt != null && dt.isValid) { | ||
const zoneUnset = dt.zone == null || dt.zone === Timezones_1.UnsetZone; | ||
let inferredZone = zoneUnset ? false : ea.inferredZone; | ||
if (inferredZone == null) { | ||
// this is pretty miserable, but luxon doesn't expose how it got | ||
// the zone, so we have to resort to this hack: | ||
const dt2 = luxon_1.DateTime.fromFormat(input, ea.fmt, { setZone: true }); | ||
inferredZone = dt.zone !== dt2.zone; | ||
} | ||
const edt = ExifDateTime.fromDateTime(dt, { | ||
rawValue: s, | ||
unsetMilliseconds: (_b = ea.unsetMilliseconds) !== null && _b !== void 0 ? _b : false, | ||
inferredZone, | ||
}); | ||
if (edt != null) | ||
return edt; | ||
} | ||
} | ||
} | ||
return; | ||
} | ||
/** | ||
@@ -139,21 +79,6 @@ * Parse the given date-time string, EXIF-formatted. | ||
static fromExifStrict(text, defaultZone) { | ||
var _a; | ||
var _b; | ||
if ((0, String_1.blank)(text)) | ||
return undefined; | ||
const patterns = []; | ||
for (const z of [ | ||
{ fmt: "ZZ", zone: undefined, inferredZone: false }, | ||
{ fmt: "'Z'", zone: "UTC", inferredZone: false }, | ||
{ fmt: "", zone: defaultZone, inferredZone: true }, | ||
]) { | ||
for (const timeFmt of TimeFmts) { | ||
patterns.push({ | ||
fmt: `y:M:d ${timeFmt.fmt}${z.fmt}`, | ||
zone: z.zone, | ||
unsetMilliseconds: timeFmt.unsetMilliseconds, | ||
inferredZone: z.inferredZone, | ||
}); | ||
} | ||
} | ||
return ((_a = this.fromPatterns(text, patterns)) !== null && _a !== void 0 ? _a : | ||
return ((_b = __classPrivateFieldGet(this, _a, "m", _ExifDateTime_fromPatterns).call(this, text, (0, TimeParsing_1.timeFormats)({ formatPrefixes: ["y:MM:dd ", "y:M:d "], defaultZone }))) !== null && _b !== void 0 ? _b : | ||
// Not found yet? Maybe it's in ISO format? See | ||
@@ -164,26 +89,14 @@ // https://github.com/photostructure/exiftool-vendored.js/issues/71 | ||
static fromExifLoose(text, defaultZone) { | ||
if ((0, String_1.blank)(text)) | ||
return undefined; | ||
const zone = (0, String_1.notBlank)(defaultZone) ? defaultZone : Timezones_1.UnsetZone; | ||
// The following are from actual datestamps seen in the wild: | ||
const formats = [ | ||
"MMM d y H:m:s", | ||
"MMM d y, H:m:s", | ||
// Thu Oct 13 00:12:27 2016: | ||
"ccc MMM d H:m:s y", | ||
]; | ||
return this.fromPatterns(text, [ | ||
...formats.map((fmt) => ({ fmt: fmt + "ZZ", inferredZone: false })), | ||
// And the same formats, without offsets with default zone: | ||
...formats.map((fmt) => ({ fmt, zone, inferredZone: true })), | ||
]); | ||
return (0, String_1.blank)(text) | ||
? undefined | ||
: __classPrivateFieldGet(this, _a, "m", _ExifDateTime_fromPatterns).call(this, text, __classPrivateFieldGet(this, _a, "m", _ExifDateTime_looseExifFormats).call(this, defaultZone)); | ||
} | ||
static fromDateTime(dt, opts) { | ||
var _a; | ||
var _b; | ||
if (dt == null || !dt.isValid || dt.year === 0 || dt.year === 1) { | ||
return undefined; | ||
} | ||
return new ExifDateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.millisecond === 0 && true === (opts === null || opts === void 0 ? void 0 : opts.unsetMilliseconds) | ||
return new _a(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.millisecond === 0 && true === (opts === null || opts === void 0 ? void 0 : opts.unsetMilliseconds) | ||
? undefined | ||
: dt.millisecond, dt.offset === Timezones_1.UnsetZoneOffsetMinutes ? undefined : dt.offset, opts === null || opts === void 0 ? void 0 : opts.rawValue, dt.zoneName == null || ((_a = dt.zone) === null || _a === void 0 ? void 0 : _a.name) === Timezones_1.UnsetZone.name | ||
: dt.millisecond, dt.offset === Timezones_1.UnsetZoneOffsetMinutes ? undefined : dt.offset, opts === null || opts === void 0 ? void 0 : opts.rawValue, dt.zoneName == null || ((_b = dt.zone) === null || _b === void 0 ? void 0 : _b.name) === Timezones_1.UnsetZone.name | ||
? undefined | ||
@@ -235,2 +148,3 @@ : dt.zoneName, opts === null || opts === void 0 ? void 0 : opts.inferredZone); | ||
_ExifDateTime_dt.set(this, void 0); | ||
this.zone = (0, Timezones_1.getZoneName)({ zoneName, tzoffsetMinutes }); | ||
} | ||
@@ -241,31 +155,28 @@ get millis() { | ||
get hasZone() { | ||
return (0, String_1.notBlank)(this.zone); | ||
return this.zone != null; | ||
} | ||
get zone() { | ||
var _a; | ||
return (_a = this.zoneName) !== null && _a !== void 0 ? _a : (0, Timezones_1.offsetMinutesToZoneName)(this.tzoffsetMinutes); | ||
get unsetMilliseconds() { | ||
return this.millisecond == null; | ||
} | ||
setZone(zone, opts) { | ||
// This is a bit tricky... We want to keep the local time and just _say_ it was in the zone of the image **if we don't already have a zone.** | ||
// If we _do_ have a zone, assume it was already converted by ExifTool into (probably the system) timezone, which means _don't_ keepLocalTime. | ||
const dt = this.toDateTime().setZone(zone, { | ||
keepLocalTime: !this.hasZone, | ||
...opts, | ||
var _b; | ||
const dt = (0, TimeParsing_1.setZone)({ | ||
zone, | ||
src: this.toDateTime(), | ||
srcHasZone: this.hasZone, | ||
opts, | ||
}); | ||
const result = ExifDateTime.fromDateTime(dt, { | ||
return _a.fromDateTime(dt, { | ||
rawValue: this.rawValue, | ||
unsetMilliseconds: this.millisecond == null, | ||
inferredZone: (_b = opts === null || opts === void 0 ? void 0 : opts.inferredZone) !== null && _b !== void 0 ? _b : true, | ||
}); | ||
// We know this will be defined: this is valid, so changing the zone will | ||
// also be valid. | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
return result; | ||
} | ||
/** | ||
* CAUTION: This instance will inherit the system timezone if this instance | ||
* has an unset zone | ||
* has an unset zone (as Luxon doesn't support "unset" timezones) | ||
*/ | ||
toDateTime() { | ||
var _a; | ||
return (__classPrivateFieldSet(this, _ExifDateTime_dt, (_a = __classPrivateFieldGet(this, _ExifDateTime_dt, "f")) !== null && _a !== void 0 ? _a : luxon_1.DateTime.fromObject({ | ||
var _b; | ||
return (__classPrivateFieldSet(this, _ExifDateTime_dt, (_b = __classPrivateFieldGet(this, _ExifDateTime_dt, "f")) !== null && _b !== void 0 ? _b : luxon_1.DateTime.fromObject({ | ||
year: this.year, | ||
@@ -289,5 +200,5 @@ month: this.month, | ||
toISOString(options = {}) { | ||
var _a; | ||
var _b; | ||
return (0, Maybe_1.denull)(this.toDateTime().toISO({ | ||
suppressMilliseconds: (_a = options.suppressMilliseconds) !== null && _a !== void 0 ? _a : this.millisecond == null, | ||
suppressMilliseconds: (_b = options.suppressMilliseconds) !== null && _b !== void 0 ? _b : this.millisecond == null, | ||
includeOffset: this.hasZone && options.includeOffset !== false, | ||
@@ -334,9 +245,10 @@ })); | ||
static fromJSON(json) { | ||
return new ExifDateTime(json.year, json.month, json.day, json.hour, json.minute, json.second, json.millisecond, json.tzoffsetMinutes, json.rawValue, json.zoneName, json.inferredZone); | ||
return new _a(json.year, json.month, json.day, json.hour, json.minute, json.second, json.millisecond, json.tzoffsetMinutes, json.rawValue, json.zoneName, json.inferredZone); | ||
} | ||
maybeMatchZone(target, maxDeltaMs = 14 * DateTime_1.MinuteMs) { | ||
var _a, _b, _c, _d, _e; | ||
if (!target.hasZone) | ||
var _b, _c, _d; | ||
const targetZone = target.zone; | ||
if (targetZone == null || !target.hasZone) | ||
return; | ||
return ((_d = (_b = (_a = this.setZone(target.zone, { keepLocalTime: false })) === null || _a === void 0 ? void 0 : _a.ifClose(target, maxDeltaMs)) !== null && _b !== void 0 ? _b : (_c = this.setZone(target.zone, { keepLocalTime: true })) === null || _c === void 0 ? void 0 : _c.ifClose(target, maxDeltaMs)) !== null && _d !== void 0 ? _d : (_e = this.setZone("UTC", { keepLocalTime: true })) === null || _e === void 0 ? void 0 : _e.ifClose(target, maxDeltaMs)); | ||
return ((_c = (_b = this.setZone(targetZone, { keepLocalTime: false })) === null || _b === void 0 ? void 0 : _b.ifClose(target, maxDeltaMs)) !== null && _c !== void 0 ? _c : (_d = this.setZone(targetZone, { keepLocalTime: true })) === null || _d === void 0 ? void 0 : _d.ifClose(target, maxDeltaMs)); | ||
} | ||
@@ -353,7 +265,28 @@ ifClose(target, maxDeltaMs = 14 * DateTime_1.MinuteMs) { | ||
} | ||
return ExifDateTime.fromDateTime(dt); | ||
return _a.fromDateTime(dt, this); | ||
} | ||
} | ||
exports.ExifDateTime = ExifDateTime; | ||
_ExifDateTime_dt = new WeakMap(); | ||
_a = ExifDateTime, _ExifDateTime_dt = new WeakMap(), _ExifDateTime_fromPatterns = function _ExifDateTime_fromPatterns(text, fmts) { | ||
const result = (0, TimeParsing_1.parseDateTime)(text, fmts); | ||
return result == null | ||
? undefined | ||
: _a.fromDateTime(result.dt, { | ||
rawValue: text, | ||
unsetMilliseconds: result.unsetMilliseconds, | ||
inferredZone: result.inferredZone, | ||
}); | ||
}, _ExifDateTime_looseExifFormats = function* _ExifDateTime_looseExifFormats(defaultZone) { | ||
// The following are from actual datestamps seen in the wild (!!) | ||
const formats = [ | ||
"MMM d y HH:mm:ss", | ||
"MMM d y, HH:mm:ss", | ||
// Thu Oct 13 00:12:27 2016: | ||
"ccc MMM d HH:mm:ss y", | ||
]; | ||
const zone = (0, String_1.notBlank)(defaultZone) ? defaultZone : Timezones_1.UnsetZone; | ||
for (const fmt of formats) { | ||
yield { fmt: fmt, zone, inferredZone: true }; | ||
} | ||
}; | ||
//# sourceMappingURL=ExifDateTime.js.map |
@@ -1,2 +0,2 @@ | ||
import { DateTime } from "luxon"; | ||
import { DateTime, Zone, ZoneOptions } from "luxon"; | ||
import { Maybe } from "./Maybe"; | ||
@@ -7,2 +7,3 @@ /** | ||
export declare class ExifTime { | ||
#private; | ||
readonly hour: number; | ||
@@ -13,10 +14,17 @@ readonly minute: number; | ||
readonly rawValue?: string | undefined; | ||
static fromEXIF(text: string): Maybe<ExifTime>; | ||
static fromDateTime(dt: DateTime, rawValue?: string): Maybe<ExifTime>; | ||
constructor(hour: number, minute: number, second: number, millisecond?: number | undefined, rawValue?: string | undefined); | ||
readonly inferredZone?: boolean | undefined; | ||
static fromEXIF(text: string, defaultZone?: Maybe<string>): Maybe<ExifTime>; | ||
static fromDateTime(dt: Maybe<DateTime>, rawValue?: string, zone?: string, inferredZone?: boolean, unsetMilliseconds?: boolean): Maybe<ExifTime>; | ||
readonly zone: Maybe<string>; | ||
constructor(hour: number, minute: number, second: number, millisecond?: number | undefined, rawValue?: string | undefined, zoneName?: Maybe<string>, inferredZone?: boolean | undefined); | ||
toDateTime(): DateTime; | ||
/** | ||
* Alias for `.millisecond` | ||
*/ | ||
get millis(): number | undefined; | ||
private subsec; | ||
get hasZone(): boolean; | ||
toString(): string; | ||
toISOString(): string; | ||
toExifString(): string; | ||
setZone(zone: string | Zone, opts?: ZoneOptions): Maybe<ExifTime>; | ||
toJSON(): { | ||
@@ -29,4 +37,6 @@ _ctor: string; | ||
rawValue: string | undefined; | ||
zone: Maybe<string>; | ||
inferredZone: boolean | undefined; | ||
}; | ||
static fromJSON(json: ReturnType<ExifTime["toJSON"]>): ExifTime; | ||
} |
"use strict"; | ||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); | ||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); | ||
}; | ||
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { | ||
if (kind === "m") throw new TypeError("Private method is not writable"); | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); | ||
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; | ||
}; | ||
var _ExifTime_instances, _ExifTime_dt, _ExifTime_z, _ExifTime_subsec, _ExifTime_shortZone; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -7,2 +19,4 @@ exports.ExifTime = void 0; | ||
const String_1 = require("./String"); | ||
const TimeParsing_1 = require("./TimeParsing"); | ||
const Timezones_1 = require("./Timezones"); | ||
/** | ||
@@ -12,24 +26,19 @@ * Encodes an ExifTime (which may not have a timezone offset) | ||
class ExifTime { | ||
static fromEXIF(text) { | ||
text = (0, String_1.toS)(text).trim(); | ||
if ((0, String_1.blank)(text)) | ||
static fromEXIF(text, defaultZone) { | ||
const s = (0, String_1.toS)(text).trim(); | ||
if (s.length === 0) | ||
return; | ||
for (const fmt of [ | ||
"HH:mm:ss.uZZ", | ||
"HH:mm:ssZZ", | ||
"HH:mm:ss.u", | ||
"HH:mm:ss", | ||
]) { | ||
const result = this.fromDateTime(luxon_1.DateTime.fromFormat(text, fmt), text); | ||
if (result != null) | ||
return result; | ||
const result = (0, TimeParsing_1.parseDateTime)(text, (0, TimeParsing_1.timeFormats)({ defaultZone })); | ||
if (result != null) { | ||
return this.fromDateTime(result.dt, text, result.unsetZone ? undefined : (0, Timezones_1.getZoneName)({ zone: result.dt.zone }), result.inferredZone, result.unsetMilliseconds); | ||
} | ||
return; | ||
} | ||
static fromDateTime(dt, rawValue) { | ||
return (0, DateTime_1.validDateTime)(dt) | ||
? new ExifTime(dt.hour, dt.minute, dt.second, dt.millisecond, rawValue) | ||
: undefined; | ||
static fromDateTime(dt, rawValue, zone, inferredZone, unsetMilliseconds) { | ||
return !(0, DateTime_1.validDateTime)(dt) | ||
? undefined | ||
: new ExifTime(dt.hour, dt.minute, dt.second, unsetMilliseconds ? undefined : dt.millisecond, rawValue, zone, inferredZone); | ||
} | ||
constructor(hour, minute, second, millisecond, rawValue) { | ||
constructor(hour, minute, second, millisecond, rawValue, zoneName, inferredZone) { | ||
_ExifTime_instances.add(this); | ||
this.hour = hour; | ||
@@ -40,13 +49,31 @@ this.minute = minute; | ||
this.rawValue = rawValue; | ||
this.inferredZone = inferredZone; | ||
_ExifTime_dt.set(this, void 0); | ||
_ExifTime_z.set(this, void 0); | ||
this.zone = (0, Timezones_1.getZoneName)({ zoneName }); | ||
} | ||
toDateTime() { | ||
var _a; | ||
return (__classPrivateFieldSet(this, _ExifTime_dt, (_a = __classPrivateFieldGet(this, _ExifTime_dt, "f")) !== null && _a !== void 0 ? _a : luxon_1.DateTime.fromObject({ | ||
hour: this.hour, | ||
minute: this.minute, | ||
second: this.second, | ||
millisecond: this.millisecond, | ||
}, { | ||
zone: this.zone, | ||
}), "f")); | ||
} | ||
/** | ||
* Alias for `.millisecond` | ||
*/ | ||
get millis() { | ||
return this.millisecond; | ||
} | ||
subsec() { | ||
return this.millisecond == null || this.millisecond === 0 | ||
? "" | ||
: "." + (0, String_1.pad3)(this.millisecond); | ||
get hasZone() { | ||
return this.zone != null; | ||
} | ||
toString() { | ||
return (0, String_1.pad2)(this.hour, this.minute, this.second).join(":") + this.subsec(); | ||
return ((0, String_1.pad2)(this.hour, this.minute, this.second).join(":") + | ||
__classPrivateFieldGet(this, _ExifTime_instances, "m", _ExifTime_subsec).call(this) + | ||
__classPrivateFieldGet(this, _ExifTime_instances, "m", _ExifTime_shortZone).call(this)); | ||
} | ||
@@ -59,2 +86,11 @@ toISOString() { | ||
} | ||
setZone(zone, opts) { | ||
const dt = (0, TimeParsing_1.setZone)({ | ||
zone, | ||
src: this.toDateTime(), | ||
srcHasZone: this.hasZone, | ||
opts, | ||
}); | ||
return ExifTime.fromDateTime(dt, this.rawValue, this.zone, this.inferredZone, this.millisecond == null); | ||
} | ||
toJSON() { | ||
@@ -68,9 +104,17 @@ return { | ||
rawValue: this.rawValue, | ||
zone: this.zone, | ||
inferredZone: this.inferredZone, | ||
}; | ||
} | ||
static fromJSON(json) { | ||
return new ExifTime(json.hour, json.minute, json.second, json.millisecond, json.rawValue); | ||
return new ExifTime(json.hour, json.minute, json.second, json.millisecond, json.rawValue, json.zone, json.inferredZone); | ||
} | ||
} | ||
exports.ExifTime = ExifTime; | ||
_ExifTime_dt = new WeakMap(), _ExifTime_z = new WeakMap(), _ExifTime_instances = new WeakSet(), _ExifTime_subsec = function _ExifTime_subsec() { | ||
return this.millisecond == null ? "" : "." + (0, String_1.pad3)(this.millisecond); | ||
}, _ExifTime_shortZone = function _ExifTime_shortZone() { | ||
var _a, _b, _c; | ||
return (__classPrivateFieldSet(this, _ExifTime_z, (_a = __classPrivateFieldGet(this, _ExifTime_z, "f")) !== null && _a !== void 0 ? _a : ((_c = (_b = (0, Timezones_1.normalizeZone)(this.zone)) === null || _b === void 0 ? void 0 : _b.formatOffset(Date.now(), "short")) !== null && _c !== void 0 ? _c : ""), "f")); | ||
}; | ||
//# sourceMappingURL=ExifTime.js.map |
@@ -72,3 +72,2 @@ "use strict"; | ||
const MaybeDateOrTimeRe = /when|date|time|subsec|creat|modif/i; | ||
const TimeRe = /time/i; | ||
class ReadTask extends ExifToolTask_1.ExifToolTask { | ||
@@ -142,2 +141,3 @@ constructor(sourceFile, args, options) { | ||
} | ||
// only exposed for tests | ||
parse(data, err) { | ||
@@ -211,8 +211,10 @@ try { | ||
if (v instanceof ExifDateTime_1.ExifDateTime && | ||
// Don't backfill from `stat()` dates, as they may include the system | ||
// zone: | ||
!key.startsWith("File") && | ||
v.hasZone && | ||
// Don't backfill from inferred zones: | ||
v.inferredZone !== true && | ||
!isUtcTagName(key) && | ||
// We don't want to use current system offset (from `File*` tags): | ||
!key.startsWith("File")) { | ||
// don't incorrectly infer UTC dates if this is a UTC tag. | ||
// Don't backfill from UTC tags: | ||
!isUtcTagName(key)) { | ||
datesWithTz.push(v); | ||
@@ -224,9 +226,15 @@ } | ||
if (this.options.backfillTimezones === true) { | ||
// prefer non-UTC offsets (which may be incorrect): | ||
// prefer non-UTC offsets (UTC may be incorrect): | ||
const candidates = (0, Array_1.sortBy)(datesWithTz, (ea) => { var _a; return -Math.abs((_a = ea.tzoffsetMinutes) !== null && _a !== void 0 ? _a : 0); }); | ||
for (const [key, value] of Object.entries(tags)) { | ||
// We don't want to backfill dates from `stat`, so skip `File*` tags: | ||
if (value instanceof ExifDateTime_1.ExifDateTime && | ||
!isUtcTagName(key) && | ||
!key.startsWith("File")) { | ||
if ( | ||
// we don't backfill ExifTime zones, as we don't know for sure what | ||
// day they are for, and if the zone isn't fixed, that would be an | ||
// issue. (We _could_ set ExifTime zones if we knew the zone was | ||
// fixed, but that's a later, low-priority TODO). | ||
value instanceof ExifDateTime_1.ExifDateTime && | ||
// Don't backfill dates from `stat`: | ||
!key.startsWith("File") && | ||
// Don't backfill UTC tags: | ||
!isUtcTagName(key)) { | ||
tags[key] = (_a = __classPrivateFieldGet(this, _ReadTask_instances, "m", _ReadTask_maybeSetZone).call(this, value, candidates)) !== null && _a !== void 0 ? _a : value; | ||
@@ -319,2 +327,8 @@ } | ||
} | ||
if (tagName === "GPSLatitude") { | ||
return this.lat; | ||
} | ||
if (tagName === "GPSLongitude") { | ||
return this.lon; | ||
} | ||
if (Array.isArray(value)) { | ||
@@ -330,8 +344,2 @@ return value.map((ea) => __classPrivateFieldGet(this, _ReadTask_instances, "m", _ReadTask_parseTag).call(this, tagName, ea)); | ||
} | ||
if (tagName === "GPSLatitude") { | ||
return this.lat; | ||
} | ||
if (tagName === "GPSLongitude") { | ||
return this.lon; | ||
} | ||
if (typeof value === "string") { | ||
@@ -347,3 +355,3 @@ const b = BinaryField_1.BinaryField.fromRawValue(value); | ||
// datestamps are all in UTC_, rather than being in `this.tz` (which | ||
// may be from GPS or other heuristics). See #153. | ||
// may be from GPS or other heuristics). See issue #153. | ||
const tz = isUtcTagName(tagName) || __classPrivateFieldGet(this, _ReadTask_instances, "m", _ReadTask_defaultToUTC).call(this) | ||
@@ -354,3 +362,8 @@ ? "UTC" | ||
: undefined; | ||
return ((_c = (_b = (_a = ExifDateTime_1.ExifDateTime.from(value, tz)) !== null && _a !== void 0 ? _a : (TimeRe.test(tagName) ? ExifTime_1.ExifTime.fromEXIF(value) : undefined)) !== null && _b !== void 0 ? _b : ExifDate_1.ExifDate.from(value)) !== null && _c !== void 0 ? _c : value); | ||
// Time-only tags have "time" but not "date" in their name: | ||
const keyIncludesTime = /time/i.test(tagName); | ||
const keyIncludesDate = /date/i.test(tagName); | ||
return ((_c = (_b = (_a = (keyIncludesTime || keyIncludesDate | ||
? ExifDateTime_1.ExifDateTime.from(value, tz) | ||
: undefined)) !== null && _a !== void 0 ? _a : (keyIncludesTime ? ExifTime_1.ExifTime.fromEXIF(value) : undefined)) !== null && _b !== void 0 ? _b : (keyIncludesDate ? ExifDate_1.ExifDate.from(value) : undefined)) !== null && _c !== void 0 ? _c : value); | ||
} | ||
@@ -357,0 +370,0 @@ } |
@@ -6,3 +6,3 @@ import { Maybe } from "./Maybe"; | ||
export declare function toS(s: Maybe<any>): string; | ||
export declare function leftPad(i: Maybe<any>, minLen: number, padChar: "0" | " "): string; | ||
export declare function leftPad(i: Maybe<number | string>, minLen: number, padChar: "0" | " "): string; | ||
export declare function pad2(...numbers: number[]): string[]; | ||
@@ -9,0 +9,0 @@ export declare function pad3(...numbers: number[]): string[]; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.stripSuffix = exports.stripPrefix = exports.pad3 = exports.pad2 = exports.leftPad = exports.toS = exports.notBlank = exports.blank = exports.isString = void 0; | ||
const Number_1 = require("./Number"); | ||
const Times_1 = require("./Times"); | ||
@@ -32,9 +33,10 @@ function isString(o) { | ||
function leftPad(i, minLen, padChar) { | ||
if (i == null || isNaN(i)) | ||
if (i == null || ((0, Number_1.isNumber)(i) && isNaN(i))) | ||
i = 0; | ||
if (i < 0) { | ||
return "-" + leftPad(-i, minLen - 1, padChar); | ||
const s = String(i); | ||
if ((0, Number_1.isNumber)(i) && i < 0 && padChar === "0") { | ||
// avoid "000-1": | ||
return "-" + padding(padChar, minLen - s.length) + Math.abs(i); | ||
} | ||
else { | ||
const s = String(i); | ||
return padding(padChar, minLen - s.length) + s; | ||
@@ -41,0 +43,0 @@ } |
@@ -7,3 +7,4 @@ import { Zone } from "luxon"; | ||
import { Tags } from "./Tags"; | ||
export declare const MaxTzOffsetHours = 14; | ||
declare const ValidTimezoneOffsets: readonly ["-12:00", "-11:00", "-10:30", "-10:00", "-09:30", "-09:00", "-08:30", "-08:00", "-07:30", "-07:00", "-06:00", "-05:00", "-04:30", "-04:00", "-03:30", "-03:00", "-02:30", "-02:00", "-01:00", "-00:44", "-00:25:21", "+00:00", "+00:20", "+00:30", "+01:00", "+01:24", "+01:30", "+02:00", "+02:30", "+03:00", "+03:30", "+04:00", "+04:30", "+04:51", "+05:00", "+05:30", "+05:40", "+05:45", "+06:00", "+06:30", "+07:00", "+07:20", "+07:30", "+08:00", "+08:30", "+08:45", "+09:00", "+09:30", "+09:45", "+10:00", "+10:30", "+11:00", "+11:30", "+12:00", "+12:45", "+13:00", "+13:45", "+14:00"]; | ||
export type TimezoneOffset = (typeof ValidTimezoneOffsets)[number]; | ||
/** | ||
@@ -29,3 +30,3 @@ * Zone instances with this offset are a placeholder for being "unset". | ||
export declare function normalizeZone(z: Maybe<string | number | Zone>): Maybe<Zone>; | ||
export declare function reasonableTzOffsetMinutes(tzOffsetMinutes: Maybe<number>): boolean; | ||
export declare function validTzOffsetMinutes(tzOffsetMinutes: Maybe<number>): tzOffsetMinutes is number; | ||
/** | ||
@@ -37,2 +38,7 @@ * Returns a "zone name" (used by `luxon`) that encodes the given offset. | ||
tz: string; | ||
/** | ||
* If given a string, this is the remaining string left after extracting the | ||
* timezone | ||
*/ | ||
leftovers?: string; | ||
src: string; | ||
@@ -43,3 +49,5 @@ } | ||
*/ | ||
export declare function extractOffset(value: Maybe<string | number | number[] | ExifDateTime | ExifTime>): Maybe<TzSrc>; | ||
export declare function extractZone(value: Maybe<string | number | number[] | ExifDateTime | ExifTime>, opts?: { | ||
stripTZA?: boolean; | ||
}): Maybe<TzSrc>; | ||
declare const TimezoneOffsetTagnames: readonly ["TimeZone", "OffsetTime", "OffsetTimeOriginal", "OffsetTimeDigitized", "TimeZoneOffset"]; | ||
@@ -64,2 +72,7 @@ declare const CreateDateTagnames: readonly ["SubSecCreateDate", "CreateDate", "SubSecDateTimeOriginal", "DateTimeOriginal", "SubSecMediaCreateDate", "MediaCreateDate", "CreationDate", "TimeCreated"]; | ||
export declare function equivalentZones(a: Maybe<string | number | Zone>, b: Maybe<string | number | Zone>): boolean; | ||
export declare function getZoneName(args?: { | ||
zone?: Zone; | ||
zoneName?: Maybe<string>; | ||
tzoffsetMinutes?: Maybe<number>; | ||
}): Maybe<string>; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.equivalentZones = exports.extractTzOffsetFromUTCOffset = exports.inferLikelyOffsetMinutes = exports.extractTzOffsetFromTags = exports.extractOffset = exports.offsetMinutesToZoneName = exports.reasonableTzOffsetMinutes = exports.normalizeZone = exports.defaultVideosToUTC = exports.UnsetZoneName = exports.UnsetZone = exports.UnsetZoneOffsetMinutes = exports.MaxTzOffsetHours = void 0; | ||
exports.getZoneName = exports.equivalentZones = exports.extractTzOffsetFromUTCOffset = exports.inferLikelyOffsetMinutes = exports.extractTzOffsetFromTags = exports.extractZone = exports.offsetMinutesToZoneName = exports.validTzOffsetMinutes = exports.normalizeZone = exports.defaultVideosToUTC = exports.UnsetZoneName = exports.UnsetZone = exports.UnsetZoneOffsetMinutes = void 0; | ||
const luxon_1 = require("luxon"); | ||
@@ -8,8 +8,77 @@ const Array_1 = require("./Array"); | ||
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 String_1 = require("./String"); | ||
// Pacific/Kiritimati is +14:00 TIL | ||
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones | ||
exports.MaxTzOffsetHours = 14; | ||
// Unique values from https://en.wikipedia.org/wiki/List_of_tz_database_time_zones | ||
const ValidTimezoneOffsets = [ | ||
"-12:00", | ||
"-11:00", | ||
"-10:30", | ||
"-10:00", | ||
"-09:30", | ||
"-09:00", | ||
"-08:30", | ||
"-08:00", | ||
"-07:30", | ||
"-07:00", | ||
"-06:00", | ||
"-05:00", | ||
"-04:30", | ||
"-04:00", | ||
"-03:30", | ||
"-03:00", | ||
"-02:30", | ||
"-02:00", | ||
"-01:00", | ||
"-00:44", | ||
"-00:25:21", | ||
"+00:00", | ||
"+00:20", | ||
"+00:30", | ||
"+01:00", | ||
"+01:24", | ||
"+01:30", | ||
"+02:00", | ||
"+02:30", | ||
"+03:00", | ||
"+03:30", | ||
"+04:00", | ||
"+04:30", | ||
"+04:51", | ||
"+05:00", | ||
"+05:30", | ||
"+05:40", | ||
"+05:45", | ||
"+06:00", | ||
"+06:30", | ||
"+07:00", | ||
"+07:20", | ||
"+07:30", | ||
"+08:00", | ||
"+08:30", | ||
"+08:45", | ||
"+09:00", | ||
"+09:30", | ||
"+09:45", | ||
"+10:00", | ||
"+10:30", | ||
"+11:00", | ||
"+11:30", | ||
"+12:00", | ||
"+12:45", | ||
"+13:00", | ||
"+13:45", | ||
"+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; | ||
} | ||
// export const localTzOffsetMinutes = lazy(() => DateTime.local().offset, hourMs) | ||
const ValidOffsetMinutes = (0, Lazy_1.lazy)(() => new Set(ValidTimezoneOffsets.map(offsetToMinutes))); | ||
/** | ||
@@ -36,6 +105,10 @@ * Zone instances with this offset are a placeholder for being "unset". | ||
try { | ||
// Info.normalizeZone returns the system zone if the input is null or | ||
// blank (!!!), but we want to return undefined instead--we don't want to introduce the system zone by accident! | ||
if (z == null || (0, String_1.blank)(String(z))) | ||
return; | ||
const zone = luxon_1.Info.normalizeZone(z); | ||
return (zone === null || zone === void 0 ? void 0 : zone.isValid) === true ? zone : undefined; | ||
return zone != null && zone.isValid && zone.name !== exports.UnsetZoneName | ||
? zone | ||
: undefined; | ||
} | ||
@@ -47,8 +120,9 @@ catch { | ||
exports.normalizeZone = normalizeZone; | ||
function reasonableTzOffsetMinutes(tzOffsetMinutes) { | ||
return ((0, Number_1.isNumber)(tzOffsetMinutes) && | ||
function validTzOffsetMinutes(tzOffsetMinutes) { | ||
return (tzOffsetMinutes != null && | ||
(0, Number_1.isNumber)(tzOffsetMinutes) && | ||
tzOffsetMinutes !== exports.UnsetZoneOffsetMinutes && | ||
Math.abs(tzOffsetMinutes) < exports.MaxTzOffsetHours * 60); | ||
ValidOffsetMinutes().has(tzOffsetMinutes)); | ||
} | ||
exports.reasonableTzOffsetMinutes = reasonableTzOffsetMinutes; | ||
exports.validTzOffsetMinutes = validTzOffsetMinutes; | ||
/** | ||
@@ -58,5 +132,3 @@ * Returns a "zone name" (used by `luxon`) that encodes the given offset. | ||
function offsetMinutesToZoneName(offsetMinutes) { | ||
if (offsetMinutes == null || | ||
!(0, Number_1.isNumber)(offsetMinutes) || | ||
offsetMinutes === exports.UnsetZoneOffsetMinutes) { | ||
if (!validTzOffsetMinutes(offsetMinutes)) { | ||
return undefined; | ||
@@ -68,4 +140,2 @@ } | ||
const absMinutes = Math.abs(offsetMinutes); | ||
if (absMinutes > exports.MaxTzOffsetHours * 60) | ||
return undefined; | ||
const hours = Math.floor(absMinutes / 60); | ||
@@ -78,8 +148,10 @@ const minutes = Math.abs(absMinutes % 60); | ||
function tzHourToOffset(n) { | ||
return (0, Number_1.isNumber)(n) && reasonableTzOffsetMinutes(n * 60) | ||
return (0, Number_1.isNumber)(n) && validTzOffsetMinutes(n * 60) | ||
? offsetMinutesToZoneName(n * 60) | ||
: undefined; | ||
} | ||
const utcTzRe = /(?:UTC)?(?<sign>[+-]?)(?<hours>\d\d?)(?::(?<minutes>\d\d))?/; | ||
const timestampTzRe = /(?<sign>[+-]?)(?<hours>\d\d?)(?::(?<minutes>\d\d))$/; | ||
// 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) { | ||
@@ -98,6 +170,8 @@ return (0, Number_1.isNumber)(hourOffset) | ||
*/ | ||
function extractOffset(value) { | ||
var _a, _b, _c, _d, _e; | ||
if (value instanceof ExifDateTime_1.ExifDateTime) { | ||
return (0, Maybe_1.map)(value.zone, (tz) => ({ tz, src: "ExifDateTime" })); | ||
function extractZone(value, opts) { | ||
var _a, _b, _c; | ||
if (value instanceof ExifDateTime_1.ExifDateTime || value instanceof ExifTime_1.ExifTime) { | ||
return value.zone == null | ||
? undefined | ||
: { tz: value.zone, src: value.constructor.name + ".zone" }; | ||
} | ||
@@ -107,33 +181,60 @@ if ((0, Number_1.isNumber)(value) || Array.isArray(value)) { | ||
} | ||
if ((0, String_1.isString)(value) && luxon_1.Info.isValidIANAZone(value)) { | ||
return { tz: value, src: "validIANAZone" }; | ||
let str = (_a = value.rawValue) !== null && _a !== void 0 ? _a : (0, String_1.toS)(value); | ||
{ | ||
if ((0, String_1.blank)(str)) | ||
return; | ||
const z = normalizeZone(str); | ||
if (z != null) { | ||
return { tz: z.name, src: "normalizeZone" }; | ||
} | ||
} | ||
// ExifTime will have a rawValue, but doesn't support zone extraction: | ||
const str = (_a = value.rawValue) !== null && _a !== void 0 ? _a : (0, String_1.toS)(value); | ||
if ((0, String_1.blank)(str)) | ||
return; | ||
for (const g of (0, Array_1.compact)([ | ||
(_b = utcTzRe.exec(str)) === null || _b === void 0 ? void 0 : _b.groups, | ||
(_c = timestampTzRe.exec(str)) === null || _c === void 0 ? void 0 : _c.groups, | ||
])) { | ||
const tz = offsetMinutesToZoneName((g.sign === "-" ? -1 : 1) * | ||
(parseInt((_d = g.hours) !== null && _d !== void 0 ? _d : "0") * 60 + parseInt((_e = g.minutes) !== null && _e !== void 0 ? _e : "0"))); | ||
if (tz != null) | ||
return { tz, src: "offsetMinutesToZoneName" }; | ||
// 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 === null || opts === void 0 ? void 0 : 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 { tz: z.name, src: "normalizeZone" }; | ||
} | ||
} | ||
const m = tzRe.exec(str); | ||
const g = m === null || m === void 0 ? void 0 : m.groups; | ||
if (m != null && g != null) { | ||
const leftovers = str.slice(0, m.index); | ||
if (g.Z === "Z") | ||
return { | ||
tz: luxon_1.FixedOffsetZone.utcInstance.name, | ||
src: "Z", | ||
leftovers, | ||
}; | ||
const offsetMinutes = (g.sign === "-" ? -1 : 1) * | ||
(parseInt((_b = g.hours) !== null && _b !== void 0 ? _b : "0") * 60 + parseInt((_c = g.minutes) !== null && _c !== void 0 ? _c : "0")); | ||
const tz = offsetMinutesToZoneName(offsetMinutes); | ||
if (tz != null) { | ||
return { tz, src: "offsetMinutesToZoneName", leftovers }; | ||
} | ||
} | ||
return; | ||
} | ||
exports.extractOffset = extractOffset; | ||
exports.extractZone = extractZone; | ||
const TimezoneOffsetTagnames = [ | ||
"TimeZone", | ||
"OffsetTime", | ||
/** time zone for DateTimeOriginal, "-08:00" */ | ||
// time zone for DateTimeOriginal, "-08:00" | ||
"OffsetTimeOriginal", | ||
/** time zone for CreateDate, "-08:00" */ | ||
// time zone for CreateDate, "-08:00" | ||
"OffsetTimeDigitized", | ||
/** | ||
* 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 | ||
*/ | ||
// 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 | ||
@@ -163,3 +264,3 @@ ]; | ||
if (t[tagName] != null && !tagName.startsWith("File")) { | ||
const offset = extractOffset(t[tagName]); | ||
const offset = extractZone(t[tagName]); | ||
if (offset != null) { | ||
@@ -245,2 +346,8 @@ return { tz: offset.tz, src: tagName }; | ||
exports.equivalentZones = equivalentZones; | ||
function getZoneName(args = {}) { | ||
var _a, _b, _c, _d; | ||
const result = (_d = (_b = (_a = normalizeZone(args.zone)) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : (_c = normalizeZone(args.zoneName)) === null || _c === void 0 ? void 0 : _c.name) !== null && _d !== void 0 ? _d : offsetMinutesToZoneName(args.tzoffsetMinutes); | ||
return (0, String_1.blank)(result) || result === exports.UnsetZoneName ? undefined : result; | ||
} | ||
exports.getZoneName = getZoneName; | ||
//# sourceMappingURL=Timezones.js.map |
@@ -132,3 +132,2 @@ "use strict"; | ||
args.push(sourceFile); | ||
// console.log("new WriteTask()", { sourceFile, args, tags }) | ||
return new WriteTask(sourceFile, args); | ||
@@ -140,3 +139,2 @@ } | ||
parse(data, error) { | ||
// console.log(this.toString() + ".parse()", { data, error }) | ||
if (error != null) | ||
@@ -143,0 +141,0 @@ throw error; |
{ | ||
"name": "exiftool-vendored", | ||
"version": "23.0.0", | ||
"version": "23.1.0", | ||
"description": "Efficient, cross-platform access to ExifTool", | ||
@@ -12,2 +12,3 @@ "main": "./dist/ExifTool.js", | ||
"scripts": { | ||
"u": "yarn npm-check-updates -u --install always", | ||
"ci": "yarn install --frozen-lockfile", | ||
@@ -73,16 +74,16 @@ "clean": "rimraf lib dist coverage .nyc_output", | ||
"@types/chai-subset": "^1.3.3", | ||
"@types/globule": "^1.1.6", | ||
"@types/he": "^1.2.0", | ||
"@types/mocha": "^10.0.1", | ||
"@types/node": "^20.6.0", | ||
"@types/globule": "^1.1.7", | ||
"@types/he": "^1.2.1", | ||
"@types/mocha": "^10.0.2", | ||
"@types/node": "^20.8.0", | ||
"@types/progress": "^2.0.5", | ||
"@types/tmp": "^0.2.4", | ||
"@types/xmldom": "^0.1.32", | ||
"@typescript-eslint/eslint-plugin": "^6.7.0", | ||
"@typescript-eslint/parser": "^6.7.0", | ||
"@typescript-eslint/eslint-plugin": "^6.7.3", | ||
"@typescript-eslint/parser": "^6.7.3", | ||
"@xmldom/xmldom": "^0.8.10", | ||
"chai": "^4.3.8", | ||
"chai": "^4.3.10", | ||
"chai-as-promised": "^7.1.1", | ||
"chai-subset": "^1.6.0", | ||
"eslint": "^8.49.0", | ||
"eslint": "^8.50.0", | ||
"eslint-plugin-import": "^2.28.1", | ||
@@ -94,2 +95,3 @@ "eslint-plugin-node": "^11.1.0", | ||
"mocha": "^10.2.0", | ||
"npm-check-updates": "^16.14.4", | ||
"npm-run-all": "^4.1.5", | ||
@@ -99,3 +101,3 @@ "prettier": "^3.0.3", | ||
"progress": "^2.0.3", | ||
"rimraf": "^5.0.1", | ||
"rimraf": "^5.0.5", | ||
"serve": "^14.2.1", | ||
@@ -117,5 +119,5 @@ "source-map-support": "^0.5.21", | ||
"optionalDependencies": { | ||
"exiftool-vendored.exe": "12.65.0", | ||
"exiftool-vendored.pl": "12.65.0" | ||
"exiftool-vendored.exe": "12.67.0", | ||
"exiftool-vendored.pl": "12.67.0" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
509216
168
8706
35