Unix Timestamp Conversion: Fix API Timezone Pitfalls
Quick win before we dive in: store epoch in UTC, convert at the edge
Here's the one thing that'll save you hours of debugging at 2 AM: always store and transmit Unix timestamps in UTC, and only convert to local timezones at the presentation layer. That's it. If you take nothing else from this guide, take that. Now let's walk through why this is harder than it sounds—and how to actually pull it off when your API serves users in Tokyo, New York, and Sydney simultaneously.
1. Never Trust the Client's Clock (The Ingestion Pitfall)
Let's say your API receives a payload from a mobile app in Berlin. The client sends order_created: 1700000000. You store it. Later, your analytics team notices a spike in orders that appear to have been placed three minutes in the future. What happened?
The client's clock was drifting. Mobile devices, IoT sensors, and especially embedded hardware are notorious for clock skew. Some drift by seconds, others by full minutes. If your API blindly trusts client-provided Unix epoch timestamps, you're building your data pipeline on quicksand.
The fix
Generate the timestamp server-side at the moment of ingestion. If the client must provide a timestamp (say, for offline-first apps), include it as a separate field like client_reported_at and always pair it with a server-generated server_received_at. That way, you can detect skew during post-processing without corrupting your canonical event timeline.
Here's a concrete example: if a device in Berlin reports 1700000000 (November 14, 2023, 22:13:20 UTC) but your server receives it at 1700000180, that's a 180-second drift. Flag it. Log it. Move on.
2. Store Epoch, Not Local Time Strings (The Storage Pitfall)
This one still haunts me. A legacy API I once inherited stored timestamps as strings like "2023-11-14 22:13:20". No timezone suffix. No offset. Just… a string. The original developer assumed the server was always in UTC. It wasn't. A cloud migration later, the server was now in US/Eastern, and every new record was quietly off by five hours.
String-based timestamps without explicit timezone metadata are a ticking bomb. They work until they don't—and when they break, the damage is silent and cumulative.
The fix
Store everything as a Unix epoch integer—either seconds or milliseconds, but pick one and stick with it across your entire API surface. Document the choice in your API spec. If you're using milliseconds (common in JavaScript ecosystems), make that explicit: created_at_ms: 1700000000000. If seconds, use created_at: 1700000000. Never mix the two in the same payload, because someone will eventually divide by 1000 when they shouldn't—or forget to when they should.
If you absolutely must store human-readable timestamps (for auditing, say), store them alongside the epoch value, not instead of it. And always, always suffix them with an explicit offset: "2023-11-14T22:13:20+00:00".
3. Serialize Consistently in Your JSON Payload (The Serialization Pitfall)
You'd be amazed how many APIs I've seen that return created_at as an epoch integer in one endpoint and as an ISO 8601 string in another. Same API. Same backend. Different developer wrote each endpoint.
Inconsistent serialization forces every API consumer to write defensive parsing logic. That's friction. That's bugs. That's a Slack message at 11 PM asking "why does the timestamp look weird on the orders endpoint?"
The fix
Standardize on one format for your entire API. My recommendation for most use cases: epoch milliseconds as a JSON number. Why milliseconds? Because JavaScript's Date.now() returns milliseconds, and a huge chunk of your API consumers are probably working in JavaScript. You eliminate a conversion step for the majority of your users.
If your domain is more Unix-native (infrastructure tooling, log aggregation), epoch seconds is fine. Just be consistent.
Here's what a clean payload looks like:
{"order_id": "abc123", "created_at": 1700000000000, "updated_at": 1700000123456}
No ambiguity. No parsing guesswork. Every consumer knows exactly what they're getting.
4. Convert at the Edge, Not in the Middle (The Conversion Pitfall)
This is the big one. The temptation is strong: your server knows the user is in Tokyo, so why not just convert the timestamp to JST before sending it in the API response? Seems helpful, right?
It's not. It's a trap.
The moment you start converting epoch timestamps to local timezones server-side, you've coupled your API to a specific presentation concern. Now your API can't be cached globally (because Tokyo users and Sydney users need different responses). Your API becomes harder to test (what timezone is the test runner in?). And every new timezone your product supports requires a backend change.
The fix
Send the raw epoch value. Let the client convert. The client knows the user's timezone better than you do—it has access to the OS locale, the browser's Intl API, and the user's explicit settings.
Take that 1700000000 timestamp. In UTC, it's November 14, 2023 at 22:13:20. In New York (EST, UTC-5), it's 17:13:20. In Tokyo (JST, UTC+9), it's November 15, 2023 at 07:13:20. In Sydney (AEDT, UTC+11), it's November 15, 2023 at 09:13:20. Same number. Four different local representations. Your API sends one number; each client renders it correctly for its context.
This isn't just cleaner architecture—it's also better performance. CDN caching works. Response payloads stay smaller. And your backend stays timezone-agnostic.
5. Handle DST Landmines When You Must Convert Server-Side (The Display Pitfall)
Okay, reality check: sometimes you do need to convert server-side. Maybe you're generating a PDF report. Maybe you're sending a transactional email with a timestamp in the recipient's local time. Fine. But do it carefully.
The number-one mistake I see is hardcoded UTC offsets. Someone writes UTC_OFFSET = -5 for New York and calls it a day. Then daylight saving time kicks in, the offset becomes -4, and suddenly every email says the meeting is an hour earlier than it actually is.
The fix
Always use the IANA timezone database (also known as the Olson database) through a well-maintained library like pytz in Python, moment-timezone in JavaScript, or java.time.ZonedDateTime in Java. These libraries handle DST transitions automatically because they're backed by the actual timezone rules published by the IANA.
For example, if you're converting 1700000000 to America/New_York, the library knows that on November 14, 2023, New York is in EST (UTC-5) because DST ended on November 5. It returns 17:13:20. No manual offset math. No surprises when the clocks change.
And please—never, ever compute offsets by doing timestamp + (hours * 3600). That's how you get bugs that only manifest two weeks per year.
6. Test with Epoch Boundary Cases (The QA Pitfall)
Most developers test their timestamp handling with Date.now() or time(). That tests the happy path. It doesn't test the edge cases that will actually bite you in production.
Here are the timestamps you should be running through your conversion logic before you ship:
- 1700000000 (November 14, 2023, 22:13:20 UTC) — a normal recent timestamp, good for sanity checks.
- 1697259600 (October 14, 2023, 06:00:00 UTC) — falls during DST in both the US and Australia, so offsets are non-standard.
- 1700006400 — exactly 6400 seconds after our reference point. Good for testing sub-hour precision.
- 0 (January 1, 1970, 00:00:00 UTC) — the epoch itself. Some libraries choke on this.
- negative values — pre-1970 timestamps. If your API ever deals with historical dates, make sure your converter handles them.
The fix
Build a small test suite that runs these values through your conversion pipeline and verifies the output for at least three timezones: one in the Western Hemisphere (America/New_York), one in the Eastern Hemisphere (Asia/Tokyo), and one in the Southern Hemisphere (Australia/Sydney). The Southern Hemisphere is important because its DST schedule is opposite to the Northern Hemisphere's—bugs that hide during Northern summer often reveal themselves during Southern winter.
If your converter produces the correct local time for all five test values across all three timezones, you're in good shape. If not, you've found a bug before your users did.
Wrapping up the workflow
Let's recap the end-to-end flow we've built: your API ingests data with server-generated epoch timestamps. It stores them as integers—seconds or milliseconds, your call, but consistent. It serializes them as raw numbers in JSON responses. Clients convert to local time at the presentation layer. And when server-side conversion is unavoidable, you use IANA-backed libraries and test against DST edge cases.
The beauty of this approach is that your API becomes timezone-agnostic by default. It speaks one language—Unix epoch time—and lets each consumer translate as needed. No cached responses invalidated by geography. No silent five-hour drift after a cloud migration. No 2 AM debugging sessions trying to figure out why Tokyo users see yesterday's date.
Unix epoch time is timezone-neutral by design. Respect that design, and your API payloads will be cleaner, faster, and far less buggy. Ignore it, and you'll learn the hard way why timezones are the final boss of software engineering.
Frequently Asked Questions
Does a Unix timestamp have a timezone?
No, a Unix timestamp represents the number of seconds or milliseconds that have elapsed since January 1, 1970, at 00:00:00 Coordinated Universal Time (UTC). It is inherently timezone-agnostic, meaning it represents a single, absolute moment in time regardless of the user's geographic location.
Should I use Unix epoch time or ISO 8601 for API payloads?
Both are acceptable, but ISO 8601 strings are generally preferred for API payloads because they are human-readable and can explicitly include timezone offsets. However, Unix epoch time is excellent for compact storage, high-performance calculations, and avoiding timezone parsing ambiguities across different systems.
How do I convert a Unix timestamp to a specific timezone in my API?
To convert an epoch timestamp to a specific timezone, first parse the Unix timestamp into a standard datetime object in UTC. Then, use your programming language's built-in datetime library to apply the target timezone offset, such as 'America/New_York' or 'Asia/Tokyo', before returning the API response.
What is the best practice for handling timezones in REST API responses?
The best practice is to store and transmit all API datetime values in UTC, either as a Unix epoch integer or an ISO 8601 string with a 'Z' suffix. Let the client-side application handle the conversion from UTC to the user's local timezone for display purposes to ensure consistency across your backend services.
How do I convert epoch milliseconds to a human-readable date across timezones?
You can use online Unix timestamp converters or programmatic libraries to translate epoch milliseconds into a formatted date string. Simply input the timestamp, select your desired target timezone, and the tool will output the corresponding local time without needing to manually calculate offsets.
Why does my API return the wrong time for users in different timezones?
This usually happens when the server applies its local timezone to a Unix timestamp before sending it, or the client misinterprets the UTC timestamp as local time. Always ensure your backend parses epoch time strictly as UTC and your frontend explicitly converts it to the user's local timezone.
How to handle Daylight Saving Time (DST) when converting Unix timestamps?
Unix timestamps automatically account for Daylight Saving Time because they are based on absolute UTC seconds. When converting the timestamp to a local timezone, rely on updated timezone databases (like the IANA Time Zone Database) in your programming language to apply the correct DST offsets automatically.
How do I send a local timezone offset in a JSON API payload?
If you must send local time, include both the local datetime string and the timezone offset in the JSON payload, such as {"time": "2023-10-25T14:30:00", "offset": "-04:00"}. Alternatively, send the Unix epoch timestamp and a separate timezone string field like 'America/New_York' so the client can handle the conversion.
Are Unix timestamps in seconds or milliseconds in API payloads?
It depends on the API design, but modern APIs typically use milliseconds (13-digit epoch) to provide higher precision, while older systems often use seconds (10-digit epoch). Always check the API documentation to verify the expected unit before parsing the timestamp to avoid massive date calculation errors.
How do I standardize multiple timezone inputs into one Unix epoch for my database?
When receiving localized time strings from users, convert them to UTC first using the provided timezone offset, then calculate the Unix timestamp from that UTC datetime. Storing epoch time in your database ensures a standardized, timezone-independent baseline for all your application data.