exiftool-vendored 33.1.0 → 33.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/ExifTool.d.ts +2 -1
- package/dist/ExifTool.js +13 -1
- package/dist/ExifTool.js.map +1 -1
- package/dist/Settings.d.ts +16 -0
- package/dist/Settings.js +16 -0
- package/dist/Settings.js.map +1 -1
- package/dist/Tags.d.ts +24 -24
- package/dist/Timezones.d.ts +470 -11
- package/dist/Timezones.js +485 -21
- package/dist/Timezones.js.map +1 -1
- package/package.json +4 -4
package/dist/Timezones.d.ts
CHANGED
|
@@ -25,9 +25,105 @@ export declare const UnsetZone: Zone<boolean>;
|
|
|
25
25
|
* Zone instances with this name are a placeholder for being "unset".
|
|
26
26
|
*/
|
|
27
27
|
export declare const UnsetZoneName: string;
|
|
28
|
+
/**
|
|
29
|
+
* Check if a timezone value represents UTC.
|
|
30
|
+
*
|
|
31
|
+
* Handles multiple UTC representations including Zone instances, strings, and
|
|
32
|
+
* numeric offsets. Recognizes common UTC aliases like "GMT", "Z", "Zulu",
|
|
33
|
+
* "+0", "+00:00", etc.
|
|
34
|
+
*
|
|
35
|
+
* @param zone - Timezone to check (Zone, string, or number)
|
|
36
|
+
* @returns true if the zone represents UTC/GMT/Zulu
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* isUTC("UTC") // true
|
|
41
|
+
* isUTC("GMT") // true
|
|
42
|
+
* isUTC("Z") // true
|
|
43
|
+
* isUTC("Zulu") // true
|
|
44
|
+
* isUTC(0) // true
|
|
45
|
+
* isUTC("+00:00") // true
|
|
46
|
+
* isUTC("UTC+0") // true
|
|
47
|
+
* isUTC("America/New_York") // false
|
|
48
|
+
* isUTC("+08:00") // false
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
28
51
|
export declare function isUTC(zone: unknown): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Check if a Zone is the library's sentinel "unset" value.
|
|
54
|
+
*
|
|
55
|
+
* The library uses a special Zone instance to represent unknown/unset
|
|
56
|
+
* timezones since Luxon doesn't officially support unset zones.
|
|
57
|
+
*
|
|
58
|
+
* @param zone - Zone instance to check
|
|
59
|
+
* @returns true if the zone is the UnsetZone sentinel value
|
|
60
|
+
*
|
|
61
|
+
* @see {@link UnsetZone}
|
|
62
|
+
* @see {@link UnsetZoneName}
|
|
63
|
+
* @see {@link UnsetZoneOffsetMinutes}
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* isZoneUnset(UnsetZone) // true
|
|
68
|
+
* isZoneUnset(Info.normalizeZone("UTC")) // false
|
|
69
|
+
* isZoneUnset(Info.normalizeZone("UTC+8")) // false
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
29
72
|
export declare function isZoneUnset(zone: Zone): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Type guard to check if a Zone is valid and usable.
|
|
75
|
+
*
|
|
76
|
+
* A zone is considered valid if it:
|
|
77
|
+
* - Is not null/undefined
|
|
78
|
+
* - Has `isValid === true` (Luxon requirement)
|
|
79
|
+
* - Is not the library's UnsetZone sentinel
|
|
80
|
+
* - Has an offset within ±14 hours (the valid range for real-world timezones)
|
|
81
|
+
*
|
|
82
|
+
* This is the canonical validation check used throughout the library.
|
|
83
|
+
*
|
|
84
|
+
* @param zone - Zone to validate
|
|
85
|
+
* @returns true if the zone is valid and usable (type guard)
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const zone = Info.normalizeZone("America/Los_Angeles")
|
|
90
|
+
* if (isZoneValid(zone)) {
|
|
91
|
+
* // TypeScript knows zone is Zone (not Zone | undefined)
|
|
92
|
+
* console.log(zone.name)
|
|
93
|
+
* }
|
|
94
|
+
*
|
|
95
|
+
* isZoneValid(Info.normalizeZone("invalid")) // false
|
|
96
|
+
* isZoneValid(Info.normalizeZone("UTC+8")) // true
|
|
97
|
+
* isZoneValid(UnsetZone) // false
|
|
98
|
+
* isZoneValid(Info.normalizeZone("UTC+20")) // false (beyond ±14 hours)
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
30
101
|
export declare function isZoneValid(zone: Maybe<Zone>): zone is Zone;
|
|
102
|
+
/**
|
|
103
|
+
* Type guard to check if a value is a Luxon Zone instance.
|
|
104
|
+
*
|
|
105
|
+
* Checks both `instanceof Zone` and constructor name to handle cross-module
|
|
106
|
+
* Zone instances that may not pass instanceof checks.
|
|
107
|
+
*
|
|
108
|
+
* @param zone - Value to check
|
|
109
|
+
* @returns true if the value is a Zone instance (type guard)
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* import { Info } from "luxon"
|
|
114
|
+
*
|
|
115
|
+
* const zone = Info.normalizeZone("UTC+8")
|
|
116
|
+
* if (isZone(zone)) {
|
|
117
|
+
* // TypeScript knows zone is Zone (not unknown)
|
|
118
|
+
* console.log(zone.offset(Date.now()))
|
|
119
|
+
* }
|
|
120
|
+
*
|
|
121
|
+
* isZone(Info.normalizeZone("UTC")) // true
|
|
122
|
+
* isZone("UTC") // false
|
|
123
|
+
* isZone(480) // false
|
|
124
|
+
* isZone(null) // false
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
31
127
|
export declare function isZone(zone: unknown): zone is Zone;
|
|
32
128
|
/**
|
|
33
129
|
* If `tzSource` matches this value, the tags are from a video, and we had to
|
|
@@ -36,16 +132,119 @@ export declare function isZone(zone: unknown): zone is Zone;
|
|
|
36
132
|
*/
|
|
37
133
|
export declare const defaultVideosToUTC = "defaultVideosToUTC";
|
|
38
134
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
135
|
+
* Normalize a timezone input to a valid Luxon Zone.
|
|
136
|
+
*
|
|
137
|
+
* Accepts multiple input formats and returns a validated Zone instance, or
|
|
138
|
+
* undefined if the input cannot be normalized to a valid timezone.
|
|
139
|
+
*
|
|
140
|
+
* Supported input formats:
|
|
141
|
+
* - **Numbers**: Timezone offset in minutes (e.g., 480 = UTC+8, -300 = UTC-5)
|
|
142
|
+
* - **Strings**: ISO offsets ("+08:00", "-05:00"), IANA zones
|
|
143
|
+
* ("America/Los_Angeles"), UTC variants ("UTC", "GMT", "Z", "Zulu")
|
|
144
|
+
* - **Zone instances**: Validated and returned if valid
|
|
145
|
+
*
|
|
146
|
+
* The function respects Settings:
|
|
147
|
+
* - {@link Settings.allowArchaicTimezoneOffsets} for pre-1982 offsets
|
|
148
|
+
* - {@link Settings.allowBakerIslandTime} for UTC-12:00
|
|
149
|
+
*
|
|
150
|
+
* @param input - Timezone in various formats
|
|
151
|
+
* @returns Valid Zone instance, or undefined if invalid
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* // Numbers (offset in minutes)
|
|
156
|
+
* normalizeZone(480)?.name // "UTC+8"
|
|
157
|
+
* normalizeZone(-300)?.name // "UTC-5"
|
|
158
|
+
* normalizeZone(0)?.name // "UTC"
|
|
159
|
+
*
|
|
160
|
+
* // ISO offset strings
|
|
161
|
+
* normalizeZone("+08:00")?.name // "UTC+8"
|
|
162
|
+
* normalizeZone("-05:30")?.name // "UTC-5:30"
|
|
163
|
+
* normalizeZone("UTC+7")?.name // "UTC+7"
|
|
164
|
+
*
|
|
165
|
+
* // IANA timezone names
|
|
166
|
+
* normalizeZone("America/Los_Angeles")?.name // "America/Los_Angeles"
|
|
167
|
+
* normalizeZone("Asia/Tokyo")?.name // "Asia/Tokyo"
|
|
168
|
+
*
|
|
169
|
+
* // UTC aliases
|
|
170
|
+
* normalizeZone("UTC")?.name // "UTC"
|
|
171
|
+
* normalizeZone("GMT")?.name // "UTC"
|
|
172
|
+
* normalizeZone("Z")?.name // "UTC"
|
|
173
|
+
* normalizeZone("Zulu")?.name // "UTC"
|
|
174
|
+
*
|
|
175
|
+
* // Invalid inputs return undefined
|
|
176
|
+
* normalizeZone("invalid") // undefined
|
|
177
|
+
* normalizeZone("+25:00") // undefined (beyond ±14 hours)
|
|
178
|
+
* normalizeZone(null) // undefined
|
|
179
|
+
* ```
|
|
41
180
|
*/
|
|
42
181
|
export declare function normalizeZone(input: unknown): Maybe<Zone>;
|
|
43
182
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
183
|
+
* Convert a timezone to its short offset format (e.g., "+08:00", "-05:00").
|
|
184
|
+
*
|
|
185
|
+
* Useful for displaying timezone offsets in a standardized format. For IANA
|
|
186
|
+
* zones with daylight saving time, provide a timestamp to get the correct
|
|
187
|
+
* offset for that moment.
|
|
188
|
+
*
|
|
189
|
+
* @param zone - Timezone as Zone, string, or offset in minutes
|
|
190
|
+
* @param ts - Optional timestamp (milliseconds) for IANA zone offset calculation.
|
|
191
|
+
* Defaults to current time if not provided.
|
|
192
|
+
* @returns Zone offset in "+HH:MM" format, or "" if zone is invalid
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```typescript
|
|
196
|
+
* // Fixed offsets
|
|
197
|
+
* zoneToShortOffset("UTC+8") // "+08:00"
|
|
198
|
+
* zoneToShortOffset(480) // "+08:00"
|
|
199
|
+
* zoneToShortOffset("UTC-5:30") // "-05:30"
|
|
200
|
+
*
|
|
201
|
+
* // IANA zones (offset depends on DST)
|
|
202
|
+
* const winter = new Date("2023-01-15").getTime()
|
|
203
|
+
* const summer = new Date("2023-07-15").getTime()
|
|
204
|
+
* zoneToShortOffset("America/Los_Angeles", winter) // "-08:00" (PST)
|
|
205
|
+
* zoneToShortOffset("America/Los_Angeles", summer) // "-07:00" (PDT)
|
|
206
|
+
*
|
|
207
|
+
* // Invalid zones return empty string
|
|
208
|
+
* zoneToShortOffset("invalid") // ""
|
|
209
|
+
* zoneToShortOffset(null) // ""
|
|
210
|
+
* ```
|
|
47
211
|
*/
|
|
48
212
|
export declare function zoneToShortOffset(zone: Maybe<string | number | Zone>, ts?: number): string;
|
|
213
|
+
/**
|
|
214
|
+
* Type guard to check if a numeric offset (in minutes) represents a valid timezone.
|
|
215
|
+
*
|
|
216
|
+
* Validates that the offset:
|
|
217
|
+
* - Is a number (not null/undefined)
|
|
218
|
+
* - Is not the UnsetZone sentinel value (-1)
|
|
219
|
+
* - Matches a real-world timezone offset (respects Settings for archaic offsets)
|
|
220
|
+
*
|
|
221
|
+
* Use this for exact validation without rounding. For error-tolerant rounding to
|
|
222
|
+
* the nearest valid offset, use {@link inferLikelyOffsetMinutes} instead.
|
|
223
|
+
*
|
|
224
|
+
* @param tzOffsetMinutes - Offset in minutes to validate (e.g., 480 for UTC+8)
|
|
225
|
+
* @returns true if the offset is exactly valid (type guard)
|
|
226
|
+
*
|
|
227
|
+
* @see {@link inferLikelyOffsetMinutes} for error-tolerant rounding
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* validTzOffsetMinutes(480) // true (UTC+8)
|
|
232
|
+
* validTzOffsetMinutes(-300) // true (UTC-5)
|
|
233
|
+
* validTzOffsetMinutes(330) // true (UTC+5:30, India)
|
|
234
|
+
* validTzOffsetMinutes(345) // true (UTC+5:45, Nepal)
|
|
235
|
+
*
|
|
236
|
+
* validTzOffsetMinutes(481) // false (not a valid timezone)
|
|
237
|
+
* validTzOffsetMinutes(-1) // false (UnsetZone sentinel)
|
|
238
|
+
* validTzOffsetMinutes(null) // false
|
|
239
|
+
*
|
|
240
|
+
* // Archaic offsets require Settings
|
|
241
|
+
* Settings.allowArchaicTimezoneOffsets.value = false
|
|
242
|
+
* validTzOffsetMinutes(-630) // false (Hawaii -10:30, archaic)
|
|
243
|
+
*
|
|
244
|
+
* Settings.allowArchaicTimezoneOffsets.value = true
|
|
245
|
+
* validTzOffsetMinutes(-630) // true (Hawaii -10:30, archaic)
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
49
248
|
export declare function validTzOffsetMinutes(tzOffsetMinutes: Maybe<number>): tzOffsetMinutes is number;
|
|
50
249
|
/**
|
|
51
250
|
* Returns a "zone name" (used by `luxon`) that encodes the given offset.
|
|
@@ -71,24 +270,284 @@ export interface TzSrc {
|
|
|
71
270
|
src: string;
|
|
72
271
|
}
|
|
73
272
|
/**
|
|
74
|
-
*
|
|
273
|
+
* Extract timezone information from various value types.
|
|
274
|
+
*
|
|
275
|
+
* Handles multiple input formats and performs intelligent parsing:
|
|
276
|
+
* - **Strings**: ISO offsets ("+08:00"), IANA zones, UTC variants, timestamps
|
|
277
|
+
* with embedded timezones ("2023:01:15 10:30:00-08:00")
|
|
278
|
+
* - **Numbers**: Hour offsets (e.g., -8 for UTC-8)
|
|
279
|
+
* - **Arrays**: Uses first non-null value
|
|
280
|
+
* - **ExifDateTime/ExifTime instances**: Extracts their zone property
|
|
281
|
+
*
|
|
282
|
+
* By default, strips timezone abbreviations (PST, PDT, etc.) as they are
|
|
283
|
+
* ambiguous. Returns provenance information indicating which parsing method
|
|
284
|
+
* succeeded.
|
|
285
|
+
*
|
|
286
|
+
* Supports Unicode minus signs (−, U+2212) and plus-minus signs (±, U+00B1)
|
|
287
|
+
* in addition to ASCII +/-.
|
|
288
|
+
*
|
|
289
|
+
* @param value - Value to extract timezone from
|
|
290
|
+
* @param opts.stripTZA - Whether to strip timezone abbreviations (default: true).
|
|
291
|
+
* TZAs like "PST" are ambiguous and usually stripped.
|
|
292
|
+
* @returns TzSrc with zone name and provenance, or undefined if no timezone found
|
|
75
293
|
*
|
|
76
|
-
* @
|
|
77
|
-
*
|
|
294
|
+
* @example
|
|
295
|
+
* ```typescript
|
|
296
|
+
* // ISO offset strings
|
|
297
|
+
* extractZone("+08:00")
|
|
298
|
+
* // { zone: "UTC+8", tz: "UTC+8", src: "offsetMinutesToZoneName" }
|
|
78
299
|
*
|
|
79
|
-
*
|
|
300
|
+
* extractZone("UTC-5:30")
|
|
301
|
+
* // { zone: "UTC-5:30", tz: "UTC-5:30", src: "normalizeZone" }
|
|
302
|
+
*
|
|
303
|
+
* // IANA zone names
|
|
304
|
+
* extractZone("America/Los_Angeles")
|
|
305
|
+
* // { zone: "America/Los_Angeles", tz: "America/Los_Angeles", src: "normalizeZone" }
|
|
306
|
+
*
|
|
307
|
+
* // Timestamps with embedded timezones
|
|
308
|
+
* extractZone("2023:01:15 10:30:00-08:00")
|
|
309
|
+
* // { zone: "UTC-8", tz: "UTC-8", src: "offsetMinutesToZoneName",
|
|
310
|
+
* // leftovers: "2023:01:15 10:30:00" }
|
|
311
|
+
*
|
|
312
|
+
* // Unicode minus signs
|
|
313
|
+
* extractZone("−08:00") // Unicode minus (U+2212)
|
|
314
|
+
* // { zone: "UTC-8", tz: "UTC-8", src: "offsetMinutesToZoneName" }
|
|
315
|
+
*
|
|
316
|
+
* // Numeric hour offsets
|
|
317
|
+
* extractZone(-8)
|
|
318
|
+
* // { zone: "UTC-8", tz: "UTC-8", src: "hourOffset" }
|
|
319
|
+
*
|
|
320
|
+
* // Arrays (uses first non-null)
|
|
321
|
+
* extractZone([null, "+05:30", undefined])
|
|
322
|
+
* // { zone: "UTC+5:30", tz: "UTC+5:30", src: "offsetMinutesToZoneName" }
|
|
323
|
+
*
|
|
324
|
+
* // Strips timezone abbreviations by default
|
|
325
|
+
* extractZone("2023:01:15 10:30:00-08:00 PST")
|
|
326
|
+
* // { zone: "UTC-8", tz: "UTC-8", src: "offsetMinutesToZoneName",
|
|
327
|
+
* // leftovers: "2023:01:15 10:30:00" }
|
|
328
|
+
*
|
|
329
|
+
* // Invalid inputs return undefined
|
|
330
|
+
* extractZone("invalid") // undefined
|
|
331
|
+
* extractZone(null) // undefined
|
|
332
|
+
* ```
|
|
80
333
|
*/
|
|
81
334
|
export declare function extractZone(value: unknown, opts?: {
|
|
82
335
|
stripTZA?: boolean;
|
|
83
336
|
}): Maybe<TzSrc>;
|
|
84
337
|
export declare const TimezoneOffsetTagnames: readonly ["TimeZone", "OffsetTimeOriginal", "OffsetTimeDigitized", "TimeZoneOffset"];
|
|
85
338
|
export declare function incrementZone(z: string | Zone | number, minutes: number): Maybe<Zone>;
|
|
339
|
+
/**
|
|
340
|
+
* Extract timezone offset from standard EXIF timezone tags.
|
|
341
|
+
*
|
|
342
|
+
* Checks timezone tags in priority order:
|
|
343
|
+
* 1. TimeZone
|
|
344
|
+
* 2. OffsetTimeOriginal (for DateTimeOriginal)
|
|
345
|
+
* 3. OffsetTimeDigitized (for CreateDate)
|
|
346
|
+
* 4. TimeZoneOffset
|
|
347
|
+
*
|
|
348
|
+
* Handles camera-specific quirks like Nikon's DaylightSavings tag, which
|
|
349
|
+
* requires adjusting the TimeZone offset forward by one hour during DST.
|
|
350
|
+
*
|
|
351
|
+
* @param t - EXIF tags object
|
|
352
|
+
* @param opts.adjustTimeZoneIfDaylightSavings - Optional function to adjust
|
|
353
|
+
* timezone for DST. Defaults to handling Nikon's DaylightSavings quirk.
|
|
354
|
+
* @returns TzSrc with zone and provenance, or undefined if no timezone found
|
|
355
|
+
*
|
|
356
|
+
* @see {@link TimezoneOffsetTagnames} for the list of tags checked
|
|
357
|
+
* @see https://github.com/photostructure/exiftool-vendored.js/issues/215
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```typescript
|
|
361
|
+
* const tags = await exiftool.read("photo.jpg")
|
|
362
|
+
*
|
|
363
|
+
* const tzSrc = extractTzOffsetFromTags(tags)
|
|
364
|
+
* if (tzSrc) {
|
|
365
|
+
* console.log(`Timezone: ${tzSrc.zone}`)
|
|
366
|
+
* console.log(`Source: ${tzSrc.src}`) // e.g., "OffsetTimeOriginal"
|
|
367
|
+
* }
|
|
368
|
+
*
|
|
369
|
+
* // Nikon DST handling
|
|
370
|
+
* const nikonTags = {
|
|
371
|
+
* TimeZone: "-08:00",
|
|
372
|
+
* DaylightSavings: "Yes",
|
|
373
|
+
* Make: "NIKON CORPORATION"
|
|
374
|
+
* }
|
|
375
|
+
* extractTzOffsetFromTags(nikonTags)
|
|
376
|
+
* // { zone: "UTC-7", tz: "UTC-7",
|
|
377
|
+
* // src: "TimeZone (adjusted for DaylightSavings)" }
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
86
380
|
export declare function extractTzOffsetFromTags(t: Tags, opts?: Pick<ExifToolOptions, "adjustTimeZoneIfDaylightSavings">): Maybe<TzSrc>;
|
|
87
381
|
export declare function extractTzOffsetFromDatestamps(t: Tags, opts: Partial<Pick<ExifToolOptions, "inferTimezoneFromDatestamps" | "inferTimezoneFromDatestampTags">>): Maybe<TzSrc>;
|
|
88
382
|
export declare function extractTzOffsetFromTimeStamp(t: Tags, opts: Partial<Pick<ExifToolOptions, "inferTimezoneFromTimeStamp" | "inferTimezoneFromDatestampTags">>): Maybe<TzSrc>;
|
|
89
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Round an arbitrary offset to the nearest valid timezone offset.
|
|
385
|
+
*
|
|
386
|
+
* This is error-tolerant timezone inference, useful for:
|
|
387
|
+
* - GPS-based timezone calculation (where GPS time drift may cause errors)
|
|
388
|
+
* - Handling clock drift in timestamp comparisons
|
|
389
|
+
* - Fuzzy timezone matching
|
|
390
|
+
*
|
|
391
|
+
* By default, uses {@link Settings.maxValidOffsetMinutes} (30 minutes) as the
|
|
392
|
+
* maximum distance from a valid timezone. This threshold handles GPS acquisition
|
|
393
|
+
* lag and clock drift while preventing false matches.
|
|
394
|
+
*
|
|
395
|
+
* Respects Settings for archaic offsets, Baker Island time, and max offset tolerance.
|
|
396
|
+
*
|
|
397
|
+
* @param deltaMinutes - Offset in minutes to round (can be fractional)
|
|
398
|
+
* @param maxValidOffsetMinutes - Maximum distance (in minutes) from a valid
|
|
399
|
+
* timezone to accept. Defaults to {@link Settings.maxValidOffsetMinutes}.
|
|
400
|
+
* @returns Nearest valid offset in minutes, or undefined if too far from any
|
|
401
|
+
* valid timezone
|
|
402
|
+
*
|
|
403
|
+
* @see {@link validTzOffsetMinutes} for exact validation without rounding
|
|
404
|
+
* @see {@link Settings.maxValidOffsetMinutes} to configure the default threshold
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* ```typescript
|
|
408
|
+
* // Exact matches
|
|
409
|
+
* inferLikelyOffsetMinutes(480) // 480 (UTC+8, exact)
|
|
410
|
+
* inferLikelyOffsetMinutes(-300) // -300 (UTC-5, exact)
|
|
411
|
+
*
|
|
412
|
+
* // Rounding within default threshold (30 minutes)
|
|
413
|
+
* inferLikelyOffsetMinutes(485) // 480 (UTC+8, rounded from 485)
|
|
414
|
+
* inferLikelyOffsetMinutes(-295) // -300 (UTC-5, rounded from -295)
|
|
415
|
+
* inferLikelyOffsetMinutes(330.5) // 330 (UTC+5:30, rounded)
|
|
416
|
+
*
|
|
417
|
+
* // GPS-based inference with clock drift (within 30 min default)
|
|
418
|
+
* const gpsTime = "2023:01:15 19:30:45" // UTC
|
|
419
|
+
* const localTime = "2023:01:15 11:32:12" // Local with 1.5min drift
|
|
420
|
+
* const deltaMinutes = 480 + 1.5 // ~481.5 minutes
|
|
421
|
+
* inferLikelyOffsetMinutes(deltaMinutes) // 480 (UTC+8)
|
|
422
|
+
*
|
|
423
|
+
* // GPS lag up to 23 minutes still works (within 30 min threshold)
|
|
424
|
+
* inferLikelyOffsetMinutes(443) // 420 (UTC-7, ~23 min from actual)
|
|
425
|
+
*
|
|
426
|
+
* // Beyond threshold returns undefined
|
|
427
|
+
* inferLikelyOffsetMinutes(100) // undefined (not near any valid offset)
|
|
428
|
+
*
|
|
429
|
+
* // Custom threshold
|
|
430
|
+
* inferLikelyOffsetMinutes(495, 30) // 480 (UTC+8 with 30min threshold)
|
|
431
|
+
* inferLikelyOffsetMinutes(495, 15) // undefined (beyond 15min threshold)
|
|
432
|
+
*
|
|
433
|
+
* // Adjust global default
|
|
434
|
+
* Settings.maxValidOffsetMinutes.value = 15 // Stricter matching
|
|
435
|
+
* inferLikelyOffsetMinutes(443) // undefined (beyond 15min threshold)
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
438
|
+
export declare function inferLikelyOffsetMinutes(deltaMinutes: number, maxValidOffsetMinutes?: number): Maybe<number>;
|
|
439
|
+
/**
|
|
440
|
+
* Infer timezone offset by comparing local time with GPS/UTC time.
|
|
441
|
+
*
|
|
442
|
+
* Calculates the timezone by finding the difference between:
|
|
443
|
+
* - A "captured at" timestamp (DateTimeOriginal, CreateDate, etc.) assumed to
|
|
444
|
+
* be in local time
|
|
445
|
+
* - A UTC timestamp (GPSDateTime, DateTimeUTC, or combined GPSDateStamp +
|
|
446
|
+
* GPSTimeStamp)
|
|
447
|
+
*
|
|
448
|
+
* Uses {@link inferLikelyOffsetMinutes} to handle minor clock drift and round
|
|
449
|
+
* to the nearest valid timezone offset.
|
|
450
|
+
*
|
|
451
|
+
* This is a fallback when explicit timezone tags are not available.
|
|
452
|
+
*
|
|
453
|
+
* @param t - Tags object with timestamp fields
|
|
454
|
+
* @returns TzSrc with inferred timezone and provenance, or undefined if
|
|
455
|
+
* inference is not possible
|
|
456
|
+
*
|
|
457
|
+
* @see {@link extractTzOffsetFromTags} to check explicit timezone tags first
|
|
458
|
+
*
|
|
459
|
+
* @example
|
|
460
|
+
* ```typescript
|
|
461
|
+
* // GPS-based inference
|
|
462
|
+
* const tags = {
|
|
463
|
+
* DateTimeOriginal: "2023:01:15 11:30:00", // Local time (PST)
|
|
464
|
+
* GPSDateTime: "2023:01:15 19:30:00" // UTC
|
|
465
|
+
* }
|
|
466
|
+
* extractTzOffsetFromUTCOffset(tags)
|
|
467
|
+
* // { zone: "UTC-8", tz: "UTC-8",
|
|
468
|
+
* // src: "offset between DateTimeOriginal and GPSDateTime" }
|
|
469
|
+
*
|
|
470
|
+
* // DateTimeUTC-based inference
|
|
471
|
+
* const tags2 = {
|
|
472
|
+
* CreateDate: "2023:07:20 14:15:30", // Local time (JST)
|
|
473
|
+
* DateTimeUTC: "2023:07:20 05:15:30" // UTC
|
|
474
|
+
* }
|
|
475
|
+
* extractTzOffsetFromUTCOffset(tags2)
|
|
476
|
+
* // { zone: "UTC+9", tz: "UTC+9",
|
|
477
|
+
* // src: "offset between CreateDate and DateTimeUTC" }
|
|
478
|
+
*
|
|
479
|
+
* // Handles clock drift
|
|
480
|
+
* const tags3 = {
|
|
481
|
+
* DateTimeOriginal: "2023:01:15 11:30:45", // Local with drift
|
|
482
|
+
* GPSDateTime: "2023:01:15 19:29:58" // UTC (old GPS fix)
|
|
483
|
+
* }
|
|
484
|
+
* extractTzOffsetFromUTCOffset(tags3)
|
|
485
|
+
* // Still infers UTC-8 despite ~1 minute drift
|
|
486
|
+
*
|
|
487
|
+
* // No UTC timestamp available
|
|
488
|
+
* const tags4 = {
|
|
489
|
+
* DateTimeOriginal: "2023:01:15 11:30:00"
|
|
490
|
+
* // No GPS or UTC timestamp
|
|
491
|
+
* }
|
|
492
|
+
* extractTzOffsetFromUTCOffset(tags4) // undefined
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
90
495
|
export declare function extractTzOffsetFromUTCOffset(t: Pick<Tags, (typeof CapturedAtTagNames)[number] | "GPSDateTime" | "DateTimeUTC" | "GPSDateStamp" | "GPSTimeStamp" | "SonyDateTime2">): Maybe<TzSrc>;
|
|
91
|
-
|
|
496
|
+
/**
|
|
497
|
+
* Check if two timezone values are equivalent at a specific point in time.
|
|
498
|
+
*
|
|
499
|
+
* Two zones are considered equivalent if they:
|
|
500
|
+
* - Are the same zone (via Luxon's Zone.equals()), OR
|
|
501
|
+
* - Have the same offset at the specified timestamp
|
|
502
|
+
*
|
|
503
|
+
* This is useful for:
|
|
504
|
+
* - De-duplicating timezone records
|
|
505
|
+
* - Comparing zones in different formats ("UTC+5" vs "UTC+05:00")
|
|
506
|
+
* - Matching IANA zones to their offset at a specific time
|
|
507
|
+
*
|
|
508
|
+
* For IANA zones with DST, you can specify a timestamp to evaluate equivalence
|
|
509
|
+
* at that moment. This is important when comparing historical records or future
|
|
510
|
+
* events where DST transitions matter.
|
|
511
|
+
*
|
|
512
|
+
* @param a - First timezone (Zone, string, or offset in minutes)
|
|
513
|
+
* @param b - Second timezone (Zone, string, or offset in minutes)
|
|
514
|
+
* @param at - Timestamp in milliseconds to evaluate zone offsets.
|
|
515
|
+
* Defaults to current time (Date.now()).
|
|
516
|
+
* @returns true if zones are equivalent at the specified time
|
|
517
|
+
*
|
|
518
|
+
* @example
|
|
519
|
+
* ```typescript
|
|
520
|
+
* // Same zone, different formats
|
|
521
|
+
* equivalentZones("UTC+5", "UTC+05:00") // true
|
|
522
|
+
* equivalentZones("UTC-8", -480) // true (480 minutes = 8 hours)
|
|
523
|
+
* equivalentZones("GMT", "UTC") // true
|
|
524
|
+
* equivalentZones("Z", 0) // true
|
|
525
|
+
*
|
|
526
|
+
* // IANA zones matched by current offset (default behavior)
|
|
527
|
+
* equivalentZones("America/New_York", "UTC-5") // true in winter (EST)
|
|
528
|
+
* equivalentZones("America/New_York", "UTC-4") // true in summer (EDT)
|
|
529
|
+
*
|
|
530
|
+
* // IANA zones at specific times
|
|
531
|
+
* const winter = new Date("2023-01-15").getTime()
|
|
532
|
+
* const summer = new Date("2023-07-15").getTime()
|
|
533
|
+
* equivalentZones("America/New_York", "UTC-5", winter) // true (EST)
|
|
534
|
+
* equivalentZones("America/New_York", "UTC-4", winter) // false (not EDT in winter)
|
|
535
|
+
* equivalentZones("America/New_York", "UTC-4", summer) // true (EDT)
|
|
536
|
+
* equivalentZones("America/New_York", "UTC-5", summer) // false (not EST in summer)
|
|
537
|
+
*
|
|
538
|
+
* // Compare two IANA zones at a specific time
|
|
539
|
+
* equivalentZones("America/New_York", "America/Toronto", winter) // true (both EST)
|
|
540
|
+
* equivalentZones("America/New_York", "America/Los_Angeles", winter) // false (EST vs PST)
|
|
541
|
+
*
|
|
542
|
+
* // Different zones
|
|
543
|
+
* equivalentZones("UTC+8", "UTC+9") // false
|
|
544
|
+
*
|
|
545
|
+
* // Invalid zones return false
|
|
546
|
+
* equivalentZones("invalid", "UTC") // false
|
|
547
|
+
* equivalentZones(null, "UTC") // false
|
|
548
|
+
* ```
|
|
549
|
+
*/
|
|
550
|
+
export declare function equivalentZones(a: Maybe<string | number | Zone>, b: Maybe<string | number | Zone>, at?: number): boolean;
|
|
92
551
|
export declare function getZoneName(args?: {
|
|
93
552
|
zone?: Zone;
|
|
94
553
|
zoneName?: Maybe<string>;
|