4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Form\Extension\Core\DataTransformer
;
14 use Symfony\Component\Form\Exception\TransformationFailedException
;
15 use Symfony\Component\Form\Exception\UnexpectedTypeException
;
18 * Transforms between a date string and a DateTime object
20 * @author Bernhard Schussek <bschussek@gmail.com>
21 * @author Florian Eckerstorfer <florian@eckerstorfer.org>
23 class DateTimeToStringTransformer
extends BaseDateTimeTransformer
26 * Format used for generating strings
29 private $generateFormat;
32 * Format used for parsing strings
34 * Different than the {@link $generateFormat} because formats for parsing
35 * support additional characters in PHP that are not supported for
43 * Whether to parse by appending a pipe "|" to the parse format.
45 * This only works as of PHP 5.3.7.
49 private $parseUsingPipe;
52 * Transforms a \DateTime instance to a string
54 * @see \DateTime::format() for supported formats
56 * @param string $inputTimezone The name of the input timezone
57 * @param string $outputTimezone The name of the output timezone
58 * @param string $format The date format
59 * @param Boolean $parseUsingPipe Whether to parse by appending a pipe "|" to the parse format
61 * @throws UnexpectedTypeException if a timezone is not a string
63 public function __construct($inputTimezone = null, $outputTimezone = null, $format = 'Y-m-d H:i:s', $parseUsingPipe = null)
65 parent
::__construct($inputTimezone, $outputTimezone);
67 $this->generateFormat
= $this->parseFormat
= $format;
69 // The pipe in the parser pattern only works as of PHP 5.3.7
70 // See http://bugs.php.net/54316
71 $this->parseUsingPipe
= null === $parseUsingPipe
72 ? version_compare(phpversion(), '5.3.7', '>=')
75 // See http://php.net/manual/en/datetime.createfromformat.php
76 // The character "|" in the format makes sure that the parts of a date
77 // that are *not* specified in the format are reset to the corresponding
78 // values from 1970-01-01 00:00:00 instead of the current time.
79 // Without "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 12:32:47",
80 // where the time corresponds to the current server time.
81 // With "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 00:00:00",
82 // which is at least deterministic and thus used here.
83 if ($this->parseUsingPipe
&& false === strpos($this->parseFormat
, '|')) {
84 $this->parseFormat
.= '|';
89 * Transforms a DateTime object into a date string with the configured format
92 * @param \DateTime $value A DateTime object
94 * @return string A value as produced by PHP's date() function
96 * @throws TransformationFailedException If the given value is not a \DateTime
97 * instance or if the output timezone
100 public function transform($value)
102 if (null === $value) {
106 if (!$value instanceof \DateTime
) {
107 throw new TransformationFailedException('Expected a \DateTime.');
110 $value = clone $value;
112 $value->setTimezone(new \
DateTimeZone($this->outputTimezone
));
113 } catch (\Exception
$e) {
114 throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
117 return $value->format($this->generateFormat
);
121 * Transforms a date string in the configured timezone into a DateTime object.
123 * @param string $value A value as produced by PHP's date() function
125 * @return \DateTime An instance of \DateTime
127 * @throws TransformationFailedException If the given value is not a string,
128 * if the date could not be parsed or
129 * if the input timezone is not supported.
131 public function reverseTransform($value)
137 if (!is_string($value)) {
138 throw new TransformationFailedException('Expected a string.');
142 $outputTz = new \
DateTimeZone($this->outputTimezone
);
143 $dateTime = \DateTime
::createFromFormat($this->parseFormat
, $value, $outputTz);
145 $lastErrors = \DateTime
::getLastErrors();
147 if (0 < $lastErrors['warning_count'] || 0 < $lastErrors['error_count']) {
148 throw new TransformationFailedException(
149 implode(', ', array_merge(
150 array_values($lastErrors['warnings']),
151 array_values($lastErrors['errors'])
156 // On PHP versions < 5.3.7 we need to emulate the pipe operator
157 // and reset parts not given in the format to their equivalent
158 // of the UNIX base timestamp.
159 if (!$this->parseUsingPipe
) {
160 list($year, $month, $day, $hour, $minute, $second) = explode('-', $dateTime->format('Y-m-d-H-i-s'));
162 // Check which of the date parts are present in the pattern
166 '(?P<month>[FMmn])|' .
168 '(?P<hour>[ghGH])|' .
171 '(?P<dayofyear>z)|' .
172 '(?P<timestamp>U)|' .
173 '[^djDlFMmnYyghGHiszU]' .
179 // preg_match() does not guarantee to set all indices, so
180 // set them unless given
181 $matches = array_merge(array(
188 'dayofyear' => false,
189 'timestamp' => false,
192 // Reset all parts that don't exist in the format to the
193 // corresponding part of the UNIX base timestamp
194 if (!$matches['timestamp']) {
195 if (!$matches['dayofyear']) {
196 if (!$matches['day']) {
199 if (!$matches['month']) {
203 if (!$matches['year']) {
206 if (!$matches['hour']) {
209 if (!$matches['minute']) {
212 if (!$matches['second']) {
215 $dateTime->setDate($year, $month, $day);
216 $dateTime->setTime($hour, $minute, $second);
220 if ($this->inputTimezone
!== $this->outputTimezone
) {
221 $dateTime->setTimeZone(new \
DateTimeZone($this->inputTimezone
));
223 } catch (TransformationFailedException
$e) {
225 } catch (\Exception
$e) {
226 throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);