NAME
Time::Normalize - Convert time and date values into standardized components.
VERSION
This is version 0.06 of Normalize.pm, May 25, 2008.
SYNOPSIS
use Time::Normalize;
$hashref = normalize_ymd ($in_yr, $in_mo, $in_d);
($year, $mon, $day,
$dow, $dow_name, $dow_abbr,
$mon_name, $mon_abbr) = normalize_ymd ($in_yr, $in_mo, $in_dy);
$hashref = normalize_ym ($in_yr, $in_mo);
@same_values_as_for_normalize_ymd = normalize_ym ($in_yr, $in_mo);
$hashref = normalize_hms ($in_h, $in_m, $in_s, $in_ampm);
($hour, $min, $sec,
$h12, $ampm, $since_midnight)
= normalize_hms ($in_h, $in_m, $in_s, $in_ampm);
$hashref = normalize_time ($time_epoch);
($sec, $min, $hour,
$day, $mon, $year,
$dow, $yday, $isdst,
$h12, $ampm, $since_midnight,
$dow_name, $dow_abbr,
$mon_name, $mon_abbr) = normalize_time ($time_epoch);
$hashref = normalize_gmtime ($time_epoch);
@same_values_as_for_normalize_time = normalize_gmtime ($time_epoch);
$year = normalize_year($input_year);
$month = normalize_month($input_month);
Utility functions (not exported by default):
use Time::Normalize qw(mon_name mon_abbr day_name day_abbr
days_in is_leap );
$name = mon_name($month_number); # input: 1 to 12
$abbr = mon_abbr($month_number); # input: 1 to 12
$name = day_name($weekday_number); # input: 0(Sunday) to 6
$abbr = day_abbr($weekday_number); # input: 0(Sunday) to 6
$num = days_in($month, $year);
$bool = is_leap($year);
DESCRIPTION
Splitting a date into its component pieces is just the beginning.
Human date conventions are quirky (and I'm not just talking about the dates I've had!) Despite the Y2K near-disaster, some people continue to use two-digit year numbers. Months are sometimes specified as a number from 1-12, sometimes as a spelled-out name, sometimes as a abbreviation. Some months have more days than others. Humans sometimes use a 12-hour clock, and sometimes a 24-hour clock.
This module performs simple but tedious (and error-prone) checks on its inputs, and returns the time and/or date components in a sanitized, standardized manner, suitable for use in the remainder of your program.
Even when you get your values from a time-tested library function, such as localtime
or gmtime
, you need to do routine transformations on the returned values. The year returned is off by 1900 (for historical reasons); the month is in the range 0-11; you may want the month name or day of week name instead of numbers. The "normalize_time" function decodes localtime
's values into commonly-needed formats.
FUNCTIONS
- normalize_ymd
-
$hashref = normalize_ymd ($in_yr, $in_mo, $in_d); ($year, $mon, $day, $dow, $dow_name, $dow_abbr, $mon_name, $mon_abbr) = normalize_ymd ($in_yr, $in_mo, $in_dy);
Takes an arbitrary year, month, and day as input, and returns various data elements in a standard, consistent format. The output may be a hash reference or a list of values. If a hash reference is desired, the keys of that hash will be the same as the variable names given in the above synopsis; that is,
day
,dow
,dow_abbr
,dow_name
,mon
,mon_abbr
,mon_name
, andyear
.Input:
The input year may be either two digits or four digits. If two digits, the century is chosen so that the resulting four-digit year is closest to the current calendar year (i.e., within 50 years).
The input month may either be a number from 1 to 12, or a full month name as defined by the current locale, or a month abbreviation as defined by the current locale. If it's a name or abbreviation, case is not significant.
The input day must be a number from 1 to the number of days in the specified month and year.
If any of the input values do not meet the above criteria, an exception will be thrown. See "DIAGNOSTICS".
Output:
year -
will always be four digits.month -
will always be two digits, 01-12.day -
will always be two digits 01-31.dow -
will be a number from 0 (Sunday) to 6 (Saturday).dow_name -
will be the name of the day of the week, as defined by the current locale, in the locale's preferred case.dow_abbr -
will be the standard weekday name abbreviation, as defined by the current locale, in the locale's preferred case.mon_name -
will be the month name, as defined by the current locale, in the locale's preferred case.mon_abbr -
will be the standard month name abbreviation, as defined by the current locale, in the locale's preferred case. - normalize_ym
-
$hashref = normalize_ym ($in_yr, $in_mo); ($year, $mon, $day, $dow, $dow_name, $dow_abbr, $mon_name, $mon_abbr) = normalize_ym ($in_yr, $in_mo);
Works exactly like normalize_ymd, except that it does not take an input day. Instead, it computes the last day of the specified year and month, and returns the values associated with that date.
This is equivalent to the following sequence:
normalize_ymd ($in_yr, $in_mo, days_in ($in_mo, $in_yr));
- normalize_year
-
$year = normalize_year ($in_yr);
This takes a two-digit or four-digit year, and returns the four-digit year.
- normalize_month
-
$month = normalize_month ($in_mo);
This takes a numeric month (1-12), alphabetic spelled-out month, or alphabetic month abbreviation, and returns the two-digit month number (01-12).
- normalize_hms
-
$hashref = normalize_hms ($in_h, $in_m, $in_s, $in_ampm); ($hour, $min, $sec, $h12, $ampm, $since_midnight) = normalize_hms ($in_h, $in_m, $in_s, $in_ampm);
Like "normalize_ymd",
normalize_hms
takes a variety of possible inputs and returns standardized values. As above, the output may be a hash reference or a list of values. If a hash reference is desired, the keys of that hash will be the same as the variable names given in the above synopsis; that is,ampm
,h12
,hour
,min
,sec
, andsince_midnight
. Also, ah24
key is provided as a synonym forhour
.Input:
The input hour may either be a 12-hour or a 24-hour time value. If
$in_ampm
is specified,$in_h
is assumed to be on a 12-hour clock, and if$in_ampm
is absent,$in_h
is assumed to be on a 24-hour clock.The input minute must be numeric, and must be in the range 0 to 59.
The input second
$in_s
is optional. If omitted, it defaults to 0. If specified, it must be in the range 0 to 59.The AM/PM indicator
$in_ampm
is optional. If specified, it may be any of the following:a am a.m. p pm p.m. A AM A.M. P PM P.M.
If any of the input values do not meet the above criteria, an exception will be thrown. See "DIAGNOSTICS".
Output:
hour -
The first output hour will always be on a 24-hour clock, and will always be two digits, 00-23.min -
will always be two digits, 00-59.sec -
will always be two digits, 00-59.h12 -
is the 12-hour clock equivalent of$hour
. It is not zero-padded.ampm -
will always be either a lowercase 'a' or a lowercase 'p', no matter what format the input AM/PM indicator was, or even if it was omitted.since_midnight -
is the number of seconds since midnight represented by this time.h24 -
is a key created if you request a hashref as the output; it's a synonym forhour
. - normalize_ymdhms
-
$hashref = normalize_ymdhms ($in_y, $in_mon, $in_d, $in_h, $in_min, $in_s, $in_ampm); ($year, $mon, $day, $hour, $min, $sec) = normalize_ymdhms ($in_y, $in_mon, $in_d, $in_h, $in_min, $in_s, $in_ampm);
This is a convenience function that combines the capabilities of "normalize_ymd" and "normalize_hms". As input, it takes a year, month, and day (like normalize_ymd); and an hour, minute, optional second, and optional am/pm indicator (like normalize_hms).
In list context, it returns the year, month, day, hour, minute, and second. (It does not return all the extra values that normalize_ymd and normalize_hms return in list context).
In scalar context, it returns a reference to a hash that contains all of the normalized values that normalize_ymd and normalize_hms return.
- normalize_rct
-
$hashref = normalize_rct ($type, @values); @list = normalize_rct ($type, @values);
This function normalizes the result of a pattern match from the Regexp::Common::time module.
Regexp::Common::time contains several stock regular expression patterns for matching common date and/or time formats. This function completes the process of parsing the matched date/time values.
The
$type
parameter indicates which Regexp::Common::time pattern was matched, and the@values
are the strings that were captured via the-keep
parameter. Supported patterns are:iso
,mail
,MAIL
,american
,ymd
,mdy
,dmy
,hms
, and variants such asy4m2d2
, etc.For the
iso
,mail
, andMAIL
patterns, normalize_rct returns the same values as the "normalize_ymdhms" function.For the
american
,ymd
,dmy
and other date-only patterns, normalize_rct returns the same values as the "normalize_ymd" function.For the
hms
pattern, normalize_rct returns the same values as "normalize_hms".Example:
$input = 'Fri, 23 May 2008 23:02:04 +0500'; @vals = $input =~ $RE{time}{mail} or die 'Bad input'; $norm = normalize_rct ('mail', @vals); print "$norm->{dow_name} at $norm->{h12} $norm->{ampm}"; # prints "Friday at 11 pm"
See "EXAMPLES WITH Regexp::Common::time" for more examples.
- normalize_time
-
$hashref = normalize_time($time_epoch); ($sec, $min, $hour, $day, $mon, $year, $dow, $yday, $isdst, $h12, $ampm, $since_midnight, $dow_name, $dow_abbr, $mon_name, $mon_abbr) = normalize_time($time_epoch);
Takes a number in the usual perl epoch, passes it to localtime, and transforms the results. If
$time_epoch
is omitted, the current time is used instead.The output values (or hash values) are exactly as for "normalize_ymd" and "normalize_hms", above.
- normalize_gmtime
-
Exactly the same as normalize_time, but uses gmtime internally instead of localtime.
- mon_name
-
$name = mon_name($m);
Returns the full name of the specified month
$m
;$m
ranges from 1 (January) to 12 (December). The name is returned in the language and case appropriate for the current locale. - mon_abbr
-
$abbr = mon_abbr($m);
Returns the abbreviated name of the specified month
$m
;$m
ranges from 1 (Jan) to 12 (Dec). The name is returned in the language and case appropriate for the current locale. - day_name
-
$name = day_name($d);
Returns the full name of the specified day of the week
$d
;$d
ranges from 0 (Sunday) to 6 (Saturday). The name is returned in the language and case appropriate for the current locale. - day_abbr
-
$abbr = day_abbr($d);
Returns the abbreviated name of the specified day of the week
$d
;$d
ranges from 0 (Sun) to 6 (Sat). The name is returned in the language and case appropriate for the current locale. - days_in
-
$num = days_in ($month, $year);
Returns the number of days in the specified month and year. If the month is not 2 (February),
$year
isn't even examined. - is_leap
-
$boolean = is_leap ($year);
Returns
true
if the given year is a leap year, according to the usual Gregorian rules.
DIAGNOSTICS
The functions in this module throw exceptions (that is, they croak
) whenever invalid arguments are passed to them. Therefore, it is generally a Good Idea to trap these exceptions with an eval
block.
The error messages are meant to be easy to parse, if you need to. There are two kinds of errors thrown: data errors, and programming errors.
Data errors are caused by invalid data values; that is, values that do not conform to the expectations listed above. These messages all look like:
Time::Normalize: Invalid
thing: "
value"
Programming errors are caused by you--passing the wrong number or type of parameters to a function. These messages look like one of the following::
1. Too
{many|few} arguments to
function_name
2. Non-integer month "
month" for mon_name
3. Unknown Regexp::Common::time pattern: "
type"
#1 can be thrown by almost any of the functions. #2 can only be thrown by the "days_in" function. #3 can only be thrown by the "normalize_rct" function.
EXAMPLES
$h = normalize_ymd (2005, 'january', 4);
#
# Returns:
# $h->{day} "04"
# $h->{dow} 2
# $h->{dow_abbr} "Tue"
# $h->{dow_name} "Tuesday"
# $h->{mon} "01"
# $h->{mon_abbr} "Jan"
# $h->{mon_name} "January"
# $h->{year} 2005
# ------------------------------------------------
$h = normalize_ymd ('05', 12, 31);
#
# Returns:
# $h->{day} 31
# $h->{dow} 6
# $h->{dow_abbr} "Sat"
# $h->{dow_name} "Saturday"
# $h->{mon} 12
# $h->{mon_abbr} "Dec"
# $h->{mon_name} "December"
# $h->{year} 2005
# ------------------------------------------------
$h = normalize_ymd (2005, 2, 29);
#
# Throws an exception:
# Time::Normalize: Invalid day: "29"
# ------------------------------------------------
$h = normalize_hms (9, 10, 0, 'AM');
#
# Returns:
# $h->{ampm} "a"
# $h->{h12} 9
# $h->{h24} "09"
# $h->{hour} "09"
# $h->{min} 10
# $h->{sec} "00"
# $h->{since_midnight} 33000
# ------------------------------------------------
$h = normalize_hms (9, 10, undef, 'p.m.');
#
# Returns:
# $h->{ampm} "p"
# $h->{h12} 9
# $h->{h24} 21
# $h->{hour} 21
# $h->{min} 10
# $h->{sec} "00"
# $h->{since_midnight} 76200
# ------------------------------------------------
$h = normalize_hms (1, 10);
#
# Returns:
# $h->{ampm} "a"
# $h->{h12} 1
# $h->{h24} "01"
# $h->{hour} "01"
# $h->{min} 10
# $h->{sec} "00"
# $h->{since_midnight} 4200
# ------------------------------------------------
$h = normalize_hms (13, 10);
#
# Returns:
# $h->{ampm} "p"
# $h->{h12} 1
# $h->{h24} 13
# $h->{hour} 13
# $h->{min} 10
# $h->{sec} "00"
# $h->{since_midnight} 47400
# ------------------------------------------------
$h = normalize_hms (13, 10, undef, 'pm');
#
# Throws an exception:
# Time::Normalize: Invalid hour: "13"
# ------------------------------------------------
$h = normalize_gmtime(1131725587);
#
# Returns:
# $h->{ampm} "p"
# $h->{sec} "07",
# $h->{min} 13,
# $h->{hour} 16,
# $h->{day} 11,
# $h->{mon} 11,
# $h->{year} 2005,
# $h->{dow} 5,
# $h->{yday} 314,
# $h->{isdst} 0,
# $h->{h12} 4
# $h->{ampm} "p"
# $h->{since_midnight} 58_387,
# $h->{dow_name} "Friday",
# $h->{dow_abbr} "Fri",
# $h->{mon_name} "November",
# $h->{mon_abbr} "Nov",
# ------------------------------------------------
EXAMPLES WITH Regexp::Common::time
This module plus Regexp::Common::time is a powerful combination for parsing date and time input.
use Regexp::Common qw(time);
use Time::Normalize;
# Informal American-style dates
$input = "January 7, '08";
@vals = $input =~ $RE{time}{american}{-keep};
$d = normalize_rct('american', @vals);
print "$d->{year}/$d->{mon}/$d->{day} was a $d->{dow_name}";
# Prints: 2008/01/07 was a Monday
#
$input = "Jan 7, 2008";
@vals = $input =~ $RE{time}{american}{-keep};
$d = normalize_rct('american', @vals);
print "$d->{year}/$d->{mon}/$d->{day} was a $d->{dow_name}";
# Prints: 2008/01/07 was a Monday
# European-style day/month/year dates
$input = '7 March 2007';
@vals = $input =~ $RE{time}{dmy}{-keep};
$d = normalize_rct('dmy', @vals);
print "$d->{year}/$d->{mon}/$d->{day} was a $d->{dow_name}";
# Prints: 2007/03/07 was a Friday
#
$input = '07.03.07';
@vals = $input =~ $RE{time}{dmy}{-keep};
$d = normalize_rct('dmy', @vals);
print "$d->{year}/$d->{mon}/$d->{day} was a $d->{dow_name}";
# Prints: 2007/03/07 was a Friday
# Time parsing:
$input = '13:24';
@vals = $input =~ $RE{time}{hms}{-keep};
$t = normalize_rct('hms', @vals);
print "$t->{hour}:$t->{min}:$t->{sec}"; # 13:24:00
#
$input = '1.24.00 P.M.';
@vals = $input =~ $RE{time}{hms}{-keep};
$t = normalize_rct('hms', @vals);
print "$t->{hour}:$t->{min}:$t->{sec}"; # 13:24:00
EXPORTS
This module exports the following symbols into the caller's namespace:
normalize_ymd
normalize_ymd3
normalize_hms
normalize_ymdhms
normalize_time
normalize_gmtime
normalize_month
normalize_year
normalize_ym
normalize_ym3
normalize_rct
The following symbols are available for export:
mon_name
mon_abbr
day_name
day_abbr
is_leap
days_in
You may use the export tag "all" to get all of the above symbols:
use Time::Normalize ':all';
REQUIREMENTS
If POSIX and I18N::Langinfo is available, this module will use them; otherwise, it will use hardcoded English values for month and weekday names.
Test::More is required for the test suite.
SEE ALSO
See Regexp::Common::time for a Regexp::Common plugin that matches nearly any date format imaginable.
BUGS
Uses Gregorian rules for computing whether a year is a leap year, no matter how long ago the year was.
NOT A BUG
By convention, noon is 12:00 pm; midnight is 12:00 am.
AUTHOR / COPYRIGHT
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.
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.
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. 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, I can offer only my sincere sympathy and apologies, and promise to endeavor to improve the software.