Solve almost any datetime need with Time::Piece

Datetimes come up all the time in programming, so being fluent in handling them is an essential skill. There are many modules on CPAN for dealing with datetimes, but for most tasks you only need one: Time::Piece.

Requirements

Time::Piece has been in Perl core since version 5.8, so you should already have it installed. If you don’t have it, you can install it via cpan at the terminal.

$ cpan Time::Piece

Create a Time::Piece object

To create a new Time::Piece object with the current system time, use new:

use Time::Piece;

my $time = Time::Piece->new;

Get a datetime string in any format

Time::Piece provides many methods for printing the datetime in common formats. For example:

$time;           # Thu Jan  9 21:21:36 2014
$time->datetime; # 2014-01-09T21:21:36
$time->date;     # 2014-01-09
$time->mdy;      # 01-09-2014
$time->fullday;  # Thursday
$time->hms;      # 21:21:36
$time->epoch;    # 1389320496 (Unix time)

If you need to get the datetime in a custom format, Time::Piece provides a “strftime” method which takes a custom string format. A complete list of formatting codes is on the man page. Here are some examples:

use Time::Piece;

my $time = Time::Piece->new;
$time->strftime('%y/%m/%d %H:%M'); # 14/01/09 21:21
$time->strftime('%y_%m_%d');       # 14_01_09
$time->strftime('%s');             # 1389320496 (Unix time)
$time->strftime('%Y %y %G %g');    # 2014 14 2014 14 (4 different years,really)

Read a datetime string in any format

Time::Piece also provides a strptime method which takes a string, and a string format and initializes a new Time::Piece object with that datetime. If either the date or time component is missing, Time::Piece assumes the current date or time. Here are some examples:

use Time::Piece;

my $yesterday    = Time::Piece->strptime('01-08-2014', '%m-%d-%Y');
my $yesterdayDMY = Time::Piece->strptime('08-01-14', '%d-%m-%y');
my $lunchhour24  = Time::Piece->strptime('12:30', '%H:%M');
my $bedtime      = Time::Piece->strptime('12:30 AM', '%l:%M %p');

# epoch - no problem
my $from_epoch   = Time::Piece->strptime(1501615857, '%s');

# timezones are easy too
my $utc_datetime = Time::Piece->strptime('Mon, 19 Jan 2015 14:56:20 +0000','%a, %d %b %Y %H:%M:%S %z');
my $eastern_datetime = Time::Piece->strptime('2015-10-05T09:34:19 -0400','%Y-%m-%dT%T %z');
my $pacific_datetime = Time::Piece->strptime('2015-10-05T09:34:19 -0700','%Y-%m-%dT%T %z');

Warning Time::Piece fails to parse timezones with colons in them. To handle that, just remove the colon from the timezone before passing it to strptime:

use Time::Piece;

my $datetime = '2015-10-05T09:34:19 -04:00';
$datetime    =~ s/([+\-]\d\d):(\d\d)/$1$2/;
my $dt       = Time::Piece->strptime($datetime, "%Y-%m-%dT%H:%M:%S %z");

strptime uses the same formatting codes as strftime, you can get the full list here.

Compare datetimes

This is easy. Just initialize two Time::Piece objects and compare them with an operator (<, <=, ==, >=, > and <=>). For example:

use Time::Piece;

my $today = Time::Piece->new;
my $yesterday = Time::Piece->strptime('01/08/2014', '%m/%d/%Y');

if ($today > $yesterday) {
    ...
}

Datetime math

The first thing to know is that Time::Piece objects are immutable so any operation performed on them will return a new object.

Time::Piece provides a couple of methods for adding months and years (“add_months”, “add_years”) from Time::Piece objects. Simply use a negative integer to subtract. For example:

use Time::Piece;

my $datetime = Time::Piece->new;
my $nextMonth   = $datetime->add_months(1);   # plus one month
my $lastQuarter = $datetime->add_months(-3);  # minus three months
my $nextDecade  = $datetime->add_years(10);   # plus 10 years
my $lastYear    = $datetime->add_years(-1);   # minus 1 year

You’ll often need more granular control over the datetime, and that’s where the Time::Seconds module comes in. Just include it in your program, and it will export several constants which can be used to adjust the datetime. The constants are: ONE_MINUTE, ONE_HOUR, ONE_DAY, ONE_WEEK, ONE_MONTH, ONE_YEAR, ONE_FINANCIAL_MONTH, LEAP_YEAR, NON_LEAP_YEAR.

Let’s see how to use the constants:

use Time::Piece;
use Time::Seconds;

my $time = Time::Piece->new;
my $tomorrow  = $time + ONE_DAY;
my $lastWeek  = $time - ONE_WEEK;
my $lastMonth = $time - ONE_MONTH;

If you need to change the datetime by seconds, with you can simply use integer arithmetic.

use Time::Piece;

my $now = Time::Piece->new;
my $30SecondsAgo = $now - 30; 

Documentation

Time::Piece has excellent documentation, you can read it on the command line with perldoc:

$ perldoc Time::Piece

Updated: Added timezone strptime example 2015-01-28. Added timezone semicolon handling 2016-03-17.


This article was originally posted on PerlTricks.com.

Tags

David Farrell

David is the editor of Perl.com. An organizer of the New York Perl Meetup, he works for ZipRecruiter as a software developer, and sometimes tweets about Perl and Open Source.

Browse their articles

Feedback

Something wrong with this article? Help us out by opening an issue or pull request on GitHub