Home > Net >  how can I invoke timelocal_modern() in perl 5.18?
how can I invoke timelocal_modern() in perl 5.18?

Time:09-17

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

  1. the behavior of timelocal() with distant dates, and
  2. 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.

  • Related