DevToys Pro

free web developer tools

Blog
Rate us:
Try browser extension:
← Back to Blog

Timestamp Bugs: Seconds vs Milliseconds

12 min read

You're debugging an API integration and the dates are showing up as 1970 or 2053. Or maybe your timestamps work fine in local development but break in production. These are classic symptoms of timestamp format confusion: mixing seconds and milliseconds, mishandling timezones, or using the wrong Unix epoch format. Understanding how timestamps work across different systems is critical for reliable date handling in APIs, databases, and logs.

The Problem: Unix Timestamp Ambiguity

Unix timestamps represent time as the number of units elapsed since January 1, 1970 00:00:00 UTC (the Unix epoch). But there's a critical ambiguity: which unit?

  • Seconds: 1704985200 (10 digits, standard Unix timestamp)
  • Milliseconds: 1704985200000 (13 digits, JavaScript standard)
  • Microseconds: 1704985200000000 (16 digits, used in some databases)
  • Nanoseconds: 1704985200000000000 (19 digits, high-precision systems)

The problem? There's no universal standard. Different languages, APIs, and systems use different units, and timestamps don't carry metadata about their format.

Classic Bug: Date Shows as 1970 or 2053

You receive a timestamp from an API and convert it to a date. The result?

// API returns timestamp in seconds
const apiTimestamp = 1704985200;

// JavaScript expects milliseconds
const date = new Date(apiTimestamp);
console.log(date.toISOString());
// Output: 1970-01-20T18:03:05.200Z  ❌ Wrong!

The date is interpreted as January 20, 1970 instead of January 11, 2026. Why? JavaScript's Date() constructor expects milliseconds, but the API sent seconds.

The Fix: Convert Seconds to Milliseconds

// Multiply by 1000 to convert seconds to milliseconds
const date = new Date(apiTimestamp * 1000);
console.log(date.toISOString());
// Output: 2026-01-11T10:00:00.000Z  ✅ Correct!

Conversely, if you're sending timestamps to an API that expects seconds but you provide milliseconds, dates will appear in the distant future (year 2053+).

How to Detect Timestamp Format

When working with unknown timestamp formats, use the digit count as a detection heuristic:

function detectTimestampUnit(timestamp: number): string {
  const digits = timestamp.toString().length;
  
  if (digits === 10) return 'seconds';
  if (digits === 13) return 'milliseconds';
  if (digits === 16) return 'microseconds';
  if (digits === 19) return 'nanoseconds';
  
  return 'unknown';
}

// Examples
console.log(detectTimestampUnit(1704985200));      // 'seconds'
console.log(detectTimestampUnit(1704985200000));   // 'milliseconds'

This heuristic works until around the year 2286 (when Unix timestamps in seconds reach 10 billion, becoming 11 digits). For practical purposes, it's reliable for decades.

Language-Specific Timestamp Formats

Different programming languages have different conventions:

JavaScript / TypeScript

  • Date.now() returns milliseconds
  • new Date(timestamp) expects milliseconds
  • date.getTime() returns milliseconds
const now = Date.now();  // 1704985200000 (milliseconds)
const date = new Date(now);
console.log(date.toISOString());  // '2026-01-11T10:00:00.000Z'

Python

  • time.time() returns seconds (with decimal for sub-second precision)
  • datetime.timestamp() returns seconds
import time
from datetime import datetime

now = time.time()  # 1704985200.123456 (seconds)
date = datetime.fromtimestamp(now)
print(date.isoformat())  # '2026-01-11T10:00:00.123456'

Java

  • System.currentTimeMillis() returns milliseconds
  • Instant.now().toEpochMilli() returns milliseconds
  • Instant.now().getEpochSecond() returns seconds
long millis = System.currentTimeMillis();  // 1704985200000
long seconds = millis / 1000;  // 1704985200

Go

  • time.Now().Unix() returns seconds
  • time.Now().UnixMilli() returns milliseconds
  • time.Now().UnixNano() returns nanoseconds
package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    fmt.Println(now.Unix())      // 1704985200 (seconds)
    fmt.Println(now.UnixMilli()) // 1704985200000 (milliseconds)
}

Database Timestamp Storage

Databases also have varying conventions:

  • PostgreSQL: TIMESTAMP stores datetime values, not numeric timestamps. Use EXTRACT(EPOCH FROM timestamp) to get Unix seconds.
  • MySQL: TIMESTAMP stores datetime values. Use UNIX_TIMESTAMP() to get seconds.
  • MongoDB: Date type stores milliseconds since Unix epoch (compatible with JavaScript).
  • Redis: Stores timestamps as strings or numbers. Common practice is to use seconds for TTL, milliseconds for event timestamps.

PostgreSQL Example

-- Convert timestamp to Unix seconds
SELECT EXTRACT(EPOCH FROM NOW());  -- 1704985200.123456

-- Convert Unix seconds to timestamp
SELECT TO_TIMESTAMP(1704985200);  -- '2026-01-11 10:00:00+00'

Timezone Pitfalls

Unix timestamps are always in UTC, but displaying them requires timezone context. Common mistakes:

Mistake #1: Displaying UTC as Local Time

const timestamp = 1704985200;  // 2026-01-11 10:00:00 UTC
const date = new Date(timestamp * 1000);

// ❌ Wrong: Displays in browser's local timezone
console.log(date.toString());
// In New York: 'Sat Jan 11 2026 05:00:00 GMT-0500'
// In Tokyo: 'Sat Jan 11 2026 19:00:00 GMT+0900'

// ✅ Correct: Display explicitly in UTC
console.log(date.toISOString());
// '2026-01-11T10:00:00.000Z' (always UTC)

Mistake #2: Creating Timestamps from Local Time

// ❌ Wrong: Assumes local timezone
const date = new Date('2026-01-11 10:00:00');
console.log(date.getTime() / 1000);
// Result varies by user's timezone!

// ✅ Correct: Specify UTC explicitly
const utcDate = new Date('2026-01-11T10:00:00Z');
console.log(utcDate.getTime() / 1000);  // 1704985200 (consistent)

Solution: Always Use ISO 8601 with Timezone

When working with human-readable dates, always use ISO 8601 format with explicit timezone:

  • UTC: 2026-01-11T10:00:00Z or 2026-01-11T10:00:00+00:00
  • New York (EST): 2026-01-11T05:00:00-05:00
  • Tokyo (JST): 2026-01-11T19:00:00+09:00

Real-World Debugging Scenario

You're integrating with an external API and timestamps look wrong. Here's how to debug:

Step 1: Inspect the Raw Timestamp

const apiResponse = {
  "created_at": 1704985200,
  "updated_at": 1704985200000
};

console.log('created_at digits:', apiResponse.created_at.toString().length);
// Output: 10 (likely seconds)

console.log('updated_at digits:', apiResponse.updated_at.toString().length);
// Output: 13 (likely milliseconds)

Step 2: Test Conversion with Known Date

Convert the timestamp and check if it matches a known date. For example, if the API documentation says created_at is January 11, 2026 10:00:00 UTC:

// Test as seconds
const dateSeconds = new Date(1704985200 * 1000);
console.log(dateSeconds.toISOString());
// '2026-01-11T10:00:00.000Z' ✅ Matches!

// Test as milliseconds (wrong)
const dateMillis = new Date(1704985200);
console.log(dateMillis.toISOString());
// '1970-01-20T18:03:05.200Z' ❌ Wrong year

Step 3: Verify Timezone Handling

// Check if the displayed time matches expectations
const date = new Date(1704985200 * 1000);

console.log('ISO (UTC):', date.toISOString());
// '2026-01-11T10:00:00.000Z'

console.log('Local string:', date.toString());
// 'Sat Jan 11 2026 05:00:00 GMT-0500 (Eastern Standard Time)'

console.log('UTC string:', date.toUTCString());
// 'Sat, 11 Jan 2026 10:00:00 GMT'

Common Timestamp Conversion Errors

Error: Date 50 Years in the Past or Future

Cause: Mixing seconds and milliseconds

Fix: Multiply by 1000 if the timestamp is in seconds, divide by 1000 if it's in milliseconds

Error: Date Off by Hours

Cause: Timezone confusion (displaying UTC as local or vice versa)

Fix: Always use toISOString() or toUTCString() for consistent UTC display

Error: Timestamps Work Locally but Fail in Production

Cause: Local dev machine has a different timezone than production servers

Fix: Set server timezone to UTC, always use UTC for timestamps internally, convert to local timezones only for display

Error: Invalid Date When Parsing Timestamps

Cause: Timestamp format not recognized by new Date()

Fix: Use numeric timestamps or ISO 8601 strings, avoid locale-specific date formats

Best Practices for Timestamp Handling

  1. Store timestamps as Unix seconds or milliseconds in UTC — never store local time without timezone
  2. Use ISO 8601 format for human-readable dates — always include timezone (Z or ±HH:MM)
  3. Document timestamp units in API contracts — specify seconds, milliseconds, or ISO format
  4. Detect format using digit count — 10 digits = seconds, 13 = milliseconds
  5. Use date libraries for complex operations — e.g., date-fns, Luxon, Day.js (instead of raw Date)
  6. Test with edge cases — year boundaries, leap seconds, daylight saving transitions
  7. Set server timezone to UTC — avoid timezone drift between environments

Using Timestamp Conversion Tools

When debugging timestamp issues, use a timestamp converter to:

  • Convert between seconds, milliseconds, and human-readable dates
  • Test timestamp values with known dates
  • Verify timezone handling
  • Generate timestamps for testing
  • Check current Unix time for reference

The date converter on DevToys Pro automatically detects timestamp format, handles multiple timezones, and provides instant conversion between Unix timestamps, ISO 8601, and human-readable formats.

Validation Checklist

Before deploying timestamp handling code, verify:

  • ✅ Timestamps converted to correct unit (seconds ↔ milliseconds)
  • ✅ UTC used for storage and API communication
  • ✅ Timezone explicitly specified when displaying dates
  • ✅ Edge cases tested (year boundaries, leap years)
  • ✅ API documentation specifies timestamp format
  • ✅ Server timezone set to UTC
  • ✅ Date libraries used for complex timezone logic

Key Takeaways

  • Unix timestamps can be in seconds (10 digits) or milliseconds (13 digits)
  • JavaScript expects milliseconds; many APIs send seconds — convert accordingly
  • Always store and communicate timestamps in UTC, convert to local only for display
  • Use ISO 8601 format with explicit timezone for human-readable dates
  • Detect timestamp format using digit count as a heuristic
  • Test timestamp conversions with known dates to verify correctness
  • Document timestamp units in API contracts to prevent integration bugs

Related Tools: