codr.tools
Tools

Timestamp / Unix epoch converter

Convert a Unix timestamp to a date and back, with the unit detected from the number, any IANA time zone, ISO 8601, RFC 2822, relative time and ISO week, plus the current epoch ticking live.

Current Unix time

—

Starting the clock…

Paste a number or a date. Both directions work from this one field.

Read numbers as
Unit for a numeric timestamp

Auto picks the unit from the size of the number: 10 digits is seconds, 13 is milliseconds, 16 microseconds, 19 nanoseconds.

Convert a Unix timestamp to a date and back, with the unit detected from the number, any IANA time zone, ISO 8601, RFC 2822, relative time and ISO week, plus the current epoch ticking live.

How to use this tool#

  1. Paste anything into the one field. A number is read as a Unix timestamp, and anything else is read as a date. There is no direction to choose. The field starts on the current time, so the page is useful the moment it loads.
  2. Check the line under the field. It says how your input was read, for example "Read as milliseconds, detected from the size of the number". If the guess is wrong, set the unit yourself with the s / ms / µs / ns buttons.
  3. Pick a time zone. It defaults to your browser's. It changes the date rows and the offset, never the Unix numbers, because a Unix timestamp carries no zone.
  4. Copy the row you need. Every result has its own copy button. The current Unix time at the top has one too, in seconds and in milliseconds, and "Use now" drops it into the field.

What a Unix timestamp is#

A Unix timestamp is a count of seconds since 1970-01-01T00:00:00Z, the moment called the Epoch. It is a single number with no time zone, no calendar and no formatting: two machines on opposite sides of the world holding 1700000000 are holding the same instant.

POSIX (IEEE Std 1003.1-2024, Base Definitions section 4.19, "Seconds Since the Epoch") defines it as "A value that approximates the number of seconds that have elapsed since the Epoch", and then gives the exact expression that turns a broken-down UTC time into that value:

tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
    (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
    ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400

Read the last three terms and you have the leap year rules. What you will not find anywhere in the expression is leap seconds. POSIX accounts for every day with exactly 86400 seconds, so the leap seconds inserted into UTC since 1972 are not counted. That is why "approximates" is in the definition: Unix time is not the number of SI seconds that have physically elapsed since 1970, it is the number of days since 1970 times 86400 plus the seconds into the day.

Seconds, milliseconds, microseconds, nanoseconds#

The epoch is fixed, the unit is not. The quickest way to tell them apart is to count the digits: for a date near today, seconds take 10, milliseconds 13, microseconds 16 and nanoseconds 19. This tool counts them for you and says which one it picked.

Unix timestamp units and the systems that use them
UnitNowWhere you meet it
Seconds
10 digits today
1700000000POSIX time(), Go Unix(), PHP time(), Python time.time() (as a float), most SQL epoch columns.
Milliseconds
13 digits today
1700000000000JavaScript Date.now(), .NET ToUnixTimeMilliseconds(), most log pipelines and message brokers.
Microseconds
16 digits today
1700000000000000PostgreSQL's timestamp resolution, Python datetime objects, C struct timeval, many tracing spans.
Nanoseconds
19 digits today
1700000000000000000Go UnixNano(), C struct timespec, clock_gettime().

The one ambiguity that digit counting cannot solve is old timestamps. A value below 100,000,000,000 is read as seconds, which is correct for every date from 1970 to about the year 5138, but a millisecond timestamp from 1970 to early 1973 is also below that line and will be read as seconds. If you are working with dates that old, set the unit by hand.

Mixing units is the most common timestamp bug there is. A seconds value fed to JavaScript'snew Date(), which expects milliseconds, gives you 1970; a milliseconds value handed to a seconds API gives you the year 55,000-odd. If a date comes out near the epoch or absurdly far in the future, you are off by a factor of 1000.

The year 2038 problem#

Traditional Unix systems stored time in a signed 32-bit time_t. The largest value that holds is 231−1, which is 2147483647, and that second is2038-01-19T03:14:07Z. One second later the counter overflows to-2147483648, which reads as 1901-12-13T20:45:52Z.

Try both in the tool above. It reads them as plain numbers with no 32-bit limit anywhere, so2147483648 comes back as 2038-01-19T03:14:08Z rather than 1901. That is the point of the fix: 64-bit time_t is standard on current desktop and server systems, and a signed 64-bit count of seconds reaches about 292 billion years either side of 1970.

What remains is data, not clocks. Any 32-bit integer epoch column, on-the-wire field or embedded device still has the 2038 ceiling regardless of the host's time_t. That is where the remaining work is.

Timestamps before 1970#

Negative timestamps count backwards from the Epoch: -1 is1969-12-31T23:59:59Z, and -2208988800 is the start of 1900. This tool accepts them and so do most real systems, because time_t is signed nearly everywhere and .NET, for one, documents that its ToUnixTimeSeconds() returns a negative value for dates before the epoch.

Standards are less committed. The POSIX section quoted above ends with the sentence "If the year is <1970 or the value is negative, the relationship is undefined." So negative Unix time works in practice and is outside what POSIX guarantees. If you are storing dates of birth or historical events, a date type is a better fit than an epoch integer.

Time zones, UTC, and why a timestamp has neither#

A Unix timestamp is not in UTC. It is not in any zone. It is an offset from a fixed instant, and UTC only enters when you turn it into a calendar date, because the Epoch itself is defined as midnight UTC. "1700000000 in London" is not a meaningful phrase; "1700000000 rendered in Europe/London" is.

That is why the zone selector here changes the ISO, RFC 2822 and long-form rows and leaves the four Unix numbers alone. It is also why storing a zone name alongside a timestamp is a real decision and not redundancy: the instant is recoverable from the number, but the wall clock somebody actually saw is not.

When you type a date with no offset

2023-11-14 22:13:20 names a wall clock, not an instant, so the tool resolves it with the selected zone. Two days a year that resolution is not one-to-one:

  • The spring gap. In America/New_York, 02:30 on 2023-03-12 never happened; the clocks went from 02:00 to 03:00. This tool moves forward past the gap, so you get 03:30 EDT, which is 2023-03-12T07:30:00Z.
  • The autumn overlap. In the same zone, 01:30 on 2023-11-05 happened twice, once on EDT and once an hour later on EST. This tool takes the first, which is2023-11-05T05:30:00Z.

Those are the rules most date libraries settle on. If the difference matters to you, type the offset explicitly and no guess is made.

ISO 8601, RFC 3339, RFC 2822 and HTTP-date#

Four names for date strings that look alike and are not interchangeable.

  • ISO 8601 is the broad international standard. It covers far more than timestamps: durations, intervals, week dates, ordinal dates, and several forms of the same date. Its breadth is the problem. "ISO 8601 format" on its own does not tell a parser what to expect.
  • RFC 3339 is the narrow one you actually want. Its abstract calls it "a profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar". Full date, literalT, full time, and an offset that is not optional: either Z or+hh:mm. 2023-11-14T22:13:20Z is RFC 3339. 2023-11-14 is ISO 8601 but not an RFC 3339 timestamp.
  • RFC 5322, which revised and superseded RFC 2822, defines the email Date: header:Tue, 14 Nov 2023 22:13:20 +0000. Day name and month name in English, day before month, and a signed four-digit offset.
  • HTTP-date is the same shape with the zone nailed down. RFC 9110 section 5.6.7 calls the preferred form IMF-fixdate and givesSun, 06 Nov 1994 08:49:37 GMT as the example. A sender must generate that form, and the literal GMT is required: no numeric offset, no UTC. Two older forms must still be accepted on the receiving side.

One detail worth knowing if you generate timestamps: RFC 3339 section 4.3 originally made-00:00 mean "the time in UTC is known, but the offset to local time is unknown", distinct from Z.RFC 9557 updates that section to align it with what software actually does, treating Z as meaning the same thing. The same RFC adds the bracketed zone suffix, as in2023-11-14T17:13:20-05:00[America/New_York], for when you need the zone name and not just the offset.

Converting timestamps in code#

JavaScript

javascript
// Current timestamp
Date.now();                     // 1700000000000  (milliseconds)
Math.floor(Date.now() / 1000);  // 1700000000     (seconds)

// Timestamp to date
new Date(1700000000 * 1000).toISOString();
// '2023-11-14T22:13:20.000Z'

// Date to timestamp
Math.floor(Date.parse('2023-11-14T22:13:20Z') / 1000);
// 1700000000

// The same instant read in a named zone. The instant does not move.
new Date(1700000000 * 1000).toLocaleString('en-US', { timeZone: 'America/New_York' });
// '11/14/2023, 5:13:20 PM'

JavaScript is the odd one out: Date.now() is milliseconds, and so is everything theDate constructor takes. The factor of 1000 in and out of an API that speaks seconds is where the bugs are. The range a Date can hold is ±8,640,000,000,000,000 milliseconds around the epoch, which prints as-271821-04-20T00:00:00.000Z and +275760-09-13T00:00:00.000Z. This tool enforces exactly that range and tells you when you leave it.

Python

python
import time
from datetime import datetime, timezone

# Current timestamp
time.time()       # 1700000000.123456  (float seconds)
int(time.time())  # 1700000000

# Timestamp to date
datetime.fromtimestamp(1700000000, timezone.utc).isoformat()
# '2023-11-14T22:13:20+00:00'

# Date to timestamp
int(datetime(2023, 11, 14, 22, 13, 20, tzinfo=timezone.utc).timestamp())
# 1700000000

datetime.fromisoformat('2023-11-14T22:13:20+00:00').timestamp()
# 1700000000.0

The Python docs describe time.time() as returning "the time in seconds since the epoch as a floating-point number", and note that "The handling of leap seconds is platform dependent. On Windows and most Unix systems, the leap seconds are not counted towards the time in seconds since the epoch." Pass timezone.utc tofromtimestamp; without it you get a naive datetime in the machine's local zone, which is the same class of bug as mixing units.

Go

go
import "time"

// Current timestamp
time.Now().Unix()       // seconds
time.Now().UnixMilli()  // milliseconds
time.Now().UnixNano()   // nanoseconds

// Timestamp to date
time.Unix(1700000000, 0).UTC().Format(time.RFC3339)
// "2023-11-14T22:13:20Z"

// Date to timestamp
t, _ := time.Parse(time.RFC3339, "2023-11-14T22:13:20Z")
t.Unix()      // 1700000000
t.UnixMilli() // 1700000000000
t.UnixNano()  // 1700000000000000000

UnixNano() returns an int64 of nanoseconds, and an int64 of nanoseconds only spans about 292 years, so its usable range is roughly 1678 to 2262. Outside that the result is meaningless. Unix() and UnixMilli() have no such problem.

PostgreSQL

sql
-- Current timestamp, as seconds with a fraction
SELECT extract(epoch from now());

-- Timestamp to date. to_timestamp returns timestamptz, which psql
-- renders in the session's TimeZone setting.
SELECT to_timestamp(1700000000);
SELECT to_timestamp(1700000000) AT TIME ZONE 'UTC';  -- 2023-11-14 22:13:20

-- Date to timestamp
SELECT extract(epoch from timestamptz '2023-11-14 22:13:20+00');  -- 1700000000

-- Milliseconds, as JavaScript and Java want them
SELECT (extract(epoch from now()) * 1000)::bigint;

The PostgreSQL docs define extract(epoch from ...) on atimestamptz as the number of seconds since 1970-01-01 00:00:00 UTC, negative for earlier times. On a plain timestamp it gives "the nominal number of seconds since 1970-01-01 00:00:00, without regard to timezone or daylight-savings rules", which is a different number for the same row if your column type is wrong. extract returnsnumeric; the older date_part returnsdouble precision and the docs recommend extract instead because of the precision loss. PostgreSQL stores timestamp with microsecond resolution.

Shell

sh
# Current timestamp. Same on every platform.
date +%s

# GNU coreutils, which is what Linux ships:
date -u -d @1700000000                      # Tue Nov 14 22:13:20 UTC 2023
date -u -d '2023-11-14 22:13:20 UTC' +%s    # 1700000000

# macOS and the BSDs have no -d. Use -r for a timestamp, and -j -f
# with an input format to go the other way:
date -u -r 1700000000
date -u -j -f '%Y-%m-%d %H:%M:%S' '2023-11-14 22:13:20' +%s

# The printed form follows the locale; LC_ALL=C pins it.

date +%s is portable. Everything past that is not: -d is a GNU extension, and the BSD date that macOS ships uses -r to read a timestamp and -j -f to parse a string without setting the system clock. A script that has to run on both should test for one or fall back to the other.

What else the tool tells you#

Under the main rows are the details that usually need a second lookup: how long ago the instant is, the day of the week, the ISO week, the day of the year, the zone's offset at that instant and whether it was on daylight saving time then.

The ISO week follows the ISO 8601 week-date rules, which are worth knowing because they surprise people every January. Weeks start on Monday, and week 1 is the week containing the year's first Thursday, equivalently the week containing 4 January. So 1 January 2023, a Sunday, is in week 52 of 2022, and 30 December 2019, a Monday, is in week 1 of 2020. A year has 52 or 53 ISO weeks. If your reporting groups by ISO week, the week-numbering year is not always the calendar year.

The offset is computed from the IANA time zone database your browser carries, read throughIntl.DateTimeFormat, so historical offsets and DST transitions are the real ones for that zone rather than a fixed number.

Nothing here leaves your browser#

The parsing, the zone arithmetic and the formatting all run on your machine, in JavaScript, after the page loads. Nothing you paste is sent anywhere, and there is nowhere to send it: this site is static files with no backend. Disconnect from the network and the tool keeps working, which is the quickest way to prove it. There is no third-party date library on the page either, only what the browser already ships.

Questions people ask#

What is the current Unix timestamp?

It is at the top of this page, ticking once a second, in seconds and in milliseconds, with a copy button for each. It comes from your own device's clock, so if the machine's time is wrong the number is wrong with it.

How do I convert a Unix timestamp to a date?

Paste the number into the field. The ISO 8601, RFC 2822 and long-form rows appear straight away, in the zone you have selected. The conversion is the same in code:new Date(seconds * 1000) in JavaScript,datetime.fromtimestamp(seconds, timezone.utc) in Python,date -d @seconds in a GNU shell.

How do I convert a date to a Unix timestamp?

Paste the date into the same field. 2023-11-14T22:13:20Z,2023-11-14 22:13:20, Tue, 14 Nov 2023 22:13:20 +0000 and what JavaScript's Date.toString() prints are all understood. The four Unix rows are the answer, in whichever unit you need.

Is my timestamp in seconds or milliseconds?

Count the digits. For a recent date, 10 means seconds and 13 means milliseconds. The tool decides the same way and says so under the field, and you can override it if the number is from a date before 1973, where the counting is ambiguous.

Does Unix time count leap seconds?

No. Every day is exactly 86400 seconds in the POSIX definition, so the leap seconds added to UTC since 1972 are not in the count. A Unix timestamp is therefore not an exact count of elapsed SI seconds since 1970, and it is not the right tool for measuring a duration precisely.

Can a Unix timestamp be negative?

Yes, and it means a date before 1970. Most systems handle it, though POSIX itself says the relationship is undefined below the epoch. This tool handles negatives in every unit.

What happens to Unix time in 2038?

A signed 32-bit time_t runs out at 2147483647, which is 2038-01-19T03:14:07Z, and wraps to 1901. 64-bit time is the fix and is already the default on current desktop and server systems. The exposure that is left is in stored data and in embedded systems, not in the operating systems.

Why did the date I typed come back an hour different?

You landed on a daylight saving transition. A wall-clock time with no offset does not always name exactly one instant: one hour a year does not exist, and one hour happens twice. The tool moves forward past a gap and picks the earlier of two overlapping instants, and says which offset it used. Type the offset yourself to remove the guess.

Which time zone does the tool use by default?

Your browser's, read from Intl.DateTimeFormat().resolvedOptions().timeZone when the page loads. UTC and every IANA zone your browser knows are in the list. The selection only affects how the instant is displayed.

  • Generate random v4 and time-ordered v7 UUIDs in your browser, one at a time or up to 1000 at once, and inspect any UUID you paste in.

  • Convert identifiers and text to camelCase, PascalCase, snake_case, SCREAMING_SNAKE_CASE, kebab-case, Train-Case, dot.case, path/case and four more at once, one line at a time, with acronyms and digits split the way style guides expect.

  • Percent-encode and decode URLs and query strings, with encodeURIComponent, encodeURI, strict RFC 3986 and form encoding, plus a breakdown of the query parameters.