leap_seconds_header.pl [plain text]
use strict;
use lib './lib';
my $VERSION = 0.03;
my $leap = shift || './leaptab.txt';
my $x = 1;
my %months = map { $_ => $x++ } qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my @LeapSeconds;
my @RD;
my %RDLength;
build_data_structures();
write_header();
sub build_data_structures
{
my $fh = do { local *FH; *FH };
open $fh, "<$leap" or die "Cannot read $leap: $!";
my $value = -1;
while (<$fh>)
{
my ( $year, $mon, $day, $leap_seconds ) = split /\s+/;
$mon =~ s/\W//;
$leap_seconds =~ s/^([+-])//;
my $mult = $1 eq '+' ? 1 : -1;
my $utc_epoch = _ymd2rd( $year, $months{$mon}, $day );
$value += $leap_seconds * $mult;
push @LeapSeconds, $value;
push @RD, $utc_epoch;
$RDLength{ $utc_epoch - 1 } = $leap_seconds;
}
close $fh;
push @LeapSeconds, ++$value;
}
sub write_header
{
my $set_leap_seconds = <<"EOF";
{ \\
{ \\
if (utc_rd < $RD[0]) { \\
ls = $LeapSeconds[0]; \\
EOF
for ( my $x = 1; $x < @RD; $x++ )
{
my $else = $x == 1 ? '' : 'else ';
my $condition =
$x == @RD ? "utc_rd < $RD[$x]" : "utc_rd >= $RD[$x - 1] && utc_rd < $RD[$x]";
$set_leap_seconds .= <<"EOF"
} else if ($condition) { \\
ls = $LeapSeconds[$x]; \\
EOF
}
$set_leap_seconds .= <<"EOF";
} else { \\
ls = $LeapSeconds[-1]; \\
} \\
} \\
}
EOF
my $set_extra_seconds = <<"EOF";
{ \\
{ \\
es = 0; \\
switch (utc_rd) { \\
EOF
my $set_day_length = <<"EOF";
{ \\
{ \\
dl = 86400; \\
switch (utc_rd) { \\
EOF
foreach my $utc_rd ( sort keys %RDLength )
{
$set_extra_seconds .= <<"EOF";
case $utc_rd: es = $RDLength{$utc_rd}; break; \\
EOF
$set_day_length .= <<"EOF";
case $utc_rd: dl = 86400 + $RDLength{$utc_rd}; break; \\
EOF
}
$set_extra_seconds .= <<"EOF";
} \\
} \\
}
EOF
$set_day_length .= <<"EOF";
} \\
} \\
}
EOF
my $header = <<"EOF";
/* This file is auto-generated by the leap second code generator
($VERSION). This code generator comes with the DateTime.pm module
distribution in the tools/ directory
Generated by $0.
Do not edit this file directly.
*/
EOF
open my $fh, '>leap_seconds.h' or die "Cannot write to leap_seconds.h: $!";
print $fh ( $header,
$set_leap_seconds,
$set_extra_seconds,
$set_day_length,
);
}
sub _ymd2rd
{
use integer;
my ( $y, $m, $d ) = @_;
my $adj;
if ( $m <= 2 )
{
$y -= ( $adj = ( 14 - $m ) / 12 );
$m += 12 * $adj;
}
elsif ( $m > 14 )
{
$y += ( $adj = ( $m - 3 ) / 12 );
$m -= 12 * $adj;
}
if ( $y < 0 )
{
$d -= 146097 * ( $adj = ( 399 - $y ) / 400 );
$y += 400 * $adj;
}
$d += ( $m * 367 - 1094 ) / 12 + $y % 100 * 1461 / 4 +
( $y / 100 * 36524 + $y / 400 ) - 306;
}