Edit: Updated to include more modules.
I was curious to find out what the fastest way to valid dates was, so ran a test using DateTime, Date::Calc, Time::Piece and Time::Local.
The results are below, but they show that Date::Calc was by far and away the quickest method, followed by Time::Piece (even though that one doesn't natively do date checking. (ie. I had to write extra code around it.))
The tests below are for checking "bad" and "good" dates, in case there was a difference. (I expect 99% of dates to be valid, after all.)
As it happens, it only made any difference to Time::Local.
I also tried testing Date::Tiny, DateTime::Tiny, and DateTime::LazyInit, however the first two didn't actually do date validation in any useful way, and the latter failed to install. (And I suspect would just return the same performance as DateTime..)
So, here are the results!
tobyc@arya:~/git/cbt/performance$ ./dates.pl
# Higher numbers are better.
gooddatetime 7328/s
baddatetime 7802/s
badtimelocal 11471/s
goodtimelocal 39665/s
goodtimepiece 63021/s
badtimepiece 63743/s
gooddatecalc 399124/s
baddatecalc 400509/s
My actual code used was:
#!/usr/bin/perl
#
# Benchmark script for Date validation methods.
#
use strict;
use warnings;
use Benchmark qw(:all :hireswallclock);
use DateTime;
use Time::Piece;
use Time::Local qw(timelocal);
use Date::Calc qw(check_date);
my $gooddate = '2009-02-01';
my $baddate = '2009-02-31';
our $dtre = qr/^(\d{4})-(\d\d)-(\d\d)$/;
our $format = '%Y-%m-%d'; # Also %F
# Check these all pass the good date:
die unless datetime($gooddate);
die unless tlocal($gooddate);
die unless timepiece($gooddate);
die unless datecalc($gooddate);
# Check they fail the invalid date:
die if datetime($baddate);
die if tlocal($baddate);
die if timepiece($baddate);
die if datecalc($baddate);
cmpthese(-5, { # ie. 5 seconds each
gooddatetime => sub { datetime($gooddate) },
goodtimepiece => sub { timepiece($gooddate) },
baddatetime => sub { datetime($baddate) },
badtimepiece => sub { timepiece($baddate) },
goodtimelocal => sub { tlocal($gooddate) },
badtimelocal => sub { tlocal($baddate) },
gooddatecalc => sub { datecalc($gooddate) },
baddatecalc => sub { datecalc($baddate) },
});
sub datetime {
my $date = shift;
eval {
$date =~ $dtre;
my $dt = DateTime->new(
year => $1,
month => $2,
day => $3
);
};
return(1) unless $@;
return;
}
sub timepiece {
my $date = shift;
my $match;
eval {
my $tp = Time::Piece->strptime($date, $format);
my $reverse = $tp->strftime($format);
$match = ($date eq $reverse);
};
return $match;
}
sub tlocal {
my $date = shift;
eval {
$date =~ $dtre;
my $time = timelocal(0,0,0, $3, ($2-1), $1);
};
return(1) unless $@;
return 0;
}
sub datecalc {
my $date = shift;
$date =~ $dtre;
return check_date($1, $2, $3);
}
It would be interesting to also see the figures for DateTime::Tiny, Date::Tiny and DateTime::LazyInit
ReplyDeleteDateTime::LazyInit looks pointless to test; it'll be fast without validation, but the moment you validate something it gets inflated to a full datetime object, and we know their performance already.
ReplyDeleteI'll post a new entry with the other two results shortly.. (Also adding Date::Calc)
Hi Steve, I've updated the post above now.
ReplyDeleteWould be interesting if you could also add DateTimeX::Lite and DateTimeX::Easy for comparison.
ReplyDeleteHi Eddy,
ReplyDeleteI think DateTimeX::Easy will be the same as DateTime.. It's a module that's about parsing a string into a date easily, with the underlying validation being provided by DateTime.
DateTimeX::Lite fails to pass its own unit tests on my machine. (Version 0.00001_04)
Thanks Toby, Interesting results!
ReplyDelete