Processing dates and times with Perl
Tags: perl, dates, times
Published: 2021-11-10 19:33:16

---
title: Processing dates and times with Perl
published: true
description: Perl has some great modern tools for working with dates and times
tags: perl, dates, times
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s67wl3yscggbb4a5iyya.jpg
---

Twenty years ago, I wrote a book called *Data Munging with Perl*. People said nice things about it, but the publishers let it go out of print several years ago. That's probably fair - to be honest a lot of its advice is looking a bit dated.

One of the things I covered was manipulating dates and times with Perl. Back then we didn't have tools like [Time::Piece](https://metacpan.org/pod/Time::Piece) or [DateTime](https://metacpan.org/pod/DateTime) so my examples used Perl's built-in date and time functions and the "state of the art" CPAN modules Date::Calc and Date::Manip.

If there were one section of the book that I could go back and rewrite, it would be this one. Date and time handling in Perl has come on a long way in the last twenty years and it pains me to see people still using things like Date::Manip.

The book took three common examples:

* Finding the date in x days time
* Finding the date of the previous Saturday
* Finding the date of the first Monday in a given year

Following a [discussion on Reddit](https://www.reddit.com/r/perl/comments/qq71g2/meditation_on_calendar_modelling/), I thought it would be interesting to reproduce my examples using more modern date and time handling tools.

So let's see how we'd do those things using modern Perl classes.

## Using Time::Piece

### Finding the date in x days time

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

    my $days = shift // 10;

    my $now = localtime;
    say $now + ($days * ONE_DAY);

This is pretty simple stuff. Time::Piece overrides the standard `localtime()` function so that I get a Time::Piece object back. I can then add seconds to that object using one of the constants defined in [Time::Seconds](https://metacpan.org/pod/Time::Piece) to get the time I want.

The output I get from running this program is:

    Sat Nov 20 17:02:19 2021

Note that by just printing my Time::Piece object, I get a nicely-formatted date/time string. If the format isn't quite to my liking, I could use the `strftime()` method to get the format that I want.

### Finding the date of the previous Saturday

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

    my $now = localtime;
    my $days = $now->day_of_week + 1;
    say $now - ($days * ONE_DAY);

This is very similar to the previous example. We get a Time::Piece object that contains the current date and time and then work out how many days to go back to get to the previous Saturday. The `day_of_week()` method returns a number between 0 and 6, with Sunday being 0. We need to add one to that number to get to Saturday.

### Finding the date of the first Monday in a given year

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

    my $year = localtime->year;

    my $first_mon = Time::Piece->strptime("$year Jan 1", '%Y %b %e');

    $first_mon += (8 - $first_mon->day_of_week) % 7 * ONE_DAY;

    say $first_mon;

This also works on a very similar principle. We get the current year and create a Time::Piece object that contains the 1st January from that year. We then just work out how many days (perhaps zero) we need to add to get to a Monday.

## Using DateTime

### Finding the date in x days time

    use DateTime;

    my $days = shift // 10;

    my $now = DateTime->now;
    say $now->add(days => $days);

The form of this is pretty similar to the Time::Piece example. We can use `DateTime->now` to get a DateTime object containing the current date and time and then use the `add()` method on that to add a number of days.

The output I get from running this program is:

    2021-11-20T18:51:57

Note that, like Time::Piece, we can just print a DateTime object and get a nicely-formatted string. I prefer DateTime's default format as it uses the ISO standard for dates and times. But, as with Time::Piece, there's a method called `strftime()` that you can use to produce strings in other formats.

### Finding the date of the previous Saturday

    use DateTime;

    my $now = DateTime->now;;
    my $days = $now->day_of_week + 1;
    say $now->subtract(days => $days);

This is also very similar in shape to the Time::Piece version. We're just converting the same logic to the DateTime syntax.

### Finding the date of the first Monday in a given year

    use DateTime;

    my $year = DateTime->now->year;

    my $first_mon = DateTime->new(
      year  => $year,
      month => 1,
      day   => 1,
    );

    my $days = (8 - $first_mon->day_of_week) % 7;

    say $first_mon->add(days => $days);

And this is another case where we're mostly just translating Time::Piece syntax to DateTime syntax. The only other real difference is that DateTime has a real constructor method, whereas to construct a Time::Piece object for an arbitrary date we needed to create a string a parse it using `strptime()`.

## Conclusions

I hope you can see from these examples that using more modern date and time tools can make you code smaller and easier to understand than it would be if you used Perl's built-in functions for this kind of work.

Here are a few advantages that I think you get from using these libraries:

* Storing your date and time in a single, structured variable rather than separate scalars for the various parts of a date and time.
* Easy parsing of date and time strings into an object.
* Easy production of many different output formats.
* Easy addition and subtraction of dates and times.
* Objects are easy to compare (and, therefore, sort).
* You no longer need to care that `localtime()` gives a month that's between 0 and 11 or a year that is the actual year minus 1900.

I haven't shown it here, but these classes also understand timezones - so that's another area that will give you far fewer headaches if you switch to using these classes.

I've covered what are probably the two most popular modern date and time classes for Perl. I expect you're wondering how you would decide which one to use.

* Time::Piece has been included in the standard Perl distribution since version 5.10 was released in 2007. It's therefore good in situations where it's hard to install CPAN modules.
* DateTime needs to be installed from CPAN, but it's an incredibly powerful module and sits at the centre of a massive ecosystem of other date and time classes. If, for example, you want to deal with calendars other than the Gregorian calendar, then there's probably a DateTime add-on that can help you.

The rule of thumb that I use is this - if it's a simple project and Time::Piece can do the job, then I use that. Otherwise, I reach for DateTime or one of its friends.

But honestly, learning to use these date and time modules will make your programming life easier.