Azure / azure-storage-php

Microsoft Azure Storage Library for PHP
MIT License
217 stars 200 forks source link

Non-UTC timestamps have wrong format #293

Closed spaze closed 3 years ago

spaze commented 3 years ago

Which service(blob, file, queue, table) does this issue concern?

Table

Which version of the SDK was used?

Latest

What's the PHP/OS version?

PHP 8, PHP 7.4, Ubuntu

What problem was encountered?

Timestamps have wrong format when the machine (or PHP) is not running in UTC

Steps to reproduce the issue?

There are multiple ways to verify, the easiest is to go to Storage Explorer and run the following query: Timestamp ge datetime'2021-06-02T10:15:00.0000000+0100' The result: image

The issue is in MicrosoftAzure\Storage\Common\Internal\Utilities::convertToEdmDateTime($value) here:

https://github.com/Azure/azure-storage-php/blob/5dc75923f11042bca5b3cb6a16c82f3d104002cd/azure-storage-common/src/Common/Internal/Utilities.php#L436

The format string contains O at the very end, which means Difference to Greenwich time (GMT) without colon between hours and minutes | Example: +0200 (emphasis mine)

>>> (new DateTime)->format("Y-m-d\TH:i:s.u0O")
=> "2021-06-02T15:04:26.2064800+0200"

According to the Table Storage docs, there should be a colon in <TZDSuffix>

A literal value Z, designating that the time value is expressed as UTC time; or An offset indicating the time bias from UTC time. The offset requires a leading + for a positive offset and leading - for a negative offset. The value of the offset must fall between -23:59 and +23:59.

Running the modified query (Timestamp ge datetime'2021-06-02T10:15:00.0000000+01:00') in Storage Explorer seems to be parsed ok.

Changing the format character to P (Difference to Greenwich time (GMT) with colon between hours and minutes) fixes the issue:

>>> (new DateTime)->format("Y-m-d\TH:i:s.u0P")
=> "2021-06-02T15:04:29.3186260+02:00"

Have you found a mitigation/solution?

Yes, PR incoming (as always) Update: #294

spaze commented 3 years ago

Also note the docs mentions ISO 8601 and PHP has DATE_ISO8601 format constant but

DateTimeInterface::ISO8601 DATE_ISO8601 ISO-8601 (example: 2005-08-15T15:52:01+0000) Note: This format is not compatible with ISO-8601, but is left this way for backward compatibility reasons. Use DateTime::ATOM or DATE_ATOM for compatibility with ISO-8601 instead.

https://www.php.net/manual/en/class.datetimeinterface.php#datetime.constants.types

and the difference between DATE_ISO8601 and DATE_ATOM/DATE_RFC3339 is the O=>P change:

>>> DATE_ISO8601
=> "Y-m-d\TH:i:sO"
>>> DATE_ATOM
=> "Y-m-d\TH:i:sP"