如何计算Perl中两个日期之间的天数?

15 perl time datetime date days

Heylo,

我想计算(仅使用默认的Perl安装)两个日期之间的天数.两个日期的格式都是这样的04-MAY-09.(DD-MMM-YY)

我找不到任何讨论该日期格式的教程.我应该为这种格式构建自定义日期检查器吗?进一步阅读CPAN 上的Date :: Calc,看起来不太可能支持这种格式.

谢谢.

Joh*_*usa 16

如果您关心准确性,请记住并非所有日子都有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";
Run Code Online (Sandbox Code Playgroud)


Jua*_*rro 15

似乎有很多混乱,因为根据你想要完成的事情,"两个日期之间的天数"至少可以表示两个不同的事情:

  1. 日历距离两个日期之间.
  2. 绝对距离两个日期之间.

作为示例并注意区别,假设您有两个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');
Run Code Online (Sandbox Code Playgroud)

请注意,$dt1是相当晚在星期二,同时$dt2非常下列周五早.

如果您想使用日历距离:

my $days = $dt2->delta_days($dt1)->delta_days();
print "$days\n" # -> 3
Run Code Online (Sandbox Code Playgroud)

实际上,周二和周五之间有3天.日历距离1表示"明天",距离-1表示"昨天".DateTime对象的"时间"部分大多无关紧要(除非两个日期落在不同的时区,否则您必须决定这两个日期之间的"日历距离"应该是什么意思).

如果你想要绝对距离,那么改为使用:

my $days = $dt2->subtract_datetime_absolute($dt1)->delta_seconds / (24*60*60);
print "$days\n"; # -> 2.06916666666667
Run Code Online (Sandbox Code Playgroud)

实际上,如果你想以24小时的方式分割两个日期之间的时间,它们之间只有大约2.07天.根据您的应用程序,您可能希望截断或舍入此数字.DateTime对象的"时间"部分非常相关,即使对于不同时区的日期,预期结果也很明确.


Mic*_*mer 5

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);
Run Code Online (Sandbox Code Playgroud)

  • 显然,问题中提到Date :: Calc意味着CPAN的公平游戏. (2认同)

小智 5

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);
Run Code Online (Sandbox Code Playgroud)


bas*_*ic6 5

这个问题已经有一个很好的答案,但我想提供一个答案,说明为什么计算秒差是错误的(当我们使用格式化/本地日期而不是浮动日期时)。

我发现有多少建议告诉人们要减去秒数,这让我感到沮丧。(这个问题是我搜索的第一个 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";
Run Code Online (Sandbox Code Playgroud)

输出:

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)
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 同样,我使用的是本地日期。如果您使用浮动日期,则不会有这个问题 - 仅仅是因为您的日期保持在同一时区。
  • 我的例子中的两个时间范围都是从星期五到星期一,所以天数的差异是 3,而不是 3.04……当然也不是 2.95……
  • 使用int()将浮点数转换为整数(如答案中所建议)是错误的,如示例所示。
  • 我确实意识到以秒为单位舍入差异也会在我的示例中返回正确的结果,但我觉得它仍然是错误的。您将计算 2 天的差异(对于 2 的大值),并且由于它是 2 的大值,因此将其转换为 3。因此,只要 DateTime 提供该功能,请使用 DateTime。

引用文档(delta_days() 与subtract_datetime()):

日期与日期时间数学

如果您只关心日期时间的日期(日历)部分,您应该使用 delta_md() 或 delta_days(),而不是subtract_datetime()。这将提供可预测的、不出所料的结果,没有与 DST 相关的并发症。

底线:如果您使用的是 DateTime,请不要区分秒数。如果您不确定要使用什么日期框架,请使用 DateTime,它很棒。


小智 1

您可以将日期转换为长整数格式,即自纪元以来的秒数(我认为是 1970 年的某个日期)。然后你有两个变量,它们是以秒为单位的日期;从较大的值中减去较小的值。现在你的时间跨度以秒为单位;将其除以 24 小时内的秒数。