Date Format Reference: ISO 8601, RFC 3339 and Every strftime Code

Dates are the most reliably mishandled data type in software. They look simple, they carry hidden regional assumptions, and a wrong one usually fails silently rather than loudly. This page collects the formats worth knowing, every strftime code in one table, and the specific traps that turn a working system into a subtly wrong one.

If you only take one thing

Use 2026-08-02T14:30:00Z — ISO 8601 with an explicit UTC offset — for anything stored, logged or transmitted. It sorts correctly as a plain string, cannot be misread, and every language parses it. Reformat for humans only at the moment of display.

ISO 8601: the standard worth knowing properly

ISO 8601 orders components from largest to smallest, which produces its most useful property almost by accident: chronological order and alphabetical order are the same. Sorting ISO date strings as text sorts them by time. No parsing required, which is why log files and filenames use it.

// Date components "2026" // year "2026-08" // year and month "2026-08-02" // calendar date "2026-W31" // ISO week "2026-W31-7" // ISO week and weekday (Sunday) "2026-214" // ordinal date — the 214th day // Time components "14:30" // hours and minutes "14:30:00" // with seconds "14:30:00.123" // with milliseconds // Combined, with a timezone "2026-08-02T14:30:00Z" // UTC "2026-08-02T14:30:00+05:30" // India Standard Time "2026-08-02T14:30:00-08:00" // US Pacific in winter "2026-08-02T14:30:00" // ⚠ no offset — meaning is unknown // Durations and intervals "P3Y6M4DT12H30M5S" // 3y 6mo 4d 12h 30m 5s "PT30M" // 30 minutes "2026-08-02/2026-08-09" // an interval

⚠️ A timestamp with no offset means nothing

2026-08-02T14:30:00 is 14:30 somewhere, and the string does not say where. Worse, parsers disagree about what to assume: some treat it as UTC, some as local time. The same string can therefore represent moments hours apart depending on which library reads it. Always include Z or an explicit offset.

ISO 8601 versus RFC 3339

These are frequently spoken of as the same thing. They overlap heavily but are not identical, and the difference matters when writing a parser or an API specification.

FormISO 8601RFC 3339
2026-08-02T14:30:00ZValidValid
20260802T143000Z (no separators)ValidInvalid
2026-W31-7 (week date)ValidInvalid
2026-214 (ordinal date)ValidInvalid
2026-08-02 (date only)ValidInvalid — time is required
P3Y6M4D (duration)ValidInvalid
2026-08-02t14:30:00z (lowercase)InvalidValid (discouraged)
2026-08-02 14:30:00Z (space)InvalidValid by extension

The practical summary: RFC 3339 is the strict subset you want for APIs, because it removes the exotic forms and guarantees a fully-specified instant. ISO 8601 is the broader standard covering weeks, durations and intervals. If a specification says "ISO 8601", a careful implementer should ask which subset is actually meant.

The complete strftime reference

The strftime format codes originate in C and are used almost unchanged by Python, PHP, Ruby, Perl, and the Unix date command. Learn them once and they work nearly everywhere.

Date codes

CodeMeaningExample
%YYear, four digits2026
%yYear, two digits26
%CCentury20
%GISO week-numbering year2026
%mMonth, zero-padded08
%BMonth, full nameAugust
%b or %hMonth, abbreviatedAug
%dDay of month, zero-padded02
%eDay of month, space-padded" 2"
%jDay of year214
%AWeekday, full nameSunday
%aWeekday, abbreviatedSun
%uWeekday number, Monday = 17
%wWeekday number, Sunday = 00
%VISO week number (01–53)31
%UWeek of year, Sunday first31
%WWeek of year, Monday first31

Time codes

CodeMeaningExample
%HHour, 24-hour, zero-padded14
%IHour, 12-hour, zero-padded02
%pAM or PMPM
%MMinute30
%SSecond00
%fMicroseconds (Python)123456
%zUTC offset+0530
%ZTimezone nameIST
%sUnix timestamp1785681000

Composite shortcuts

CodeEquivalent toExample output
%F%Y-%m-%d2026-08-02
%T%H:%M:%S14:30:00
%R%H:%M14:30
%D%m/%d/%y08/02/26
%cLocale date and timeSun Aug 2 14:30:00 2026
%xLocale date08/02/2026
%XLocale time14:30:00
%%A literal percent sign%

🚨 The %G and %Y trap

Using %Y-W%V instead of %G-W%V produces a wrong year at every year boundary. On 1 January 2027, %Y gives 2027 but %V gives week 53 — of 2026. The result, 2027-W53, is a week that does not exist. This bug surfaces once a year, in early January, in reporting code that ran fine for eleven months.

Common format strings

Resultstrftime
2026-08-02%Y-%m-%d
2026-08-02 14:30:00%Y-%m-%d %H:%M:%S
2026-08-02T14:30:00Z%Y-%m-%dT%H:%M:%SZ
02/08/2026%d/%m/%Y
08/02/2026%m/%d/%Y
2 August 2026%-d %B %Y
August 2, 2026%B %-d, %Y
Sun, 02 Aug 2026%a, %d %b %Y
2:30 PM%-I:%M %p
20260802_143000 (filenames)%Y%m%d_%H%M%S
2026-W31%G-W%V

The - flag (as in %-d) strips the leading zero on Linux and macOS. Windows uses %#d instead — a genuine portability difference that catches people moving scripts between platforms.

The ambiguity problem

Consider 03/04/2026. It is a valid date in at least two reading conventions, and they refer to different days a month apart:

ConventionUsed inReads as
MM/DD/YYYYUnited States, Philippines3 April 2026
DD/MM/YYYYMost of the world4 March 2026
YYYY/MM/DDChina, Japan, Korea, IranNot applicable to this string

What makes this dangerous is not that it fails — it is that it succeeds incorrectly. A spreadsheet import, a CSV parse or a form submission will happily accept the wrong day and produce no error. The mistake surfaces weeks later when something is scheduled on the wrong date.

✅ Formats that cannot be misread

  • 2026-08-02 — ISO. Unambiguous everywhere. Use for data.
  • 2 Aug 2026 — a named month cannot be confused with a day number. Use for display.
  • Aug 2, 2026 — same, in American order.

Any format with three numbers separated by slashes is ambiguous. If your interface must show one, label the expected order explicitly next to the field.

Formatting in each language

// JavaScript — Intl is the correct tool const d = new Date('2026-08-02T14:30:00Z'); d.toISOString(); // "2026-08-02T14:30:00.000Z" new Intl.DateTimeFormat('en-GB', { dateStyle: 'long', timeStyle: 'short', timeZone: 'Asia/Kolkata' }).format(d); // "2 August 2026 at 20:00" // Relative time, localised, without a library new Intl.RelativeTimeFormat('en', { numeric: 'auto' }) .format(-3, 'day'); // "3 days ago"
# Python from datetime import datetime, timezone d = datetime(2026, 8, 2, 14, 30, tzinfo=timezone.utc) d.isoformat() # '2026-08-02T14:30:00+00:00' d.strftime('%Y-%m-%d %H:%M') # '2026-08-02 14:30' # Parsing — fromisoformat handles Z from Python 3.11 datetime.fromisoformat('2026-08-02T14:30:00Z') datetime.strptime('02/08/2026', '%d/%m/%Y')
-- SQL -- PostgreSQL SELECT TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS'); SELECT NOW() AT TIME ZONE 'UTC'; -- MySQL — different code letters entirely SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'); -- note %i for minutes, not %M — %M is the month name in MySQL

⚠️ MySQL's format codes are not strftime

MySQL uses %i for minutes and %M for the full month name — the reverse of strftime, where %M is minutes and %B is the month name. Writing '%Y-%m-%d %H:%M' in MySQL produces 2026-08-02 14:August. It parses, it runs, and it is wrong.

Database column types

DatabaseTypeStores timezone?Notes
PostgreSQLTIMESTAMPTZYesStores UTC, converts on read. Use this.
PostgreSQLTIMESTAMPNoA wall-clock reading with no context
PostgreSQLDATENoCorrect for birthdays and anniversaries
MySQLTIMESTAMPConvertsUTC internally; range ends in 2038
MySQLDATETIMENoWider range, no conversion
SQLiteTEXTIf you include itNo date type — store ISO 8601 strings
SQL ServerDATETIMEOFFSETYesThe one to use

One nuance worth stating: a date without a time is sometimes correct. A birthday is not an instant — someone born on 2 August was born on 2 August regardless of which timezone you view it from. Storing it as a UTC timestamp introduces a bug where the date shifts by a day for users west of Greenwich. Use a plain DATE for calendar dates and a timestamp for events.

The recurring pitfalls

  1. Two-digit years. Is %y = 30 the year 1930 or 2030? Every library has a cutoff and they do not all agree. Use four digits.
  2. Assuming a day has 86,400 seconds. On DST transition days it has 23 or 25 hours. Adding 86,400 to a timestamp does not reliably produce "the same time tomorrow".
  3. Timezone abbreviations. "CST" means Central Standard Time, China Standard Time or Cuba Standard Time. Use IANA identifiers like America/Chicago.
  4. Local time at midnight. Storing "2026-08-02 00:00:00" in local time and reading it in UTC can shift it to the previous day. This is the single most common date bug in reporting.
  5. Sorting formatted dates. "12/01/2026" sorts before "02/01/2027" as text. Only ISO format sorts correctly as a string.
  6. Leap years. Divisible by 4, except centuries, unless divisible by 400. 2000 was a leap year; 1900 and 2100 are not.
  7. The 29 February anniversary. What is "one year after 2024-02-29"? Libraries disagree between 28 February and 1 March. Decide explicitly.

Converting a Unix timestamp?

Paste a timestamp and get a readable date in any timezone, or go the other way — instantly, in your browser.

Open the Timestamp Converter →

The working rules

  • Store and transmit as 2026-08-02T14:30:00Z. Always include the offset.
  • Store UTC, display local. Convert at the last possible moment.
  • Use IANA timezone names, never three-letter abbreviations.
  • Never use slash-separated numeric dates for data. They are silently ambiguous.
  • Pair %G with %V, never %Y, or January will break.
  • Use a plain DATE type for birthdays and calendar dates that have no instant attached.
  • Never write your own date parser. Every edge case listed here is already handled by your standard library.

Frequently Asked Questions

What is the difference between ISO 8601 and RFC 3339?

RFC 3339 is a stricter profile of ISO 8601 built for internet protocols. ISO 8601 permits forms RFC 3339 forbids — omitting separators (20260802), week dates (2026-W31-7), durations, and ordinal dates. RFC 3339 requires the full YYYY-MM-DDTHH:MM:SSZ shape with an explicit offset. Almost every valid RFC 3339 timestamp is valid ISO 8601, but not the reverse.

What does the T mean in a date like 2026-08-02T14:30:00Z?

T is a literal separator marking where the date ends and the time begins, required so parsers do not have to guess. The trailing Z means Zulu time, military phonetic for the zero meridian — it declares the timestamp is in UTC with a zero offset. 2026-08-02T14:30:00Z and 2026-08-02T14:30:00+00:00 are identical.

What date format should I use in a database or API?

Store UTC and serialise as ISO 8601 with an explicit offset: 2026-08-02T14:30:00Z. It sorts correctly as plain text, has no regional ambiguity, is parseable by every language's standard library, and is human-readable in a log file. Never store or transmit a date without timezone information.

Why is 03/04/2026 ambiguous?

In the United States it reads as March 4th; in most of the rest of the world as 3rd April. There is no way to tell which was meant from the string alone, and both are plausible dates. Because the ambiguity is silent — no software errors, it just produces the wrong day — this format should never be used for data interchange, only ever for display in a known locale.

What is an ISO week date?

A format like 2026-W31-7 identifying the year, ISO week number and weekday. ISO weeks start on Monday, and week 1 is the week containing the year's first Thursday. This means early January can belong to the previous ISO year — 1 January 2027 falls in ISO week 53 of 2026 — which regularly breaks reporting code that assumes the calendar year and ISO year match.

P

Written by Paras

We build free, browser-based file tools and write the reference material we wish existed when we were looking things up. Spotted an error? Tell us and we will fix it.