How to Convert Unix Timestamps Without Landing on the Wrong Day

A Unix timestamp looks harmless. It is just a number. Then you convert it, the meeting invite lands on Tuesday instead of Wednesday, and a user in Tokyo swears they never booked 11 p.m.

That is not a formatting nitpick. Time is one of the few things in software that can be both mathematically correct and still wrong for a person. The number was fine. The timezone, the unit, or the daylight-saving assumption was not.

Unix time counts seconds since 00:00:00 UTC on 1 January 1970. It does not know about your office, your phone, or the fact that your country skipped an hour last spring. When you “convert a timestamp,” you are choosing a timezone and a calendar, not just printing a date.

This is a practical walk through how timestamps actually work, where conversions go sideways, and how to read a number before you trust the date on screen.

What a Unix timestamp is really counting

Unix time is an elapsed count. In its classic form it is seconds. In JavaScript it is often milliseconds. In some databases it is microseconds. Same idea, different rulers. If you treat a 13-digit value as seconds, you will get a date centuries in the future. If you treat a 10-digit value as milliseconds, you will get a date in 1970 that looks like a bug in your seed data.

The epoch itself is UTC. That part is stable. What is not stable is the wall clock you print afterward. “2026-03-08 02:30” means different things in New York and in London, and on some nights it does not exist at all because the clocks jumped forward.

People also mix Unix time with other clocks. Excel serial dates. ISO-8601 strings with offsets. “Local time” from a phone that the user never set correctly. A converter that only accepts an integer will not save you if the integer was built from the wrong clock to begin with.

Start by asking three questions. Is this seconds, milliseconds, or something else? Is it UTC, or did someone already bake in a local offset? Am I displaying this for a machine, a log, or a human who lives in a city?

Seconds, milliseconds, and the extra zeros that lie

Count the digits. Ten digits is usually seconds. Thirteen is usually milliseconds. Sixteen is often microseconds. This is a heuristic, not a law, but it catches most accidents in web work.

JavaScript’s Date.now() returns milliseconds. Python’s time.time() is seconds as a float. PHP’s time() is seconds as an integer. MySQL’s UNIX_TIMESTAMP() is seconds. If two services disagree by a factor of 1,000, look at the language before you look at the network.

A converter should let you pick the unit instead of guessing. Guessing is how a QA report says “the event is in the year 57064.” That looks absurd, so you notice it. The dangerous bug is quieter: milliseconds treated as seconds after you divide wrong, or a truncated integer that is still a plausible date, just off by a few weeks.

If you are unsure, convert the same number both ways. One result will be obviously wrong. Keep the one that matches the rest of your data: nearby log lines, the user’s “created at” field, the email that went out the same hour.

UTC is not optional, even when the UI is local

Store UTC. Display local. That sentence is old and still ignored.

Teams store “local time” because the first customer was in one city. Then the second customer is not. Then a cron job in UTC fires an email at 5 a.m. for someone who thought it was scheduled at 9. The timestamp in the database was never lying. The product was translating it too early.

When you convert a Unix timestamp, convert it to UTC first and read that. Then convert to the timezone you actually care about. If you skip UTC and jump straight to “my laptop’s zone,” you will get a different answer than your coworker, and both of you will be right according to your machines.

Servers should run in UTC. CI should run in UTC. If a test fails only at 11 p.m. in one office, you probably encoded a local midnight somewhere. A timestamp converter is a fast way to prove it: paste the integer, switch zones, and watch the calendar day flip.

Daylight saving is where “correct” still looks wrong

A Unix timestamp does not spring forward. Timezones do.

In spring, some local clocks skip an hour. In autumn, some local hours happen twice. If your code says “2:30 a.m. on the changeover day” without an offset, you have an ambiguous or impossible time. Unix time does not have that problem. The instant exists. The label you print might not.

This is why scheduling UIs should store an instant plus a timezone name, not a naive local string. “America/New_York” is a rule. “GMT-4” is a snapshot. If you store the snapshot, next year’s DST change will not apply, and next year’s reminders will drift.

When a date looks off by one hour, do not round the timestamp. Check whether the conversion used a fixed offset instead of a named zone. Check whether the library’s timezone database is stale. Check whether the user crossed a boundary during a flight and the phone updated while your session did not.

The off-by-one day that is usually a timezone, not a bug in math

A user in California creates a record “on Monday.” The dashboard in UTC shows Sunday night. Support calls it a bug. Engineering prints the Unix value and sees it is correct. Both sides are looking at different clocks for the same instant.

This happens constantly with date-only fields. A birthday, a due date, an invoice date. If you store those as Unix timestamps at “midnight local,” you have already mixed a calendar date with a timezone. Someone else reads midnight UTC and the day rolls backward.

For a true calendar date, store a date, not an instant. 2026-09-02 is not the same kind of value as 1756800000. Converting one into the other requires a timezone even if nobody in the meeting wants to admit it.

When you must show a Unix value as a date, be explicit: “Monday 2 Sep 2026, 00:00 UTC” or “Monday 2 Sep 2026 in the user’s timezone.” Hide the zone and you will get tickets that cannot be reproduced.

A simple way to convert a timestamp without fooling yourself

Paste the number. Confirm the unit. Convert to UTC and read the ISO string. Then convert to the zone you will show in the product.

If the result is in 1970, you treated milliseconds as seconds or you passed a zero. If the result is in the 50000s, you treated seconds as milliseconds. If the clock looks right and the weekday is wrong, you are probably displaying UTC as if it were local, or the other way around.

Cross-check with a second source. Your language’s standard library, a trusted converter, and the original log line should agree. If they do not, the input is not the Unix time you think it is. It might already include an offset. It might be an Excel serial. It might be a truncated ID that only looks like a timestamp.

Keep the original integer in the ticket. Pretty dates are for humans. The integer is the evidence. When someone says “it shows the 3rd for me,” you want the number, not a screenshot of a formatted string that already lost the zone.

How programming languages quietly disagree

JavaScript’s Date is a millisecond instant plus local formatting. new Date(seconds * 1000) is a common fix and a common place to forget the multiply. toISOString() is UTC. toString() is local. Logging the wrong one in production is how two logs from the same request disagree by several hours.

Python’s aware datetimes need a timezone. Naive datetimes pretend they do not. datetime.utcfromtimestamp() looks convenient and is easy to misuse if you then treat the result as local. Prefer an explicit UTC object, then convert with a real zone database.

SQL is its own mess. TIMESTAMP and TIMESTAMPTZ are not the same. Some databases store UTC and convert on read. Some store whatever you sent. If your ORM prints a Unix value, check whether it converted before hashing the number into the API.

Mobile apps add another layer. iOS and Android format with the device locale. A converter on the web using your laptop zone will not match a phone in another country. That is expected. Document which zone the API uses, and make the app convert for display only.

Common mistakes that look like converter bugs

Leading zeros and commas do not belong in the integer. Neither does scientific notation copied from a spreadsheet.

Negative timestamps are valid. They are dates before 1970. If your UI refuses them, you cannot represent historical events. If a converter overflows on dates after 2038, you are on a 32-bit second counter. That is a real limit in old systems. It is not a reason to guess.

Floating-point seconds can round. A value like 1756800000.7 is not the same instant as the truncated integer. If you need sub-second accuracy, keep the fraction or switch to milliseconds.

Copy-paste from a URL can turn the number into a string with whitespace. Trim it. If the converter fails, look at the characters, not the math.

People also convert the display string back into a timestamp and store that. You just rounded through a locale. Month and day swap in some formats. 03/04/2026 is March or April depending on who wrote it. Prefer the original Unix value when it still exists.

A few situations where a careful conversion saves the day

A webhook retries at exp from a JWT. You decode the claim, treat it as milliseconds, and think the token is valid for decades. It was seconds. The converter would have shown a date next hour, not next century.

A report groups orders by “day.” UTC midnight splits a Pacific evening into two days. Revenue looks like it dropped. The timestamps were right. The bucket was in the wrong zone.

A calendar invite sent from a browser uses local time without a zone. The guest in another country sees the wrong hour. Unix time in UTC plus a named timezone on the event would have survived the email client.

A log aggregator parses timestamp as milliseconds because the field is numeric and long. It was seconds with extra IDs concatenated. The chart is empty for “today” because every event sits in 1970. Counting digits would have caught it in a minute.

Keep human labels and machine values separate

Show people a weekday, a date, and a time with a zone abbreviation when the zone matters. Keep Unix time in APIs, logs, and databases. Do not sort by a formatted string. Do not store “2 hours ago” as a timestamp. Those are display layers.

If you need to debug, paste the integer into a converter, pin the unit, read UTC, then the user’s zone. Write both into the ticket. That is enough to end most “wrong day” arguments.

Redact nothing about the number itself. A Unix timestamp is rarely a secret. The rest of the payload might be. Convert a copy, not the only copy in a production secret store.

A simple habit that pays off

When a date looks wrong, convert the integer before you rewrite the feature. Check the unit. Read UTC. Then convert to the zone the user actually lives in. If the day still flips, you are displaying a calendar date as if it were an instant, or an instant as if it were a date.

Unix time is a clean ruler. The calendar is the messy part. Treat conversion as a translation between those two, not as a pretty-print step, and the next “it happened yesterday” ticket will be smaller, and the next scheduled job will fire on the day you meant.

Leave a Comment