Unix Timestamp Pitfalls: A Complete Guide to Timestamp Conversion#

Last week, I was debugging a production issue. The logs were filled with numbers like 1745678901. Our new intern looked confused: “What are these?” I said, “Unix timestamps.” He asked, “How do I convert them to human-readable time?”

That question made me realize timestamp conversion is trickier than I thought.

What is a Unix Timestamp?#

A Unix timestamp is the number of seconds since January 1, 1970, 00:00:00 UTC. Why 1970? That’s when Unix was born, and it needed a unified starting point.

// Get current timestamp (seconds)
const timestamp = Math.floor(Date.now() / 1000)
console.log(timestamp)  // 1745678901

// Get current timestamp (milliseconds)
const ms = Date.now()
console.log(ms)  // 1745678901234

JavaScript’s Date.now() returns milliseconds, but Unix timestamps are typically seconds. That’s the first pitfall.

Seconds vs Milliseconds: Auto-Detection#

Backend APIs might return timestamps in seconds or milliseconds. You need to detect which:

function normalizeTimestamp(ts: number): number {
  // Seconds: 10 digits (1970-2286)
  // Milliseconds: 13 digits (1970-5138)
  if (ts > 9999999999) {
    // Milliseconds, convert to seconds
    return Math.floor(ts / 1000)
  }
  return ts
}

// Usage
console.log(normalizeTimestamp(1745678901))      // 1745678901 (seconds)
console.log(normalizeTimestamp(1745678901234))   // 1745678901 (converted)

The logic is simple: if it’s larger than 9999999999 (year 2286), it’s milliseconds.

Timestamp to Date: The Timezone Trap#

function timestampToDate(ts: number): string {
  const normalized = normalizeTimestamp(ts)
  const date = new Date(normalized * 1000)
  
  // Local time
  return date.toLocaleString('en-US', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false
  })
}

console.log(timestampToDate(1745678901))
// Output: 04/26/2025, 20:28:21 (Beijing time)

But here’s the problem: new Date() uses the local timezone. If your server is in Beijing and the user is in New York, the displayed time will be wrong.

Solution: Specify Timezone Explicitly#

function timestampToDateWithTimezone(ts: number, timezone: string): string {
  const normalized = normalizeTimestamp(ts)
  const date = new Date(normalized * 1000)
  
  return date.toLocaleString('en-US', {
    timeZone: timezone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false
  })
}

// Different timezones
console.log(timestampToDateWithTimezone(1745678901, 'Asia/Shanghai'))    // 04/26/2025, 20:28:21
console.log(timestampToDateWithTimezone(1745678901, 'America/New_York')) // 04/26/2025, 08:28:21
console.log(timestampToDateWithTimezone(1745678901, 'UTC'))              // 04/26/2025, 12:28:21

Date to Timestamp: String Format Pitfalls#

function dateToTimestamp(dateStr: string): number {
  const date = new Date(dateStr)
  if (isNaN(date.getTime())) {
    throw new Error('Invalid date format')
  }
  return Math.floor(date.getTime() / 1000)
}

// Different formats
console.log(dateToTimestamp('2025-04-26 20:28:21'))  // 1745678901 ✅
console.log(dateToTimestamp('2025/04/26 20:28:21'))  // 1745678901 ✅
console.log(dateToTimestamp('2025-04-26T20:28:21'))  // 1745678901 ✅ (ISO format)
console.log(dateToTimestamp('04/26/2025'))           // Browser-dependent ⚠️

Use ISO 8601 format (YYYY-MM-DDTHH:mm:ss) — it’s the only format consistent across browsers.

Safer Approach: Manual Parsing#

function parseDateSafely(dateStr: string): number {
  // Support: YYYY-MM-DD HH:mm:ss or YYYY-MM-DDTHH:mm:ss
  const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})[T\s](\d{2}):(\d{2}):(\d{2})$/)
  
  if (!match) {
    throw new Error('Format must be YYYY-MM-DD HH:mm:ss')
  }
  
  const [, year, month, day, hour, minute, second] = match.map(Number)
  const date = new Date(year, month - 1, day, hour, minute, second)
  
  return Math.floor(date.getTime() / 1000)
}

console.log(parseDateSafely('2025-04-26 20:28:21'))  // 1745678901 ✅

Common Timestamp Traps#

1. 32-bit Integer Overflow#

Legacy systems store timestamps as 32-bit signed integers. The maximum value is 2147483647, which corresponds to January 19, 2038. This is the famous “Year 2038 problem.”

// Problem on 32-bit systems
const max32bit = 2147483647
const date = new Date(max32bit * 1000)
console.log(date.toISOString())  // 2038-01-19T03:14:07.000Z

// Beyond this value, 32-bit systems wrap to negative
const overflow = 2147483648
// 32-bit: -2147483648 (year 1969)
// 64-bit: normal

2. Leap Seconds#

UTC occasionally inserts leap seconds, but Unix timestamps don’t account for them. This means some timestamps correspond to two different UTC times.

// 2016-12-31 23:59:60 UTC exists (leap second)
// But JavaScript's Date doesn't support it
const date = new Date('2016-12-31T23:59:60Z')
console.log(date)  // Invalid Date

3. Browser Compatibility#

// Safari doesn't support YYYY-MM-DD HH:mm:ss format
const date1 = new Date('2025-04-26 20:28:21')
// Safari: Invalid Date ❌
// Chrome: Works ✅

// Solution: Replace space with T
const date2 = new Date('2025-04-26 20:28:21'.replace(' ', 'T'))
// Works in all browsers ✅

Performance Optimization for Batch Conversion#

When converting many timestamps, looping with new Date() can be slow:

// Batch conversion (slow)
function batchConvertSlow(timestamps: number[]): string[] {
  return timestamps.map(ts => new Date(ts * 1000).toISOString())
}

// Batch conversion (fast)
function batchConvertFast(timestamps: number[]): string[] {
  const results: string[] = new Array(timestamps.length)
  
  for (let i = 0; i < timestamps.length; i++) {
    const date = new Date(timestamps[i] * 1000)
    results[i] = date.toISOString()
  }
  
  return results
}

// Performance: 1 million items
// batchConvertSlow: ~1200ms
// batchConvertFast: ~800ms

Pre-allocated array + for loop is about 30% faster than map.

A Practical Tool#

For daily use, I built an online tool: Timestamp Converter

Features:

  • Real-time current timestamp display
  • Auto-detection of seconds/milliseconds
  • Multiple date format support
  • One-click copy

The implementation isn’t complex, but getting the details right takes effort. Hope this helps.


Related: Timezone Converter | Date Calculator