Only sites/modules doing advanced things with datetime are affected; nothing in core today is affected.
The solved problem
Datetime (@FieldType=datetime) fields are stored in UTC, but when retrieving not just the value but when interacting with their value property (which is a @DataType=datetime_iso8601 instance), you can interact with it not only through the getValue() method mandated by \Drupal\Core\TypedData\PrimitiveInterface, but also through the getDateTime() method mandated by \Drupal\Core\TypedData\Type\DateTimeInterface.
A few factors played a role in this bug having existed for many years:
datetimefields are stored in the database without a timezone offset.- The
getValue()method simply returns the data stored in the database. So you'd get the data as stored in the database: a datetime string without a timezone offset. - The
getDateTime()method needed to convert this (offsetless) datetime string into a\Drupal\Core\Datetime\DrupalDateTimeobject - When passing offsetless datetime strings to the
\Drupal\Core\Datetime\DrupalDateTimeconstructor, the default timezone (date_default_timezone_get()) is used, which is set to the site's timezone, or the currently authenticated user's preferred timezone, if any - In Drupal core, there are zero calls to
DateTimeIso8601::getDateTime(). Only tests are calling it. - Result: datetime strings that are actually in UTC are assumed to be in the current timezone and hence are interpreted incorrectly.
It's thanks to the API-First Initiative adding a normalizer to correctly and consistently normalize datetime information that we discovered this at all.
Concrete example
- Assumption
- Your site is in the
Australia/Sydneytimezone. 2018-12-13T17:00:00is stored in the database- Europe/Brussels is UTC+1, Australia/Sydney is UTC+10, so 10 hour time difference
-
var_dump($typed_data->getDateTime()->format(\DateTime::RFC3339)); var_dump($typed_data->getDateTime()->setTimezone(new \DateTimeZone('Europe/Brussels'))->format(\DateTime::RFC3339)); - Before
-
2018-12-13T17:00:00+11:00 2018-12-13T07:00:00+01:00Note how the stored datetime information is interpreted as being in Australia/Sydney.
- After
-
2018-12-13T17:00:00+00:00 2018-12-13T18:00:00+01:00Note how the stored datetime information is interpreted as being in UTC.