21

(デフォルトの Perl インストールのみを使用して) 2 つの日付間の日数を計算したいと考えています。両方の日付の形式は、04-MAY-09 のようになります。(DD-MMM-YY)

その日付形式について説明したチュートリアルは見つかりませんでした。この形式のカスタム日付チェッカーを作成する必要がありますか? CPANのDate::Calcをさらに読むと、この形式がサポートされている可能性は低いようです。

4

7 に答える 7

19

達成しようとしていることによっては、「2つの日付の間の日数」は、少なくとも2つの異なることを意味する可能性があるため、かなり混乱しているようです。

  1. 2つの日付間のカレンダー距離。
  2. 2つの日付間の絶対距離。

例として、違いに注意するために、次のように構築された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');

$dt1火曜日はかなり遅く、次の金曜日$dt2非常に早いことに注意してください。

カレンダーの距離を使用する場合:

my $days = $dt2->delta_days($dt1)->delta_days();
print "$days\n" # -> 3

実際、火曜日と金曜日の間には3日間あります。カレンダーの距離1は「明日」を意味し、距離-1は「昨日」を意味します。DateTimeオブジェクトの「時間」部分はほとんど関係ありません(2つの日付が異なるタイムゾーンにある場合を除いて、これら2つの日付間の「カレンダー距離」が何を意味するかを決定する必要があります)。

絶対距離が必要な場合は、代わりに次を使用してください。

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

実際、2つの日付の間の時間を24時間単位で分割したい場合、それらの間には約2。07日しかありません。アプリケーションによっては、この数値を切り捨てたり丸めたりすることができます。DateTimeオブジェクトの「時間」部分は非常に関連性が高く、異なるタイムゾーンの日付でも期待される結果は明確に定義されています。

于 2011-08-18T17:40:59.680 に答える
19

精度を重視する場合は、すべての日が 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";
于 2009-05-04T20:57:23.683 に答える
5

この質問にはすでに良い回答がありますが、差を秒単位で計算することが間違っている理由を示す回答を提供したいと思います (浮動日付ではなく、書式設定された/ローカルの日付を使用している場合)。

人々に数秒を差し引くように指示する提案がいくつあるかは、私は苦痛だと思います。(この質問は、私の検索で最初にヒットした Google の質問だったので、それが何年経ったかは気にしません。)

私は自分でその間違いを犯し、なぜアプリケーションが突然 (週末に) 間違った時刻を表示するのか疑問に思いました。したがって、このコードが (このような問題に直面している可能性がある) 人々に、このアプローチが間違っている理由を理解し、その間違いを回避するのに役立つことを願っています。

これは完全な例で、重要なポイントに「...」が含まれていません (同じタイム ゾーンに 2 つの日付を挿入しても、エラーが表示されない可能性があるため)。

#!/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)

ノート:

  • ここでも、ローカルの日付を使用しています。変動日付を使用する場合、日付が同じタイムゾーンにあるため、その問題は発生しません。
  • 私の例の両方の時間範囲は金曜日から月曜日までなので、日数の差は 3.04 ではなく 3 です...そしてもちろん 2.95 ではありません...
  • 例に示すように、 int()を使用して float を整数に変換すること ( answerで提案されているように) は間違っています。
  • 私の例では、差を秒単位で丸めても正しい結果が返されることはわかっていますが、それでも間違っているように感じます。日差 2 (2 という大きな値) を計算し、2 という大きな値であるため、3 に変えます。したがって、DateTime が機能を提供する限り、DateTime を使用してください。

ドキュメントを引用する(delta_days()とsubtract_datetime()):

日付と日時の計算

datetime の日付 (カレンダー) 部分のみを気にする場合は、subtract_datetime() ではなく、delta_md() または delta_days() を使用する必要があります。これにより、DST に関連した複雑な問題がなく、予測可能で驚くべき結果が得られません。

結論: DateTime を使用している場合は、秒を比較しないでください。どの日付フレームワークを使用すればよいかわからない場合は、DateTime を使用してください。

于 2014-11-04T10:23:03.997 に答える
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);
于 2009-05-04T19:25:11.567 に答える
5

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);
于 2009-05-04T19:54:50.900 に答える
1

2 つの日付を秒に変換し、計算を行います。

#!/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";
于 2009-05-04T20:26:31.263 に答える
1

日付を長整数形式に変換できます。これは、エポックからの秒数です (1970 年の日付だと思います)。次に、秒単位の日付である 2 つの変数があります。大きい方から小さい方を引きます。これで、秒単位の期間ができました。24 時間の秒数で割ります。

于 2009-05-04T19:06:19.583 に答える