Basic Usage

Dave Rolsky edited this page Jan 30, 2017 · 1 revision

How do I use DateTime?

# This is the bit you really want
use DateTime;

How do I make a DateTime object?

There are a variety of ways to create a DateTime object. You can either specify each item in the date. All parameters are optional except for year. The defaults are shown for the other parameters:

my $dt1 = DateTime->new(
    year       => 2003,
    month      => 1,
    day        => 1,
    hour       => 0,
    minute     => 0,
    second     => 0,
    nanosecond => 0,
    time_zone  => "floating",
);

Create a time from the Unix epoch (e.g. what perl's time() function returns):

# Create a DateTime object for the current time
my $dt2 = DateTime->from_epoch( epoch => time() );

# And to get the time back as an offset from the epoch
my $time = $dt2->epoch();

See also DateTime::Format::Epoch if you need finer control over the epoch conversion.

Since you often want to represent the current time, there is a simpler syntax:

my $dt3 = DateTime->now();   # With date and time
my $dt4 = DateTime->today(); # Truncated to date

All of the above take optional time_zone and locale parameters, for information about the floating time zone see What is the floating time zone?. For other more sophisticated constructors, see the DateTime documentation.

Why do I need to truncate dates?

Since DateTime objects represent exact instants in time (down to the nanosecond resolution), when comparing two dates you need to decide what units you want to use. e.g:

my $some_date = ...;
my $now = DateTime->now();

# Naive comparison
if ($some_date == $now) { # WRONG!
}

my $some_date2 = $some_date->clone()->truncate(to => 'day');
my $now = DateTime->today();  # Same as now() truncated to days
if ($some_date == $now) { # Right!
}

If you hadn't changed both to days then they are unlikely to match. Of course if you are trying to work out if an hour long meeting is going on now then you should truncate to hours... but for that kind of thing you probably want a DateTime::Span.

When do I need to clone dates?

You need to clone a date if you plan on changing it and have copied the variable. Since the dates are hash references internally, just copying the variable holding the date doesn't actually make a new date object, so changing one will change the other.

my $dt1 = DateTime->new( year => 2000 );

# "copy" the date and change the "copy"
$dt2 = $dt1;
$dt2->set( year => 2003 );

print $dt1->year();  # Surprise, it is 2003 The right way to do it is to clone when you make the copy: 

my $dt1 = DateTime->new( year => 2000 );

# copy the date and change the copy
$dt2 = $dt1->clone();
$dt2->set( year => 2003 );

print $dt1->year();  # Prints: 2000

How do I compare two dates?

There are a few ways to do this. You can do it explicitly:

$dt1 = DateTime->new( year => 1999 );
$dt2 = DateTime->new( year => 2000 );

my $cmp = DateTime->compare($dt1, $dt2);
# $cmp is -1, 0, or 1 if $dt1 is <, ==, or > than $dt2 respectively

Or using the normal perl syntax:

if ($dt1 > $dt2)   { $foo = 3; }
if ($dt1 == $dt2)  { $foo = 4; }
if ($dt1 < $dt2)   { $foo = 5; }
my @sorted_dates = sort ($dt2, $dt1);

There are some issues doing date comparisons when one of the objects has a floating time zone. See (link to a non-existent wiki in a page link - FAQ: Time Zones)

How do I convert a date from format FOO into a DateTime object (and vice versa)?

First you should check to see if there is an appropriate DateTime::Format module, these usually have both input and output filters so you can read and write dates to external sources.

If there is no appropriate module already you can use DateTime::Format::Builder to easily build a parser.

I printed a date with strftime, how do I parse it again?

Use DateTime::Format::Strptime. This module implements the POSIX strptime function that is the inverse of strftime. The difference between this module and DateTime::Format::Builder which has the capability to create a parser from strptime formats is that DateTime::Format::Strptime is oriented to parsing a string in the program a few times, whereas DateTime::Format::Builder is really for building a new DateTime::Format module and can chain together string definitions that will be tried in succession until one matches the source or all definitions have been exhausted.

use DateTime::Format::Strptime;

my $parser
    = DateTime::Format::Strptime->new( pattern => '%Y-%m-%d %H:%M:%S' );

my $dt = $parser->parse_datetime('2003-05-04 12:55:10');

# prints '2003-05-04 12:55:10';
print $dt->ymd . ' '
    . $dt->hms;    ## How can I get a string representing a date?

my $dt = DateTime->new(
    year => 1998, month  => 4, day => 7,
    hour => 13,   minute => 55
);

# Some standard ones
my $s1 = $dt->date();        # 1998-04-07
my $s2 = $dt->mdy('|');      # 04|07|1998
my $s3 = $dt->datetime();    # 1998-04-07T13:55:00
my $s4 = $dt->time();        # 13:55:00
my $s5 = $dt->hms('x');      # 13x55x00

# Then you can get fancy with custom strftime formats (see the
# DateTime perldoc for the full format details

# 1998-04-07 01:55:00 PM
my $s6 = $dt->strftime("%F %r");

# Tue, 07 Apr 1998 13:55:00 +0000 (RFC 2925)
my $s7 = $dt->strftime("%a, %d %b %Y %H:%M:%S %z");

How small an increment of time can I represent?

DateTime can represent nanoseconds. You can create obects with that resolution using the nanosecond parameter to new or set and there is a corresponding nanosecond accessor. For these you give an integer count of the nanoseconds.

A millisecond is a thousandth of a second (10^-3 or 0.001). The abbreviation is ms. A microsecond is a millionth of a second (10^-6 or 0.000001). The abbreviation is us (or more properly µs). A nanosecond is a billionth (US) of a second (10^-9 or 0.000000001). The abbreviation is ns.

# The ns part is 0.000000230 below
my $dt_ns = DateTime->new(
    year       => 2003, month  => 3,  day    => 1,
    hour       => 6,    minute => 55, second => 23,
    nanosecond => 230
);
print "ns: ", $dt_ns->nanosecond, "\n";    # Prints: "ns: 230\n"

# Assuming we got milliseconds as an argument
my $ms    = 42;
my $dt_ms = DateTime->new(
    year       => 2003, month  => 3,  day    => 1,
    hour       => 6,    minute => 55, second => 23,
    nanosecond => $ms * 1_000_000
);
print "ns: ", $dt_ms->nanosecond, "\n";    # Prints: "ns: 42000000\n"

How do I parse multiple formats at once?

If your date string could be one of several existing formats then you can use DateTime::Format::Builder to make a single parser that tries several in sequence (and you can add your own additional rules if needed).

package DateTime::Format::Multi;
use DateTime::Format::HTTP;
use DateTime::Format::Mail;
use DateTime::Format::IBeat;

use DateTime::Format::Builder (
    parsers => {
        parse_datetime => [
            sub {
                eval { DateTime::Format::HTTP->parse_datetime( $_[1] ) };
            },
            sub {
                eval { DateTime::Format::Mail->parse_datetime( $_[1] ) };
            },
            sub {
                eval { DateTime::Format::IBeat->parse_datetime( $_[1] ) };
            },
        ]
    }
);

then in in another package :

for ( '20030719T155345', 'Sat, 19 Jul 2003 15:53:45 -0500',
    '@d19.07.03 @704' ) {
    print DateTime::Format::Multi->parse_datetime($_)->datetime(), "\n";
}

# Prints: "2003-07-19T15:53:45\n2003-07-19T15:53:45\n2003-07-19T15:53:45\n"

How do I make a DateTime from a string?

If you know the format of the string, you can use DateTime::Format::Strptime. See I printed a date with strftime, how do I parse it again?.

If you don't know the format, try using DateTime::Format::HTTP, which parses a variety of common datetime string formats, and DateTime::Format::DateManip, which uses the powerful (but slow) Date::Manip module to parse strings.