Lesson 96 of 100
Dates15 min5 checksPHP 8.5

Add and Compare Date Intervals

Learn how DateInterval moves explicit UTC dates forward, how month overflow behaves, and how to compare the results safely.

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.

Example
<?php
$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";
Output
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.

Example
<?php
$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";
Output
{"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.

Example
<?php
$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";
Output
{"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.

  • $start and $deadline are dates in Y-m-d form, interpreted at midnight UTC.
  • $duration is an ISO 8601 duration such as P1M or P1D.
  • 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.

Before you start

Functions · Arrays · Types

You’ll practice
  • Apply fixed intervals to explicit UTC dates
  • Implement and test a deterministic PHP 8.4 function
Your task

Make compareAfterInterval() pass all 5 checks. Run your code as often as you like, then check your answer.

exercise.phpSaved in this browserPHP 8.5
CONSOLE & CHECKS● Ready

Ready when you are

Your output will appear here.
Make it work. Then make it pass.

PHP loads on your first run. Each check starts fresh. Your code stays in this browser; clearing site data removes your progress. A check or the task looks wrong? Report a problem with this exercise.