innreport's ConvDate

Alexander Bartolich alexander.bartolich at gmx.at
Sun Dec 14 22:46:06 UTC 2008


Hello,

The date parsing code in innreport checks neither leap years nor daylight saving.
Incorrect handling of 29th of February loses the report of a whole day.
This must be fixed and is fortunately easy to fix.
However, I'm not so sure about consequences and cure of the second issue.
Probably the 25th hour can be recognized by checking non-sequential time stamps.

Anyway, for documentation purposes I post a benchmark comparing innreport's
current implementation (ConvDate), my version using a hash (ConvHash), and
Perl's Time::Local::timelocal.

This is the output on Linux 2.6.24-22-server x86_64 with Perl v5.8.8.
My time zone is CET.

year=2008 isLeapYear=1
                  ConvDate  ConvHash ConvLocal
Aug 22 01:49:40  20137780  20224180  20220580
Feb 29 23:59:59   5183999   5183999   5183999
Mar 01 00:00:00   5097600   5184000   5184000
Jan 01 00:00:00         0         0         0
Dec 31 23:59:59  31535999  31622399  31622399
              Rate ConvLocal  ConvDate  ConvHash
ConvLocal  6227/s        --      -75%      -87%
ConvDate  24691/s      297%        --      -49%
ConvHash  48077/s      672%       95%        --

And this is the code:

#!/usr/bin/perl -w
use Benchmark();
use Time::Local;

my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
   localtime(time);
$year += 1900;
my $isLeapYear =
  (($year % 4 == 0) && ($year % 100 != 0)) || ($year % 400 == 0);
printf "year=%d isLeapYear=%d\n", $year, $isLeapYear;

my %month_to_dayofyear = $isLeapYear
? ( 'Jan' =>  -1,
     'Feb' =>  30,
     'Mar' =>  59,
     'Apr' =>  90,
     'May' => 120,
     'Jun' => 151,
     'Jul' => 181,
     'Aug' => 212,
     'Sep' => 243,
     'Oct' => 273,
     'Nov' => 304,
     'Dec' => 334 )
: ( 'Jan' =>  -1,
     'Feb' =>  30,
     'Mar' =>  58,
     'Apr' =>  89,
     'May' => 119,
     'Jun' => 150,
     'Jul' => 180,
     'Aug' => 211,
     'Sep' => 242,
     'Oct' => 272,
     'Nov' => 303,
     'Dec' => 333 );

sub ConvDate($) {
   # usage: $num = &ConvDate ($date);
   # date format is Aug 22 01:49:40
   my $T = shift;
   my ($m, $d, $h, $mn, $s) = $T =~ /^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)$/;
   my $out = $s + 60 * $mn + 3600 * $h + 86400 * ($d - 1);

   $m = substr("000031059090120151181212243273304334",
               index ("JanFebMarAprMayJunJulAugSepOctNovDec", $m), 3);
   $out += $m * 86400;
   return $out;
}

sub ConvHash($) {
   my $T = shift;
   my ($m, $d, $h, $mn, $s) = $T =~ /^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)$/;
   $m = $month_to_dayofyear{ $m };
   return $s + 60 * $mn + 3600 * $h + 86400 * ($d + $m)
}

sub ConvLocal($)
{
   my $T = shift;
   my ($m, $mday, $hour,, $min, $sec) = $T =~ /^(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)$/;
   my $out = $sec + 60 * $min + 3600 * $hour + 86400 * ($mday - 1);
   $m = index ("JanFebMarAprMayJunJulAugSepOctNovDec", $m) / 3;
   return timelocal($sec,$min,$hour,$mday,$m,2008);
}

my @input = (
   'Aug 22 01:49:40',
   'Feb 29 23:59:59',
   'Mar 01 00:00:00',
   'Jan 01 00:00:00',
   'Dec 31 23:59:59'
);

my $day0 = ConvLocal('Jan 01 00:00:00');
printf "%-15s  ConvDate  ConvHash ConvLocal\n", '';
for my $i( @input )
{
   printf "%-15s %9d %9d %9d\n", $i, ConvDate($i),
     ConvHash($i), ConvLocal($i) - $day0;
}

Benchmark::cmpthese(10000, {
   'ConvDate'  => sub { map { ConvDate $_ } @input },
   'ConvHash'  => sub { map { ConvHash $_ } @input },
   'ConvLocal' => sub { map { ConvLocal $_ } @input },
});




More information about the inn-workers mailing list