Files
panel/DOCKER_TIMEZONE_FIX.md

4.2 KiB

Docker Timezone Configuration Fix

Problem

Even after fixing the SQLite datetime('now', 'localtime') calls, notes posted at 10:00 still showed as 08:00 when running in Docker.

Root Cause

Docker containers run in UTC timezone by default!

When using datetime('now', 'localtime') in SQLite:

  • On local Windows machine: Uses Windows timezone (Europe/Warsaw) → Correct
  • In Docker container: Uses container timezone (UTC) → Wrong by 2 hours

Example:

User posts at 10:00 Poland time (UTC+2)
↓
Docker container thinks local time is 08:00 UTC
↓
SQLite datetime('now', 'localtime') stores: 08:00
↓
Display shows: 08:00 (wrong!)

Solution

Set the Docker container timezone to Europe/Warsaw

1. Updated Dockerfile (Production)

# Use Node.js 22.11.0 as the base image
FROM node:22.11.0

# Set timezone to Europe/Warsaw (Polish timezone)
ENV TZ=Europe/Warsaw
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# ... rest of Dockerfile

2. Updated Dockerfile.dev (Development)

# Use Node.js 22.11.0 as the base image
FROM node:22.11.0

# Set timezone to Europe/Warsaw (Polish timezone)
ENV TZ=Europe/Warsaw
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# ... rest of Dockerfile

3. Updated docker-compose.yml (Development)

environment:
  - NODE_ENV=development
  - TZ=Europe/Warsaw

4. Updated docker-compose.prod.yml (Production)

environment:
  - NODE_ENV=production
  - TZ=Europe/Warsaw
  - NEXTAUTH_SECRET=...
  - NEXTAUTH_URL=...

How to Apply

Option 1: Rebuild Docker Images

# Stop containers
docker-compose down

# Rebuild images
docker-compose build --no-cache

# Start containers
docker-compose up -d

Option 2: For Production Deployment

# Pull latest code with fixes
git pull

# Rebuild production image
docker-compose -f docker-compose.prod.yml build --no-cache

# Restart
docker-compose -f docker-compose.prod.yml up -d

Verification

After rebuilding and restarting, verify timezone inside container:

# Check timezone
docker exec -it <container_name> date
# Should show: Sat Oct  4 19:00:00 CEST 2025

# Check Node.js sees correct timezone
docker exec -it <container_name> node -e "console.log(new Date().toLocaleString('pl-PL', {timeZone: 'Europe/Warsaw'}))"
# Should show current Polish time

# Check SQLite sees correct timezone
docker exec -it <container_name> node -e "const db = require('better-sqlite3')('./data/database.sqlite'); console.log(db.prepare(\"SELECT datetime('now', 'localtime')\").get());"
# Should show current Polish time

Why This Works

  1. TZ Environment Variable: Tells all processes (including Node.js and SQLite) what timezone to use
  2. Symlink /etc/localtime: Updates system timezone for the entire container
  3. echo TZ > /etc/timezone: Ensures the timezone persists

Now when SQLite uses datetime('now', 'localtime'):

  • Container local time is 10:00 Poland time
  • SQLite stores: 10:00
  • Display shows: 10:00

Important Notes

  1. Must rebuild images: Just restarting containers is not enough - the timezone configuration is baked into the image
  2. All existing data: Old notes will still show incorrect times (they were stored in UTC)
  3. New notes: Will now display correctly
  4. DST handling: Europe/Warsaw automatically handles Daylight Saving Time transitions

Instead of changing container timezone, you could:

  1. Store everything in UTC (like audit logs do with ISO format)
  2. Always convert on display

But this requires more code changes and the current approach is simpler and more maintainable.

Files Modified

  1. Dockerfile - Added TZ configuration
  2. Dockerfile.dev - Added TZ configuration
  3. docker-compose.yml - Added TZ environment variable
  4. docker-compose.prod.yml - Added TZ environment variable

Testing Checklist

After deployment:

  • Container shows correct date/time with docker exec <container> date
  • Post a new note at known time (e.g., 10:30)
  • Verify note displays the same time (10:30)
  • Check both project notes and task notes
  • Verify audit logs still work correctly
  • Check task timestamps (date_started, date_completed)