Signature
date_add(DateTime $object, DateInterval $interval): DateTimePurpose
date_add() is the procedural alias of DateTime::add(). It takes a date-time object and a DateInterval, moves the date-time forward by that interval, and gives you back the same object. Use it when you work in a procedural style, for example next to date_create() and date_format(), and need to compute a due date, an expiry, or the next step in a schedule.
The key point is that the function modifies the object you pass in. It does not create a new date.
Signature
Parameters and defaults
$object: a mutableDateTimeinstance, typically created withdate_create()ornew DateTime(). It is changed in place. There is no default.$interval: aDateInterval, such asnew DateInterval('P1M10D'). There is no default. The interval can hold years, months, days, hours, minutes and seconds. An interval marked as inverted is subtracted instead of added.
Return value and failure cases
The function returns the same DateTime object that you passed in, now updated. Because it is the same instance, $result === $object is true.
Failures show up as type errors, not as a false return value:
- Passing a
DateTimeImmutablethrows aTypeError, because the parameter is typed asDateTime. UseDateTimeImmutable::add(), which returns a new object, for immutable values. - Passing a string or integer instead of a
DateIntervalalso throws aTypeError. - An invalid interval specification fails earlier, when you construct the
DateInterval, and that constructor throws an exception.
Example 1: Add a mixed interval
The interval P1M10D means one month and ten days. The time of day is kept unchanged.
date_default_timezone_set('UTC');
$date = date_create('2024-01-15 10:30:00');
$interval = new DateInterval('P1M10D');
date_add($date, $interval);
echo date_format($date, 'Y-m-d H:i:s'), "\n";2024-02-25 10:30:00
Example 2: The object is mutated and returned
The return value is not a copy. Both variables point to one object, and the 36-hour interval crosses a day boundary.
$a = new DateTime('2024-03-01 00:00:00', new DateTimeZone('UTC'));
$b = date_add($a, new DateInterval('PT36H'));
echo json_encode(['same' => $a === $b, 'value' => $a->format('Y-m-d H:i')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"same":true,"value":"2024-03-02 12:00"}
Example 3: Keep the original with clone
If you still need the starting date, clone it before calling date_add(). This example also shows month-end overflow, described below.
$start = date_create('2024-01-31', new DateTimeZone('UTC'));
$end = clone $start;
date_add($end, new DateInterval('P1M'));
echo json_encode([$start->format('Y-m-d'), $end->format('Y-m-d')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";["2024-01-31","2024-03-02"]
Edge cases
- Month-end overflow. Adding one month to January 31 first produces the non-existent February 31. PHP then rolls the extra days into March, so in 2024 the result is March 2. If you need "last day of next month", use a modifier such as
last day of next monthinstead of an interval. - Time zones and daylight saving. Intervals made of days are applied to the local calendar date, while intervals made of hours are elapsed time. Around a daylight saving change,
P1DandPT24Hmay therefore give different wall-clock results. Set the time zone explicitly when results must be reproducible. - Inverted intervals. An interval with its
invertflag set, such as one fromDateInterval::createFromDateString('-1 day'), moves the date backward when passed todate_add(). Many developers preferdate_sub()for clarity. - Weeks and days in the format. In an ISO 8601 interval string, weeks are written as
Wand days asD, for exampleP2WorP1W3D. You can combine them in one string, but the date part comes before theTthat introduces the time part.
Common mistakes
- Expecting a new object. Code such as
$next = date_add($today, $interval)leaves$todayand$nextidentical. Clone first, or useDateTimeImmutable. - Forgetting the
T. InP1HtheHis invalid, because time units must come afterT, as inPT1H. Likewise,P1Mis one month, whilePT1Mis one minute. - Passing an immutable date.
date_add()only acceptsDateTime. Use$immutable->add($interval)forDateTimeImmutable. - Assuming month arithmetic is clamped. PHP overflows into the next month rather than stopping at the last valid day.
- Depending on the default time zone. Always pass a
DateTimeZoneor set the default zone yourself so the same code gives the same answer everywhere.
Related functions
DateTime::add(): the method form of the same operation.date_sub(): subtracts an interval from aDateTime.date_create()anddate_create_immutable(): create date objects.date_interval_create_from_date_string(): builds an interval from a relative string.date_diff(): computes the interval between two dates.date_format(): formats the resulting date.
Recap
date_add() applies a DateInterval to a DateTime in place and returns that same object. Clone the date when you need to keep the original, watch for month-end overflow, and set the time zone explicitly. For immutable dates, call the add() method on the immutable object instead.
Tested examples
date_default_timezone_set('UTC');
$date = date_create('2024-01-15 10:30:00');
$interval = new DateInterval('P1M10D');
date_add($date, $interval);
echo date_format($date, 'Y-m-d H:i:s'), "\n";2024-02-25 10:30:00
$a = new DateTime('2024-03-01 00:00:00', new DateTimeZone('UTC'));
$b = date_add($a, new DateInterval('PT36H'));
echo json_encode(['same' => $a === $b, 'value' => $a->format('Y-m-d H:i')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"same":true,"value":"2024-03-02 12:00"}
$start = date_create('2024-01-31', new DateTimeZone('UTC'));
$end = clone $start;
date_add($end, new DateInterval('P1M'));
echo json_encode([$start->format('Y-m-d'), $end->format('Y-m-d')], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";["2024-01-31","2024-03-02"]