How to calculate the difference between two days as a formatted string?

Here is what I have so far:

/**
 * Parse a duration between 2 date/times in seconds
 * and to convert that duration into a formatted string
 *
 * @param integer $time_start start time in seconds
 * @param integer $time_end   end time in seconds
 * @param string  $format     like the php strftime formatting uses %y %m %w %d %h or %i.
 * @param boolean $chop       chop off sections that have 0 values
 */
public static function FormatDateDiff($time_start = 0, $time_end = 0, $format = "%s", $chop = false) {

        if($time_start > $time_end) list($time_start, $time_end) = array($time_end, $time_start);

        list($year_start,$month_start,$day_start) = explode('-',date('Y-m-d',$time_start));
        list($year_end,$month_end,$day_end) = explode('-',date('Y-m-d',$time_end));

        $years = $year_end - $year_start;
        $months = $month_end - $month_start;
        $days = $day_start - $day_end;
        $weeks = 0;
        $hours = 0;
        $mins = 0;
        $secs = 0;

        if(mktime(0,0,0,$month_end,$day_end) < mktime(0,0,0,$month_start,$day_start)) {
            $years -= 1;
        }
        if($days < 0) {
            $months -= 1;
            $days += 30; // this is an approximation...not sure how to figure this out
        }
        if($months < 0) $months += 12;
        if(strpos($format, '%y')===false) {
            $months += $years * 12;
        }
        if(strpos($format, '%w')!==false) {
            $weeks = floor($days/7);
            $days %= 7;
        }
        echo date('Y-m-d',$time_start).' to '.date('Y-m-d',$time_end).": {$years}y {$months}m {$weeks}w {$days}d<br/>";
}

(This is incomplete and inaccurate)

I can't seem to get the math right. Its naive division will not work due to leap years and different lengths of months.

The logic should also vary depending on the format string. For example, passing from 04 to February 2010 to June 28, 2011 (as unix timestamps) with a format string %y year %m month %d dayshould output 1 year 4 month 24 day, but if %y yearomitted, it is necessary to add 12 months to a month, i.e., there should be a way out 16 month 24 day.

Should also handle the time ... but I haven't figured it out yet.


date_diff . , date_diff, .

, $diff->format , ... , " " . :

>>> $start = new DateTime('04-Feb-2010')
>>> $end = new DateTime('28-Jun-2011')
>>> $diff = $start->diff($end)
>>> $diff->format('%m months, %d days')
'4 months, 24 days'

16 months, 24 days, . , , , . , , , , , , , .

,

  • %y ,
  • %m ,
  • %w ,
  • %h ,
  • %m ,

" " , , .

+5
5

, date_diff .

<?php

$january = new DateTime('2010-01-01');
$february = new DateTime('2011-02-20 3:35:28');
$interval = $february->diff($january);

$parts = $interval->format('%y %m %d %h %i %s %a');

$weeks = 0;
list($years, $months, $days, $hours, $minutes, $seconds, $total_days) = explode(' ', $parts);

if ($days >= 7) {
    $weeks = (int)($days / 7);
    $days  %= 7;
}

echo "$years years, $months months, $weeks weeks, $days days, $hours hours, $minutes minutes $seconds seconds";
// 1 years, 1 months, 2 weeks, 5 days, 3 hours, 35 minutes 28 seconds

, , .

, . (.. 1 1 - , 12 ). "" , , , .

+1
$absolute = false; //default, returns years, months, days, etc. True would return seconds
$difference = date_diff($dateTimeStart, $dateTimeEnd, $absolute);
echo $difference->format("%y Year(s) %m Month(s) ...");

, , Calendar .

, . $weeks = $difference->d / 7;, . , , , , ( , , ..), , , 3 () , . 28-, ? , , ?

, (35 ), .

+1

DateTime:: diff() :

$start = new DateTime('2012-01-01 12:00:00');
$end   = new DateTime('2012-01-20 06:59:59');
$diff  = $start->diff($end);

echo $diff->format('%d days, %h hours, %m minutes, %s seconds');

. DateInterval:: format().

+1

, :

if($time_start > $time_end) list($time_start, $time_end) = array($time_end, $time_start);

$start_dt = new DateTime();
$end_dt = new DateTime();
$start_dt->setTimestamp($time_start);
$end_dt->setTimestamp($time_end);
$has_time = preg_match('`%[his]`',$format) > 0;
if(!$has_time) {
    $start_dt->setTime(0,0,0);
    $end_dt->setTime(0,0,0);
}
$interval = $end_dt->diff($start_dt);
$parts = $interval->format('%y %m %d %h %i %s %a');
$weeks = 0;
list($years, $months, $days, $hours, $mins, $secs, $total_days) = explode(' ',$parts);
if(strpos($format,'%y')===false) {
    $months += $years * 12;
}
if(strpos($format,'%m')===false) {
    $days = $total_days;
    if(strpos($format,'%y')!==false) {
        $start_dt->add(new DateInterval('P'.$years.'Y'));
        $interval = $end_dt->diff($start_dt);
        $days = $interval->days;
    }
}
if(strpos($format,'%w')!==false) {
    $weeks = (int)($days/7);
    $days %= 7;
}
if(strpos($format,'%d')===false) {
    $hours += $days * 24;
}
if(strpos($format,'%h')===false) {
    $mins += $hours * 60;
}
if(strpos($format,'%i')===false) {
    $secs += $mins * 60;
}

(FYI, str_replacing , )

: , , ... , , . , 365.... .

Edit2: ! .

+1

. 100%, .

, . , $tokens, , $format. $format, .

, $format . , , .

I tested it briefly and it works for all the examples in this thread. You can check it on codepad to see if you can break it! :)I would be interested to improve this.

<?php

function calc( $start, $end, $format = '%s', $chop = false)
{
    $tokens = array( '%y', '%m', '%w', '%d', '%h', '%i', '%s');

    if( !is_a( $start, 'DateTime') || !is_a( $end, 'DateTime'))
    {
        return;
    }
    $diff = $start->diff( $end);

    $months = ($diff->y * 12) + $diff->m;
    $secs  = ($diff->d * 24 * 3600) + 
             ($diff->h * 3600) +
             ($diff->i * 60) +
             ($diff->s);

    $output = array();
    while( $token = array_shift( $tokens))
    {
        $token_present = !(strpos( $format, $token) === false);

        switch( $token)
        {
            case '%y':
                if( ($months / 12) > 0 && $token_present)
                {
                    $output[$token] = floor( $months / 12);
                    $months -= $output[$token] * 12;
                }
            break;
            case '%m':
                if( $months > 0 && $token_present)
                {
                    $output[$token] = $months;
                    $months = 0;
                }
            break;
            case '%w':
                // Rollover between (months or years) and seconds
                if( (!isset( $output['%y']) || !isset( $output['%m'])) && $months > 0)
                {
                    $days = $diff->format( '%a');
                // Need a fix for leap year probably.
                $days -= (isset( $output['%y'])) ? ($output['%y'] * 365) : 0;
                $days -= $diff->d;
                $secs += ($days * 24 * 60 * 60);
                }

                $val = (7 * 24 * 60 * 60);
                if( ($secs / $val) > 0 && $token_present)
                {
                    $output[$token] = floor( $secs / $val);
                    $secs -= $output[$token] * $val;
                }
            break;
            case '%d':
                $val = (24 * 60 * 60);
                if( ($secs / $val) > 0 && $token_present)
                {
                    $output[$token] = floor( $secs / $val);
                    $secs -= $output[$token] * $val;
                }
            break;
            case '%h':
                $val = (60 * 60);
                if( ($secs / $val) > 0 && $token_present)
                {
                    $output[$token] = floor( $secs / $val);
                    $secs -= $output[$token] * $val;
                }
            break;
            case '%i':
                $val = (60);
                if( ($secs / $val) > 0 && $token_present)
                {
                    $output[$token] = floor( $secs / $val);
                    $secs -= $output[$token] * $val;
                }
            break;
            case '%s':
                if( $secs > 0 && $token_present)
                {
                    $output[$token] = $secs;
                }
            break;
        }
    }

    // Filter out blank keys and replace their tokens in the $format string
    $filtered = $chop ? array_filter( $output) : $output;
    $format = str_replace( array_diff( array_keys($output), array_keys($filtered)), '', $format);

    return str_replace( array_keys( $filtered), array_values( $filtered), $format);
}

$start = new DateTime('04-Feb-2010');
$end = new DateTime('28-Jun-2011');
echo calc( $start, $end, "%m months %d days\n"); // 16 months 24 days

$january = new DateTime('2010-01-01');
$february = new DateTime('2011-02-20 3:35:28');
echo calc( $january, $february, '%y years %m months %w weeks %d days %h hours %i minutes %s seconds'); // 1 years 1 months 2 weeks 5 days 3 hours 35 minutes 28 seconds
+1
source

All Articles