37375ab02e4440c6ffbf9990e42ca1c7f95f4179
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Property / TypeConverter / DateTimeConverter.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Property\TypeConverter;
3
4 /* *
5 * This script belongs to the Extbase framework *
6 * *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU Lesser General Public License as published by the *
9 * Free Software Foundation, either version 3 of the License, or (at your *
10 * option) any later version. *
11 * *
12 * This script is distributed in the hope that it will be useful, but *
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
14 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
15 * General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with the script. *
19 * If not, see http://www.gnu.org/licenses/lgpl.html *
20 * *
21 * The TYPO3 project - inspiring people to share! *
22 * */
23 /**
24 * Converter which transforms from different input formats into DateTime objects.
25 *
26 * Source can be either a string or an array. The date string is expected to be formatted
27 * according to DEFAULT_DATE_FORMAT.
28 *
29 * But the default date format can be overridden in the initialize*Action() method like this::
30 *
31 * $this->arguments['<argumentName>']
32 * ->getPropertyMappingConfiguration()
33 * ->forProperty('<propertyName>') // this line can be skipped in order to specify the format for all properties
34 * ->setTypeConverterOption(\TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter::CONFIGURATION_DATE_FORMAT, '<dateFormat>');
35 *
36 * If the source is of type array, it is possible to override the format in the source::
37 *
38 * array(
39 * 'date' => '<dateString>',
40 * 'dateFormat' => '<dateFormat>'
41 * );
42 *
43 * By using an array as source you can also override time and timezone of the created DateTime object::
44 *
45 * array(
46 * 'date' => '<dateString>',
47 * 'hour' => '<hour>', // integer
48 * 'minute' => '<minute>', // integer
49 * 'seconds' => '<seconds>', // integer
50 * 'timezone' => '<timezone>', // string, see http://www.php.net/manual/timezones.php
51 * );
52 *
53 * As an alternative to providing the date as string, you might supply day, month and year as array items each::
54 *
55 * array(
56 * 'day' => '<day>', // integer
57 * 'month' => '<month>', // integer
58 * 'year' => '<year>', // integer
59 * );
60 *
61 * @api
62 */
63 class DateTimeConverter extends \TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter
64 {
65 /**
66 * @var string
67 */
68 const CONFIGURATION_DATE_FORMAT = 'dateFormat';
69
70 /**
71 * The default date format is "YYYY-MM-DDT##:##:##+##:##", for example "2005-08-15T15:52:01+00:00"
72 * according to the W3C standard @see http://www.w3.org/TR/NOTE-datetime.html
73 *
74 * @var string
75 */
76 const DEFAULT_DATE_FORMAT = \DateTime::W3C;
77
78 /**
79 * @var array<string>
80 */
81 protected $sourceTypes = ['string', 'integer', 'array'];
82
83 /**
84 * @var string
85 */
86 protected $targetType = 'DateTime';
87
88 /**
89 * @var int
90 */
91 protected $priority = 10;
92
93 /**
94 * If conversion is possible.
95 *
96 * @param string $source
97 * @param string $targetType
98 * @return bool
99 */
100 public function canConvertFrom($source, $targetType)
101 {
102 if (!is_callable([$targetType, 'createFromFormat'])) {
103 return false;
104 }
105 if (is_array($source)) {
106 return true;
107 }
108 if (is_int($source)) {
109 return true;
110 }
111 return is_string($source);
112 }
113
114 /**
115 * Converts $source to a \DateTime using the configured dateFormat
116 *
117 * @param string|int|array $source the string to be converted to a \DateTime object
118 * @param string $targetType must be "DateTime"
119 * @param array $convertedChildProperties not used currently
120 * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
121 * @return \DateTime
122 * @throws \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException
123 */
124 public function convertFrom($source, $targetType, array $convertedChildProperties = [], \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
125 {
126 $dateFormat = $this->getDefaultDateFormat($configuration);
127 if (is_string($source)) {
128 $dateAsString = $source;
129 } elseif (is_int($source)) {
130 $dateAsString = strval($source);
131 } else {
132 if (isset($source['date']) && is_string($source['date'])) {
133 $dateAsString = $source['date'];
134 } elseif (isset($source['date']) && is_int($source['date'])) {
135 $dateAsString = strval($source['date']);
136 } elseif ($this->isDatePartKeysProvided($source)) {
137 if ($source['day'] < 1 || $source['month'] < 1 || $source['year'] < 1) {
138 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);
139 }
140 $dateAsString = sprintf('%d-%d-%d', $source['year'], $source['month'], $source['day']);
141 } else {
142 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);
143 }
144 if (isset($source['dateFormat']) && $source['dateFormat'] !== '') {
145 $dateFormat = $source['dateFormat'];
146 }
147 }
148 if ($dateAsString === '') {
149 return null;
150 }
151 if (ctype_digit($dateAsString) && $configuration === null && (!is_array($source) || !isset($source['dateFormat']))) {
152 $dateFormat = 'U';
153 }
154 if (is_array($source) && isset($source['timezone']) && (string)$source['timezone'] !== '') {
155 try {
156 $timezone = new \DateTimeZone($source['timezone']);
157 } catch (\Exception $e) {
158 throw new \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException('The specified timezone "' . $source['timezone'] . '" is invalid.', 1308240974);
159 }
160 $date = $targetType::createFromFormat($dateFormat, $dateAsString, $timezone);
161 } else {
162 $date = $targetType::createFromFormat($dateFormat, $dateAsString);
163 }
164 if ($date === false) {
165 return new \TYPO3\CMS\Extbase\Validation\Error('The date "%s" was not recognized (for format "%s").', 1307719788, [$dateAsString, $dateFormat]);
166 }
167 if (is_array($source)) {
168 $this->overrideTimeIfSpecified($date, $source);
169 }
170 return $date;
171 }
172
173 /**
174 * Returns whether date information (day, month, year) are present as keys in $source.
175 *
176 * @param array $source
177 * @return bool
178 */
179 protected function isDatePartKeysProvided(array $source)
180 {
181 return isset($source['day']) && ctype_digit($source['day'])
182 && isset($source['month']) && ctype_digit($source['month'])
183 && isset($source['year']) && ctype_digit($source['year']);
184 }
185
186 /**
187 * Determines the default date format to use for the conversion.
188 * If no format is specified in the mapping configuration DEFAULT_DATE_FORMAT is used.
189 *
190 * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
191 * @return string
192 * @throws \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
193 */
194 protected function getDefaultDateFormat(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration = null)
195 {
196 if ($configuration === null) {
197 return self::DEFAULT_DATE_FORMAT;
198 }
199 $dateFormat = $configuration->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter::class, self::CONFIGURATION_DATE_FORMAT);
200 if ($dateFormat === null) {
201 return self::DEFAULT_DATE_FORMAT;
202 }
203 if ($dateFormat !== null && !is_string($dateFormat)) {
204 throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException('CONFIGURATION_DATE_FORMAT must be of type string, "' . (is_object($dateFormat) ? get_class($dateFormat) : gettype($dateFormat)) . '" given', 1307719569);
205 }
206 return $dateFormat;
207 }
208
209 /**
210 * Overrides hour, minute & second of the given date with the values in the $source array
211 *
212 * @param \DateTime $date
213 * @param array $source
214 */
215 protected function overrideTimeIfSpecified(\DateTime $date, array $source)
216 {
217 if (!isset($source['hour']) && !isset($source['minute']) && !isset($source['second'])) {
218 return;
219 }
220 $hour = isset($source['hour']) ? (int)$source['hour'] : 0;
221 $minute = isset($source['minute']) ? (int)$source['minute'] : 0;
222 $second = isset($source['second']) ? (int)$source['second'] : 0;
223 $date->setTime($hour, $minute, $second);
224 }
225 }