From 7abe36f82007dc0128bc758c3f5a5c401c0e6205 Mon Sep 17 00:00:00 2001 From: Carsten Otto Date: Sun, 15 Sep 2024 19:33:55 +0200 Subject: [PATCH] fix(server): prefer explicit timezone over GPS based guess exiftool-vendored sadly treats the offset +00:00 (used in several countries) as unknown and may return a timezone which is based on GPS coordinates. Because of this, when one manually fixes the timezone offset to +00:00, this fix is overwritten. With this fix, we explicitly prefer +00:00 (if set). --- server/src/services/metadata.service.spec.ts | 23 +++++++++++++++++++- server/src/services/metadata.service.ts | 10 ++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 19aaa2ea1a323..2d1397cebf5dc 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -1,4 +1,5 @@ import { BinaryField, ExifDateTime } from 'exiftool-vendored'; +import { DateTime } from 'luxon'; import { randomBytes } from 'node:crypto'; import { Stats } from 'node:fs'; import { constants } from 'node:fs/promises'; @@ -854,10 +855,30 @@ describe(MetadataService.name, () => { metadataMock.readTags.mockResolvedValue(tags); await sut.handleMetadataExtraction({ id: assetStub.image.id }); - expect(assetMock.getByIds).toHaveBeenCalledWith([assetStub.image.id]); expect(assetMock.upsertExif).toHaveBeenCalledWith( expect.objectContaining({ timeZone: 'UTC+0', + dateTimeOriginal: DateTime.fromISO('2024-09-01T00:00:00.000Z').toJSDate(), + }), + ); + }); + + it('should extract +00:00 timezone from raw value despite conflicting GPS location/timezone', async () => { + // exiftool-vendored returns a timezone derived from GPS coordinates even though "+00:00" might be set explicitly + // https://github.com/photostructure/exiftool-vendored.js/issues/203 + + const tags: ImmichTags = { + DateTimeOriginal: ExifDateTime.fromISO('2024-09-15T00:00:00.000+00:00'), + tz: 'America/Santiago', + }; + assetMock.getByIds.mockResolvedValue([assetStub.image]); + metadataMock.readTags.mockResolvedValue(tags); + + await sut.handleMetadataExtraction({ id: assetStub.image.id }); + expect(assetMock.upsertExif).toHaveBeenCalledWith( + expect.objectContaining({ + timeZone: 'UTC+0', + dateTimeOriginal: DateTime.fromISO('2024-09-15T00:00:00.000Z').toJSDate(), }), ); }); diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index eaa491c3ee7d8..22f4f9f35eb48 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -608,12 +608,10 @@ export class MetadataService { } // timezone - let timeZone = exifTags.tz ?? null; - if (timeZone == null && dateTime?.rawValue?.endsWith('+00:00')) { - // exiftool-vendored returns "no timezone" information even though "+00:00" might be set explicitly - // https://github.com/photostructure/exiftool-vendored.js/issues/203 - timeZone = 'UTC+0'; - } + // exiftool-vendored returns "no timezone" information or one derived from GPS coordinates even though "+00:00" + // might be set explicitly + // https://github.com/photostructure/exiftool-vendored.js/issues/203 + const timeZone = dateTime?.rawValue?.endsWith('+00:00') ? 'UTC+0' : (exifTags.tz ?? null); if (timeZone) { this.logger.debug(`Asset ${asset.id} timezone is ${timeZone} (via ${exifTags.tzSource})`);