Creating and Setting Dates

DateTime and DateTimeImmutable both implement DateTimeInterface. DateTime is mutable: modify(), add() and the setters change the object itself, while DateTimeImmutable returns a new one. Prefer the immutable class, because any function handed a DateTime can move your date.

The mutability trap, the setters and relative formats (create.php)PHP
<?php
$utc = new DateTimeZone('UTC');
$start = new DateTime('2026-09-23 09:00', $utc);
$end = $start->modify('+2 hours');                       // DateTime: moves $start too
$istart = new DateTimeImmutable('2026-09-23 09:00', $utc);
$iend = $istart->modify('+2 hours');                     // immutable: a new object
echo "{$start->format('H:i')} {$end->format('H:i')} | {$istart->format('H:i')} ",
    $iend->format('H:i'), ' | ', $istart->setDate(2028, 2, 29)->setTime(18, 45)->format('c'),
    ' | ', $istart->setISODate(2026, 53, 5)->format('D Y-m-d'), "\n";
$base = new DateTimeImmutable('2026-01-31 10:30', $utc); // a Saturday
foreach (['+1 month', 'last day of next month', 'first monday of next month',
          '+1 weekday'] as $rel) {
    printf("%-27s %s\n", $rel, $base->modify($rel)->format('D Y-m-d H:i'));
}
Output
11:00 11:00 | 09:00 11:00 | 2028-02-29T18:45:00+00:00 | Fri 2027-01-01
+1 month                    Tue 2026-03-03 10:30
last day of next month      Sat 2026-02-28 10:30
first monday of next month  Mon 2026-02-02 00:00
+1 weekday                  Mon 2026-02-02 10:30

+1 month from 31 January is 3 March, because "31 February" overflows; write last day of next month when you mean it. Formats naming a day reset the time to midnight. Friday of ISO week 53 of 2026 is 1 January 2027. createFromMutable() and createFromImmutable() convert between the classes. Without a zone argument PHP uses date.timezone (UTC when unset), so pass one. To test code that needs "now", inject a PSR-20 (https://www.php-fig.org/psr/psr-20/ 23,464 ) ClockInterface, such as symfony/clock.