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.