@@ -117,20 +117,29 @@ public function reverseTransform($value)
117
117
return ;
118
118
}
119
119
120
- $ timestamp = $ this ->getIntlDateFormatter ()->parse ($ value );
120
+ // date-only patterns require parsing to be done in UTC, as midnight might not exist in the local timezone due
121
+ // to DST changes
122
+ $ dateOnly = $ this ->isPatternDateOnly ();
123
+
124
+ $ timestamp = $ this ->getIntlDateFormatter ($ dateOnly )->parse ($ value );
121
125
122
126
if (intl_get_error_code () != 0 ) {
123
127
throw new TransformationFailedException (intl_get_error_message ());
124
128
}
125
129
126
130
try {
127
- // read timestamp into DateTime object - the formatter delivers in UTC
128
- $ dateTime = new \DateTime (sprintf ('@%s ' , $ timestamp ));
131
+ if ($ dateOnly ) {
132
+ // we only care about year-month-date, which has been delivered as a timestamp pointing to UTC midnight
133
+ return new \DateTime (gmdate ('Y-m-d ' , $ timestamp ), new \DateTimeZone ($ this ->inputTimezone ));
134
+ }
135
+
136
+ // read timestamp into DateTime object - the formatter delivers a timestamp
137
+ $ dateTime = new \DateTime (sprintf ('@%s ' , $ timestamp ), new \DateTimeZone ($ this ->outputTimezone ));
129
138
} catch (\Exception $ e ) {
130
139
throw new TransformationFailedException ($ e ->getMessage (), $ e ->getCode (), $ e );
131
140
}
132
141
133
- if (' UTC ' !== $ this ->inputTimezone ) {
142
+ if ($ this -> outputTimezone !== $ this ->inputTimezone ) {
134
143
$ dateTime ->setTimezone (new \DateTimeZone ($ this ->inputTimezone ));
135
144
}
136
145
@@ -140,15 +149,17 @@ public function reverseTransform($value)
140
149
/**
141
150
* Returns a preconfigured IntlDateFormatter instance.
142
151
*
152
+ * @param bool $ignoreTimezone Use UTC regardless of the configured timezone.
153
+ *
143
154
* @return \IntlDateFormatter
144
155
*
145
156
* @throws TransformationFailedException in case the date formatter can not be constructed.
146
157
*/
147
- protected function getIntlDateFormatter ()
158
+ protected function getIntlDateFormatter ($ ignoreTimezone = false )
148
159
{
149
160
$ dateFormat = $ this ->dateFormat ;
150
161
$ timeFormat = $ this ->timeFormat ;
151
- $ timezone = $ this ->outputTimezone ;
162
+ $ timezone = $ ignoreTimezone ? ' UTC ' : $ this ->outputTimezone ;
152
163
$ calendar = $ this ->calendar ;
153
164
$ pattern = $ this ->pattern ;
154
165
@@ -163,4 +174,24 @@ protected function getIntlDateFormatter()
163
174
164
175
return $ intlDateFormatter ;
165
176
}
177
+
178
+ /**
179
+ * Checks if the pattern contains only a date.
180
+ *
181
+ * @param string $pattern The input pattern
182
+ *
183
+ * @return bool
184
+ */
185
+ protected function isPatternDateOnly ()
186
+ {
187
+ if (null === $ this ->pattern ) {
188
+ return false ;
189
+ }
190
+
191
+ // strip escaped text
192
+ $ pattern = preg_replace ("#'(.*?)'# " , '' , $ this ->pattern );
193
+
194
+ // check for the absence of time-related placeholders
195
+ return 0 === preg_match ('#[ahHkKmsSAzZOvVxX]# ' , $ pattern );
196
+ }
166
197
}
0 commit comments