Try the Timestamp Converter

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

Share:
ISO 8601 and Date Handling Mistakes: The Bugs That Surface Months Later

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 TIMESTAMP type, 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 (Date uses 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

  1. Enter a Unix timestamp β†’ convert to ISO 8601 with timezone offset
  2. Enter a date/time β†’ convert to Unix timestamp for storage or API submission
  3. Verify timezone context β€” always check whether the output is UTC or local time

Debug use cases:

  • JWT exp claim: paste the Unix timestamp to see exactly when a token expires
  • Database created_at columns: 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.

Share:
Try the related tool:
Open Timestamp Converter

More Timestamp Converter articles