Unix Epoch Timestamp: The Complete Developer's Reference

Convert Epoch Timestamps Instantly

Need to convert a Unix timestamp right now? Use our Epoch Converter to translate between Unix timestamps and human-readable dates in real time, right in your browser.

1. What Is Unix Epoch Time and Why It Matters

Unix epoch time (also called POSIX time, Unix time, or simply "epoch") is a system for tracking time as a single number: the count of seconds that have elapsed since January 1, 1970, at 00:00:00 UTC. That moment in time is known as the Unix epoch, and every second since then increments this counter by one. Right now, as you read this in 2026, the epoch value is somewhere around 1.77 billion.

This deceptively simple concept underpins virtually every piece of software you interact with. When your browser stores a cookie expiration, when a database records when a row was created, when a log file marks when an error occurred, when a message queue orders events, they are almost always using epoch timestamps under the hood.

Why January 1, 1970?

The choice of epoch date is historical. Early Unix systems at Bell Labs in the late 1960s needed a reference point for time. The original Unix time used January 1, 1971, as the epoch and stored time as a 32-bit integer counting in sixtieths of a second. This was quickly changed: the resolution was reduced to whole seconds (making the 32-bit range span many more years) and the epoch was shifted to January 1, 1970, a conveniently round date close to Unix's birth. This decision, made over half a century ago, still defines how billions of devices track time today.

Why Epoch Time Matters

Epoch timestamps solve several fundamental problems in computing:

These properties make epoch timestamps the de facto standard for storing and transmitting time in software systems. The human-readable format (February 11, 2026, 3:00 PM EST) is for display only. Under the surface, everything is epoch.

2. The Y2K38 Problem: 32-Bit Timestamp Overflow

If you are old enough to remember the Y2K scare at the turn of the millennium, the Y2K38 problem may feel like deja vu. On January 19, 2038, at 03:14:07 UTC, the Unix epoch timestamp will reach 2,147,483,647, which is the maximum value a signed 32-bit integer can hold. One second later, systems still using 32-bit timestamps will overflow, wrapping around to -2,147,483,648, which corresponds to December 13, 1901.

Critical Date: January 19, 2038, 03:14:07 UTC. At epoch value 2147483647, signed 32-bit integers overflow. Systems using time_t as a 32-bit int will interpret the next second as a date in 1901.

Why 32-Bit?

When Unix was designed in the early 1970s, 32-bit integers were the natural word size. A signed 32-bit integer can represent values from -2,147,483,648 to 2,147,483,647. Starting from January 1, 1970, that gives a range of about 68 years in each direction: back to 1901 and forward to 2038. In 1970, a 68-year horizon felt impossibly distant. No one expected software written that year to still be running in 2038. And yet here we are.

What Systems Are Affected?

The Fix: 64-Bit Timestamps

The solution is straightforward: use 64-bit integers for timestamps. A signed 64-bit integer can represent epoch values up to 9,223,372,036,854,775,807, which corresponds to a date approximately 292 billion years in the future. That should be sufficient.

Most modern systems have already migrated. Linux moved to a 64-bit time_t on all architectures. Modern versions of MySQL, PostgreSQL, and most databases use 64-bit timestamps. JavaScript, Python, Java, and Go all use 64-bit (or larger) representations internally. The risk lies in legacy systems, embedded devices, and binary file formats that were never updated.

If you maintain any system that stores or processes timestamps, audit your code now. Search for 32-bit timestamp usage: int or unsigned int storing epoch values in C/C++, INT columns storing timestamps in databases, and any serialization format that writes timestamps as 4-byte values.

3. Getting the Current Epoch Time in 8 Languages

Here is how to get the current Unix epoch timestamp in every major programming language. Pay close attention to whether each returns seconds or milliseconds, as this is the single most common source of timestamp bugs.

JavaScript

// Milliseconds since epoch (JavaScript's default)
const msEpoch = Date.now();
console.log(msEpoch); // 1739232000000

// Seconds since epoch
const secEpoch = Math.floor(Date.now() / 1000);
console.log(secEpoch); // 1739232000

// From a Date object
const date = new Date();
const epoch = Math.floor(date.getTime() / 1000);

// High-resolution timestamp (for performance measurement)
const hrTime = performance.now(); // milliseconds with microsecond precision

Watch out: JavaScript's Date.now() returns milliseconds, not seconds. Forgetting to divide by 1000 is the most common JavaScript timestamp bug. If your epoch value has 13 digits, it is milliseconds. If it has 10, it is seconds.

Python

import time
from datetime import datetime, timezone

# Seconds since epoch (float with sub-second precision)
epoch = time.time()
print(epoch)  # 1739232000.123456

# Integer seconds
epoch_int = int(time.time())
print(epoch_int)  # 1739232000

# Using datetime
epoch_dt = int(datetime.now(timezone.utc).timestamp())

# Nanoseconds (Python 3.7+)
epoch_ns = time.time_ns()
print(epoch_ns)  # 1739232000123456789

Java

import java.time.Instant;

// Seconds since epoch
long epochSeconds = Instant.now().getEpochSecond();
System.out.println(epochSeconds); // 1739232000

// Milliseconds since epoch
long epochMillis = System.currentTimeMillis();
System.out.println(epochMillis); // 1739232000000

// Using Instant (preferred in modern Java)
Instant now = Instant.now();
long seconds = now.getEpochSecond();
int nanos = now.getNano();

Go

package main

import (
    "fmt"
    "time"
)

func main() {
    // Seconds since epoch
    epoch := time.Now().Unix()
    fmt.Println(epoch) // 1739232000

    // Milliseconds since epoch
    epochMs := time.Now().UnixMilli()
    fmt.Println(epochMs) // 1739232000000

    // Nanoseconds since epoch
    epochNs := time.Now().UnixNano()
    fmt.Println(epochNs) // 1739232000000000000
}

Ruby

# Seconds since epoch (integer)
epoch = Time.now.to_i
puts epoch  # 1739232000

# Seconds with fractional precision
epoch_f = Time.now.to_f
puts epoch_f  # 1739232000.123456

# Milliseconds
epoch_ms = (Time.now.to_f * 1000).to_i
puts epoch_ms  # 1739232000123

PHP

<?php
// Seconds since epoch
$epoch = time();
echo $epoch; // 1739232000

// Milliseconds (PHP 8.0+)
$epochMs = hrtime(true) / 1e6;

// With microtime
$epochMicro = microtime(true);
echo $epochMicro; // 1739232000.123456

// Using DateTime
$dt = new DateTime();
$epoch = $dt->getTimestamp();
?>

Bash

# Seconds since epoch
date +%s
# 1739232000

# Nanoseconds since epoch (GNU date)
date +%s%N
# 1739232000123456789

# Milliseconds since epoch
echo $(($(date +%s%N) / 1000000))

# macOS (BSD date)
date +%s

C

#include <stdio.h>
#include <time.h>

int main() {
    // Seconds since epoch
    time_t epoch = time(NULL);
    printf("Epoch: %ld\n", (long)epoch);

    // With higher precision (POSIX)
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    printf("Seconds: %ld\n", ts.tv_sec);
    printf("Nanoseconds: %ld\n", ts.tv_nsec);

    return 0;
}

Verify Your Timestamps

Not sure if your epoch value is correct? Paste it into our Epoch Converter to see the human-readable date instantly. It handles both seconds and milliseconds automatically.

4. Converting Epoch to Human-Readable Dates and Back

The most common operation with epoch timestamps is converting between the numeric value and a human-readable date string. Here are the patterns you will use daily.

Epoch to Date String

// JavaScript
const date = new Date(1739232000 * 1000); // multiply by 1000 for ms
console.log(date.toISOString());     // "2025-02-11T00:00:00.000Z"
console.log(date.toLocaleString());  // locale-dependent output

// Python
from datetime import datetime, timezone
dt = datetime.fromtimestamp(1739232000, tz=timezone.utc)
print(dt.isoformat())  # "2025-02-11T00:00:00+00:00"
print(dt.strftime("%Y-%m-%d %H:%M:%S"))  # "2025-02-11 00:00:00"

// Go
t := time.Unix(1739232000, 0)
fmt.Println(t.UTC().Format(time.RFC3339))  // "2025-02-11T00:00:00Z"

// PHP
echo date('Y-m-d H:i:s', 1739232000);  // "2025-02-11 00:00:00"

// Bash
date -d @1739232000  # GNU
date -r 1739232000   # macOS/BSD

Date String to Epoch

// JavaScript
const epoch = Math.floor(new Date("2026-02-11T00:00:00Z").getTime() / 1000);
console.log(epoch); // 1770681600

// Python
from datetime import datetime, timezone
dt = datetime.strptime("2026-02-11 00:00:00", "%Y-%m-%d %H:%M:%S")
dt = dt.replace(tzinfo=timezone.utc)
epoch = int(dt.timestamp())
print(epoch)  # 1770681600

// Go
t, _ := time.Parse(time.RFC3339, "2026-02-11T00:00:00Z")
fmt.Println(t.Unix())  // 1770681600

// PHP
$epoch = strtotime("2026-02-11 00:00:00 UTC");
echo $epoch;  // 1770681600

// Bash
date -d "2026-02-11 00:00:00 UTC" +%s  # 1770681600

Converting Between Time Zones

Remember: epoch time is always UTC. Conversion to a local time zone happens only when displaying to a user.

// JavaScript: Display epoch in different time zones
const epoch = 1770681600;
const date = new Date(epoch * 1000);

console.log(date.toLocaleString('en-US', { timeZone: 'America/New_York' }));
// "2/10/2026, 7:00:00 PM"

console.log(date.toLocaleString('en-US', { timeZone: 'Asia/Tokyo' }));
// "2/11/2026, 9:00:00 AM"

console.log(date.toLocaleString('en-US', { timeZone: 'Europe/London' }));
// "2/11/2026, 12:00:00 AM"

// Python: Display in different time zones
from datetime import datetime, timezone
from zoneinfo import ZoneInfo  # Python 3.9+

epoch = 1770681600
dt_utc = datetime.fromtimestamp(epoch, tz=timezone.utc)

dt_ny = dt_utc.astimezone(ZoneInfo("America/New_York"))
print(dt_ny)  # 2026-02-10 19:00:00-05:00

dt_tokyo = dt_utc.astimezone(ZoneInfo("Asia/Tokyo"))
print(dt_tokyo)  # 2026-02-11 09:00:00+09:00

Explore Timestamp Formats

Working with multiple timestamp formats? Our Timestamp Tool lets you convert between Unix timestamps, ISO 8601 strings, and human-readable dates side by side.

5. Epoch Time in Databases

Every major database handles epoch timestamps, but they each have different conventions, functions, and pitfalls. Choosing the right column type and knowing the conversion functions for your database can save you hours of debugging.

PostgreSQL

PostgreSQL has rich timestamp support and makes epoch conversions straightforward:

-- Current epoch time
SELECT EXTRACT(EPOCH FROM NOW());
-- 1770681600.123456

-- Epoch to timestamp
SELECT TO_TIMESTAMP(1770681600);
-- 2026-02-11 00:00:00+00

-- Timestamp to epoch
SELECT EXTRACT(EPOCH FROM TIMESTAMP '2026-02-11 00:00:00 UTC');
-- 1770681600

-- Store as integer column (seconds)
CREATE TABLE events (
    id SERIAL PRIMARY KEY,
    name TEXT,
    created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW())::BIGINT
);

-- Or use native TIMESTAMPTZ (recommended)
CREATE TABLE events_v2 (
    id SERIAL PRIMARY KEY,
    name TEXT,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Query with epoch input
SELECT * FROM events_v2
WHERE created_at > TO_TIMESTAMP(1770681600);

Recommendation: Use TIMESTAMPTZ for storage and convert to/from epoch at the application layer. PostgreSQL stores TIMESTAMPTZ as a 64-bit integer internally (microseconds since 2000-01-01), so you get the storage efficiency of epoch with the query convenience of native timestamps.

MySQL

-- Current epoch time
SELECT UNIX_TIMESTAMP();
-- 1770681600

-- Epoch to datetime
SELECT FROM_UNIXTIME(1770681600);
-- '2026-02-11 00:00:00'

-- Datetime to epoch
SELECT UNIX_TIMESTAMP('2026-02-11 00:00:00');
-- 1770681600

-- TIMESTAMP column (auto-converts, but historically 32-bit limited)
CREATE TABLE events (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- BIGINT column for epoch storage (no Y2038 risk)
CREATE TABLE events_v2 (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255),
    created_at BIGINT DEFAULT (UNIX_TIMESTAMP())
);

-- DATETIME column (no Y2038 risk, range to 9999-12-31)
CREATE TABLE events_v3 (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255),
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

MySQL Y2038 Warning: The TIMESTAMP type in MySQL historically used a 32-bit integer, with a range of 1970-01-01 00:00:01 to 2038-01-19 03:14:07. MySQL 8.0.28+ extended this range, but older versions will break. If you are on an older MySQL version, use DATETIME or BIGINT instead.

SQLite

-- SQLite has no native date type; timestamps are stored as text, integer, or real

-- Current epoch
SELECT strftime('%s', 'now');
-- '1770681600'

-- Epoch to readable date
SELECT datetime(1770681600, 'unixepoch');
-- '2026-02-11 00:00:00'

-- Epoch to local time
SELECT datetime(1770681600, 'unixepoch', 'localtime');
-- '2026-02-10 19:00:00' (depends on server TZ)

-- Readable date to epoch
SELECT strftime('%s', '2026-02-11 00:00:00');
-- '1770681600'

-- Create table with integer epoch
CREATE TABLE events (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT,
    created_at INTEGER DEFAULT (strftime('%s', 'now'))
);

MongoDB

// MongoDB stores dates as 64-bit millisecond epoch internally

// Current date (stored as ms epoch internally)
db.events.insertOne({
    name: "deploy",
    createdAt: new Date()
});

// From epoch seconds
db.events.insertOne({
    name: "deploy",
    createdAt: new Date(1770681600 * 1000) // multiply by 1000!
});

// Query by epoch range
db.events.find({
    createdAt: {
        $gte: new Date(1770681600 * 1000),
        $lt: new Date(1770768000 * 1000)
    }
});

// Get epoch from a document
const doc = db.events.findOne();
const epochMs = doc.createdAt.getTime(); // milliseconds
const epochSec = Math.floor(doc.createdAt.getTime() / 1000); // seconds

// Aggregation: extract epoch
db.events.aggregate([
    {
        $project: {
            name: 1,
            epochMs: { $toLong: "$createdAt" }
        }
    }
]);

6. Epoch Time in APIs

Timestamps are a fundamental part of API design. The choice between epoch integers and ISO 8601 strings affects every consumer of your API. Here are the conventions and trade-offs.

REST API Conventions

There is no single standard, but two dominant patterns exist:

// Pattern 1: Epoch seconds (used by Stripe, Slack, Twitter/X)
{
    "id": "evt_123",
    "created": 1770681600,
    "amount": 2000
}

// Pattern 2: ISO 8601 strings (used by GitHub, Google, Microsoft)
{
    "id": "evt_123",
    "created_at": "2026-02-11T00:00:00Z",
    "amount": 2000
}

// Pattern 3: Epoch milliseconds (used by Elasticsearch, Discord)
{
    "id": "evt_123",
    "timestamp": 1770681600000,
    "content": "Hello"
}

The choice depends on your audience:

Whichever you choose, document it clearly and be consistent. Mixing formats within the same API is the fastest way to create confusion.

GraphQL Conventions

GraphQL does not have a built-in DateTime scalar. Most implementations define a custom scalar:

# Schema definition
scalar DateTime  # ISO 8601 string
scalar Timestamp # Epoch integer

type Event {
    id: ID!
    name: String!
    createdAt: DateTime!   # "2026-02-11T00:00:00Z"
    # or
    createdAt: Timestamp!  # 1770681600
}

# Common custom scalar implementation (JavaScript)
const { GraphQLScalarType } = require('graphql');

const DateTimeScalar = new GraphQLScalarType({
    name: 'DateTime',
    description: 'ISO 8601 date-time string',
    serialize(value) {
        return value instanceof Date ? value.toISOString() : value;
    },
    parseValue(value) {
        return new Date(value);
    }
});

The GraphQL community generally favors ISO 8601 strings because they are self-descriptive and work naturally with GraphQL's introspection system. Libraries like graphql-scalars provide pre-built DateTime, Timestamp, and DateTimeISO scalars.

API Best Practices for Timestamps

Debug JSON API Responses

Inspecting timestamps in API responses? Our JSON Formatter pretty-prints JSON so you can quickly spot and compare timestamp fields across objects.

7. Milliseconds vs Seconds: A Common Source of Bugs

The single most frequent epoch timestamp bug is confusing seconds and milliseconds. This mistake has caused outages at companies of every size, because the error is not always obvious: a millisecond epoch value treated as seconds produces a date millions of years in the future, while a seconds value treated as milliseconds produces a date in January 1970.

The Quick Test

Unit Digits (2020-2030) Example Value Languages That Default to This
Seconds 10 digits 1770681600 Python, PHP, Ruby, Go, C, Bash
Milliseconds 13 digits 1770681600000 JavaScript, Java, MongoDB
Microseconds 16 digits 1770681600000000 PostgreSQL (internal), Python time.time_ns()
Nanoseconds 19 digits 1770681600000000000 Go (UnixNano), Python time.time_ns()

Rule of thumb: Count the digits. 10 digits = seconds. 13 digits = milliseconds. This simple check can prevent hours of debugging.

Common Bug Scenarios

// BUG: JavaScript epoch (ms) sent to Python backend expecting seconds
// Frontend sends: 1770681600000 (ms)
// Backend interprets as seconds: year 58,104

// BUG: Python epoch (seconds) used in JavaScript Date constructor
const date = new Date(1770681600); // Treats as ms!
console.log(date); // 1970-01-21T11:51:21.600Z  -- WRONG

// CORRECT: Multiply by 1000
const date = new Date(1770681600 * 1000);
console.log(date); // 2026-02-11T00:00:00.000Z  -- RIGHT

// BUG: MongoDB date stored from Python timestamp
// Python: time.time() returns 1770681600.0 (seconds)
// MongoDB expects milliseconds for new Date()
db.events.insertOne({ ts: new Date(1770681600) });     // WRONG: Jan 21, 1970
db.events.insertOne({ ts: new Date(1770681600 * 1000) }); // RIGHT: Feb 11, 2026

Defensive Coding

// Utility function: auto-detect and normalize to milliseconds
function normalizeToMs(timestamp) {
    if (timestamp < 1e12) {
        // 10 digits or fewer: seconds
        return timestamp * 1000;
    } else if (timestamp < 1e15) {
        // 13 digits: already milliseconds
        return timestamp;
    } else if (timestamp < 1e18) {
        // 16 digits: microseconds
        return Math.floor(timestamp / 1000);
    } else {
        // 19 digits: nanoseconds
        return Math.floor(timestamp / 1e6);
    }
}

// Python equivalent
def normalize_to_seconds(timestamp):
    if timestamp < 1e12:
        return timestamp  # already seconds
    elif timestamp < 1e15:
        return timestamp / 1000  # milliseconds to seconds
    elif timestamp < 1e18:
        return timestamp / 1e6  # microseconds to seconds
    else:
        return timestamp / 1e9  # nanoseconds to seconds

Convert Between Number Bases

When debugging binary protocols, you may need to see a timestamp's binary or hexadecimal representation. Our Number Base Converter makes it easy to convert epoch values between decimal, hex, octal, and binary.

8. Time Zones and Epoch Time

One of epoch time's greatest strengths is that it is inherently UTC. The value 1770681600 represents the same absolute instant in time regardless of where you are on Earth. Time zone complexity only enters the picture when converting to or from human-readable formats.

The Golden Rule

Store and transmit timestamps as UTC epoch. Convert to local time only at the display layer. If you follow this single rule, you will avoid the vast majority of timezone bugs.

Common Timezone Pitfalls

Pitfall 1: Using local time for epoch conversion.

// WRONG: This uses the browser's local timezone
const epoch = new Date("2026-02-11 00:00:00").getTime() / 1000;
// Result depends on the user's timezone!

// RIGHT: Explicitly specify UTC
const epoch = new Date("2026-02-11T00:00:00Z").getTime() / 1000;
// Always returns the same value regardless of timezone

# Python WRONG: naive datetime uses local time
from datetime import datetime
epoch = datetime(2026, 2, 11).timestamp()  # Local timezone!

# Python RIGHT: explicit UTC
from datetime import datetime, timezone
epoch = datetime(2026, 2, 11, tzinfo=timezone.utc).timestamp()

Pitfall 2: Daylight Saving Time (DST) transitions.

# A time that doesn't exist (spring forward)
# In US Eastern, 2026-03-08 02:30:00 doesn't exist
# Clocks jump from 2:00 AM to 3:00 AM

# A time that exists twice (fall back)
# In US Eastern, 2026-11-01 01:30:00 exists twice
# Once in EDT (UTC-4) and once in EST (UTC-5)

# Using epoch avoids this entirely:
# There is no ambiguity in epoch time during DST transitions

Pitfall 3: Server timezone vs database timezone.

-- MySQL: Check your server's timezone setting
SELECT @@global.time_zone, @@session.time_zone;

-- If the server is in UTC but you insert without specifying timezone:
INSERT INTO events (name, created_at) VALUES ('test', NOW());
-- NOW() returns the server's timezone, which may differ from UTC

-- Always store in UTC:
INSERT INTO events (name, created_at) VALUES ('test', UTC_TIMESTAMP());

-- PostgreSQL: AT TIME ZONE conversion
SELECT created_at AT TIME ZONE 'America/New_York' FROM events;

Best Practices for Timezone Handling

9. Epoch Values Every Developer Should Know

Certain epoch values appear frequently in development, debugging, and interview questions. Having these committed to memory (or bookmarked) makes you faster at diagnosing timestamp issues.

Epoch Value Date Significance
0 Jan 1, 1970 00:00:00 UTC The Unix epoch. If you see this date in your data, a timestamp field was initialized to zero or left unset.
-1 Dec 31, 1969 23:59:59 UTC One second before epoch. Often used as a sentinel value meaning "not set" or "error".
-62167219200 Jan 1, 0001 00:00:00 UTC The "zero date" in some systems (Go's time.Time{} zero value).
946684800 Jan 1, 2000 00:00:00 UTC The Y2K boundary. Start of the 21st century. Useful reference point.
1000000000 Sep 9, 2001 01:46:40 UTC One billion seconds. A fun milestone. The "billennium."
1234567890 Feb 13, 2009 23:31:30 UTC The ascending-digit epoch. Celebrated by programmers worldwide.
2000000000 May 18, 2033 03:33:20 UTC Two billion seconds. Near the 32-bit limit.
2147483647 Jan 19, 2038 03:14:07 UTC Maximum signed 32-bit integer. The Y2K38 boundary.
4294967295 Feb 7, 2106 06:28:15 UTC Maximum unsigned 32-bit integer. Some systems use unsigned timestamps.
32503680000 Jan 1, 3000 00:00:00 UTC Sometimes used as a "far future" sentinel value.

Negative Epoch Values

Epoch timestamps before January 1, 1970, are represented as negative numbers. This is important for historical data:

// Dates before the epoch
const moonLanding = -14182940;  // July 20, 1969 20:17:40 UTC
const wwiiEnd = -769395600;     // Sep 2, 1945 (approximate)

// JavaScript handles negative epochs
const date = new Date(-14182940 * 1000);
console.log(date.toISOString()); // "1969-07-20T20:17:40.000Z"

# Python handles negative epochs
from datetime import datetime, timezone
dt = datetime.fromtimestamp(-14182940, tz=timezone.utc)
print(dt)  # 1969-07-20 20:17:40+00:00

Note that not all systems support negative epoch values. Some databases, APIs, and serialization formats treat timestamps as unsigned integers, making pre-1970 dates impossible to represent. Always test if your stack handles negative epochs before relying on them.

10. Debugging Timestamp Issues

Timestamp bugs are insidious because they can appear to work correctly in one timezone, one locale, or one time of year, and then break when any of those change. Here is a systematic approach to diagnosing and fixing them.

Step 1: Identify the Unit

The very first thing to determine is whether you are dealing with seconds, milliseconds, microseconds, or nanoseconds. Count the digits:

1770681600       // 10 digits = seconds
1770681600000    // 13 digits = milliseconds
1770681600000000 // 16 digits = microseconds

// Quick command-line check (Bash)
date -d @1770681600
# Wed Feb 11 00:00:00 UTC 2026

// If the date looks wrong (year 58104 or Jan 1970), you have a unit mismatch

Step 2: Check for Zero or Sentinel Values

// Common sentinel values that indicate bugs:
0         // Jan 1, 1970 — uninitialized timestamp
-1        // Dec 31, 1969 — error sentinel
NaN       // JavaScript: invalid date parsing
null      // Missing field
""        // Empty string parsed as date

Step 3: Verify Timezone Assumptions

// Is the timestamp UTC or local?
// Create a date you know (e.g., midnight UTC on a specific day)
// and verify the epoch value matches

// Expected: 2026-02-11 00:00:00 UTC = 1770681600
// If you get a different value, your code is applying a timezone offset

// JavaScript debug
const d = new Date("2026-02-11T00:00:00Z");
console.log(d.getTime() / 1000); // Should be 1770681600
console.log(d.getTimezoneOffset()); // Minutes offset from UTC

Step 4: Trace the Data Flow

When a timestamp is wrong, trace it from origin to display:

  1. Where was it created? Check the source system's timezone and epoch convention (seconds vs milliseconds).
  2. How was it stored? Check the database column type (INT, BIGINT, TIMESTAMP, DATETIME) and its timezone behavior.
  3. How was it transmitted? Check the API serialization format. Is it JSON (number or string)? Is the unit documented?
  4. How is it parsed? Check the client-side parsing code. Is it multiplying/dividing by 1000 correctly?
  5. How is it displayed? Check the timezone conversion at display time. Is the user's timezone applied correctly?

Step 5: Common Fixes

// Fix: Date shows as January 1970
// Cause: Seconds value used where milliseconds expected
const epoch = 1770681600;
const wrong = new Date(epoch);      // Jan 21, 1970
const right = new Date(epoch * 1000); // Feb 11, 2026

// Fix: Date shows as year 58104
// Cause: Milliseconds value used where seconds expected
const epochMs = 1770681600000;
const wrong = new Date(epochMs * 1000); // Year 58104
const right = new Date(epochMs);        // Feb 11, 2026

// Fix: Date is off by a few hours
// Cause: Local timezone applied instead of UTC
const d = new Date(1770681600 * 1000);
const wrong = d.toString();           // Local timezone
const right = d.toISOString();        // UTC

// Fix: Date is off by exactly 1 hour
// Cause: DST transition not accounted for
// Solution: Always store and compare in UTC

// Fix: Date is January 1, 0001 or similar "zero date"
// Cause: Uninitialized date object (Go, .NET)
// Solution: Check for zero values before processing

Debugging Tools

Timestamp Testing Checklist

Add these test cases to your suite to catch timestamp bugs before they reach production:

Conclusion

Unix epoch timestamps are one of those concepts that seem trivially simple but have surprising depth. The core idea, counting seconds from January 1, 1970, is elegant and powerful. But the real-world complexities of time zones, unit confusion (seconds vs. milliseconds), 32-bit overflow, DST transitions, and database idiosyncrasies mean that timestamp handling requires careful attention.

The key takeaways from this guide:

Epoch time has endured for over 50 years because it solves a hard problem with elegant simplicity. Understand its nuances, respect its quirks, and it will serve you well in every system you build.

Start Converting Timestamps

Put your knowledge into practice with our Epoch Converter. Convert between Unix timestamps and human-readable dates instantly, right in your browser. Supports seconds, milliseconds, and custom formats.