Skip to content
$ epochly

Unix Timestamp to Date

Paste the raw number out of your log, database column or API response. The converter counts its digits, picks the unit, and prints the instant four ways: ISO 8601 in UTC, RFC 2822, your own time zone by name, and how long ago it was. No division needed first — a 13-digit value works exactly like a 10-digit one.

~/epochly — decode··········
unit

detected: seconds (s)
10 digits — 1 to 11 digits is read as seconds, because 11 digits of seconds already reaches the year 5138 and nothing longer would still be a second count.

UTC · ISO 86012023-11-14T22:13:20.000Z
UTC · RFC 2822Tue, 14 Nov 2023 22:13:20 +0000
UTC · readableTuesday, 14 November 2023, 22:13:20 UTC
Local timeresolving your time zone…
Local · ISOresolving your time zone…
Relativestarting clock…Months and years are approximated as 30 and 365 days, so this is a distance, not a measurement.
Epoch · seconds1700000000
Epoch · milliseconds1700000000000
try

What decoding actually involves

Turning a timestamp into a date is two steps, and they fail in different ways. Step one is scaling: deciding whether the integer is seconds, milliseconds, microseconds or nanoseconds and converting it to a single common unit. Step two is calendar arithmetic: taking that count and walking it out into a year, month, day and time, honouring the Gregorian leap-year rule and the fact that a Unix day is always exactly 86,400 seconds long.

Step two is boring and reliable — every date library on earth agrees on it. Step one is where the bugs live, because the raw number carries no unit and nothing will warn you. A wrong scaling factor does not produce an error; it produces a perfectly valid date on the wrong side of a century. That is why this page leads with the detection verdict rather than hiding it.

Every digit length, and the window it covers

This is the whole basis for auto-detection, computed rather than asserted. For each value length, the table below shows the unit Epochly assigns and the exact calendar range that length can express in that unit. Read down the "covers" column and the pattern is unmistakable: 10-digit seconds, 13-digit milliseconds and 16-digit microseconds all describe the same span of history, 2001-09-09 to 2286-11-20. They are the same window written at three resolutions, which is precisely why counting digits identifies the unit.

Derived at build time: for each length N, the range 10N−1 to 10N−1 is converted using the unit auto-detection assigns to N. Arithmetic runs through BigInt, so the 16- and 17-digit rows are exact rather than rounded to a float.
Digits Detected as Value range Covers (UTC)
1 seconds (s) 0
9
1970-01-01T00:00:00.000Z
1970-01-01T00:00:09.000Z
2 seconds (s) 10
99
1970-01-01T00:00:10.000Z
1970-01-01T00:01:39.000Z
3 seconds (s) 100
999
1970-01-01T00:01:40.000Z
1970-01-01T00:16:39.000Z
4 seconds (s) 1000
9999
1970-01-01T00:16:40.000Z
1970-01-01T02:46:39.000Z
5 seconds (s) 10000
99999
1970-01-01T02:46:40.000Z
1970-01-02T03:46:39.000Z
6 seconds (s) 100000
999999
1970-01-02T03:46:40.000Z
1970-01-12T13:46:39.000Z
7 seconds (s) 1000000
9999999
1970-01-12T13:46:40.000Z
1970-04-26T17:46:39.000Z
8 seconds (s) 10000000
99999999
1970-04-26T17:46:40.000Z
1973-03-03T09:46:39.000Z
9 seconds (s) 100000000
999999999
1973-03-03T09:46:40.000Z
2001-09-09T01:46:39.000Z
10 seconds (s) 1000000000
9999999999
2001-09-09T01:46:40.000Z
2286-11-20T17:46:39.000Z
11 seconds (s) 10000000000
99999999999
2286-11-20T17:46:40.000Z
5138-11-16T09:46:39.000Z
12 milliseconds (ms) 100000000000
999999999999
1973-03-03T09:46:40.000Z
2001-09-09T01:46:39.999Z
13 milliseconds (ms) 1000000000000
9999999999999
2001-09-09T01:46:40.000Z
2286-11-20T17:46:39.999Z
14 milliseconds (ms) 10000000000000
99999999999999
2286-11-20T17:46:40.000Z
5138-11-16T09:46:39.999Z
15 microseconds (µs) 100000000000000
999999999999999
1973-03-03T09:46:40.000Z
2001-09-09T01:46:39.999Z
16 microseconds (µs) 1000000000000000
9999999999999999
2001-09-09T01:46:40.000Z
2286-11-20T17:46:39.999Z
17 microseconds (µs) 10000000000000000
99999999999999999
2286-11-20T17:46:40.000Z
5138-11-16T09:46:39.999Z

Two rows are worth pausing on. Eleven digits still reads as seconds and reaches the year 5138 — that is the headroom which makes the seconds window safe to extend past 10. And twelve digits flips to milliseconds even though twelve digits of seconds is arithmetically valid, because twelve digits of seconds would be the year 33,658 and no real system emits that. Detection is a bet on which reading is plausible, and the table is the evidence behind the bet. Force the unit with the s / ms / µs / ns buttons whenever you know better.

The four output formats, and when each one is the right answer

  • ISO 8601 UTC2023-11-14T22:13:20.000Z. Sorts lexicographically in the same order it sorts chronologically, which makes it the correct choice for filenames, log lines and anything that will be compared as a string. The trailing Z is not decoration; it is what makes the string unambiguous.
  • RFC 2822Tue, 14 Nov 2023 22:13:20 +0000. The email format, and what an SMTP Date: header holds. Note the numeric +0000 offset: the superficially similar HTTP date header uses the literal word GMT instead, so the two are not interchangeable despite looking alike.
  • Local time with the zone named — the line to quote when you are talking to a person rather than a machine. Epochly prints the IANA zone identifier your browser resolved (something like Europe/Berlin) rather than an abbreviation, because abbreviations are ambiguous: CST alone is three different offsets depending on the continent.
  • Relative — "3 hours ago". Best for triage, worst for records. Months and years in the relative label use 30-day and 365-day approximations, so treat it as a distance rather than a measurement.

Worked example: the row nobody could date

A support ticket quotes an updated_at of 1700000000 and asks why the record "changed in the future". Decode it: ten digits, so seconds, so 2023-11-14T22:13:20.000Z. Nothing is in the future; the reporter was reading the number as milliseconds in their head, which would put it 19 days after the epoch. Two people, one number, two units — and the dashboard that rendered it as 1970 was making exactly the same mistake in code.

The general procedure when a date looks wrong: paste the raw stored value here before touching the code. If Epochly's date is the one you expected, the storage is fine and the renderer is broken. If Epochly's date is also wrong, the value was written wrong and no amount of display fixing will help. That one test splits the problem in half in about five seconds.

Decoding one value without leaving your terminal

For a single value on a machine you already have open, three one-liners cover almost everything. GNU date: date -u -d @1700000000. Python: python3 -c "import datetime,sys;print(datetime.datetime.fromtimestamp(int(sys.argv[1]),datetime.timezone.utc))" 1700000000. Node: node -e "console.log(new Date(1700000000*1000).toISOString())". All three expect seconds, so a 13-digit value needs dividing first — which is the entire reason the digit rule is worth memorising. The full ten-language reference lives on the main converter page.

Related conversions

  • Date to Unix timestamp — the opposite direction, including how the same wall clock maps to eleven different epoch values across eleven zones.
  • Milliseconds to date — the 13-digit case on its own, with both readings of five real literals printed side by side.
  • Current Unix timestamp — the live count and the milestone table, including the 2038 ceiling.
  • Discord timestamp generator — turn a decoded instant into a tag that renders in each reader's own zone.

Timestamp to date FAQ

01 What date is Unix timestamp 1700000000?
It is 2023-11-14T22:13:20.000Z in UTC — Tuesday, 14 November 2023, 22:13:20 UTC. Ten digits, so it is read as seconds. In RFC 2822 form that is Tue, 14 Nov 2023 22:13:20 +0000. Your own local rendering depends on your zone: it is 15 November in Tokyo and still 14 November in Los Angeles, which is why the tool prints both lines.
02 My timestamp has 13 digits. Do I need to divide by 1000 first?
Not here — paste it as it is. Thirteen digits falls in the millisecond window, the converter says so in the detection line, and the date it prints is already correct. You only need the division when you are handing the value to something else that expects seconds, such as date -d @… or Python's fromtimestamp. The milliseconds to date page covers that direction in detail.
03 Why do I get a different date than another converter?
Almost always a time-zone difference rather than a maths difference. Many converters print only local time and do not say which zone they used; if that zone is not yours, the calendar date can be a day out either side of midnight. Epochly prints the UTC line and the local line together, with the resolved IANA zone name spelled out, so there is nothing to guess. If the two tools disagree by hours rather than a day, check whether one of them read the value in the other unit.
04 What does a five- or six-digit year mean, like +055840?
That is ISO 8601's expanded-year notation and it is a reliable symptom of one specific bug: a millisecond value was read as seconds. The leading + is required by the standard for years outside 0000–9999. If you see it, multiply nothing — divide by 1000, or better, paste the raw value here and let the digit count choose.
05 Can it decode a timestamp from before 1970?
Yes. Negative values count backwards from the epoch, so -1 decodes to 1969-12-31T23:59:59Z and -315619200 to 1960-01-01T00:00:00Z. Use the minus sign; the digit rule ignores it and counts only the digits. Note that some languages cannot express these — Rust's unsigned Duration is the common example.
06 Does the decoded date account for daylight saving?
The UTC line never changes, because UTC has no daylight saving. The local line does: the browser resolves your IANA zone and applies whichever rule was in force at that instant, not the one in force today. Decode a timestamp from last July and a timestamp from last January and you will see the two local offsets differ, which is the correct behaviour and the reason a fixed "UTC+2" offset should never be stored in place of a zone name.

Related tools