How We Can Add Two Date Intervals in PHP

How we can add two date intervals in PHP

PHP has no operator overloading* so + with objects makes PHP trying it to convert them to string first, but DateInterval does not support that:

interval 1: 03:05
interval 2: 05:00
Total interval : 08:05

Instead you need to create a new DateTime object, then use the add function to add the intervals and finally display the difference to the reference point:

$e = new DateTime('00:00');
$f = clone $e;
$e->add($interval1);
$e->add($interval2);
echo "Total interval : ", $f->diff($e)->format("%H:%I"), "\n";

Full Example/(Demo):

$a = new DateTime('14:25');
$b = new DateTime('17:30');
$interval1 = $a->diff($b);
echo "interval 1: ", $interval1->format("%H:%I"), "\n";

$c = new DateTime('08:00');
$d = new DateTime('13:00');
$interval2 = $c->diff($d);
echo "interval 2: ", $interval2->format("%H:%I"), "\n";

$e = new DateTime('00:00');
$f = clone $e;
$e->add($interval1);
$e->add($interval2);
echo "Total interval : ", $f->diff($e)->format("%H:%I"), "\n";

You might also want to consider looking how DateInterval stores its' values and then extend from it to do the calculation your own. The following example (Demo) is rough, it does not take into account the inverted thingy, it does not (re)set $days to false and I have not checked/tested the ISO specification of the period specifier on creation but I think it is enough to show the idea:

class MyDateInterval extends DateInterval
{
/**
* @return MyDateInterval
*/
public static function fromDateInterval(DateInterval $from)
{
return new MyDateInterval($from->format('P%yY%dDT%hH%iM%sS'));
}

public function add(DateInterval $interval)
{
foreach (str_split('ymdhis') as $prop)
{
$this->$prop += $interval->$prop;
}
}
}

$a = new DateTime('14:25');
$b = new DateTime('17:30');
$interval1 = $a->diff($b);
echo "interval 1: ", $interval1->format("%H:%I"), "\n";

$c = new DateTime('08:00');
$d = new DateTime('13:00');
$interval2 = $c->diff($d);
echo "interval 2: ", $interval2->format("%H:%I"), "\n";

$e = MyDateInterval::fromDateInterval($interval1);
$e->add($interval2);
echo "Total interval: ", $e->format("%H:%I"), "\n";

* If you write a PHP extension, it actually is possible (at least sort-of).

How calculate the sum of two DateInterval object

The probably easiest way is to create a new object and cloning it, adding the two (or more) DateTimeIntervals (in your case $diff_1 and $diff_2) to the new object. By now finding the difference between the new object and its clone, is the sum of the two DateTimeIntervals you originally had.

// Define two intervals
$date1 = date_create("2013-03-15");
$date2 = date_create("2013-12-12");
$diff_1 = date_diff($date1,$date2);

$date3 = date_create("2015-02-15");
$date4 = date_create("2015-12-12");
$diff_2 = date_diff($date3,$date4);


// Create a datetime object and clone it
$dt = new DateTime();
$dt_diff = clone $result;

// Add the two intervals from before to the first one
$dt->add($diff_2);
$dt->add($diff_1);

// The result of the two intervals is now the difference between the datetimeobject and its clone
$result = $dt->diff($dt_diff);
var_dump($result);

Result of the dump includes

  ["y"]=>
int(1)
["m"]=>
int(6)
["d"]=>
int(21)

..which is 1 year, 6 months and 21 days.

Live demo

Sidenote

You don't have to concat so many different formats with your format(). You can do it all in a single line,

echo $result->format("%y years %m months %d days");

PHP adding two DateInterval-objects together

2021 Update

Almost four years wiser, I think the best way is to let PHP compute the sum and normalize the fields in the process:

function sumDateIntervals(DateInterval $a, DateInterval $b)
{
$base = new DateTimeImmutable();

return $base->add($a)->add($b)->diff($base);
}

Check it online.

The originally accepted answer

To me, your solution looks like the best way to implement this functionality.

There is no need to explicitly convert the sums to strings (PHP does it anyway). If you want to make it more readable you can use sprintf() to produce the string and shorter names for the arguments:

function sumDateIntervals(DateInterval $a, DateInterval $b)
{
return new DateInterval(sprintf('P%dY%dM%dDT%dH%dM%dS',
$a->y + $b->y,
$a->m + $b->m,
$a->d + $b->d,
$a->h + $b->h,
$a->i + $b->i,
$a->s + $b->s
));
}

Add multiple of interval to date until date is in current year

More generically, the algorithm would be to find the number of intervals between the given date and the last day of last year. Then multiplying the interval by the number of intervals + 1 to get the first interval of the current year.

$date1="12/9/2013"; 
$ts1 = strtotime($date1);
$ts2 = strtotime("12/31/" . Date("Y")-1);

//get the number of seconds between the date and first of the year
$seconds_diff = $ts2 - $ts1;
echo "$seconds_diff <br>";

//get the number of days
$dayDiff=$seconds_diff/86400;

//how many intervals?
$intervalDays = "10";

//get the number of intervals from start date to last day of last year
$numIntervals = floor($dayDiff/$intervalDays);
echo $numIntervals."<br>";

//now the total intervals to get into the current year is one more interval, turn this into days
$totIntervals= ($numIntervals* $intervalDays)+$intervalDays;

//Date Time date in question
$theDt = new DateTime($date1);

//Add the intervals we calculated to the date in question, and we have the first date of the interval for the current year...
$theDt->add(new DateInterval('P' . $totIntervals. 'D'));

echo "The first date of the intreval is: " . $theDt->format('Y-m-d');

Adding two DateTime objects in php

function addtime($time1,$time2)
{
$x = new DateTime($time1);
$y = new DateTime($time2);

$interval1 = $x->diff(new DateTime('00:00:00')) ;
$interval2 = $y->diff(new DateTime('00:00:00')) ;

$e = new DateTime('00:00');
$f = clone $e;
$e->add($interval1);
$e->add($interval2);
$total = $f->diff($e)->format("%H:%I:%S");
return $total;
}

How to get difference of two dates in php and add that difference to third date to obtain another new date?

You can achieve that as following,
you can learn more about the datetime manipulation from this How we can add two date intervals in PHP

$old_start = new DateTime("2020-12-13 14:20");
$old_end = new DateTime("2021-03-25 12:29");

$interval_diff = $old_start->diff($old_end);

$new_start = new DateTime("2020-12-27 11:47");

$new_end = $new_start->add($interval_diff);

print_r($interval_diff);
print_r($new_end);

How to calculate the difference between two dates using PHP?

Use this for legacy code (PHP < 5.3). For up to date solution see jurka's answer below

You can use strtotime() to convert two dates to unix time and then calculate the number of seconds between them. From this it's rather easy to calculate different time periods.

$date1 = "2007-03-24";
$date2 = "2009-06-26";

$diff = abs(strtotime($date2) - strtotime($date1));

$years = floor($diff / (365*60*60*24));
$months = floor(($diff - $years * 365*60*60*24) / (30*60*60*24));
$days = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));

printf("%d years, %d months, %d days\n", $years, $months, $days);

Edit: Obviously the preferred way of doing this is like described by jurka below. My code is generally only recommended if you don't have PHP 5.3 or better.

Several people in the comments have pointed out that the code above is only an approximation. I still believe that for most purposes that's fine, since the usage of a range is more to provide a sense of how much time has passed or remains rather than to provide precision - if you want to do that, just output the date.

Despite all that, I've decided to address the complaints. If you truly need an exact range but haven't got access to PHP 5.3, use the code below (it should work in PHP 4 as well). This is a direct port of the code that PHP uses internally to calculate ranges, with the exception that it doesn't take daylight savings time into account. That means that it's off by an hour at most, but except for that it should be correct.

<?php

/**
* Calculate differences between two dates with precise semantics. Based on PHPs DateTime::diff()
* implementation by Derick Rethans. Ported to PHP by Emil H, 2011-05-02. No rights reserved.
*
* See here for original code:
* http://svn.php.net/viewvc/php/php-src/trunk/ext/date/lib/tm2unixtime.c?revision=302890&view=markup
* http://svn.php.net/viewvc/php/php-src/trunk/ext/date/lib/interval.c?revision=298973&view=markup
*/

function _date_range_limit($start, $end, $adj, $a, $b, $result)
{
if ($result[$a] < $start) {
$result[$b] -= intval(($start - $result[$a] - 1) / $adj) + 1;
$result[$a] += $adj * intval(($start - $result[$a] - 1) / $adj + 1);
}

if ($result[$a] >= $end) {
$result[$b] += intval($result[$a] / $adj);
$result[$a] -= $adj * intval($result[$a] / $adj);
}

return $result;
}

function _date_range_limit_days($base, $result)
{
$days_in_month_leap = array(31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
$days_in_month = array(31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

_date_range_limit(1, 13, 12, "m", "y", &$base);

$year = $base["y"];
$month = $base["m"];

if (!$result["invert"]) {
while ($result["d"] < 0) {
$month--;
if ($month < 1) {
$month += 12;
$year--;
}

$leapyear = $year % 400 == 0 || ($year % 100 != 0 && $year % 4 == 0);
$days = $leapyear ? $days_in_month_leap[$month] : $days_in_month[$month];

$result["d"] += $days;
$result["m"]--;
}
} else {
while ($result["d"] < 0) {
$leapyear = $year % 400 == 0 || ($year % 100 != 0 && $year % 4 == 0);
$days = $leapyear ? $days_in_month_leap[$month] : $days_in_month[$month];

$result["d"] += $days;
$result["m"]--;

$month++;
if ($month > 12) {
$month -= 12;
$year++;
}
}
}

return $result;
}

function _date_normalize($base, $result)
{
$result = _date_range_limit(0, 60, 60, "s", "i", $result);
$result = _date_range_limit(0, 60, 60, "i", "h", $result);
$result = _date_range_limit(0, 24, 24, "h", "d", $result);
$result = _date_range_limit(0, 12, 12, "m", "y", $result);

$result = _date_range_limit_days(&$base, &$result);

$result = _date_range_limit(0, 12, 12, "m", "y", $result);

return $result;
}

/**
* Accepts two unix timestamps.
*/
function _date_diff($one, $two)
{
$invert = false;
if ($one > $two) {
list($one, $two) = array($two, $one);
$invert = true;
}

$key = array("y", "m", "d", "h", "i", "s");
$a = array_combine($key, array_map("intval", explode(" ", date("Y m d H i s", $one))));
$b = array_combine($key, array_map("intval", explode(" ", date("Y m d H i s", $two))));

$result = array();
$result["y"] = $b["y"] - $a["y"];
$result["m"] = $b["m"] - $a["m"];
$result["d"] = $b["d"] - $a["d"];
$result["h"] = $b["h"] - $a["h"];
$result["i"] = $b["i"] - $a["i"];
$result["s"] = $b["s"] - $a["s"];
$result["invert"] = $invert ? 1 : 0;
$result["days"] = intval(abs(($one - $two)/86400));

if ($invert) {
_date_normalize(&$a, &$result);
} else {
_date_normalize(&$b, &$result);
}

return $result;
}

$date = "1986-11-10 19:37:22";

print_r(_date_diff(strtotime($date), time()));
print_r(_date_diff(time(), strtotime($date)));

All dates between two dates with 30 minutes intervals

You could do it like this:

<?php
$interval = 1800; // Interval in seconds

$date_first = "2011-12-22 07:00";
$date_second = "2011-12-22 11:30";

$time_first = strtotime($date_first);
$time_second = strtotime($date_second);

for ($i = $time_first; $i < $time_second; $i += $interval)
echo date('Y-m-d H:i', $i) . "<br />";
?>

Periods of 20 days between two dates including the last one

This is probably more simply done by just incrementing the $start value by the $interval until it is greater than the $end value. Note that the interval needs to be 19 days to make a 20-day (inclusive of start and end) period.

$start = new DateTime('2019-01-01');
$end = new DateTime('2019-02-27');
$interval = new DateInterval('P19D');
while ($start < $end) {
echo $start->format('Y-m-d') . ' => ';
$start->add($interval);
echo min($start, $end)->format('Y-m-d') . "\n";
$start->add(new DateInterval('P1D'));
}

Output:

2019-01-01 => 2019-01-20 
2019-01-21 => 2019-02-09
2019-02-10 => 2019-02-27

Demo on 3v4l.org

Update

Here is a version of the code that also skips weekends:

$start = new DateTime('2019-01-01');
$end = new DateTime('2019-02-27');
$interval = new DateInterval('P1D');
$days = 19;
while ($start < $end) {
echo $start->format('Y-m-d') . ' => ';
for ($i = 0; $i < $days; ) {
$start->add($interval);
$day_of_week = $start->format('N');
if ($day_of_week == 6 || $day_of_week == 7) continue;
$i++;
}
echo min($start, $end)->format('Y-m-d') . "\n";
$start->add($interval);
}

Demo on 3v4l.org



Related Topics



Leave a reply



Submit