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 });
}