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 withinRFC2616,RFC5280), 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:
49becomes 2049,50becomes 1950,99becomes 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_offsetreflects that offset rather than zero.tz_abbrev- The timezone abbreviation as it appeared in the input, if present and not a UTC designator.tz_offsetwill not be present whentz_abbrevis set. See "TIMEZONE ABBREVIATIONS".tz_annotation- Bracketed timezone tag from the input, if present (RFC 9557 IXDTF or JavaZoneIdformat, e.g.,[Europe/Stockholm]). Informational only; does not affecttz_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
.999at 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
$timeand bypasses rounding. Use this for exact control over fractional output or to preserve nanosecond precision that floating-point cannot represent. Can be combined withprecisionto control zero-padding.
Returns
A formatted date/time string.
Errors
Croaks if:
$timeis outside the supported rangeA 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
nanosecondparameter 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.
FORMAT OVERVIEW
Profiles of ISO 8601
ISO8601 20241224T153045.500+0100
RFC4287 2024-12-24T15:30:45Z
W3CDTF 2024-12-24T15:30:45+01:00
RFC5545 20241224T153045Z
Based on ISO 8601
RFC3339 2024-12-24 15:30:45+01:00
RFC9557 2024-12-24 15:30:45+01:00[Europe/Paris]
ISO9075 2024-12-24 15:30:45 +01:00
ASN1GT 20241224153045Z
ASN1UT 241224153045Z
RFC5280 241224153045Z
RFC / Email / HTTP / IMAP
RFC2822 Tue, 24 Dec 2024 15:30:45 +0100
RFC2616 Tue, 24 Dec 2024 15:30:45 GMT
RFC3501 24-Dec-2024 15:30:45 +0100
Unix / C Library
ANSIC Tue Dec 24 15:30:45 2024
UnixDate Tue Dec 24 15:30:45 UTC 2024
UnixStamp Tue Dec 24 15:30:45.500 2024 UTC
GitDate Tue Dec 24 15:30:45 2024 +0100
RubyDate Tue Dec 24 15:30:45 +0100 2024
UnixStamp is a superset of ANSIC, GitDate, RubyDate, and UnixDate.
Other
ECMAScript Tue Dec 24 2024 15:30:45 GMT+0100
CLF 24/Dec/2024:15:30:45 +0100
DateTime (permissive, multi-format parser)
DateTime can parse ISO8601 (extended format), RFC3339, RFC9557, RFC4287, W3CDTF, ISO9075, RFC2822, RFC2616, RFC3501, and ECMAScript formats.
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
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.
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
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. A superset of ANSIC, GitDate, RubyDate, and UnixDate formats.
[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
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. 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.
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
Day names, when present, must match the date
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_utcto the matched designator andtz_offsetto 0, unless followed by a numeric offset.All other abbreviations
Returned as-is in
tz_abbrev.tz_offsetwill not be present. This module intentionally does not resolve abbreviations likeEST,CET, orISTto numeric offsets, as many are ambiguous (e.g.,ISTcould 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
RFC 3501 / RFC 9051 - Internet Message Access Protocol (IMAP)
RFC 9557 - Date and Time on the Internet: Timestamps with Additional Information
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
strftimeandstrptimeare 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.,SepvsSept). 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.strptimerequires 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 multiplestrptimecalls or manual pre-processing.strptimecannot parse ISO 8601 basic format date-times (e.g.,20241224T153045Z) where date and time components lack separators.strftimecannot produce ISO 8601 extended timezone offsets (±HH:MM) portably. The%zconversion 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
UKoption 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/03are 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_PASTandPREFER_FUTUREoptions.
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, RFC5280) 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)
str2dateorstr2timecould not parse the input string. - Unable to convert: timestamp string without a UTC designator or numeric offset
-
(F)
str2timesuccessfully 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 intz_abbrevbut are not resolved to UTC offsets.str2timerequires 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.