Date and time formats — working reference
On this page
- Common date and time formats
- ISO 8601 — the format you should default to
- RFC 3339 — ISO 8601’s stricter cousin
- RFC 2822 — the email and HTTP date format
- Unix timestamps — the developer favourite
- US format — MM/DD/YYYY
- EU / UK format — DD/MM/YYYY and DD.MM.YYYY
- SQL DATETIME / TIMESTAMP
- Cookie expiry — RFC 1123
- Programming-language quirks
- Format tokens across languages
- Token pitfalls
- What format to actually use
- FAQ
- What’s the difference between RFC 3339 and ISO 8601?
- What is the standard timestamp format?
- How do I write a date in ISO format?
- What’s the difference between %m and %M in strftime?
- Why does my date show next year’s number in late December?
- How do I format the current time as ISO 8601?
- What does the Z at the end of a timestamp mean?
- Tools
- TL;DR
There are at least nine date formats in common use, three of them ambiguous, and most software gets at least one of them wrong on edge cases. This page is the reference I keep rebuilding by hand — every format, with an example, the systems that emit it, and the gotchas.
Short answer: use ISO 8601 (2026-04-28T14:30:00Z) for anything a machine reads or stores, and a locale-aware formatter for anything a person reads.
Common date and time formats
| Format | Example | Used for |
|---|---|---|
| ISO 8601 / RFC 3339, UTC | 2026-04-28T14:30:00Z | APIs, JSON, logs |
| RFC 3339 with offset | 2026-04-28T10:30:00-04:00 | The same moment in Eastern time |
| ISO 8601 date only | 2026-04-28 | Dates with no time |
| RFC 5322 (email) | Tue, 28 Apr 2026 14:30:00 +0000 | Email headers |
| HTTP-date | Tue, 28 Apr 2026 14:30:00 GMT | HTTP headers, cookies |
| Unix timestamp | 1777386600 | Code and databases (see the epoch converter) |
| SQL DATETIME | 2026-04-28 14:30:00 | MySQL, SQL Server, SQLite |
Each row below has its own section with the details and pitfalls.
ISO 8601 — the format you should default to
2026-04-28T14:30:00Z
2026-04-28T14:30:00+09:00
2026-04-28T14:30:00.123456Z
The international standard. Year-month-day, time, then a timezone
suffix (Z for UTC or ±HH:MM for an offset). Sortable as plain
strings — alphabetical sort matches chronological sort. Unambiguous
across locales.
Use it as your default. Every modern API, log format, and programming language supports it.
Common variants:
- Date only:
2026-04-28— no time component, treated as “the whole day” or sometimes as midnight UTC depending on parser. - Calendar week:
2026-W18-2— year, ISO week, day. Tuesday of week 18, 2026. Used in scheduling and supply-chain systems. - Ordinal day:
2026-118— year and day-of-year. Astronomy, some business systems. - No timezone:
2026-04-28T14:30:00— local time. Avoid. This is the source of half of all timezone bugs. Always include the suffix.
Gotcha: the T separator is required by the spec, but many
systems accept a space (2026-04-28 14:30:00Z) for human readability.
Don’t write parsers that depend on this — the spec requires T.
RFC 3339 — ISO 8601’s stricter cousin
2026-04-28T14:30:00Z
2026-04-28T14:30:00.123-05:00
A profile of ISO 8601 used in JSON / HTTP / many APIs. It is stricter
than ISO 8601 in that the timezone is required (no naïve datetimes) and
the format is fixed (four-digit year, two-digit fields). RFC 3339 also
notes that applications may use a space instead of T for readability,
but the T form is the one to emit.
Practically, if you emit RFC 3339 with a T separator, you also emit
valid ISO 8601. If you emit ISO 8601, you might not be RFC 3339
compliant.
Use it for: JSON APIs, OpenAPI specs, log files. JavaScript’s
new Date().toISOString() produces RFC 3339 output.
RFC 2822 — the email and HTTP date format
Tue, 28 Apr 2026 14:30:00 +0000
Tue, 28 Apr 2026 14:30:00 GMT
Originally for email headers (Date:). RFC 2822 has since been replaced
by RFC 5322, which keeps the same date format. HTTP headers (Date:,
Last-Modified:, Expires:) use a stricter fixed-length form,
IMF-fixdate (the preferred form of HTTP-date), defined in RFC 9110 as a
subset of the RFC 5322 format.
The day-of-week prefix is derivable from the date — including it is redundant, and the spec makes it optional. If you include it, it must match the date.
Gotcha: named timezones (GMT, EST, PST) are obsolete syntax in
RFC 5322 (formerly RFC 2822). Senders must use numeric offsets (+0000,
-0500), though receivers must still accept the old names. Some parsers
reject named timezones; some accept them but guess wrong (does “EST”
mean Eastern Standard Time or Australian Eastern Standard Time?).
Use it for: email headers, HTTP headers, legacy systems that require it. Avoid for new APIs.
Unix timestamps — the developer favourite
1777386600 (seconds)
1777386600123 (milliseconds)
1777386600123456 (microseconds)
1777386600123456789 (nanoseconds)
Seconds since 1970-01-01T00:00:00Z. The most compact, sortable, and unambiguous representation. No timezone needed (it’s always UTC).
The big four resolutions:
- Seconds: 10 digits today. Used by Unix utilities, JWT (
exp,iat,nbf), most file systems. - Milliseconds: 13 digits. Used by JavaScript’s
Date.now(), most databaseBIGINTtimestamp columns. - Microseconds: 16 digits. Some databases, profiling tools.
- Nanoseconds: 19 digits. Go’s
time.UnixNano(), kernel-level timing.
Spotting which one you have: count digits. If a “timestamp” has 13 digits, it’s milliseconds. If 16, microseconds. The conversion is trivial — just divide by 1000.
The epoch converter auto-detects which resolution you’ve pasted.
Gotcha 1: Y2038. Signed 32-bit Unix timestamps overflow on 2038-01-19. Most modern systems use 64-bit, but check yours if you have any expiry dates after January 19, 2038.
Gotcha 2: leap seconds. Strict POSIX time ignores leap seconds — the timestamp repeats a second on insertion. Some providers, such as Google’s public time service (“leap smear”), spread the extra second across many hours instead, but plain systems do not. If you compare timestamps across leap-second boundaries on systems that handle it differently, you can be off by 1 second.
US format — MM/DD/YYYY
4/28/2026
04/28/2026
4-28-2026
US default. Used in form inputs, news copy, banking statements.
Gotcha: ambiguous against EU format. 04/05/2026 is April 5 (US)
or May 4 (EU). Without context you can’t tell. Never use slash-format
dates in APIs or logs. Reserve them for human-formatted output where
the locale is known.
EU / UK format — DD/MM/YYYY and DD.MM.YYYY
28/04/2026 (UK, France, Australia)
28.04.2026 (Germany, Russia)
28-04-2026 (Netherlands)
Day-month-year. Aligns with how dates are spoken in most European languages.
Gotcha: same ambiguity as US format. Reserve for human display.
SQL DATETIME / TIMESTAMP
2026-04-28 14:30:00 (MySQL DATETIME)
2026-04-28 14:30:00.123456+00 (Postgres TIMESTAMPTZ)
2026-04-28 14:30:00.000 (SQL Server)
Most relational databases accept ISO 8601 input but emit a locale-specific or vendor-specific format on display.
Critical distinction: TIMESTAMPTZ (timestamp with time zone) in
Postgres is timezone-aware and stored as UTC; plain TIMESTAMP
(which means timestamp without time zone) is naïve and stored
literally. MySQL DATETIME is always naïve; MySQL TIMESTAMP is
auto-converted to UTC on insert and back to the connection’s timezone
on read. Know which one your column is.
Use it for: database columns. Convert to ISO 8601 at the API boundary.
Cookie expiry — RFC 1123
Tue, 28 Apr 2026 14:30:00 GMT
This is the fixed-length HTTP-date form that RFC 6265 requires for the
Expires attribute of Set-Cookie, with GMT mandatory. Browsers
parse cookie dates leniently (RFC 6265 section 5.1.1), but always emit
exactly this form.
Programming-language quirks
| Language | Common output | Notes |
|---|---|---|
| JavaScript | new Date().toISOString() | RFC 3339 with millisecond precision |
| Python | datetime.isoformat() | ISO 8601, optional timezone |
| Java | Instant.toString() | RFC 3339, nanosecond precision |
| Go | time.Now().Format(time.RFC3339) | Strict RFC 3339 |
| Ruby | Time.now.iso8601 | ISO 8601, optional fractional seconds |
| C# | DateTime.UtcNow.ToString("o") | ”round-trip” format ≈ RFC 3339 |
| Rust | chrono::Utc::now().to_rfc3339() | RFC 3339 |
| SQL | varies — see above | varies |
The “default toString” of each language often emits something different
— Date.toString() in JavaScript gives a human-locale form
(Tue Apr 28 2026 ...) that is not ISO 8601 and not parseable
back to a Date in all engines. Always use the explicit ISO method.
Format tokens across languages
The same moment, Tuesday 2026-04-28 at 14:30:05, written with each library’s pattern letters. Python and C use strftime codes. Java and Kotlin use DateTimeFormatter.ofPattern. JavaScript has no pattern string in the language itself (Intl.DateTimeFormat takes options such as { dateStyle: "long" }), so the JavaScript column shows date-fns, with moment and Day.js alongside.
| Part | Output | strftime | Java / Kotlin | date-fns | .NET | moment / Day.js |
|---|---|---|---|---|---|---|
| 4-digit year | 2026 | %Y | yyyy (or uuuu) | yyyy | yyyy | YYYY |
| Month, 2 digits | 04 | %m | MM | MM | MM | MM |
| Day of month | 28 | %d | dd | dd | dd | DD |
| Hour, 24-hour | 14 | %H | HH | HH | HH | HH |
| Hour, 12-hour | 02 | %I | hh | hh | hh | hh |
| Minute | 30 | %M | mm | mm | mm | mm |
| Second | 05 | %S | ss | ss | ss | ss |
| AM/PM | PM | %p | a | a | tt | A |
| Short month name | Apr | %b | MMM | MMM | MMM | MMM |
| Short weekday | Tue | %a | EEE | EEE | ddd | ddd |
Month and weekday names, and %p, follow the locale. The outputs above are for English.
Token pitfalls
YYYYis not always the year. In Java and date-fns,Yis the week-based year, which differs from the calendar year for a few days around New Year. Monday 2024-12-30 formats as2025withYYYYand2024withyyyy. date-fns refusesYYYYandDDunless you opt in, and throws aRangeErrorpointing you toyyyyanddd. In .NET,YandDaren’t format letters at all, soYYYYcomes out as the literal text “YYYY”. Only moment and Day.js useYYYYfor the calendar year.DDchanges meaning. In moment and Day.js it’s the day of the month. In Java and date-fns,Dis the day of the year, soDDon April 28 gives118, not28.mmis minutes,MMis months. This holds in Java, date-fns, .NET and moment. Instrftimeit’s%Mfor minutes and%mfor months, the other way round.hhis the 12-hour clock. Without an AM/PM token,hh:mmshows 2:30 in the afternoon as02:30. UseHH(or%H) for 24-hour time.- .NET treats
/and:as culture separators.dd/MM/yyyycan print dots or dashes in some cultures. PassCultureInfo.InvariantCulture, or quote the separator (dd'/'MM'/'yyyy), when the output must be exact.
What format to actually use
A short decision tree:
- API request/response: ISO 8601 / RFC 3339, UTC, millisecond precision. Fight for this in code review.
- Log files: ISO 8601 with timezone. Sortable as text, debuggable from anywhere.
- Database: language-native timestamp type, configured to UTC. Convert on the way out.
- Cookies: RFC 1123 date form, as required by RFC 6265.
- Email or HTTP headers: RFC 5322 for email dates, HTTP-date (RFC 9110) for HTTP headers.
- Filename or URL: ISO 8601 date-only, no spaces or colons.
2026-04-28-myreport.jsonsorts and reads cleanly. - Human-facing display: format for the user’s locale (
Intl.DateTimeFormatin JavaScript,babel.datesin Python). Never expose ISO 8601 directly to an end user — they want “April 28, 2026,” not “2026-04-28T14:30:00Z.”
FAQ
What’s the difference between RFC 3339 and ISO 8601?
RFC 3339 is a stricter profile of ISO 8601 for internet timestamps. It needs a full date, a time and an offset, such as 2026-04-28T10:30:00-04:00. ISO 8601 also allows forms like week dates and dates without a time. A valid RFC 3339 timestamp that uses the T separator is also valid ISO 8601. See RFC 3339 above.
What is the standard timestamp format?
ISO 8601, usually in its RFC 3339 form: 2026-04-28T14:30:00Z. It sorts as text, includes the time zone, and every major language can parse it.
How do I write a date in ISO format?
Year, month and day, largest first, with hyphens: 2026-04-28. Add a time after a T and end with Z for UTC or an offset like -04:00.
What’s the difference between %m and %M in strftime?
%m is the month as two digits (04) and %M is the minute (30). Java, date-fns, .NET and moment use MM for the month and mm for the minute.
Why does my date show next year’s number in late December?
The pattern almost certainly uses YYYY, the week-based year, instead of yyyy. Switch to yyyy (or uuuu in Java) and the year matches the calendar again.
How do I format the current time as ISO 8601?
In JavaScript, new Date().toISOString() gives UTC with milliseconds, such as 2026-04-28T14:30:00.000Z. In Python, datetime.now(timezone.utc).isoformat(timespec="seconds") gives 2026-04-28T14:30:00+00:00. Both are valid ISO 8601 and RFC 3339.
What does the Z at the end of a timestamp mean?
Z means UTC, an offset of zero. 2026-04-28T14:30:00Z and 2026-04-28T14:30:00+00:00 are the same moment.
Tools
- Epoch converter — auto-detects which resolution your timestamp is in (sec/ms/µs/ns/ISO/RFC) and shows every other format.
- Current Unix Epoch (live) — the current Unix timestamp, ticking live.
- Days-between calculator — for date math without DST drift.
date -u +"%Y-%m-%dT%H:%M:%SZ"(POSIX shell) — emit current ISO 8601 UTC.
TL;DR
- Default to ISO 8601 (or RFC 3339 if you want to be strict).
- Always include a timezone. Always.
- For storage: 64-bit Unix milliseconds is the simplest; ISO 8601 is the most readable. Both are fine.
- For display: convert with
Intl.DateTimeFormator your language’s locale-aware formatter. - Avoid: naïve datetimes, US/EU short forms in APIs, anything with named (rather than offset) timezones.
When in doubt, paste the value into the epoch converter to see what it actually decodes as.