http://qs321.pair.com?node_id=258721

vladb has asked for the wisdom of the Perl Monks concerning the following question:

While working on a small log monitoring utility, I encountered a few issues trying to convert datetime string appearing in a log entry to a valid localtime value such that localtime($logtime_in_seconds) would represent the exact time the log entry was made.

I've played with a number of Date:: modules to no avail. One particular module that I thought would help is Date::Manip. Here's the code snippet that demonstrates the problem (or shell I call it a 'bug'?):
use strict; use Date::Manip; my $line = qq{1.2.3.4 - - [15/May/2003:01:05:02 -0600] "GET /foobar"}; my( @mon ) = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep" , "Oct", "Nov", "Dec" ); my $i = 0; my %mon = map { $_ => ++$i } @mon; if ($aline =~ m/ ([\d\.]+) # IP .*?\[([^\s]+?)\s # date ([-+]\d+) # zone \]\s (.*) # text /x) { my ($ip, $date, $zone, $text) = ($1, $2, $3, $4); print "($ip, $date, $zone, $text)\n"; my ($d, $mon, $timestr) = split("/", $date); $d =~ s/^0//g; my ($y, $h, $mn, $s) = split(":", $timestr); # calculate zone offset (in seconds) my $zf = ($zone =~ m/^-/) ? -1 : 1; # zone factor my @z = split("",$zone); shift @z; my $zsec = $z[0]*216000+$z[1]*3600+$z[2]*60+$z[3]; print "Log date values: $mon{$mon}, $d, $y, $h, $mn, $s\n"; print "*** ERROR ***>> Date_SecsSince1970($mon{$mon}, $d, $y, $h, +$mn, $s) = " . localtime(Date_SecsSince1970($mon{$mon}, $d, $y, $h, $mn, $s)) + . " <<***\n"; my $time = Date_SecsSince1970($mon{$mon}, $d, $y, $h, $mn, $s) + $ +zf*$zsec; print "parsed time: ".localtime($time)."; original: $date $zone\n" +; }
This produces the following output:
(1.2.3.4, 15/May/2003:01:05:02, -0600, "GET /foobar") Log date values: 5, 15, 2003, 01, 05, 02 *** ERROR ***>> Date_SecsSince1970(5, 15, 2003, 01, 05, 02) = Wed May +14 18:05:02 2003 <<*** parsed time: Wed May 14 12:05:02 2003; original: 15/May/2003:01:05:02 +-0600
Note the *** ERROR *** line. While the log date value is 15 (of May), the Date_SecsSince1970() method converts it to 14th (or, to be precise, it returns an invalid second count value by, apparently, missing a day). You may disregard the hour value as this may be related to time zone offset calculations (the $zsec variable).

Having played with this module for awhile, I now feel it may not be the right tool in this case. Has any of you come across similar issues? What would be the best module / approach to use?

_____________________
"We've all heard that a million monkeys banging on a million typewriters will eventually reproduce
the entire works of Shakespeare. Now, thanks to the Internet, we know this is not true."

Robert Wilensky, University of California