Regexp...::time(3pm)

πŸ“¦
version 0.16 Β· 2 versions
  • 0.16 Β· debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking (latest)
  • 0.16 Β· ubuntu jammy
, in debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

✨ Regexp::Common::time ✨

okay bestie, gather round β€” this is the Regexp::Common::time page from the User Contributed Perl Documentation section (it's a 3pm manpage, iykyk). It's the one that builds regexps for dates and times. Buckle up, it's a long one. πŸ’…


🌸 NAME 🌸

Regexp::Common::time β€” date and time regexps, fr fr.


πŸŽ€ SYNOPSIS πŸŽ€

use Regexp::Common qw(time);

# Piecemeal, Time::Format-like patterns
$RE{time}{tf}{-pat => 'pattern'}

# Piecemeal, strftime-like patterns
$RE{time}{strftime}{-pat => 'pattern'}

# Match ISO8601-style date/time strings
$RE{time}{iso}

# Match RFC2822-style date/time strings
$RE{time}{mail}
$RE{time}{MAIL}    # more-strict matching

# Match informal American date strings
$RE{time}{american}

# Fuzzy date patterns
#               YEAR/MONTH/DAY
$RE{time}{ymd}         # Most flexible
$RE{time}{YMD}         # Strictest (equivalent to y4m2d2)
                # Other available patterns: y2md, y4md, y2m2d2, y4m2d2

#               MONTH/DAY/YEAR  (American style)
$RE{time}{mdy}         # Most flexible
$RE{time}{MDY}         # Strictest (equivalent to m2d2y4)
                # Other available patterns: mdy2, mdy4, m2d2y2, m2d2y4

#               DAY/MONTH/YEAR  (European style)
$RE{time}{mdy}         # Most flexible
$RE{time}{MDY}         # Strictest (equivalent to d2m2y4)
                # Other available patterns: dmy2, dmy4, d2m2y2, d2m2y4

# Fuzzy time pattern
#               HOUR/MINUTE/SECOND
$RE{time}{hms}    # H: matches 1 or 2 digits; 12 or 24 hours
                  # M: matches 2 digits.
                  # S: matches 2 digits; may be omitted
                  # May be followed by "a", "am", "p.m.", etc.

πŸ’– DESCRIPTION πŸ’–

This module makes regular expressions you can use to parse dates and times. πŸ—“οΈ For the whole "how do I even use this interface" thing, go read Regexp::Common β€” it explains the general vibe.

ngl, parsing dates is a dirty business. 😭 Dates show up in one of three orders: year/month/day, month/day/year, or day/month/year. Years can be four digits or two digits (and then you just have to assume stuff about the century, love that for us). Months can be one digit, two digits, a spelled-out name, or a three-letter abbreviation. Day numbers can be one or two digits, and the limits depend on the month β€” and if it's February, bestie, it even depends on the year. πŸ™ˆ And on top of all that, everybody uses different punctuation between the pieces. Chaos.

A human clocking "October 21, 2005" and "21.10.05" as the same day? Easy. Getting a program to agree? That's the tricky part. This module is here to make that possible with the least amount of suffering. πŸ’…

  • If you know the exact format of the data you're matching, use one of the specific, piecemeal pattern builders: tf or strftime.
  • If you're parsing RFC-2822 mail headers, use the mail pattern.
  • If you're parsing informal American dates, use the american pattern.
  • If your input data is a little variable, use one of the fuzzy-matching patterns in the dmy, mdy, or ymd families.
  • If the data is wildly variable β€” like, raw user input levels of wild β€” you should probably give up and use Date::Manip or Date::Parse. 😭 Not me telling you to tap out, but... tap out.

Time values are generally way simpler to parse than date values, thank goodness. There's only one fuzzy pattern for time, and it should cover basically everything you need. ✨


πŸ¦‹ Time::Format PATTERNS πŸ¦‹

The Time::Format module uses simple, intuitive strings to describe date and time formats. You can use those same patterns right here. πŸ’– For the full deets on its format specifiers, go see Time::Format.

Example:

$str = 'Thu November 2, 2005';
$str =~ $RE{time}{tf}{-pat => 'Day Month d, yyyy'};

The patterns can hold more complex regexp expressions too, bestie:

$str =~ $RE{time}{tf}{-pat => '(Weekday|Day) (Month|Mon) d, yyyy'};

Time zone matching (that's the tz format code) tries its hardest to stick to RFC2822 and ISO8601. These time zones get matched:

Z
UT        UTC
+hh:mm    -hh:mm
+hhmm     -hhmm
+hh       -hh
GMT   EST EDT   CST CDT   MST MDT   PST PDT

🌈 strftime PATTERNS 🌈

The POSIX strftime function is a long-recognized standard for formatting dates and times β€” iconic, honestly. πŸ‘‘ This module supports most of its codes for matching; specifically, the aAbBcCDdeHIjmMnprRSTtuUVwWyxXYZ% codes. The %Z format matches time zones exactly the same way as described up in Time::Format PATTERNS above. 🫢

Also β€” this module hands you these nonstandard codes:

  • %_d β€” 1- or 2-digit day number (1-31)
  • %_H β€” 1- or 2-digit hour (0-23)
  • %_I β€” 1- or 2-digit hour (1-12)
  • %_m β€” 1- or 2-digit month number (1-12)
  • %_M β€” 1- or 2-digit minute (0-59)

Example:

$str = 'Thu November 2, 2005';
$str =~ $RE{time}{strftime}{-pat => '%a %B %_d, %Y'};

And yep, these patterns can hold more complex regexp expressions too:

$str =~ $RE{time}{strftime}{-pat => '(%A|%a)? (%B|%b) ?%_d, %Y'};

⭐ ISO-8601 DATE/TIME MATCHING ⭐

The $RE{time}{iso} pattern matches most (all? πŸ‘€) strings formatted the way ISO-8601 recommends. The canonical ISO-8601 form is:

YYYY-MM-DDTHH:MM:SS

(where "T" is a literal T character, no cap). The $RE{time}{iso} pattern matches that form, plus some variants:

  • The date separator character may be a hyphen, a slash ("/"), a period, or an empty string (omitted). The two date separators must match.
  • The time separator character may be a colon, a period, a space, or an empty string (omitted). The two time separators must match.
  • The date-time separator may be a T, an underscore, a space, or an empty string (omitted).
  • Either the date or the time may be omitted. But at least one has to be there. πŸ’€
  • If the date is not omitted, all three of its components must be present.
  • If the time is not omitted, all three of its components must be present.

πŸ’Œ RFC 2822 MATCHING πŸ’Œ

RFC 2822 specifies the format of date/time values in e-mail message headers. In a nutshell, the format is:

dd Mon yyyy hh:mm:ss +zzzz

where dd is the day of the month; Mon is the abbreviated month name (apparently always in English); yyyy is the year; hh:mm:ss is the time; and +zzzz is the time zone, generally given as an offset from GMT.

RFC 2822 also requires the weekday be specified, but this module ignores the weekday β€” it's redundant and only there for human readability anyway. πŸ™ˆ

RFC 2822 also requires that older, obsolete date forms be allowed, like alphanumeric time zone codes (e.g. EDT). This module's mail allows for those obsolete date forms. But if you want to match only the proper date forms recommended by RFC 2822, you can use the MAIL pattern instead. ✨

Either way β€” mail or MAIL β€” the pattern it generates is very flexible about whitespace. The main differences: with MAIL, two-digit years are not permitted, and the time zone must be four digits preceded by a + or - sign.


πŸ‡ΊπŸ‡Έ INFORMAL AMERICAN MATCHING πŸ‡ΊπŸ‡Έ

People in North America β€” especially in the United States β€” are fond of writing dates as "Month dd, yyyy", or sometimes with a two-digit year and an apostrophe: "Month dd, 'yy". The american pattern matches this style of date. It allows either a month name or a month abbreviation, and it's chill about commas and whitespace. πŸ’…


🧚 FUZZY PATTERN OVERVIEW 🧚

Fuzzy date patterns all share these properties, bestie:

  • The pattern names are made of the letters y, m, and d, each optionally followed by a digit (2 for m and d; 2 or 4 for y).
  • If a y is followed by a 2 or a 4, it must match that many digits.
  • If a y has no trailing digit, it can match either 2 or 4 digits, trying 4 first.
  • If an m is followed by a 2, then only two-digit matches for the month are considered, and month names are not matched.
  • If an m is not followed by a 2, then the month may be 1 or 2 digits, or a spelled-out name.
  • Just like for months: if a d is followed by a 2, then only two-digit matches for the day are considered.
  • Just like for months: if a d has no trailing digit, then the day may be 1 or 2 digits, and a 1-digit match may not have any adjacent digits.
  • The uppercase DMY, MDY, and YMD patterns are synonyms for the strict d2m2y4, m2d2y4, and y4m2d2 patterns, respectively.
  • If a one-digit match is considered for the month, then no adjacent digits are allowed. (e.g.: "1/23/45" in M/D/Y format has a valid one-digit month match, but "12345" does not. Nor does "91/23/45".)
  • If a pattern begins with a digitless d, m, or y, then, in the string being matched, any leading digits will cause the pattern to fail. For example: "012/23/45" will not match $RE{time}{mdy}. However, it will match $RE{time}{m2d2y2}. If you specify an exact pattern by using m2 instead of m, this module assumes you know what you're doing. πŸ‘‘
  • Likewise, a pattern ending with a digitless d or y will not match if there are trailing digits in the string.

πŸ” FUZZY PATTERN DETAILS πŸ”

okay bestie, this is where it gets good β€” all the actual patterns, one by one, no skips πŸ’…

πŸ“… Year-Month-Day order πŸ“…

  • $RE{time}{ymd}
"05/4/2"      =~ $RE{time}{ymd};
"2005-APR-02" =~ $RE{time}{ymd};

This one is the most flexible of all the numeric-only year/month/day formats β€” she does the most, periodt. It matches a date shaped like year/month/day, where:

  • the year can be 2 or 4 digits πŸŽ€
  • the month can be 1 or 2 digits, OR a spelled-out name, OR a name abbreviation
  • the day can be 1 or 2 digits

And the year/month/day pieces can be squished right up against each other, or separated by a space, a period, a slash (/), or a hyphen. She's flexible like that πŸ’–

  • $RE{time}{y4md}
"2005/4/2"    =~ $RE{time}{y4md};
"2005 APR 02" =~ $RE{time}{y4md};

Same energy as $RE{time}{ymd}, except the year is locked to exactly 4 digits. no negotiation ✨

  • $RE{time}{y4m2d2}
"2005/04/02" =~ $RE{time}{y4m2d2};

Again like $RE{time}{ymd}, but the year is exactly 4 digits and both the month and the day must be exactly 2 digits each. strict queen πŸ‘‘

  • $RE{time}{y2md}
"05/4/2"    =~ $RE{time}{y2md};
"05.APR.02" =~ $RE{time}{y2md};

Like $RE{time}{ymd}, but the year is exactly 2 digits. that's it, that's the whole vibe 🌸

  • $RE{time}{y2m2d2}
"05/04/02" =~ $RE{time}{y2m2d2};

Like $RE{time}{ymd}, but the year is exactly 2 digits and the month and day must each be exactly 2 digits. everyone's on their 2-digit best behavior 🫢

  • $RE{time}{YMD}
"2005/04/02" =~ $RE{time}{YMD};

This is just a shorthand for the "canonical" year/month/day format, y4m2d2. iconic abbreviation behavior πŸ’…

πŸ‡ΊπŸ‡Έ Month-Day-Year (American) order πŸ‡ΊπŸ‡Έ

  • $RE{time}{mdy}
  • $RE{time}{mdy4}
  • $RE{time}{m2d2y4}
  • $RE{time}{mdy2}
  • $RE{time}{m2d2y2}
  • $RE{time}{MDY}

These do exactly what the equivalent year/month/day patterns above do β€” the only difference is the order the elements show up in. And MDY is a synonym for m2d2y4. same brain, different outfit ✨

πŸ‡ͺπŸ‡Ί Day-Month-Year (European) order πŸ‡ͺπŸ‡Ί

  • $RE{time}{dmy}
  • $RE{time}{dmy4}
  • $RE{time}{d2m2y4}
  • $RE{time}{dmy2}
  • $RE{time}{d2m2y2}
  • $RE{time}{DMY}

Same deal, European edition: these work like the equivalent year/month/day patterns above, and the only difference is element order. DMY is a synonym for d2m2y4. slay across the pond 🌈

⏰ Time pattern (Hour-minute-second) ⏰

  • $RE{time}{hms}
"10:06:12a" =~ /$RE{time}{hms}/;
"9:00 p.m." =~ /$RE{time}{hms}/;

Matches a time value inside a string. here are the rules, bestie, don't skip them:

  • The hour must be in the range 0 to 24.
  • The minute and second values must be in the range 0 to 59, and must be two digits β€” meaning they need leading zeroes if they're less than 10. no shortcuts πŸŽ€
  • The hour, minute, and second components can be separated by colons (:), periods, or spaces.
  • The "seconds" value may be omitted.
  • The time may be followed by an "am/pm" indicator, which is one of these exact values:
a   am   a.m.  p   pm   p.m.   A   AM   A.M.  P   PM   P.M.
  • And yes, there may be a space between the time and the am/pm indicator. she's chill about the gap πŸ’–

🎁 CAPTURES (-keep) 🎁

Under -keep, the tf and strftime patterns capture the entire match as $1, plus one capture variable for each format specifier. BUT β€” huge but β€” if your pattern contains any parentheses, tf and strftime will not capture anything extra beyond what you yourself specified, -keep or not. In other words: if you use parentheses, you are responsible for all the capturing. it's giving "you built this, you own this" πŸ’€

The iso pattern captures:

  • $1 β€” the entire match
  • $2 β€” the year
  • $3 β€” the month
  • $4 β€” the day
  • $5 β€” the hour
  • $6 β€” the minute
  • $7 β€” the second

Heads up: the year, month, and day ($2, $3, and $4) will be undef if the matched string contains only a time value (like 12:34:56). And the hour, minute, and second ($5, $6, and $7) will be undef if the matched string contains only a date value (like 2005-01-23). she gives you what exists, and nothing else πŸ™ˆ

The mail and MAIL patterns capture:

  • $1 β€” the entire match
  • $2 β€” the day
  • $3 β€” the month
  • $4 β€” the year
  • $5 β€” the hour
  • $6 β€” the minute
  • $7 β€” the second
  • $8 β€” the time zone

The american pattern captures:

  • $1 β€” the entire match
  • $2 β€” the month
  • $3 β€” the day
  • $4 β€” the year

The fuzzy y/m/d patterns capture:

  • $1 β€” the entire match
  • $2 β€” the year
  • $3 β€” the month
  • $4 β€” the day

The fuzzy m/d/y patterns capture:

  • $1 β€” the entire match
  • $2 β€” the month
  • $3 β€” the day
  • $4 β€” the year

The fuzzy d/m/y patterns capture:

  • $1 β€” the entire match
  • $2 β€” the day
  • $3 β€” the month
  • $4 β€” the year

The fuzzy h/m/s pattern captures:

  • $1 β€” the entire match
  • $2 β€” the hour
  • $3 β€” the minute
  • $4 β€” the second (undef if omitted)
  • $5 β€” the am/pm indicator (undef if omitted)

✨ EXAMPLES ✨

okay here's where it all clicks, bestie, watch this πŸ‘‘

# Typical usage: parsing a data record.
#
$rec = "blah blah 2005/10/21 blah blarrrrrgh";
@date = $rec =~ m{^blah blah $RE{time}{YMD}{-keep}};
# or
@date = $rec =~ m{^blah blah $RE{time}{tf}{-pat=>'yyyy/mm/dd'}{-keep}};
# or
@date = $rec =~ m{^blah blah $RE{time}{strftime}{-pat=>'%Y/%m/%d'}{-keep}};

and now the chaotic version, where the format is not your friend:

# Typical usage: parsing variable-format data.
#
use Time::Normalize;

$record = "10-SEP-2005";

# This block tries M-D-Y first, then D-M-Y, then Y-M-D
my $matched;
foreach my $pattern (qw(mdy dmy ymd))
{
    @values = $record =~ /^$RE{time}{$pattern}{-keep}/
        or next;

    $matched = $pattern;
}
if ($matched)
{
    eval{ ($year, $month, $day) = normalize_rct($matched, @values) };
    if ($@)
    {
        .... # handle erroneous data
    }
}
else
{
    .... # no match
}
#
# $day is now 10; $month is now 09; $year is now 2005.

and the time examples, because we do those too πŸ’–

# Time examples

$time = '9:10pm';

@time_data = $time =~ /$RE{time}{hms}{-keep}/;
# captures '9:10pm', '9', '10', undef, 'pm'

@time_data = $time =~ /$RE{time}{tf}{-pat => '(h):(mm)(:ss)?(am)?'}{-keep}/;
# captures '9', '10', undef, 'pm'

πŸ“¦ EXPORTS πŸ“¦

This module exports no symbols to the caller's namespace. she minds her own business, respectfully πŸŽ€


πŸ‘€ SEE ALSO πŸ‘€

ngl bestie, it's not enough that the date regexps can match a bunch of formats β€” after that you still have to parse those matched data values and translate them into useful values. So: the Time::Normalize module is highly recommended for doing that repetitive, error-prone task. let it carry the emotional labor for you 🫢


πŸ› οΈ REQUIREMENTS πŸ› οΈ

Requires Regexp::Common, of course. obviously. it's the whole point πŸ’…

If POSIX and I18N::Langinfo are available, this module will use them; otherwise, it falls back to hardcoded English values for month and weekday names. improvising, but in English 🌸

Test::More is required for the test suite. gotta test, no cap ✨


πŸ’œ AUTHOR πŸ’œ

Eric J. Roode, ROODE -at- cpan -dot- org


Copyright (c) 2005-2008 by Eric J. Roode, ROODE -at- cpan -dot- org

All rights reserved.

To avoid my spam filter, please include "Perl", "module", or this module's name in the message's subject line, and/or GPG-sign your message. (he's asking nicely, bestie, just do it πŸ™ˆ)

This module is copyrighted only to ensure proper attribution of authorship and to ensure that it remains available to all. This module is free, open-source software. This module may be freely used for any purpose β€” commercial, public, or private β€” provided that proper credit is given, and that no more-restrictive license is applied to derivative (not dependent) works. generous king behavior πŸ‘‘

Substantial efforts have been made to ensure that this software meets high quality standards; however, no guarantee can be made that there are no undiscovered bugs, and no warranty is made as to suitability to any given use, including merchantability. And then the absolutely unhinged disclaimer, which I am legally obligated to relay verbatim in spirit: should this module cause your house to burn down, your dog to collapse, your heart-lung machine to fail, your spouse to desert you, or George Bush to be re-elected, the author can offer only his sincere sympathy and apologies, and promises to endeavor to improve the software. πŸ˜­πŸ’€ not the heart-lung machineβ€”


aaaand that's a wrap on Regexp::time(3pm), bestie!! this page came from the Perl v5.36.0 docs, dated 2022-12-06, in the User Contributed Perl Documentation collection. you now know more about date regexps than 99% of people alive. go forth and parse responsibly πŸ¦‹βœ¨πŸ’–