diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 1b5c95cd39443..3c51182c504d1 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 7.3 --- + * Add `PRESERVE_CONTEXT_TIMEZONE_KEY` to `DateTimeNormalizer` to preserve the context timezone * Deprecate the `CompiledClassMetadataFactory` and `CompiledClassMetadataCacheWarmer` classes * Register `NormalizerInterface` and `DenormalizerInterface` aliases for named serializers * Add `NumberNormalizer` to normalize `BcMath\Number` and `GMP` as `string` diff --git a/src/Symfony/Component/Serializer/Context/Normalizer/DateTimeNormalizerContextBuilder.php b/src/Symfony/Component/Serializer/Context/Normalizer/DateTimeNormalizerContextBuilder.php index de83b1245455e..53a6dffcdf637 100644 --- a/src/Symfony/Component/Serializer/Context/Normalizer/DateTimeNormalizerContextBuilder.php +++ b/src/Symfony/Component/Serializer/Context/Normalizer/DateTimeNormalizerContextBuilder.php @@ -69,4 +69,13 @@ public function withCast(?string $cast): static { return $this->with(DateTimeNormalizer::CAST_KEY, $cast); } + + /** + * Configures if the timezone should be used from the context and ignore any timezone + * defined in date strings. + */ + public function withPreserveContextTimezone(?bool $preserveContextTimezone): static + { + return $this->with(DateTimeNormalizer::PRESERVE_CONTEXT_TIMEZONE_KEY, $preserveContextTimezone); + } } diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index a136ec227a3ae..d28687dfb9126 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -25,11 +25,15 @@ final class DateTimeNormalizer implements NormalizerInterface, DenormalizerInter public const FORMAT_KEY = 'datetime_format'; public const TIMEZONE_KEY = 'datetime_timezone'; public const CAST_KEY = 'datetime_cast'; + /* if set to true during denormalization DateTime(Immutable) objects will always receive the timezone set in the context + and the tz from date strings and timestamps will be ignored */ + public const PRESERVE_CONTEXT_TIMEZONE_KEY = 'datetime_preserve_context_timezone'; private array $defaultContext = [ self::FORMAT_KEY => \DateTimeInterface::RFC3339, self::TIMEZONE_KEY => null, self::CAST_KEY => null, + self::PRESERVE_CONTEXT_TIMEZONE_KEY => false ]; private const SUPPORTED_TYPES = [ @@ -112,7 +116,7 @@ public function denormalize(mixed $data, string $type, ?string $format = null, a if (null !== $dateTimeFormat) { if (false !== $object = $type::createFromFormat($dateTimeFormat, $data, $timezone)) { - return $object; + return $this->preserveContextTimezone($object, $context); } $dateTimeErrors = $type::getLastErrors(); @@ -124,11 +128,11 @@ public function denormalize(mixed $data, string $type, ?string $format = null, a if (null !== $defaultDateTimeFormat) { if (false !== $object = $type::createFromFormat($defaultDateTimeFormat, $data, $timezone)) { - return $object; + return $this->preserveContextTimezone($object, $context); } } - return new $type($data, $timezone); + return $this->preserveContextTimezone(new $type($data, $timezone), $context); } catch (NotNormalizableValueException $e) { throw $e; } catch (\Exception $e) { @@ -167,4 +171,16 @@ private function getTimezone(array $context): ?\DateTimeZone return $dateTimeZone instanceof \DateTimeZone ? $dateTimeZone : new \DateTimeZone($dateTimeZone); } + + private function preserveContextTimezone(\DateTime|\DateTimeImmutable $object, array $context): \DateTime|\DateTimeImmutable + { + $timezone = $this->getTimezone($context); + $preserveTimezone = $context[self::PRESERVE_CONTEXT_TIMEZONE_KEY] ?? $this->defaultContext[self::PRESERVE_CONTEXT_TIMEZONE_KEY]; + + if (null === $timezone || !$preserveTimezone) { + return $object; + } + + return $object->setTimezone($timezone); + } } diff --git a/src/Symfony/Component/Serializer/Tests/Context/Normalizer/DateTimeNormalizerContextBuilderTest.php b/src/Symfony/Component/Serializer/Tests/Context/Normalizer/DateTimeNormalizerContextBuilderTest.php index ac4badc19486b..feb1f4163004e 100644 --- a/src/Symfony/Component/Serializer/Tests/Context/Normalizer/DateTimeNormalizerContextBuilderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Context/Normalizer/DateTimeNormalizerContextBuilderTest.php @@ -39,6 +39,7 @@ public function testWithers(array $values) ->withFormat($values[DateTimeNormalizer::FORMAT_KEY]) ->withTimezone($values[DateTimeNormalizer::TIMEZONE_KEY]) ->withCast($values[DateTimeNormalizer::CAST_KEY]) + ->withPreserveContextTimezone($values[DateTimeNormalizer::PRESERVE_CONTEXT_TIMEZONE_KEY]) ->toArray(); $this->assertEquals($values, $context); @@ -53,12 +54,14 @@ public static function withersDataProvider(): iterable DateTimeNormalizer::FORMAT_KEY => 'format', DateTimeNormalizer::TIMEZONE_KEY => new \DateTimeZone('GMT'), DateTimeNormalizer::CAST_KEY => 'int', + DateTimeNormalizer::PRESERVE_CONTEXT_TIMEZONE_KEY => true, ]]; yield 'With null values' => [[ DateTimeNormalizer::FORMAT_KEY => null, DateTimeNormalizer::TIMEZONE_KEY => null, DateTimeNormalizer::CAST_KEY => null, + DateTimeNormalizer::PRESERVE_CONTEXT_TIMEZONE_KEY => null, ]]; } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php index 81219652b3ef1..f5094c453bcca 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php @@ -308,6 +308,59 @@ public static function denormalizeUsingTimezonePassedInContextProvider() ]; } + public function testDenormalizeUsingPreserveContextTimezoneAndFormatPassedInConstructor() + { + $normalizer = new DateTimeNormalizer( + [ + DateTimeNormalizer::TIMEZONE_KEY => new \DateTimeZone('Japan'), + DateTimeNormalizer::FORMAT_KEY => 'Y-m-d\\TH:i:sO', + DateTimeNormalizer::PRESERVE_CONTEXT_TIMEZONE_KEY => true, + ] + ); + $actual = $normalizer->denormalize('2016-12-01T12:34:56+0000', \DateTimeInterface::class); + $this->assertEquals(new \DateTimeZone('Japan'), $actual->getTimezone()); + } + + public function testDenormalizeUsingPreserveContextTimezoneAndFormatPassedInContext() + { + $actual = $this->normalizer->denormalize( + '2016-12-01T12:34:56+0000', + \DateTimeInterface::class, + null, + [ + DateTimeNormalizer::TIMEZONE_KEY => new \DateTimeZone('Japan'), + DateTimeNormalizer::FORMAT_KEY => 'Y-m-d\\TH:i:sO', + DateTimeNormalizer::PRESERVE_CONTEXT_TIMEZONE_KEY => true, + ] + ); + $this->assertEquals(new \DateTimeZone('Japan'), $actual->getTimezone()); + } + + public function testDenormalizeUsingPreserveContextTimezoneWithoutFormat() + { + $actual = $this->normalizer->denormalize( + '2016-12-01T12:34:56+0000', + \DateTimeInterface::class, + null, + [ + DateTimeNormalizer::TIMEZONE_KEY => new \DateTimeZone('Japan'), + DateTimeNormalizer::PRESERVE_CONTEXT_TIMEZONE_KEY => true, + ] + ); + $this->assertEquals(new \DateTimeZone('Japan'), $actual->getTimezone()); + } + + public function testDenormalizeUsingPreserveContextShouldBeIgnoredWithoutTimezoneInContext() + { + $actual = $this->normalizer->denormalize( + '2016-12-01T12:34:56+0000', + \DateTimeInterface::class, + null, + [DateTimeNormalizer::PRESERVE_CONTEXT_TIMEZONE_KEY => true] + ); + $this->assertEquals(new \DateTimeZone('+00:00'), $actual->getTimezone()); + } + public function testDenormalizeInvalidDataThrowsException() { $this->expectException(UnexpectedValueException::class); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy