Dates in Perl: Hawaiian Vacation Planning
Since we’re starting a new year, let’s look at handling dates in Perl. Let’s say the user enters a date and you want to check if it’s between a particular range of start/end dates.
In particular, let’s say you want to go to Hawaii and your kids are in school for the spring semester from January 9 through June 2. Your travel agent gives you a list of possible dates when you can go to Hawaii really cheaply, and you want to know which ones conflict with your kids’ school schedule so you can include the budget for a babysitter in the cost of the trip.
The easiest way to compare dates in a date range is to get them into a format that you can compare using regular comparison operators. There are two typical approaches for doing this:
The first is by converting the dates to numbers. This is the way C programmers do it. You convert the dates into the number of seconds since January 1 1970 at midnight, GMT. (And you thought Perl programmers were arbitrary?) Perl supports this by providing interfaces to C library functions such as localtime() and timelocal(). You parse the dates into a set of numbers for year, month, day, hours, minutes, and seconds and pass it to the timelocal() function (which is found in the Time::Local module, natch), and out comes a big number. Do that for the start and end dates of the school calendar, and dates of your Hawaii trip, and you can compare them as numbers.
The trouble with this solution is that it’s difficult to parse dates. You have to deal with all the different formats they can be written in (or use a fussy user interface that requires them in a very particular format). You have to worry about weird things like subtracting 1 from the month so it’s in the range 0-11 (which timelocal needs) and more importantly, every time you need to write a script or module to deal with dates you have to do it all over again!
So a much better approach is to use a Perl module that has built-in support for dates. Perl doesn’t come with any but there are a couple you can download from www.cpan.org (or if you’re using ActivePerl on Windows, installed using PPM). They handle all the date parsing and can even do calculations to determine not just where the date of your trip to Hawaii lies within the semester at your kids’ school, but how many days (or minutes, or seconds…) away it is from summer vacation.
Two modules that I can suggest for doing this are Date::Manip and Date::Calc. I don’t love the API for Date::Manip, and it can be a little slow, but I love what it does. It can parse just about any date format you can think of (plus a few you probably can’t think of) and generate dates in just about any format. It can do calculations between dates and even consider business days vs. calendar days. It’s written all in Perl so you can install it easily on any platform. The other, Date::Calc, is faster and better designed, and does almost all the things Date::Manip does, but it has a C-compiled back-end which means you have to do a little more work to install it.
For now we’ll look at Date::Manip, but the same approach can be taken with the other modules such as Date::Calc. The strategy we’ll take is to use string comparison rather than numeric, and convert the dates to a common string format that can be compared easily. The format used by Date::Manip is YYYYMMDDHH:MM:SS. So in our example, your school semester runs from 2006010600:00:00 to 2006060200:00:00 (we don’t care about the time of day, so just use zeros). You get the list of dates from your travel agent and maybe each tour company returns the information in a different date format, so you get a list such as “April 1st” and “July 11″ and “2/26/2006″ and you need to parse them.
Date::Manip’s ParseDate subroutine can convert each of those to the right string format, and then getting the answer is easy using Perl’s gt and lt operators. Remember, don’t use > and < for comparing strings! (Although in this case the results would be the same, they wouldn’t if the minutes & seconds mattered. And besides if you have warnings enabled - and you should - it’ll complain when it hits the colons in the strings.)
So there you have it - run the dates through ParseDate subroutine and then just compare them as strings. The answer? 2006040100:00:00 and 2006022600:00:00 fall within the school year, but 2006071100:00:00 doesn’t. Don’t forget your sunscreen!
Technorati Tags: Perl, parsing dates, comparing dates, date formats

Do you provide a blog feed subscription for this blog so I can get it via email?
Comment by nicholas — November 29, 2006 @ 11:45 am