diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php new file mode 100644 index 000000000000..3935810b76c2 --- /dev/null +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Normalizer; + +use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\Exception\UnexpectedValueException; + +/** + * Normalizes an object implementing the {@see \DateTimeInterface} to a date string. + * Denormalizes a date string to an instance of {@see \DateTime} or {@see \DateTimeImmutable}. + * + * @author Kévin Dunglas + */ +class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface +{ + const FORMAT_KEY = 'datetime_format'; + + /** + * @var string + */ + private $format; + + /** + * @param string $format + */ + public function __construct($format = \DateTime::RFC3339) + { + $this->format = $format; + } + + /** + * {@inheritdoc} + * + * @throws InvalidArgumentException + */ + public function normalize($object, $format = null, array $context = array()) + { + if (!$object instanceof \DateTimeInterface) { + throw new InvalidArgumentException('The object must implement the "\DateTimeInterface".'); + } + + $format = isset($context[self::FORMAT_KEY]) ? $context[self::FORMAT_KEY] : $this->format; + + return $object->format($format); + } + + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof \DateTimeInterface; + } + + /** + * {@inheritdoc} + * + * @throws UnexpectedValueException + */ + public function denormalize($data, $class, $format = null, array $context = array()) + { + try { + return \DateTime::class === $class ? new \DateTime($data) : new \DateTimeImmutable($data); + } catch (\Exception $e) { + throw new UnexpectedValueException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * {@inheritdoc} + */ + public function supportsDenormalization($data, $type, $format = null) + { + $supportedTypes = array( + \DateTimeInterface::class => true, + \DateTimeImmutable::class => true, + \DateTime::class => true, + ); + + return isset($supportedTypes[$type]); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php new file mode 100644 index 000000000000..3ef1ed7b985b --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Normalizer; + +use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; + +/** + * @author Kévin Dunglas + */ +class DateTimeNormalizerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var DateTimeNormalizer + */ + private $normalizer; + + public function setUp() + { + $this->normalizer = new DateTimeNormalizer(); + } + + public function testSupportNormalization() + { + $this->assertTrue($this->normalizer->supportsNormalization(new \DateTime())); + $this->assertTrue($this->normalizer->supportsNormalization(new \DateTimeImmutable())); + $this->assertFalse($this->normalizer->supportsNormalization(new \stdClass())); + } + + public function testNormalize() + { + $this->assertEquals('2016-01-01T00:00:00+00:00', $this->normalizer->normalize(new \DateTime('2016/01/01'))); + $this->assertEquals('2016-01-01T00:00:00+00:00', $this->normalizer->normalize(new \DateTimeImmutable('2016/01/01'))); + } + + public function testContextFormat() + { + $this->assertEquals('2016', $this->normalizer->normalize(new \DateTime('2016/01/01'), null, array(DateTimeNormalizer::FORMAT_KEY => 'Y'))); + } + + public function testConstructorFormat() + { + $this->assertEquals('16', (new DateTimeNormalizer('y'))->normalize(new \DateTime('2016/01/01'))); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\InvalidArgumentException + * @expectedExceptionMessage The object must implement the "\DateTimeInterface". + */ + public function testInvalidDataThrowException() + { + $this->normalizer->normalize(new \stdClass()); + } + + public function testSupportDenormalization() + { + $this->assertTrue($this->normalizer->supportsDenormalization('2016-01-01T00:00:00+00:00', \DateTimeInterface::class)); + $this->assertTrue($this->normalizer->supportsDenormalization('2016-01-01T00:00:00+00:00', \DateTime::class)); + $this->assertTrue($this->normalizer->supportsDenormalization('2016-01-01T00:00:00+00:00', \DateTimeImmutable::class)); + $this->assertFalse($this->normalizer->supportsDenormalization('foo', 'Bar')); + } + + public function testDenormalize() + { + $this->assertEquals(new \DateTimeImmutable('2016/01/01'), $this->normalizer->denormalize('2016-01-01T00:00:00+00:00', \DateTimeInterface::class)); + $this->assertEquals(new \DateTimeImmutable('2016/01/01'), $this->normalizer->denormalize('2016-01-01T00:00:00+00:00', \DateTimeImmutable::class)); + $this->assertEquals(new \DateTime('2016/01/01'), $this->normalizer->denormalize('2016-01-01T00:00:00+00:00', \DateTime::class)); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException + */ + public function testInvalidDateThrowException() + { + $this->normalizer->denormalize('invalid date', \DateTimeInterface::class); + } +}