0
$time_start = mktime(12,0,0,1,1,2011);
$time_end = mktime(12,0,0,7,1,2011);
$format = '%m months';

$start_date = new DateTime(date(DATE_ATOM,$time_start));
$end_date = new DateTime(date(DATE_ATOM,$time_end));
$diff = $start_date->diff($end_date, true);
echo $diff->format($format);

Outputs "5 months", I guess because it's off by an hour due to DST. However, I need to calculate the difference in calendar months; is there another class/function that will do this?


Added some fixes:

if($time_start > $time_end) list($time_start, $time_end) = array($time_end, $time_start);
$time_end += (date('I',$time_end)-date('I',$time_start))*3600; // correct for DST
$start_date = new DateTime(date(DATE_ATOM,$time_start));
$end_date = new DateTime(date(DATE_ATOM,$time_end));
$start_date->modify('12pm'); // ignore time difference
$end_date->modify('12pm');
$diff = $start_date->diff($end_date);
echo $diff->format($format);

This seems to give the results I want, but I haven't fully tested it yet.


More fixes, based on Herbert's suggestions:

if($time_start > $time_end) list($time_start, $time_end) = array($time_end, $time_start);
$start_date = new DateTime();
$end_date = new DateTime();
$start_date->setTimestamp($time_start);
$end_date->setTimestamp($time_end);
$diff = $start_date->diff($end_date);
$hours = $diff->format('%h');
$mins = $diff->format('%i');
$secs = $diff->format('%s');
$start_date->setTime(12,0,0);//ignore time difference for date calculations
$end_date->setTime(12,0,0);
$diff = $start_date->diff($end_date);
$years = $diff->format('%y');
$months = $diff->format('%m');
$weeks = $diff->format('%w');
$days = $diff->format('%d');

Note that the $start_date->modify('12pm') wasn't actually doing anything at all. Not sure why it didn't error.

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • 5
    It's probably 5 months 29 days or something - try outputting the full period including days (`%m` gives you only the month *portion* of the full date) – Pekka Sep 29 '11 at 17:42
  • 3
    Come on Mark. With 15k you shouldn't be so quick to throw around the word *bug*. `var_dump($diff);` and see what **Pekka** mentioned. – Jason McCreary Sep 29 '11 at 17:44
  • 1
    Well you're both correct that it says 5 months, 29 days, 23 hours (DST I guess), but that's not really the point. If a human looked at those 2 dates, he would think they are 6 months apart because they differ by 6 calendar months. That's the answer I need. – mpen Sep 29 '11 at 17:56
  • 2
    Then `DateDiff::format` may not be the right thing for you - maybe you need to check out a "nice dates" library that rounds up periods in a way that makes sense to humans. – Pekka Sep 29 '11 at 18:00
  • I get 6 months exaclty. Curious. – Herbert Sep 29 '11 at 18:27
  • @Herbert: Do you observe DST where you live (or where your server is)? Probably different locale settings. – mpen Sep 29 '11 at 18:34
  • Yeah, it's currently EDT here and my test server is on my machine. I just copied and pasted your original code (just now noticed the update) and got _exactly_ 6 months. `var_dump` yields `public 'days' => int 6015` – Herbert Sep 29 '11 at 18:38
  • Hold on. `date_default_timezone_get()` reveals UTC. Maybe that's it? – Herbert Sep 29 '11 at 18:44
  • @Herbert: Mine appears to be set to `America/Los_Angeles`. Could be it. – mpen Sep 29 '11 at 19:39

1 Answers1

2

Update

After messing around with a lot of different ideas it occurred to me that timestamps are in GMT (UTC). date(DATE_ATOM,$time_start) is applying the default timezone. However, if you set the timestamp explicitly, DateTime will assume UTC — thus, no DST problem.

The following code seems to work regardless of timezone or DST.

<?php

$time_start = mktime(12,0,0,1,1,2011);
$time_end = mktime(12,0,0,7,1,2011)

$start_date = new DateTime();
$end_date = new DateTime();

$start_date->setTimestamp($time_start);
$end_date->setTimestamp($time_end);

$diff = $start_date->diff($end_date);

$format = '%m months';
echo $diff->format($format);

?>

I tested some edge cases — both date and time, and a variety of timezones — but I haven’t tested every possibility so if you come across an issue, I’d be interested in hearing about it.

Herbert
  • 5,698
  • 2
  • 26
  • 34
  • I suspect the UTC trick will fail when switching to UTC causes the day to change. It's also possible that my trick could have the same effect if I didn't add/subtract the hour in the right place... – mpen Sep 29 '11 at 19:45
  • This is an interesting problem and I fully intend to keep working on it. :-) – Herbert Sep 29 '11 at 19:58
  • @Mark: I think I found a solution. – Herbert Sep 29 '11 at 22:00
  • I think you might be right. Unix timestamps are always in GMT, so if we stay consistent with that, I think we should be safe. Thanks for all your help! I'll let you know if I come across any issues with this. – mpen Sep 29 '11 at 22:35
  • Dumping `$start_date` after calling `setTimestamp` actually does show my default timezone, not GMT/UTC...but it still seems to work. – mpen Sep 29 '11 at 23:31
  • That's odd. Maybe it's a formatting thing. Nevertheless, internally it seems to see it as UTC. Hey, as long as it works... :-) – Herbert Sep 30 '11 at 02:33