Perl 函数 localtime 给出 1964 年到 1967 年之间的错误值

Perl function localtime giving incorrect values for years between 1964 and 1967

我从 Perl 中的 localtime 函数中得到了一些古怪的值。以下是一些我得到不正确值的代码。

特别是,此代码用于确定每年第一天的工作日。

#!/usr/bin/perl
use strict 'vars';
use Time::Local;
use POSIX qw(strftime);

mytable();

sub mytable {
   print "Year" . " "x4 .  "Jan 1st (localtime)" . " "x4 . "Jan 1st (Gauss)\n";
   foreach my $year ( 1964 .. 2017 )
     {
       my $janlocaltime = evalweekday( 1,1,$year);
       my $jangauss     = gauss($year);
       my $diff = $jangauss - $janlocaltime;
       printf "%4s%10s%-12s ",$year,"",$janlocaltime;
       printf "%12s",$jangauss;
       printf " <----- ERROR: off by %2s", $diff if ( $diff != 0 );
     print "\n";
     }
}

sub evalweekday {
   ## Using "localtime"
   my ($day,$month,$year) = @_;
   my $epoch   =  timelocal(0,0,0, $day,$month-1,$year-1900);
   my $weekday = ( localtime($epoch) ) [6];
   return $weekday;
 }

sub gauss {
   ## Alternative approach
   my ($year) = @_;
   my $weekday =
     (  1 + 5 * ( ( $year - 1 ) % 4 )
      + 4 * ( ( $year - 1 ) % 100 )
      + 6 * ( ( $year - 1 ) % 400 )
    ) % 7;
   return $weekday;
 }

以下是显示年份值不正确的输出:

Year    Jan 1st (localtime)    Jan 1st (Gauss)
1964          2                       3 <----- ERROR: off by  1
1965          4                       5 <----- ERROR: off by  1
1966          5                       6 <----- ERROR: off by  1
1967          6                       0 <----- ERROR: off by -6
1968          1                       1
1969          3                       3
1970          4                       4
1971          5                       5
1972          6                       6
1973          1                       1
1974          2                       2
1975          3                       3
1976          4                       4
1977          6                       6
1978          0                       0
1979          1                       1
1980          2                       2
1981          4                       4
1982          5                       5
1983          6                       6
1984          0                       0
1985          2                       2
1986          3                       3
1987          4                       4
1988          5                       5
1989          0                       0
1990          1                       1
1991          2                       2
1992          3                       3
1993          5                       5
1994          6                       6
1995          0                       0
1996          1                       1
1997          3                       3
1998          4                       4
1999          5                       5
2000          6                       6
2001          1                       1
2002          2                       2
2003          3                       3
2004          4                       4
2005          6                       6
2006          0                       0
2007          1                       1
2008          2                       2
2009          4                       4
2010          5                       5
2011          6                       6
2012          0                       0
2013          2                       2
2014          3                       3
2015          4                       4
2016          5                       5
2017          0                       0

事实上,错误似乎可以追溯到 1900 年,但我只是没有证实它们在 1964 年之前确实是错误的。

perl --version returns 如下:

This is perl 5, version 18, subversion 2 (v5.18.2) 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.

我不确定它是否相关,但我的操作系统是 macOS Sierra 版本 10.12.3。

我已经通读了文档,但我没有看到任何关于 1968 年之前返回的值的信息(或者我是盲人)。我也尝试进行网络搜索,但没有提取任何信息超越对数组值和一年中月数和天数的典型误解。

有人可以帮我解释一下我错了什么吗?或者,如果这是我的 Perl 版本的问题,请告诉我如何解决它。

这可能与 Time::Local 中负历元值的处理方式有关。看看perldoc Time::Local #Negative-Epoch-Values

在我的 Linux 框中 (perl 5.20),您的代码很好地演示了这个问题。如果打印出收到的纪元值,您会看到问题,即 timelocal 返回的纪元变得很大而不是更负:

Year       Epoch Jan 1st (localtime)     Jan 1st (Gauss)
1964  2966342400 2                       3 <----- ERROR: off by  1
1965  2997964800 4                       5 <----- ERROR: off by  1
1966  3029500800 5                       6 <----- ERROR: off by  1
1967  3061036800 6                       0 <----- ERROR: off by -6
1968   -63185400 1                       1
1969   -31563000 3                       3
1970      -27000 4                       4
1971    31509000 5                       5
1972    63045000 6                       6

为什么不尝试使用 DateTime 库:

use DateTime;
my $dt = DateTime->new(
    year   => 1966, # Real Year
    day    => 1,    # 1-31
    month  => 1,    # 1-12
    hour   => 0,    # 0-23
    second => 0,    # 0-59
);
print $dt->dow . "\n";

6

6 = Saturday 符合维基百科观点:Jan 1, 1966 (Saturday)