ISO 8601 and Date Handling Mistakes: The Bugs That Surface Months Later
"06/07/2024" means different dates in the US and UK. ISO 8601 solves this unambiguously. Here's the standard, common date handling mistakes (adding months with timedelta, DST in date arithmetic), storing UTC in databases, and the MySQL 2038 timestamp problem.
By sadiqbd Β· June 9, 2026
ISO 8601 solved date formatting confusion in 1988 β and the web still hasn't fully adopted it
"06/07/2024" β is that the 6th of July or the 7th of June? The answer depends entirely on where you are. In the US, it's June 7. In the UK and most of Europe, it's July 6. In API responses, logs, and databases, this ambiguity has caused real data corruption. ISO 8601 solves the problem entirely: 2024-07-06 means July 6, unambiguously, to everyone.
Understanding the standard, the ways developers get date handling wrong, and the specific pitfalls in date arithmetic prevents the class of bugs that surface only months later when a date crosses a year or DST boundary.
ISO 8601: the standard that should be universal
ISO 8601 defines unambiguous date and time representations:
Date: YYYY-MM-DD β e.g., 2024-07-06
Time: THH:MM:SS β e.g., T14:30:00
Combined (datetime): 2024-07-06T14:30:00
With timezone offset: 2024-07-06T14:30:00+01:00 (1 hour ahead of UTC)
UTC: 2024-07-06T14:30:00Z (Z = UTC)
Milliseconds: 2024-07-06T14:30:00.500Z (decimal fraction of seconds)
The ordering principle: largest to smallest unit, left to right. Year β Month β Day β Hour β Minute β Second. This makes ISO 8601 dates lexicographically sortable β alphabetical sorting of ISO 8601 strings produces chronological order.
Why APIs shouldn't use anything else
Several date formats are commonly found in API responses:
RFC 2822: Mon, 07 Oct 2024 13:28:00 +0100 β used in email headers, RSS feeds, HTTP Date headers. Readable but verbose, and the month abbreviations are locale-dependent.
Unix timestamp: 1728304080 β compact, unambiguous, but not human-readable.
US format: 10/07/2024 or October 7, 2024 β ambiguous for international audiences.
ISO 8601 is the correct choice for APIs: unambiguous, parseable by every modern language without a library, lexicographically sortable, and human-readable.
The common mistake: returning dates as "2024-07-06" (date-only, no timezone context) when you mean a specific moment in time. A date without time and timezone is inherently ambiguous β "July 6, 2024" happens at different UTC instants depending on where the observer is.
Common date handling mistakes in code
Mistake 1: Adding months by multiplying days
# Wrong: what's "one month from now"?
one_month_later = datetime.now() + timedelta(days=30)
# January 31 + 30 days = March 1 or 2 (not February 28/29)
# October 31 + 30 days = November 30 (not October 31)
Month lengths are variable: 28, 29, 30, or 31 days. Adding timedelta(days=30) doesn't add "one month." Use a proper date library:
from dateutil.relativedelta import relativedelta
one_month_later = datetime.now() + relativedelta(months=1)
# January 31 + 1 month = February 28 (or 29 in leap year)
# October 31 + 1 month = November 30 (last day of November)
Mistake 2: Ignoring DST in local time arithmetic
# Wrong: assuming all days have 24 hours
tomorrow = local_datetime + timedelta(hours=24)
# On DST transition day: 24 hours lands at wrong local time
On the day clocks spring forward, the "same time tomorrow" is 23 hours away in UTC. On the day clocks fall back, it's 25 hours away. Use local-timezone-aware arithmetic:
from zoneinfo import ZoneInfo
from datetime import datetime, timedelta
london_tz = ZoneInfo('Europe/London')
today = datetime(2024, 3, 31, 10, 0, tzinfo=london_tz) # Before spring forward
tomorrow = today + timedelta(days=1) # Correctly adds calendar day
Mistake 3: Storing local time in the database
-- Wrong: stores local time, loses timezone context
INSERT INTO events (time) VALUES ('2024-03-31 10:00:00');
-- Better: store UTC explicitly
INSERT INTO events (time) VALUES ('2024-03-31 09:00:00Z');
-- Best for PostgreSQL: use TIMESTAMPTZ which handles this automatically
INSERT INTO events (time) VALUES ('2024-03-31 10:00:00+01:00');
-- PostgreSQL converts to UTC for storage, back to local on retrieval
Mistake 4: Comparing dates without normalising timezone
// Wrong: comparing Date objects from different sources
const dateA = new Date('2024-07-06T14:30:00'); // Local time
const dateB = new Date('2024-07-06T14:30:00Z'); // UTC
dateA.getTime() === dateB.getTime() // false if in UTC+1 or any non-UTC timezone
Always normalise to UTC before comparison.
The year 2038 problem
On January 19, 2038 at 03:14:07 UTC, Unix timestamps stored as 32-bit signed integers will overflow. The value will wrap from 2,147,483,647 to -2,147,483,648, which represents December 13, 1901.
What's still affected:
- Legacy C code using 32-bit
time_t - Older embedded systems with 32-bit clocks
- Some database field types (MySQL's
TIMESTAMPtype, which uses 32-bit internally, only supports dates up to 2038-01-19) - Older file systems with 32-bit timestamps
What's not affected:
- Python (arbitrary precision integers; datetime supports dates through year 9999)
- JavaScript (
Dateuses 64-bit floats for millisecond timestamps) - PostgreSQL
TIMESTAMPTZ(uses 64-bit integers) - Most modern software using 64-bit
time_t
MySQL's TIMESTAMP type is a common gotcha: DATETIME supports up to year 9999, but TIMESTAMP is limited to 2038. For any date field that might store future dates beyond 2038 (insurance, financial, legal), use DATETIME in MySQL.
How to use the Timestamp Converter on sadiqbd.com
- Enter a Unix timestamp β convert to ISO 8601 with timezone offset
- Enter a date/time β convert to Unix timestamp for storage or API submission
- Verify timezone context β always check whether the output is UTC or local time
Debug use cases:
- JWT
expclaim: paste the Unix timestamp to see exactly when a token expires - Database
created_atcolumns: convert raw Unix values to readable dates - API request/response debugging: verify timestamps are in the right format and timezone
Frequently Asked Questions
Should APIs always return ISO 8601?
For dates and datetimes: yes. ISO 8601 with explicit UTC offset (or Z suffix) is unambiguous, parseable in every language without a library, and sortable as a string. The only reasonable exception is Unix timestamps for high-volume machine-to-machine communication where parsing overhead matters.
What date library should I use in JavaScript?
The built-in Date object has significant quirks (month is 0-indexed, no timezone handling). For production code: date-fns (tree-shakeable, functional) or Temporal (the forthcoming Temporal API, now Stage 3 in TC39 and available via polyfill). Avoid moment.js β it's in maintenance mode.
Is the Timestamp Converter free? Yes β completely free, no sign-up required.
ISO 8601 isn't just a preference β it's the format that eliminates the entire class of date ambiguity bugs. Using it consistently in APIs, databases, and logs produces systems where dates mean the same thing everywhere.
Try the Timestamp Converter free at sadiqbd.com β convert between Unix timestamps and ISO 8601 dates with timezone handling instantly.