50

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

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

4

13 回答 13

36

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

使用 DateTimes 可以很容易地从 epoch 转换为 date 对象。

或者,localtime和 gmtime 会将 epoch 转换为包含日期、月份和年份的数组,而来自Time::Local 模块的 timelocal 和 timegm将执行相反的操作,转换时间元素数组(秒、分钟、...、天, 月等) 进入一个时代。

于 2008-09-18T18:49:25.780 回答
25

这是获取 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;
}
于 2008-09-18T18:43:36.777 回答
17

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

于 2008-09-18T18:53:34.163 回答
9

我知道这是一个老问题,但我想我会提供另一个答案。

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

于 2015-02-12T17:42:27.023 回答
8
$ENV{TZ}="GMT";
POSIX::tzset();
$time = POSIX::mktime($s,$m,$h,$d,$mo-1,$y-1900);
于 2008-09-24T07:39:21.183 回答
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 服务器上使用它来解析大量数据。

作者评论

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

于 2008-09-18T19:09:58.493 回答
2

我最喜欢的日期时间解析器是DateTime::Format::ISO8601 一旦你得到它的工作,你就会有一个 DateTime 对象,很容易用 epoch() 转换为纪元秒

于 2008-09-18T19:07:18.467 回答
2

无论有没有 CPAN 的帮助,这可能是“有不止一种方法”的更好例子之一。

如果您可以控制作为“日期/时间”传递的内容,我建议您使用DateTime路线,或者使用特定的 Date::Time::Format 子类,或者使用 DateTime::Format::Strptime if没有一个支持您古怪的日期格式(有关详细信息,请参阅日期时间常见问题解答)。一般来说,如果你想对结果做任何严肃的事情,Date::Time 是要走的路:CPAN 上很少有课程能像肛门保持和痴迷一样准确。

如果你期待奇怪的自由形式的东西,把它扔到Date::Parse的 str2time() 方法,它会给你一个 seconds-since-epoch 值,然后你可以用你邪恶的方式来处理,而没有Date 的开销: : 操作

于 2008-09-18T20:25:23.413 回答
2

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

于 2008-09-18T20:32:15.317 回答
2

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

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

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

于 2010-03-23T11:36:33.113 回答
0

我正在使用一个非常旧的操作系统,我不敢在上面安装库,所以这就是我使用的;

%MonthMatrix=("Jan",0,"Feb",31,"Mar",59,"Apr",90,"May",120,"Jun",151,"Jul",181,"Aug",212,"Sep",243,"Oct",273,"Nov",304,"Dec",334);
$LeapYearCount=int($YearFourDigits/4);
$EpochDayNumber=$MonthMatrix{$MonthThreeLetters};
if ($LeapYearCount==($YearFourDigits/4)) { if ($EpochDayNumber<32) { $EpochDayNumber--; }}
$EpochDayNumber=($YearFourDigits-1970)*365+$LeapYearCount+$EpochDayNumber+$DayAsNumber-493;
$TimeOfDaySeconds=($HourAsNumber*3600)+($MinutesAsNumber*60)+$SecondsAsNumber;
$ActualEpochTime=($EpochDayNumber*86400)+$TimeOfDaySeconds;

输入变量是;

$MonthThreeLetters
$DayAsNumber
$YearFourDigits
$HourAsNumber
$MinutesAsNumber
$SecondsAsNumber

...这应该是不言自明的。

当然,输入变量假定为 GMT (UTC)。输出变量是“$ActualEpochTime”。(通常,我只需要 $EpochDayNumber,这就是为什么这个多余的变量可以独立存在的原因。)

我多年来一直使用这个公式,没有任何错误。

于 2020-03-28T23:09:19.723 回答
-3

一个过滤器将标准输入上各种 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 ($1, "%Y-%m-%d$2%H:%M:%S")->epoch - (defined ($3) ? $3 * 3600 : 0)/eg;
  print;
}
martind@whitewater:~$ 
于 2008-09-18T19:01:27.750 回答
-4

如果您只是在寻找一个命令行实用程序(即,不是从其他函数调用的东西),试试这个脚本。它假定存在 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
于 2008-09-18T18:51:21.017 回答