Issue #2926508 by Wim Leers, mpdonadio, joelstein, tacituseu, jhedstrom, effulgentsia, tedbow, mradcliffe, borisson_, dawehner, larowlan: Add DateTimeNormalizer+TimestampNormalizer, deprecate TimestampItemNormalizer: @DataType-level normalizers are reusable by JSON:API
parent
c1f7ffae3c
commit
c8dc57eede
|
@ -26,7 +26,7 @@ class Timestamp extends IntegerData implements DateTimeInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDateTime() {
|
||||
if ($this->value) {
|
||||
if (isset($this->value)) {
|
||||
return DrupalDateTime::createFromTimestamp($this->value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ class EntityTestDateonlyTest extends EntityTestResourceTestBase {
|
|||
|
||||
$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 @@ class EntityTestDateonlyTest extends EntityTestResourceTestBase {
|
|||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ class EntityTestDatetimeTest extends EntityTestResourceTestBase {
|
|||
return parent::getExpectedNormalizedEntity() + [
|
||||
static::$fieldName => [
|
||||
[
|
||||
'value' => $this->entity->get(static::$fieldName)->value,
|
||||
'value' => '2017-03-02T07:02:00+11:00',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
@ -103,6 +103,24 @@ class EntityTestDatetimeTest extends EntityTestResourceTestBase {
|
|||
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. 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,
|
||||
],
|
||||
],
|
||||
|
@ -136,7 +154,7 @@ class EntityTestDatetimeTest extends EntityTestResourceTestBase {
|
|||
|
||||
$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.
|
||||
|
@ -146,9 +164,29 @@ class EntityTestDatetimeTest extends EntityTestResourceTestBase {
|
|||
|
||||
$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\\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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\datetime_range\Functional\EntityResource\EntityTest;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\Tests\entity_test\Functional\Rest\EntityTestResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
|
||||
/**
|
||||
* Tests the 'daterange' field's normalization.
|
||||
*
|
||||
* @group datetime_range
|
||||
*/
|
||||
class EntityTestDateRangeTest extends EntityTestResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* The ISO date string to use throughout the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $dateString = '2017-03-01T20:02:00';
|
||||
|
||||
/**
|
||||
* Datetime Range test field name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $fieldName = 'field_daterange';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['datetime_range', 'entity_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Add datetime_range field.
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => static::$fieldName,
|
||||
'type' => 'daterange',
|
||||
'entity_type' => static::$entityTypeId,
|
||||
'settings' => ['datetime_type' => DateRangeItem::DATETIME_TYPE_ALLDAY],
|
||||
])->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_name' => static::$fieldName,
|
||||
'entity_type' => static::$entityTypeId,
|
||||
'bundle' => $this->entity->bundle(),
|
||||
])->save();
|
||||
|
||||
// Reload entity so that it has the new field.
|
||||
$this->entity = $this->entityStorage->load($this->entity->id());
|
||||
$this->entity->set(static::$fieldName, [
|
||||
'value' => static::$dateString,
|
||||
'end_value' => static::$dateString,
|
||||
]);
|
||||
$this->entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$entity_test = EntityTest::create([
|
||||
'name' => 'Llama',
|
||||
'type' => static::$entityTypeId,
|
||||
]);
|
||||
$entity_test->setOwnerId(0);
|
||||
$entity_test->save();
|
||||
|
||||
return $entity_test;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
return parent::getExpectedNormalizedEntity() + [
|
||||
static::$fieldName => [
|
||||
[
|
||||
'value' => '2017-03-02T07:02:00+11:00',
|
||||
'end_value' => '2017-03-02T07:02:00+11:00',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
return parent::getNormalizedPostEntity() + [
|
||||
static::$fieldName => [
|
||||
[
|
||||
'value' => '2017-03-01T20:02:00+00:00',
|
||||
'end_value' => '2017-03-01T20:02:00+00:00',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertNormalizationEdgeCases($method, Url $url, array $request_options) {
|
||||
parent::assertNormalizationEdgeCases($method, $url, $request_options);
|
||||
|
||||
if ($this->entity->getEntityType()->hasKey('bundle')) {
|
||||
$fieldName = static::$fieldName;
|
||||
|
||||
// DX: 422 when 'value' data type is incorrect.
|
||||
$normalization = $this->getNormalizedPostEntity();
|
||||
$normalization[static::$fieldName][0]['value'] = [
|
||||
'2017', '03', '01', '21', '53', '00',
|
||||
];
|
||||
$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.value: This value should be of the correct primitive type.\n";
|
||||
$this->assertResourceErrorResponse(422, $message, $response);
|
||||
|
||||
// DX: 422 when 'end_value' is not specified.
|
||||
$normalization = $this->getNormalizedPostEntity();
|
||||
unset($normalization[static::$fieldName][0]['end_value']);
|
||||
$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.end_value: This value should not be null.\n";
|
||||
$this->assertResourceErrorResponse(422, $message, $response);
|
||||
|
||||
// DX: 422 when 'end_value' data type is incorrect.
|
||||
$normalization = $this->getNormalizedPostEntity();
|
||||
$normalization[static::$fieldName][0]['end_value'] = [
|
||||
'2017', '03', '01', '21', '53', '00',
|
||||
];
|
||||
$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.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.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -4,15 +4,18 @@ namespace Drupal\hal\Normalizer;
|
|||
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem;
|
||||
use Drupal\serialization\Normalizer\TimeStampItemNormalizerTrait;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Timestamp;
|
||||
|
||||
/**
|
||||
* Converts values for TimestampItem to and from common formats for hal.
|
||||
*
|
||||
* Overrides FieldItemNormalizer to
|
||||
* - during normalization, add the 'format' key to assist consumers
|
||||
* - during denormalization, use
|
||||
* \Drupal\serialization\Normalizer\TimestampNormalizer
|
||||
*/
|
||||
class TimestampItemNormalizer extends FieldItemNormalizer {
|
||||
|
||||
use TimeStampItemNormalizerTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -22,8 +25,21 @@ class TimestampItemNormalizer extends FieldItemNormalizer {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function normalizedFieldValues(FieldItemInterface $field_item, $format, array $context) {
|
||||
$normalized = parent::normalizedFieldValues($field_item, $format, $context);
|
||||
return $this->processNormalizedValues($normalized);
|
||||
return parent::normalizedFieldValues($field_item, $format, $context) + [
|
||||
// 'format' is not a property on Timestamp objects. This is present to
|
||||
// assist consumers of this data.
|
||||
'format' => \DateTime::RFC3339,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function constructValue($data, $context) {
|
||||
if (!empty($data['format'])) {
|
||||
$context['datetime_allowed_formats'] = [$data['format']];
|
||||
}
|
||||
return ['value' => $this->serializer->denormalize($data['value'], Timestamp::class, NULL, $context)];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ trait BcTimestampNormalizerUnixTestTrait {
|
|||
// \Drupal\serialization\Normalizer\TimestampItemNormalizer will produce.
|
||||
$date = new \DateTime();
|
||||
$date->setTimestamp($timestamp);
|
||||
// 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.
|
||||
|
|
|
@ -58,6 +58,18 @@ services:
|
|||
# Priority must be higher than serializer.normalizer.field_item and lower
|
||||
# than hal normalizers.
|
||||
- { name: normalizer, priority: 8, bc: bc_timestamp_normalizer_unix, bc_config_name: 'serialization.settings' }
|
||||
serializer.normalizer.timestamp:
|
||||
class: Drupal\serialization\Normalizer\TimestampNormalizer
|
||||
arguments: ['@config.factory']
|
||||
tags:
|
||||
# Priority must be higher than serializer.normalizer.primitive_data.
|
||||
- { name: normalizer, priority: 20, bc: bc_timestamp_normalizer_unix, bc_config_name: 'serialization.settings' }
|
||||
serializer.normalizer.datetimeiso8601:
|
||||
class: \Drupal\serialization\Normalizer\DateTimeIso8601Normalizer
|
||||
arguments: ['@config.factory']
|
||||
tags:
|
||||
# Priority must be higher than serializer.normalizer.primitive_data.
|
||||
- { name: normalizer, priority: 20 }
|
||||
serializer.normalizer.password_field_item:
|
||||
class: Drupal\serialization\Normalizer\NullNormalizer
|
||||
arguments: ['Drupal\Core\Field\Plugin\Field\FieldType\PasswordItem']
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\serialization\Normalizer;
|
||||
|
||||
use Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Converts values for the DateTimeIso8601 data type to RFC3339.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DateTimeIso8601Normalizer extends DateTimeNormalizer {
|
||||
|
||||
/**
|
||||
* {@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.
|
||||
// @see https://en.wikipedia.org/wiki/ISO_8601#Calendar_dates
|
||||
// @see \Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface::DATE_STORAGE_FORMAT
|
||||
'date-only' => 'Y-m-d',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $supportedInterfaceOrClass = DateTimeIso8601::class;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($datetime, $format = NULL, array $context = []) {
|
||||
assert($datetime instanceof DateTimeIso8601);
|
||||
$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) {
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function denormalize($data, $class, $format = NULL, array $context = []) {
|
||||
// @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);
|
||||
if ($field_definition === NULL) {
|
||||
throw new InvalidArgumentException('$context[\'target_instance\'] or $context[\'field_definition\'] must be set to denormalize with the DateTimeIso8601Normalizer');
|
||||
}
|
||||
|
||||
$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);
|
||||
if (!$datetime instanceof \DateTime) {
|
||||
return $datetime;
|
||||
}
|
||||
return $datetime->format(DateTimeItemInterface::DATE_STORAGE_FORMAT);
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\serialization\Normalizer;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\TypedData\Type\DateTimeInterface;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
|
||||
/**
|
||||
* Converts values for datetime objects to RFC3339 and from common formats.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DateTimeNormalizer extends NormalizerBase implements DenormalizerInterface {
|
||||
|
||||
/**
|
||||
* Allowed datetime formats for the denormalizer.
|
||||
*
|
||||
* The list is chosen to be unambiguous and language neutral, but also common
|
||||
* for data interchange.
|
||||
*
|
||||
* @var string[]
|
||||
*
|
||||
* @see http://php.net/manual/en/datetime.createfromformat.php
|
||||
*/
|
||||
protected $allowedFormats = [
|
||||
'RFC 3339' => \DateTime::RFC3339,
|
||||
'ISO 8601' => \DateTime::ISO8601,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $supportedInterfaceOrClass = DateTimeInterface::class;
|
||||
|
||||
/**
|
||||
* The system's date configuration.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ImmutableConfig
|
||||
*/
|
||||
protected $systemDateConfig;
|
||||
|
||||
/**
|
||||
* Constructs a new DateTimeNormalizer instance.
|
||||
*
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* A config factory for retrieving required config objects.
|
||||
*/
|
||||
public function __construct(ConfigFactoryInterface $config_factory) {
|
||||
$this->systemDateConfig = $config_factory->get('system.date');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($datetime, $format = NULL, array $context = []) {
|
||||
assert($datetime instanceof DateTimeInterface);
|
||||
$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.
|
||||
// @see \Drupal\Core\Datetime\DrupalDateTime::prepareTimezone()
|
||||
// @see drupal_get_user_timezone()
|
||||
->setTimezone($this->getNormalizationTimezone())
|
||||
->format(\DateTime::RFC3339);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timezone to be used during normalization.
|
||||
*
|
||||
* @see ::normalize
|
||||
*
|
||||
* @returns \DateTimeZone
|
||||
* The timezone to use.
|
||||
*/
|
||||
protected function getNormalizationTimezone() {
|
||||
$default_site_timezone = $this->systemDateConfig->get('timezone.default');
|
||||
return new \DateTimeZone($default_site_timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function denormalize($data, $class, $format = NULL, array $context = []) {
|
||||
// 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.
|
||||
$allowed_formats = isset($context['datetime_allowed_formats'])
|
||||
? $context['datetime_allowed_formats']
|
||||
: $this->allowedFormats;
|
||||
foreach ($allowed_formats as $format) {
|
||||
$date = \DateTime::createFromFormat($format, $data);
|
||||
$errors = \DateTime::getLastErrors();
|
||||
if ($date !== FALSE && empty($errors['errors']) && empty($errors['warnings'])) {
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
|
||||
$format_strings = [];
|
||||
|
||||
foreach ($allowed_formats as $label => $format) {
|
||||
$format_strings[] = "\"$format\" ($label)";
|
||||
}
|
||||
|
||||
$formats = implode(', ', $format_strings);
|
||||
throw new UnexpectedValueException(sprintf('The specified date "%s" is not in an accepted format: %s.', $data, $formats));
|
||||
}
|
||||
|
||||
}
|
|
@ -4,8 +4,12 @@ namespace Drupal\serialization\Normalizer;
|
|||
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
@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.7.0, use \Drupal\serialization\Normalizer\TimestampNormalizer instead.
|
||||
*/
|
||||
trait TimeStampItemNormalizerTrait {
|
||||
|
||||
|
|
|
@ -3,15 +3,19 @@
|
|||
namespace Drupal\serialization\Normalizer;
|
||||
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Timestamp;
|
||||
|
||||
/**
|
||||
* Converts values for TimestampItem to and from common formats.
|
||||
*
|
||||
* Overrides FieldItemNormalizer to use \Drupal\serialization\Normalizer\TimestampNormalizer
|
||||
*
|
||||
* Overrides FieldItemNormalizer to
|
||||
* - during normalization, add the 'format' key to assist consumers
|
||||
* - during denormalization, use \Drupal\serialization\Normalizer\TimestampNormalizer
|
||||
*/
|
||||
class TimestampItemNormalizer extends FieldItemNormalizer {
|
||||
|
||||
use TimeStampItemNormalizerTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -20,21 +24,22 @@ class TimestampItemNormalizer extends FieldItemNormalizer {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($field_item, $format = NULL, array $context = []) {
|
||||
$data = parent::normalize($field_item, $format, $context);
|
||||
|
||||
return $this->processNormalizedValues($data);
|
||||
public function normalize($object, $format = NULL, array $context = []) {
|
||||
return parent::normalize($object, $format, $context) + [
|
||||
// 'format' is not a property on Timestamp objects. This is present to
|
||||
// assist consumers of this data.
|
||||
'format' => \DateTime::RFC3339,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function denormalize($data, $class, $format = NULL, array $context = []) {
|
||||
if (empty($data['value'])) {
|
||||
throw new InvalidArgumentException('No "value" attribute present');
|
||||
protected function constructValue($data, $context) {
|
||||
if (!empty($data['format'])) {
|
||||
$context['datetime_allowed_formats'] = [$data['format']];
|
||||
}
|
||||
|
||||
return parent::denormalize($data, $class, $format, $context);
|
||||
return ['value' => $this->serializer->denormalize($data['value'], Timestamp::class, NULL, $context)];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\serialization\Normalizer;
|
||||
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Timestamp;
|
||||
|
||||
/**
|
||||
* Converts values for the Timestamp data type to and from common formats.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* 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 {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $allowedFormats = [
|
||||
'UNIX timestamp' => 'U',
|
||||
'ISO 8601' => \DateTime::ISO8601,
|
||||
'RFC 3339' => \DateTime::RFC3339,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $supportedInterfaceOrClass = Timestamp::class;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizationTimezone() {
|
||||
return new \DateTimeZone('UTC');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function denormalize($data, $class, $format = NULL, array $context = []) {
|
||||
$denormalized = parent::denormalize($data, $class, $format, $context);
|
||||
return $denormalized->getTimestamp();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\ImmutableConfig;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemBase;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
|
||||
use Drupal\Core\TypedData\Type\DateTimeInterface;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\serialization\Normalizer\DateTimeIso8601Normalizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Unit test coverage for the "datetime_iso8601" @DataType.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\DateTimeIso8601Normalizer
|
||||
* @group serialization
|
||||
* @see \Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601
|
||||
* @see \Drupal\datetime\Plugin\Field\FieldType\DateTimeItem::DATETIME_TYPE_DATE
|
||||
*/
|
||||
class DateTimeIso8601NormalizerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested data type's normalizer.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\DateTimeIso8601Normalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The tested data type.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$system_date_config = $this->prophesize(ImmutableConfig::class);
|
||||
$system_date_config->get('timezone.default')
|
||||
->willReturn('Australia/Sydney');
|
||||
$config_factory = $this->prophesize(ConfigFactoryInterface::class);
|
||||
$config_factory->get('system.date')
|
||||
->willReturn($system_date_config->reveal());
|
||||
|
||||
$this->normalizer = new DateTimeIso8601Normalizer($config_factory->reveal());
|
||||
$this->data = $this->prophesize(DateTimeIso8601::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization() {
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($this->data->reveal()));
|
||||
|
||||
$datetime = $this->prophesize(DateTimeInterface::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($datetime->reveal()));
|
||||
|
||||
$integer = $this->prophesize(IntegerData::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($integer->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsDenormalization
|
||||
*/
|
||||
public function testSupportsDenormalization() {
|
||||
$this->assertTrue($this->normalizer->supportsDenormalization($this->data->reveal(), DateTimeIso8601::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
* @dataProvider providerTestNormalize
|
||||
*/
|
||||
public function testNormalize($parent_field_item_class, $datetime_type, $expected_format) {
|
||||
$formatted_string = $this->randomMachineName();
|
||||
|
||||
$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);
|
||||
|
||||
$drupal_date_time = $this->prophesize(DateTimeIso8601NormalizerTestDrupalDateTime::class);
|
||||
$drupal_date_time->setTimezone(new \DateTimeZone('Australia/Sydney'))
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
$drupal_date_time->format($expected_format)
|
||||
->willReturn($formatted_string);
|
||||
$this->data->getDateTime()
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->data->reveal());
|
||||
$this->assertSame($formatted_string, $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
public function providerTestNormalize() {
|
||||
return [
|
||||
// @see \Drupal\datetime\Plugin\Field\FieldType\DateTimeItem::DATETIME_TYPE_DATE
|
||||
'datetime field, configured to store only date: must be handled by DateTimeIso8601Normalizer' => [
|
||||
DateTimeItem::class,
|
||||
DateTimeItem::DATETIME_TYPE_DATE,
|
||||
// This expected format call proves that normalization is handled by \Drupal\serialization\Normalizer\DateTimeIso8601Normalizer::normalize().
|
||||
'Y-m-d',
|
||||
],
|
||||
// @see \Drupal\datetime\Plugin\Field\FieldType\DateTimeItem::DATETIME_TYPE_DATETIME
|
||||
'datetime field, configured to store date and time; must be handled by the parent normalizer' => [
|
||||
DateTimeItem::class,
|
||||
DateTimeItem::DATETIME_TYPE_DATETIME,
|
||||
\DateTime::RFC3339,
|
||||
],
|
||||
'non-datetime field; must be handled by the parent normalizer' => [
|
||||
FieldItemBase::class,
|
||||
NULL,
|
||||
\DateTime::RFC3339,
|
||||
],
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with good data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
* @dataProvider providerTestDenormalizeValidFormats
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testDenormalizeValidFormats.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestDenormalizeValidFormats() {
|
||||
$data = [];
|
||||
$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 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 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';
|
||||
|
||||
$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()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with incomplete serialization context.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeNoTargetInstanceOrFieldDefinitionException() {
|
||||
$this->setExpectedException(InvalidArgumentException::class, '$context[\'target_instance\'] or $context[\'field_definition\'] must be set to denormalize with the DateTimeIso8601Normalizer');
|
||||
$this->normalizer->denormalize('', DateTimeIso8601::class, NULL, []);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: Prophecy does not support magic methods. By subclassing and specifying
|
||||
* an explicit method, Prophecy works.
|
||||
* @see https://github.com/phpspec/prophecy/issues/338
|
||||
* @see https://github.com/phpspec/prophecy/issues/34
|
||||
* @see https://github.com/phpspec/prophecy/issues/80
|
||||
*/
|
||||
class DateTimeIso8601NormalizerTestDrupalDateTime extends DrupalDateTime {
|
||||
|
||||
public function setTimezone(\DateTimeZone $timezone) {
|
||||
parent::setTimezone($timezone);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\ImmutableConfig;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
|
||||
use Drupal\Core\TypedData\Type\DateTimeInterface;
|
||||
use Drupal\serialization\Normalizer\DateTimeNormalizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Unit test coverage for @DataTypes implementing DateTimeInterface.
|
||||
*
|
||||
* @group serialization
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\DateTimeNormalizer
|
||||
* @see \Drupal\Core\TypedData\Type\DateTimeInterface
|
||||
*/
|
||||
class DateTimeNormalizerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested data type's normalizer.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\DateTimeNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The tested data type.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\Type\DateTimeInterface
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$system_date_config = $this->prophesize(ImmutableConfig::class);
|
||||
$system_date_config->get('timezone.default')
|
||||
->willReturn('Australia/Sydney');
|
||||
$config_factory = $this->prophesize(ConfigFactoryInterface::class);
|
||||
$config_factory->get('system.date')
|
||||
->willReturn($system_date_config->reveal());
|
||||
|
||||
$this->normalizer = new DateTimeNormalizer($config_factory->reveal());
|
||||
$this->data = $this->prophesize(DateTimeInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization() {
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($this->data->reveal()));
|
||||
|
||||
$datetimeiso8601 = $this->prophesize(DateTimeIso8601::class);
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($datetimeiso8601->reveal()));
|
||||
|
||||
$integer = $this->prophesize(IntegerData::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($integer->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsDenormalization
|
||||
*/
|
||||
public function testSupportsDenormalization() {
|
||||
$this->assertTrue($this->normalizer->supportsDenormalization($this->data->reveal(), DateTimeInterface::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalize() {
|
||||
$random_rfc_3339_string = $this->randomMachineName();
|
||||
|
||||
$drupal_date_time = $this->prophesize(DateTimeNormalizerTestDrupalDateTime::class);
|
||||
$drupal_date_time->setTimezone(new \DateTimeZone('Australia/Sydney'))
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
$drupal_date_time->format(\DateTime::RFC3339)
|
||||
->willReturn($random_rfc_3339_string);
|
||||
|
||||
$this->data->getDateTime()
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->data->reveal());
|
||||
$this->assertSame($random_rfc_3339_string, $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @dataProvider providerTestDenormalizeValidFormats
|
||||
*/
|
||||
public function testDenormalizeValidFormats($normalized, $expected) {
|
||||
$denormalized = $this->normalizer->denormalize($normalized, DateTimeInterface::class, NULL, []);
|
||||
$this->assertSame(0, $denormalized->getTimestamp() - $expected->getTimestamp());
|
||||
$this->assertEquals($expected, $denormalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testDenormalizeValidFormats.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestDenormalizeValidFormats() {
|
||||
$data = [];
|
||||
|
||||
$data['RFC3339'] = ['2016-11-06T09:02:00+00:00', new \DateTimeImmutable('2016-11-06T09:02:00+00:00')];
|
||||
$data['RFC3339 +0100'] = ['2016-11-06T09:02:00+01:00', new \DateTimeImmutable('2016-11-06T09:02:00+01:00')];
|
||||
$data['RFC3339 -0600'] = ['2016-11-06T09:02:00-06:00', new \DateTimeImmutable('2016-11-06T09:02:00-06:00')];
|
||||
|
||||
$data['ISO8601'] = ['2016-11-06T09:02:00+0000', new \DateTimeImmutable('2016-11-06T09:02:00+00:00')];
|
||||
$data['ISO8601 +0100'] = ['2016-11-06T09:02:00+0100', new \DateTimeImmutable('2016-11-06T09:02:00+01:00')];
|
||||
$data['ISO8601 -0600'] = ['2016-11-06T09:02:00-0600', new \DateTimeImmutable('2016-11-06T09:02:00-06:00')];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with a user supplied format.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
* @dataProvider providerTestDenormalizeUserFormats
|
||||
*/
|
||||
public function testDenormalizeUserFormats($normalized, $format, $expected) {
|
||||
$denormalized = $this->normalizer->denormalize($normalized, DateTimeInterface::class, NULL, ['datetime_allowed_formats' => [$format]]);
|
||||
$this->assertSame(0, $denormalized->getTimestamp() - $expected->getTimestamp());
|
||||
$this->assertEquals($expected, $denormalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testDenormalizeUserFormats.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestDenormalizeUserFormats() {
|
||||
$data = [];
|
||||
|
||||
$data['Y/m/d H:i:s P'] = ['2016/11/06 09:02:00 +00:00', 'Y/m/d H:i:s P', new \DateTimeImmutable('2016-11-06T09:02:00+00:00')];
|
||||
$data['H:i:s Y/m/d P'] = ['09:02:00 2016/11/06 +01:00', 'H:i:s Y/m/d P', new \DateTimeImmutable('2016-11-06T09:02:00+01:00')];
|
||||
$data['Y/m/d H:i:s'] = ['09:02:00 2016/11/06', 'H:i:s Y/m/d', new \DateTimeImmutable('2016-11-06T09:02:00+11:00')];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with bad data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeException() {
|
||||
$this->setExpectedException(UnexpectedValueException::class, 'The specified date "2016/11/06 09:02am GMT" is not in an accepted format: "Y-m-d\TH:i:sP" (RFC 3339), "Y-m-d\TH:i:sO" (ISO 8601).');
|
||||
|
||||
$normalized = '2016/11/06 09:02am GMT';
|
||||
|
||||
$this->normalizer->denormalize($normalized, DateTimeInterface::class, NULL, []);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Note: Prophecy does not support magic methods. By subclassing and specifying
|
||||
* an explicit method, Prophecy works.
|
||||
* @see https://github.com/phpspec/prophecy/issues/338
|
||||
* @see https://github.com/phpspec/prophecy/issues/34
|
||||
* @see https://github.com/phpspec/prophecy/issues/80
|
||||
*/
|
||||
class DateTimeNormalizerTestDrupalDateTime extends DrupalDateTime {
|
||||
|
||||
public function setTimezone(\DateTimeZone $timezone) {
|
||||
parent::setTimezone($timezone);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests that TimeStampItemNormalizerTrait throws a deprecation error.
|
||||
*
|
||||
* @group serialization
|
||||
* @group legacy
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\TimeStampItemNormalizerTrait
|
||||
*/
|
||||
class TimeStampItemNormalizerTraitDeprecatedTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests that TimeStampItemNormalizerTrait throws a deprecation error.
|
||||
*
|
||||
* @expectedDeprecation Drupal\serialization\Normalizer\TimeStampItemNormalizerTrait is deprecated in Drupal 8.7.0 and will be removed in Drupal 9.0.0. Use \Drupal\serialization\Normalizer\TimestampNormalizer instead.
|
||||
*/
|
||||
public function testDeprecated() {
|
||||
$test = new TimeStampItemNormalizerTraitDeprecatedTestClass();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\serialization\Normalizer\TimeStampItemNormalizerTrait;
|
||||
|
||||
/**
|
||||
* For testing that TimeStampItemNormalizerTrait throws a deprecation error.
|
||||
*
|
||||
* @see \Drupal\Tests\serialization\Unit\Normalizer\TimeStampItemNormalizerTraitDeprecatedTest
|
||||
*/
|
||||
class TimeStampItemNormalizerTraitDeprecatedTestClass {
|
||||
use TimeStampItemNormalizerTrait;
|
||||
|
||||
}
|
|
@ -5,21 +5,21 @@ namespace Drupal\Tests\serialization\Unit\Normalizer;
|
|||
use Drupal\Core\Field\Plugin\Field\FieldType\CreatedItem;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem;
|
||||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Timestamp;
|
||||
use Drupal\serialization\Normalizer\TimestampItemNormalizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* Tests that entities can be serialized to supported core formats.
|
||||
* Tests that TimestampItem (de)normalization uses Timestamp (de)normalization.
|
||||
*
|
||||
* @group serialization
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\TimestampItemNormalizer
|
||||
* @see \Drupal\serialization\Normalizer\TimestampNormalizer
|
||||
*/
|
||||
class TimestampItemNormalizerTest extends UnitTestCase {
|
||||
|
||||
use InternalTypedDataTestTrait;
|
||||
|
||||
/**
|
||||
* @var \Drupal\serialization\Normalizer\TimestampItemNormalizer
|
||||
*/
|
||||
|
@ -68,90 +68,78 @@ class TimestampItemNormalizerTest extends UnitTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Tests the normalize function.
|
||||
*
|
||||
* @covers ::normalize
|
||||
* @see \Drupal\Tests\serialization\Unit\Normalizer\TimestampNormalizerTest
|
||||
*/
|
||||
public function testNormalize() {
|
||||
$expected = ['value' => '2016-11-06T09:02:00+00:00', 'format' => \DateTime::RFC3339];
|
||||
|
||||
// Mock TimestampItem @FieldType, which contains a Timestamp @DataType,
|
||||
// which has a DataDefinition.
|
||||
$data_definition = $this->prophesize(DataDefinitionInterface::class);
|
||||
$data_definition->isInternal()
|
||||
->willReturn(FALSE)
|
||||
->shouldBeCalled();
|
||||
$timestamp = $this->prophesize(Timestamp::class);
|
||||
$timestamp->getDataDefinition()
|
||||
->willReturn($data_definition->reveal())
|
||||
->shouldBeCalled();
|
||||
$timestamp = $timestamp->reveal();
|
||||
$timestamp_item = $this->createTimestampItemProphecy();
|
||||
$timestamp_item->getIterator()
|
||||
->willReturn(new \ArrayIterator(['value' => 1478422920]));
|
||||
|
||||
$value_property = $this->getTypedDataProperty(FALSE);
|
||||
$timestamp_item->getProperties(TRUE)
|
||||
->willReturn(['value' => $value_property])
|
||||
->willReturn(['value' => $timestamp])
|
||||
->shouldBeCalled();
|
||||
|
||||
// Mock Serializer service, to assert that the Timestamp @DataType
|
||||
// normalizer would be called.
|
||||
$timestamp_datetype_normalization = $this->randomMachineName();
|
||||
$serializer_prophecy = $this->prophesize(Serializer::class);
|
||||
|
||||
$serializer_prophecy->normalize($value_property, NULL, [])
|
||||
->willReturn(1478422920)
|
||||
// This is where \Drupal\serialization\Normalizer\TimestampNormalizer would
|
||||
// be called.
|
||||
$serializer_prophecy->normalize($timestamp, NULL, [])
|
||||
->willReturn($timestamp_datetype_normalization)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->normalizer->setSerializer($serializer_prophecy->reveal());
|
||||
|
||||
$normalized = $this->normalizer->normalize($timestamp_item->reveal());
|
||||
$this->assertSame($expected, $normalized);
|
||||
$this->assertSame(['value' => $timestamp_datetype_normalization, 'format' => \DateTime::RFC3339], $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with good data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
* @dataProvider providerTestDenormalizeValidFormats
|
||||
*/
|
||||
public function testDenormalizeValidFormats($value, $expected) {
|
||||
$normalized = ['value' => $value];
|
||||
public function testDenormalize() {
|
||||
$timestamp_item_normalization = [
|
||||
'value' => $this->randomMachineName(),
|
||||
'format' => \DateTime::RFC3339,
|
||||
];
|
||||
$timestamp_data_denormalization = $this->randomMachineName();
|
||||
|
||||
$timestamp_item = $this->createTimestampItemProphecy();
|
||||
// The field item should be set with the expected timestamp.
|
||||
$timestamp_item->setValue(['value' => $expected])
|
||||
// The field item should get the Timestamp @DataType denormalization set as
|
||||
// a value, in FieldItemNormalizer::denormalize().
|
||||
$timestamp_item->setValue(['value' => $timestamp_data_denormalization])
|
||||
->shouldBeCalled();
|
||||
|
||||
$context = ['target_instance' => $timestamp_item->reveal()];
|
||||
$context = [
|
||||
'target_instance' => $timestamp_item->reveal(),
|
||||
'datetime_allowed_formats' => [\DateTime::RFC3339],
|
||||
];
|
||||
|
||||
$denormalized = $this->normalizer->denormalize($normalized, TimestampItem::class, NULL, $context);
|
||||
// Mock Serializer service, to assert that the Timestamp @DataType
|
||||
// denormalizer would be called.
|
||||
$serializer_prophecy = $this->prophesize(Serializer::class);
|
||||
// This is where \Drupal\serialization\Normalizer\TimestampNormalizer would
|
||||
// be called.
|
||||
$serializer_prophecy->denormalize($timestamp_item_normalization['value'], Timestamp::class, NULL, $context)
|
||||
->willReturn($timestamp_data_denormalization)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->normalizer->setSerializer($serializer_prophecy->reveal());
|
||||
|
||||
$denormalized = $this->normalizer->denormalize($timestamp_item_normalization, TimestampItem::class, NULL, $context);
|
||||
$this->assertTrue($denormalized instanceof TimestampItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testDenormalizeValidFormats.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestDenormalizeValidFormats() {
|
||||
$expected_stamp = 1478422920;
|
||||
|
||||
$data = [];
|
||||
|
||||
$data['U'] = [$expected_stamp, $expected_stamp];
|
||||
$data['RFC3339'] = ['2016-11-06T09:02:00+00:00', $expected_stamp];
|
||||
$data['RFC3339 +0100'] = ['2016-11-06T09:02:00+01:00', $expected_stamp - 1 * 3600];
|
||||
$data['RFC3339 -0600'] = ['2016-11-06T09:02:00-06:00', $expected_stamp + 6 * 3600];
|
||||
|
||||
$data['ISO8601'] = ['2016-11-06T09:02:00+0000', $expected_stamp];
|
||||
$data['ISO8601 +0100'] = ['2016-11-06T09:02:00+0100', $expected_stamp - 1 * 3600];
|
||||
$data['ISO8601 -0600'] = ['2016-11-06T09:02:00-0600', $expected_stamp + 6 * 3600];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with bad data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeException() {
|
||||
$this->setExpectedException(UnexpectedValueException::class, 'The specified date "2016/11/06 09:02am GMT" is not in an accepted format: "U" (UNIX timestamp), "Y-m-d\TH:i:sO" (ISO 8601), "Y-m-d\TH:i:sP" (RFC 3339).');
|
||||
|
||||
$context = ['target_instance' => $this->createTimestampItemProphecy()->reveal()];
|
||||
|
||||
$normalized = ['value' => '2016/11/06 09:02am GMT'];
|
||||
$this->normalizer->denormalize($normalized, TimestampItem::class, NULL, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TimestampItem prophecy.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Timestamp;
|
||||
use Drupal\Core\TypedData\Type\DateTimeInterface;
|
||||
use Drupal\serialization\Normalizer\TimestampNormalizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Unit test coverage for the "Timestamp" @DataType.
|
||||
*
|
||||
* @group serialization
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\TimestampNormalizer
|
||||
* @see \Drupal\Core\TypedData\Plugin\DataType\Timestamp
|
||||
*/
|
||||
class TimestampNormalizerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The tested data type's normalizer.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\TimestampNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The tested data type.
|
||||
*
|
||||
* @var \Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->normalizer = new TimestampNormalizer($this->prophesize(ConfigFactoryInterface::class)->reveal());
|
||||
$this->data = $this->prophesize(Timestamp::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization() {
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($this->data->reveal()));
|
||||
|
||||
$integer = $this->prophesize(IntegerData::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($integer->reveal()));
|
||||
|
||||
$datetime = $this->prophesize(DateTimeInterface::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($datetime->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsDenormalization
|
||||
*/
|
||||
public function testSupportsDenormalization() {
|
||||
$this->assertTrue($this->normalizer->supportsDenormalization($this->data->reveal(), Timestamp::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalize() {
|
||||
$random_rfc_3339_string = $this->randomMachineName();
|
||||
|
||||
$drupal_date_time = $this->prophesize(TimestampNormalizerTestDrupalDateTime::class);
|
||||
$drupal_date_time->setTimezone(new \DateTimeZone('UTC'))
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
$drupal_date_time->format(\DateTime::RFC3339)
|
||||
->willReturn($random_rfc_3339_string);
|
||||
|
||||
$this->data->getDateTime()
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->data->reveal());
|
||||
$this->assertSame($random_rfc_3339_string, $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with good data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
* @dataProvider providerTestDenormalizeValidFormats
|
||||
*/
|
||||
public function testDenormalizeValidFormats($normalized, $expected) {
|
||||
$denormalized = $this->normalizer->denormalize($normalized, Timestamp::class, NULL, []);
|
||||
$this->assertSame($expected, $denormalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testDenormalizeValidFormats.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function providerTestDenormalizeValidFormats() {
|
||||
$expected_stamp = 1478422920;
|
||||
|
||||
$data = [];
|
||||
|
||||
$data['U'] = [$expected_stamp, $expected_stamp];
|
||||
$data['RFC3339'] = ['2016-11-06T09:02:00+00:00', $expected_stamp];
|
||||
$data['RFC3339 +0100'] = ['2016-11-06T09:02:00+01:00', $expected_stamp - 1 * 3600];
|
||||
$data['RFC3339 -0600'] = ['2016-11-06T09:02:00-06:00', $expected_stamp + 6 * 3600];
|
||||
|
||||
$data['ISO8601'] = ['2016-11-06T09:02:00+0000', $expected_stamp];
|
||||
$data['ISO8601 +0100'] = ['2016-11-06T09:02:00+0100', $expected_stamp - 1 * 3600];
|
||||
$data['ISO8601 -0600'] = ['2016-11-06T09:02:00-0600', $expected_stamp + 6 * 3600];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with bad data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeException() {
|
||||
$this->setExpectedException(UnexpectedValueException::class, 'The specified date "2016/11/06 09:02am GMT" is not in an accepted format: "U" (UNIX timestamp), "Y-m-d\TH:i:sO" (ISO 8601), "Y-m-d\TH:i:sP" (RFC 3339).');
|
||||
|
||||
$normalized = '2016/11/06 09:02am GMT';
|
||||
|
||||
$this->normalizer->denormalize($normalized, Timestamp::class, NULL, []);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: Prophecy does not support magic methods. By subclassing and specifying
|
||||
* an explicit method, Prophecy works.
|
||||
* @see https://github.com/phpspec/prophecy/issues/338
|
||||
* @see https://github.com/phpspec/prophecy/issues/34
|
||||
* @see https://github.com/phpspec/prophecy/issues/80
|
||||
*/
|
||||
class TimestampNormalizerTestDrupalDateTime extends DrupalDateTime {
|
||||
|
||||
public function setTimezone(\DateTimeZone $timezone) {
|
||||
parent::setTimezone($timezone);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue