Web Development

Unix Timestamps and Timezones: How UTC Conversion Works

Understand how Unix timestamps handle timezones, why they're always in UTC, and how to correctly convert between Unix time and local timezones in your applications.

By Inventive HQ Team

The Fundamental Truth About Unix Timestamps and Timezones

A Unix timestamp has no timezone. It is a single integer counting the seconds since January 1, 1970, 00:00:00 UTC, and it names the same absolute instant everywhere on Earth. A timezone only enters the picture at the very end, when you convert that instant into a human-readable local time to show a person. Timestamp 1735689600 is the same number in New York, Tokyo, London, and Sydney — what differs is the wall-clock string each machine prints when it formats that instant for display.

That's the summary an AI overview gives you. Here's what it can't: the reason "store in UTC, display in local" keeps tripping up experienced developers is that the languages you use hide the conversion. new Date('2025-01-01 12:00:00') in JavaScript and a naive datetime in Python silently borrow the machine's local timezone, so the exact same line of code produces a different instant on a laptop in New York and a server in UTC. Below we make the model concrete, give you a lookup table you can check against, and show the correct conversion in JavaScript, Python, and PHP — with the timezone stated explicitly every time.

One Unix timestamp is one absolute instant; timezone applies only at display The timestamp 1735689600 is a single UTC instant. Three panels format that same instant as different local wall-clock times for New York, London, and Tokyo, while the underlying number is unchanged. One instant, many clocks The number never changes — only the formatted display does 1735689600 absolute instant · UTC epoch seconds format for a timezone ↓ America/New_York UTC−5 (EST) Dec 31 2024 19:00:00 Europe/London UTC+0 (GMT) Jan 01 2025 00:00:00 Asia/Tokyo UTC+9 (JST) Jan 01 2025 09:00:00

Need to see a timestamp in another timezone right now? Try our free Unix Timestamp Converter to convert epoch time to any timezone instantly.

Loading interactive tool...

Understanding UTC: The Universal Time Reference

What is UTC?

UTC (Coordinated Universal Time) is the primary time standard by which the world regulates clocks and time. It's the modern successor to GMT (Greenwich Mean Time) and serves as the zero-point reference for all timezones worldwide.

Key characteristics of UTC:

  • Never observes Daylight Saving Time - UTC remains constant year-round
  • Scientifically maintained - Based on International Atomic Time with occasional leap seconds
  • Global standard - Used by aviation, navigation, science, and computing
  • Timezone offset reference - All timezones are expressed relative to UTC

Timezone Offsets from UTC

Every timezone on Earth is defined by its offset from UTC:

UTC+0:  London (UK), Reykjavik, Lisbon
UTC-5:  New York (EST), Toronto, Lima
UTC-8:  Los Angeles (PST), Vancouver, Tijuana
UTC+1:  Paris, Berlin, Rome, Madrid
UTC+5:30: New Delhi, Mumbai (note: 30-minute offset)
UTC+9:  Tokyo, Seoul
UTC+10: Sydney (AEDT), Melbourne

When we say "New York is UTC-5," we mean that when it's 12:00 PM (noon) in New York, it's 5:00 PM (17:00) in UTC.

How Unix Timestamps Handle Timezones

Storage: Always UTC

Unix timestamps are always stored in UTC. This is not negotiable or configurable—it's fundamental to how Unix time works. The single value 1735689600 renders as a different wall-clock string in every zone, yet it is the same instant in all of them:

TimezoneUTC offsetLocal display of 1735689600
UTC+00:002025-01-01 00:00:00
America/Los_Angeles (PST)−08:002024-12-31 16:00:00
America/New_York (EST)−05:002024-12-31 19:00:00
Europe/London (GMT)+00:002025-01-01 00:00:00
Europe/Paris (CET)+01:002025-01-01 01:00:00
Asia/Kolkata (IST)+05:302025-01-01 05:30:00
Asia/Tokyo (JST)+09:002025-01-01 09:00:00
Australia/Sydney (AEDT)+11:002025-01-01 11:00:00

Notice two things: India is offset by thirty minutes off the hour (so integer-hour math will silently corrupt it), and Sydney is on daylight time (AEDT, +11) in January, not its standard +10 — both are reasons to convert with a named IANA zone rather than a hardcoded number.

Display: Convert to Local Time

When you need to show a timestamp to users, you convert it from UTC to their local timezone. This conversion happens only for display purposes—the underlying timestamp remains unchanged:

const timestamp = 1735689600; // UTC timestamp

// Convert to user's local timezone
const date = new Date(timestamp * 1000);

// Different users see different local times:
// User in New York sees: 12/31/2024, 7:00 PM
// User in London sees:   1/1/2025, 12:00 AM
// User in Tokyo sees:    1/1/2025, 9:00 AM

The Golden Rule

Store in UTC, display in local time. Never store local time as a timestamp without timezone information.

Converting Unix Timestamps to Local Timezones

JavaScript Date Object

JavaScript's Date object automatically handles timezone conversion:

const timestamp = 1735689600; // Unix timestamp in seconds

// Convert to Date object (milliseconds required)
const date = new Date(timestamp * 1000);

// These methods return values in LOCAL timezone
date.getFullYear();  // Local year
date.getMonth();     // Local month (0-11)
date.getDate();      // Local day of month
date.getHours();     // Local hour

// These methods return values in UTC
date.getUTCFullYear();  // UTC year
date.getUTCMonth();     // UTC month
date.getUTCHours();     // UTC hour
Advertisement

Converting to Specific Timezones

Modern JavaScript provides the Intl.DateTimeFormat API for timezone-aware formatting:

const timestamp = 1735689600;
const date = new Date(timestamp * 1000);

// Format for New York timezone
const nyFormatter = new Intl.DateTimeFormat('en-US', {
  timeZone: 'America/New_York',
  year: 'numeric',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
  hour12: false
});

console.log(nyFormatter.format(date));
// Output: 12/31/2024, 19:00:00

// Format for Tokyo timezone
const tokyoFormatter = new Intl.DateTimeFormat('en-US', {
  timeZone: 'Asia/Tokyo',
  year: 'numeric',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
  hour12: false
});

console.log(tokyoFormatter.format(date));
// Output: 01/01/2025, 09:00:00

Using moment-timezone Library

For more complex timezone operations, libraries like moment-timezone are invaluable:

const moment = require('moment-timezone');

const timestamp = 1735689600;

// Convert to specific timezones
const nyTime = moment.unix(timestamp).tz('America/New_York');
const londonTime = moment.unix(timestamp).tz('Europe/London');
const tokyoTime = moment.unix(timestamp).tz('Asia/Tokyo');

console.log(nyTime.format('YYYY-MM-DD HH:mm:ss z'));
// Output: 2024-12-31 19:00:00 EST

console.log(londonTime.format('YYYY-MM-DD HH:mm:ss z'));
// Output: 2025-01-01 00:00:00 GMT

console.log(tokyoTime.format('YYYY-MM-DD HH:mm:ss z'));
// Output: 2025-01-01 09:00:00 JST

Converting Local Time to Unix Timestamps

When converting from local time to Unix timestamps, you must explicitly specify the timezone:

JavaScript: Specifying Timezone

// ❌ WRONG: Ambiguous - what timezone?
const date = new Date('2025-01-01 12:00:00');
const timestamp = date.getTime() / 1000;
// Interprets as local timezone of the system

// ✅ CORRECT: Explicit UTC time
const dateUTC = Date.UTC(2025, 0, 1, 12, 0, 0);
const timestampUTC = dateUTC / 1000;

// ✅ CORRECT: Using ISO 8601 with Z suffix (UTC)
const dateISO = new Date('2025-01-01T12:00:00Z');
const timestampISO = dateISO.getTime() / 1000;

// ✅ CORRECT: Using moment-timezone for specific timezone
const moment = require('moment-timezone');
const dateNY = moment.tz('2025-01-01 12:00:00', 'America/New_York');
const timestampNY = dateNY.unix();

Python: Timezone-Aware Conversion

from datetime import datetime
import pytz

# ❌ WRONG: Naive datetime (no timezone info)
dt = datetime(2025, 1, 1, 12, 0, 0)
timestamp = dt.timestamp()  # Assumes local timezone

# ✅ CORRECT: Timezone-aware datetime
ny_tz = pytz.timezone('America/New_York')
dt_ny = ny_tz.localize(datetime(2025, 1, 1, 12, 0, 0))
timestamp_ny = int(dt_ny.timestamp())

# ✅ CORRECT: UTC datetime
dt_utc = datetime(2025, 1, 1, 12, 0, 0, tzinfo=pytz.UTC)
timestamp_utc = int(dt_utc.timestamp())

PHP: DateTimeZone Class

// ❌ WRONG: No timezone specified
$date = new DateTime('2025-01-01 12:00:00');
$timestamp = $date->getTimestamp();

// ✅ CORRECT: Explicit timezone
$nyTz = new DateTimeZone('America/New_York');
$date = new DateTime('2025-01-01 12:00:00', $nyTz);
$timestamp = $date->getTimestamp();

// ✅ CORRECT: UTC timezone
$utcTz = new DateTimeZone('UTC');
$date = new DateTime('2025-01-01 12:00:00', $utcTz);
$timestamp = $date->getTimestamp();

Common Timezone Pitfalls and Solutions

Pitfall 1: Assuming Local Time is Universal

// ❌ WRONG: Developer in New York creates timestamp
const meetingTime = new Date('2025-06-15 14:00:00').getTime() / 1000;
// This creates a timestamp for 2:00 PM New York time

// User in Tokyo retrieves it
const date = new Date(meetingTime * 1000);
console.log(date.getHours()); // Shows 3:00 AM Tokyo time
// User is confused - meeting is at 3 AM?

// ✅ CORRECT: Always specify timezone explicitly
const meetingTimeUTC = Date.UTC(2025, 5, 15, 18, 0, 0) / 1000;
// 14:00 New York = 18:00 UTC
// User in any timezone can convert correctly

Pitfall 2: Storing Timezone Separately

// ❌ WRONG: Storing timezone as separate field
database.save({
  timestamp: 1735689600,
  timezone: 'America/New_York'
});
// Redundant and confusing - timestamp is already UTC

// ✅ CORRECT: Store only UTC timestamp
database.save({
  timestamp: 1735689600  // Always UTC
});
// Convert to user's timezone when displaying

Pitfall 3: Daylight Saving Time Complications

// Scenario: Scheduling a meeting 6 months in advance

// ❌ WRONG: Adding hours without considering DST
const now = Date.now() / 1000;
const sixMonthsLater = now + (6 * 30 * 24 * 60 * 60);
// This doesn't account for DST transitions

// ✅ CORRECT: Use date libraries that handle DST
const moment = require('moment-timezone');
const meeting = moment()
  .tz('America/New_York')
  .add(6, 'months')
  .unix();
// Correctly handles DST transitions

Pitfall 4: Parsing User Input

// User enters: "January 15, 2025, 3:00 PM"
// Question: Which timezone?

// ❌ WRONG: Assume local system timezone
const userDate = new Date('January 15, 2025 15:00:00');

// ✅ CORRECT: Ask user for timezone or detect it
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const moment = require('moment-timezone');
const userDate = moment.tz(
  '2025-01-15 15:00:00',
  userTimezone
).unix();

Best Practices for Timezone Handling

1. Always Store UTC Timestamps

// ✅ Database schema
CREATE TABLE events (
  id INT PRIMARY KEY,
  name VARCHAR(255),
  event_time INT,  -- Unix timestamp (always UTC)
  -- DON'T store separate timezone field
);

2. Convert Only for Display

// ✅ API response (server returns UTC timestamp)
{
  "event": {
    "id": 123,
    "name": "Product Launch",
    "event_time": 1735689600  // Unix timestamp (UTC)
  }
}

// ✅ Client converts to local time for display
const eventTime = new Date(event.event_time * 1000);
const localTime = eventTime.toLocaleString(undefined, {
  timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone
});

3. Use ISO 8601 for Human-Readable Formats

When you need human-readable timestamps (logs, debugging), use ISO 8601 with Z suffix:

const timestamp = 1735689600;
const date = new Date(timestamp * 1000);

// ISO 8601 with UTC indicator
console.log(date.toISOString());
// Output: 2025-01-01T00:00:00.000Z
//                                 ^ Z means UTC

4. Document Timezone Expectations

/**
 * Creates a new event
 * @param {string} name - Event name
 * @param {number} eventTime - Unix timestamp in SECONDS (UTC)
 * @returns {Object} Created event with UTC timestamp
 */
function createEvent(name, eventTime) {
  // Implementation
}

5. Test Across Timezones

// Unit tests should cover multiple timezones
describe('Event scheduling', () => {
  const timezones = [
    'America/New_York',
    'Europe/London',
    'Asia/Tokyo',
    'Australia/Sydney'
  ];

  timezones.forEach(tz => {
    it(`should handle ${tz} correctly`, () => {
      // Test implementation
    });
  });
});

Tools and Libraries for Timezone Handling

JavaScript

  • date-fns-tz: Lightweight, modern, tree-shakeable
  • moment-timezone: Feature-rich, widely adopted (maintenance mode)
  • luxon: Modern alternative to moment, better timezone support
  • day.js: Ultra-lightweight with timezone plugin

Python

  • pytz: Standard timezone database for Python
  • python-dateutil: Parser and timezone utilities
  • arrow: User-friendly dates and times

PHP

  • Carbon: Expressive datetime library
  • DateTimeZone: Built-in PHP class

Database

  • PostgreSQL: TIMESTAMP WITH TIME ZONE type
  • MySQL: Store as INT (Unix timestamp) or use TIMESTAMP
  • MongoDB: Stores dates as UTC milliseconds

Conclusion

Unix timestamps handle timezones through a simple but powerful principle: store everything in UTC, convert to local time only when displaying to users. This approach:

  • Eliminates timezone confusion in storage
  • Simplifies time calculations and comparisons
  • Ensures consistency across distributed systems
  • Makes it easy to support users in any timezone

Remember these key points:

  1. Unix timestamps are always UTC—this is not configurable
  2. Never store local time as a Unix timestamp without proper conversion
  3. Convert to local timezone only for display, never for storage or calculations
  4. Use proper libraries and APIs for timezone conversion—don't calculate manually
  5. Daylight Saving Time is handled automatically when you convert to local time
  6. Test your application with users in different timezones and during DST transitions

By following these principles, you'll build applications that correctly handle time across all timezones, preventing the all-too-common bugs that plague timezone-naive code.

Need to convert Unix timestamps between different timezones? Try our Unix Timestamp Converter with support for hundreds of timezones worldwide, or use our World Clock tool to coordinate times across multiple locations.

Frequently Asked Questions

Does a Unix timestamp have a timezone?

No. A Unix timestamp is a single integer counting the seconds since January 1, 1970, 00:00:00 UTC, and it carries no timezone information at all. The same number refers to the same absolute instant everywhere on Earth. A timezone only enters the picture when you convert that instant into a human-readable local time for display.

Is Unix time always in UTC?

Yes. Unix time is defined relative to the UTC epoch and is not configurable. It is often described as "timezone-independent" because the count of seconds is the same regardless of where the machine reading it sits. When people say a timestamp is "in EST" or "in local time," they really mean the formatted string they produced after converting the UTC instant — the underlying number never changes.

Why does the same timestamp show a different time on two computers?

Because each computer formats the same absolute instant using its own local timezone. Timestamp 1735689600 is Jan 1 2025 00:00:00 in UTC, but a machine set to America/New_York prints Dec 31 2024 19:00:00 and a machine in Asia/Tokyo prints Jan 1 2025 09:00:00. The instant is identical; only the display differs. This is expected behaviour, not a bug.

What is the most common Unix timestamp timezone bug?

Treating a UTC timestamp as if it were local time — or the reverse, building a timestamp from a local wall-clock string without stating its timezone. Functions like JavaScript's new Date('2025-01-01 12:00:00') or Python's naive datetime silently assume the system's local timezone, so the same code produces different instants on a laptop in New York and a server in UTC. Always parse and store in UTC and convert only for display.

How do I convert a Unix timestamp to a specific timezone?

Convert the timestamp to a date object, then format it with a named IANA timezone. In JavaScript use Intl.DateTimeFormat with a timeZone option such as 'America/New_York'; in Python use datetime.fromtimestamp(ts, tz=ZoneInfo('America/New_York')); in PHP set a DateTimeZone. Always use a named zone (e.g. Europe/London) rather than a hardcoded offset so that Daylight Saving Time is handled for you.

Should I store the user's timezone alongside the timestamp?

Not for the instant itself — the UTC timestamp already fixes the moment unambiguously, so a separate timezone column is redundant. The exception is future events tied to a place (for example a recurring 9am meeting in New York): there you store the local time plus the IANA timezone name, because DST rule changes can shift what UTC instant "9am next year" actually maps to.

Does Unix time account for Daylight Saving Time?

Unix time itself never observes DST — it is a continuous count of seconds in UTC, and UTC has no DST. DST only affects the offset applied when you convert an instant to a local time for display. Because that conversion is driven by the IANA timezone database, using a named zone means the correct seasonal offset is applied automatically.

What is the difference between UTC and a Unix timestamp?

UTC is a human-readable time standard (calendar date plus clock time, like 2025-01-01 00:00:00Z). A Unix timestamp is a machine-friendly integer count of seconds since the UTC epoch. They describe the same instants, but the timestamp strips out formatting, timezones, and calendar arithmetic, which makes it ideal for storage, comparison, and duration math.

unixtimestamptimezoneutcdatetime