MODE
Developers8 min read

Timestamps and Time Zones: Fixing Off-by-Hours Bugs and Seconds vs Milliseconds

How to diagnose date bugs: Unix seconds versus milliseconds, UTC storage, daylight saving traps, JavaScript date parsing quirks and ISO 8601 offsets.

Published September 18, 2026 · By Sudip Bhowmick

Date and time bugs have a special talent for appearing only on certain days, in certain countries or after a clock change. They are rarely random. Almost all of them come from mixing units, mixing time zones or relying on a default that was different from the one you assumed. This guide gives you the rules that remove most of those bugs.

What a Unix Timestamp Is

A Unix timestamp counts the seconds since 1 January 1970 at 00:00 UTC. It has no time zone, because it identifies one exact moment. The same number means the same instant in Tokyo and in Toronto, and only the display differs. That makes it an excellent storage format and a poor display format.

Seconds or Milliseconds

The most common mistake is a unit mismatch. Many systems, including Unix tools and JWTs, use seconds. JavaScript's Date works in milliseconds. Java, many databases and mobile libraries also use milliseconds.

  • ▸A current timestamp has 10 digits in seconds and 13 digits in milliseconds. Count the digits first.
  • ▸A date in January 1970 usually means a seconds value was read as milliseconds, because it is a thousand times too small.
  • ▸A date thousands of years in the future means a milliseconds value was read as seconds.
  • ▸Document the unit in every API field name, such as expires_at_seconds, or use ISO 8601 strings that carry no ambiguity.

Store in UTC, Convert at the Edges

Keep every stored time in UTC, whether as a timestamp or as an ISO string with a Z or an offset. Convert to the user's time zone only when you display it. The reverse, storing local times, makes data ambiguous: when clocks go back an hour in autumn, a local time such as 01:30 happens twice, and when they go forward in spring, 02:30 may not exist.

Store the user's time zone as a name, such as Europe/Berlin, not as a fixed offset. A fixed offset such as plus one hour does not know when daylight saving starts.

Daylight Saving Traps

  • ▸A day is not always 24 hours. Adding 24 hours to a timestamp across a clock change gives a different local time than adding one calendar day.
  • ▸Recurring events should be stored as local time plus a time zone, so a nine o'clock meeting stays at nine after the clocks change.
  • ▸Duration calculations between dates should use timestamps. Calendar calculations such as the same time next month should use a date library that understands the zone.
  • ▸Some zones have unusual offsets, such as plus 5:30 or plus 5:45, and some changed their rules in the past. Never assume offsets are whole hours.

JavaScript Parsing Quirks

  • ▸A date-only ISO string such as 2026-10-03 is parsed as midnight UTC, which can show as the previous day in time zones behind UTC.
  • ▸A date and time string without an offset, such as 2026-10-03T00:00, is parsed as local time. Two strings that look almost alike produce moments hours apart.
  • ▸Month numbers are zero based in the Date constructor, so month 9 means October.
  • ▸Non-ISO formats such as 03/10/2026 are parsed in an implementation dependent way. Avoid them.
  • ▸Use Intl.DateTimeFormat or a date library to display in a given zone, instead of building strings by hand.

Python and Databases

In Python, a naive datetime has no zone and an aware datetime does. Mixing them raises errors or silently gives wrong results when one is assumed to be local. Use aware datetimes in UTC for storage. In databases, prefer types that store an absolute instant, such as timestamptz in PostgreSQL, over types that store a bare local time, and set the session time zone explicitly.

Debugging Checklist

Convert the suspicious value to its UTC, ISO 8601 and local forms with the Unix Timestamp Converter and check each one. Count digits to check the unit. Compare the server, database and browser zones. If the error is exactly one hour, suspect daylight saving, and if it is a whole number of hours that matches a user's offset, suspect a missing or doubled zone conversion.

Conclusion

Keep timestamps unambiguous: say whether they are seconds or milliseconds, store instants in UTC, keep the user's zone by name, convert only for display and be careful with date-only strings. When a bug appears, look at the same moment in UTC and in local time, because the difference between them is the clue.

Free Tool

Open the Unix Timestamp Converter

Try It Free →