From 6aba3a2dc1ce733f62790217454e953533f71f0c Mon Sep 17 00:00:00 2001 From: compscitwilight Date: Sat, 18 Jul 2026 09:22:25 -0500 Subject: [PATCH 1/5] fix(web): preserve historical timezone offsets when editing dates --- web/src/lib/modals/timezone-utils.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/web/src/lib/modals/timezone-utils.ts b/web/src/lib/modals/timezone-utils.ts index 157c736dfb..929db9f22f 100644 --- a/web/src/lib/modals/timezone-utils.ts +++ b/web/src/lib/modals/timezone-utils.ts @@ -136,16 +136,8 @@ export function getPreferredTimeZone( } export function toDatetime(selectedDate: string, selectedZone: ZoneOption) { - const dtComponents = DateTime.fromISO(selectedDate, { zone: 'utc' }); - - // Determine the modern, DST-aware offset for the selected IANA zone - const { offsetMinutes } = getModernOffsetForZoneAndDate(selectedZone.value, selectedDate); - - // Construct the final ISO string with a fixed-offset zone. - const fixedOffsetZone = `UTC${offsetMinutes >= 0 ? '+' : ''}${Duration.fromObject({ minutes: offsetMinutes }).toFormat('hh:mm')}`; - // Create a DateTime object in this fixed-offset zone, preserving the local time. - return DateTime.fromObject(dtComponents.toObject(), { zone: fixedOffsetZone }); + return DateTime.fromISO(selectedDate, { zone: selectedZone.value }); } export function toIsoDate(selectedDate: string, selectedZone: ZoneOption) { From b270d269022855f989fa2a02dc880a1c40b1c93a Mon Sep 17 00:00:00 2001 From: compscitwilight Date: Sat, 18 Jul 2026 09:23:37 -0500 Subject: [PATCH 2/5] fix(web): use modernOffset when rendering timestamps --- .../lib/components/asset-viewer/DetailPanelDate.svelte | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/web/src/lib/components/asset-viewer/DetailPanelDate.svelte b/web/src/lib/components/asset-viewer/DetailPanelDate.svelte index 65578735ef..278553388b 100644 --- a/web/src/lib/components/asset-viewer/DetailPanelDate.svelte +++ b/web/src/lib/components/asset-viewer/DetailPanelDate.svelte @@ -3,6 +3,7 @@ import AssetChangeDateModal from '$lib/modals/AssetChangeDateModal.svelte'; import { locale } from '$lib/stores/preferences.store'; import { fromISODateTime, fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util'; + import { getModernOffsetForZoneAndDate } from '$lib/modals/timezone-utils'; import { type AssetResponseDto } from '@immich/sdk'; import { Icon, modalManager } from '@immich/ui'; import { mdiCalendar, mdiPencil } from '@mdi/js'; @@ -15,6 +16,11 @@ const { asset }: Props = $props(); const timeZone = $derived(asset.exifInfo?.timeZone ?? undefined); + const modernOffset = $derived( + timeZone && asset.exifInfo?.dateTimeOriginal + ? getModernOffsetForZoneAndDate(timeZone, asset.exifInfo.dateTimeOriginal).offsetFormat + : undefined, + ); const dateTime = $derived( timeZone && asset.exifInfo?.dateTimeOriginal ? fromISODateTime(asset.exifInfo.dateTimeOriginal, timeZone) @@ -59,11 +65,13 @@ hour: 'numeric', minute: '2-digit', second: '2-digit', - timeZoneName: timeZone ? 'longOffset' : undefined, }, { locale: $locale }, )}

+ {#if modernOffset} + GMT{modernOffset} + {/if} From 96d552004bdfc98422531eac6a7dfc55af8e0843 Mon Sep 17 00:00:00 2001 From: compscitwilight Date: Sat, 18 Jul 2026 10:12:17 -0500 Subject: [PATCH 3/5] fix(web): fix state UI desync when updating asset dates --- .../components/asset-viewer/DetailPanelDate.svelte | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/web/src/lib/components/asset-viewer/DetailPanelDate.svelte b/web/src/lib/components/asset-viewer/DetailPanelDate.svelte index 278553388b..538b0401a4 100644 --- a/web/src/lib/components/asset-viewer/DetailPanelDate.svelte +++ b/web/src/lib/components/asset-viewer/DetailPanelDate.svelte @@ -8,12 +8,23 @@ import { Icon, modalManager } from '@immich/ui'; import { mdiCalendar, mdiPencil } from '@mdi/js'; import { t } from 'svelte-i18n'; + import { onMount } from 'svelte'; + import { websocketEvents } from '$lib/stores/websocket'; type Props = { asset: AssetResponseDto; }; - const { asset }: Props = $props(); + onMount(() => { + return websocketEvents.on('on_asset_update', (assetUpdate) => { + if (assetUpdate.id === asset.id) { + asset = assetUpdate; + } + }); + }); + + let { asset: initialState }: Props = $props(); + let asset: AssetResponseDto = $state(initialState); const timeZone = $derived(asset.exifInfo?.timeZone ?? undefined); const modernOffset = $derived( From c2bd4093c4a6db60c55c60dbb51739b872d01a94 Mon Sep 17 00:00:00 2001 From: compscitwilight Date: Sat, 18 Jul 2026 10:21:26 -0500 Subject: [PATCH 4/5] refactor: rename initialState -> initialAsset for clarity --- web/src/lib/components/asset-viewer/DetailPanelDate.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/lib/components/asset-viewer/DetailPanelDate.svelte b/web/src/lib/components/asset-viewer/DetailPanelDate.svelte index 538b0401a4..381fd93a56 100644 --- a/web/src/lib/components/asset-viewer/DetailPanelDate.svelte +++ b/web/src/lib/components/asset-viewer/DetailPanelDate.svelte @@ -23,8 +23,8 @@ }); }); - let { asset: initialState }: Props = $props(); - let asset: AssetResponseDto = $state(initialState); + let { asset: initialAsset }: Props = $props(); + let asset: AssetResponseDto = $state(initialAsset); const timeZone = $derived(asset.exifInfo?.timeZone ?? undefined); const modernOffset = $derived( From 944289cccf3e4d450884fd7c12abefecd3b666f9 Mon Sep 17 00:00:00 2001 From: compscitwilight Date: Mon, 20 Jul 2026 07:18:47 -0500 Subject: [PATCH 5/5] refactor: remove unused Duration import and obsolete comment --- web/src/lib/modals/timezone-utils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/src/lib/modals/timezone-utils.ts b/web/src/lib/modals/timezone-utils.ts index 929db9f22f..0a77d0d9c0 100644 --- a/web/src/lib/modals/timezone-utils.ts +++ b/web/src/lib/modals/timezone-utils.ts @@ -1,4 +1,4 @@ -import { DateTime, Duration } from 'luxon'; +import { DateTime } from 'luxon'; export type ZoneOption = { /** @@ -136,7 +136,6 @@ export function getPreferredTimeZone( } export function toDatetime(selectedDate: string, selectedZone: ZoneOption) { - // Create a DateTime object in this fixed-offset zone, preserving the local time. return DateTime.fromISO(selectedDate, { zone: selectedZone.value }); }