From 0b02fda4e04b44f95fa886425fee623e0982a953 Mon Sep 17 00:00:00 2001
From: Jason Rasmussen <jason@rasm.me>
Date: Wed, 18 Sep 2024 17:07:50 -0400
Subject: [PATCH] fix(web): only show the timezone when it is known (#12779)

---
 .../asset-viewer/detail-panel.svelte          | 47 +++++++++----------
 web/src/lib/utils/timeline-util.ts            |  3 ++
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte
index 5dc4fc0812..ad80d64bf2 100644
--- a/web/src/lib/components/asset-viewer/detail-panel.svelte
+++ b/web/src/lib/components/asset-viewer/detail-panel.svelte
@@ -1,5 +1,9 @@
 <script lang="ts">
+  import { goto } from '$app/navigation';
+  import DetailPanelDescription from '$lib/components/asset-viewer/detail-panel-description.svelte';
   import DetailPanelLocation from '$lib/components/asset-viewer/detail-panel-location.svelte';
+  import DetailPanelRating from '$lib/components/asset-viewer/detail-panel-star-rating.svelte';
+  import DetailPanelTags from '$lib/components/asset-viewer/detail-panel-tags.svelte';
   import Icon from '$lib/components/elements/icon.svelte';
   import ChangeDate from '$lib/components/shared-components/change-date.svelte';
   import { AppRoute, QueryParameter, timeToLoadTheMap } from '$lib/constants';
@@ -9,6 +13,9 @@
   import { preferences, user } from '$lib/stores/user.store';
   import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError, isSharedLink } from '$lib/utils';
   import { delay, isFlipped } from '$lib/utils/asset-utils';
+  import { getByteUnitString } from '$lib/utils/byte-units';
+  import { handleError } from '$lib/utils/handle-error';
+  import { fromDateTimeOriginal, fromLocalDateTime } from '$lib/utils/timeline-util';
   import {
     AssetMediaSize,
     getAssetInfo,
@@ -18,6 +25,7 @@
     type ExifResponseDto,
   } from '@immich/sdk';
   import {
+    mdiAccountOff,
     mdiCalendar,
     mdiCameraIris,
     mdiClose,
@@ -26,24 +34,17 @@
     mdiImageOutline,
     mdiInformationOutline,
     mdiPencil,
-    mdiAccountOff,
   } from '@mdi/js';
   import { DateTime } from 'luxon';
   import { createEventDispatcher } from 'svelte';
+  import { t } from 'svelte-i18n';
   import { slide } from 'svelte/transition';
-  import { getByteUnitString } from '$lib/utils/byte-units';
-  import { handleError } from '$lib/utils/handle-error';
   import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
   import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
   import PersonSidePanel from '../faces-page/person-side-panel.svelte';
-  import UserAvatar from '../shared-components/user-avatar.svelte';
   import LoadingSpinner from '../shared-components/loading-spinner.svelte';
+  import UserAvatar from '../shared-components/user-avatar.svelte';
   import AlbumListItemDetails from './album-list-item-details.svelte';
-  import DetailPanelDescription from '$lib/components/asset-viewer/detail-panel-description.svelte';
-  import DetailPanelRating from '$lib/components/asset-viewer/detail-panel-star-rating.svelte';
-  import { t } from 'svelte-i18n';
-  import { goto } from '$app/navigation';
-  import DetailPanelTags from '$lib/components/asset-viewer/detail-panel-tags.svelte';
 
   export let asset: AssetResponseDto;
   export let albums: AlbumResponseDto[] = [];
@@ -99,6 +100,12 @@
 
   $: unassignedFaces = asset.unassignedFaces || [];
 
+  $: timeZone = asset.exifInfo?.timeZone;
+  $: dateTime =
+    timeZone && asset.exifInfo?.dateTimeOriginal
+      ? fromDateTimeOriginal(asset.exifInfo.dateTimeOriginal, timeZone)
+      : fromLocalDateTime(asset.localDateTime);
+
   const dispatch = createEventDispatcher<{
     close: void;
   }>();
@@ -261,10 +268,7 @@
       <p class="text-sm">{$t('no_exif_info_available').toUpperCase()}</p>
     {/if}
 
-    {#if asset.exifInfo?.dateTimeOriginal}
-      {@const assetDateTimeOriginal = DateTime.fromISO(asset.exifInfo.dateTimeOriginal, {
-        zone: asset.exifInfo.timeZone ?? undefined,
-      })}
+    {#if dateTime}
       <button
         type="button"
         class="flex w-full text-left justify-between place-items-start gap-4 py-4"
@@ -280,7 +284,7 @@
 
           <div>
             <p>
-              {assetDateTimeOriginal.toLocaleString(
+              {dateTime.toLocaleString(
                 {
                   month: 'short',
                   day: 'numeric',
@@ -291,12 +295,12 @@
             </p>
             <div class="flex gap-2 text-sm">
               <p>
-                {assetDateTimeOriginal.toLocaleString(
+                {dateTime.toLocaleString(
                   {
                     weekday: 'short',
                     hour: 'numeric',
                     minute: '2-digit',
-                    timeZoneName: 'longOffset',
+                    timeZoneName: timeZone ? 'longOffset' : undefined,
                   },
                   { locale: $locale },
                 )}
@@ -325,16 +329,9 @@
     {/if}
 
     {#if isShowChangeDate}
-      {@const assetDateTimeOriginal = asset.exifInfo?.dateTimeOriginal
-        ? DateTime.fromISO(asset.exifInfo.dateTimeOriginal, {
-            zone: asset.exifInfo.timeZone ?? undefined,
-            locale: $locale,
-          })
-        : DateTime.now()}
-      {@const assetTimeZoneOriginal = asset.exifInfo?.timeZone ?? ''}
       <ChangeDate
-        initialDate={assetDateTimeOriginal}
-        initialTimeZone={assetTimeZoneOriginal}
+        initialDate={dateTime}
+        initialTimeZone={timeZone ?? ''}
         on:confirm={({ detail: date }) => handleConfirmChangeDate(date)}
         on:cancel={() => (isShowChangeDate = false)}
       />
diff --git a/web/src/lib/utils/timeline-util.ts b/web/src/lib/utils/timeline-util.ts
index 541ebea7f5..7e65dcdb99 100644
--- a/web/src/lib/utils/timeline-util.ts
+++ b/web/src/lib/utils/timeline-util.ts
@@ -36,6 +36,9 @@ export type ScrollTargetListener = ({
 export const fromLocalDateTime = (localDateTime: string) =>
   DateTime.fromISO(localDateTime, { zone: 'UTC', locale: get(locale) });
 
+export const fromDateTimeOriginal = (dateTimeOriginal: string, timeZone: string) =>
+  DateTime.fromISO(dateTimeOriginal, { zone: timeZone });
+
 export const groupDateFormat: Intl.DateTimeFormatOptions = {
   weekday: 'short',
   month: 'short',