01 · THE IDEAREAD → TRY → LEARN
What a DateInterval is
A DateInterval is an instruction for getting from one date and time to another. It stores years, months, days, hours, minutes, seconds and a fraction of a second, plus an invert flag for negative periods. It is not a calendar date and it is not a fixed number of seconds. A month interval means something different in January than in February, because the interval is applied to a particular starting date.
The most direct way to create one is with an ISO 8601 duration string passed to the constructor. P1M10D means one month and ten days. A P starts the period, T separates the date part from the time part, so PT36H means thirty-six hours.
Always anchor dates explicitly
In lessons and tests, create dates with an explicit DateTimeZone('UTC'). If you rely on a default timezone, the same code can behave differently on different machines. Use DateTimeImmutable so that add() returns a new object and leaves the original untouched. That avoids a class of bugs where one variable changes while you still expect its old value.
$start = new DateTimeImmutable('2024-01-15 00:00:00', new DateTimeZone('UTC'));
$end = $start->add(new DateInterval('P1M10D'));
echo $end->format('Y-m-d'), "\n";2024-02-25
The month is added first, giving 15 February, and then ten days are added, giving 25 February.
Month overflow
Adding a month does not clamp to the end of the shorter month. PHP adds one to the month number and keeps the day number. If that date does not exist, the extra days spill into the next month. 31 January plus one month asks for 31 February. In a leap year February has 29 days, so the result is 2 March. In a common year it has 28 days, so the result is 3 March.
$utc = new DateTimeZone('UTC');
$month = new DateInterval('P1M');
$leap = (new DateTimeImmutable('2024-01-31', $utc))->add($month);
$common = (new DateTimeImmutable('2023-01-31', $utc))->add($month);
echo json_encode([
'leap' => $leap->format('Y-m-d'),
'common' => $common->format('Y-m-d'),
]), "\n";{"leap":"2024-03-02","common":"2023-03-03"}
This behaviour is predictable, but it surprises people who expect the last day of February. If your business rule is "end of month", you must write that rule separately.
Differences are intervals too
Calling diff() on two date objects also produces a DateInterval. For that object, the days property holds the total number of full days, while m and d hold the calendar-style breakdown. The invert property is 1 when the second date is earlier than the first. For intervals you build yourself, days is false.
$utc = new DateTimeZone('UTC');
$a = new DateTimeImmutable('2024-01-10', $utc);
$b = new DateTimeImmutable('2024-03-15', $utc);
$forward = $a->diff($b);
$backward = $b->diff($a);
echo json_encode([
'm' => $forward->m,
'd' => $forward->d,
'days' => $forward->days,
'backwardInvert' => $backward->invert,
]), "\n";{"m":2,"d":5,"days":65,"backwardInvert":1}
Comparing intervals
Do not compare two DateInterval objects with <, > or == and expect a meaningful answer. PHP treats them as incomparable, because there is no well-defined ordering between, for example, one month and thirty days. Instead, apply each interval to the same starting date and compare the resulting DateTimeImmutable objects. Date objects can be compared directly, and the spaceship operator <=> returns -1, 0 or 1.
Exercise contract
Write compareAfterInterval(string $start, string $duration, string $deadline): string.
$startand$deadlineare dates inY-m-dform, interpreted at midnight UTC.$durationis an ISO 8601 duration such asP1MorP1D.- Add the duration to the start date and compare the result with the deadline.
- Return
'before'if the result is earlier than the deadline,'same'if it is equal, and'after'if it is later.
The tests include a leap day, a year boundary and month overflow, so rely on PHP's date arithmetic rather than your own day counting. Return the words, not the integers from <=>.
Recap
A DateInterval describes a movement, not a fixed length of time. Anchor every calculation to an explicit UTC date, prefer immutable dates, remember that month addition can overflow, and compare resulting dates instead of comparing interval objects.
Functions · Arrays · Types
- Apply fixed intervals to explicit UTC dates
- Implement and test a deterministic PHP 8.4 function