How can I calculate the number of days between two dates in Perl?

前端 未结 7 2322
长发绾君心
长发绾君心 2020-12-05 19:05

I want to calculate (using the default Perl installation only) the number of days between two dates. The format of both the dates are like so 04-MAY-09. (DD-MMM-YY)

I

7条回答
  •  悲哀的现实
    2020-12-05 19:38

    This question already has a nice answer, but I want to provide a answer showing why calculating the difference in seconds is WRONG (when we're using formatted/local dates rather than floating dates).

    I find it distressing how many suggestions tell people to subtract seconds. (This question was the first Google hit for my search, so I don't care how old it is.)

    I've made that mistake myself and wondered why the application would suddenly (over the weekend) show incorrent times. So I'm hoping this code will help people (who may be facing such an issue) understand why this approach is wrong and help them avoid that mistake.

    Here is a complete example, one that doesn't contain "..." at some crucial point (because if you insert two dates in the same time zone, you may not see an error).

    #!/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";
    

    Output:

    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)
    

    Notes:

    • Again, I'm using local dates. If you use floating dates, you won't have that problem - simply because your dates stay in the same time zone.
    • Both time ranges in my example go from friday to monday, so the difference in days is 3, not 3.04... and of course not 2.95...
    • Turning the float into an integer using int() (as suggested in an answer) is just wrong, as shown in the example.
    • I do realize that rounding the difference in seconds would also return correct results in my example, but I feel like it's still wrong. You'd be calculating a day difference of 2 (for a large value of 2) and, because it is a large value of 2, turn it into a 3. So as long as DateTime provides the functionality, use DateTime.

    Quoting the documentation (delta_days() vs subtract_datetime()):

    date vs datetime math

    If you only care about the date (calendar) portion of a datetime, you should use either delta_md() or delta_days(), not subtract_datetime(). This will give predictable, unsurprising results, free from DST-related complications.

    Bottom line: Don't diff seconds if you're using DateTime. If you're not sure what date framework to use, use DateTime, it's awesome.

提交回复
热议问题