reverted: --- b/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php +++ a/core/lib/Drupal/Core/TypedData/Plugin/DataType/DateTimeIso8601.php @@ -23,10 +23,10 @@ public function getDateTime() { if ($this->value) { if (is_array($this->value)) { + $datetime = DrupalDateTime::createFromArray($this->value); - $datetime = DrupalDateTime::createFromArray($this->value, 'UTC'); } else { + $datetime = new DrupalDateTime($this->value); - $datetime = new DrupalDateTime($this->value, 'UTC'); } return $datetime; } diff -u b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php --- b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php +++ b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDatetimeTest.php @@ -103,8 +103,24 @@ return parent::getNormalizedPostEntity() + [ static::$fieldName => [ [ + 'value' => static::$dateString . '+00:00', + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + protected function getNormalizedPatchEntity() { + return parent::getNormalizedPostEntity() + [ + static::$fieldName => [ + [ // Omitting the timezone is allowed, this should result in the site's - // timezone being used automatically. + // timezone being used automatically. This does not make sense, but + // it's how it functioned in the past, so we explicitly test this to + // guarantee backward compatibility. ::getNormalizedPostEntity() tests + // the recommended case, this tests backward compatibility. 'value' => static::$dateString, ], ], @@ -138,7 +154,7 @@ $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); $response = $this->request($method, $url, $request_options); - $message = "Unprocessable Entity: validation failed.\n{$fieldName}.0: The datetime value '{$value}' is invalid for the format 'Y-m-d\\TH:i:s'\n"; + $message = "The specified date \"$value\" is not in an accepted format: \"Y-m-d\\TH:i:sP\" (RFC 3339), \"Y-m-d\\TH:i:sO\" (ISO 8601), \"Y-m-d\\TH:i:s\" (backward compatibility — deprecated)."; $this->assertResourceErrorResponse(422, $message, $response); // DX: 422 when date format is incorrect. @@ -150,7 +166,27 @@ $response = $this->request($method, $url, $request_options); - $message = "Unprocessable Entity: validation failed.\n{$fieldName}.0: The datetime value '{$value}' did not parse properly for the format 'Y-m-d\\TH:i:s'\n{$fieldName}.0.value: This value should be of the correct primitive type.\n"; + $message = "The specified date \"$value\" is not in an accepted format: \"Y-m-d\\TH:i:sP\" (RFC 3339), \"Y-m-d\\TH:i:sO\" (ISO 8601), \"Y-m-d\\TH:i:s\" (backward compatibility — deprecated)."; + $this->assertResourceErrorResponse(422, $message, $response); + + // DX: 422 when date value is invalid. + $normalization = $this->getNormalizedPostEntity(); + $value = '2017-13-55T20:02:00+00:00'; + $normalization[static::$fieldName][0]['value'] = $value; + + $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); + $response = $this->request($method, $url, $request_options); + $message = "The specified date \"$value\" is not in an accepted format: \"Y-m-d\\TH:i:sP\" (RFC 3339), \"Y-m-d\\TH:i:sO\" (ISO 8601), \"Y-m-d\\TH:i:s\" (backward compatibility — deprecated)."; $this->assertResourceErrorResponse(422, $message, $response); } } + /** + * {@inheritdoc} + * + * @group legacy + * @expectedDeprecation The provided datetime string format (Y-m-d\TH:i:s) is deprecated and will be removed before Drupal 9.0.0. Use the RFC3339 format instead (Y-m-d\TH:i:sP). + */ + public function testPatch() { + return parent::testPatch(); + } + } diff -u b/core/modules/datetime_range/tests/src/Functional/EntityResource/EntityTest/EntityTestDateRangeTest.php b/core/modules/datetime_range/tests/src/Functional/EntityResource/EntityTest/EntityTestDateRangeTest.php --- b/core/modules/datetime_range/tests/src/Functional/EntityResource/EntityTest/EntityTestDateRangeTest.php +++ b/core/modules/datetime_range/tests/src/Functional/EntityResource/EntityTest/EntityTestDateRangeTest.php @@ -103,8 +103,8 @@ return parent::getNormalizedPostEntity() + [ static::$fieldName => [ [ - 'value' => '2017-03-01T20:02:00', - 'end_value' => '2017-03-01T20:02:00', + 'value' => '2017-03-01T20:02:00+00:00', + 'end_value' => '2017-03-01T20:02:00+00:00', ], ], ]; @@ -147,6 +147,16 @@ $message = "Unprocessable Entity: validation failed.\n{$fieldName}.0.end_value: This value should be of the correct primitive type.\n"; $this->assertResourceErrorResponse(422, $message, $response); + // DX: 422 when end date value is invalid. + $normalization = $this->getNormalizedPostEntity(); + $value = '2017-13-55T20:02:00+00:00'; + $normalization[static::$fieldName][0]['end_value'] = $value; + + $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); + $response = $this->request($method, $url, $request_options); + $message = "The specified date \"$value\" is not in an accepted format: \"Y-m-d\\TH:i:sP\" (RFC 3339), \"Y-m-d\\TH:i:sO\" (ISO 8601), \"Y-m-d\\TH:i:s\" (backward compatibility — deprecated)."; + $this->assertResourceErrorResponse(422, $message, $response); + // @todo Expand in https://www.drupal.org/project/drupal/issues/2847041. } } diff -u b/core/modules/hal/src/Normalizer/TimestampItemNormalizer.php b/core/modules/hal/src/Normalizer/TimestampItemNormalizer.php --- b/core/modules/hal/src/Normalizer/TimestampItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/TimestampItemNormalizer.php @@ -39,7 +39,7 @@ */ protected function constructValue($data, $context) { if (!empty($data['format'])) { - $context['datetime_format'] = $data['format']; + $context['datetime_allowed_formats'] = [$data['format']]; } return ['value' => $this->serializer->denormalize($data['value'], Timestamp::class, NULL, $context)]; } diff -u b/core/modules/serialization/src/Normalizer/DateTimeIso8601Normalizer.php b/core/modules/serialization/src/Normalizer/DateTimeIso8601Normalizer.php --- b/core/modules/serialization/src/Normalizer/DateTimeIso8601Normalizer.php +++ b/core/modules/serialization/src/Normalizer/DateTimeIso8601Normalizer.php @@ -4,18 +4,12 @@ use Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601; use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem; +use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface; /** * Converts values for the DateTimeIso8601 data type to RFC3339. * * @internal - * - * Overrides DateTimeNormalizer in only one case: datetime fields that are - * configured to be "date only". - * - * @see \Drupal\datetime\Plugin\Field\FieldType\DateTimeItem::DATETIME_TYPE_DATE - * - * @todo Remove this in https://www.drupal.org/project/drupal/issues/2958416. */ class DateTimeIso8601Normalizer extends DateTimeNormalizer { @@ -23,6 +17,9 @@ * {@inheritdoc} */ protected $allowedFormats = [ + 'RFC 3339' => \DateTime::RFC3339, + 'ISO 8601' => \DateTime::ISO8601, + // @todo Remove this in https://www.drupal.org/project/drupal/issues/2958416. // RFC3339 only covers combined date and time representations. For date-only // representations, we need to use ISO 8601. There isn't a constant on the // \DateTime class that we can use, so we have to hardcode the format. @@ -41,8 +38,13 @@ */ public function normalize($datetime, $format = NULL, array $context = []) { $field_item = $datetime->getParent(); + // @todo Remove this in https://www.drupal.org/project/drupal/issues/2958416. if ($field_item instanceof DateTimeItem && $field_item->getFieldDefinition()->getFieldStorageDefinition()->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) { - return $datetime->getDateTime()->format($this->allowedFormats['date-only']); + $drupal_date_time = $datetime->getDateTime(); + if ($drupal_date_time === NULL) { + return $drupal_date_time; + } + return $drupal_date_time->format($this->allowedFormats['date-only']); } return parent::normalize($datetime, $format, $context); } @@ -51,20 +53,45 @@ * {@inheritdoc} */ public function denormalize($data, $class, $format = NULL, array $context = []) { - $date = parent::denormalize($data, $class, $format, $context); - // Extract the year, month, and day from the object. - $ymd = $date->format('Y-m-d'); - // Rebuild the date object using the extracted year, month, and day, but - // for consistency set the time to 12:00:00 UTC upon creation for date-only - // fields. Rebuilding, instead of using the object methods, is done to - // avoid the initial date object picking up the local time and time zone - // from an input value with a missing or partial time string, and then - // rolling over to a different day when changing the object to UTC. - // @see \Drupal\Component\Datetime\DateTimePlus::setDefaultDateTime() - // @see \Drupal\datetime\Plugin\views\filter\Date::getOffset() - // @see \Drupal\datetime\DateTimeComputed::getValue() - // @see http://php.net/manual/en/datetime.createfromformat.php - return \DateTime::createFromFormat('Y-m-d\TH:i:s e', $ymd . 'T12:00:00 UTC'); + // @todo Move the date-only handling out of here in https://www.drupal.org/project/drupal/issues/2958416. + $field_definition = isset($context['target_instance']) + ? $context['target_instance']->getFieldDefinition() + : (isset($context['field_definition']) ? $context['field_definition'] : NULL); + $datetime_type = $field_definition->getSetting('datetime_type'); + $is_date_only = $datetime_type === DateTimeItem::DATETIME_TYPE_DATE; + + if ($is_date_only) { + $context['datetime_allowed_formats'] = array_intersect_key($this->allowedFormats, ['date-only' => TRUE]); + $datetime = parent::denormalize($data, $class, $format, $context); + unset($context['datetime_allowed_formats']); + if (!$datetime instanceof \DateTime) { + return $datetime; + } + return $datetime->format(DateTimeItemInterface::DATE_STORAGE_FORMAT); + } + else { + $context['datetime_allowed_formats'] = array_diff_key($this->allowedFormats, ['date-only' => TRUE]); + try { + $datetime = parent::denormalize($data, $class, $format, $context); + } + catch (\UnexpectedValueException $e) { + // If denormalization didn't work using any of the actively supported + // formats, try again with the BC format too. Explicitly label it as + // being deprecated and trigger a deprecation error. + $using_deprecated_format = TRUE; + $context['datetime_allowed_formats']['backward compatibility — deprecated'] = DateTimeItemInterface::DATETIME_STORAGE_FORMAT; + $datetime = parent::denormalize($data, $class, $format, $context); + } + unset($context['datetime_allowed_formats']); + if (!$datetime instanceof \DateTime) { + return $datetime; + } + if (isset($using_deprecated_format)) { + @trigger_error('The provided datetime string format (Y-m-d\\TH:i:s) is deprecated and will be removed before Drupal 9.0.0. Use the RFC3339 format instead (Y-m-d\\TH:i:sP).', E_USER_DEPRECATED); + } + $datetime->setTimezone(new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE)); + return $datetime->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT); + } } } diff -u b/core/modules/serialization/src/Normalizer/DateTimeNormalizer.php b/core/modules/serialization/src/Normalizer/DateTimeNormalizer.php --- b/core/modules/serialization/src/Normalizer/DateTimeNormalizer.php +++ b/core/modules/serialization/src/Normalizer/DateTimeNormalizer.php @@ -10,15 +10,6 @@ * Converts values for datetime objects to RFC3339 and from common formats. * * @internal - * - * Note that this is class can become the 'serializer.normalizer.datetime' - * service in Drupal 9.0.0 and allow the 'serializer.normalizer.datetimeiso8601' - * service to be removed in Drupal 9.0.0. That is not possible today, because - * this class also works for \Drupal\Core\TypedData\Plugin\DataType\Timestamp - * objects, but those must be ignored while the 'bc_timestamp_normalizer_unix' - * BC flag is enabled. If this class were already an active service, it'd cause - * 'timestamp' fields to not have (numeric) UNIX timestamps as normalized values - * anymore, which would break BC. */ class DateTimeNormalizer extends NormalizerBase implements DenormalizerInterface { @@ -46,7 +37,11 @@ * {@inheritdoc} */ public function normalize($datetime, $format = NULL, array $context = []) { - return $datetime->getDateTime() + $drupal_date_time = $datetime->getDateTime(); + if ($drupal_date_time === NULL) { + return $drupal_date_time; + } + return $drupal_date_time // Set an explicit timezone. Otherwise, timestamps may end up being // normalized using the user's preferred timezone. Which would result in // many variations and complex caching. @@ -73,26 +68,30 @@ * {@inheritdoc} */ public function denormalize($data, $class, $format = NULL, array $context = []) { - // First check for a provided format, and if provided, create \DateTime - // object using it. - if (!empty($context['datetime_format'])) { - return \DateTime::createFromFormat($context['datetime_format'], $data); + // This only knows how to denormalize datetime strings and timestamps. If + // something else is received, let validation constraints handle this. + if (!is_string($data) && !is_numeric($data)) { + return $data; } // Loop through the allowed formats and create a \DateTime from the // input data if it matches the defined pattern. Since the formats are // unambiguous (i.e., they reference an absolute time with a defined time // zone), only one will ever match. - foreach ($this->allowedFormats as $format) { + $allowed_formats = isset($context['datetime_allowed_formats']) + ? $context['datetime_allowed_formats'] + : $this->allowedFormats; + foreach ($allowed_formats as $format) { $date = \DateTime::createFromFormat($format, $data); - if ($date !== FALSE) { + $errors = \DateTime::getLastErrors(); + if ($date !== FALSE && empty($errors['errors']) && empty($errors['warnings'])) { return $date; } } $format_strings = []; - foreach ($this->allowedFormats as $label => $format) { + foreach ($allowed_formats as $label => $format) { $format_strings[] = "\"$format\" ($label)"; } diff -u b/core/modules/serialization/src/Normalizer/TimeStampItemNormalizerTrait.php b/core/modules/serialization/src/Normalizer/TimeStampItemNormalizerTrait.php --- b/core/modules/serialization/src/Normalizer/TimeStampItemNormalizerTrait.php +++ b/core/modules/serialization/src/Normalizer/TimeStampItemNormalizerTrait.php @@ -4,12 +4,12 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException; -@trigger_error(__NAMESPACE__ . '\TimeStampItemNormalizerTrait is deprecated in Drupal 8.6.0 and will be removed in Drupal 9.0.0. Use \Drupal\serialization\Normalizer\TimestampNormalizer instead.', E_USER_DEPRECATED); +@trigger_error(__NAMESPACE__ . '\TimeStampItemNormalizerTrait is deprecated in Drupal 8.7.0 and will be removed in Drupal 9.0.0. Use \Drupal\serialization\Normalizer\TimestampNormalizer instead.', E_USER_DEPRECATED); /** * A trait for TimestampItem normalization functionality. * - * @deprecated in 8.6.0, use \Drupal\serialization\Normalizer\TimestampNormalizer instead. + * @deprecated in 8.7.0, use \Drupal\serialization\Normalizer\TimestampNormalizer instead. */ trait TimeStampItemNormalizerTrait { diff -u b/core/modules/serialization/src/Normalizer/TimestampItemNormalizer.php b/core/modules/serialization/src/Normalizer/TimestampItemNormalizer.php --- b/core/modules/serialization/src/Normalizer/TimestampItemNormalizer.php +++ b/core/modules/serialization/src/Normalizer/TimestampItemNormalizer.php @@ -39,7 +39,7 @@ */ protected function constructValue($data, $context) { if (!empty($data['format'])) { - $context['datetime_format'] = $data['format']; + $context['datetime_allowed_formats'] = [$data['format']]; } return ['value' => $this->serializer->denormalize($data['value'], Timestamp::class, NULL, $context)]; } diff -u b/core/modules/serialization/tests/src/Unit/Normalizer/DateTimeIso8601NormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/DateTimeIso8601NormalizerTest.php --- b/core/modules/serialization/tests/src/Unit/Normalizer/DateTimeIso8601NormalizerTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/DateTimeIso8601NormalizerTest.php @@ -19,10 +19,6 @@ /** * Unit test coverage for the "datetime_iso8601" @DataType. * - * Only tests the "date only" mode of DateTimeIso8601, because everything else - * is handled by \Drupal\serialization\Normalizer\DateTimeNormalizer, for which - * we have \Drupal\Tests\serialization\Unit\Normalizer\DateTimeNormalizerTest. - * * @coversDefaultClass \Drupal\serialization\Normalizer\DateTimeIso8601Normalizer * @group serialization * @see \Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601 @@ -123,6 +119,36 @@ } /** + * @covers ::normalize + * @dataProvider providerTestNormalize + */ + public function testNormalizeWhenNull($parent_field_item_class, $datetime_type, $expected_format) { + $field_item = $this->prophesize($parent_field_item_class); + if ($parent_field_item_class === DateTimeItem::class) { + $field_storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class); + $field_storage_definition->getSetting('datetime_type') + ->willReturn($datetime_type); + $field_definition = $this->prophesize(FieldDefinitionInterface::class); + $field_definition->getFieldStorageDefinition() + ->willReturn($field_storage_definition); + $field_item->getFieldDefinition() + ->willReturn($field_definition); + } + else { + $field_item->getFieldDefinition(Argument::any()) + ->shouldNotBeCalled(); + } + $this->data->getParent() + ->willReturn($field_item); + + $this->data->getDateTime() + ->willReturn(NULL); + + $normalized = $this->normalizer->normalize($this->data->reveal()); + $this->assertNull($normalized); + } + + /** * Data provider for testNormalize. * * @return array @@ -157,12 +183,13 @@ * @covers ::denormalize * @dataProvider providerTestDenormalizeValidFormats */ - public function testDenormalizeValidFormats($normalized, $expected) { - $denormalized = $this->normalizer->denormalize($normalized, DateTimeIso8601::class, NULL, []); - $this->assertInstanceOf(\DateTime::class, $denormalized); - $this->assertEquals('UTC', $denormalized->getTimezone()->getName()); - $this->assertEquals('12:00:00', $denormalized->format('H:i:s')); - $this->assertEquals($expected->format(\DateTime::RFC3339), $denormalized->format(\DateTime::RFC3339)); + public function testDenormalizeValidFormats($type, $normalized, $expected) { + $field_definition = $this->prophesize(FieldDefinitionInterface::class); + $field_definition->getSetting('datetime_type')->willReturn($type === 'date-only' ? DateTimeItem::DATETIME_TYPE_DATE : DateTimeItem::DATETIME_TYPE_DATETIME); + $denormalized = $this->normalizer->denormalize($normalized, DateTimeIso8601::class, NULL, [ + 'field_definition' => $field_definition->reveal(), + ]); + $this->assertSame($expected, $denormalized); } /** @@ -172,21 +199,62 @@ */ public function providerTestDenormalizeValidFormats() { $data = []; - $data["denormalized dates have the UTC timezone"] = ['2016-11-06', new \DateTimeImmutable('2016-11-06T12:00:00', new \DateTimeZone('UTC'))]; + $data['just a date'] = ['date-only', '2016-11-06', '2016-11-06']; + + $data['RFC3339'] = ['date+time', '2016-11-06T09:02:00+00:00', '2016-11-06T09:02:00']; + $data['RFC3339 +0100'] = ['date+time', '2016-11-06T09:02:00+01:00', '2016-11-06T08:02:00']; + $data['RFC3339 -0600'] = ['date+time', '2016-11-06T09:02:00-06:00', '2016-11-06T15:02:00']; + + $data['ISO8601'] = ['date+time', '2016-11-06T09:02:00+0000', '2016-11-06T09:02:00']; + $data['ISO8601 +0100'] = ['date+time', '2016-11-06T09:02:00+0100', '2016-11-06T08:02:00']; + $data['ISO8601 -0600'] = ['date+time', '2016-11-06T09:02:00-0600', '2016-11-06T15:02:00']; + return $data; } /** - * Tests the denormalize function with bad data. + * Tests the denormalize function with the date+time deprecated format. + * + * @covers ::denormalize + * @group legacy + * @expectedDeprecation The provided datetime string format (Y-m-d\TH:i:s) is deprecated and will be removed before Drupal 9.0.0. Use the RFC3339 format instead (Y-m-d\TH:i:sP). + */ + public function testDenormalizeDateAndTimeDeprecatedFormat() { + $normalized = '2016-11-06T08:00:00'; + + $field_definition = $this->prophesize(FieldDefinitionInterface::class); + $field_definition->getSetting('datetime_type')->willReturn(DateTimeItem::DATETIME_TYPE_DATETIME); + $this->normalizer->denormalize($normalized, DateTimeIso8601::class, NULL, ['field_definition' => $field_definition->reveal()]); + } + + /** + * Tests the denormalize function with bad data for the date-only case. * * @covers ::denormalize */ - public function testDenormalizeException() { + public function testDenormalizeDateOnlyException() { $this->setExpectedException(UnexpectedValueException::class, 'The specified date "2016/11/06" is not in an accepted format: "Y-m-d" (date-only).'); $normalized = '2016/11/06'; - $this->normalizer->denormalize($normalized, DateTimeIso8601::class, NULL, []); + $field_definition = $this->prophesize(FieldDefinitionInterface::class); + $field_definition->getSetting('datetime_type')->willReturn(DateTimeItem::DATETIME_TYPE_DATE); + $this->normalizer->denormalize($normalized, DateTimeIso8601::class, NULL, ['field_definition' => $field_definition->reveal()]); + } + + /** + * Tests the denormalize function with bad data for the date+time case. + * + * @covers ::denormalize + */ + public function testDenormalizeDateAndTimeException() { + $this->setExpectedException(UnexpectedValueException::class, 'The specified date "on a rainy day" is not in an accepted format: "Y-m-d\TH:i:sP" (RFC 3339), "Y-m-d\TH:i:sO" (ISO 8601), "Y-m-d\TH:i:s" (backward compatibility — deprecated).'); + + $normalized = 'on a rainy day'; + + $field_definition = $this->prophesize(FieldDefinitionInterface::class); + $field_definition->getSetting('datetime_type')->willReturn(DateTimeItem::DATETIME_TYPE_DATETIME); + $this->normalizer->denormalize($normalized, DateTimeIso8601::class, NULL, ['field_definition' => $field_definition->reveal()]); } } diff -u b/core/modules/serialization/tests/src/Unit/Normalizer/DateTimeNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/DateTimeNormalizerTest.php --- b/core/modules/serialization/tests/src/Unit/Normalizer/DateTimeNormalizerTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/DateTimeNormalizerTest.php @@ -95,6 +95,17 @@ } /** + * @covers ::normalize + */ + public function testNormalizeWhenNull() { + $this->data->getDateTime() + ->willReturn(NULL); + + $normalized = $this->normalizer->normalize($this->data->reveal()); + $this->assertNull($normalized); + } + + /** * Tests the denormalize function with good data. * * @covers ::denormalize @@ -132,7 +143,7 @@ * @dataProvider providerTestDenormalizeUserFormats */ public function testDenormalizeUserFormats($normalized, $format, $expected) { - $denormalized = $this->normalizer->denormalize($normalized, DateTimeInterface::class, NULL, ['datetime_format' => $format]); + $denormalized = $this->normalizer->denormalize($normalized, DateTimeInterface::class, NULL, ['datetime_allowed_formats' => [$format]]); $this->assertSame(0, $denormalized->getTimestamp() - $expected->getTimestamp()); $this->assertEquals($expected, $denormalized); } diff -u b/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php --- b/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/TimestampItemNormalizerTest.php @@ -122,7 +122,7 @@ $context = [ 'target_instance' => $timestamp_item->reveal(), - 'datetime_format' => \DateTime::RFC3339, + 'datetime_allowed_formats' => [\DateTime::RFC3339], ]; // Mock Serializer service, to assert that the Timestamp @DataType only in patch2: unchanged: --- a/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDateonlyTest.php +++ b/core/modules/datetime/tests/src/Functional/EntityResource/EntityTest/EntityTestDateonlyTest.php @@ -136,7 +136,7 @@ protected function assertNormalizationEdgeCases($method, Url $url, array $reques $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); $response = $this->request($method, $url, $request_options); - $message = "Unprocessable Entity: validation failed.\n{$fieldName}.0: The datetime value '{$value}' is invalid for the format 'Y-m-d'\n"; + $message = "The specified date \"$value\" is not in an accepted format: \"Y-m-d\" (date-only)."; $this->assertResourceErrorResponse(422, $message, $response); // DX: 422 when value is not a valid date. @@ -146,7 +146,7 @@ protected function assertNormalizationEdgeCases($method, Url $url, array $reques $request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format); $response = $this->request($method, $url, $request_options); - $message = "Unprocessable Entity: validation failed.\n{$fieldName}.0: The datetime value '{$value}' did not parse properly for the format 'Y-m-d'\n{$fieldName}.0.value: This value should be of the correct primitive type.\n"; + $message = "The specified date \"$value\" is not in an accepted format: \"Y-m-d\" (date-only)."; $this->assertResourceErrorResponse(422, $message, $response); } }