My objective is to handle a wide range of dates in perl, including, for instance, 453 CE, the year "Atilla the Hun" died https://en.wikipedia.org/wiki/Attila.
Now, https://perldoc.perl.org/Time::Local points out a limitation of timelocal()
in handling years distant from now:
timelocal_modern() and timegm_modern()
When Time::Local was first written, it was a common practice to represent years as a two-digit value like 99 for 1999 or 1 for 2001. This caused all sorts of problems (google "Y2K problem" if you're very young) and developers eventually realized that this was a terrible idea.
Regarding timelocal()
, it says
Warning: The year value interpretation that these functions and their nocheck variants use will almost certainly lead to bugs in your code, if not now, then in the future. You are strongly discouraged from using these in new code, and you should convert old code to using either the *_posix or *_modern functions if possible.
...
Year Value Interpretation This does not apply to the *_posix or *_modern functions. Use those exports if you want to ensure consistent behavior as your code ages.
Accordingly, I want to use timelocal_modern()
. But my code below shows both
- the behavior of
timelocal()
with distant dates, and - the fact that my attempt to invoke
timelocal_modern()
generates an exception.
#!/usr/bin/perl
use strict; use warnings;
use Time::Local;
use POSIX qw(strftime);
print $^V; print "\n";
print "the year Atilla the Hun died:\n";
epochsTOyear(-47855224609);
my $modern;
eval{$modern=timelocal_modern(0,0,0,1,0,0)};
if(length $@)
{
print join('', '$modern=timelocal_modern(0,0,0,1,0,0) threw error: ', $@, "\n",);
}
else
{
print "modern==$modern\n";
}
yearTOformat1january(-1);
yearTOformat1january(0);
yearTOformat1january(2000);
yearTOformat1january(1);
yearTOformat1january(2001);
yearTOformat1january(1000);
yearTOformat1january(-999);
sub yearTOformat1january
{
my @base=(0,0,0,1,0,);
my $answer = timelocal( @base, $_[0]);
print join('', "year== ", $_[0], "\tso \tepoch seconds== ", $answer, "\tyields " , (strftime '%Y-%m-%d %a %H:%M:%S', localtime($answer)), "\n",);
}
sub epochsTOyear
{
my $yearresult=(strftime '%Y', localtime($_[0]));
print join('', "\t\tepoch seconds==", $_[0], "\tyields " , $yearresult, "\n",);
return $yearresult;
}
and what I get on my computer
~/u/kh/bin> perl --version
This is perl 5, version 18, subversion 4 (v5.18.4) built for darwin-thread-multi-2level
(with 2 registered patches, see perl -V for more detail)
Copyright 1987-2013, Larry Wall
Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.
Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl". If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.
~/u/kh/bin> z.pl
v5.18.4
the year Atilla the Hun died:
epoch seconds==-47855224609 yields 0453
$modern=timelocal_modern(0,0,0,1,0,0) threw error: Undefined subroutine &main::timelocal_modern called at /Users/kpr/u/kh/bin/z.pl line 12.
year== -1 so epoch seconds== -2240506800 yields 1899-01-01 Sun 00:00:00
year== 0 so epoch seconds== 946702800 yields 2000-01-01 Sat 00:00:00
year== 2000 so epoch seconds== 946702800 yields 2000-01-01 Sat 00:00:00
year== 1 so epoch seconds== 978325200 yields 2001-01-01 Mon 00:00:00
year== 2001 so epoch seconds== 978325200 yields 2001-01-01 Mon 00:00:00
year== 1000 so epoch seconds== -30610206238 yields 1000-01-01 Wed 00:00:00
year== -999 so epoch seconds== -33734343838 yields 0901-01-01 Sat 00:00:00
~/u/kh/bin>
So, what do I need to do to get timelocal_modern()
?
CodePudding user response:
You need to use Time::Local
version 1.27 or better.
Check $Time::Local::VERSION
to see which version you currently have.
CodePudding user response:
Time::Local doesn't export timelocal_modern
by default. Replace
use Time::Local;
with
use Time::Local qw( timelocal timelocal_modern );
I believe it's a good practice to always list your imports anyway. It allows the reader to figure out from where each function comes.
You may also need to upgrade Time::Local. timelocal_modern
was added to relatively recent 1.27, but Perl 5 18.4 comes with Time::Local 1.2300.
CodePudding user response:
The solution was
> brew install perl
<snip>
> brew link --overwrite --dry-run perl
<snip>
> brew link --overwrite perl
Linking /usr/local/Cellar/perl/5.34.0... 2476 symlinks created.
> perl --version
This is perl 5, version 34, subversion 0 (v5.34.0) built for darwin-thread-multi-2level
And with the following code
#!/usr/local/bin/perl
use Time::Local;
use Time::Local qw( timelocal_posix timegm_posix );
use POSIX qw(strftime);
my $yearFROM1900;
unless(scalar @ARGV and $ARGV[0]=~/^[-]?\d $/)
{
$yearFROM1900=0;
}
else
{
$yearFROM1900=$ARGV[0];
}
print "yearFROM1900==$yearFROM1900\n";
my $a;
$a=timelocal(0,0,0,1,0,$yearFROM1900);
print join('', (strftime '%Y', localtime($a)), ' <- ', $a, "\ttimelocal()\n",);
$a=timelocal_posix(0,0,0,1,0,$yearFROM1900);
print join('', (strftime '%Y', localtime($a)), ' <- ', $a, "\ttimelocal_posix()\n",);
we obtain two very different results.
If we give it the number -2470, which is the difference between the date of the poet Sappho's flourishing and 1900,
result is consistent between timelocal
and timelocal_posix
.
But if we give it the year 0, we see a drastic difference.
> z.pl $((-1900-570)) # poet Sappho
yearFROM1900==-2470
-570 <- -80154644400 timelocal()
-570 <- -80154644400 timelocal_posix()
> z.pl
yearFROM1900==0
2000 <- 946702800 timelocal()
1900 <- -2208970800 timelocal_posix()
To my surprise, rebooting was not necessary on this 2019 macOS computer.