[doc-en] master: Document that DateInterval addition depends on how the interval was created (#5830)

From: Date: Thu, 03 Sep 2026 14:15:06 +0000
Subject: [doc-en] master: Document that DateInterval addition depends on how the interval was created (#5830)
Groups: php.doc.cvs 
Request: Send a blank email to doc-cvs+get-23713@lists.php.net to get a copy of this message
Author: Louis-Arnaud (lacatoire) Committer: GitHub (web-flow) Pusher: lacatoire Date: 2026-09-03T16:15:01+02:00 Commit: https://github.com/php/doc-en/commit/18c0977d5ec59f4207cb0b306c5473508bf640ed Raw diff: https://github.com/php/doc-en/commit/18c0977d5ec59f4207cb0b306c5473508bf640ed.diff Document that DateInterval addition depends on how the interval was created (#5830) * Unwrap examples and lists from para in the DateTime reference An example, a variablelist or a simplelist that is alone in a para should be a direct child of its container. * Document that DateInterval addition depends on how the interval was made 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. * Scope the elapsed-time wording to the time part of the interval 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. * Drop weeks from the list of date components DateInterval has no week component: new DateInterval('P1W') yields d=7, and property_exists($i, 'w') is false. Changed paths: M reference/datetime/dateinterval.xml M reference/datetime/dateinterval/createfromdatestring.xml M reference/datetime/datetimeimmutable/add.xml M reference/datetime/datetimeimmutable/sub.xml M reference/datetime/examples.xml Diff: diff --git a/reference/datetime/dateinterval.xml b/reference/datetime/dateinterval.xml index 1b6fdeed4798..e42ff185aef9 100644 --- a/reference/datetime/dateinterval.xml +++ b/reference/datetime/dateinterval.xml @@ -28,6 +28,20 @@ is by calculating the difference between two date/time objects through <methodname>DateTimeInterface::diff</methodname>. </para> + <simpara> + How the time part of an interval, that is hours, minutes, seconds and + microseconds, is applied when the interval is added to, or subtracted + from, a date/time object depends on how the interval was created. For an + interval created with <methodname>DateInterval::__construct</methodname> + the time part covers elapsed time, whereas for an interval created with + <methodname>DateInterval::createFromDateString</methodname>, or returned + by <methodname>DateTimeInterface::diff</methodname>, it increments or + decrements the individual component values. The date part, that is years, + months and days, always increments or decrements the individual component + values. The two therefore only give a different result when a + timezone transition falls within the time part of the interval; see + <link linkend="datetime.examples-arithmetic">Date/Time Arithmetic</link>. + </simpara> <para> Since there is no well defined way to compare date intervals, <classname>DateInterval</classname> instances are diff --git a/reference/datetime/dateinterval/createfromdatestring.xml b/reference/datetime/dateinterval/createfromdatestring.xml index 886cac0d345b..7598a71d0395 100644 --- a/reference/datetime/dateinterval/createfromdatestring.xml +++ b/reference/datetime/dateinterval/createfromdatestring.xml @@ -28,26 +28,24 @@ <refsect1 role="parameters"> &reftitle.parameters; - <para> - <variablelist> - <varlistentry> - <term><parameter>datetime</parameter></term> - <listitem> - <para> - A date with relative parts. Specifically, the - <link linkend="datetime.formats.relative">relative formats</link> supported - by the parser used for <classname>DateTimeImmutable</classname>, - <classname>DateTime</classname>, and <function>strtotime</function> - will be used to construct the DateInterval. - </para> - <para> - To use an ISO-8601 format string like <literal>P7D</literal>, you must - use the <methodname>DateInterval::__construct</methodname>. - </para> - </listitem> - </varlistentry> - </variablelist> - </para> + <variablelist> + <varlistentry> + <term><parameter>datetime</parameter></term> + <listitem> + <simpara> + A date with relative parts. Specifically, the + <link linkend="datetime.formats.relative">relative formats</link> supported + by the parser used for <classname>DateTimeImmutable</classname>, + <classname>DateTime</classname>, and <function>strtotime</function> + will be used to construct the DateInterval. + </simpara> + <simpara> + To use an ISO-8601 format string like <literal>P7D</literal>, you must + use the <methodname>DateInterval::__construct</methodname>. + </simpara> + </listitem> + </varlistentry> + </variablelist> </refsect1> <refsect1 role="returnvalues"> @@ -110,13 +108,12 @@ <refsect1 role="examples"> &reftitle.examples; - <para> - <example> - <title>Parsing valid date intervals</title> - <programlisting role="php" annotations="non-interactive"> + <example> + <title>Parsing valid date intervals</title> + <programlisting role="php" annotations="non-interactive"> <![CDATA[ <?php -// Each set of intervals is equal. +// Each set of intervals describes the same duration. $i = new DateInterval('P1D'); $i = DateInterval::createFromDateString('1 day'); @@ -138,13 +135,23 @@ $i = DateInterval::createFromDateString('1 day + 12 hours'); $i = new DateInterval('PT3600S'); $i = DateInterval::createFromDateString('3600 seconds'); ]]> - </programlisting> - </example> - </para> - <para> - <example> - <title>Parsing combinations and negative intervals</title> - <programlisting role="php"> + </programlisting> + </example> + <note> + <simpara> + The intervals in a set that have a time part, such as + <literal>P1DT12H</literal> and <literal>1 day + 12 hours</literal>, are + not interchangeable when they are added to a date/time object and a + timezone transition falls within that time part: for an interval created + with <methodname>DateInterval::__construct</methodname> the time part + covers elapsed time, whereas for an interval created with this method it + increments or decrements the individual component values. See + <link linkend="datetime.examples-arithmetic">Date/Time Arithmetic</link>. + </simpara> + </note> + <example> + <title>Parsing combinations and negative intervals</title> + <programlisting role="php"> <![CDATA[ <?php $i = DateInterval::createFromDateString('62 weeks + 1 day + 2 weeks + 2 hours + 70 minutes'); @@ -153,20 +160,18 @@ echo $i->format('%d %h %i'), "\n"; $i = DateInterval::createFromDateString('1 year - 10 days'); echo $i->format('%y %d'), "\n"; ]]> - </programlisting> - &example.outputs; - <screen> + </programlisting> + &example.outputs; + <screen> <![CDATA[ 449 2 70 1 -10 ]]> - </screen> - </example> - </para> - <para> - <example> - <title>Parsing special relative date intervals</title> - <programlisting role="php"> + </screen> + </example> + <example> + <title>Parsing special relative date intervals</title> + <programlisting role="php"> <![CDATA[ <?php $i = DateInterval::createFromDateString('last day of next month'); @@ -175,9 +180,9 @@ var_dump($i); $i = DateInterval::createFromDateString('last weekday'); var_dump($i); ]]> - </programlisting> - &example.outputs.82; - <screen role="php"> + </programlisting> + &example.outputs.82; + <screen role="php"> <![CDATA[ object(DateInterval)#1 (2) { ["from_string"]=> @@ -192,9 +197,9 @@ object(DateInterval)#2 (2) { string(12) "last weekday" } ]]> - </screen> - &example.outputs.8.similar; - <screen role="php"> + </screen> + &example.outputs.8.similar; + <screen role="php"> <![CDATA[ object(DateInterval)#1 (16) { ["y"]=> @@ -265,9 +270,8 @@ object(DateInterval)#2 (16) { int(1) } ]]> - </screen> - </example> - </para> + </screen> + </example> </refsect1> </refentry> diff --git a/reference/datetime/datetimeimmutable/add.xml b/reference/datetime/datetimeimmutable/add.xml index 5d2595cb0a74..7715645771b4 100644 --- a/reference/datetime/datetimeimmutable/add.xml +++ b/reference/datetime/datetimeimmutable/add.xml @@ -117,6 +117,7 @@ echo $newDate2->format('Y-m-d') . "\n"; <member><function>DateTimeImmutable::sub</function></member> <member><function>DateTimeImmutable::diff</function></member> <member><function>DateTimeImmutable::modify</function></member> + <member><link linkend="datetime.examples-arithmetic">Date/Time Arithmetic</link></member> </simplelist> </refsect1> </refentry> diff --git a/reference/datetime/datetimeimmutable/sub.xml b/reference/datetime/datetimeimmutable/sub.xml index 8b4c2f66a6f4..6ed595a4fed7 100644 --- a/reference/datetime/datetimeimmutable/sub.xml +++ b/reference/datetime/datetimeimmutable/sub.xml @@ -155,6 +155,7 @@ echo $newDate2->format('Y-m-d') . "\n"; <member><function>DateTimeImmutable::add</function></member> <member><function>DateTimeImmutable::diff</function></member> <member><function>DateTimeImmutable::modify</function></member> + <member><link linkend="datetime.examples-arithmetic">Date/Time Arithmetic</link></member> </simplelist> </refsect1> diff --git a/reference/datetime/examples.xml b/reference/datetime/examples.xml index c58489eabed7..e8139edd67c5 100644 --- a/reference/datetime/examples.xml +++ b/reference/datetime/examples.xml @@ -10,14 +10,13 @@ The following examples show some pitfalls of Date/Time arithmetic with regard to DST transitions and months having different numbers of days. </para> - <para> - <example> - <title>DateTimeImmutable::add/sub add intervals which cover elapsed time</title> - <simpara> - Adding PT24H over a DST transition will appear to add 23/25 hours (for - most timezones). - </simpara> - <programlisting role="php"> + <example> + <title>DateTimeImmutable::add/sub add intervals which cover elapsed time</title> + <simpara> + Adding PT24H over a DST transition will appear to add 23/25 hours (for + most timezones). + </simpara> + <programlisting role="php"> <![CDATA[ <?php $dt = new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York")); @@ -25,25 +24,23 @@ echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL; $dt = $dt->add(new DateInterval("PT3H")); echo "End: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL; ]]> - </programlisting> - &example.outputs; - <screen> + </programlisting> + &example.outputs; + <screen> <![CDATA[ Start: 2015-11-01 00:00:00 -04:00 End: 2015-11-01 02:00:00 -05:00 ]]> - </screen> - </example> - </para> - <para> - <example> - <title>DateTimeImmutable::modify and strtotime increment or decrement individual component values</title> - <simpara> - Adding +24 hours over a DST transition will add exactly 24 hours as seen in - the date/time string (unless the start or end time is on a transition - point). - </simpara> - <programlisting role="php"> + </screen> + </example> + <example> + <title>DateTimeImmutable::modify and strtotime increment or decrement individual component values</title> + <simpara> + Adding +24 hours over a DST transition will add exactly 24 hours as seen in + the date/time string (unless the start or end time is on a transition + point). + </simpara> + <programlisting role="php"> <![CDATA[ <?php $dt = new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York")); @@ -51,24 +48,52 @@ echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL; $dt = $dt->modify("+24 hours"); echo "End: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL; ]]> - </programlisting> - &example.outputs; - <screen> + </programlisting> + &example.outputs; + <screen> <![CDATA[ Start: 2015-11-01 00:00:00 -04:00 End: 2015-11-02 00:00:00 -05:00 ]]> - </screen> - </example> - </para> - <para> - <example> - <title>Adding or subtracting times can over- or underflow dates</title> - <simpara> - Like where January 31st + 1 month will result in March 2nd (leap year) or - 3rd (normal year). - </simpara> - <programlisting role="php"> + </screen> + </example> + <example> + <title>DateInterval::createFromDateString creates intervals which increment or decrement component values</title> + <simpara> + <methodname>DateInterval::createFromDateString</methodname> uses the + date/time parser, and the time part of the resulting interval increments + or decrements the individual component values. For an interval created + with <methodname>DateInterval::__construct</methodname> the time part + covers elapsed time instead, so the two are not interchangeable when a + timezone transition falls within that time part. The date part is applied + the same way by both, so <literal>P1D</literal> and + <literal>1 day</literal> always give the same result. + </simpara> + <programlisting role="php"> +<![CDATA[ +<?php +$dt = new DateTimeImmutable("2015-11-01 00:00:00", new DateTimeZone("America/New_York")); +echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL; +echo "PT24H: ", $dt->add(new DateInterval("PT24H"))->format("Y-m-d H:i:s P"), PHP_EOL; +echo "24 hours: ", $dt->add(DateInterval::createFromDateString("24 hours"))->format("Y-m-d H:i:s P"), PHP_EOL; +]]> + </programlisting> + &example.outputs; + <screen> +<![CDATA[ +Start: 2015-11-01 00:00:00 -04:00 +PT24H: 2015-11-01 23:00:00 -05:00 +24 hours: 2015-11-02 00:00:00 -05:00 +]]> + </screen> + </example> + <example> + <title>Adding or subtracting times can over- or underflow dates</title> + <simpara> + Like where January 31st + 1 month will result in March 2nd (leap year) or + 3rd (normal year). + </simpara> + <programlisting role="php"> <![CDATA[ <?php echo "Normal year:\n"; // February has 28 days @@ -83,9 +108,9 @@ echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL; $dt = $dt->modify("+1 month"); echo "End: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL; ]]> - </programlisting> - &example.outputs; - <screen> + </programlisting> + &example.outputs; + <screen> <![CDATA[ Normal year: Start: 2015-01-31 00:00:00 -05:00 @@ -94,12 +119,12 @@ Leap year: Start: 2016-01-31 00:00:00 -05:00 End: 2016-03-02 00:00:00 -05:00 ]]> - </screen> - <simpara> - To get the last day of the next month (i.e. to prevent the overflow), - the <literal>last day of</literal> format is available. - </simpara> - <programlisting role="php"> + </screen> + <simpara> + To get the last day of the next month (i.e. to prevent the overflow), + the <literal>last day of</literal> format is available. + </simpara> + <programlisting role="php"> <![CDATA[ <?php echo "Normal year:\n"; // February has 28 days @@ -114,9 +139,9 @@ echo "Start: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL; $dt = $dt->modify("last day of next month"); echo "End: ", $dt->format("Y-m-d H:i:s P"), PHP_EOL; ]]> - </programlisting> - &example.outputs; - <screen> + </programlisting> + &example.outputs; + <screen> <![CDATA[ Normal year: Start: 2015-01-31 00:00:00 -05:00 @@ -125,9 +150,8 @@ Leap year: Start: 2016-01-31 00:00:00 -05:00 End: 2016-02-29 00:00:00 -05:00 ]]> - </screen> - </example> - </para> + </screen> + </example> </section> </chapter>

« previous php.doc.cvs (#23713) next »