Dates and Times (Perl Cookbook, 2nd Edition)

# Chapter 3. Dates and Times

#### Contents:

Introduction
Finding Today's Date
Converting DMYHMS to Epoch Seconds
Converting Epoch Seconds to DMYHMS
Adding to or Subtracting from a Date
Difference of Two Dates
Day in a Week/Month/Year or Week Number
Parsing Dates and Times from Strings
Printing a Date
High-Resolution Timers
Short Sleeps
Program: hopdelta

> IEEE Std 1003.1b-1993 (POSIX) Section B.2.2.2

> It is inappropriate to require that a time represented as seconds since the Epoch precisely represent the number of seconds between the referenced time and the Epoch.

## 3.0. 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).[5]

> [5]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.[6] 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.

> [6]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 `Fri` `Dec` `13` `20:45:52` `1901` to `Tue` `Jan` `19` `03:14:07` `2038` (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.

Copyright © 2003 O'Reilly & Associates. All rights reserved.