#!/usr/bin/perl -w
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";
#define SET_LEAP_SECONDS(utc_rd, ls) \\
{ \\
{ \\
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";
#define SET_EXTRA_SECONDS(utc_rd, es) \\
{ \\
{ \\
es = 0; \\
switch (utc_rd) { \\
EOF
my $set_day_length = <<"EOF";
#define SET_DAY_LENGTH(utc_rd, dl) \\
{ \\
{ \\
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, '>', 'c/leap_seconds.h'
or die "Cannot write to c/leap_seconds.h: $!";
print $fh (
$header,
$set_leap_seconds,
$set_extra_seconds,
$set_day_length,
);
}
# from lib/DateTimePP.pm
sub _ymd2rd {
use integer;
my ( $y, $m, $d ) = @_;
my $adj;
# make month in range 3..14 (treat Jan & Feb as months 13..14 of
# prev year)
if ( $m <= 2 ) {
$y -= ( $adj = ( 14 - $m ) / 12 );
$m += 12 * $adj;
}
elsif ( $m > 14 ) {
$y += ( $adj = ( $m - 3 ) / 12 );
$m -= 12 * $adj;
}
# make year positive (oh, for a use integer 'sane_div'!)
if ( $y < 0 ) {
$d -= 146097 * ( $adj = ( 399 - $y ) / 400 );
$y += 400 * $adj;
}
# add: day of month, days of previous 0-11 month period that began
# w/March, days of previous 0-399 year period that began w/March
# of a 400-multiple year), days of any 400-year periods before
# that, and 306 days to adjust from Mar 1, year 0-relative to Jan
# 1, year 1-relative (whew)
$d
+= ( $m * 367 - 1094 ) / 12
+ $y % 100 * 1461 / 4
+ ( $y / 100 * 36524 + $y / 400 ) - 306;
}