我想计算(仅使用默认的 Perl 安装)两个日期之间的天数。两个日期的格式都是 04-MAY-09。(DD-MMM-YY)
我找不到任何讨论该日期格式的教程。我应该为此格式构建自定义日期检查器吗?进一步阅读 CPAN 上的Date::Calc似乎不太可能支持这种格式。
如果您关心准确性,请记住并非所有日子都有 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";
似乎有些混乱,因为根据您要完成的任务,“两个日期之间的天数”可能意味着至少两个不同的东西:
作为示例并注意区别,假设您有两个 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
事实上,周二和周五之间有 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 对象的“时间”部分非常相关,即使对于不同时区的日期,预期结果也得到了很好的定义。
Date::Calc 有 Decode_Date_EU (和美国等)
#!/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);
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);
这个问题已经有一个很好的答案,但我想提供一个答案,说明为什么计算以秒为单位的差异是错误的(当我们使用格式化/本地日期而不是浮动日期时)。
我发现有多少建议告诉人们要减去秒数,这让我很痛苦。(这个问题是我搜索的第一个 Google 热门问题,所以我不在乎它的年龄。)
我自己也犯了这个错误,想知道为什么应用程序会突然(在周末)显示不正确的时间。所以我希望这段代码能帮助人们(他们可能面临这样的问题)理解为什么这种方法是错误的,并帮助他们避免这个错误。
这是一个完整的示例,在某个关键点不包含“...”(因为如果您在同一时区插入两个日期,您可能不会看到错误)。
#!/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";
您可以将日期转换为长整数格式,即自纪元以来的秒数(我认为是 1970 年的某个日期)。然后,您有两个变量,它们是以秒为单位的日期;从较大的减去较小的。现在你有一个以秒为单位的时间跨度;除以 24 小时内的秒数。