NAME Time::Str - Parse and format date/time strings in multiple standard formats SYNOPSIS use Time::Str qw( str2time str2date time2str ); # Parse to Unix timestamp my $time = str2time('2024-12-24T15:30:45Z'); my $time = str2time('Mon, 24 Dec 2012 15:30:45 +0100', format => 'RFC2822'); # Parse to components my %date = str2date('2024-12-24T15:30:45.500+01:00'); # (year => 2024, month => 12, day => 24, hour => 15, # minute => 30, second => 45, nanosecond => 500000000, # tz_offset => 60) # Format Unix timestamp my $str = time2str(1735052445); # '2024-12-24T15:30:45Z' my $str = time2str(1735052445, format => 'RFC2822', offset => 60); # 'Tue, 24 Dec 2024 16:30:45 +0100' DESCRIPTION Time::Str parses date/time strings from various standard formats into Unix timestamps or components, and formats Unix timestamps back into strings. Supported standards include ISO 8601, RFC 3339, RFC 2822, RFC 2616 (HTTP), ISO 9075 (SQL), ASN.1, and others. A permissive DateTime parser handles most real-world date/time representations that can be parsed without ambiguity. FUNCTIONS str2time my $time = str2time($string); my $time = str2time($string, format => $format); my $time = str2time($string, format => $format, precision => $precision); my $time = str2time($string, format => 'ASN1UT', pivot_year => 2000); Parses a date/time string and returns a Unix timestamp (seconds since 1970-01-01T00:00:00Z). The timestamp may include fractional seconds. The input must include a UTC designator ("Z", "UTC", "GMT") or a numeric timezone offset. Strings with only an unresolved timezone abbreviation (e.g., "EST", "IST") will croak. See "TIMEZONE ABBREVIATIONS". Parameters * $string (required) The date/time string to parse. * "format" (optional, default: 'RFC3339') The format specification. See "SUPPORTED FORMATS". * "precision" (optional, default: 6 or 9 depending on float size) Number of decimal places to preserve for fractional seconds (0-9). Fractional digits beyond the specified precision are truncated. See "PRECISION HANDLING". * "pivot_year" (optional, default: 1950) For formats with two-digit years ("ASN1UT", RFC 850 within "RFC2616"), sets the pivot year for century expansion. Two-digit years less than "(pivot_year % 100)" map to the next century; others map to the current century. With the default pivot of 1950: 49 becomes 2049, 50 becomes 1950, 99 becomes 1999. Returns A numeric Unix timestamp, possibly with a fractional part. The supported range is 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z. Errors Croaks if: * The string cannot be parsed in the specified format * Date or time components are out of valid ranges * No timezone offset or UTC designator is present * A named parameter is unknown or out of range Examples # RFC 3339 (default) my $t = str2time('2024-12-24T15:30:45Z'); # 1735052445 my $t = str2time('2024-12-24T15:30:45.500+01:00'); # 1735048845.5 # RFC 2822 my $t = str2time('Mon, 24 Dec 2012 15:30:45 +0100', format => 'RFC2822'); # HTTP my $t = str2time('Mon, 24 Dec 2012 15:30:45 GMT', format => 'RFC2616'); # Precision (truncation) my $t = str2time('2024-12-24T15:30:45.123456789Z', precision => 3); # 1735052445.123 my $t = str2time('2024-12-24T15:30:45.999999Z', precision => 3); # 1735052445.999 str2date my %date = str2date($string); my %date = str2date($string, format => $format); my %date = str2date($string, format => 'ASN1UT', pivot_year => 2000); Parses a date/time string and returns the parsed components. Unlike "str2time", this does not require a timezone and preserves all parsed information without converting to a timestamp. Parameters * $string (required) The date/time string to parse. * "format" (optional, default: 'RFC3339') The format specification. See "SUPPORTED FORMATS". * "pivot_year" (optional, default: 1950) For formats with two-digit years, sets the pivot year for century expansion. Returns In list context, returns key-value pairs. In scalar context, returns a hash reference. All values are numeric except "tz_utc", "tz_abbrev", and "tz_annotation". The following components may be present: * "year" - Four-digit year (1-9999) * "month" - Month (1-12) * "day" - Day of month (1-31) * "hour" - Hour in 24-hour format (0-23) * "minute" - Minute (0-59) * "second" - Second (0-60; 60 allows for leap seconds) * "nanosecond" - Fractional seconds as nanoseconds (0-999,999,999) * "tz_offset" - Timezone offset in minutes from UTC (e.g., 60 for +01:00). Present when a numeric offset or UTC designator was parsed. * "tz_utc" - The UTC designator ("Z", "UTC", "GMT") if one was present. When followed by a numeric offset (e.g., "UTC+05:30"), "tz_offset" reflects that offset rather than zero. * "tz_abbrev" - The timezone abbreviation as it appeared in the input, if present and not a UTC designator. "tz_offset" will not be present when "tz_abbrev" is set. See "TIMEZONE ABBREVIATIONS". * "tz_annotation" - Bracketed timezone tag from the input, if present (RFC 9557 IXDTF or Java "ZoneId" format, e.g., "[Europe/Stockholm]"). Informational only; does not affect "tz_offset". Errors Croaks if: * The string cannot be parsed in the specified format * Date or time components are out of valid ranges * A named parameter is unknown or out of range Examples # Full RFC 3339 timestamp my %d = str2date('2024-12-24T15:30:45.500+01:00'); # (year => 2024, # month => 12, # day => 24, # hour => 15, # minute => 30, # second => 45, # nanosecond => 500000000, # tz_offset => 60) # Partial dates my %d = str2date('2024-12-24', format => 'W3CDTF'); # (year => 2024, month => 12, day => 24) my %d = str2date('2024', format => 'W3CDTF'); # (year => 2024) # 12-hour clock my %d = str2date('December 24, 2024, 3:30 PM', format => 'DateTime'); # (..., hour => 15) # Two-digit year my %d = str2date('Monday, 24-Dec-50 15:30:45 GMT', format => 'RFC2616'); # (year => 1950, ...) # Two-digit year with custom pivot my %d = str2date('Monday, 24-Dec-50 15:30:45 GMT', format => 'RFC2616', pivot_year => 1970); # (year => 2050, ...) # UTC designator my %d = str2date('24 Dec 2012 15:30:45 GMT', format => 'RFC2822'); # (..., tz_utc => 'GMT', tz_offset => 0) # UTC designator with offset my %d = str2date('December 24, 2024 at 3:30 pm UTC+05:30', format => 'DateTime'); # (..., tz_utc => 'UTC', tz_offset => 330) # Timezone abbreviation (unresolved) my %d = str2date('24 Dec 2012 15:30:45 IST', format => 'RFC2822'); # (..., tz_abbrev => 'IST') # RFC 9557 annotation my %d = str2date('2024-12-24T15:30:45.500+01:00[Europe/Stockholm]', format => 'DateTime'); # (..., tz_offset => 60, tz_annotation => '[Europe/Stockholm]') time2str my $str = time2str($time); my $str = time2str($time, format => $format); my $str = time2str($time, format => $format, offset => $offset); my $str = time2str($time, nanosecond => $ns, precision => $prec); Formats a Unix timestamp into a date/time string. Parameters * $time (required) Unix timestamp (seconds since 1970-01-01T00:00:00Z). May be an integer or floating-point number. Supported range: 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z. * "format" (optional, default: 'RFC3339') The output format. See "SUPPORTED FORMATS". Not all formats support fractional seconds or timezone offsets. * "offset" (optional, default: 0) Timezone offset in minutes from UTC. Positive is east, negative is west. Valid range: -1439 to +1439 (-23:59 to +23:59). * "precision" (optional) Number of decimal places for fractional seconds (0-9). Fractional seconds are rounded to this precision, which may carry into the seconds field (e.g., rounding .999 at precision 0 yields the next whole second). When omitted and the timestamp has fractional seconds, precision is auto-detected: 3 if the fractional part is divisible by 0.001, 6 if divisible by 0.000001, or 9 otherwise. When specified as 0, fractional seconds are omitted after rounding. See "PRECISION HANDLING". * "nanosecond" (optional) Explicit nanosecond value (0-999,999,999) for fractional seconds. Overrides any fractional part of $time and bypasses rounding. Use this for exact control over fractional output or to preserve nanosecond precision that floating-point cannot represent. Can be combined with "precision" to control zero-padding. Returns A formatted date/time string. Errors Croaks if: * $time is outside the supported range * A named parameter is unknown or out of range Examples # RFC 3339 (default) my $str = time2str(1735052445); # '2024-12-24T15:30:45Z' # Timezone offset my $str = time2str(1735052445, offset => 60); # '2024-12-24T16:30:45+01:00' # Fractional seconds (auto-detected precision) my $str = time2str(1735052445.123456); # '2024-12-24T15:30:45.123456Z' # Explicit precision (rounded) my $str = time2str(1735052445.123456, precision => 3); # '2024-12-24T15:30:45.123Z' # Rounding carries into seconds my $str = time2str(1735052445.999999, precision => 3); # '2024-12-24T15:30:46.000Z' # Zero-padding my $str = time2str(1735052445, precision => 3); # '2024-12-24T15:30:45.000Z' # Nanosecond override (no rounding, auto-detected precision) my $str = time2str(1735052445, nanosecond => 500_000_000); # '2024-12-24T15:30:45.500Z' # Zero-padding with nanosecond my $str = time2str(1735052445, nanosecond => 0, precision => 3); # '2024-12-24T15:30:45.000Z' # RFC 2822 my $str = time2str(1735052445, format => 'RFC2822', offset => 60); # 'Tue, 24 Dec 2024 16:30:45 +0100' # HTTP (always GMT) my $str = time2str(1735052445, format => 'RFC2616'); # 'Tue, 24 Dec 2024 15:30:45 GMT' # SQL with timezone my $str = time2str(1735052445.5, format => 'ISO9075', offset => 60); # '2024-12-24 16:30:45.500 +01:00' # Common Log Format my $str = time2str(1735052445, format => 'CLF', offset => 60); # '24/Dec/2024:16:30:45 +0100' PRECISION HANDLING The module handles fractional seconds differently when parsing versus formatting. Parsing (str2time) Fractional seconds beyond the specified precision are truncated. This preserves the exact digits provided in the input up to the requested precision: str2time('2024-12-24T15:30:45.123456Z', precision => 3) # Returns: 1735052445.123 (digits beyond 3rd truncated) str2time('2024-12-24T15:30:45.999999Z', precision => 3) # Returns: 1735052445.999 (NOT .000 of next second) Note: "str2date" does not accept a precision parameter. It always preserves fractional seconds at full nanosecond resolution in the "nanosecond" component. Formatting (time2str) Fractional seconds are rounded to the specified precision. Rounding prevents floating-point artifacts from appearing in formatted output: time2str(1735052445.123456, precision => 3) # '2024-12-24T15:30:45.123Z' time2str(1735052445.999999, precision => 3) # '2024-12-24T15:30:46.000Z' (rounds up to next second) time2str(1735052445.999999, precision => 0) # '2024-12-24T15:30:46Z' (rounds up, fraction omitted) Bypassing Rounding The "nanosecond" parameter overrides the fractional part of $time and is not subject to rounding, giving exact control over the output: time2str(1735052445, nanosecond => 999_999_000, precision => 6) # '2024-12-24T15:30:45.999999Z' (exact, no rounding) time2str(1735052445, nanosecond => 0) # '2024-12-24T15:30:45Z' (no fractional part) FLOATING-POINT PRECISION Perl's floating-point type (NV) is typically IEEE 754 double-precision (64-bit), providing approximately 15-17 significant decimal digits shared between the integer and fractional parts of a number. Implications for Timestamps Whole seconds are always represented exactly within the supported date range (0001-01-01 to 9999-12-31). Fractional precision depends on the magnitude of the timestamp: * Millisecond precision ("precision => 3") is exact and stable across the entire supported date range. * Microsecond precision ("precision => 6") is reliable for timestamps within roughly ±140 years of the Unix epoch (1830-2110). Beyond this range, values may shift by ±1 microsecond due to floating-point spacing. * Nanosecond precision cannot be represented faithfully in a floating-point timestamp. Use the "nanosecond" parameter for exact sub-microsecond values. Default Precision When "time2str" formats a timestamp with fractional seconds and neither "precision" nor "nanosecond" is specified, it uses a default based on Perl's NV (floating-point) type: * 6 decimal places when NV is double-precision (64-bit) * 9 decimal places when NV has extended precision (e.g., long double) This default prevents spurious trailing digits caused by floating-point representation. For explicit control, always specify "precision" or "nanosecond". SUPPORTED FORMATS The following format specifiers are recognized (case-insensitive). The default format is "RFC3339". ANSIC (alias: ctime) ANSI C asctime() / ctime() format. DDD MMM (_D|DD) HH:MM:SS YYYY Where "_D" is a space-padded single-digit day. Parsing: Mon Dec 1 03:04:05 2024 Tue Dec 24 15:30:45 2024 Formatting: time2str(1735052445, format => 'ANSIC') # 'Tue Dec 24 15:30:45 2024' Limitations: Always UTC. Fractional seconds and timezone offsets are not supported; the "offset" parameter is ignored. ASN1GT ASN.1 GeneralizedTime as defined in ITU-T X.680. YYYYMMDDhh[mm[ss]][(.|,)fraction][Z|±hh[mm]] Parsing: Hours are required; minutes and seconds are optional. The fractional part may use a period or comma and applies to the least significant time component present. The timezone designator is optional. 2024122415 # hour only, no timezone 2024122415Z # hour only, UTC 2024122415,5Z # decimal hour (30 minutes) 201212241530Z # hour and minute 201212241530,5Z # decimal minute (30 seconds) 20121224153045 # full time, no timezone 20121224153045Z # full time, UTC 20121224153045.500Z # with fractional seconds 20121224153045,123456789Z # nanoseconds, comma separator 20121224153045+0100 # with numeric offset Timezone offset format: "±HHMM" or "±HH" Formatting: time2str(1735052445, format => 'ASN1GT') # '20241224153045Z' time2str(1735052445, format => 'ASN1GT', precision => 3) # '20241224153045.500Z' time2str(1735052445, format => 'ASN1GT', offset => 60) # '20241224163045+0100' Limitations: Formatting always outputs hours, minutes, and seconds. Decimal hours and decimal minutes are not produced. ASN1UT ASN.1 UTCTime as defined in ITU-T X.680. YYMMDDhhmm[ss](Z|±hhmm) Parsing: Two-digit year; seconds are optional. A timezone designator is required. 9412211010Z # without seconds 241224153045Z # with seconds 241224153045+0100 # with numeric offset Timezone offset format: "±HHMM" Formatting: time2str(1735052445, format => 'ASN1UT') # '241224153045Z' time2str(1735052445, format => 'ASN1UT', offset => 60) # '241224163045+0100' Limitations: Fractional seconds are not supported. CLF Common Log Format. Used by web servers like Apache and Nginx. DD/MMM/YYYY:HH:MM:SS[.fraction] ±HHMM Parsing: 24/Dec/2024:15:30:45 +0100 24/Dec/2024:15:30:45.500 +0100 24/Dec/2024:15:30:45.123456789 -0500 Timezone offset format: "±HHMM" Formatting: time2str(1735052445, format => 'CLF') # '24/Dec/2024:15:30:45 +0000' time2str(1735052445, format => 'CLF', offset => 60) # '24/Dec/2024:16:30:45 +0100' time2str(1735052445.5, format => 'CLF', precision => 3, offset => 60) # '24/Dec/2024:16:30:45.500 +0100' DateTime A permissive parser that accepts a wide variety of real-world date/time representations, restricted to those that can be parsed deterministically. Parsing only; cannot be used with "time2str". See "DATETIME FORMAT PARSING" for the full grammar, rules, and examples. ECMAScript (alias: JavaScript) ECMAScript "Date.prototype.toString" format. DDD MMM DD YYYY HH:MM:SS [GMT|UTC]±HHMM [(comment)] Parsing: Day name is required. An optional parenthesized comment (e.g., timezone name) may follow the offset and is discarded. "GMT" or "UTC" before the offset is captured in "tz_utc". Tue Dec 24 2024 15:30:45 GMT+0100 (Central European Standard Time) Tue Dec 24 2024 15:30:45 +0000 Tue Dec 24 2024 15:30:45 UTC+0530 (India Standard Time) Mon Jan 01 2024 00:00:00 GMT+0000 (Coordinated Universal Time) Timezone offset format: "±HHMM" (required) Formatting: time2str(1735052445, format => 'ECMAScript') # 'Tue Dec 24 2024 15:30:45 GMT+0000' time2str(1735052445, format => 'ECMAScript', offset => 60) # 'Tue Dec 24 2024 16:30:45 GMT+0100' Limitations: Fractional seconds are not supported. Parenthesized comment is not preserved in formatting. Generic Deprecated. Use "DateTime" instead. GitDate (alias: Git) Default date format used by Git. DDD MMM D HH:MM:SS YYYY ±HHMM Parsing: Mon Dec 1 03:04:05 2024 +0100 Mon Dec 24 15:30:45 2012 +0100 Timezone offset format: "±HHMM" Formatting: time2str(1735052445, format => 'GitDate') # 'Tue Dec 24 15:30:45 2024 +0000' time2str(1735052445, format => 'GitDate', offset => 60) # 'Tue Dec 24 16:30:45 2024 +0100' Limitations: Fractional seconds are not supported. ISO8601 Calendar date and time as defined in ISO 8601. Accepts both extended (with separators) and basic (compact) forms. Minutes, seconds, fractional seconds, and timezone are all optional when a time is present. The fractional part applies to the least significant time component present, allowing decimal hours and decimal minutes. Extended format: YYYY-MM-DD YYYY-MM-DDThh[:mm[:ss]][(.|,)fraction][Z|±hh[:mm]] Basic format: YYYYMMDD YYYYMMDDThh[mm[ss]][(.|,)fraction][Z|±hh[mm]] Parsing: 2024-12-24 # date only (extended) 20241224 # date only (basic) 2024-12-24T15Z # hour only 2024-12-24T15,5Z # decimal hour (15:30) 2024-12-24T15:30Z # hour and minute 2024-12-24T15:30.5Z # decimal minute (15:30:30) 2024-12-24T15:30:45Z # full time 2024-12-24T15:30:45.500Z # fractional seconds 2024-12-24T15:30:45,500Z # comma separator 20241224T153045Z # basic 20241224T1530,5Z # basic, decimal minute 20241224T153045+0100 # basic with offset 2024-12-24T15:30:45+01:00 # extended with offset Formatting: Produces extended format, identical to RFC3339. time2str(1735052445, format => 'ISO8601') # '2024-12-24T15:30:45Z' time2str(1735052445, format => 'ISO8601', offset => 60) # '2024-12-24T16:30:45+01:00' time2str(1735052445.5, format => 'ISO8601', precision => 3) # '2024-12-24T15:30:45.500Z' Limitations: Formatting always produces extended format with full time (hours, minutes, seconds). Date-only, basic format, decimal hours, and decimal minutes are not produced. ISO9075 (alias: SQL) ISO 9075 Database Language SQL timestamp format. YYYY-MM-DD YYYY-MM-DD HH:MM:SS[.fraction] YYYY-MM-DD HH:MM:SS[.fraction] ±HH:MM Parsing: 2024-12-24 2024-12-24 15:30:45 2024-12-24 15:30:45.500 2024-12-24 15:30:45.123456789 2024-12-24 15:30:45 +01:00 2024-12-24 15:30:45.500 +01:00 Timezone offset format: "±HH:MM" Formatting: time2str(1735052445, format => 'ISO9075') # '2024-12-24 15:30:45 +00:00' time2str(1735052445, format => 'ISO9075', offset => 60) # '2024-12-24 16:30:45 +01:00' time2str(1735052445.5, format => 'ISO9075', precision => 3, offset => 60) # '2024-12-24 16:30:45.500 +01:00' Limitations: Formatting always includes a timezone offset. Date-only parsing is supported but date-only output is not. RFC2616 (aliases: RFC7231, HTTP) HTTP-date as defined in RFC 2616 / RFC 7231. Parses three sub-formats: DDD, DD MMM YYYY HH:MM:SS GMT # IMF-fixdate (preferred) DDDD, DD-MMM-YY HH:MM:SS GMT # RFC 850 (obsolete) DDD MMM _D HH:MM:SS YYYY # ANSI C asctime Parsing: Mon, 24 Dec 2012 15:30:45 GMT # IMF-fixdate Monday, 24-Dec-12 15:30:45 GMT # RFC 850 Mon Dec 24 15:30:45 2012 # asctime Formatting: Always produces IMF-fixdate in GMT: time2str(1735052445, format => 'RFC2616') # 'Tue, 24 Dec 2024 15:30:45 GMT' Limitations: Always GMT. The "offset" parameter is ignored. Fractional seconds are not supported. RFC2822 (aliases: RFC5322, IMF, EMAIL) Internet Message Format date as defined in RFC 2822 / RFC 5322. [DDD,] D MMM YYYY HH:MM[:SS] (±HHMM|UT|UTC|GMT|abbrev) Parsing: Day name and seconds are optional. Accepts numeric offsets, UTC designators ("UT", "UTC", "GMT"), and timezone abbreviations. Abbreviations are returned in "tz_abbrev" without resolution. Mon, 24 Dec 2012 15:30:45 +0100 Mon, 24 Dec 2012 15:30 +0100 24 Dec 2012 15:30:45 +0100 24 Dec 2012 15:30:45 GMT 24 Dec 2012 15:30:45 CET Timezone offset format: "±HHMM" See "TIMEZONE ABBREVIATIONS". Formatting: Always produces a numeric offset: time2str(1735052445, format => 'RFC2822') # 'Tue, 24 Dec 2024 15:30:45 +0000' time2str(1735052445, format => 'RFC2822', offset => 60) # 'Tue, 24 Dec 2024 16:30:45 +0100' Limitations: Fractional seconds are not supported. RFC3339 Internet timestamp as defined in RFC 3339, a profile of ISO 8601. YYYY-MM-DD(T|t| )HH:MM:SS[.fraction](Z|z|±HH:MM) Parsing: The date/time separator may be "T", "t", or a space. The UTC designator may be "Z" or "z". 2024-12-24T15:30:45Z 2024-12-24t15:30:45z 2024-12-24 15:30:45Z 2024-12-24T15:30:45.500Z 2024-12-24T15:30:45.123456789Z 2024-12-24T15:30:45+01:00 2024-12-24T15:30:45-05:00 Timezone offset format: "±HH:MM" Formatting: Always produces uppercase "T" and "Z" (or "±HH:MM"): time2str(1735052445, format => 'RFC3339') # '2024-12-24T15:30:45Z' time2str(1735052445, format => 'RFC3339', offset => 60) # '2024-12-24T16:30:45+01:00' time2str(1735052445.5, format => 'RFC3339', precision => 3) # '2024-12-24T15:30:45.500Z' RFC3501 (aliases: RFC9051, IMAP) IMAP internal date format as defined in RFC 3501 and RFC 9051. DD-MMM-YYYY HH:MM:SS ±HHMM Parsing: Day is always two digits (zero-padded). Month name is case-insensitive. 24-Dec-2024 15:30:45 +0100 01-Jan-2024 00:00:00 +0000 24-dec-2024 15:30:45 -0500 Timezone offset format: "±HHMM" (required) Formatting: time2str(1735052445, format => 'RFC3501') # '24-Dec-2024 15:30:45 +0000' time2str(1735052445, format => 'RFC3501', offset => 60) # '24-Dec-2024 16:30:45 +0100' Limitations: Fractional seconds are not supported. RFC4287 (alias: ATOM) Atom feed timestamp as defined in RFC 4287. Stricter than RFC 3339: requires uppercase "T" and "Z", no space separator, no lowercase designators. YYYY-MM-DDTHH:MM:SS[.fraction](Z|±HH:MM) Parsing: 2024-12-24T15:30:45Z 2024-12-24T15:30:45.500Z 2024-12-24T15:30:45.123456789Z 2024-12-24T15:30:45+01:00 Timezone offset format: "±HH:MM" Formatting: Identical to RFC3339. RFC5280 (alias: x509) X.509 certificate validity times as defined in RFC 5280. Certificates encode validity as ASN.1 UTCTime (two-digit year) for dates before 2050 and GeneralizedTime (four-digit year) for dates from 2050 onward. Both forms require UTC and do not permit fractional seconds or timezone offsets. YYMMDDhhmmssZ # UTCTime YYYYMMDDhhmmssZ # GeneralizedTime Parsing: Accepts both forms. Two-digit years are expanded using "pivot_year" (default 1950), which matches the RFC 5280 convention: years 00-49 map to 2000-2049, years 50-99 map to 1950-1999. 121224153045Z # UTCTime, year 12 -> 2012 491231235959Z # UTCTime, year 49 -> 2049 500101000000Z # UTCTime, year 50 -> 1950 20500101000000Z # GeneralizedTime 99991231235959Z # GeneralizedTime Formatting: Automatically selects the form based on the timestamp: UTCTime for dates before 2050-01-01T00:00:00Z, GeneralizedTime for dates from 2050 onward. time2str(1356359445, format => 'RFC5280') # '121224143045Z' - before 2050, UTCTime time2str(2524608000, format => 'RFC5280') # '20500101000000Z' - from 2050, GeneralizedTime Limitations: Always UTC. The "offset" parameter is ignored. Fractional seconds are not supported. RFC5545 (alias: iCal) iCalendar date and date-time as defined in RFC 5545. YYYYMMDD YYYYMMDDThhmmss[Z] Parsing: Date-only and date-time forms are accepted. Without "Z", parses as local time with no "tz_offset". 20241224 # date only 20241224T153045 # local time 20241224T153045Z # UTC Formatting: time2str(1735052445, format => 'RFC5545') # '20241224T153045Z' Limitations: Always UTC. The "offset" parameter is ignored. Fractional seconds are not supported. RFC9557 (alias: IXDTF) Internet Extended Date/Time Format as defined in RFC 9557. Extends RFC 3339 with optional bracketed suffix tags for timezone annotations and calendar systems. YYYY-MM-DD(T|t| )HH:MM:SS[.fraction](Z|z|±HH:MM)[tags] Parsing: The base timestamp follows RFC 3339 syntax. One or more bracketed suffix tags may follow the timezone. Tag content matches "[0-9A-Za-z!+-._/]+". Tags are captured verbatim in "tz_annotation" without validation or interpretation; no constraints are enforced on tag content beyond the character class. Intended for use with "str2date". 2024-12-24T15:30:45Z 2024-12-24T15:30:45+01:00[Europe/Stockholm] 2024-12-24T15:30:45Z[Europe/Stockholm] 2024-12-24T15:30:45.500+05:30[Asia/Kolkata] 2024-12-24T15:30:45Z[Europe/Stockholm][u-ca=gregory] Timezone offset format: "±HH:MM" Formatting: Identical to RFC3339 (tags are not preserved). RubyDate (alias: Ruby) Date format popularized by Ruby on Rails and Twitter. DDD MMM DD HH:MM:SS ±HHMM YYYY Parsing: Mon Dec 01 03:04:05 +0100 2024 Mon Dec 24 15:30:45 +0100 2012 Timezone offset format: "±HHMM" Formatting: time2str(1735052445, format => 'RubyDate') # 'Tue Dec 24 15:30:45 +0000 2024' time2str(1735052445, format => 'RubyDate', offset => 60) # 'Tue Dec 24 16:30:45 +0100 2024' Limitations: Fractional seconds are not supported. UnixDate (alias: Unix) The date(1) command output format as defined by POSIX. DDD MMM (_D|DD) HH:MM:SS (±HHMM|UTC|GMT|abbrev) YYYY DDD MMM (_D|DD) HH:MM:SS YYYY (±HHMM|UTC|GMT|abbrev) Where "_D" is a space-padded single-digit day. Parsing: Accepts the timezone before or after the year. Accepts numeric offsets, UTC designators ("UTC", "GMT"), and timezone abbreviations. Abbreviations are returned in "tz_abbrev" without resolution. Mon Dec 1 03:04:05 2024 UTC Mon Dec 24 15:30:45 2012 UTC Mon Dec 1 03:04:05 UTC 2024 Mon Dec 24 15:30:45 UTC 2012 Timezone offset format: "±HHMM" See "TIMEZONE ABBREVIATIONS". Formatting: Always produces the zone-before-year. Uses "UTC" for zero offset and a numeric offset otherwise. time2str(1735052445, format => 'UnixDate') # 'Tue Dec 24 15:30:45 UTC 2024' time2str(1735052445, format => 'UnixDate', offset => 60) # 'Tue Dec 24 16:30:45 +0100 2024' Limitations: Fractional seconds are not supported. UnixStamp Unix date(1) based format with optional fractional seconds and timezone. [DDD ] MMM (_D|D|DD) hh:mm[:ss[.fraction]] [±HHMM|UTC|GMT|abbrev] YYYY [DDD ] MMM (_D|D|DD) hh:mm[:ss[.fraction]] YYYY [±HHMM|UTC|GMT|abbrev] Parsing: Day name is optional. Day may be space-padded or zero-padded. Seconds and fractional seconds are optional. Timezone may appear before or after the year. Sun Jul 17 06:53:23 2022 Sun Jul 3 06:53:23 2022 Jul 17 06:53:23 2022 Sun Jul 17 20:35:45.089105460 2022 Sun Jul 17 06:53:23 +0100 2022 Sun Jul 17 06:53:23 EDT 2022 Sun Jul 17 06:53:23 2022 +0100 Sun Jul 17 06:53:23 2022 EDT Sun Jul 17 06:53:23 GMT+0100 2022 Jul 17 06:53 2022 Timezone offset format: "±HHMM" See "TIMEZONE ABBREVIATIONS". Formatting: Always produces the zone-before-year. Uses "UTC" for zero offset and a numeric offset otherwise. time2str(1658044403, format => 'UnixStamp') # 'Sun Jul 17 06:53:23 UTC 2022' time2str(1658044403, format => 'UnixStamp', offset => 60) # 'Sun Jul 17 07:53:23 +0100 2022' time2str(1658044403.5, format => 'UnixStamp', precision => 3) # 'Sun Jul 17 06:53:23.500 UTC 2022' W3CDTF (alias: W3C) W3C Date and Time Format, a profile of ISO 8601. YYYY YYYY-MM YYYY-MM-DD YYYY-MM-DDTHH:MM:SS[.fraction](Z|±HH:MM) Parsing: Supports partial dates. A timezone designator is required when a time component is present. 2024 # year only 2024-12 # year and month 2024-12-24 # date only 2024-12-24T15:30:45Z # full datetime 2024-12-24T15:30:45.500Z 2024-12-24T15:30:45.123456789Z 2024-12-24T15:30:45+01:00 Timezone offset format: "±HH:MM" Formatting: Identical to RFC3339. DATETIME FORMAT PARSING The "DateTime" format parser accepts a wide variety of real-world date/time representations, restricted to those that can be parsed deterministically. A date is always required. Time and timezone are optional, but a timezone may only appear when a time is present. Date A date is always required. Three categories are accepted. Numeric Dates (Y-M-D only) When all three components are numeric, they must appear in year-month-day order to avoid the ambiguity between American (M/D/Y) and European (D/M/Y) conventions. The separator must be a hyphen, slash, or period, consistent within the date. 2024-12-24 2024/12/24 2024.12.24 These are rejected: 12-24-2024 # M-D-Y not accepted 24-12-2024 # D-M-Y not accepted 2024-12/24 # mixed separators Dates with Textual Months When the month is given as a name or Roman numeral it is unambiguous, so the day and year may appear in any order relative to the month. Month names may be abbreviated ("Jan", "Feb", ...) or written in full ("January", "February", ...). Roman numerals ("I" through "XII") are accepted only in day-month-year order. Month names and Roman numerals are all case-insensitive. Separator-based (hyphen, slash, or period, consistent within the date): 2024-Dec-24 # Y-M-D 24-Dec-2024 # D-M-Y Dec-24-2024 # M-D-Y 2024/December/24 24.XII.2024 # Roman numeral, D-M-Y only Space-separated: 24 December 2024 December 24, 2024 24. XII. 2024 In space-separated dates, an ordinal suffix ("st", "nd", "rd", "th") or a trailing period may follow the day. A trailing period or comma may follow the month: 24th December 2024 24. December 2024 December 24th, 2024 24. XII. 2024 24 XII. 2024 24. XII 2024 Compact Dates (No Separators) Accepted only when the month is textual. Roman numerals are accepted in day-month-year order: 24DEC2024 24Dec2024 2024DEC24 2024Dec24 24XII2024 Day Names An optional day name prefix (e.g., "Monday," or "Mon,") is accepted before the date but not validated against the actual date. Day names are case-insensitive. Monday, 24 December 2024 Mon, 24 Dec 2024 Year A four-digit year is always required. Two-digit years are not accepted. Time of Day Optional. When present, it must be separated from the date by "T", a comma and space, the word "at" surrounded by spaces, or a plain space. 2024-12-24T15:30:45 2024-12-24 15:30:45 December 24, 2024, 15:30 Monday, 24th December 2024 at 3:30 pm Either "HH:MM" or "HH" with an AM/PM indicator is required as a minimum. 24-Hour Time Hours and minutes separated by a colon. Hours may be one or two digits; minutes are always two digits. Seconds are optional (two digits, colon-separated). An optional fractional part may follow the seconds, separated by a period or comma, up to nine digits. 15:30 9:05 15:30:45 15:30:45.500 15:30:45,500 15:30:45.123456789 12-Hour Time An AM/PM indicator is required. Hours must be 1-12. Minutes, seconds, and fractional seconds follow the same rules as 24-hour time but are optional. The indicator may appear with or without a separating space. Accepted forms: "AM", "am", "PM", "pm", "A.M.", "a.m.", "P.M.", "p.m." 3 PM 3PM 3:30 PM 3:30:45 pm 3:30:45.500 P.M. 12 p.m. Midnight is "12:00 AM", noon is "12:00 PM". Timezone Optional; may only appear when a time is present. UTC Designators "Z", "UTC", and "GMT" set "tz_utc" to the matched designator and "tz_offset" to 0, unless followed by a numeric offset (e.g., "UTC+05:30"), in which case "tz_offset" reflects that offset. Numeric Offsets Two-digit hours only: ±HH ±HHMM ±HH:MM +01 +0100 +01:00 +05:30 Combined UTC and Offset "UTC" or "GMT" followed immediately by a numeric offset. Single-digit hours are only accepted in this combined form. "tz_utc" is set to the designator; "tz_offset" reflects the numeric part: UTC+1 UTC+5:30 UTC+01:00 GMT+5:30 Abbreviations Abbreviations matching "[A-Z][A-Za-z][A-Z]{1,4}" are accepted. UTC designators are handled as above; all others (e.g., "EST", "CET", "IST") are returned in "tz_abbrev" without resolution. See "TIMEZONE ABBREVIATIONS". Annotations RFC 9557 (IXDTF) bracketed tags and Java "ZoneId" annotations are captured in "tz_annotation". Parenthesized comments are accepted but discarded. +01:00[Europe/Stockholm] +01:00[u-ca=hebrew] +0100[Europe/Stockholm] +0100 (CET) +0100 (Central European Time) Validation * Dates must be valid for the Gregorian calendar, including leap years * Hours: 0-23 (24-hour) or 1-12 (12-hour with AM/PM) * Minutes: 0-59 * Seconds: 0-60 (60 allows for leap seconds) Examples # ISO 8601 2024-12-24 2024-12-24T15:30 2024-12-24T15:30+01 2024-12-24T15:30:45,500+01 # RFC 3339 2024-12-24T15:30:45+01:00 2024-12-24T15:30:45.500+01:00 # RFC 9557 2024-12-24T15:30:45.500+01:00[Europe/Stockholm] # RFC 2822 Mon, 24 Dec 2024 15:30:45 +0100 24 Dec 2024 15:30 +0100 # RFC 2616 (HTTP) Mon, 24 Dec 2024 15:30:45 GMT # RFC 3501 (IMAP) 24-Dec-2024 15:30:45 +0100 # ISO 9075 (SQL) 2024-12-24 15:30:45 2024-12-24 15:30:45.500 +01:00 # ECMAScript Date.prototype.toString Mon Dec 24 2024 15:30:45 GMT+0100 (Central European Time) # Long-form textual Monday, 24 December 2024, 15:30 GMT+1 Monday, 24th December 2024 at 3:30 pm UTC+1 December 24th, 2024 at 3:30 PM # Short-form variations Dec/24/2024 03:30:45 PM 24. XII. 2024 12PM UTC+1 24DEC2024 12:30:45.500 24.Dec.2024 15:30:45 TIMEZONE ABBREVIATIONS The "DateTime", "RFC2822", and "Unix" parsers accept timezone abbreviations. * UTC designators ("UTC", "GMT", "UT", "Z") Unambiguous. Set "tz_utc" to the matched designator and "tz_offset" to 0, unless followed by a numeric offset. * All other abbreviations Returned as-is in "tz_abbrev". "tz_offset" will not be present. This module intentionally does not resolve abbreviations like "EST", "CET", or "IST" to numeric offsets, as many are ambiguous (e.g., "IST" could mean India Standard Time UTC+5:30, Israel Standard Time UTC+2, or Irish Standard Time UTC+1). "str2time" requires a resolved offset and will croak if only an unresolved abbreviation is present. Use "str2date" to retrieve the components including "tz_abbrev", then resolve externally: my %d = str2date('24 Dec 2012 15:30:45 IST', format => 'RFC2822'); # (..., tz_abbrev => 'IST') STANDARDS * ISO 8601:2019 - Data elements and interchange formats * RFC 3339 - Date and Time on the Internet: Timestamps * RFC 2822 / RFC 5322 - Internet Message Format * RFC 2616 / RFC 7231 - HTTP/1.1 * RFC 3501 / RFC 9051 - Internet Message Access Protocol (IMAP) * RFC 4287 - The Atom Syndication Format * RFC 5280 - Internet X.509 Public Key Infrastructure (PKIX) * RFC 5545 - Internet Calendaring and Scheduling (iCalendar) * RFC 9557 - Date and Time on the Internet: Timestamps with Additional Information * ISO 9075 - SQL Database Language * ITU-T X.680 (ISO/IEC 8824-1) - ASN.1 * W3C Date and Time Formats * ECMAScript Date.prototype.toString RATIONALE strftime and strptime strftime and strptime are the traditional C library interfaces for formatting and parsing date/time strings. While widely available, they have limitations that make them unsuitable for reliable timestamp handling across formats: * Both "strftime" and "strptime" are subject to the current locale. Month and day names vary by locale, making them unreliable for formats that require English names (e.g., RFC 2822, RFC 2616). Even English locales are not consistent - abbreviated month names may differ (e.g., "Sep" vs "Sept"). Using these functions for standard formats requires the C locale; altering the locale in a running process has issues with thread safety, side effects, and slow performance. * "strptime" requires a fixed format string and cannot express optional components (e.g., optional day name in RFC 2822, optional seconds in ISO 8601). Handling variations requires multiple "strptime" calls or manual pre-processing. * "strptime" cannot parse ISO 8601 basic format date-times (e.g., "20241224T153045Z") where date and time components lack separators. * "strftime" cannot produce ISO 8601 extended timezone offsets ("±HH:MM") portably. The %z conversion produces basic format ("±HHMM") and there is no standard conversion for the extended form. * Neither interface handles fractional seconds or nanosecond precision. * There is no built-in validation of parsed components - out-of-range values such as month 13, day 32, or hour 25 are silently accepted. Time::Str provides named format specifiers that encapsulate the parsing and formatting rules for each standard, with strict validation of all components. Numeric Date Ambiguity Numeric dates in year-month-day order ("YYYY-MM-DD") are unambiguous. However, when the year is not the leading component, the interpretation depends on convention: "01/02/2024" is January 2nd in American format (MM/DD/YYYY) but February 1st in European format (DD/MM/YYYY). When both the first and second components are in the range 1-12, either interpretation produces a valid date - 144 such combinations exist (12 months x 12 days), making it impossible to determine the intended format from the string alone. Existing modules handle this differently: Date::Parse Assumes American (MM/DD). Swaps to year-month-day only if the first value exceeds 12. Documents this as a known bug with no workaround. Date::Parse::Modern Assumes European (DD/MM/YYYY) for non-year-leading formats. Swaps day and month if the month value exceeds 12. Time::ParseDate Assumes American (MM/DD) by default. A "UK" option switches to European (DD/MM). Applies heuristics when values exceed 12. Time::Str avoids this problem entirely: numeric dates must be in year-month-day order. Ambiguous formats are rejected. When the month is given as a name or Roman numeral, day and year may appear in any order since the month is unambiguous. Two-Digit Year Ambiguity A two-digit year introduces century ambiguity: "01/02/03" could be 2003-01-02, 2003-02-01, or 2001-02-03 depending on which component is assumed to be the year and which convention is used for month and day. Existing modules apply different heuristics: Date::Parse Assumes the year is the last component (MM/DD/YY). Expands two-digit years using a fixed split: 69-99 to 1969-1999, 0-68 to 2000-2068. Date::Parse::Modern Requires a four-digit year in numeric dates. Two-digit year dates such as "01/02/03" are not recognised. When a two-digit year appears with a textual month (e.g., "17 March 94"), it is unconditionally mapped to the 1900s. Time::ParseDate Infers the year position from value ranges. When all three values are 12 or below, defaults to MM/DD/YY. Expands two-digit years relative to the current year with "PREFER_PAST" and "PREFER_FUTURE" options. The same input produces different results depending on the module and, in some cases, the current date. The "datetime" format in Time::Str requires a four-digit year, avoiding century ambiguity entirely. Formats that inherently use two-digit years ("ASN1UT", RFC 850 within "RFC2616") provide a configurable "pivot_year" parameter with a documented default. Ambiguous Timezone Abbreviations Timezone abbreviations such as "EST", "IST", and "CST" are inherently ambiguous. "IST" may refer to India Standard Time (+05:30), Israel Standard Time (+02:00), or Irish Standard Time (+01:00). "CST" may refer to US Central Standard Time (-06:00), China Standard Time (+08:00), or Cuba Standard Time (-05:00). There is no authoritative registry of abbreviations, and existing modules resolve them differently: Date::Parse , Date::Parse::Modern , and Time::ParseDate each ship their own lookup tables with conflicting mappings. Even when a mapping is correct for the present, it may not be historically accurate. Timezone rules change: regions adopt new offsets, daylight saving rules are revised or abolished, and political decisions alter zone boundaries. A static lookup table cannot account for these changes across time. Time::Str captures abbreviations in "tz_abbrev" without attempting to resolve them, leaving interpretation to the caller. DIAGNOSTICS Usage: %s (F) A function was called with an incorrect number of arguments. Parameter 'format' is unknown: '%s' (F) The format specifier is not recognised. Parameter '%s' is out of range (F) A parameter value is outside its valid range. Unknown named parameter: '%s' (F) An unrecognised named parameter was given. Unable to parse: %s (F) "str2date" or "str2time" could not parse the input string. Unable to convert: timestamp string without a UTC designator or numeric offset (F) "str2time" successfully parsed the input string but cannot convert it to a Unix timestamp without a UTC designator or numeric offset. LIMITATIONS * No support for locale-specific formatting or parsing. Month and day names are English only. * No support for ISO 8601 week-dates (e.g., "2024-W52-2") or ordinal dates (e.g., "2024-359"). * Timestamps are limited to the year range 0001-9999. Most string representation formats cannot represent years outside this range. * Timezone abbreviations (e.g., "EST", "IST") are captured in "tz_abbrev" but are not resolved to UTC offsets. "str2time" requires a UTC designator or numeric offset and will croak if only an abbreviation is present. EXPORTS Nothing is exported by default. Functions can be imported individually or by category: use Time::Str qw(time2str str2time str2date); # individual functions use Time::Str qw(:all); # all functions SEE ALSO Time::Moment, DateTime, Time::Piece AUTHOR Christian Hansen COPYRIGHT AND LICENSE Copyright (C) 2026 by Christian Hansen This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.