exiftool-vendored 33.2.0 → 33.4.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.
@@ -25,10 +25,106 @@ 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;
29
- export declare function isZoneUnset(zone: Zone): 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
+ */
72
+ export declare function isZoneUnset(zone: unknown): 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;
31
- export declare function isZone(zone: unknown): 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
+ */
127
+ export declare function isZone(zone: unknown): zone is Zone<true>;
32
128
  /**
33
129
  * If `tzSource` matches this value, the tags are from a video, and we had to
34
130
  * resort to assuming time fields are in UTC.
@@ -36,16 +132,201 @@ export declare function isZone(zone: unknown): zone is Zone;
36
132
  */
37
133
  export declare const defaultVideosToUTC = "defaultVideosToUTC";
38
134
  /**
39
- * @param input must be either a number, which is the offset in minutes, or a
40
- * string in the format "UTC+H" or "UTC+HH:mm"
135
+ * Composable regex pattern for matching timezone offsets.
136
+ *
137
+ * Designed for embedding in larger patterns (no ^ or $ anchors).
138
+ * Matches UTC/GMT/Z or signed offsets like +08:00, -05:30.
139
+ *
140
+ * Named capture groups:
141
+ * - `tz_utc`: Matches "Z", "UTC", or "GMT"
142
+ * - `tz_sign`: The sign character (+, -, or Unicode minus −)
143
+ * - `tz_hours`: Hour offset (1-2 digits)
144
+ * - `tz_minutes`: Optional minute offset (2 digits)
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * // Concatenate with other patterns
149
+ * const dateTimeRE = new RegExp(
150
+ * `(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})${TimezoneOffsetRE.source}`
151
+ * )
152
+ *
153
+ * // Use standalone
154
+ * const match = TimezoneOffsetRE.exec("2023-01-15T10:30:00-08:00")
155
+ * if (match?.groups) {
156
+ * const { tz_sign, tz_hours, tz_minutes } = match.groups
157
+ * // tz_sign = "-", tz_hours = "08", tz_minutes = "00"
158
+ * }
159
+ * ```
160
+ */
161
+ export declare const TimezoneOffsetRE: RegExp;
162
+ /**
163
+ * Result of parsing a timezone offset regex match.
164
+ */
165
+ export interface TimezoneOffsetMatch {
166
+ /** Offset in minutes (e.g., 480 for UTC+8, -300 for UTC-5) */
167
+ offsetMinutes: number;
168
+ /** Whether this was a UTC/GMT/Z match */
169
+ isUtc: boolean;
170
+ }
171
+ /**
172
+ * Parse timezone offset from a regex match result.
173
+ *
174
+ * Use with {@link TimezoneOffsetRE} to extract offset minutes from a match.
175
+ *
176
+ * @param match - RegExp exec result from TimezoneOffsetRE
177
+ * @returns Parsed offset info, or undefined if match is invalid
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * const match = TimezoneOffsetRE.exec("2023-01-15T10:30:00-08:00")
182
+ * const result = parseTimezoneOffsetMatch(match)
183
+ * // { offsetMinutes: -480, isUtc: false }
184
+ * ```
185
+ */
186
+ export declare function parseTimezoneOffsetMatch(match: RegExpExecArray | null): Maybe<TimezoneOffsetMatch>;
187
+ /**
188
+ * Parse a timezone offset string to offset minutes.
189
+ *
190
+ * Accepts multiple formats:
191
+ * - ISO 8601: "+08:00", "-05:30", "Z"
192
+ * - Luxon format: "UTC+8", "GMT-5"
193
+ * - UTC variants: "UTC", "GMT", "Zulu"
194
+ *
195
+ * Supports seconds for archaic offsets like "-00:25:21" (Ireland 1880-1916).
196
+ *
197
+ * **Note:** Does NOT validate that the offset is a real-world timezone offset.
198
+ * Use {@link validTzOffsetMinutes} for validation.
199
+ *
200
+ * @param str - Timezone offset string
201
+ * @returns Offset in minutes, or undefined if invalid
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * parseTimezoneOffsetToMinutes("+08:00") // 480
206
+ * parseTimezoneOffsetToMinutes("UTC-5") // -300
207
+ * parseTimezoneOffsetToMinutes("Z") // 0
208
+ * parseTimezoneOffsetToMinutes("-00:25:21") // -25.35 (archaic Ireland)
209
+ * parseTimezoneOffsetToMinutes("invalid") // undefined
210
+ * ```
211
+ */
212
+ export declare function parseTimezoneOffsetToMinutes(str: string): Maybe<number>;
213
+ /**
214
+ * Normalize a timezone input to a valid Luxon Zone.
215
+ *
216
+ * Accepts multiple input formats and returns a validated Zone instance, or
217
+ * undefined if the input cannot be normalized to a valid timezone.
218
+ *
219
+ * Supported input formats:
220
+ * - **Numbers**: Timezone offset in minutes (e.g., 480 = UTC+8, -300 = UTC-5)
221
+ * - **Strings**: ISO offsets ("+08:00", "-05:00"), IANA zones
222
+ * ("America/Los_Angeles"), UTC variants ("UTC", "GMT", "Z", "Zulu")
223
+ * - **Zone instances**: Validated and returned if valid
224
+ *
225
+ * The function respects Settings:
226
+ * - {@link Settings.allowArchaicTimezoneOffsets} for pre-1982 offsets
227
+ * - {@link Settings.allowBakerIslandTime} for UTC-12:00
228
+ *
229
+ * @param input - Timezone in various formats
230
+ * @returns Valid Zone instance, or undefined if invalid
231
+ *
232
+ * @example
233
+ * ```typescript
234
+ * // Numbers (offset in minutes)
235
+ * normalizeZone(480)?.name // "UTC+8"
236
+ * normalizeZone(-300)?.name // "UTC-5"
237
+ * normalizeZone(0)?.name // "UTC"
238
+ *
239
+ * // ISO offset strings
240
+ * normalizeZone("+08:00")?.name // "UTC+8"
241
+ * normalizeZone("-05:30")?.name // "UTC-5:30"
242
+ * normalizeZone("UTC+7")?.name // "UTC+7"
243
+ *
244
+ * // IANA timezone names
245
+ * normalizeZone("America/Los_Angeles")?.name // "America/Los_Angeles"
246
+ * normalizeZone("Asia/Tokyo")?.name // "Asia/Tokyo"
247
+ *
248
+ * // UTC aliases
249
+ * normalizeZone("UTC")?.name // "UTC"
250
+ * normalizeZone("GMT")?.name // "UTC"
251
+ * normalizeZone("Z")?.name // "UTC"
252
+ * normalizeZone("Zulu")?.name // "UTC"
253
+ *
254
+ * // Invalid inputs return undefined
255
+ * normalizeZone("invalid") // undefined
256
+ * normalizeZone("+25:00") // undefined (beyond ±14 hours)
257
+ * normalizeZone(1200) // undefined (20 hours, beyond ±14 hours)
258
+ * normalizeZone(100) // undefined (not a valid timezone offset)
259
+ * normalizeZone(-1) // undefined (UnsetZone sentinel)
260
+ * normalizeZone(null) // undefined
261
+ * ```
41
262
  */
42
263
  export declare function normalizeZone(input: unknown): Maybe<Zone>;
43
264
  /**
44
- * @param ts must be provided if the zone is not a fixed offset
45
- * @return the zone offset (in "±HH:MM" format) for the given zone, or "" if
46
- * the zone is invalid
265
+ * Convert a timezone to its short offset format (e.g., "+08:00", "-05:00").
266
+ *
267
+ * Useful for displaying timezone offsets in a standardized format. For IANA
268
+ * zones with daylight saving time, provide a timestamp to get the correct
269
+ * offset for that moment.
270
+ *
271
+ * @param zone - Timezone as Zone, string, or offset in minutes
272
+ * @param ts - Optional timestamp (milliseconds) for IANA zone offset calculation.
273
+ * Defaults to current time if not provided.
274
+ * @returns Zone offset in "+HH:MM" format, or "" if zone is invalid
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * // Fixed offsets
279
+ * zoneToShortOffset("UTC+8") // "+08:00"
280
+ * zoneToShortOffset(480) // "+08:00"
281
+ * zoneToShortOffset("UTC-5:30") // "-05:30"
282
+ *
283
+ * // IANA zones (offset depends on DST)
284
+ * const winter = new Date("2023-01-15").getTime()
285
+ * const summer = new Date("2023-07-15").getTime()
286
+ * zoneToShortOffset("America/Los_Angeles", winter) // "-08:00" (PST)
287
+ * zoneToShortOffset("America/Los_Angeles", summer) // "-07:00" (PDT)
288
+ *
289
+ * // Invalid zones return empty string
290
+ * zoneToShortOffset("invalid") // ""
291
+ * zoneToShortOffset(null) // ""
292
+ * ```
47
293
  */
48
294
  export declare function zoneToShortOffset(zone: Maybe<string | number | Zone>, ts?: number): string;
295
+ /**
296
+ * Type guard to check if a numeric offset (in minutes) represents a valid timezone.
297
+ *
298
+ * Validates that the offset:
299
+ * - Is a number (not null/undefined)
300
+ * - Is not the UnsetZone sentinel value (-1)
301
+ * - Matches a real-world timezone offset (respects Settings for archaic offsets)
302
+ *
303
+ * Use this for exact validation without rounding. For error-tolerant rounding to
304
+ * the nearest valid offset, use {@link inferLikelyOffsetMinutes} instead.
305
+ *
306
+ * @param tzOffsetMinutes - Offset in minutes to validate (e.g., 480 for UTC+8)
307
+ * @returns true if the offset is exactly valid (type guard)
308
+ *
309
+ * @see {@link inferLikelyOffsetMinutes} for error-tolerant rounding
310
+ *
311
+ * @example
312
+ * ```typescript
313
+ * validTzOffsetMinutes(480) // true (UTC+8)
314
+ * validTzOffsetMinutes(-300) // true (UTC-5)
315
+ * validTzOffsetMinutes(330) // true (UTC+5:30, India)
316
+ * validTzOffsetMinutes(345) // true (UTC+5:45, Nepal)
317
+ *
318
+ * validTzOffsetMinutes(481) // false (not a valid timezone)
319
+ * validTzOffsetMinutes(-1) // false (UnsetZone sentinel)
320
+ * validTzOffsetMinutes(null) // false
321
+ *
322
+ * // Archaic offsets require Settings
323
+ * Settings.allowArchaicTimezoneOffsets.value = false
324
+ * validTzOffsetMinutes(-630) // false (Hawaii -10:30, archaic)
325
+ *
326
+ * Settings.allowArchaicTimezoneOffsets.value = true
327
+ * validTzOffsetMinutes(-630) // true (Hawaii -10:30, archaic)
328
+ * ```
329
+ */
49
330
  export declare function validTzOffsetMinutes(tzOffsetMinutes: Maybe<number>): tzOffsetMinutes is number;
50
331
  /**
51
332
  * Returns a "zone name" (used by `luxon`) that encodes the given offset.
@@ -71,24 +352,284 @@ export interface TzSrc {
71
352
  src: string;
72
353
  }
73
354
  /**
74
- * Parse a timezone offset and return the offset minutes
355
+ * Extract timezone information from various value types.
356
+ *
357
+ * Handles multiple input formats and performs intelligent parsing:
358
+ * - **Strings**: ISO offsets ("+08:00"), IANA zones, UTC variants, timestamps
359
+ * with embedded timezones ("2023:01:15 10:30:00-08:00")
360
+ * - **Numbers**: Hour offsets (e.g., -8 for UTC-8)
361
+ * - **Arrays**: Uses first non-null value
362
+ * - **ExifDateTime/ExifTime instances**: Extracts their zone property
363
+ *
364
+ * By default, strips timezone abbreviations (PST, PDT, etc.) as they are
365
+ * ambiguous. Returns provenance information indicating which parsing method
366
+ * succeeded.
367
+ *
368
+ * Supports Unicode minus signs (−, U+2212) and plus-minus signs (±, U+00B1)
369
+ * in addition to ASCII +/-.
370
+ *
371
+ * @param value - Value to extract timezone from
372
+ * @param opts.stripTZA - Whether to strip timezone abbreviations (default: true).
373
+ * TZAs like "PST" are ambiguous and usually stripped.
374
+ * @returns TzSrc with zone name and provenance, or undefined if no timezone found
375
+ *
376
+ * @example
377
+ * ```typescript
378
+ * // ISO offset strings
379
+ * extractZone("+08:00")
380
+ * // { zone: "UTC+8", tz: "UTC+8", src: "offsetMinutesToZoneName" }
381
+ *
382
+ * extractZone("UTC-5:30")
383
+ * // { zone: "UTC-5:30", tz: "UTC-5:30", src: "normalizeZone" }
384
+ *
385
+ * // IANA zone names
386
+ * extractZone("America/Los_Angeles")
387
+ * // { zone: "America/Los_Angeles", tz: "America/Los_Angeles", src: "normalizeZone" }
388
+ *
389
+ * // Timestamps with embedded timezones
390
+ * extractZone("2023:01:15 10:30:00-08:00")
391
+ * // { zone: "UTC-8", tz: "UTC-8", src: "offsetMinutesToZoneName",
392
+ * // leftovers: "2023:01:15 10:30:00" }
393
+ *
394
+ * // Unicode minus signs
395
+ * extractZone("−08:00") // Unicode minus (U+2212)
396
+ * // { zone: "UTC-8", tz: "UTC-8", src: "offsetMinutesToZoneName" }
397
+ *
398
+ * // Numeric hour offsets
399
+ * extractZone(-8)
400
+ * // { zone: "UTC-8", tz: "UTC-8", src: "hourOffset" }
75
401
  *
76
- * @param opts.stripTZA If false, do not strip off the timezone abbreviation
77
- * (TZA) from the value. Defaults to true.
402
+ * // Arrays (uses first non-null)
403
+ * extractZone([null, "+05:30", undefined])
404
+ * // { zone: "UTC+5:30", tz: "UTC+5:30", src: "offsetMinutesToZoneName" }
78
405
  *
79
- * @return undefined if the value cannot be parsed as a valid timezone offset
406
+ * // Strips timezone abbreviations by default
407
+ * extractZone("2023:01:15 10:30:00-08:00 PST")
408
+ * // { zone: "UTC-8", tz: "UTC-8", src: "offsetMinutesToZoneName",
409
+ * // leftovers: "2023:01:15 10:30:00" }
410
+ *
411
+ * // Invalid inputs return undefined
412
+ * extractZone("invalid") // undefined
413
+ * extractZone(null) // undefined
414
+ * ```
80
415
  */
81
416
  export declare function extractZone(value: unknown, opts?: {
82
417
  stripTZA?: boolean;
83
418
  }): Maybe<TzSrc>;
84
419
  export declare const TimezoneOffsetTagnames: readonly ["TimeZone", "OffsetTimeOriginal", "OffsetTimeDigitized", "TimeZoneOffset"];
85
420
  export declare function incrementZone(z: string | Zone | number, minutes: number): Maybe<Zone>;
421
+ /**
422
+ * Extract timezone offset from standard EXIF timezone tags.
423
+ *
424
+ * Checks timezone tags in priority order:
425
+ * 1. TimeZone
426
+ * 2. OffsetTimeOriginal (for DateTimeOriginal)
427
+ * 3. OffsetTimeDigitized (for CreateDate)
428
+ * 4. TimeZoneOffset
429
+ *
430
+ * Handles camera-specific quirks like Nikon's DaylightSavings tag, which
431
+ * requires adjusting the TimeZone offset forward by one hour during DST.
432
+ *
433
+ * @param t - EXIF tags object
434
+ * @param opts.adjustTimeZoneIfDaylightSavings - Optional function to adjust
435
+ * timezone for DST. Defaults to handling Nikon's DaylightSavings quirk.
436
+ * @returns TzSrc with zone and provenance, or undefined if no timezone found
437
+ *
438
+ * @see {@link TimezoneOffsetTagnames} for the list of tags checked
439
+ * @see https://github.com/photostructure/exiftool-vendored.js/issues/215
440
+ *
441
+ * @example
442
+ * ```typescript
443
+ * const tags = await exiftool.read("photo.jpg")
444
+ *
445
+ * const tzSrc = extractTzOffsetFromTags(tags)
446
+ * if (tzSrc) {
447
+ * console.log(`Timezone: ${tzSrc.zone}`)
448
+ * console.log(`Source: ${tzSrc.src}`) // e.g., "OffsetTimeOriginal"
449
+ * }
450
+ *
451
+ * // Nikon DST handling
452
+ * const nikonTags = {
453
+ * TimeZone: "-08:00",
454
+ * DaylightSavings: "Yes",
455
+ * Make: "NIKON CORPORATION"
456
+ * }
457
+ * extractTzOffsetFromTags(nikonTags)
458
+ * // { zone: "UTC-7", tz: "UTC-7",
459
+ * // src: "TimeZone (adjusted for DaylightSavings)" }
460
+ * ```
461
+ */
86
462
  export declare function extractTzOffsetFromTags(t: Tags, opts?: Pick<ExifToolOptions, "adjustTimeZoneIfDaylightSavings">): Maybe<TzSrc>;
87
463
  export declare function extractTzOffsetFromDatestamps(t: Tags, opts: Partial<Pick<ExifToolOptions, "inferTimezoneFromDatestamps" | "inferTimezoneFromDatestampTags">>): Maybe<TzSrc>;
88
464
  export declare function extractTzOffsetFromTimeStamp(t: Tags, opts: Partial<Pick<ExifToolOptions, "inferTimezoneFromTimeStamp" | "inferTimezoneFromDatestampTags">>): Maybe<TzSrc>;
89
- export declare function inferLikelyOffsetMinutes(deltaMinutes: number): Maybe<number>;
465
+ /**
466
+ * Round an arbitrary offset to the nearest valid timezone offset.
467
+ *
468
+ * This is error-tolerant timezone inference, useful for:
469
+ * - GPS-based timezone calculation (where GPS time drift may cause errors)
470
+ * - Handling clock drift in timestamp comparisons
471
+ * - Fuzzy timezone matching
472
+ *
473
+ * By default, uses {@link Settings.maxValidOffsetMinutes} (30 minutes) as the
474
+ * maximum distance from a valid timezone. This threshold handles GPS acquisition
475
+ * lag and clock drift while preventing false matches.
476
+ *
477
+ * Respects Settings for archaic offsets, Baker Island time, and max offset tolerance.
478
+ *
479
+ * @param deltaMinutes - Offset in minutes to round (can be fractional)
480
+ * @param maxValidOffsetMinutes - Maximum distance (in minutes) from a valid
481
+ * timezone to accept. Defaults to {@link Settings.maxValidOffsetMinutes}.
482
+ * @returns Nearest valid offset in minutes, or undefined if too far from any
483
+ * valid timezone
484
+ *
485
+ * @see {@link validTzOffsetMinutes} for exact validation without rounding
486
+ * @see {@link Settings.maxValidOffsetMinutes} to configure the default threshold
487
+ *
488
+ * @example
489
+ * ```typescript
490
+ * // Exact matches
491
+ * inferLikelyOffsetMinutes(480) // 480 (UTC+8, exact)
492
+ * inferLikelyOffsetMinutes(-300) // -300 (UTC-5, exact)
493
+ *
494
+ * // Rounding within default threshold (30 minutes)
495
+ * inferLikelyOffsetMinutes(485) // 480 (UTC+8, rounded from 485)
496
+ * inferLikelyOffsetMinutes(-295) // -300 (UTC-5, rounded from -295)
497
+ * inferLikelyOffsetMinutes(330.5) // 330 (UTC+5:30, rounded)
498
+ *
499
+ * // GPS-based inference with clock drift (within 30 min default)
500
+ * const gpsTime = "2023:01:15 19:30:45" // UTC
501
+ * const localTime = "2023:01:15 11:32:12" // Local with 1.5min drift
502
+ * const deltaMinutes = 480 + 1.5 // ~481.5 minutes
503
+ * inferLikelyOffsetMinutes(deltaMinutes) // 480 (UTC+8)
504
+ *
505
+ * // GPS lag up to 23 minutes still works (within 30 min threshold)
506
+ * inferLikelyOffsetMinutes(443) // 420 (UTC-7, ~23 min from actual)
507
+ *
508
+ * // Beyond threshold returns undefined
509
+ * inferLikelyOffsetMinutes(100) // undefined (not near any valid offset)
510
+ *
511
+ * // Custom threshold
512
+ * inferLikelyOffsetMinutes(495, 30) // 480 (UTC+8 with 30min threshold)
513
+ * inferLikelyOffsetMinutes(495, 15) // undefined (beyond 15min threshold)
514
+ *
515
+ * // Adjust global default
516
+ * Settings.maxValidOffsetMinutes.value = 15 // Stricter matching
517
+ * inferLikelyOffsetMinutes(443) // undefined (beyond 15min threshold)
518
+ * ```
519
+ */
520
+ export declare function inferLikelyOffsetMinutes(deltaMinutes: Maybe<number>, maxValidOffsetMinutes?: number): Maybe<number>;
521
+ /**
522
+ * Infer timezone offset by comparing local time with GPS/UTC time.
523
+ *
524
+ * Calculates the timezone by finding the difference between:
525
+ * - A "captured at" timestamp (DateTimeOriginal, CreateDate, etc.) assumed to
526
+ * be in local time
527
+ * - A UTC timestamp (GPSDateTime, DateTimeUTC, or combined GPSDateStamp +
528
+ * GPSTimeStamp)
529
+ *
530
+ * Uses {@link inferLikelyOffsetMinutes} to handle minor clock drift and round
531
+ * to the nearest valid timezone offset.
532
+ *
533
+ * This is a fallback when explicit timezone tags are not available.
534
+ *
535
+ * @param t - Tags object with timestamp fields
536
+ * @returns TzSrc with inferred timezone and provenance, or undefined if
537
+ * inference is not possible
538
+ *
539
+ * @see {@link extractTzOffsetFromTags} to check explicit timezone tags first
540
+ *
541
+ * @example
542
+ * ```typescript
543
+ * // GPS-based inference
544
+ * const tags = {
545
+ * DateTimeOriginal: "2023:01:15 11:30:00", // Local time (PST)
546
+ * GPSDateTime: "2023:01:15 19:30:00" // UTC
547
+ * }
548
+ * extractTzOffsetFromUTCOffset(tags)
549
+ * // { zone: "UTC-8", tz: "UTC-8",
550
+ * // src: "offset between DateTimeOriginal and GPSDateTime" }
551
+ *
552
+ * // DateTimeUTC-based inference
553
+ * const tags2 = {
554
+ * CreateDate: "2023:07:20 14:15:30", // Local time (JST)
555
+ * DateTimeUTC: "2023:07:20 05:15:30" // UTC
556
+ * }
557
+ * extractTzOffsetFromUTCOffset(tags2)
558
+ * // { zone: "UTC+9", tz: "UTC+9",
559
+ * // src: "offset between CreateDate and DateTimeUTC" }
560
+ *
561
+ * // Handles clock drift
562
+ * const tags3 = {
563
+ * DateTimeOriginal: "2023:01:15 11:30:45", // Local with drift
564
+ * GPSDateTime: "2023:01:15 19:29:58" // UTC (old GPS fix)
565
+ * }
566
+ * extractTzOffsetFromUTCOffset(tags3)
567
+ * // Still infers UTC-8 despite ~1 minute drift
568
+ *
569
+ * // No UTC timestamp available
570
+ * const tags4 = {
571
+ * DateTimeOriginal: "2023:01:15 11:30:00"
572
+ * // No GPS or UTC timestamp
573
+ * }
574
+ * extractTzOffsetFromUTCOffset(tags4) // undefined
575
+ * ```
576
+ */
90
577
  export declare function extractTzOffsetFromUTCOffset(t: Pick<Tags, (typeof CapturedAtTagNames)[number] | "GPSDateTime" | "DateTimeUTC" | "GPSDateStamp" | "GPSTimeStamp" | "SonyDateTime2">): Maybe<TzSrc>;
91
- export declare function equivalentZones(a: Maybe<string | number | Zone>, b: Maybe<string | number | Zone>): boolean;
578
+ /**
579
+ * Check if two timezone values are equivalent at a specific point in time.
580
+ *
581
+ * Two zones are considered equivalent if they:
582
+ * - Are the same zone (via Luxon's Zone.equals()), OR
583
+ * - Have the same offset at the specified timestamp
584
+ *
585
+ * This is useful for:
586
+ * - De-duplicating timezone records
587
+ * - Comparing zones in different formats ("UTC+5" vs "UTC+05:00")
588
+ * - Matching IANA zones to their offset at a specific time
589
+ *
590
+ * For IANA zones with DST, you can specify a timestamp to evaluate equivalence
591
+ * at that moment. This is important when comparing historical records or future
592
+ * events where DST transitions matter.
593
+ *
594
+ * @param a - First timezone (Zone, string, or offset in minutes)
595
+ * @param b - Second timezone (Zone, string, or offset in minutes)
596
+ * @param at - Timestamp in milliseconds to evaluate zone offsets.
597
+ * Defaults to current time (Date.now()).
598
+ * @returns true if zones are equivalent at the specified time
599
+ *
600
+ * @example
601
+ * ```typescript
602
+ * // Same zone, different formats
603
+ * equivalentZones("UTC+5", "UTC+05:00") // true
604
+ * equivalentZones("UTC-8", -480) // true (480 minutes = 8 hours)
605
+ * equivalentZones("GMT", "UTC") // true
606
+ * equivalentZones("Z", 0) // true
607
+ *
608
+ * // IANA zones matched by current offset (default behavior)
609
+ * equivalentZones("America/New_York", "UTC-5") // true in winter (EST)
610
+ * equivalentZones("America/New_York", "UTC-4") // true in summer (EDT)
611
+ *
612
+ * // IANA zones at specific times
613
+ * const winter = new Date("2023-01-15").getTime()
614
+ * const summer = new Date("2023-07-15").getTime()
615
+ * equivalentZones("America/New_York", "UTC-5", winter) // true (EST)
616
+ * equivalentZones("America/New_York", "UTC-4", winter) // false (not EDT in winter)
617
+ * equivalentZones("America/New_York", "UTC-4", summer) // true (EDT)
618
+ * equivalentZones("America/New_York", "UTC-5", summer) // false (not EST in summer)
619
+ *
620
+ * // Compare two IANA zones at a specific time
621
+ * equivalentZones("America/New_York", "America/Toronto", winter) // true (both EST)
622
+ * equivalentZones("America/New_York", "America/Los_Angeles", winter) // false (EST vs PST)
623
+ *
624
+ * // Different zones
625
+ * equivalentZones("UTC+8", "UTC+9") // false
626
+ *
627
+ * // Invalid zones return false
628
+ * equivalentZones("invalid", "UTC") // false
629
+ * equivalentZones(null, "UTC") // false
630
+ * ```
631
+ */
632
+ export declare function equivalentZones(a: Maybe<string | number | Zone>, b: Maybe<string | number | Zone>, at?: number): boolean;
92
633
  export declare function getZoneName(args?: {
93
634
  zone?: Zone;
94
635
  zoneName?: Maybe<string>;