如何在 Perl 中获取昨天的日期?

9

目前我使用以下代码获取今天的日期。现在我需要获取昨天的日期,应该如何实现?

my $today = `date "+%Y-%m-%d"`;

这个问题问到了“昨天”,但是示例使用了HMS……你确实需要时间,还是只需要日期? - zdim
实际上@Heyddo的答案对我有用。但是我使用以上内容使用时间戳创建目录。但是我得到了\n。例如:2019-05-14\n,但我没有输入任何回车符。你有什么想法是什么原因? - Debugger
1
"但是我用反引号得到它。\n" - 反引号返回它;你需要使用 chomp - zdim
其实我想要今天和昨天两个日期。所以我使用上面的代码获取了今天的日期,现在我可以通过 @Heyddo 的答案使用今天的日期来获取昨天的日期。 - Debugger
如果时间不存在,您希望得到什么输出(例如,在纽约的2019-03-10T02:30:00之前1天)? - ikegami
显示剩余2条评论
6个回答

13

如果您想获取昨天的日期:

use DateTime qw( );

my $yday_date =
   DateTime
      ->now( time_zone => 'local' )
      ->set_time_zone('floating')
      ->truncate( to => 'day' )
      ->subtract( days => 1 )
      ->strftime('%Y-%m-%d');

例如,在纽约的2019年05月14日01:00:00,它应该返回2019年05月13日。
例如,在纽约的2019年11月04日00:00:00,它应该返回2019年11月03日。
请注意,使用->now( time_zone => 'local' )->set_time_zone('floating')->truncate( to => 'day' )而不是->today( time_zone => 'local' )来避免这个问题

如果您想提前一天获得时间:

use DateTime qw( );

my $yday_datetime =
   DateTime
      ->now( time_zone => 'local' )
      ->subtract( days => 1 )
      ->strftime('%Y-%m-%d %H:%M:%S');

例如,在2019年5月14日01:00:00在纽约,它将返回2019年5月13日01:00:00。
例如,在2019年3月10日12:00:00在纽约,它将返回2019年3月9日12:00:00。
例如,在2019年3月11日02:30:00在纽约,它将导致错误。由于进入夏令时,2019年3月10日02:30:00不存在。

如果你想把时间提前24小时:

use DateTime qw( );

my $yday_datetime =
   DateTime
      ->now( time_zone => 'local' )
      ->subtract( hours => 24 )
      ->strftime('%Y-%m-%d %H:%M:%S');

例如,在2019-05-14 01:00:00时在纽约,它会返回2019-05-13 01:00:00。
例如,在2019-03-10 12:00:00时在纽约,它会返回2019-03-09 11:00:00。
例如,在2019-03-11 02:30:00时在纽约,它会返回2019-03-10 01:30:00。

我必须使用day而不是days来截断,以防止以下异常:Validation failed for type named TruncationLevel declared in package DateTime::Types (C:/Strawberry/perl/vendor/lib/DateTime/Types.pm) at line 135 in sub named (eval) with value "days" - nck
@nck 确实。已修复。 - ikegami

9
使用DateTime模块。
use strict;
use warnings;
use feature 'say';

use DateTime;

my $today = DateTime->today(time_zone => 'local');

my $yesterday = $today->clone->add(days => -1);

say $yesterday->ymd;  #--> 2019-05-13

或者直接
my $yesterday = DateTime->today(time_zone => 'local')->add(days => -1);

现在,这些对象可以实现与日期时间相关的几乎任何操作。
要提取符合问题格式的字符串,请使用$yesterday->ymd,因为ymd的默认格式是%Y-%m-%d。可以使用strftime方法获得几乎任意格式。
没有必要为这样的工作使用系统设施。更重要的是,该模块在各种奇特情况和需求方面都更加复杂和全面。
例如:在某些日期时间,夏令时开始时,某些时间段就根本不存在了。文档提供了一个示例,即2003-04-06,当01:59:59“跳”到03:00:00时,那个小时就消失了。该模块通过引发错误来正确处理使用这些(不存在的时间)的尝试。
如果您需要处理日期,则使用系统的date甚至不是一种选择,因为您只会得到一个字符串,您必须手动解析以进行进一步使用。
已经添加了时间,不仅仅是日期。例如:
my $today = DateTime->now(time_zone => 'local');

say $today->strftime("%Y%m%d_%H.%M.%S");         #--> 20190522_22.44.23

my $yesterday = $today->clone->add(days => -1);

say $yesterday->strftime("%Y%m%d_%H.%M.%S");     #--> 20190521_22.44.23

where方法now“保留”(本地)时间和日期,其中today是对now进行truncate操作的结果。


1
注意:对于不存在的时间(例如在纽约时间2019-03-10T02:30之前一天),此操作会出现错误。如果您只需要日期而非时间,则可以使用->set_time_zone('floating')来避免出错。 - ikegami
@zdim 我只是好奇。是否可能运行一次并获取多天?例如:使用datetime获取今天(YYYY-MM-DD-HH-MM-SS),使用今天可以获取昨天或X天前的日期,而无需每次运行? - Debugger
1
@Debugger 如果我理解你的意思,那将是答案中的第二种方式(“_or directly_”)?像这样:my $d_N_back = DateTime->today(time_zone=>"local")->add(days=>-$N);(其中 $N 是数字)。现在这是一个可以使用的对象。如果您更喜欢获得一个准备好的字符串,请继续该链:my $str = DateTime->[as above]->ymd;。或者,对于更具体的格式,使用 ->strftime(...)。这是你的意思吗? - zdim
1
@Debugger 当我再次阅读时,我不理解“_without running each time_”是什么意思?一旦你有其中之一,你可以在一个语句中从它获取任何其他日期--这就是你想要的“_using today can i take yesterday or X days before_”吗?像 $N_back = $today->clone->add(days=>-$N); 这样。这里的 clone$today 制作另一个对象,因此您保留了 $today 并获得了一个新对象,即今天之前 $N 天。或者,要获取字符串,$N_back_str = $today->clone->add(days=>-$N)->ymd; - zdim
@zdim,这是我的问题。我正在创建Perl脚本,需要使用时间戳创建目录。之前我使用了Y-M-D,但现在我计划改为Y-M-D-H-M-S,以防脚本在一天内运行多次。你提供的代码输出2019-05-23T00:00:00,非常感谢你的帮助。 - Debugger
@Debugger 好的,谢谢 -- 为了完整性已经添加了时间到答案。 - zdim

4

我建议使用DateTime(如ikegami的答案所述)。但是如果您不想安装CPAN模块,则此方法仅使用标准Perl安装中提供的功能。

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use Time::Local;
use POSIX 'strftime';

# Get the current date/time
my ($sec, $min, $hr, $day, $mon, $yr) = localtime;

# Get the epoch seconds for midday today
# (we use midday to eliminate potential problems
# when entering or leaving daylight savings time)
my $midday = timelocal(0, 0, 12, $day, $mon, $yr);

# Subtract a day's worth of seconds from the epoch
my $midday_yesterday = $midday - (24 * 60 * 60);

# Convert that epoch value to a string date using
# localtime() and POSIX::strftime().
my $yesterday = strftime('%Y%m%d', localtime($midday_yesterday));

say $yesterday;

3

使用Time::Moment,只需获取昨天的日期:


use strict;
use warnings;
use Time::Moment;
my $yesterday = Time::Moment->now->minus_days(1)->strftime('%Y-%m-%d');

Time::Moment在瞬间和偏移量上工作,而不是时区,所以如果您想要与DateTime示例中的本地时间相同的“一天前”的确切日期和时间,您可以使用Time::Moment::Role::TimeZone

最初的回答使用了DateTime,但是Time::Moment::Role::TimeZone可以更好地处理时区问题。

use strict;
use warnings;
use Time::Moment;
use Role::Tiny ();
my $class = Role::Tiny->create_class_with_roles('Time::Moment', 'Time::Moment::Role::TimeZone');
my $yesterday = $class->now->minus_days(1)->with_system_offset_same_local; # 1 day ago
my $yesterday = $class->now->minus_days(1)->with_system_offset_same_instant; # 24 hours ago

如果当前时间在本地时区的前一天不存在,则相同的警告适用。

最初的回答:

如果当前时间在先前一天的本地时区中不存在,那么同样的注意事项也适用。


1
使用反引号字符串有一些怪癖...我通常不喜欢那个操作符。
我建议使用由Time::Piece模块提供的localtime()函数(虽然有一个内置的localtime()函数,但来自Time::Piece的函数更棒 :)

use Time::Piece;

my ($t, $today, $yesterday);

$t = localtime();
$today = sprintf('%04d/%02d/%02d', $t->year, $t->mon, $t->mday);

$t = localtime(time() - 86400);
$yesterday = sprintf('%04d/%02d/%02d', $t->year, $t->mon, $t->mday);


1
这并不总是有效的。有时它会给出今天的日期,有时它会给出两天前的日期。Time::Piece是一个糟糕的模块,因为它使得这些错误变得非常容易发生。 - ikegami
@ikegami 我认为我的示例代码中的数学计算有误,而不是 Time::Piece。 :-) 在我看来,Time::Piece 的好坏取决于它的文档说明。我可能不应该使用时间数学来进行日期计算... - gugod
1
我也这么认为。这明显是对模块的误用。(你应该使用localtime(time()) - 86400或者只是localtime() - 86400而不是localtime(time() - 86400))但是,这个模块鼓励这种错误的事实是这个模块存在问题的原因。 - ikegami

1
my $yesterday = `date -d yesterday +%Y%m%d-%H%M%S`;

谢谢帮助,那么如果我需要前天,我可以加上-2d吗? - Debugger
date -d -2天 +%Y%m%d - Heyddo
2
这并不总是有效的。有时会出现时间相差一个小时的情况。(例如,在纽约, 20190310-120000 的时间会被转换为 20190309-110000。) - ikegami
3
使用外部程序处理这么简单的事情并不是一个好主意。它会降低你的代码可移植性,并且不必要地使其变得更慢。 - Dave Cross

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