Tech Tidbits - Ruby, Ruby On Rails, Merb, .Net, Javascript, jQuery, Ajax, CSS...and other random bits and pieces.

Saturday, April 28, 2007

Perl - DateTime

Our servers were recently switched from using local time to UTC. This had an affect on our content, causing it to show several hours early since content availability is based on local time. In the past we had relied on Date::Calc on other various date manipulations using epoch seconds. Having recently worked on two projects with Catalyst, I had become familiar with the Perl module DateTime. Not only was converting UTC to local time a breeze, DateTime required far fewer lines of code, not to mention the ease and readability of date manipulations.

The standard Unix date command shows the server is on UTC:

kl93@oiche/home/kl93:$ date
Sat Apr 28 13:10:15 UTC 2007


or in Perl:

my $date = localtime(time);
print "$date\n";


returns

2007-04-28T13:16:29


Now, using DateTime module:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->now();
print $dt->datetime(), "\n";


Output:

kl93@oiche:/home/kl93$ perl datetime1.pl
2007-04-28T13:23:15


To get local time, you can either set the desired time zone when you create the DateTime object:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->now(time_zone => 'America/Chicago');
print $dt->datetime(), "\n";


or set it after you've created the object:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->now();
$dt->set_time_zone('America/Chicago');
print $dt->datetime(), "\n";


Now we have local time for US Central:

kl93@oiche:/home/kl93$ perl datetime2.pl
2007-04-28T08:29:56


Using a named time zone like 'America/Chicago' is nice, as it will calculate DST changes. However, it's not recommended for dates far in the future (read the Pod).

You can also use "today" in place of "now," which will only initialize the date, not the time:


#/usr/bin/perl
use strict;
use DateTime;
my $dt1 = DateTime->today();
print "dt1: ", $dt1->datetime(), "\n";
my $dt2 = DateTime->now();
print "dt2: ", $dt2->datetime(), "\n";



kl93@oiche:/home/kl93$ perl datetime3.pl
dt1: 2007-04-28T00:00:00
dt2: 2007-04-28T17:53:32


You can also initialize the DateTime object to any valid date by passing passing parameters to the constructor:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->new( year => 2001,
month => 4,
day => 23,
hour => 10,
minute => 45,
second => 30,
time_zone => 'America/Chicago',
);
print $dt->datetime(), "\n";


We can see that the date has been initialized to April 23, 2005 at 10:45:30 (am):

kl93@oiche:/home/kl93$ perl datetime4.pl
2001-04-23T10:45:30


There are several accessors - a quick perusal of the Pod shows:

$dt->year;
$dt->month; # 1-12 - also mon
$dt->day; # 1-31 - also day_of_month, mday
$dt->day_of_week; # 1-7 (Monday is 1) - also dow, wday
$dt->hour; # 0-23
$dt->minute; # 0-59 - also min
$dt->second; # 0-61 (leap seconds!) - also sec
$dt->day_of_year; # 1-366 (leap years) - also doy
$dt->day_of_quarter; # 1.. - also doq
$dt->quarter; # 1-4


I won't cover everything, that's what the Pod is for, but a few handy features include easy date formating and date calculations.

A few formatting built-ins:

$dt->ymd; # 2007-04-28
$dt->ymd('/'); # 2007/04/28
$dt->ymd('') # 20070428

$dt->mdy; # 04-28-2007
$dt->mdy('/'); # 04/28/2007
$dt->mdy(''); # 04282007

$dt->dmy; # 28-04-200
$dt->dmy('/'); # 28/04/2007
$dt->dmy(''); # 28042007

$dt->hms; # 12:33:45


And if you need it, you can use 'strftime' for further formatting:

$dt->strftime("%A, %B %d, %Y"); # Saturday, April 28, 2007


A few date manipulations:

You can update an existing DateTime object to another date:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->new( year => 2001,
month => 4,
day => 23,
hour => 10,
time_zone => 'America/Chicago',
);
print $dt->ymd, "\n";

$dt->set( year => 2007 );
$dt->set( month => 11 );
$dt->set( day => 30 );
print $dt->ymd, "\n";


We can see that the date was changed from 2001-04-23 to 2007-11-30:

kl93@oiche:/home/kl93$ perl datetime5.pl
2001-04-23
2007-11-30


You can find x number of years, months, days from a date by using "add" and "subtract" on your DateTime object.

To find one month from today:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->now();
print $dt->datetime(), "\n";
print $dt->add(months => 1), "\n";


Output:

kl93@oiche:/home/kl93$ perl datetime6.pl
2007-04-28T14:05:35
2007-05-28T14:05:35


Or one month previous:

#!/usr/bin/perl
use strict;
use DateTime;
my $dt = DateTime->now();
print $dt->datetime(), "\n";
print $dt->subtract(months => 1), "\n";


Output:

kl93@oiche:/home/kl93$ perl datetime7.pl
2007-04-28T14:07:42
2007-03-28T14:07:42


One note - DateTime objects are hash references, so if you want to keep your original DateTime object unchanged, you need to clone your object and then use "add" or "subtract" to get the calculated date.

Here we simply copy $dt1 and update $dt2.

#/usr/bin/perl
use strict;
use DateTime;
my $dt1 = DateTime->now();
print "dt1: ", $dt1->datetime(), "\n";
my $dt2 = $dt1;
$dt2->add(months => 1), "\n";
print "dt2: ", $dt2->datetime(), "\n";
print "dt1: ", $dt1->datetime(), "\n";


Note that once $dt2 is modified, $dt1 is modified as well (they both "point" to the same DateTime object:

kl93@oiche:/home/kl93$ perl datetime8.pl
dt1: 2007-04-28T17:19:46
dt2: 2007-05-28T17:19:46
dt1: 2007-05-28T17:19:46


To prevent this, use the clone method to create a totally new DateTime object:

#/usr/bin/perl
use strict;
use DateTime;
my $dt1 = DateTime->now();
print "dt1: $dt1->datetime(), "\n";
my $dt2 = $dt1->clone;
$dt2->add(months => 1), "\n";
print "dt2: ", $dt2->datetime(), "\n";
print "dt1: ", $dt1->datetime(), "\n";


Now $dt1 remains unchanged:

kl93@oiche:/home/kl93$ perl datetime9.pl
dt1: 2007-04-28T17:23:39
dt2: 2007-05-28T17:23:39
dt1: 2007-04-28T17:23:39


Find out more about the DateTime module at the Perl DateTime Wiki or at CPAN (written by Dave Rolsky)

No comments:

About Me

My photo
Developer (Ruby on Rails, iOS), musician/composer, Buddhist, HSP, Vegan, Aspie.

Labels