fix: emit full seconds in Instant and ZonedDateTime toISOString (RFC 3339)
Instant::toISOString() and ZonedDateTime::toISOString() emitted a compact time that dropped zero seconds (e.g. 2023-11-14T22:14Z), which is valid ISO 8601 but not valid RFC 3339. Section 5.6 of RFC 3339 requires the full HH:MM:SS time even when the seconds are zero. These types are meant for interoperability, so they must always emit RFC 3339 compliant output. Build the time part of ZonedDateTime::toISOString() explicitly with a mandatory seconds component instead of delegating to LocalTime's compact formatter. Instant delegates to ZonedDateTime, so it is fixed transitively; jsonSerialize() and __toString() route through toISOString(). LocalTime's documented compact format is intentional and left unchanged. This changes the serialized string representation and is therefore a BC break; fixtures that pinned the compact output are updated accordingly.
This commit is contained in:
1 parent
104867f051
commit
6bb686d4af
6 files changed
+163
-36
No files matched your search
+21
-1
@@ -735,11 +735,31 @@ final readonly class ZonedDateTime implements JsonSerializable, Stringable
|
||||
/**
|
||||
* Returns the ISO 8601 representation of this zoned date time.
|
||||
*
|
||||
* Unlike {@see LocalTime::toISOString()}, the time part always includes the
|
||||
* seconds, so that the output is a valid RFC 3339 date-time. This type is
|
||||
* primarily meant for interoperability, and RFC 3339 section 5.6 requires
|
||||
* the full `HH:MM:SS` time even when the seconds are zero.
|
||||
*
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function toISOString(): string
|
||||
{
|
||||
$string = $this->localDateTime . $this->timeZoneOffset;
|
||||
$time = $this->localDateTime->getTime();
|
||||
|
||||
$hour = $time->getHour();
|
||||
$minute = $time->getMinute();
|
||||
$second = $time->getSecond();
|
||||
$nano = $time->getNano();
|
||||
|
||||
$string = $this->localDateTime->getDate()
|
||||
. 'T'
|
||||
. ($hour < 10 ? '0' . $hour : $hour)
|
||||
. ':'
|
||||
. ($minute < 10 ? '0' . $minute : $minute)
|
||||
. ':'
|
||||
. ($second < 10 ? '0' . $second : $second)
|
||||
. ($nano !== 0 ? '.' . rtrim(str_pad((string) $nano, 9, '0', STR_PAD_LEFT), '0') : '')
|
||||
. $this->timeZoneOffset;
|
||||
|
||||
if ($this->timeZone instanceof TimeZoneRegion) {
|
||||
$string .= '[' . $this->timeZone . ']';
|
||||
|
||||
Reference in new issue
Block a user