Development

Timestamp Debugging Guide: Fix Common Date Bugs

By AZ Utils Editorial · · 11 min read

Timestamp Debugging Guide: Fix Common Date Bugs

A date shows up as 1970, or January 1901, or fifty thousand years in the future. The "created" time is off by exactly five hours. A scheduled job fires an hour early twice a year. Timestamp bugs are some of the most common — and most confusing — issues in software, but almost all of them come from a short list of causes. This guide is a practical, symptom-driven manual for debugging timestamps: how to recognise each classic bug, what causes it, and how to fix it.

It is written for developers and engineers chasing date bugs, and for anyone who wants a systematic way to diagnose them rather than guessing.

First, Memorise a Few Reference Numbers

The fastest debugging tool you have is a sense of what a correct current timestamp looks like. Burn these into memory:

  • A current Unix timestamp in seconds is about 1,7xx,xxx,xxx — ten digits, starting with 17 in this era.
  • In milliseconds it is about 1,7xx,xxx,xxx,xxx — thirteen digits.
  • 0 is 1 January 1970; a tiny number means something defaulted to the epoch.
  • 2,147,483,647 is the 32-bit limit — 19 January 2038.

With these anchors, a wrong timestamp often diagnoses itself: a 13-digit number where you expected 10 is a milliseconds-versus-seconds bug; a date in 1970 is a zero or null sneaking through; a date in 1901 or 2038 smells like a 32-bit overflow.

In short: Most timestamp bugs are one of a few patterns — seconds/milliseconds mix-ups, time-zone and DST errors, zero/null defaulting to 1970, 32-bit overflow, or ambiguous string parsing. Compare the bad value against a known-good current timestamp to identify which.

A Symptom-to-Cause Reference

SymptomLikely cause
Date is ~50,000 years in the futureMilliseconds value treated as seconds
Date is back near 1970Seconds value treated as milliseconds, or a zero/null
Date is exactly 1 Jan 1970Timestamp is 0, null, or an empty value
Date is in 1901 (or jumps negative)32-bit signed overflow (Year 2038)
Off by a fixed number of hoursTime-zone offset applied or missed
Off by one hour, twice a yearDaylight-saving (DST) handling
Date is in 1601 or 1900Wrong epoch (Windows FILETIME / NTP)
Month and day swappedAmbiguous locale string parsing

Bug 1: Seconds vs Milliseconds

This is the most common timestamp bug, full stop. JavaScript works in milliseconds while most back-ends and databases work in seconds, so a value crossing that boundary without conversion is off by a factor of 1000. The symptom is a date either far in the future (ms read as seconds) or near 1970 (seconds read as ms).

// Bug: a seconds timestamp passed straight to JS Date (expects ms)
new Date(1700000000);            // 1970-01-20  -- wrong!

// Fix: multiply seconds by 1000
new Date(1700000000 * 1000);     // 2023-11-14  -- correct

Fix: confirm the unit at every boundary, and name variables with the unit (tsSeconds, tsMs). When ingesting an unknown value, check its digit count first.

Bug 2: Time-Zone Offsets

If a time is consistently off by a whole number of hours (or 30/45 minutes for some zones), a time-zone conversion is being applied or skipped. A frequent cause is constructing or formatting a date in local time when you meant UTC, or parsing an ISO string that lacks a zone designator so the runtime guesses.

// Ambiguous: no zone, parsed as local time on the server
new Date("2023-11-14 22:13:20");

// Unambiguous: the Z marks it as UTC
new Date("2023-11-14T22:13:20Z");

Fix: keep all internal times in UTC, always include a zone (Z or an explicit offset) in strings, and convert to local time only at the display layer. Reproduce the bug on a machine set to a different time zone — if the result changes, you have found a zone-handling problem.

Bug 3: Daylight Saving Time

A bug that appears or shifts by exactly one hour, but only around late March and late October (in zones that observe DST), points to daylight saving. Local-time arithmetic is the usual culprit: adding "24 hours" across a DST transition does not always land at the same wall-clock time, and some local times either do not exist or occur twice on transition days.

Fix: do date arithmetic in UTC or with a DST-aware date library, never by adding raw seconds to a local time. Storing UTC timestamps sidesteps DST entirely, because UTC has no daylight saving.

Bug 4: The 1970 and 1901 Dates

A date showing exactly 1 January 1970 almost always means the timestamp was 0, null, undefined or empty and got coerced to the epoch — a missing value rather than a real one. Trace back to where the value should have been set. A date in 1901 (or a sudden negative number around January 2038) is the signature of a signed 32-bit overflow; the fix is to store and handle time as 64-bit.

Bug 5: Ambiguous String Parsing

If days and months come out swapped — 03/04/2023 read as 3 April instead of 4 March, or vice versa — you are parsing a locale-dependent format. Different regions order day and month differently, and parsers guess based on locale.

Fix: never exchange dates in locale formats. Use ISO 8601 (2023-03-04), which is unambiguous and parsed consistently everywhere, and store timestamps as integers rather than display strings.

Why Timestamp Bugs Are So Common

Timestamp bugs recur in every codebase for a structural reason: time has to pass through many hands, and each handoff is a chance for an assumption to go unstated. A single date might originate in a browser using milliseconds, travel through an API as a number, be stored in a database in seconds, be read by a back-end service, and finally be formatted for display in a user's local zone. At every one of those boundaries, two questions must be answered consistently — what unit is this, and what time zone does it refer to — and a bug appears the moment two sides answer differently. Because the boundaries are numerous and the assumptions are usually implicit, the opportunities for mismatch are everywhere.

What makes these bugs especially slippery is that they often produce plausible-looking wrong answers rather than obvious errors. A time that is off by five hours still looks like a valid time; a date that is wrong by a factor of 1000 still renders as a date, just a strange one. Unlike a crash or a null-reference error, a timestamp bug frequently passes silently through tests and only surfaces when a user in a particular time zone, or a value at a particular magnitude, exposes the mismatch. This combination — many boundaries, implicit assumptions, and failures that masquerade as valid data — is why time handling has a reputation for being deceptively hard, and why a systematic approach beats intuition when you are tracking one down.

A Systematic Debugging Method

  1. Capture the exact bad value — the raw number or string, not the formatted display.
  2. Compare it to a known-good current timestamp to spot factor-of-1000 or epoch errors at a glance.
  3. Decode it explicitly in UTC (for example with a converter) so the time zone cannot mislead you.
  4. Check the boundaries — every place the value is stored, transmitted, parsed or formatted is a candidate for the bug.
  5. Reproduce under a different time zone to flag zone/DST issues.
  6. Log timestamps as ISO 8601 UTC so future debugging starts from an unambiguous value.

A quick way to perform step 3 is to paste the value into our Timestamp Converter, which shows the UTC date immediately so you can confirm whether the number itself is right before hunting through code.

The reason this method is so effective is that it separates two questions that beginners tend to tangle together: is the value wrong, or is the presentation wrong? Decoding the raw number in UTC answers the first question definitively. If the UTC decode is correct but the displayed date is not, you know with certainty that the value is fine and the bug lives in formatting or time-zone handling, which dramatically narrows where you need to look. If the UTC decode is itself wrong, the value was already corrupt before any formatting happened, so you turn your attention upstream to how it was produced, stored or transmitted. By always establishing which side of that line the problem falls on before diving into code, you avoid the common trap of rewriting correct formatting logic in pursuit of a bug that actually lived in the stored value, or vice versa. A minute spent decoding the raw number in UTC routinely saves an hour of searching in the wrong place.

Try Our Free Timestamp Converter

When a date looks wrong, the first question is always "what does this number actually decode to?" Our Timestamp Converter answers it instantly.

  • ✅ Decode any Unix timestamp to a UTC date
  • ✅ Handles seconds and milliseconds
  • ✅ Private and instant in your browser

👉 Decode a suspicious timestamp now →

Building Timestamp-Resilient Systems

The best way to debug timestamp problems is to design systems where they cannot easily occur, and a few habits go a long way. The most powerful is to commit to a single internal representation of time — a UTC-based Unix timestamp — and to treat any other form as a boundary concern. Time enters the system from the outside (a user's input, a third-party API, a legacy field) and is immediately normalised to that internal form; it leaves the system at the display edge and is formatted to local time only there. In between, every comparison, calculation and storage operation works in the one canonical representation, which eliminates whole categories of unit and zone mismatches simply because there is nothing to mismatch against.

Beyond a canonical representation, defensive validation pays for itself many times over. When a timestamp arrives from outside, check that it falls within a plausible range before trusting it, so a zero, a null or a wildly out-of-range value fails loudly at the boundary rather than silently becoming a 1970 date deep in your logic. Name fields and variables with their unit, so a reviewer can see at a glance that expires_at_seconds and created_at_ms are different. And log every time in ISO 8601 UTC, so that when something does go wrong, your diagnostic trail is already in an unambiguous form. None of these habits is difficult, but together they convert time handling from a recurring source of subtle bugs into a solved, boring part of your system — which is exactly what you want it to be.

A Debugging Mindset

When you are actually staring at a wrong date, the most useful mental shift is to stop trusting anything you have not verified, starting with the display. The formatted string you see on screen has already passed through unit interpretation and time-zone formatting, either of which could be the culprit, so it tells you less than the raw value behind it. Pull out that raw number or unparsed string and treat it as the source of truth for your investigation. From there, the question is narrow and answerable: does this raw value, interpreted with the unit and epoch you believe it has, decode to the date you expect? If yes, the bug is downstream in formatting or zone handling; if no, the bug is upstream in how the value was produced or stored.

This habit of working from the raw value outward turns a vague "the date is wrong" into a precise bisection of the pipeline. You can confirm at each boundary whether the value is still correct, and the boundary where it first goes wrong is where your bug lives. Combined with the reference numbers and the symptom table from earlier in this guide, it makes even unfamiliar timestamp bugs tractable. The developers who seem to fix date issues effortlessly are not gifted with special intuition; they have simply learned to distrust the display, anchor on the raw value, and check the pipeline boundary by boundary until the mismatch reveals itself.

Common Mistakes

  1. Debugging the formatted display instead of the raw value. Always inspect the underlying number first.
  2. Forgetting the unit. The seconds/milliseconds mix-up causes more timestamp bugs than anything else.
  3. Doing arithmetic in local time. DST and offsets make this unreliable; compute in UTC.
  4. Letting zero/null flow through. It silently becomes 1970 and hides the real missing value.
  5. Exchanging locale date strings. Use ISO 8601 to avoid day/month swaps.

Best Practices

  • Store and compute in UTC / Unix integers; format to local only for display.
  • Label units in names and schemas (_ms, _seconds).
  • Log times in ISO 8601 UTC for unambiguous diagnostics.
  • Validate incoming timestamps against a plausible range so bad values fail fast.
  • Use 64-bit storage and DST-aware libraries.
  • Keep a converter handy to check raw values quickly.

Frequently Asked Questions

Why is my timestamp showing 1970?

A date of 1 January 1970 means the timestamp value was 0, null, undefined or empty and was coerced to the Unix epoch. It usually indicates a missing value rather than a real time — trace back to where it should have been set.

Why is my date thousands of years in the future?

You are almost certainly treating a milliseconds value as seconds. A milliseconds timestamp is about a thousand times larger, so interpreting it as seconds projects the date far into the future. Divide by 1000 or use the correct unit.

Why is my timestamp off by a few hours?

A whole-hour offset points to a time-zone problem — a date built or formatted in local time when UTC was intended, or a string parsed without a zone. Keep times in UTC and convert to local only for display.

Why does my date bug only appear twice a year?

That is the hallmark of daylight-saving time. Doing arithmetic in local time around DST transitions causes one-hour errors. Compute in UTC or use a DST-aware date library.

Why is my date showing 1901?

A date in 1901, or a value that suddenly turns negative around January 2038, indicates a signed 32-bit integer overflow. Store and handle time as a 64-bit integer to fix it.

How do I debug a wrong timestamp quickly?

Capture the raw value, compare its digit count to a known-good current timestamp, decode it explicitly in UTC with a converter, and check every boundary where it is stored, parsed or formatted.

Summary

Timestamp bugs feel mysterious but are remarkably predictable: a value far in the future or near 1970 is a seconds/milliseconds mix-up, a whole-hour offset is a time-zone issue, a twice-yearly one-hour slip is daylight saving, an exact 1970 is a zero or null, and 1901 is a 32-bit overflow. The fastest path to a fix is to stop debugging the formatted display, capture the raw value, compare it to a known-good current timestamp, and decode it explicitly in UTC. Store and compute in UTC, label your units, log in ISO 8601, and keep a converter within reach — and these bugs go from baffling to routine.

👉 Check a timestamp with our free tool →

AZ Utils Editorial

AZ Utils Editorial

Finance & web-tools writer

AZ Utilis writes practical, plain-English guides on calculators, finance and everyday web tools, drawing on years of experience helping beginners and small businesses get the numbers right.

Development

How to Format JSON (Beautify & Minify)

How to format JSON — beautify it for readability or minify it for production — in tools, editors, the command line and code, with the why behind each.

AZ Utils Editorial · · 10 min read