[TASK] Timestamp support for DateTimeConverter
authorClaus Due <claus@wildside.dk>
Sat, 20 Aug 2011 16:27:20 +0000 (18:27 +0200)
committerTymoteusz Motylewski <t.motylewski@gmail.com>
Mon, 26 Nov 2012 23:10:08 +0000 (00:10 +0100)
This change adds DateTimeConverter support for integer values
by using the "U" dateFormat which reads a UnixTimestamp date
format (i.e. integer as string).

Under the assumption that the new PropertyMapper is being
used, this fixes the bugs below.

At the same time backport the changes done in Flow and use the
same solution which is currently pending in Flow (#41989)

Fixes: #7574
Fixes: #8536
Fixes: #5595
Releases: 6.0

Change-Id: Id39e37451767ea732288423911109d83d47aa6e4
Reviewed-on: http://review.typo3.org/4440
Reviewed-by: Tymoteusz Motylewski
Tested-by: Tymoteusz Motylewski
typo3/sysext/extbase/Classes/Property/Mapper.php
typo3/sysext/extbase/Classes/Property/TypeConverter/DateTimeConverter.php
typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/DateTimeConverterTest.php

index 46d67e4..aee1def 100644 (file)
@@ -276,7 +276,6 @@ class Mapper implements \TYPO3\CMS\Core\SingletonInterface {
         */
        protected function transformToObject($propertyValue, $targetType, $propertyName) {
                if ($targetType === 'DateTime' || is_subclass_of($targetType, 'DateTime')) {
-                       // TODO replace this with converter implementation of FLOW3
                        if ($propertyValue === '') {
                                $propertyValue = NULL;
                        } else {
@@ -287,7 +286,7 @@ class Mapper implements \TYPO3\CMS\Core\SingletonInterface {
                                }
                        }
                } else {
-                       if (is_numeric($propertyValue)) {
+                       if (ctype_digit((string)$propertyValue)) {
                                $propertyValue = $this->findObjectByUid($targetType, $propertyValue);
                                if ($propertyValue === FALSE) {
                                        $this->mappingResults->addError(new \TYPO3\CMS\Extbase\Error\Error('Querying the repository for the specified object with UUID ' . $propertyValue . ' was not successful.', 1249379517), $propertyName);
@@ -350,4 +349,4 @@ class Mapper implements \TYPO3\CMS\Core\SingletonInterface {
        }
 }
 
-?>
\ No newline at end of file
+?>
index 3c7ddee..eba963e 100644 (file)
@@ -58,7 +58,9 @@ class DateTimeConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\Abstra
 
        /**
         * The default date format is "YYYY-MM-DDT##:##:##+##:##", for example "2005-08-15T15:52:01+00:00"
-        * according to the W3C standard @see http://www.w3.org/TR/NOTE-datetime.html
+        * according to the W3C standard
+        *
+        * @see http://www.w3.org/TR/NOTE-datetime.html
         *
         * @var string
         */
@@ -67,7 +69,7 @@ class DateTimeConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\Abstra
        /**
         * @var array<string>
         */
-       protected $sourceTypes = array('string', 'array');
+       protected $sourceTypes = array('string', 'integer','array');
 
        /**
         * @var string
@@ -80,43 +82,56 @@ class DateTimeConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\Abstra
        protected $priority = 1;
 
        /**
-        * Empty strings can't be converted
+        * If conversion is possible.
         *
         * @param string $source
         * @param string $targetType
+        *
         * @return boolean
-        * @author Bastian Waidelich <bastian@typo3.org>
         */
        public function canConvertFrom($source, $targetType) {
-               if ($targetType !== 'DateTime') {
+               if (!is_callable(array($targetType, 'createFromFormat'))) {
                        return FALSE;
                }
                if (is_array($source)) {
                        return TRUE;
                }
+               if (is_integer($source)) {
+                       return TRUE;
+               }
                return is_string($source);
        }
 
        /**
-        * Converts $source to a DateTime using the configured dateFormat
+        * Converts $source to a \DateTime using the configured dateFormat
         *
-        * @param string $source the string to be converted to a DateTime object
-        * @param string $targetType must be "DateTime
+        * @param string|integer|array $source the string to be converted to a DateTime object
+        * @param string $targetType must be "DateTime"
         * @param array $convertedChildProperties not used currently
         * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
-        * @throws \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException
+        *
         * @return \DateTime
-        * @author Bastian Waidelich <bastian@typo3.org>
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException
         */
        public function convertFrom($source, $targetType, array $convertedChildProperties = array(), \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = NULL) {
                $dateFormat = $this->getDefaultDateFormat($configuration);
                if (is_string($source)) {
                        $dateAsString = $source;
+               } elseif (is_integer($source)) {
+                       $dateAsString = strval($source);
                } else {
-                       if (!isset($source['date']) || !is_string($source['date'])) {
+                       if (isset($source['date']) && is_string($source['date'])) {
+                               $dateAsString = $source['date'];
+                       } elseif (isset($source['date']) && is_integer($source['date'])) {
+                               $dateAsString = strval($source['date']);
+                       } elseif ($this->isDatePartKeysProvided($source)) {
+                               if ($source['day'] < 1 || $source['month'] < 1 || $source['year'] < 1) {
+                                       return new \TYPO3\CMS\Extbase\Error\Error('Could not convert the given date parts into a DateTime object because one or more parts were 0.', 1333032779);
+                               }
+                               $dateAsString = sprintf('%d-%d-%d', $source['year'], $source['month'], $source['day']);
+                       } else {
                                throw new \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException('Could not convert the given source into a DateTime object because it was not an array with a valid date as a string', 1308003914);
                        }
-                       $dateAsString = $source['date'];
                        if (isset($source['dateFormat']) && strlen($source['dateFormat']) > 0) {
                                $dateFormat = $source['dateFormat'];
                        }
@@ -124,25 +139,52 @@ class DateTimeConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\Abstra
                if ($dateAsString === '') {
                        return NULL;
                }
-               $date = \DateTime::createFromFormat($dateFormat, $dateAsString);
+               if (ctype_digit($dateAsString) && (!is_array($source) || !isset($source['dateFormat'])) && $configuration === NULL) {
+                       $dateFormat = 'U';
+               }
+               if (is_array($source) && isset($source['timezone']) && strlen($source['timezone']) !== 0) {
+                       try {
+                               $timezone = new \DateTimeZone($source['timezone']);
+                       } catch (\Exception $e) {
+                               throw new \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException('The specified timezone "' . $source['timezone'] . '" is invalid.', 1308240974);
+                       }
+                       $date = $targetType::createFromFormat($dateFormat, $dateAsString, $timezone);
+               } else {
+                       $date = $targetType::createFromFormat($dateFormat, $dateAsString);
+               }
                if ($date === FALSE) {
-                       return new \TYPO3\CMS\Extbase\Error\Error('The string"' . $dateAsString . '" could not be converted to DateTime with format "' . $dateFormat . '"', 1307719788);
+                       return new \TYPO3\CMS\Extbase\Error\Error('The date "%s" was not recognized (for format "%s").', 1307719788, array(
+                               $dateAsString,
+                               $dateFormat
+                       ));
                }
                if (is_array($source)) {
                        $this->overrideTimeIfSpecified($date, $source);
-                       $this->overrideTimezoneIfSpecified($date, $source);
                }
                return $date;
        }
 
        /**
+        * Returns whether date information (day, month, year) are present as keys in $source.
+        *
+        * @param array $source
+        *
+        * @return boolean
+        */
+       protected function isDatePartKeysProvided(array $source) {
+               return isset($source['day']) && ctype_digit((string)$source['day'])
+                       && isset($source['month']) && ctype_digit((string)$source['month'])
+                       && isset($source['year']) && ctype_digit((string)$source['year']);
+       }
+
+       /**
         * Determines the default date format to use for the conversion.
         * If no format is specified in the mapping configuration DEFAULT_DATE_FORMAT is used.
         *
         * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
-        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
+        *
         * @return string
-        * @author Bastian Waidelich <bastian@typo3.org>
+        * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
         */
        protected function getDefaultDateFormat(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = NULL) {
                if ($configuration === NULL) {
@@ -162,36 +204,18 @@ class DateTimeConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\Abstra
         *
         * @param \DateTime $date
         * @param array $source
+        *
         * @return void
         */
        protected function overrideTimeIfSpecified(\DateTime $date, array $source) {
                if (!isset($source['hour']) && !isset($source['minute']) && !isset($source['second'])) {
                        return;
                }
-               $hour = isset($source['hour']) ? (integer) $source['hour'] : 0;
-               $minute = isset($source['minute']) ? (integer) $source['minute'] : 0;
-               $second = isset($source['second']) ? (integer) $source['second'] : 0;
-               $date->setTime($hour, $minute, $second);
-       }
 
-       /**
-        * Overrides timezone of the given date with $source['timezone']
-        *
-        * @param \DateTime $date
-        * @param array $source
-        * @throws \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException
-        * @return void
-        */
-       protected function overrideTimezoneIfSpecified(\DateTime $date, array $source) {
-               if (!isset($source['timezone']) || strlen($source['timezone']) === 0) {
-                       return;
-               }
-               try {
-                       $timezone = new \DateTimeZone($source['timezone']);
-               } catch (\Exception $e) {
-                       throw new \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException('The specified timezone "' . $source['timezone'] . '" is invalid', 1308240974);
-               }
-               $date->setTimezone($timezone);
+               $hour = isset($source['hour']) ? (integer)$source['hour'] : 0;
+               $minute = isset($source['minute']) ? (integer)$source['minute'] : 0;
+               $second = isset($source['second']) ? (integer)$source['second'] : 0;
+               $date->setTime($hour, $minute, $second);
        }
 }
 
index 28ff239..ac88b77 100644 (file)
@@ -42,7 +42,7 @@ class DateTimeConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
         * @author Bastian Waidelich <bastian@typo3.org>
         */
        public function checkMetadata() {
-               $this->assertEquals(array('string', 'array'), $this->converter->getSupportedSourceTypes(), 'Source types do not match');
+               $this->assertEquals(array('string', 'integer', 'array'), $this->converter->getSupportedSourceTypes(), 'Source types do not match');
                $this->assertEquals('DateTime', $this->converter->getSupportedTargetType(), 'Target type does not match');
                $this->assertEquals(1, $this->converter->getPriority(), 'Priority does not match');
        }
@@ -129,7 +129,7 @@ class DateTimeConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
                        array('13.12.1980', 'd.m.Y', TRUE),
                        array('2005-08-15T15:52:01+00:00', NULL, TRUE),
                        array('2005-08-15T15:52:01+0000', \DateTime::ISO8601, TRUE),
-                       array('1308174051', 'U', TRUE)
+                       array('1308174051', 'U', TRUE),
                );
        }
 
@@ -161,8 +161,45 @@ class DateTimeConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
        }
 
        /**
+        * @return array
+        * @see convertFromIntegerOrDigitStringWithoutConfigurationTests()
+        * @see convertFromIntegerOrDigitStringInArrayWithoutConfigurationTests()
+        */
+       public function convertFromIntegerOrDigitStringsWithoutConfigurationDataProvider() {
+               return array(
+                       array('1308174051'),
+                       array(1308174051),
+               );
+       }
+
+       /**
+        * @test
+        * @param $source
+        * @dataProvider convertFromIntegerOrDigitStringsWithoutConfigurationDataProvider
+        */
+       public function convertFromIntegerOrDigitStringWithoutConfigurationTests($source) {
+               $date = $this->converter->convertFrom($source, 'DateTime', array(), NULL);
+               $this->assertInstanceOf('DateTime', $date);
+               $this->assertSame(strval($source), $date->format('U'));
+
+       }
+
+       /**
         * Array to DateTime testcases  *
         */
+
+       /**
+        * @test
+        * @param $source
+        * @dataProvider convertFromIntegerOrDigitStringsWithoutConfigurationDataProvider
+        */
+       public function convertFromIntegerOrDigitStringInArrayWithoutConfigurationTests($source) {
+               $date = $this->converter->convertFrom(array('date' => $source), 'DateTime', array(), NULL);
+               $this->assertInstanceOf('DateTime', $date);
+               $this->assertSame(strval($source), $date->format('U'));
+
+       }
+
        /**
         * @test
         * @author Bastian Waidelich <bastian@typo3.org>
@@ -201,6 +238,30 @@ class DateTimeConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
        }
 
        /**
+        * @return array
+        * @see convertFromThrowsExceptionIfDatePartKeysHaveInvalidValuesSpecified
+        */
+       public function invalidDatePartKeyValuesDataProvider() {
+               return array(
+                       array(array('day' => '13.0', 'month' => '10', 'year' => '2010')),
+                       array(array('day' => '13', 'month' => '10.0', 'year' => '2010')),
+                       array(array('day' => '13', 'month' => '10', 'year' => '2010.0')),
+                       array(array('day' => '-13', 'month' => '10', 'year' => '2010')),
+                       array(array('day' => '13', 'month' => '-10', 'year' => '2010')),
+                       array(array('day' => '13', 'month' => '10', 'year' => '-2010')),
+               );
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException
+        * @dataProvider invalidDatePartKeyValuesDataProvider
+        */
+       public function convertFromThrowsExceptionIfDatePartKeysHaveInvalidValuesSpecified($source) {
+               $this->converter->convertFrom($source, 'DateTime');
+       }
+
+       /**
         * @test
         * @author Bastian Waidelich <bastian@typo3.org>
         */
@@ -271,7 +332,6 @@ class DateTimeConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
         */
        public function convertFromArrayDataProvider() {
                return array(
-                       array(array('date' => '1308174051'), FALSE),
                        array(array('date' => '2005-08-15T15:52:01+01:00'), TRUE),
                        array(array('date' => '1308174051', 'dateFormat' => ''), FALSE),
                        array(array('date' => '13-12-1980', 'dateFormat' => 'd.m.Y'), FALSE),
@@ -280,7 +340,8 @@ class DateTimeConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
                        array(array('date' => '13.12.1980', 'dateFormat' => 'd.m.Y'), TRUE),
                        array(array('date' => '2005-08-15T15:52:01+00:00', 'dateFormat' => ''), TRUE),
                        array(array('date' => '2005-08-15T15:52:01+0000', 'dateFormat' => \DateTime::ISO8601), TRUE),
-                       array(array('date' => '1308174051', 'dateFormat' => 'U'), TRUE)
+                       array(array('date' => '1308174051', 'dateFormat' => 'U'), TRUE),
+                       array(array('date' => 1308174051, 'dateFormat' => 'U'), TRUE),
                );
        }
 
@@ -309,7 +370,7 @@ class DateTimeConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
                        $dateFormat = \TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter::DEFAULT_DATE_FORMAT;
                }
                $dateAsString = isset($source['date']) ? $source['date'] : '';
-               $this->assertSame($dateAsString, $date->format($dateFormat));
+               $this->assertSame(strval($dateAsString), $date->format($dateFormat));
        }
 }