# Time Format Patterns - Cross-System Comparison ## Overview This document provides a comprehensive comparison of time format patterns across different programming languages and systems. This research will help inform the design of a widely-recognized, human-readable time format. --- ## 1. Unicode LDML (Locale Data Markup Language) **Used by:** ICU, Java, Swift, JavaScript (Intl), and many internationalization libraries ### Key Characteristics - Single letters represent minimal/numeric forms - Repeated letters increase width or change format type - Case-sensitive (different meanings for upper/lower case) - Canonical order matters for skeletons ### Format Tokens | Element | Token | Examples | Notes | |---------|-------|----------|-------| | **Year** | | 4-digit year | `yyyy` or `y` | 2024, 0070 | `y` adapts to context | | 2-digit year | `yy` | 24, 70 | 00-99 | | **Month** | | Numeric, no zero | `M` | 1, 12 | 1-12 | | Numeric, zero-padded | `MM` | 01, 12 | 01-12 | | Abbreviated name | `MMM` | Jan, Dec | Locale-specific | | Full name | `MMMM` | January, December | Locale-specific | | Stand-alone abbrev | `LLL` | Jan, Dec | Nominative form | | Stand-alone full | `LLLL` | January, December | Nominative form | | **Day of Month** | | Numeric, no zero | `d` | 1, 31 | 1-31 | | Numeric, zero-padded | `dd` | 01, 31 | 01-31 | | **Weekday** | | Abbreviated | `EEE` | Mon, Fri | Locale-specific | | Full name | `EEEE` | Monday, Friday | Locale-specific | | Narrow | `EEEEE` | M, F | Single character | | Short | `EEEEEE` | Mo, Fr | Between abbrev & narrow | | **Hour (12-hour)** | | No zero (1-12) | `h` | 1, 12 | Requires AM/PM | | Zero-padded (01-12) | `hh` | 01, 12 | Requires AM/PM | | **Hour (24-hour)** | | No zero (0-23) | `H` | 0, 23 | | | Zero-padded (00-23) | `HH` | 00, 23 | | | **Minute** | | No zero | `m` | 0, 59 | 0-59 | | Zero-padded | `mm` | 00, 59 | 00-59 | | **Second** | | No zero | `s` | 0, 59 | 0-59 | | Zero-padded | `ss` | 00, 59 | 00-59 | | **Fractional Seconds** | | Milliseconds | `SSS` | 000, 999 | Always 3 digits | | Variable precision | `S` to `SSSSSSSSS` | 0 to 9 digits | Fractional precision | | **AM/PM** | | Period | `a` | AM, PM | Locale-specific | | Period, narrow | `aaaaa` | a, p | Single character | | Day period (flexible) | `b` | at night, in the morning | Locale-specific | | Day period (specific) | `B` | noon, midnight | Locale-specific | | **Timezone** | | ISO offset | `Z` to `ZZZZZ` | -0800, -08:00, Z | Various formats | | Localized GMT | `O` to `OOOO` | GMT-8, GMT-08:00 | | | Generic non-location | `v` | PT | Short form | | Generic non-location | `vvvv` | Pacific Time | Long form | | Specific non-location | `z` | PST | Short form | | Specific non-location | `zzzz` | Pacific Standard Time | Long form | ### Common Pattern Examples - `yyyy-MM-dd` → 2024-02-08 - `MMM d, yyyy` → Feb 8, 2024 - `EEEE, MMMM d, yyyy` → Thursday, February 8, 2024 - `h:mm a` → 3:45 PM - `HH:mm:ss` → 15:45:30 - `yyyy-MM-dd'T'HH:mm:ss` → 2024-02-08T15:45:30 --- ## 2. strftime (C, Python, Ruby, PHP, etc.) **Used by:** C, C++, Python, Ruby, PHP, Perl, Unix/Linux systems ### Key Characteristics - All format codes start with `%` - Single character codes (case-sensitive) - Platform-dependent for some codes - Locale-aware variants with `E` and `O` modifiers ### Format Codes | Element | Code | Examples | Notes | |---------|------|----------|-------| | **Year** | | 4-digit year | `%Y` | 2024, 0070 | | | 2-digit year | `%y` | 24, 70 | 00-99 | | Century | `%C` | 20 | First 2 digits | | **Month** | | Numeric, zero-padded | `%m` | 01, 12 | 01-12 | | Abbreviated name | `%b` or `%h` | Jan, Dec | Locale-specific | | Full name | `%B` | January, December | Locale-specific | | **Day of Month** | | Zero-padded | `%d` | 01, 31 | 01-31 | | Space-padded | `%e` | " 1", "31" | 1-31 with space | | **Weekday** | | Abbreviated | `%a` | Mon, Fri | Locale-specific | | Full name | `%A` | Monday, Friday | Locale-specific | | Numeric (0-6) | `%w` | 0, 6 | Sunday = 0 | | Numeric (1-7) | `%u` | 1, 7 | Monday = 1 | | **Hour (12-hour)** | | Zero-padded | `%I` | 01, 12 | 01-12, requires AM/PM | | **Hour (24-hour)** | | Zero-padded | `%H` | 00, 23 | 00-23 | | Space-padded | `%k` | " 0", "23" | 0-23 with space | | **Minute** | | Zero-padded | `%M` | 00, 59 | 00-59 | | **Second** | | Zero-padded | `%S` | 00, 59 | 00-59 | | **Fractional Seconds** | | Microseconds | `%f` | 000000, 999999 | Python-specific, 6 digits | | **AM/PM** | | Period | `%p` | AM, PM | Locale-specific | | Period lowercase | `%P` | am, pm | GNU extension | | **Timezone** | | Name or abbreviation | `%Z` | PST, EST | Platform-dependent | | Offset | `%z` | -0800, +0530 | ±HHMM format | | **Composite Formats** | | Date (MM/DD/YY) | `%D` | 02/08/24 | Equivalent to %m/%d/%y | | ISO date | `%F` | 2024-02-08 | Equivalent to %Y-%m-%d | | 12-hour time | `%r` | 03:45:30 PM | Locale-specific | | 24-hour time (HH:MM) | `%R` | 15:45 | Equivalent to %H:%M | | Time with seconds | `%T` | 15:45:30 | Equivalent to %H:%M:%S | | Date and time | `%c` | Thu Feb 8 15:45:30 2024 | Locale-specific | ### Common Pattern Examples - `%Y-%m-%d` → 2024-02-08 - `%b %d, %Y` → Feb 08, 2024 - `%A, %B %d, %Y` → Thursday, February 08, 2024 - `%I:%M %p` → 03:45 PM - `%H:%M:%S` → 15:45:30 - `%Y-%m-%dT%H:%M:%S` → 2024-02-08T15:45:30 --- ## 3. Moment.js / Day.js (JavaScript) **Used by:** Moment.js, Day.js (legacy JavaScript libraries) ### Key Characteristics - No special prefix character - Case-sensitive - Repetition changes format - Inspired by PHP date() function ### Format Tokens | Element | Token | Examples | Notes | |---------|-------|----------|-------| | **Year** | | 4-digit year | `YYYY` | 2024, 0070 | | | 2-digit year | `YY` | 24, 70 | 00-99 | | **Month** | | Numeric, no zero | `M` | 1, 12 | 1-12 | | Numeric, zero-padded | `MM` | 01, 12 | 01-12 | | Abbreviated name | `MMM` | Jan, Dec | | | Full name | `MMMM` | January, December | | | **Day of Month** | | Numeric, no zero | `D` | 1, 31 | 1-31 | | Numeric, zero-padded | `DD` | 01, 31 | 01-31 | | Ordinal | `Do` | 1st, 31st | With suffix | | **Weekday** | | Abbreviated | `ddd` | Mon, Fri | | | Full name | `dddd` | Monday, Friday | | | Min (2 chars) | `dd` | Mo, Fr | | | Numeric (0-6) | `d` | 0, 6 | Sunday = 0 | | **Hour (12-hour)** | | No zero (1-12) | `h` | 1, 12 | Requires A or a | | Zero-padded (01-12) | `hh` | 01, 12 | Requires A or a | | **Hour (24-hour)** | | No zero (0-23) | `H` | 0, 23 | | | Zero-padded (00-23) | `HH` | 00, 23 | | | **Minute** | | No zero | `m` | 0, 59 | 0-59 | | Zero-padded | `mm` | 00, 59 | 00-59 | | **Second** | | No zero | `s` | 0, 59 | 0-59 | | Zero-padded | `ss` | 00, 59 | 00-59 | | **Fractional Seconds** | | 1-3 digits | `S`, `SS`, `SSS` | 0, 00, 000 | Tenths, hundredths, ms | | Up to 9 digits | `SSSS` to `SSSSSSSSS` | Variable | Extended precision | | **AM/PM** | | Lowercase | `a` | am, pm | | | Uppercase | `A` | AM, PM | | | **Timezone** | | Offset | `Z` | -08:00, +05:30 | With colon | | Compact offset | `ZZ` | -0800, +0530 | Without colon | ### Common Pattern Examples - `YYYY-MM-DD` → 2024-02-08 - `MMM D, YYYY` → Feb 8, 2024 - `dddd, MMMM D, YYYY` → Thursday, February 8, 2024 - `h:mm A` → 3:45 PM - `HH:mm:ss` → 15:45:30 - `YYYY-MM-DDTHH:mm:ss` → 2024-02-08T15:45:30 --- ## 4. date-fns (Modern JavaScript) **Used by:** date-fns library ### Key Characteristics - Based on Unicode LDML tokens - More standardized than Moment.js - Case-sensitive - Uses escape sequences for literals ### Format Tokens *date-fns uses Unicode LDML tokens (see Section 1) with some differences:* | Element | Token | Examples | Notes | |---------|-------|----------|-------| | **Year** | | Extended year | `uuuu` | 2024, -0001 | Recommended over yyyy | | Calendar year | `yyyy` | 2024 | Use uuuu instead | | 2-digit year | `yy` or `uu` | 24 | | | **Month** | | | Same as LDML | | **Day** | | Day of month | `d`, `do`, `dd` | 1, 1st, 01 | | | Day of year | `D`, `Do`, `DD`, `DDD` | 1, 1st, 01, 001 | | | **Week** | | Local week | `w`, `wo`, `ww` | 1, 1st, 01 | | | ISO week | `I`, `Io`, `II` | 1, 1st, 01 | | | **Weekday** | | Short | `eee`, `eeeee` | Mon, M | Locale day | | Long | `eeee`, `eeeeee` | Monday, Mo | Locale day | | ISO | `i`, `io`, `iii`, `iiii`, `iiiii`, `iiiiii` | 1, 1st, Mon, Monday, M, Mo | | | **Hour, Minute, Second** | | | Same as LDML | | **AM/PM** | | | Same as LDML | | **Timezone** | | | Extended LDML support | ### Common Pattern Examples - `yyyy-MM-dd` → 2024-02-08 - `MMM d, yyyy` → Feb 8, 2024 - `EEEE, MMMM d, yyyy` → Thursday, February 8, 2024 - `h:mm a` → 3:45 PM - `HH:mm:ss` → 15:45:30 --- ## 5. .NET DateTime Formats **Used by:** C#, F#, VB.NET, .NET platform ### Key Characteristics - Single character codes (case-sensitive) - Can use multiple characters for wider format - Custom format strings combine individual specifiers - Standard format strings (single character) produce locale-specific output ### Format Specifiers | Element | Specifier | Examples | Notes | |---------|-----------|----------|-------| | **Year** | | 1-2 digit year | `y` | 8, 24 | Minimum digits | | 2-digit year | `yy` | 08, 24 | 00-99 | | 3-digit year | `yyy` | 008, 024 | Minimum 3 digits | | 4-digit year | `yyyy` | 0008, 2024 | | | 5-digit year | `yyyyy` | 00008, 02024 | | | **Month** | | Numeric, no zero | `M` | 1, 12 | 1-12 | | Numeric, zero-padded | `MM` | 01, 12 | 01-12 | | Abbreviated name | `MMM` | Jan, Dec | Locale-specific | | Full name | `MMMM` | January, December | Locale-specific | | **Day of Month** | | Numeric, no zero | `d` | 1, 31 | 1-31 | | Numeric, zero-padded | `dd` | 01, 31 | 01-31 | | **Weekday** | | Abbreviated | `ddd` | Mon, Fri | Locale-specific | | Full name | `dddd` | Monday, Friday | Locale-specific | | **Hour (12-hour)** | | No zero (1-12) | `h` | 1, 12 | Requires tt or t | | Zero-padded (01-12) | `hh` | 01, 12 | Requires tt or t | | **Hour (24-hour)** | | No zero (0-23) | `H` | 0, 23 | | | Zero-padded (00-23) | `HH` | 00, 23 | | | **Minute** | | No zero | `m` | 0, 59 | 0-59 | | Zero-padded | `mm` | 00, 59 | 00-59 | | **Second** | | No zero | `s` | 0, 59 | 0-59 | | Zero-padded | `ss` | 00, 59 | 00-59 | | **Fractional Seconds** | | Tenths | `f` | 0-9 | Always shown | | Hundredths | `ff` | 00-99 | Always shown | | Milliseconds | `fff` | 000-999 | Always shown | | Optional tenths | `F` | 0-9 or nothing | Not shown if zero | | Optional hundredths | `FF` | 00-99 or nothing | Not shown if zero | | Optional milliseconds | `FFF` | 000-999 or nothing | Not shown if zero | | Up to 7 digits | `fffffff`, `FFFFFFF` | Nanosecond precision | | | **AM/PM** | | First char | `t` | A, P | Locale-specific | | Full designator | `tt` | AM, PM | Locale-specific | | **Timezone** | | Hours offset | `z` | -8, +5 | No leading zero | | Hours offset padded | `zz` | -08, +05 | With leading zero | | Full offset | `zzz` | -08:00, +05:30 | Hours and minutes | | **Other** | | Era | `g` or `gg` | A.D. | Period/era | | Time separator | `:` | : | Locale-specific | | Date separator | `/` | / | Locale-specific | ### Standard Format Strings - `d` - Short date pattern (MM/dd/yyyy) - `D` - Long date pattern (dddd, MMMM dd, yyyy) - `t` - Short time pattern (h:mm tt) - `T` - Long time pattern (h:mm:ss tt) - `f` - Full date/time short (dddd, MMMM dd, yyyy h:mm tt) - `F` - Full date/time long (dddd, MMMM dd, yyyy h:mm:ss tt) - `g` - General short (M/d/yyyy h:mm tt) - `G` - General long (M/d/yyyy h:mm:ss tt) - `o` - ISO 8601 (yyyy-MM-ddTHH:mm:ss.fffffffK) - `s` - Sortable (yyyy-MM-ddTHH:mm:ss) ### Common Pattern Examples - `yyyy-MM-dd` → 2024-02-08 - `MMM d, yyyy` → Feb 8, 2024 - `dddd, MMMM d, yyyy` → Thursday, February 8, 2024 - `h:mm tt` → 3:45 PM - `HH:mm:ss` → 15:45:30 - `yyyy-MM-ddTHH:mm:ss` → 2024-02-08T15:45:30 --- ## 6. ISO 8601 Standard **International Standard for date and time representation** ### Key Characteristics - Designed for unambiguous machine-readable formats - Always uses Gregorian calendar - Year-month-day order (largest to smallest units) - Uses T as date/time separator - Uses Z to denote UTC ### Standard Representations | Format | Pattern | Example | Notes | |--------|---------|---------|-------| | **Date** | | Calendar date | `YYYY-MM-DD` | 2024-02-08 | Extended format | | Calendar date | `YYYYMMDD` | 20240208 | Basic format | | Week date | `YYYY-Www-D` | 2024-W06-4 | Week 6, day 4 | | Week date | `YYYYWwwD` | 2024W064 | Basic format | | Ordinal date | `YYYY-DDD` | 2024-039 | Day 39 of year | | Ordinal date | `YYYYDDD` | 2024039 | Basic format | | Year and month | `YYYY-MM` | 2024-02 | | | Year only | `YYYY` | 2024 | | | **Time** | | Hours and minutes | `hh:mm` | 15:45 | Extended format | | Hours and minutes | `hhmm` | 1545 | Basic format | | Hours, min, sec | `hh:mm:ss` | 15:45:30 | Extended format | | Hours, min, sec | `hhmmss` | 154530 | Basic format | | With fractional sec | `hh:mm:ss.sss` | 15:45:30.123 | Variable precision | | **DateTime** | | Combined | `YYYY-MM-DDThh:mm:ss` | 2024-02-08T15:45:30 | T separator | | Combined basic | `YYYYMMDDThhmmss` | 20240208T154530 | No separators | | **Timezone** | | UTC indicator | `Z` | 2024-02-08T15:45:30Z | Zulu time | | Offset | `±hh:mm` | 2024-02-08T15:45:30-08:00 | Extended format | | Offset | `±hhmm` | 2024-02-08T15:45:30-0800 | Basic format | | Offset | `±hh` | 2024-02-08T15:45:30-08 | Hours only | ### Common ISO 8601 Examples - `2024-02-08` (Date only) - `15:45:30` (Time only) - `2024-02-08T15:45:30` (Local datetime) - `2024-02-08T15:45:30Z` (UTC datetime) - `2024-02-08T15:45:30-08:00` (With timezone offset) - `2024-W06-4` (Week date: year 2024, week 6, Thursday) --- ## Cross-System Comparison Table ### Year Formats | Format | LDML | strftime | Moment | date-fns | .NET | ISO 8601 | |--------|------|----------|--------|----------|------|----------| | 4-digit | `yyyy` | `%Y` | `YYYY` | `yyyy` | `yyyy` | `YYYY` | | 2-digit | `yy` | `%y` | `YY` | `yy` | `yy` | `YY` | ### Month Formats | Format | LDML | strftime | Moment | date-fns | .NET | ISO 8601 | |--------|------|----------|--------|----------|------|----------| | Numeric no zero | `M` | - | `M` | `M` | `M` | - | | Numeric zero-pad | `MM` | `%m` | `MM` | `MM` | `MM` | `MM` | | Abbreviated name | `MMM` | `%b` | `MMM` | `MMM` | `MMM` | - | | Full name | `MMMM` | `%B` | `MMMM` | `MMMM` | `MMMM` | - | ### Day Formats | Format | LDML | strftime | Moment | date-fns | .NET | ISO 8601 | |--------|------|----------|--------|----------|------|----------| | Numeric no zero | `d` | - | `D` | `d` | `d` | - | | Numeric zero-pad | `dd` | `%d` | `DD` | `dd` | `dd` | `DD` | | Abbreviated weekday | `EEE` | `%a` | `ddd` | `eee` | `ddd` | - | | Full weekday | `EEEE` | `%A` | `dddd` | `eeee` | `dddd` | - | ### Hour Formats (12-hour) | Format | LDML | strftime | Moment | date-fns | .NET | ISO 8601 | |--------|------|----------|--------|----------|------|----------| | No zero (1-12) | `h` | - | `h` | `h` | `h` | - | | Zero-pad (01-12) | `hh` | `%I` | `hh` | `hh` | `hh` | - | ### Hour Formats (24-hour) | Format | LDML | strftime | Moment | date-fns | .NET | ISO 8601 | |--------|------|----------|--------|----------|------|----------| | No zero (0-23) | `H` | - | `H` | `H` | `H` | - | | Zero-pad (00-23) | `HH` | `%H` | `HH` | `HH` | `HH` | `hh` | ### Minute/Second Formats | Format | LDML | strftime | Moment | date-fns | .NET | ISO 8601 | |--------|------|----------|--------|----------|------|----------| | Minutes no zero | `m` | - | `m` | `m` | `m` | - | | Minutes zero-pad | `mm` | `%M` | `mm` | `mm` | `mm` | `mm` | | Seconds no zero | `s` | - | `s` | `s` | `s` | - | | Seconds zero-pad | `ss` | `%S` | `ss` | `ss` | `ss` | `ss` | ### AM/PM Indicators | Format | LDML | strftime | Moment | date-fns | .NET | ISO 8601 | |--------|------|----------|--------|----------|------|----------| | Uppercase | `a` | `%p` | `A` | `a` | `tt` | N/A | | Lowercase | `aaaaa` | `%P`* | `a` | `aaa` | - | N/A | | First char only | - | - | - | - | `t` | N/A | *GNU extension, not POSIX standard ### Timezone Formats | Format | LDML | strftime | Moment | date-fns | .NET | ISO 8601 | |--------|------|----------|--------|----------|------|----------| | Offset with colon | `ZZZZZ` or `XXXXX` | - | `Z` | `XXX` | `zzz` | `±hh:mm` | | Offset no colon | `ZZZ` or `XX` | `%z` | `ZZ` | `XX` | - | `±hhmm` | | UTC indicator | - | - | - | `X` | - | `Z` | | Named timezone | `z`, `zzzz` | `%Z` | - | - | - | N/A | --- ## Recommendations for Human-Readable Format Based on this research, here are recommendations for a widely-recognized human-readable format: ### Most Universal Patterns 1. **Date Only - ISO 8601 style (MOST UNIVERSAL)** - Pattern: `YYYY-MM-DD` - Example: `2024-02-08` - Recognized by: ALL systems - Pros: Unambiguous, sortable, internationally recognized - Cons: Less readable for some English speakers 2. **Date Only - Text month** - Pattern: `MMM DD, YYYY` or `DD MMM YYYY` - Example: `Feb 08, 2024` or `08 Feb 2024` - Recognized by: All major systems (with slight variations) - Pros: Very readable, avoids MM/DD vs DD/MM confusion - Cons: Not sortable, locale-dependent 3. **Time Only - 24-hour (MOST UNIVERSAL)** - Pattern: `HH:mm:ss` - Example: `15:45:30` - Recognized by: ALL systems - Pros: Unambiguous, no AM/PM needed - Cons: Some users prefer 12-hour format 4. **Time Only - 12-hour** - Pattern: `hh:mm:ss AM` or `h:mm:ss a` - Example: `03:45:30 PM` - Recognized by: All major systems (with variations) - Pros: Familiar to many users - Cons: Requires AM/PM designator 5. **Full DateTime - ISO 8601 (MOST UNIVERSAL)** - Pattern: `YYYY-MM-DDTHH:mm:ss` - Example: `2024-02-08T15:45:30` - Recognized by: ALL systems - Pros: Unambiguous, sortable, international standard - Cons: T separator less readable 6. **Full DateTime - Human-readable** - Pattern: `MMMM DD, YYYY HH:mm:ss` or `DD MMMM YYYY HH:mm:ss` - Example: `February 08, 2024 15:45:30` - Recognized by: All major systems (with slight variations) - Pros: Very readable - Cons: Verbose, locale-dependent month names ### Token Compatibility Summary **Highest Compatibility (work in 5-6 systems):** - `yyyy`/`YYYY` - 4-digit year - `MM` - 2-digit month (zero-padded) - `dd`/`DD` - 2-digit day (zero-padded) - `HH` - 24-hour time (zero-padded) - `mm` - minutes (zero-padded) - `ss` - seconds (zero-padded) **High Compatibility (work in 4-5 systems):** - `MMM` - Abbreviated month name - `MMMM` - Full month name - `hh` - 12-hour time (zero-padded) - `a`/`A`/`tt` - AM/PM (varies by system) **Moderate Compatibility:** - `EEE`/`ddd` - Abbreviated weekday - `EEEE`/`dddd` - Full weekday - Single-letter tokens (no zero padding) - variable support ### Recommended Format for timefmt Project For maximum compatibility and readability, consider: 1. **For machine-readable output**: `YYYY-MM-DD HH:mm:ss` (ISO 8601 without T) 2. **For human-readable output**: `MMM DD, YYYY HH:mm:ss` (e.g., "Feb 08, 2024 15:45:30") 3. **For compact output**: `YYYY-MM-DD HH:mm` (omit seconds if not needed) These formats: - Avoid MM/DD vs DD/MM confusion - Don't require AM/PM logic - Use widely recognized token patterns - Are unambiguous across cultures - Balance readability with precision