我希望能够使用默认的 Perl 安装程序计算两个日期之间的天数,这两个日期的格式都像这样:04-MAY-09 (DD-MMM-YY)。
我找不到任何介绍这种日期格式的教程。我应该为此格式构建一个自定义日期检查器吗?阅读 CPAN 上的 Date::Calc 的更多信息,看起来不太可能支持这种格式。
看起来有些混淆,因为根据您想要实现的目标,“两个日期之间的天数”可能意味着至少两种不同的东西:
以一个例子来说明并注意其区别,假设您有两个如下构造的DateTime对象:
use DateTime;
sub iso8601_date {
die unless $_[0] =~ m/^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/;
return DateTime->new(year => $1, month => $2, day => $3,
hour => $4, minute => $5, second => $6, time_zone => 'UTC');
}
my $dt1 = iso8601_date('2014-11-04T23:35:42Z');
my $dt2 = iso8601_date('2014-11-07T01:15:18Z');
$dt1
是星期二的很晚,而$dt2
是紧随其后的星期五非常早。my $days = $dt2->delta_days($dt1)->delta_days();
print "$days\n" # -> 3
实际上,周二和周五之间有三天的时间间隔。日历距离为1表示“明天”,距离为-1表示“昨天”。DateTime对象中的“时间”部分大多是无关紧要的(除非两个日期处于不同的时区,那么您必须决定这两个日期之间的“日历距离”应该意味着什么)。
如果您想要绝对距离,则应使用以下方法:
my $days = $dt2->subtract_datetime_absolute($dt1)->delta_seconds / (24*60*60);
print "$days\n"; # -> 2.06916666666667
事实上,如果你想按照24小时的间隔将两个日期之间的时间分割开,那么它们之间只有大约2.07天的时间。根据你的应用,你可能想要截断或舍入这个数字。DateTime对象的“时间”部分非常相关,即使是不同时区的日期也有清晰定义的预期结果。
int
是否正确,但由于它取决于他对其的使用方式,我也不知道什么是正确的。有时我可以看到想要floor()
(对于正数给出与您得到的相同),有时是ceil()
,有时是sprintf "%.0f"
,有时只需保留浮点数。但这些都可以根据他的需求进行简单调整。困难的是说你不能使用CPAN,但可以征求代码。发现:该代码要求他将所有DateTime
复制并粘贴到程序中,这是最糟糕的代码重用方式。唉! - tchristdays, delta_days 或 in_units('days')
不起作用 - 你提到了 subtract_datetime_absolute
,但它计算的是秒和纳秒的绝对差异,这不是我们想要的。 - basic6my $days = $dt2->delta_days($dt1)->days;
对我来说返回了一个错误的数字,我需要将其更改为 my $days = $dt2->delta_days($dt1)->delta_days;
。 - hlidkadelta_days()
),取消了踩。 - basic6如果你关心准确性,请记住并不是所有的一天都有86400秒。基于这个假设的任何解决方案对某些情况来说都不正确。
这里有一个片段,我保留它来使用DateTime库以几种不同的方式计算和显示日期/时间差异。我认为打印的最后一个答案就是你想要的。
#!/usr/bin/perl -w
use strict;
use DateTime;
use DateTime::Format::Duration;
# XXX: Create your two dates here
my $d1 = DateTime->new(...);
my $d2 = DateTime->new(...);
my $dur = ($d1 > $d2 ? ($d1->subtract_datetime_absolute($d2)) :
($d2->subtract_datetime_absolute($d1)));
my $f = DateTime::Format::Duration->new(pattern =>
'%Y years, %m months, %e days, %H hours, %M minutes, %S seconds');
print $f->format_duration($dur), "\n";
$dur = $d1->delta_md($d2);
my $dy = int($dur->delta_months / 12);
my $dm = $dur->delta_months % 12;
print "$dy years $dm months ", $dur->delta_days, " days\n";
print $dur->delta_months, " months ", $dur->delta_days, " days\n";
print $d1->delta_days($d2)->delta_days, " days\n";
Time::ParseDate可以很好地处理这种格式:
use Time::ParseDate qw(parsedate);
$d1="04-MAR-09";
$d2="06-MAR-09";
printf "%d days difference\n", (parsedate($d2) - parsedate($d1)) / (60 * 60 * 24);
Date::Calc拥有Decode_Date_EU(以及US等)功能
#!/usr/bin/perl
use Date::Calc qw(Delta_Days Decode_Date_EU);
($year1,$month1,$day1) = Decode_Date_EU('02-MAY-09');
($year2,$month2,$day2) = Decode_Date_EU('04-MAY-09');
print "Diff = " . Delta_Days($year1,$month1,$day1, $year2,$month2,$day2);
我发现很多建议告诉人们要减去秒数,这让我感到不安。(这个问题是我的搜索中第一个谷歌搜索结果,所以我不在乎它有多老。)
我自己也犯过这个错误,想知道应用程序为什么会突然(在周末)显示不正确的时间。因此,我希望这段代码能帮助那些可能面临这种问题的人理解为什么这种方法是错误的,并帮助他们避免这个错误。
下面是一个完整的示例,没有在某个关键点处包含“...”(因为如果您在同一时区插入两个日期,您可能看不到错误)。
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
use DateTime;
# Friday, Oct 31
my $dt1 = DateTime->new(
time_zone => "America/Chicago",
year => 2014,
month => 10,
day => 31,
);
my $date1 = $dt1->strftime("%Y-%m-%d (%Z %z)");
# Monday, Nov 01
my $dt2 = $dt1->clone->set(month => 11, day => 3);
my $date2 = $dt2->strftime("%Y-%m-%d (%Z %z)");
# Friday, Mar 06
my $dt3 = DateTime->new(
time_zone => "America/Chicago",
year => 2015,
month => 3,
day => 6,
);
my $date3 = $dt3->strftime("%Y-%m-%d (%Z %z)");
# Monday, Mar 09
my $dt4 = $dt3->clone->set(day => 9);
my $date4 = $dt4->strftime("%Y-%m-%d (%Z %z)");
# CDT -> CST
print "dt1:\t$dt1 ($date1):\t".$dt1->epoch."\n";
print "dt2:\t$dt2 ($date2):\t".$dt2->epoch."\n";
my $diff1_duration = $dt2->subtract_datetime_absolute($dt1);
my $diff1_seconds = $diff1_duration->seconds;
my $diff1_seconds_days = $diff1_seconds / 86400;
print "diff:\t$diff1_seconds seconds = $diff1_seconds_days days (WRONG)\n";
my $diff1_seconds_days_int = int($diff1_seconds_days);
print "int:\t$diff1_seconds_days_int days (RIGHT in this case)\n";
print "days\t".$dt2->delta_days($dt1)->days." days (RIGHT)\n";
print "\n";
# CST -> CDT
print "dt3:\t$dt3 ($date3):\t".$dt3->epoch."\n";
print "dt4:\t$dt4 ($date4):\t".$dt4->epoch."\n";
my $diff3_duration = $dt4->subtract_datetime_absolute($dt3);
my $diff3_seconds = $diff3_duration->seconds;
my $diff3_seconds_days = $diff3_seconds / 86400;
print "diff:\t$diff3_seconds seconds = $diff3_seconds_days days (WRONG)\n";
my $diff3_seconds_days_int = int($diff3_seconds_days);
print "int:\t$diff3_seconds_days_int days (WRONG!!)\n";
print "days\t".$dt4->delta_days($dt3)->days." days (RIGHT)\n";
print "\n";
输出:
dt1: 2014-10-31T00:00:00 (2014-10-31 (CDT -0500)): 1414731600
dt2: 2014-11-03T00:00:00 (2014-11-03 (CST -0600)): 1414994400
diff: 262800 seconds = 3.04166666666667 days (WRONG)
int: 3 days (RIGHT in this case)
days 3 days (RIGHT)
dt3: 2015-03-06T00:00:00 (2015-03-06 (CST -0600)): 1425621600
dt4: 2015-03-09T00:00:00 (2015-03-09 (CDT -0500)): 1425877200
diff: 255600 seconds = 2.95833333333333 days (WRONG)
int: 2 days (WRONG!!)
days 3 days (RIGHT)
注意:
引用文档(delta_days()与subtract_datetime()):
日期与日期时间的运算
如果您只关心日期(日历)部分的日期时间,您应该使用delta_md()或delta_days(),而不是subtract_datetime()。这将提供可预测的、没有与DST相关的复杂性的结果。
总之:如果你使用DateTime,请不要比较秒数。如果你不确定要使用哪个日期框架,请使用DateTime,它非常棒。
将这两个日期转换为秒,然后进行数学计算:
#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw/mktime/;
{
my %mon = (
JAN => 0,
FEB => 1,
MAR => 2,
APR => 3,
MAY => 4,
JUN => 5,
JUL => 6,
AUG => 7,
SEP => 8,
OCT => 9,
NOV => 10,
DEC => 11,
);
sub date_to_seconds {
my $date = shift;
my ($day, $month, $year) = split /-/, $date;
$month = $mon{$month};
if ($year < 50) { #or whatever your cutoff is
$year += 100; #make it 20??
}
#return midnight on the day in question in
#seconds since the epoch
return mktime 0, 0, 0, $day, $month, $year;
}
}
my $d1 = "04-MAY-99";
my $d2 = "04-MAY-00";
my $s1 = date_to_seconds $d1;
my $s2 = date_to_seconds $d2;
my $days = int(($s2 - $s1)/(24*60*60));
print "there are $days days between $d1 and $d2\n";