Unix Timestamp Converter: Convert Epoch Time to Date Instantly
Free Unix timestamp converter — auto-detects seconds/ms/µs/ns, converts to any timezone with BigInt precision, and covers the Year 2038 problem.
Try it now: Unix Timestamp Converter — Convert epoch seconds, milliseconds, microseconds and nanoseconds to a date in any timezone and back, with BigInt precision so nanosecond values are never rounded.
What Unix Time Actually Is
Unix time — also called epoch time — is a count of seconds elapsed since 1970-01-01T00:00:00 UTC, a single moment known as the epoch. There is nothing special about that date astronomically; it was chosen because it was a convenient round point for the Unix systems that first adopted the convention in the early 1970s, and it stuck. Every mainstream operating system, database, and programming language runtime has agreed on the same reference point ever since, which is the entire reason a raw integer like 1785758400means the same instant whether it's read by a Python script, a Postgres row, or a browser running JavaScript.
The reason systems reach for a single integer instead of a formatted string like 2026-08-03T12:00:00Zcomes down to what you can do with it cheaply. Comparing two integers is one CPU instruction; comparing two date strings correctly means parsing both first, handling variable field widths, and getting timezone offsets right before the comparison even starts. Sorting a million rows by an integer column is a plain numeric sort; sorting by a formatted date string only works if every row happens to use the same format and the same zone, which is exactly the kind of assumption that breaks quietly. An epoch value also makes arithmetic trivial — "how many seconds until this token expires" is one subtraction, not a call into a date-math library.
None of that means epoch values are meant to be read by humans. They're the storage and transport format; a unix timestamp converteris the tool that sits at the boundary, turning a raw integer into a date someone can actually read, and turning a date someone picks on a calendar back into the integer a system expects. That's exactly what GenKitLab's Unix Timestamp Converter does, in both directions, entirely in your browser — nothing you paste in is ever uploaded anywhere.
The Same Number, Four Different Meanings
Here is the single most common source of bugs in anything that touches Unix time: the epoch is defined in seconds, but almost nothing in practice only ever hands you seconds. JavaScript's Date.now() returns milliseconds. Some logging pipelines emit microseconds. High-resolution tracing systems increasingly emit nanoseconds. The number itself gives no hint which one you're holding — it's just digits — and a converter that assumes "seconds" unconditionally will confidently render the wrong date.
The fix is that the magnitude of the number tells you which unit it's in, because each unit multiplies the same moment by a fixed power of ten. As of 2026, current Unix time in seconds sits around 1.78 billion — a 10-digit number — which fixes the digit count for every other unit relative to it:
| Unit | Digits, as of 2026 | Where it comes from |
|---|---|---|
| Seconds | 10 | The Unix time definition itself; time(), most database epoch columns |
| Milliseconds | 13 | JavaScript's Date.now(), Java's System.currentTimeMillis() |
| Microseconds | 16 | Python's time.time_ns() / 1000, some database high-precision columns |
| Nanoseconds | 19 | Go's time.UnixNano(), tracing and observability systems |
seconds: 1785758400 milliseconds: 1785758400000 microseconds: 1785758400000000 nanoseconds: 1785758400000000000 // Same instant. Four different integers, each ten times longer than the // last. A parser that assumes "seconds" unconditionally will render one // of the other three as the wrong date -- confidently, with no error.
This is why a tool built to handle timestamp to date and epoch to date conversion has to detect the unit rather than assume it. A reasonable rule of thumb — count the digits, and pick the unit whose typical width they match — catches the overwhelming majority of real values:
function detectUnit(value) {
const digits = Math.abs(value).toString().length;
if (digits <= 10) return "seconds";
if (digits <= 13) return "milliseconds";
if (digits <= 16) return "microseconds";
return "nanoseconds"; // 17-19 digits
}
detectUnit(1785758400); // "seconds"
detectUnit(1785758400000); // "milliseconds"
detectUnit(1785758400000000); // "microseconds"Get the unit wrong in either direction and the failure is dramatic, not subtle. Feed a 10-digit seconds value straight into an API that expects milliseconds, and it computes a moment about 20 days after the epoch — a date parked in January 1970, which is exactly the "why is this row dated 1970" bug that shows up whenever a seconds value leaks into a milliseconds field unconverted. Go the other way — feed a 13-digit milliseconds value into something that treats the raw integer as seconds and then multiplies by 1000 to get milliseconds — and the result overshoots the other direction entirely, landing somewhere around the year 58,564. Neither failure is a rounding error you might miss; both are obviously broken dates, if anyone happens to be looking at the output.
Floating-Point Precision: Why Nanoseconds Need BigInt
JavaScript's Number type is a 64-bit float, and it only represents every integer exactly up to Number.MAX_SAFE_INTEGER — 2^53 - 1, or 9,007,199,254,740,991. Past that point, some integers simply have no exact representation as a double; the runtime silently rounds to the nearest value it can represent, with no error, no warning, and no indication that anything happened at all.
What that ceiling means for each Unix time unit is worth being precise about, because it isn't the same for all four:
- Seconds and millisecondsstay safely inside that range for a very long time — a millisecond epoch value doesn't reach
Number.MAX_SAFE_INTEGERuntil roughly the year 287,396. Not a practical concern. - Microsecondshave more headroom than most people assume, but it isn't unlimited — the safe range runs out around the year 2255. Comfortable today, but not something to treat as infinite.
- Nanoseconds are the one that actually bites, and it bites now, not decades from now: a nanosecond epoch value exceeds
Number.MAX_SAFE_INTEGERfor any date after roughly April 1970 — about 104 days after the epoch itself. Every nanosecond timestamp anyone generates today for a real event is already past that ceiling.
In practice, that means naive JavaScript math silently corrupts the least significant digits of any nanosecond-since-epoch value the moment it's parsed as a plain Number — which is precisely why a tool that claims nanosecond precision has to hold the raw value as a BigIntfrom the moment it's parsed, and only ever do arithmetic on it as a BigInt, never by round-tripping through Number along the way:
// Number.MAX_SAFE_INTEGER = 9,007,199,254,740,991 (2^53 - 1) const ns = 1785758400123456789; // a nanosecond timestamp, as a plain Number const nsPlusOne = 1785758400123456790; // one nanosecond later console.log(ns === nsPlusOne); // true -- both round to the same double const nsBig = 1785758400123456789n; // the same value, as a BigInt const nsBigPlusOne = 1785758400123456790n; console.log(nsBig === nsBigPlusOne); // false -- BigInt keeps every digit exact
This is the reason GenKitLab's timestamp converter parses and stores the raw value as a BigInt end to end. A nanosecond value pasted in comes back out exactly as entered — no digits swapped, no silent rounding — regardless of how far the date sits from the epoch.
Timezones: the Epoch Is Absolute, the Date You See Isn't
An epoch value names one specific, physical instant — the same instant everywhere on Earth, at the same moment, for everyone. There is no such thing as "1785758400 in Tokyo time" as distinct from "1785758400 in New York time": the integer itself carries no timezone, because it doesn't need one. It's a distance from a fixed point, measured in seconds, full stop.
Timezone only enters the picture the moment you renderthat instant as a human-readable date — year, month, day, hour. And that's the actual root cause behind almost every "why does this timestamp show the wrong day" report: it's never the epoch value that's wrong, it's an unstated or mismatched assumption about which timezone it was rendered into. A timestamp logged at 11:58 PM in one zone and displayed in a zone eleven or twelve hours ahead genuinely does fall on the next calendar day — that isn't a bug in the number, it's two different, both-correct answers to two different questions.
The fix isn't to declare UTC the universal default and stop worrying about it. UTC avoids the ambiguity of "whose local time," but a user-facing date — a booking's check-in day, an invoice's due date, a log line a support engineer is reading at their desk — usually needs to be rendered in aspecifictimezone that means something to whoever's reading it, and that isn't always UTC and isn't always the viewer's own browser zone either. Being explicit about which zone a timestamp renders into is the actual fix; treating any one zone as a default that makes the question disappear is how the bug keeps coming back.
const epochSeconds = 1785758400; // 2026-08-03T12:00:00Z
new Intl.DateTimeFormat("en-US", {
timeZone: "UTC",
dateStyle: "full",
timeStyle: "long",
}).format(epochSeconds * 1000);
// "Monday, August 3, 2026 at 12:00:00 PM UTC"
new Intl.DateTimeFormat("en-US", {
timeZone: "Pacific/Kiritimati", // UTC+14, among the earliest local dates on Earth
dateStyle: "full",
timeStyle: "long",
}).format(epochSeconds * 1000);
// "Tuesday, August 4, 2026 at 2:00:00 AM +14"
new Intl.DateTimeFormat("en-US", {
timeZone: "Pacific/Midway", // UTC-11, among the latest
dateStyle: "full",
timeStyle: "long",
}).format(epochSeconds * 1000);
// "Monday, August 3, 2026 at 1:00:00 AM -11"One epoch integer, and the calendar date it prints as spans two different days depending purely on which zone you asked for — a 25-hour spread between the earliest and latest local dates on Earth at the exact same instant. That's why GenKitLab's converter lets you pick any IANA timezone for the rendered date, not just a toggle between UTC and whatever zone your own browser happens to be set to.
The Year 2038 Problem
Plenty of systems don't store Unix time as an arbitrary-width integer — they store it as a signed 32-bit integer, a convention baked into decades of C code, filesystem formats, and embedded firmware. A signed 32-bit integer tops out at 2,147,483,647. Counting seconds from the epoch, that ceiling is reached at exactly 2038-01-19T03:14:07 UTC.
One second later, the correct value would be 2,147,483,648— which doesn't fit. The counter overflows and wraps around to the most negative value a signed 32-bit integer can hold instead, -2,147,483,648, which renders as a date in 1901, not 2038. A system reading that wrapped value doesn't crash; it just quietly believes it's 1901, which is often worse.
// A signed 32-bit integer holds values from -2,147,483,648 to 2,147,483,647. // Unix time reaches that ceiling at exactly this instant: 2147483647 // 2038-01-19T03:14:07 UTC -- the largest value that still fits // One second later, the correct value is 2147483648 -- which doesn't fit // in a signed 32-bit int. It wraps to the most negative representable // value instead: -2147483648 // renders as 1901-12-13T20:45:52 UTC on an affected system
This isn't a solved historical curiosity — it's a real, still-open issue for anything built on a 32-bit time_t: older embedded controllers, some file formats and on-disk structures that reserved exactly 32 bits for a timestamp field, and 32-bit builds of software that hasn't moved to a 64-bit time_t. Mainstream 64-bit operating systems and modern language runtimes sidestepped the problem years ago by widening the integer, but that only helps the systems that were rebuilt — legacy and embedded systems running on the old assumption don't get fixed by anyone else's upgrade.
Converting Timestamps in Code
A browser tool is the right call for a one-off value, a support ticket, or eyeballing a log line. The moment the conversion needs to run inside an application, it belongs in code — and the unit and timezone traps above apply exactly the same way there.
Python: datetime, explicitly
from datetime import datetime, timezone epoch_seconds = 1785758400 # Naive -- no tzinfo attached, silently uses whatever zone the machine is set to. naive = datetime.fromtimestamp(epoch_seconds) print(naive) # e.g. 2026-08-03 08:00:00, if the box happens to be set to US Eastern # Explicit -- always name the zone, in code and in review. utc = datetime.fromtimestamp(epoch_seconds, tz=timezone.utc) print(utc) # 2026-08-03 12:00:00+00:00 # fromtimestamp() always expects seconds. A milliseconds value has to be # divided down first, or it lands on a wildly wrong date. epoch_millis = 1785758400000 ms_as_utc = datetime.fromtimestamp(epoch_millis / 1000, tz=timezone.utc)
datetime.fromtimestamp() without a tzargument doesn't mean "no timezone" — it means the local timezone of whatever machine runs the code, silently. That's the Python equivalent of the rendering trap from the timezone section above: pass tz=timezone.utc explicitly, or whichever specific zone the output actually needs to be shown in.
SQL: to_timestamp()
-- to_timestamp() expects seconds -- the same trap as Python's fromtimestamp(). SELECT to_timestamp(1785758400); -- 2026-08-03 12:00:00+00 -- A milliseconds column needs the same division before conversion: SELECT to_timestamp(1785758400000 / 1000.0); -- Render into a specific zone explicitly, rather than trusting the -- session's timezone setting to be the one you meant: SELECT to_timestamp(1785758400) AT TIME ZONE 'UTC'; SELECT to_timestamp(1785758400) AT TIME ZONE 'Asia/Tokyo';
AT TIME ZONE is doing the exact same job as Intl.DateTimeFormat's timeZoneoption earlier — converting one absolute instant into a specific, named zone's local calendar date, rather than leaving it to whatever the session happens to be configured with.
GenKitLab vs. Generic Epoch Converters
| GenKitLab | epochconverter.com | unixtimestamp.com | |
|---|---|---|---|
| Automatic unit detection | Detects seconds, milliseconds, microseconds, or nanoseconds from magnitude, with an explicit override | Assumes seconds by default; other units need a manual conversion step | Seconds and milliseconds only, no microsecond or nanosecond input |
| Timezone rendering | Any IANA timezone, not just a UTC-vs-local toggle | UTC and your browser's local zone | Local and UTC only |
| Precision on very large values | Raw value held as BigInt end to end -- nanosecond digits never round | Standard JS number math; not built for nanosecond-scale input | Standard JS number math |
| Explains the why (unit ambiguity, precision loss, Y2038) | This page, plus inline guidance in the tool itself | Minimal explanatory content | Minimal explanatory content |
| Sign-up required | No | No | No |
| Best fit | Any unit, any timezone, and values large enough that float rounding would matter | A quick default lookup in seconds or milliseconds | A quick default lookup in seconds or milliseconds |
Both named alternatives are genuinely useful for the common case — a seconds or milliseconds value, rendered in UTC or your own local zone. Where they run out of road is exactly where the harder bugs live: a microsecond or nanosecond value from a tracing system, a date that needs to render in a timezone that isn't yours, or a value large enough that ordinary floating-point math would quietly round it. GenKitLab's Unix Timestamp Converter runs entirely client-side and handles all three, with nothing you paste in ever transmitted anywhere.
Explore More Developer Tools
Timestamps show up constantly alongside a handful of other everyday conversions:
- UUID Generator — UUID v7 embeds a 48-bit Unix millisecond timestamp directly in the identifier itself; see the UUID guide for how that embedded timestamp is laid out and decoded.
- JWT Decoder — a JWT's
iatandexpclaims are Unix timestamps in seconds; decoding a token means running exactly this same epoch-to-date conversion on two of its fields. - Cron Expression Parser — once a schedule's next run time is computed, it's the same rendering question as any other timestamp: which timezone is that next run actually in?
See the full Utilities category for the rest of the everyday conversions developers reach for constantly.
Frequently asked questions
›What is a Unix timestamp?
A count of seconds elapsed since 1970-01-01T00:00:00 UTC, a fixed reference point called the epoch. It's an arbitrary but universally-adopted starting line, which is why the same integer means the same instant across every operating system, database, and programming language that uses it.
›How do I know if a timestamp is in seconds or milliseconds?
Count the digits. As of 2026, a 10-digit number is seconds, a 13-digit number is milliseconds (JavaScript's Date.now() convention), 16 digits is microseconds, and 19 digits is nanoseconds. A converter should detect the unit from that magnitude rather than assume seconds — assuming wrong renders a date that's either stuck in early 1970 or tens of thousands of years in the future.
›Why does my timestamp show the wrong day or hour?
The epoch value itself is timezone-free -- it names one absolute instant. The wrong-looking date almost always comes from rendering that instant into an unintended or mismatched timezone, not from a bad number. The fix is to be explicit about which timezone you're rendering into, rather than assuming UTC or your browser's local zone is automatically the right default.
›What is the Year 2038 problem?
Systems that store Unix time in a signed 32-bit integer overflow at exactly 2038-01-19T03:14:07 UTC, the largest value that fits. The next second wraps to the most negative representable value, which renders as a date in 1901 instead of 2038. It's a real, ongoing issue for 32-bit embedded systems and legacy time_t implementations, not a solved historical footnote.
›Why does JavaScript lose precision on large timestamps?
JavaScript's Number type is a 64-bit float that only represents every integer exactly up to Number.MAX_SAFE_INTEGER (2^53 - 1, or 9,007,199,254,740,991). A nanosecond-since-epoch value exceeds that ceiling for any date after roughly April 1970, so parsing one as a plain Number silently rounds the least significant digits. Using BigInt for the raw value avoids that silent precision loss.
›Is UTC always the safe default for displaying a timestamp?
UTC avoids the ambiguity of whose local time you mean, but it isn't a universal default that makes the timezone question disappear -- a user-facing date usually needs to render in a specific zone that means something to the person reading it, which isn't always UTC. Being explicit about which zone you're rendering into is the actual fix, not picking one zone and treating it as neutral.
›How do I convert a Unix timestamp to a date in Python or SQL?
Python: datetime.fromtimestamp(seconds, tz=timezone.utc), always passing an explicit tz -- omitting it silently uses the machine's local zone. PostgreSQL: to_timestamp(seconds), then AT TIME ZONE 'Zone/Name' to render it into a specific zone explicitly. Both functions expect seconds, so a milliseconds value needs to be divided by 1000 first.
›Does GenKitLab's timestamp converter run entirely in the browser?
Yes. Parsing, unit detection, BigInt arithmetic, and timezone rendering all happen client-side in JavaScript -- nothing you paste into the converter is uploaded or logged anywhere.
Last updated