diff --git a/src/Jms/Handler/XmlSchemaDateHandler.php b/src/Jms/Handler/XmlSchemaDateHandler.php index 11a2b13..eae4517 100644 --- a/src/Jms/Handler/XmlSchemaDateHandler.php +++ b/src/Jms/Handler/XmlSchemaDateHandler.php @@ -58,6 +58,12 @@ public static function getSubscribingMethods() 'format' => 'xml', 'method' => 'deserializeDateIntervalXml', ), + array( + 'type' => 'DateInterval', + 'direction' => GraphNavigator::DIRECTION_SERIALIZATION, + 'format' => 'xml', + 'method' => 'serializeDateInterval', + ), ); } @@ -72,12 +78,50 @@ public function deserializeDateIntervalXml(XmlDeserializationVisitor $visitor, $ if (isset($attributes['nil'][0]) && (string) $attributes['nil'][0] === 'true') { return null; } - return new \DateInterval((string)$data); + + //Accept negative intervals like -PT1M23S. Safe to assume that "-" doesn't exist elsewhere in a valid interval spec. + $interval = str_replace('-', '', (string)$data, $count); + $dateInterval = new \DateInterval($interval); + + //Invert if a negative sign was found + $dateInterval->invert = !!$count; + + return $dateInterval; } - public function serializeDate(XmlSerializationVisitor $visitor, \DateTime $date, array $type, Context $context) + public function serializeDateInterval(XmlSerializationVisitor $visitor, \DateInterval $interval, array $type, Context $context) { + $date = array_filter(array( + 'Y' => $interval->y, + 'M' => $interval->m, + 'D' => $interval->d + )); + + // Reading all non-zero time parts. + $time = array_filter(array( + 'H' => $interval->h, + 'M' => $interval->i, + 'S' => $interval->s + )); + + $specString = ($interval->invert === 1) ? '-P' : 'P'; + + // Adding each part to the spec-string. + foreach ($date as $key => $value) { + $specString .= $value . $key; + } + if (count($time) > 0) { + $specString .= 'T'; + foreach ($time as $key => $value) { + $specString .= $value . $key; + } + } + return $visitor->visitSimpleString($specString, $type, $context); + } + + public function serializeDate(XmlSerializationVisitor $visitor, \DateTime $date, array $type, Context $context) + { $v = $date->format('Y-m-d'); return $visitor->visitSimpleString($v, $type, $context); diff --git a/tests/XmlSchemaDateHandlerDeserializationTest.php b/tests/XmlSchemaDateHandlerDeserializationTest.php index e33df0b..84e33ad 100644 --- a/tests/XmlSchemaDateHandlerDeserializationTest.php +++ b/tests/XmlSchemaDateHandlerDeserializationTest.php @@ -95,4 +95,35 @@ public function testDeserializeInvalidDate() $element = new \SimpleXMLElement("<Date>2015-01-01T</Date>"); $this->handler->deserializeDate($this->visitor, $element, [], $this->context); } + + /** + * @dataProvider getDeserializeDateInterval + * @param string $interval + * @param \DateInterval $expected + */ + public function testDeserializeDateInterval(string $interval, \DateInterval $expected) + { + $element = new \SimpleXMLElement("<DateInterval>$interval</DateInterval>"); + $deserialized = $this->handler->deserializeDateIntervalXml($this->visitor, $element, []); + $this->assertEquals($expected, $deserialized); + } + + public function getDeserializeDateInterval() + { + $interval1 = new \DateInterval('PT1M23S'); + $interval2 = new \DateInterval('P2DT3H'); + + $interval1Invert = clone $interval1; + $interval1Invert->invert = 1; + + $interval2Invert = clone $interval2; + $interval2Invert->invert = 1; + + return [ + ['PT1M23S', $interval1], + ['-PT1M23S', $interval1Invert], + ['P2DT3H', $interval2], + ['-P2DT3H', $interval2Invert], + ]; + } } diff --git a/tests/XmlSchemaDateHandlerSerializationTest.php b/tests/XmlSchemaDateHandlerSerializationTest.php index f445aff..637dbaf 100644 --- a/tests/XmlSchemaDateHandlerSerializationTest.php +++ b/tests/XmlSchemaDateHandlerSerializationTest.php @@ -110,4 +110,36 @@ public function getSerializeDate() [new \DateTime('2015-01-01 12:00:56', new \DateTimeZone("Europe/London")), '2015-01-01'], ]; } + + /** + * @dataProvider getSerializeDateInterval + * @param \DateInterval $interval + * @param string $expected + */ + public function testSerializeDateInterval(\DateInterval $interval, string $expected) + { + $ret = $this->handler->serializeDateInterval($this->visitor, $interval, [], $this->context); + + $actual = $ret ? $ret->nodeValue : $this->visitor->getCurrentNode()->nodeValue; + $this->assertEquals($expected, $actual); + } + + public function getSerializeDateInterval() + { + $interval1 = new \DateInterval('PT1M23S'); + $interval2 = new \DateInterval('P2DT3H'); + + $interval1Invert = clone $interval1; + $interval1Invert->invert = 1; + + $interval2Invert = clone $interval2; + $interval2Invert->invert = 1; + + return [ + [$interval1, 'PT1M23S'], + [$interval1Invert, '-PT1M23S'], + [$interval2, 'P2DT3H'], + [$interval2Invert, '-P2DT3H'], + ]; + } }