package inc::LeapSecondsHeader;
use strict;
use warnings;
use autodie;
my $VERSION = 0.04;
use Dist::Zilla::File::InMemory;
use Moose;
has _leap_second_data => (
is => 'ro',
isa => 'HashRef',
lazy => 1,
builder => '_build_leap_second_data',
);
with 'Dist::Zilla::Role::FileGatherer';
sub gather_files {
my $self = shift;
$self->add_file(
Dist::Zilla::File::InMemory->new(
name => 'leap_seconds.h',
encoding => 'bytes',
content => $self->_header,
),
);
}
my $x = 1;
my %months = map { $_ => $x++ }
qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
sub _build_leap_second_data {
my $self = shift;
open my $fh, '<', 'leaptab.txt';
my @leap_seconds;
my @rd;
my %rd_length;
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 @leap_seconds, $value;
push @rd, $utc_epoch;
$rd_length{ $utc_epoch - 1 } = $leap_seconds;
}
close $fh;
push @leap_seconds, ++$value;
return {
leap_seconds => \@leap_seconds,
rd => \@rd,
rd_length => \%rd_length,
};
}
sub _header {
my $self = shift;
my ( $leap_seconds, $rd, $rd_length )
= @{ $self->_leap_second_data }{qw( leap_seconds rd rd_length )};
my $set_leap_seconds = <<"EOF";
#define SET_LEAP_SECONDS(utc_rd, ls) \\
{ \\
{ \\
if (utc_rd < $rd->[0]) { \\
ls = $leap_seconds->[0]; \\
EOF
for ( my $x = 1; $x < @{$rd}; $x++ ) {
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 = $leap_seconds->[$x]; \\
EOF
}
$set_leap_seconds .= <<"EOF";
} else { \\
ls = $leap_seconds->[-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 %{$rd_length} ) {
$set_extra_seconds .= <<"EOF";
case $utc_rd: es = $rd_length->{$utc_rd}; break; \\
EOF
$set_day_length .= <<"EOF";
case $utc_rd: dl = 86400 + $rd_length->{$utc_rd}; break; \\
EOF
}
$set_extra_seconds .= <<"EOF";
} \\
} \\
}
EOF
$set_day_length .= <<"EOF";
} \\
} \\
}
EOF
my $generator = ref $self;
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 $generator.
Do not edit this file directly.
*/
EOF
return join q{}, (
$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;
}
1;