Why Timestamp Conversion Between Systems Still Hurts

By Freddy ·

Every system that stores a point in time has to pick a representation for it, and almost none of them pick the same one. That would be a minor annoyance if you only ever talked to your own database, but real integrations rarely stay that contained. The moment a payload crosses a boundary, your service to a partner's API, a webhook from a payment processor, a CSV export from a client's legacy system, timestamp conversion between systems stops being a formality and starts being the thing that silently corrupts your data.

The standard exists. Almost nobody fully follows it.

ISO 8601 (and its stricter API-friendly subset, RFC 3339) is the closest thing the industry has to an agreed-upon answer: a human-readable string like 2024-01-15T09:00:00Zthat encodes the date, time, and time zone offset in one unambiguous package. If every system used it consistently, this article wouldn't need to exist. But standards only help when every party in an integration adopts them, and time predates most of today's API conventions by decades. Unix systems have used seconds-since-epoch since the 1970s. Windows and .NET use 100-nanosecond ticks since January 1, year 1. Excel and Google Sheets use a serial day count starting from December 30, 1899, complete with a famous off-by-one leap year bug that Microsoft intentionally preserved for backward compatibility. None of these were wrong when they were designed; they just weren't designed to talk to each other.

Where it actually breaks: versions, vendors, and third parties

Seconds versus millisecondsis the single most common failure inside a single stack. JavaScript's Date.now() and Date.getTime() return milliseconds. Python's time.time() and most Unix tooling return seconds. Java's Instant.getEpochSecond() returns seconds, but System.currentTimeMillis()on the same JVM returns milliseconds. Mix a value from one into code expecting the other and you don't get an error, you get a date that's either stuck in 1970 or sitting somewhere around the year 50,000. Nothing crashes. A report just quietly shows the wrong dates until someone notices.

Third-party APIs each make their own call, and you rarely get to negotiate. Stripe timestamps are Unix seconds. Twitter's (now X's) older API returned a custom string format like Wed Oct 10 20:19:24 +0000 2018that needed its own parser. AWS services are inconsistent even with each other: CloudTrail logs use ISO 8601 with milliseconds, while some SDK responses return native epoch values. When you're integrating with two or three vendors at once, each one is internally consistent and none of them agree with the others, and the translation layer between them is entirely your problem to build and maintain.

Naive datetimes without a time zonecause a quieter but more dangerous class of bug. A date and time on its own, “2024-01-15 09:00,” means nothing until you know whose clock it's measured against. A legacy system exporting local server time with no offset, handed to a service that assumes everything is UTC, produces records that are off by whatever the server's time zone happens to be, and that offset changes twice a year in any region that observes daylight saving. This is exactly the kind of bug that passes every test written in one time zone and fails the moment a customer, a server region, or a teammate is in a different one.

Versions inside your own systemsaren't immune either. A database migration that changes a column from an integer epoch to a native timestamp type, an API v2 that switches from seconds to ISO 8601 while v1 still ships seconds, or a client library upgrade that changes a default serialization format: each of these is a timestamp format change wearing a version number, and every consumer of that data has to catch up at the same time or start silently misinterpreting it.

Why this keeps happening

None of this is negligence. It's what happens when integration involves multiple parties who don't control each other's internals. You can enforce ISO 8601 inside your own codebase, but you can't enforce it on a payment processor's webhook payload, a partner's legacy export job, or an internal system another team owns and isn't prioritizing a format change for. Add enough third parties to a single pipeline and you end up juggling three or four timestamp representations simultaneously, each correct in its own context and each a landmine everywhere else. The fix isn't convincing the world to standardize, that ship sailed fifty years ago. The fix is having a fast, reliable way to check exactly what a given value means before it causes a bug that ships to production.

A practical fix: KeyForge's Timestamp Converter

That's the gap KeyForge's newly released Timestamp Converter is built for. Paste any raw value, a 10-digit epoch, a 13-digit epoch, or an ISO 8601 string, and it detects which format you've got by magnitude and syntax rather than making you specify it up front. The result shows every representation you're likely to need side by side: Unix seconds, Unix milliseconds, ISO 8601 in UTC, the same instant formatted in your local time zone, a time zone you pick explicitly, and a plain-English relative time like “3 days ago” for a quick sanity check.

The reverse direction gets the same care. The Build panel lets you pick a date and time and choose explicitly whether to interpret it as your local time zone or as UTC, producing the exact Unix timestamp either interpretation implies, so you're never left guessing which one a downstream system actually wants. And because it runs entirely client-side using the browser's built-in Date and IntlAPIs, there's no round trip to a server: a production timestamp you're debugging, which can indirectly reveal when a record was created or a session started, never leaves your machine.

It pairs naturally with the rest of the toolkit when timestamps show up somewhere other than a log line. A JWT's exp and iat claims are Unix seconds by spec, so decoding a token with the JWT Decoder and then pasting the raw claim value into the Timestamp Converter turns an opaque number into an actual date in seconds. Comparing two versions of an API response for a schema mismatch is its own kind of integration pain, covered in JSON Schema Validation: Syntax Errors vs Schema Errors, and it's common for that mismatch to be a timestamp field that changed shape between versions.

A quick checklist for the next integration

A few habits catch most of this before it ships. Always check whether a numeric timestamp is 10 digits (seconds) or 13 digits (milliseconds) before trusting it; never assume based on which system you expected to receive it from. Treat every date-time string without an explicit offset or Zsuffix as ambiguous, and confirm the sending system's convention rather than guessing UTC or local. When a third-party API's docs don't state the timestamp format explicitly, test it against a known value rather than assuming it matches the last API you integrated. And when a bug report says a date is “off,” check seconds-versus-milliseconds and time zone interpretation before looking anywhere else; between them, they account for most timestamp bugs that make it past code review. For everything else, the rest of the toolkit covers the other formats that tend to show up in the same integrations: GUIDs, JSON payloads, and the encoded or encrypted values that travel alongside the timestamps.

← Back to KeyForge