.../Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php | 10 ++++++++-- core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php | 7 ++++++- .../EntityResource/EntityTest/EntityTestDatetimeTest.php | 3 +++ .../src/Functional/BcTimestampNormalizerUnixTestTrait.php | 6 +++--- .../serialization/src/Normalizer/TimestampNormalizer.php | 4 ++++ 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php index 3fe189f..594166d 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php @@ -22,11 +22,17 @@ class DateTimeIso8601 extends StringData implements DateTimeInterface { */ public function getDateTime() { if ($this->value) { + // Ensure that all string representations generated from this value use + // the site's default timezone. Otherwise, they may end up being + // normalized in the user's preferred timezone. + // @see \Drupal\Core\Datetime\DrupalDateTime::prepareTimezone() + // @see drupal_get_user_timezone() + $default_timezone = \Drupal::config('system.date')->get('timezone.default'); if (is_array($this->value)) { - $datetime = DrupalDateTime::createFromArray($this->value); + $datetime = DrupalDateTime::createFromArray($this->value, new \DateTimeZone($default_timezone)); } else { - $datetime = new DrupalDateTime($this->value); + $datetime = new DrupalDateTime($this->value, new \DateTimeZone($default_timezone)); } return $datetime; } diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php index 60cd9b4..07b5bc5 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Timestamp.php @@ -27,7 +27,12 @@ class Timestamp extends IntegerData implements DateTimeInterface { */ public function getDateTime() { if (isset($this->value)) { - return DrupalDateTime::createFromTimestamp($this->value); + // Ensure that all string representations generated from this value use + // the 'UTC' timezone, because the timestamp data type specifically stores + // UNIX timestamps, which are always in UTC. In other words: the preferred + // timezone for string representations of this DateTime object is UTC. It + // is still possible for calling code to override this preference. + return DrupalDateTime::createFromTimestamp($this->value, 'UTC'); } } diff --git a/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php index c0937d7..66108da 100644 --- a/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php +++ b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php @@ -71,9 +71,12 @@ public function setUp() { public function testWTF() { $value = $this->entity->field_datetime->value; $date = $this->entity->field_datetime->date; + $value_typed_data_object = $this->entity->field_datetime->first()->getProperties()['value']; $this->assertEquals(static::$dateString, $value); $this->assertEquals(static::$dateString . '+00:00', $date->format('c')); + $this->assertEquals(static::$dateString . '+11:00', $value_typed_data_object->getDateTime()->format('c')); + $this->assertEquals('2017-03-01T09:02:00+00:00', $value_typed_data_object->getDateTime()->setTimezone(new \DateTimeZone('UTC'))->format('c')); } /** diff --git a/core/modules/rest/tests/src/Functional/BcTimestampNormalizerUnixTestTrait.php b/core/modules/rest/tests/src/Functional/BcTimestampNormalizerUnixTestTrait.php index 3ba5d32..506db71 100644 --- a/core/modules/rest/tests/src/Functional/BcTimestampNormalizerUnixTestTrait.php +++ b/core/modules/rest/tests/src/Functional/BcTimestampNormalizerUnixTestTrait.php @@ -29,11 +29,11 @@ protected function formatExpectedTimestampItemValues($timestamp) { // Otherwise, format the date string to the same that // \Drupal\serialization\Normalizer\TimestampItemNormalizer will produce. - // 'Australia/Sydney' is the default time zone for tests. It is hardcoded - // here for test cases where the config isn't available. $date = new \DateTime(); $date->setTimestamp($timestamp); - $date->setTimezone(new \DateTimeZone('Australia/Sydney')); + // Per \Drupal\Core\TypedData\Plugin\DataType\Timestamp::getDateTime(), they + // default to string representations in the UTC timezone. + $date->setTimezone(new \DateTimeZone('UTC')); // Format is also added to the expected return values. return [ diff --git a/core/modules/serialization/src/Normalizer/TimestampNormalizer.php b/core/modules/serialization/src/Normalizer/TimestampNormalizer.php index 123f105..eac3b97 100644 --- a/core/modules/serialization/src/Normalizer/TimestampNormalizer.php +++ b/core/modules/serialization/src/Normalizer/TimestampNormalizer.php @@ -6,6 +6,10 @@ /** * Converts values for the Timestamp data type to and from common formats. + * + * Note that \Drupal\Core\TypedData\Plugin\DataType\Timestamp::getDateTime() + * explicitly sets a default timezone of UTC. This ensures the string + * representation generated by DateTimeNormalizer::normalize() is also in UTC. */ class TimestampNormalizer extends DateTimeNormalizer {