Skip to content

Document that DateInterval addition depends on how the interval was created - #5830

Merged
lacatoire merged 4 commits into
php:masterfrom
lacatoire:doc/dateinterval-wall-civil
Sep 3, 2026
Merged

Document that DateInterval addition depends on how the interval was created#5830
lacatoire merged 4 commits into
php:masterfrom
lacatoire:doc/dateinterval-wall-civil

Conversation

@lacatoire

@lacatoire lacatoire commented Sep 3, 2026

Copy link
Copy Markdown
Member

Fixes: #5653

DateTimeImmutable::add() applies the time part of an interval, that is hours, minutes, seconds and microseconds, in one of two ways, and it picks based on how the interval was created rather than on what the interval holds:

Created by Time part applied as
new DateInterval('PT24H') elapsed time
DateInterval::createFromDateString('24 hours') component values, like modify()
DateTimeInterface::diff() component values

The date part, that is years, months and days, always increments or decrements the individual component values, whichever way the interval was created: P1D and '1 day' never diverge. The two therefore only give a different result when a timezone transition falls within the time part, PT24H versus '24 hours' across a DST transition being the canonical case.

Nothing documented this, and the first example on the DateInterval::createFromDateString page claimed the opposite ("Each set of intervals is equal."), for sets that mostly cannot diverge at all.

The first commit unwraps the examples and lists of the two files it touches from their surrounding para; the others are the change itself. Example output verified on PHP 8.5.4.

Sources (line numbers as of php-src 3c6a57189e):

  • add()/sub() branch on civil_or_wall: ext/date/php_date.c:3432 and :3497
  • DateInterval::__construct sets PHP_DATE_WALL: ext/date/php_date.c:4646
  • DateInterval::createFromDateString sets PHP_DATE_CIVIL: ext/date/php_date.c:4890
  • DateTimeInterface::diff sets PHP_DATE_CIVIL: ext/date/php_date.c:4021
  • only h/i/s/us are added to sse in wall mode, y/m/d go through the same timelib_update_ts as in civil mode: ext/date/lib/interval.c, timelib_add_wall versus timelib_add

@lacatoire
lacatoire force-pushed the doc/dateinterval-wall-civil branch from 342a0e1 to 777ba7a Compare September 3, 2026 12:52
An example, a variablelist or a simplelist that is alone in a para
should be a direct child of its container.
DateTimeImmutable::add and DateTime::add apply an interval in one of two
ways, picked from how the interval was created rather than from what it
holds:

  new DateInterval('PT24H')                       -> elapsed time
  DateInterval::createFromDateString('24 hours')  -> component values
  DateTimeInterface::diff()                       -> component values

The two agree on any ordinary day and differ by an hour across a DST
transition, which is not documented anywhere. The example on the
DateInterval::createFromDateString page stated the opposite outright
("Each set of intervals is equal.").

Add a third example to the Date/Time Arithmetic page contrasting the two,
state the rule on the DateInterval class page, note it where the equality
was claimed, and link the arithmetic page from
DateTimeImmutable::add/sub.

Output verified on PHP 8.5.4.
@lacatoire
lacatoire force-pushed the doc/dateinterval-wall-civil branch from 777ba7a to 261bbc4 Compare September 3, 2026 12:59
Only the time part of an interval (hours, minutes, seconds,
microseconds) is applied as elapsed time when the interval comes from
DateInterval::__construct. The date part goes through the same calendar
arithmetic in both cases, so P1D and '1 day' never diverge:

  2015-11-01 00:00:00 -04:00 America/New_York
  + P1D    -> 2015-11-02 00:00:00 -05:00
  + 1 day  -> 2015-11-02 00:00:00 -05:00

Over 400 days of hourly start times, PT24H and '24 hours' diverge 46
times while P1D and '1 day' never do. Of the seven sets listed in the
first createFromDateString example, only the two carrying a time part
can diverge at all.
DateInterval has no week component: new DateInterval('P1W') yields d=7,
and property_exists($i, 'w') is false.
@lacatoire
lacatoire merged commit 18c0977 into php:master Sep 3, 2026
2 checks passed
@lacatoire
lacatoire deleted the doc/dateinterval-wall-civil branch September 3, 2026 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DateInterval: significant difference in behaviour deserves to be formally noted

1 participant