
Timestamp / Epoch Converter
Convert between Unix timestamps and human-readable dates. Live epoch clock included. 100% client-side.
Last reviewed: May 2026New to this tool? Click here for instructions
Unix Timestamp → Human Date
Human Date → Unix Timestamp
Paste any Unix epoch integer and get the corresponding instant rendered in your local timezone, UTC, ISO 8601, and human-readable relative time — entirely in your browser. The reverse direction works the same way: pick a date and time in the calendar input and read off the epoch value in seconds or milliseconds.
What This Tool Does
A Unix timestamp (sometimes called POSIX time, epoch time, or Unix time) is the number of seconds that have elapsed since 1970-01-01T00:00:00Z, ignoring leap seconds. It is the lingua franca for time on the open web: every HTTP Date header originates from one, every database TIMESTAMPTZ column stores one internally, every JWT iat and exp claim is one, and every Kubernetes pod log line is annotated with one. This tool converts between the integer form and the human-readable forms — both directions, both common precisions (seconds and milliseconds), both common reference frames (UTC and local).
The audience is anyone whose work involves machine-generated time data. Backend engineers reading server logs. SRE staff correlating events across distributed traces. Embedded engineers decoding sensor packets where the timestamp field is a single uint32. Security analysts pulling AWS CloudTrail records or correlating Sigma rules across a SIEM. Mobile developers debugging push notification delivery windows. Anyone, in short, who has ever stared at 1735689600 in a log line and wondered whether that's last week, last decade, or two minutes from now.
Outputs include the Unix integer (in seconds or milliseconds, your choice), the full UTC string in RFC 7231 format (used by HTTP headers), the local-timezone rendering via Date.prototype.toLocaleString, the ISO 8601 / RFC 3339 form (used by JSON APIs and Postgres), and a human-friendly relative-time phrase ("3 hours ago", "in 4 days"). Conversions happen entirely in JavaScript on your machine. No timestamp you enter is uploaded, logged, or seen by any server. The same code runs identically offline — useful if you're auditing logs on an air-gapped engineering laptop.
How to Use It
The tool offers two conversion directions and one always-visible live clock. The clock at the top of the page ticks once per second showing the current Unix timestamp in seconds — useful for quickly grabbing "now" without opening a terminal or running date +%s.
- Pick your unit. The default is Seconds (10-digit timestamps like
1735689600). Toggle to Milliseconds (13-digit timestamps like1735689600000) if your source data is from JavaScriptDate.now(), JavaSystem.currentTimeMillis(), or a logging system that records ms precision. The unit choice applies to both input parsing and output formatting. - For epoch → date: paste your timestamp into the upper input field and click Convert (or hit Enter). The output panel shows five renderings: the input in your selected unit, a UTC string, your local time, ISO 8601, and a relative phrase. Click Now to populate the field with the current epoch value and immediately see all five forms — handy when you need to quickly grab a timestamp for a test fixture.
- For date → epoch: use the calendar-and-clock picker in the lower input. Browsers render this as a native
<input type="datetime-local">, so the picker style matches the rest of your OS. The value you choose is interpreted as your local timezone; the converter then computes the corresponding UTC instant and shows it in all five forms. - Copy the results. The Copy Results button writes a tab-delimited five-line block to your clipboard ("Unix: 1735689600", "UTC: Wed, 01 Jan 2025 00:00:00 GMT", etc.) suitable for pasting into a ticket comment, a chat message, or a code comment.
The status bar at the bottom of the tool card shows pass/fail state for the most recent input. Invalid input (non-numeric for the timestamp field, malformed date for the calendar) produces a red Invalid state with an explanation; a valid conversion shows the parsed UTC string for verification at a glance.
Worked Example: Decoding 1735689600
Suppose you find this exact integer in an AWS CloudTrail event under the eventTime field, or in a Postgres query result that returned EXTRACT(EPOCH FROM created_at). The number is 10 digits, so it's almost certainly seconds (a 13-digit value would be milliseconds). Pasting 1735689600 into the Seconds field produces:
- Unix (seconds): 1735689600
- UTC: Wed, 01 Jan 2025 00:00:00 GMT
- Local (US Eastern, UTC-5): 12/31/2024, 7:00:00 PM
- ISO 8601: 2025-01-01T00:00:00.000Z
- Relative: some-number-of months ago (depends on when you read this)
Two things worth noting from that output. First: the local rendering is not January 1 if your timezone is west of UTC. The integer encodes an absolute instant; what wall clock reads at that instant depends entirely on where you are standing. A timestamp arriving from a Tokyo server and rendered in Tokyo will display the local Tokyo time; the same integer rendered in San Francisco will display a moment on the previous calendar day. This is the source of countless "off-by-one-day" reporting bugs in dashboards that join UTC server data against locally-rendered date pickers without applying an explicit offset.
Second: if you mistakenly fed 1735689600000 (13 digits, the milliseconds form) into the Seconds field instead, the result would be Friday, 12 December 56,948 — a year so far in the future that the bug is immediately obvious. But the reverse mistake is more pernicious: feeding the 10-digit seconds value 1735689600 into a system that expects milliseconds yields 1970-01-21T02:08:09Z — only 20 days past the epoch. A timestamp landing in early 1970 is the classic fingerprint of an unintentional ms-vs-s confusion, and it's caught by sanity-checking the year in your log output before trusting the value.
One more practical thing: 1735689600 is exactly midnight UTC on a calendar boundary. Boundary timestamps like this — 1577836800 for 2020-01-01, 1893456000 for 2030-01-01, 0 for the epoch itself — are useful mental anchors. Building a small set of memorized landmarks ("this number is later than 1577836800, so it's after 2020") lets you reason about timestamp magnitudes without firing up the converter every time.
Common Use Cases
Unix timestamps show up everywhere in software infrastructure. The seven scenarios below are the ones that drive most converter usage in practice.
Debugging server log timestamps
Application logs (nginx access logs, syslog, structured JSON logs from winston / pino / zap) very often emit timestamps as raw epoch integers or as ISO 8601 strings with an offset. When chasing a 3 AM incident, you'll routinely need to translate 1715655234 into "Sunday morning at 02:53 EDT" so you can correlate it with the on-call paging timestamp. Paste-convert-read is the workflow; the tool is optimized for that single-shot pattern.
AWS CloudTrail and Cloud event analysis
CloudTrail's eventTime field is ISO 8601 UTC ("2024-12-15T14:23:45Z"), but the related requestParameters often contain Unix epoch values that came from caller code. The same is true of GCP Cloud Audit Logs, Azure Activity Logs, and most SaaS audit feeds. Converting between the two forms is necessary to confirm that a CloudTrail event and an application-emitted log line refer to the same moment.
JWT iat, exp, and nbf claims
JSON Web Tokens (RFC 7519) carry timestamps for "issued at" (iat), "expires at" (exp), and "not before" (nbf) — all expressed as numeric date values per RFC 7519 §2: the number of seconds since the epoch. Decoded JWTs typically display these as raw integers; converting them back to human time is the difference between "this token is valid for another two hours" and "this token expired three weeks ago." Pair this tool with the JWT Decoder to chase auth bugs end-to-end.
Database timestamp columns
Some schemas store created_at and updated_at as BIGINT epoch milliseconds rather than native TIMESTAMP types — common in event-sourced architectures, time-series databases (InfluxDB, TimescaleDB), and high-volume telemetry tables where the 8-byte integer storage is meaningfully cheaper than a TIMESTAMP record. When inspecting raw rows via psql or DBeaver, you'll see the integer; the converter turns it into a date you can reason about.
Unix find -newer and shell scripting
Unix tools like find -newer, touch -d @1700000000, stat -c %Y file, and most rsync filters work in epoch seconds. Calculating "everything modified in the last 24 hours" via find . -newermt @$(($(date +%s) - 86400)) is fluent once you can read epoch integers at a glance — and writing one-off ad-hoc cron expressions becomes much easier when you can sanity-check the resulting "next run" time.
Embedded sensor data and binary protocols
IoT devices, GPS modules, automotive ECUs, and most industrial protocols pack timestamps into a single 32-bit or 64-bit field for bandwidth efficiency. Decoding a CAN bus frame, an MQTT payload, or a Wireshark capture often means staring at raw uint32 values and asking "what date is that?" Embedded engineers also need to know the Y2038 cutoff cold (see below), because legacy firmware can carry signed 32-bit time_t into production for another decade.
Cache TTLs, scheduled jobs, and idempotency windows
Redis EXPIREAT, Sidekiq's scheduled jobs, Cloudflare Workers Cron Triggers, AWS EventBridge schedules — anything that schedules work for "do this at instant T" — accepts or returns an epoch value. Verifying that the value you stored matches the wall-clock time you intended often catches off-by-one-hour DST bugs before they go to production.
Edge Cases and Gotchas
The Unix timestamp abstraction is mostly clean, but a handful of edge cases bite hard when they bite.
The Year 2038 problem
POSIX time_t on 32-bit Unix systems is a signed 32-bit integer. Its maximum value is 231−1 = 2147483647, which corresponds to 2038-01-19T03:14:07Z. The next second wraps to −2147483648, which renders as 1901-12-13T20:45:52Z. This is the Y2038 problem (sometimes called the Unix Y2K). Modern desktops, servers, and phones moved to 64-bit time_t long ago. The lingering exposure is in embedded firmware, legacy industrial control systems, smart-meter ICs, and database columns explicitly typed as 32-bit INT instead of BIGINT or native TIMESTAMP. Some Linux distributions (Debian, Ubuntu) shipped 64-bit time_t for 32-bit ARM only with glibc 2.32 in 2020 — anything frozen before that may still be vulnerable.
Leap seconds and POSIX time's deliberate fiction
POSIX time pretends every UTC day is exactly 86400 seconds long. In reality, the International Earth Rotation Service (IERS) has periodically inserted positive leap seconds to keep UTC aligned with astronomical time — 27 of them between 1972 and 2017, most recently 2016-12-31T23:59:60Z. Unix timestamps simply don't model these. A POSIX-conformant clock will either repeat the same timestamp for two real seconds (the "stop-the-clock" approach) or smear the leap across many hours (Google's leap smear stretches each second around the leap by roughly 14 parts per million for ~20 hours). The IERS announced in 2022 that no leap seconds will be added before at least 2035, deferring this problem rather than solving it.
Negative timestamps and pre-1970 dates
Signed time_t and JavaScript's Date both accept negative values, so −86400 represents 1969-12-31T00:00:00Z. JavaScript's Date supports the range ±8.64×1015 ms from the epoch, which spans roughly years −271821 through 275760. Python datetime handles dates from year 1 onward natively. SQL Server's DATETIME, however, only goes back to 1753 (the year the British Empire adopted the Gregorian calendar); MySQL's DATETIME starts at 1000-01-01. For dates further back, store the ISO 8601 string or use a calendar-aware library — never assume your timestamp type covers BCE dates.
Milliseconds vs. seconds confusion
The single most common timestamp bug. JavaScript and Java natively work in milliseconds; C, Python, Go, and most databases work in seconds. The magnitudes differ by 1000×, so a unit mismatch shifts your computed date by roughly 32 years. The 10-digit-vs-13-digit heuristic (covered in the FAQ below) is your sanity check: a 10-digit "timestamp" interpreted as ms lands in the 1970s; a 13-digit value interpreted as seconds lands far in the future.
JavaScript Date object pitfalls
new Date("2025-01-01") is parsed as UTC midnight. new Date("2025-01-01T00:00:00") (no Z, no offset) is parsed as local midnight. This subtle difference, documented in ECMAScript §21.4.3.2, has caused enough production incidents to warrant its own MDN warning. Always include the Z suffix for UTC or an explicit ±HH:MM offset when constructing Date from a string. Library alternatives — date-fns, luxon, Temporal (Stage 3 proposal) — sidestep this by being explicit about timezones in every API.
Timezone-naive vs. timezone-aware datetimes
Python's datetime objects are "naive" by default — they carry no timezone information. datetime.now() returns a naive local-time datetime; datetime.utcnow() returns naive UTC. Mixing the two without explicit conversion via .astimezone(timezone.utc) produces silent off-by-N-hours bugs. PEP 495, PEP 615 (zoneinfo), and the broader move toward aware datetimes in modern Python (3.6+) have helped — but legacy code routinely fails on DST transitions. The corresponding Go discipline (always use time.Time with explicit Location) is generally cleaner.
Behind the Scenes: How Timestamps Work
The Unix timestamp predates the Unix specification itself. POSIX.1-2017 defines Seconds Since the Epoch as "a value to be interpreted as the number of seconds between a specified time and the Epoch" where the Epoch is 1970-01-01T00:00:00Z. The same standard documents the time(2) system call, the struct timespec with tv_sec (whole seconds) and tv_nsec (nanoseconds remainder), and the family of conversion functions (gmtime, localtime, mktime, strftime) that turn between integer time and broken-down calendar representations. Every modern language's time library is a wrapper around or reimplementation of this surface.
Wall clock vs. monotonic clock
What we usually call "the system time" is the wall clock — what a person reading a clock face would see. It is subject to NTP adjustments, manual changes by the operator, DST transitions, and leap-second handling. For measuring elapsed time inside a program, you almost never want the wall clock. You want the monotonic clock — a counter that only ever moves forward, immune to NTP adjustments, and not tied to any real-world calendar. Linux exposes both via clock_gettime(CLOCK_REALTIME, ...) and clock_gettime(CLOCK_MONOTONIC, ...). Go's time.Since uses monotonic by default; Java's System.nanoTime() is monotonic; JavaScript's performance.now() is monotonic. Mixing the two (subtracting one wall-clock timestamp from another to measure duration) produces nonsense values whenever NTP steps the clock.
How the kernel keeps time
At boot, the kernel reads the hardware RTC (real-time clock — a battery-backed clock chip on the motherboard) to initialize wall time. Thereafter, a periodic timer interrupt (the "tick") increments an internal counter, and the system synchronizes against external NTP servers to correct drift. NTP refines the clock continuously via small frequency adjustments rather than abrupt steps. Linux's chrony daemon is more aggressive at adjusting drift than legacy ntpd; both implement the same RFC 5905 protocol. Clock skew — the difference between two systems' clocks — typically stays under 10 ms inside a well-NTP'd datacenter and under 100 ms across the internet. For tighter sync, Precision Time Protocol (PTP, IEEE 1588) achieves sub-microsecond accuracy on dedicated hardware.
Why we use UTC internally
UTC has no daylight saving transitions, no political timezone changes, and no calendar irregularities to model. Storing all timestamps in UTC at the storage layer and converting to local time only at the display layer is the universally-recommended pattern (Joel Spolsky's 2008 essay, every SQL textbook published since 2010, every major company's internal engineering style guide). The corresponding anti-pattern is storing local time without offset — that data is ambiguous through DST transitions (the same wall-clock hour occurs twice when clocks fall back, and never at all when they spring forward) and impossible to compare across regions. If your database column type is "datetime without timezone" or "naive datetime," that's a latent bug.
Comparison: This Tool vs. date vs. Date() vs. Python datetime
Different environments expose different surfaces for the same fundamental operation. The table below compares the four most common conversion paths a developer reaches for during a debugging session.
| Capability | This Browser Tool | Unix date command |
JavaScript Date() |
Python datetime |
|---|---|---|---|---|
| Epoch → human (seconds) | Paste, click Convert | date -u -d @1735689600 |
new Date(1735689600 * 1000) |
datetime.fromtimestamp(1735689600, tz=UTC) |
| Epoch → human (milliseconds) | Toggle "Milliseconds" chip | date -u -d @$((1735689600000 / 1000)) |
new Date(1735689600000) |
datetime.fromtimestamp(1735689600000 / 1000) |
| Current epoch ("now") | Visible live clock at top | date +%s |
Math.floor(Date.now() / 1000) |
int(time.time()) |
| ISO 8601 output | Always shown | date -u --iso-8601=seconds |
d.toISOString() |
d.isoformat() |
| Relative time ("3 hours ago") | Always shown | Not built in | Requires library (date-fns, luxon) | Requires library (humanize, arrow) |
| Handles negative timestamps | Yes | Yes | Yes (to year −271821) | Yes (to year 1) |
| Offline / no install | Yes (after first load) | Yes | Requires Node or browser | Requires Python install |
date behavior varies between GNU coreutils (Linux) and BSD (macOS). BSD date doesn't accept the @ prefix — use date -u -r 1735689600 instead. The browser tool sidesteps these differences entirely.Each environment has its niche. date at the command line is fastest when you're already in a terminal and need a one-shot conversion. Date() in a browser DevTools console is fastest when you're already debugging frontend code. Python's datetime wins when you need to do bulk processing across thousands of timestamps with consistent timezone handling. The browser tool here wins when you have a screen full of log lines and need to translate a half-dozen timestamps without context-switching to a terminal, when you're on a machine where you can't run arbitrary commands, or when you simply want all five output forms — UTC, local, ISO 8601, milliseconds, relative — visible side by side without typing five different incantations.
Frequently Asked Questions
What's the difference between Unix epoch seconds and milliseconds?
Unix epoch seconds count whole seconds since 1970-01-01T00:00:00Z and are typically a 10-digit integer (1735689600 represents 2025-01-01). Milliseconds count thousandths of a second since the same instant and are typically 13 digits (1735689600000 for the same moment). Java's System.currentTimeMillis() and JavaScript's Date.now() return milliseconds; the C library time(2), POSIX struct timespec.tv_sec, AWS CloudTrail eventTime, Linux file mtime, and most database TIMESTAMP columns store seconds. A common bug is mixing units: dividing by 1000 when the source was already seconds throws you to the year 2025-01-21 (50.5 years post-epoch) — or interpreting seconds as milliseconds drops you to 1970-01-21 (20 days post-epoch).
Why does my timestamp show the wrong date?
Nine times out of ten this is a unit mismatch (seconds vs milliseconds, see above) or a timezone misinterpretation. Unix timestamps are always UTC by definition — they encode an absolute instant in time with no timezone component. When you render that instant as a string, the displaying system applies its local timezone offset. JavaScript's new Date(1735689600000).toString() returns local time; .toUTCString() and .toISOString() return UTC. If your server stores UTC timestamps but your dashboard renders them in browser-local time without an explicit offset display, two users in different timezones will see different wall-clock times for the same event. Always show the offset (e.g. "2025-01-01 00:00 UTC") rather than just the wall-clock string.
What happens when 32-bit Unix time overflows in 2038?
At 2038-01-19T03:14:07Z, the signed 32-bit integer that holds POSIX time_t on legacy systems reaches its maximum value of 2147483647 (231−1). The next second wraps to −2147483648, which is interpreted as 1901-12-13T20:45:52Z. This is the Y2038 problem. Modern Linux (since glibc 2.32 and kernel 5.6) widened time_t to 64 bits even on 32-bit ARM. macOS, Windows FILETIME, Java long, JavaScript Date (uses double-precision float with ms precision), Python datetime, and Go time.Time are all safe. The lingering risk sits in embedded firmware, older industrial control systems, and database columns explicitly typed as INT instead of BIGINT or TIMESTAMP. Audit your schema before 2038 — a single 32-bit timestamp column is enough to cause production data corruption.
How do I store timestamps in a database — as integer or datetime?
For event timestamps where you need range queries, ordering, and timezone-correct rendering, use the native temporal type: TIMESTAMPTZ in PostgreSQL, TIMESTAMP in MySQL 8+, DATETIMEOFFSET in SQL Server. These preserve UTC internally and let the client render in any timezone via SET TIMEZONE. Use BIGINT epoch milliseconds only when you need sub-second precision the native type can't give you, when interoperating with systems that already speak epoch (Kafka, time-series databases like InfluxDB or TimescaleDB), or when storing high-volume telemetry where the 8-byte integer is meaningfully smaller than the 12-byte timestamp record. Avoid storing local time without an offset — that data is ambiguous across DST transitions and impossible to compare across regions.
Can negative Unix timestamps represent dates before 1970?
Yes, in any language whose time type is a signed integer or float. -86400 represents 1969-12-31T00:00:00Z (one day before the epoch). -2208988800 is approximately 1900-01-01. JavaScript Date handles negative values back to roughly the year −271821 (the minimum date is new Date(-8640000000000000)). POSIX strftime, GNU date, and Python datetime all accept negative epoch values, though some Windows APIs and SQL Server's DATETIME (range 1753-9999) do not. For historical dates outside the supported range, store the value as an ISO 8601 string (e.g. -0044-03-15 for the Ides of March, 44 BCE in proleptic Gregorian calendar) and parse with a library that supports BCE dates.
Why is JavaScript's Date.now() in milliseconds but Python time.time() in seconds?
Historical accident. JavaScript inherited the design of Java's java.util.Date, which standardized on milliseconds when it was added to Java 1.0 in 1996 — millisecond resolution was already enough for UI event timing and there was no need for finer granularity. Python's time module follows the older POSIX time(2) convention from 1970s Unix, which returned whole seconds as time_t. Modern Python time.time() returns a float with microsecond precision under the hood, but the integer part is still seconds since the epoch. The practical takeaway: when a value crosses the JS/Python boundary, you almost always need to multiply by 1000 or divide by 1000 — and the timestamp's magnitude is your sanity check (10 digits = seconds, 13 digits = ms).
How do leap seconds affect Unix timestamps?
They don't, by deliberate design — and that's also their biggest source of confusion. POSIX time pretends every UTC day is exactly 86400 seconds long. The IERS has inserted 27 positive leap seconds between 1972 and 2017 (most recently at 2016-12-31T23:59:60Z), but POSIX clocks step over them: the timestamp during a leap second is typically repeated (the same value appears for two real seconds) or smeared (Google's leap smear stretches each second around the leap by ~14ppm for ~20 hours). The IERS announced in 2022 that no new leap seconds will be added through at least 2035. For most application code this doesn't matter; for timing-sensitive systems (high-frequency trading, GPS-derived clocks, NTP server design) it matters a great deal.
What's the largest Unix timestamp I can use?
It depends on the storage type. Signed 32-bit (legacy time_t on 32-bit Unix): max 2147483647 = 2038-01-19T03:14:07Z (the Y2038 limit). Unsigned 32-bit: max 4294967295 = 2106-02-07T06:28:15Z. Signed 64-bit (modern time_t, Go int64): max 9223372036854775807 seconds ≈ year 292 billion — more than 20 times the age of the universe. JavaScript Date (double-precision float, ms): safe range ±8.64×1015 ms = ±100 million days from epoch = years −271821 to 275760 (Date.prototype.toISOString throws RangeError outside this). For practical engineering work, BIGINT (64-bit) seconds or milliseconds is overkill in the best sense — you will never need more.