[BUG] Invalid timezone in DSLR photos uploaded via CLI #749

Closed
opened 2026-02-04 22:17:31 +03:00 by OVERLORD · 6 comments
Owner

Originally created by @etnoy on GitHub (Mar 15, 2023).

The bug

Just spent a few hours digging into why my DSLR photos were one hour off in time compared to my mobile photos.

Interestingly enough, this issue has plagued me over at google photos, too.

In the end, it was due to the microservice container having its timezone set to UTC.

But wait, we are using exif-vendored so this shouldn't be an issue? Alex himself says so here: https://github.com/immich-app/immich/issues/1519#issuecomment-1414953736

But alas, timezones still matter, because exif-vendored has to rely on local timezone information in some special cases, just like the DSLR photos.

The issue was finally solved for me by setting a timezone environment variable in the microservices container corresponding to my local timezone.

The obvious problem is that setting timezones in docker containers is messy, so it might not be an ideal solution.

Another solution is to move more of the timezone logic to somewhere that has native timezone information, like the CLI. Right now, the CLI uses a different exif library but I published a PR that makes the logic more consistent with immich itself: too: https://github.com/immich-app/CLI/pull/69

Note that the computed timezone data from the CLI is not even considered in the current logic in most cases.

The OS that Immich Server is running on

Ubuntu 22.04 via docker

Version of Immich Server

1.50.1

Version of Immich Mobile App

1.50.0 73

Platform with the issue

  • Server
  • Web
  • Mobile

Your docker-compose.yml content

version: "3.8"

services:
  immich-server:
    container_name: immich_server
    image: altran1502/immich-server:release
    entrypoint: [ "/bin/sh", "./start-server.sh" ]
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
    env_file:
      - .env
    environment:
      - NODE_ENV=production
      - TZ=Europe/Stockholm
    depends_on:
      - redis
      - database
    restart: always

  immich-microservices:
    container_name: immich_microservices
    image: altran1502/immich-server:release
    entrypoint: [ "/bin/sh", "./start-microservices.sh" ]
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
    env_file:
      - .env
    environment:
      - NODE_ENV=production
      - TZ=Europe/Stockholm
    depends_on:
      - redis
      - database
    restart: always

  immich-machine-learning:
    container_name: immich_machine_learning
    image: altran1502/immich-machine-learning:release
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
      - model-cache:/cache
    env_file:
      - .env
    environment:
      - NODE_ENV=production
      - TZ=Europe/Stockholm
    restart: always

  immich-web:
    container_name: immich_web
    image: altran1502/immich-web:release
    entrypoint: [ "/bin/sh", "./entrypoint.sh" ]
    env_file:
      - .env
    restart: always

  redis:
    container_name: immich_redis
    image: redis:6.2
    restart: always

  database:
    container_name: immich_postgres
    image: postgres:14
    env_file:
      - .env
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
      PG_DATA: /var/lib/postgresql/data
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: always

  immich-proxy:
    container_name: immich_proxy
    image: altran1502/immich-proxy:release
    environment:
      # Make sure these values get passed through from the env file
      - IMMICH_SERVER_URL
      - IMMICH_WEB_URL
    ports:
      - 2283:8080
    logging:
      driver: none
    depends_on:
      - immich-server
    restart: always

volumes:
  pgdata:
  model-cache:

Your .env content

###################################################################################
# Database
###################################################################################

DB_HOSTNAME=immich_postgres
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_DATABASE_NAME=immich

# Optional Database settings:
# DB_PORT=5432

###################################################################################
# Redis
###################################################################################

REDIS_HOSTNAME=immich_redis

# Optional Redis settings:
# REDIS_PORT=6379
# REDIS_DBINDEX=0
# REDIS_PASSWORD=
# REDIS_SOCKET=

###################################################################################
# Upload File Config
###################################################################################

UPLOAD_LOCATION=/mnt/immich

###################################################################################
# Log message level - [simple|verbose]
###################################################################################

LOG_LEVEL=simple

###################################################################################
# Reverse Geocoding
####################################################################################

DISABLE_REVERSE_GEOCODING=false

# Reverse geocoding is done locally which has a small impact on memory usage
# This memory usage can be altered by changing the REVERSE_GEOCODING_PRECISION variable
# This ranges from 0-3 with 3 being the most precise
# 3 - Cities > 500 population: ~200MB RAM
# 2 - Cities > 1000 population: ~150MB RAM
# 1 - Cities > 5000 population: ~80MB RAM
# 0 - Cities > 15000 population: ~40MB RAM

REVERSE_GEOCODING_PRECISION=3

####################################################################################
# WEB - Optional
####################################################################################

# Custom message on the login page, should be written in HTML form.
# For example PUBLIC_LOGIN_PAGE_MESSAGE="This is a demo instance of Immich.<br><br>Email: <i>demo@demo.de</i><br>Password: <i>demo</i>"

PUBLIC_LOGIN_PAGE_MESSAGE="Photos"

Reproduction steps

1. Take a JPG from a Nikon camera processed with Lightroom
2. Upload said JPG with immich-cli
3. Take a JPG from a mobile phone and upload it with either CLI or mobile app
4. Compare the dates reported by immich
...

Additional information

No response

Originally created by @etnoy on GitHub (Mar 15, 2023). ### The bug Just spent a few hours digging into why my DSLR photos were one hour off in time compared to my mobile photos. Interestingly enough, this issue has plagued me over at google photos, too. In the end, it was due to the microservice container having its timezone set to UTC. But wait, we are using exif-vendored so this shouldn't be an issue? Alex himself says so here: https://github.com/immich-app/immich/issues/1519#issuecomment-1414953736 But alas, timezones still matter, because exif-vendored has to rely on local timezone information in some special cases, just like the DSLR photos. The issue was finally solved for me by setting a timezone environment variable in the microservices container corresponding to my local timezone. The obvious problem is that setting timezones in docker containers is messy, so it might not be an ideal solution. Another solution is to move more of the timezone logic to somewhere that has native timezone information, like the CLI. Right now, the CLI uses a different exif library but I published a PR that makes the logic more consistent with immich itself: too: https://github.com/immich-app/CLI/pull/69 Note that the computed timezone data from the CLI is not even considered in [the current logic](https://github.com/immich-app/immich/blob/2ca560ebf80aadf1f7904bd88d7447d9a2a59ed3/server/apps/microservices/src/processors/metadata-extraction.processor.ts#L170) in most cases. ### The OS that Immich Server is running on Ubuntu 22.04 via docker ### Version of Immich Server 1.50.1 ### Version of Immich Mobile App 1.50.0 73 ### Platform with the issue - [X] Server - [ ] Web - [ ] Mobile ### Your docker-compose.yml content ```YAML version: "3.8" services: immich-server: container_name: immich_server image: altran1502/immich-server:release entrypoint: [ "/bin/sh", "./start-server.sh" ] volumes: - ${UPLOAD_LOCATION}:/usr/src/app/upload env_file: - .env environment: - NODE_ENV=production - TZ=Europe/Stockholm depends_on: - redis - database restart: always immich-microservices: container_name: immich_microservices image: altran1502/immich-server:release entrypoint: [ "/bin/sh", "./start-microservices.sh" ] volumes: - ${UPLOAD_LOCATION}:/usr/src/app/upload env_file: - .env environment: - NODE_ENV=production - TZ=Europe/Stockholm depends_on: - redis - database restart: always immich-machine-learning: container_name: immich_machine_learning image: altran1502/immich-machine-learning:release volumes: - ${UPLOAD_LOCATION}:/usr/src/app/upload - model-cache:/cache env_file: - .env environment: - NODE_ENV=production - TZ=Europe/Stockholm restart: always immich-web: container_name: immich_web image: altran1502/immich-web:release entrypoint: [ "/bin/sh", "./entrypoint.sh" ] env_file: - .env restart: always redis: container_name: immich_redis image: redis:6.2 restart: always database: container_name: immich_postgres image: postgres:14 env_file: - .env environment: POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_USER: ${DB_USERNAME} POSTGRES_DB: ${DB_DATABASE_NAME} PG_DATA: /var/lib/postgresql/data volumes: - pgdata:/var/lib/postgresql/data restart: always immich-proxy: container_name: immich_proxy image: altran1502/immich-proxy:release environment: # Make sure these values get passed through from the env file - IMMICH_SERVER_URL - IMMICH_WEB_URL ports: - 2283:8080 logging: driver: none depends_on: - immich-server restart: always volumes: pgdata: model-cache: ``` ### Your .env content ```Shell ################################################################################### # Database ################################################################################### DB_HOSTNAME=immich_postgres DB_USERNAME=postgres DB_PASSWORD=postgres DB_DATABASE_NAME=immich # Optional Database settings: # DB_PORT=5432 ################################################################################### # Redis ################################################################################### REDIS_HOSTNAME=immich_redis # Optional Redis settings: # REDIS_PORT=6379 # REDIS_DBINDEX=0 # REDIS_PASSWORD= # REDIS_SOCKET= ################################################################################### # Upload File Config ################################################################################### UPLOAD_LOCATION=/mnt/immich ################################################################################### # Log message level - [simple|verbose] ################################################################################### LOG_LEVEL=simple ################################################################################### # Reverse Geocoding #################################################################################### DISABLE_REVERSE_GEOCODING=false # Reverse geocoding is done locally which has a small impact on memory usage # This memory usage can be altered by changing the REVERSE_GEOCODING_PRECISION variable # This ranges from 0-3 with 3 being the most precise # 3 - Cities > 500 population: ~200MB RAM # 2 - Cities > 1000 population: ~150MB RAM # 1 - Cities > 5000 population: ~80MB RAM # 0 - Cities > 15000 population: ~40MB RAM REVERSE_GEOCODING_PRECISION=3 #################################################################################### # WEB - Optional #################################################################################### # Custom message on the login page, should be written in HTML form. # For example PUBLIC_LOGIN_PAGE_MESSAGE="This is a demo instance of Immich.<br><br>Email: <i>demo@demo.de</i><br>Password: <i>demo</i>" PUBLIC_LOGIN_PAGE_MESSAGE="Photos" ``` ### Reproduction steps ```bash 1. Take a JPG from a Nikon camera processed with Lightroom 2. Upload said JPG with immich-cli 3. Take a JPG from a mobile phone and upload it with either CLI or mobile app 4. Compare the dates reported by immich ... ``` ### Additional information _No response_
Author
Owner

@jrasm91 commented on GitHub (Mar 15, 2023):

I assume they don't have gps tags or any timezones tags or timezones information in any of the datetime tags.

If that's the case, I think it defaults to UTC or the timezones of the local machine.

The solution to this probably should not be dependent on the local client. The same photo, regardless where it is uploaded from should produce the same output. Also, clearing and extracting exif should always yield the same results and not be dependent on anything except what's in the exif of the asset.

I'd probably recommend manually fixing your exif beforehand so it has timestamp aware dates or includes a timestamp exif tag, and then uploading them.

@jrasm91 commented on GitHub (Mar 15, 2023): I assume they don't have gps tags or any timezones tags or timezones information in any of the datetime tags. If that's the case, I think it defaults to UTC or the timezones of the local machine. The solution to this probably should not be dependent on the local client. The same photo, regardless where it is uploaded from should produce the same output. Also, clearing and extracting exif should always yield the same results and not be dependent on anything except what's in the exif of the asset. I'd probably recommend manually fixing your exif beforehand so it has timestamp aware dates or includes a timestamp exif tag, and then uploading them.
Author
Owner

@jrasm91 commented on GitHub (Mar 15, 2023):

Better yet would be setting the timezone on the camera itself 😁

@jrasm91 commented on GitHub (Mar 15, 2023): Better yet would be setting the timezone on the camera itself 😁
Author
Owner

@etnoy commented on GitHub (Mar 15, 2023):

I've done more digging. My Nikon cameras seems to produce exif data that has DateTimeOriginal without timezone even thoug the camera is time zone aware. Sample here: https://www.imaging-resource.com/PRODS/D700/EXIF/D700DFL_MFR200M0200.HTM

As you say, since the timestaps are in local time the exif tools will assume UTC for all those dates.

As a consequence I have near 100k (!) photos in my collection that won't import correctly, and that would also explain why Google Photos has this issue.

I'm thinking I can't be the only one with this issue, Surely more Nikon users are here on Immich :)

Not saying it's only an issue for Nikon, but that's the only data I have available to me.

Edit: To clarify, my DSLR photos don't have GPS tags.

@etnoy commented on GitHub (Mar 15, 2023): I've done more digging. My Nikon cameras seems to produce exif data that has DateTimeOriginal without timezone even thoug the camera is time zone aware. Sample here: https://www.imaging-resource.com/PRODS/D700/EXIF/D700DFL_MFR200M0200.HTM As you say, since the timestaps are in local time the exif tools will assume UTC for all those dates. As a consequence I have near 100k (!) photos in my collection that won't import correctly, and that would also explain why Google Photos has this issue. I'm thinking I can't be the only one with this issue, Surely more Nikon users are here on Immich :) Not saying it's only an issue for Nikon, but that's the only data I have available to me. Edit: To clarify, my DSLR photos don't have GPS tags.
Author
Owner

@etnoy commented on GitHub (Mar 15, 2023):

Maybe a last-resort-if-all-else-fails-fallback time zone should be configurable? I can handle it locally with the above docker timezone setting but that's not very obvious to the average user.

@etnoy commented on GitHub (Mar 15, 2023): Maybe a last-resort-if-all-else-fails-fallback time zone should be configurable? I can handle it locally with the above docker timezone setting but that's not very obvious to the average user.
Author
Owner

@jrasm91 commented on GitHub (Mar 15, 2023):

Yeah, we could maybe update the documentation to explain this and specify you can correctly set the timezone on the container to change the fallback timezone used in these scenarios.

@jrasm91 commented on GitHub (Mar 15, 2023): Yeah, we could maybe update the documentation to explain this and specify you can correctly set the timezone on the container to change the fallback timezone used in these scenarios.
Author
Owner

@etnoy commented on GitHub (Mar 15, 2023):

I documented the issue in #2003, hope this helps someone in the future

@etnoy commented on GitHub (Mar 15, 2023): I documented the issue in #2003, hope this helps someone in the future
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: immich-app/immich#749