Introduction

Times and dates are important
things to be able to manipulate. "How many users logged in last
month?", "How many seconds should I sleep if I want to wake up at
midday?", and "Has this user's password expired yet?" are common
questions whose answers involve surprisingly non-obvious
manipulations.

Perl represents points in time as intervals, measuring seconds past a
point in time called the Epoch. On Unix and
many other systems, the Epoch was 00:00 Jan 1, 1970, UTC (Universal
Corrected Time).[1]

[1] UTC is the preferred way to specify
what used to be called GMT, or Greenwich Mean Time.

When we talk about dates and times, we often interchange two
different concepts: points in time (dates and times) and intervals
between points in time (weeks, months, days, etc.). Epoch seconds
represent intervals and points in the same units, so you can do basic
arithmetic on them.

However, people are not used to working with Epoch seconds. We are
more used to dealing with individual year, month, day, hour, minute,
and second values. Furthermore, the month can be represented by its
full name or its abbreviation. The day can precede or follow the
month. Because of the difficulty of performing calculations with a
variety of formats, we typically convert human-supplied strings or
lists to Epoch seconds, calculate, and then convert back to strings
or lists for output.

Epoch seconds are an absolute number of seconds, so they don't take
into account time zones or daylight saving times. When converting to
or from distinct values, always consider whether the time represented
is UTC or local. Use different conversion functions depending on
whether you need to convert from UTC to local time or vice versa.

Perl's
time function returns the number of seconds that
have passed since the Epoch—more or less.[2] POSIX requires that
time not include leap seconds, a peculiar practice
of adjusting the world's clock by a second here and there to account
for the slowing down of the Earth's rotation due to tidal
angular-momentum dissipation. (See the sci.astro
FAQ, section 3, at http://sciastro.astronomy.net/sci.astro.3.FAQ.)
To convert Epoch seconds into distinct values for days, months,
years, hours, minutes, and seconds, use the
localtime and gmtime functions.
In list context, these functions return a nine-element list, as shown
in Table 3-1.

[2] Well,
less actually. To be precise, 22 seconds less as of this
writing.

Table 3-1. Values (and their ranges) returned from localtime and gmtime

Variable

Values

Range

$sec

seconds

0-60

$min

minutes

0-59

$hours

hours

0-23

$mday

day of month

1-31

$mon

month of year

0-11, 0 = = January

$year

years since 1900

1-138 (or more)

$wday

day of week

0-6, 0 = = Sunday

$yday

day of year

0-365

$isdst

0 or 1

true if daylight saving is in effect

The values for seconds range from 0-60 to account for leap seconds;
you never know when a spare second will leap into existence at the
urging of various standards bodies.

From now on,
we'll refer to a list of day, month, year, hour, minute, and seconds
as DMYHMS, for no better reason than that writing and reading
"distinct day, month, year, hour, minute, and seconds values" is
wearisome. The abbreviation is not meant to suggest an order of
return values.

Perl does
not return a two-digit year value. It returns
the year minus 1900, which just happens to be a two-digit number
through 1999. Perl doesn't intrinsically have a Year 2000 problem,
unless you make one yourself. (Your computer, and Perl, may have a
2038 problem, though, if we're still using 32 bits by that time.) Add
1900 to get the full year value instead of using the construct
"20$year", or your programs will refer to the year
as something like "20103". We can't pin down the
year value's range, because it depends on how big an integer your
operating system uses for Epoch seconds. Small integers mean a small
range; big (64-bit) integers mean a very big range.

In scalar context, localtime and
gmtime return the date and time formatted as an
ASCII string:

Fri Apr 11 09:27:08 1997

The standard Time::tm
module provides a named interface to these values. The standard
Time::localtime and Time::gmtime modules override the list-returning
localtime and gmtime functions,
replacing them with versions that return Time::tm objects. Compare
these two pieces of code:

# using arrays
print "Today is day ", (localtime)[7], " of the current year.\n";
Today is day 117 of the current year.
# using Time::tm objects
use Time::localtime;
$tm = localtime;
print "Today is day ", $tm->yday, " of the current year.\n";
Today is day 117 of the current year.

To go
from a list to Epoch
seconds, use the standard Time::Local module. It provides the
functions timelocal and timegm,
both of which take a nine-element list and return an integer. The
list's values have the same meaning and ranges as those returned by
localtime and gmtime.

Epoch seconds values are limited by the size of an integer. If you
have a 32-bit signed integer holding your Epoch seconds, you can only
represent dates (in UTC) from FriDec1320:45:521901 to
TueJan1903:14:072038 (inclusive). By
2038, it is assumed, computers will change to use larger integers for
Epoch seconds. We hope. For operations on dates outside this range,
you must use another representation or work from distinct year,
month, and day values.

The
Date::Calc and Date::Manip modules on CPAN both work from these
distinct values, but be warned: years don't necessarily have 1900
subtracted from them the way the year value returned by
localtime does, nor do months and weeks always
start at 0. As always, consult the manpage of the appropriate module
to make sure you're giving it what it expects and getting back from
it what you expect. There's little more embarrassing than realizing
you've calculated your company payroll based on a calendar that's
1,900 years in the past.