Search code examples
phpdateiso8601

PHP Date Format: ISO8601 vs ISO8601_EXPANDED


Context

I want to produce an ISO-8601 string from a DateTime object in PHP 8.2. The Date class has two related predefined constants for the date_format() function:

  • DATE_ISO8601 - "Y-m-d\\TH:i:sO" Note: This format is not compatible with ISO-8601, but is left this way for backward compatibility reasons. Use DateTimeInterface::ISO8601_EXPANDED, DateTimeInterface::ATOM for compatibility with ISO-8601 instead. (ref ISO8601:2004 section 4.3.3 clause d)

  • DATE_ISO8601_EXPANDED - "X-m-d\\TH:i:sP" This format allows for year ranges outside of ISO-8601's normal range of 0000-9999 by always including a sign character. It also addresses that that timezone part (+01:00) is compatible with ISO-8601.

Question

The DATE_ISO8601_EXPANDED format adds a leading + character to the string, which is NOT recognized by ISO8601 parsers in other languages (specifically Swift):

+2023-03-29T05:54:21+00:00

The output of DATE_ISO8601 is:

2023-03-29T05:54:21+0000

I cannot determine what the warning about DATE_ISO8601 means. Does it mean:

  1. "This format doesn't adhere to the latest revision of ISO8601, so you should use the 'expanded' variant instead."

or

  1. "There are bugs in this format that sometimes produce invalid ISO8601 output, so you should use the expanded format."

Hilariously, section 4 of ISO 8601 is not publicly available without purchase, so I can't read it. But the "expanded" format doesn't appear to be standard ISO8601 either, since the spec calls for a leading + symbol only when the year is greater than 9999.


Solution

  • The note means that the format DATE_ISO8601 is not at all compatible with ISO-8601 and you should not use it, instead use either DATE_ISO8601_EXPANDED or DATE_ATOM. The compatibility problem is not related to whether or not you use a date with a year beyond 0000-9999. You can read more about that here.

    The DATE_ISO8601_EXPANDED format differs from DATE_ATOM only in the year representation, DATE_ISO8601_EXPANDED uses 'X' instead of 'Y':

    "Y-m-d\\TH:i:sP"
    "X-m-d\\TH:i:sP"
    

    So if you don't need expanded year representation for values outside 0-9999, use DATE_ATOM.

    Expanded representation should be used only by prior agreement, this is probably why some parsers will not correctly handle the sign before the year. Although I'm also not sure if DATE_ISO8601_EXPANDED is compatible with ISO-8601, because it talks about a constant number of digits for expanded year representation.

    You can find some links to ISO-8601 without paying here: https://github.com/php/doc-en/pull/1619