timestamper.online

Timezones & Daylight Saving Time (DST) for Unix Timestamps

Last updated 2025-09-10

Practical rules for storing in UTC, displaying in local time, converting between timezones, and avoiding DST pitfalls.

Core Principles
Production-proof rules you can adopt today
  • Store UTC: Keep timestamps in UTC. Do not store local time in databases.
  • Display local: Convert to the user’s timezone for UI only.
  • Carry timezone context: For calendar/events, store a timezone ID (e.g. "America/New_York") alongside UTC timestamps.
  • Be DST-aware: Local times can jump/skip/duplicate around transitions; timestamps themselves do not.
  • Normalize I/O: Accept ISO 8601/UTC inputs; output clearly labeled ISO or epoch values.
JavaScript Examples
Node/Browser timezone-safe conversions

UTC storage and local display

// UTC epoch seconds -> Date -> Local string
const seconds = 1640995200; // 2022-01-01T00:00:00Z
const d = new Date(seconds * 1000);
console.log(d.toISOString());      // UTC
console.log(d.toLocaleString());   // Local time (based on system timezone)

Convert to specific timezone (Intl API)

const ts = 1640995200; // seconds, UTC
const date = new Date(ts * 1000);
const inNY = date.toLocaleString('en-US', { timeZone: 'America/New_York' });
const inTokyo = date.toLocaleString('en-US', { timeZone: 'Asia/Tokyo' });
console.log({ inNY, inTokyo });

DST transition caution

// Around DST, local times can repeat or skip.
// Prefer storing UTC timestamps and rendering with explicit timezone.
function toZone(d, tz) {
  return d.toLocaleString('en-US', { timeZone: tz });
}
const d1 = new Date('2021-03-14T01:30:00-05:00'); // US spring forward day
const d2 = new Date('2021-11-07T01:30:00-04:00'); // US fall back day
console.log(toZone(d1, 'America/New_York'));
console.log(toZone(d2, 'America/New_York'));
Python Examples
UTC-first handling with timezone conversions

UTC storage and local display

import datetime, zoneinfo

ts = 1640995200
utc_dt = datetime.datetime.utcfromtimestamp(ts).replace(tzinfo=datetime.timezone.utc)
local_dt = utc_dt.astimezone(zoneinfo.ZoneInfo('America/New_York'))
print(utc_dt.isoformat())
print(local_dt.isoformat())

Parsing ISO and normalizing to UTC

from datetime import datetime, timezone

def parse_iso_to_utc(iso_str: str) -> datetime:
    dt = datetime.fromisoformat(iso_str.replace('Z', '+00:00'))
    if dt.tzinfo is None:
        # treat naive input as UTC or reject based on your policy
        dt = dt.replace(tzinfo=timezone.utc)
    return dt.astimezone(timezone.utc)

print(parse_iso_to_utc('2022-01-01T00:00:00Z').isoformat())
Common Pitfalls
Where teams usually get it wrong
  • Storing local time: Causes ambiguity and DST bugs; always store UTC.
  • Dropping timezone IDs for events: For calendar-like features, store a zone ID alongside UTC for correct recurring calculations.
  • Assuming fixed offsets: Offsets change with DST. Use timezone databases (IANA) instead of hard-coded offsets.
  • Naive datetimes: When parsing strings, attach or infer timezone explicitly; avoid timezone-less values.
  • Inconsistent rounding: Always use floor when converting ms→s; document conventions in APIs.
FAQs
Quick answers

Why does the same timestamp show different local times?

Because local time depends on the timezone offset (and DST). The timestamp itself is UTC and constant.

Should I store offsets or timezone names?

Store UTC timestamps, and when event semantics matter, store the IANA timezone ID (e.g., "Europe/Berlin"). Offsets alone are not enough across DST changes.

How do I convert a timestamp to a specific timezone?

In JS, use toLocaleString with a timeZone option. In Python, use zoneinfo.ZoneInfo and astimezone.