如何在Perl中将日期/时间转换为Epoch时间(Unix时间/自1970年以来的秒数)?

54

如何将一个日期/时间数组(year, month, day, hour, minute, second)转换成epoch时间,即1970-01-01 00:00:00 GMT以来的秒数?

附加问题:如果给出日期/时间字符串,如何首先解析为(y,m,d,h,m,s)数组?


2
这个问题的投票结果很奇怪。 - jdelator
2
必须注意:Unix纪元仅定义了1901年至2038年的年份。 - Piskvor left the building
6
@Piskvor:那不是严格正确的。纪元被定义为1970年1月1日,而不是一个范围。然而,如果你用32位整数表示UNIX时间戳(这是广泛使用的约定),那么这确实是你能得到的最佳范围。 - Lightness Races in Orbit
14个回答

37

如果你正在使用 DateTime 模块,你可以在 DateTime 对象上调用 epoch() 方法,因为它就是你想要的 Unix 时间。

使用 DateTimes 可以相对容易地从 Unix 时间转换为日期对象。

或者,使用 localtime 和 gmtime 可以将 Unix 时间转换为一个包含日期、月份和年份的数组,而来自 Time::Local 模块 的 timelocal 和 timegm 则可以执行相反的操作,将时间元素(秒、分、...、日、月等)的数组转换为 Unix 时间。


1
最好使用库函数来执行常见操作,而不是自己编写(有错误的)解决方案。 - Anthony Giorgio
下面的“timelocal”解决方案更好,因为DateTime不是标准Perl安装的一部分。 - Axel Bregnsbo

27

获取Unix时间最简单的方法如下:

use Time::Local;
timelocal($second,$minute,$hour,$day,$month-1,$year);

注意参数的反向顺序,以及一月是第0个月。 如需更多选项,请参见来自CPAN的DateTime模块。

至于解析,请参见来自CPAN的Date::Parse模块。如果您确实需要在日期解析方面变得复杂,则Date::Manip可能会有所帮助,尽管其自身的文档警告您远离它,因为它包含许多功能(例如,它知道常见的商业假期),而其他解决方案要快得多。

如果您碰巧了解要解析的日期/时间格式,则简单的正则表达式可能足够了,但最好使用适当的CPAN模块。例如,如果您知道日期始终按YMDHMS顺序排列,请使用CPAN模块DateTime::Format::ISO8601


对于我自己的参考,以下是我用于应用程序的函数,其中我知道日期始终按YMDHMS顺序排列,并且“HMS”部分全部或部分可选。它接受任何定界符(例如,“2009-02-15”或“2009.02.15”)。它返回相应的Unix时间(自1970-01-01 00:00:00 GMT以来的秒数),如果无法解析,则返回-1(这意味着您最好确定您永远不需要合法地解析日期1969-12-31 23:59:59)。它还假定两位数字的年份XX直到“69”指的是“20XX”,否则是“19XX”(例如,“50-02-15”表示2050-02-15,但“75-02-15”表示1975-02-15)。

use Time::Local;

sub parsedate { 
  my($s) = @_;
  my($year, $month, $day, $hour, $minute, $second);

  if($s =~ m{^\s*(\d{1,4})\W*0*(\d{1,2})\W*0*(\d{1,2})\W*0*
                 (\d{0,2})\W*0*(\d{0,2})\W*0*(\d{0,2})}x) {
    $year = $1;  $month = $2;   $day = $3;
    $hour = $4;  $minute = $5;  $second = $6;
    $hour |= 0;  $minute |= 0;  $second |= 0;  # defaults.
    $year = ($year<100 ? ($year<70 ? 2000+$year : 1900+$year) : $year);
    return timelocal($second,$minute,$hour,$day,$month-1,$year);  
  }
  return -1;
}

Time::Local相对于更常见的POSIX(strftime)有什么优势? - Paul Tomblin
3
strftime用于输出时间,timelocal用于“输入”时间。 - Leon Timmermans
1
$month-1 表示从零开始计算的月份!!!!我会尽量不再忘记这个。 - Grant Bowman
1
不要使用大括号来包含包含大括号的正则表达式! - Michael Goldshteyn

17

10

虽然这已经是一个老问题了,但我想提供另一个答案。

Time::Piece 是 Perl 5.9.5 的核心模块。

通过 strptime 方法,可以解析任意格式的时间。

例如:

my $t = Time::Piece->strptime("Sunday 3rd Nov, 1943",
                              "%A %drd %b, %Y");

有用的部分是——由于它是一个重载对象,您可以将其用于数字比较。

例如:

if ( $t < time() ) { #do something }

如果您在字符串上下文中访问它:

print $t,"\n"; 

您将获得:

Wed Nov  3 00:00:00 1943

有一堆访问方法可以进行其他有用的基于时间的转换。 https://metacpan.org/pod/Time::Piece


8
$ENV{TZ}="GMT";
POSIX::tzset();
$time = POSIX::mktime($s,$m,$h,$d,$mo-1,$y-1900);

时间戳已经是格林威治标准时间,没有时区的概念。通过显式设置TZ=GMT,你解析的时间会被认为是来自GMT时区而不是本地日期/时间吗?(我认为你应该使用TZ=UTC,GMT是一个国家的时区)。 - Thomas Guyot-Sionnest
1
@ThomasGuyot-Sionnest 我的意图是说明 mktime 将本地时间转换为 epoch 秒,因此您需要知道输入的本地时间并相应设置 TZ(或者只是假设 TZ 已经设置为所需值,但这很草率)。 - ysth
我认为可以安全地假设大多数人都希望使用本地时间,这也是计算机/服务器上设置的时间。我确实理解在处理不同区域时区很重要,但我认为展示将TZ设置为GMT的示例会分散注意力,甚至可能误导新手使用错误的时区进行时间输入。另一方面,在更改TZ后调用tzset()可以获得额外的加分点 ;) - Thomas Guyot-Sionnest

6

从CPAN获取Date::Manip,然后:

use Date::Manip;
$string = '18-Sep-2008 20:09'; # or a wide range of other date formats
$unix_time = UnixDate( ParseDate($string), "%s" );

编辑:

Date::Manip很大而且速度较慢,但在解析方面非常灵活,并且它是纯perl编写的。如果你写代码时赶时间并且知道运行时不会赶时间,那么请使用它。

例如:在启动时仅使用它来解析命令行选项,但不要在繁忙的Web服务器上解析大量数据。

请参见作者的评论

(感谢下面第一条评论的作者)


6
不,请不要这样做。Date::Manip 的官方文档有一个很大的章节试图说服你不要使用它。 - Aristotle Pagaltzis
2
Date::Manip会在你最脆弱的时候背叛你,请使用DateTime。 :) - Kevin
@AristotlePagaltzis,“试图说服你不要使用它”意味着只是“说它可以用一个日历完成所有事情,但允许多个其他模块更有效地执行多个小于所有事情的任务。” 因此,与scottc已经说过的一切相同,更加强调Date::Manip是一站式解决方案。 - Julian Fondren
@scottc 对于 $string = '18-Sep-2008 12:09:16 PM' 无效,我该如何将 PM 转换为 24 小时格式? - Shantesh

2

为了进一步参考,可以在例如!#/ bin / sh 脚本中应用的一行代码。

EPOCH="`perl -e 'use Time::Local; print timelocal('${SEC}','${MIN}','${HOUR}','${DAY}','${MONTH}','${YEAR}'),\"\n\";'`"

请记住避免使用八进制数值!


1
为什么要使用Perl,当你可以使用更加灵活的GNU日期呢?date -d "STRING" +%s 几乎可以接受任何东西,从“昨天”到“现在之后90天”,甚至更传统的日期格式如“2016年3月15日”,也可以输出到几乎任何格式(+%s是时代,参见date --help获取更多信息)。 (很遗憾,Perl DateTime没有这个功能。) - Adam Katz

2

我最喜欢的日期时间解析器是DateTime::Format::ISO8601。一旦你使用它,你就会得到一个DateTime对象,可以轻松地通过epoch()方法转换为epoch秒。


2

这是“有多种方法可以做到”的最好例子之一,无论是否使用CPAN的帮助。

如果你能控制传递给你的“日期/时间”内容,我建议使用DateTime,可以通过使用特定的Date::Time::Format子类或者使用DateTime::Format::Strptime(如果没有支持你的怪异日期格式的子类,请参阅datetime FAQ获取更多详细信息)。总的来说,如果你想在结果上做任何严肃的事情,那么Date::Time是正确的选择:CPAN上很少有类像它一样如此苛刻和精确。

如果你期望奇怪的自由形式内容,请将其传递给Date::Parse的str2time()方法,它会为你提供一个从时代开始的秒数值,你可以随心所欲地处理它,而不需要使用Date::Manip


2

CPAN上有许多日期操作模块。我特别喜欢的是DateTime,你可以使用strptime模块来解析任意格式的日期。此外,在CPAN上还有许多DateTime::Format模块可用于处理专门的日期格式,但strptime是最通用的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接