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

时间:2020-03-06 14:22:50  来源:igfitidea点击:

给定日期/时间数组(年,月,日,时,分,秒),我们如何将其转换为纪元时间,即自1970-01-01 00:00:00 GMT以来的秒数?

额外的问题:如果以字符串形式给出日期/时间,我们将如何首先将其解析为(y,m,d,h,m,s)数组?

解决方案

这是获取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以来的秒数),如果无法解析,则返回-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 = ;  $month = ;   $day = ;
    $hour = ;  $minute = ;  $second = ;
    $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;
}

如果我们使用的是DateTime模块,则可以在DateTime对象上调用epoch()方法,因为这就是我们认为的unix时间。

使用DateTimes可以使我们轻松地从纪元转换为日期对象。

或者,localtime和gmtime会将一个纪元转换为包含日月和年的数组,而Time :: Local模块中的timelocal和timegm则相反,转换时间元素数组(秒,分钟,...,天,月份等)。

如果我们只是在寻找命令行实用程序(即不是从其他函数调用的实用程序),请尝试使用此脚本。它假定存在GNU日期(几乎在所有Linux系统上都存在):

#! /usr/bin/perl -w

use strict;

$_ = (join ' ', @ARGV);
$_ ||= <STDIN>;

chomp;

if (/^[\d.]+$/) {
    print scalar localtime $_;
    print "\n";
}
else {
    exec "date -d '$_' +%s";
}

运作方式如下:

$ Time now
1221763842

$ Time yesterday
1221677444

$ Time 1221677444
Wed Sep 17 11:50:44 2008

$ Time '12:30pm jan 4 1987'
536790600

$ Time '9am 8 weeks ago'
1216915200

要解析日期,请查看CPAN中的Date :: Parse。

将标准输入上的日期转换为标准时间后的秒数的过滤器可以将任何与ISO相关的各种格式的日期转换为标准日期(在读完Mighty Kuhn的著作之后谁还会使用其他格式?) :

martind@whitewater:~$ cat `which isoToEpoch`
#!/usr/bin/perl -w
use strict;
use Time::Piece;
# sudo apt-get install libtime-piece-perl
while (<>) {
  # date --iso=s:
  # 2007-02-15T18:25:42-0800
  # Other matched formats:
  # 2007-02-15 13:50:29 (UTC-0800)
  # 2007-02-15 13:50:29 (UTC-08:00)
  s/(\d{4}-\d{2}-\d{2}([T ])\d{2}:\d{2}:\d{2})(?:\.\d+)? ?(?:\(UTC)?([+\-]\d{2})?:?00\)?/Time::Piece->strptime (, "%Y-%m-%d%H:%M:%S")->epoch - (defined () ?  * 3600 : 0)/eg;
  print;
}
martind@whitewater:~$

我最喜欢的日期时间解析器是DateTime :: Format :: ISO8601一旦完成该工作,我们将拥有一个DateTime对象,可以使用epoch()轻松将其转换为纪元秒

从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服务器上使用它来解析大量数据。

见作者评论。

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

不管有没有CPAN的帮助,"可能有不止一种方法"可能是更好的例子之一。

如果我们可以控制作为"日期/时间"传递的内容,我建议我们使用特定的Date :: Time :: Format子类或者使用DateTime :: Format :: Strptime进行DateTime路由没有一种格式支持我们古怪的日期格式(有关更多详细信息,请参见datetime常见问题解答)。通常,如果我们想对结果进行任何认真的处理,则必须使用Date :: Time:CPAN上很少有类对肛门保持性和强迫性准确的。

如果我们期望使用奇怪的自由格式的东西,请将其扔到Date :: Parse的str2time()方法中,这将使我们获得一个秒-自纪元的值,然后可以采用邪恶的方式,而不会花费Date :: Manip 。

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

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

作为进一步参考,可以在例如!! / bin / sh脚本中应用一个衬里。

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

请记住要避免八进制值!