Mercurial > hg > rc1
comparison vendor/sabre/vobject/lib/DateTimeParser.php @ 7:430dbd5346f7
vendor sabre as distributed
author | Charlie Root |
---|---|
date | Sat, 13 Jan 2018 09:06:10 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
6:cec75ba50afc | 7:430dbd5346f7 |
---|---|
1 <?php | |
2 | |
3 namespace Sabre\VObject; | |
4 | |
5 use DateTime; | |
6 use DateTimeZone; | |
7 use DateInterval; | |
8 use InvalidArgumentException; | |
9 use LogicException; | |
10 | |
11 /** | |
12 * DateTimeParser | |
13 * | |
14 * This class is responsible for parsing the several different date and time | |
15 * formats iCalendar and vCards have. | |
16 * | |
17 * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/). | |
18 * @author Evert Pot (http://evertpot.com/) | |
19 * @license http://sabre.io/license/ Modified BSD License | |
20 */ | |
21 class DateTimeParser { | |
22 | |
23 /** | |
24 * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object | |
25 * | |
26 * Specifying a reference timezone is optional. It will only be used | |
27 * if the non-UTC format is used. The argument is used as a reference, the | |
28 * returned DateTime object will still be in the UTC timezone. | |
29 * | |
30 * @param string $dt | |
31 * @param DateTimeZone $tz | |
32 * @return DateTime | |
33 */ | |
34 static public function parseDateTime($dt, DateTimeZone $tz = null) { | |
35 | |
36 // Format is YYYYMMDD + "T" + hhmmss | |
37 $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches); | |
38 | |
39 if (!$result) { | |
40 throw new LogicException('The supplied iCalendar datetime value is incorrect: ' . $dt); | |
41 } | |
42 | |
43 if ($matches[7]==='Z' || is_null($tz)) { | |
44 $tz = new DateTimeZone('UTC'); | |
45 } | |
46 $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz); | |
47 | |
48 // Still resetting the timezone, to normalize everything to UTC | |
49 // $date->setTimeZone(new \DateTimeZone('UTC')); | |
50 return $date; | |
51 | |
52 } | |
53 | |
54 /** | |
55 * Parses an iCalendar (rfc5545) formatted date and returns a DateTime object. | |
56 * | |
57 * @param string $date | |
58 * @param DateTimeZone $tz | |
59 * @return DateTime | |
60 */ | |
61 static public function parseDate($date, DateTimeZone $tz = null) { | |
62 | |
63 // Format is YYYYMMDD | |
64 $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])$/',$date,$matches); | |
65 | |
66 if (!$result) { | |
67 throw new LogicException('The supplied iCalendar date value is incorrect: ' . $date); | |
68 } | |
69 | |
70 if (is_null($tz)) { | |
71 $tz = new DateTimeZone('UTC'); | |
72 } | |
73 | |
74 $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], $tz); | |
75 return $date; | |
76 | |
77 } | |
78 | |
79 /** | |
80 * Parses an iCalendar (RFC5545) formatted duration value. | |
81 * | |
82 * This method will either return a DateTimeInterval object, or a string | |
83 * suitable for strtotime or DateTime::modify. | |
84 * | |
85 * @param string $duration | |
86 * @param bool $asString | |
87 * @return DateInterval|string | |
88 */ | |
89 static public function parseDuration($duration, $asString = false) { | |
90 | |
91 $result = preg_match('/^(?P<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $duration, $matches); | |
92 if (!$result) { | |
93 throw new LogicException('The supplied iCalendar duration value is incorrect: ' . $duration); | |
94 } | |
95 | |
96 if (!$asString) { | |
97 $invert = false; | |
98 if ($matches['plusminus']==='-') { | |
99 $invert = true; | |
100 } | |
101 | |
102 | |
103 $parts = array( | |
104 'week', | |
105 'day', | |
106 'hour', | |
107 'minute', | |
108 'second', | |
109 ); | |
110 foreach($parts as $part) { | |
111 $matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0; | |
112 } | |
113 | |
114 | |
115 // We need to re-construct the $duration string, because weeks and | |
116 // days are not supported by DateInterval in the same string. | |
117 $duration = 'P'; | |
118 $days = $matches['day']; | |
119 if ($matches['week']) { | |
120 $days+=$matches['week']*7; | |
121 } | |
122 if ($days) | |
123 $duration.=$days . 'D'; | |
124 | |
125 if ($matches['minute'] || $matches['second'] || $matches['hour']) { | |
126 $duration.='T'; | |
127 | |
128 if ($matches['hour']) | |
129 $duration.=$matches['hour'].'H'; | |
130 | |
131 if ($matches['minute']) | |
132 $duration.=$matches['minute'].'M'; | |
133 | |
134 if ($matches['second']) | |
135 $duration.=$matches['second'].'S'; | |
136 | |
137 } | |
138 | |
139 if ($duration==='P') { | |
140 $duration = 'PT0S'; | |
141 } | |
142 $iv = new DateInterval($duration); | |
143 if ($invert) $iv->invert = true; | |
144 | |
145 return $iv; | |
146 | |
147 } | |
148 | |
149 | |
150 | |
151 $parts = array( | |
152 'week', | |
153 'day', | |
154 'hour', | |
155 'minute', | |
156 'second', | |
157 ); | |
158 | |
159 $newDur = ''; | |
160 foreach($parts as $part) { | |
161 if (isset($matches[$part]) && $matches[$part]) { | |
162 $newDur.=' '.$matches[$part] . ' ' . $part . 's'; | |
163 } | |
164 } | |
165 | |
166 $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur); | |
167 if ($newDur === '+') { | |
168 $newDur = '+0 seconds'; | |
169 }; | |
170 return $newDur; | |
171 | |
172 } | |
173 | |
174 /** | |
175 * Parses either a Date or DateTime, or Duration value. | |
176 * | |
177 * @param string $date | |
178 * @param DateTimeZone|string $referenceTz | |
179 * @return DateTime|DateInterval | |
180 */ | |
181 static public function parse($date, $referenceTz = null) { | |
182 | |
183 if ($date[0]==='P' || ($date[0]==='-' && $date[1]==='P')) { | |
184 return self::parseDuration($date); | |
185 } elseif (strlen($date)===8) { | |
186 return self::parseDate($date, $referenceTz); | |
187 } else { | |
188 return self::parseDateTime($date, $referenceTz); | |
189 } | |
190 | |
191 } | |
192 | |
193 /** | |
194 * This method parses a vCard date and or time value. | |
195 * | |
196 * This can be used for the DATE, DATE-TIME, TIMESTAMP and | |
197 * DATE-AND-OR-TIME value. | |
198 * | |
199 * This method returns an array, not a DateTime value. | |
200 * | |
201 * The elements in the array are in the following order: | |
202 * year, month, date, hour, minute, second, timezone | |
203 * | |
204 * Almost any part of the string may be omitted. It's for example legal to | |
205 * just specify seconds, leave out the year, etc. | |
206 * | |
207 * Timezone is either returned as 'Z' or as '+08:00' | |
208 * | |
209 * For any non-specified values null is returned. | |
210 * | |
211 * List of date formats that are supported: | |
212 * YYYY | |
213 * YYYY-MM | |
214 * YYYYMMDD | |
215 * --MMDD | |
216 * ---DD | |
217 * | |
218 * YYYY-MM-DD | |
219 * --MM-DD | |
220 * ---DD | |
221 * | |
222 * List of supported time formats: | |
223 * | |
224 * HH | |
225 * HHMM | |
226 * HHMMSS | |
227 * -MMSS | |
228 * --SS | |
229 * | |
230 * HH | |
231 * HH:MM | |
232 * HH:MM:SS | |
233 * -MM:SS | |
234 * --SS | |
235 * | |
236 * A full basic-format date-time string looks like : | |
237 * 20130603T133901 | |
238 * | |
239 * A full extended-format date-time string looks like : | |
240 * 2013-06-03T13:39:01 | |
241 * | |
242 * Times may be postfixed by a timezone offset. This can be either 'Z' for | |
243 * UTC, or a string like -0500 or +1100. | |
244 * | |
245 * @param string $date | |
246 * @return array | |
247 */ | |
248 static public function parseVCardDateTime($date) { | |
249 | |
250 $regex = '/^ | |
251 (?: # date part | |
252 (?: | |
253 (?: (?P<year> [0-9]{4}) (?: -)?| --) | |
254 (?P<month> [0-9]{2})? | |
255 |---) | |
256 (?P<date> [0-9]{2})? | |
257 )? | |
258 (?:T # time part | |
259 (?P<hour> [0-9]{2} | -) | |
260 (?P<minute> [0-9]{2} | -)? | |
261 (?P<second> [0-9]{2})? | |
262 | |
263 (?P<timezone> # timezone offset | |
264 | |
265 Z | (?: \+|-)(?: [0-9]{4}) | |
266 | |
267 )? | |
268 | |
269 )? | |
270 $/x'; | |
271 | |
272 | |
273 if (!preg_match($regex, $date, $matches)) { | |
274 | |
275 // Attempting to parse the extended format. | |
276 $regex = '/^ | |
277 (?: # date part | |
278 (?: (?P<year> [0-9]{4}) - | -- ) | |
279 (?P<month> [0-9]{2}) - | |
280 (?P<date> [0-9]{2}) | |
281 )? | |
282 (?:T # time part | |
283 | |
284 (?: (?P<hour> [0-9]{2}) : | -) | |
285 (?: (?P<minute> [0-9]{2}) : | -)? | |
286 (?P<second> [0-9]{2})? | |
287 | |
288 (?P<timezone> # timezone offset | |
289 | |
290 Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2}) | |
291 | |
292 )? | |
293 | |
294 )? | |
295 $/x'; | |
296 | |
297 if (!preg_match($regex, $date, $matches)) { | |
298 throw new InvalidArgumentException('Invalid vCard date-time string: ' . $date); | |
299 } | |
300 | |
301 } | |
302 $parts = array( | |
303 'year', | |
304 'month', | |
305 'date', | |
306 'hour', | |
307 'minute', | |
308 'second', | |
309 'timezone' | |
310 ); | |
311 | |
312 $result = array(); | |
313 foreach($parts as $part) { | |
314 | |
315 if (empty($matches[$part])) { | |
316 $result[$part] = null; | |
317 } elseif ($matches[$part] === '-' || $matches[$part] === '--') { | |
318 $result[$part] = null; | |
319 } else { | |
320 $result[$part] = $matches[$part]; | |
321 } | |
322 | |
323 } | |
324 | |
325 return $result; | |
326 | |
327 } | |
328 | |
329 /** | |
330 * This method parses a vCard TIME value. | |
331 * | |
332 * This method returns an array, not a DateTime value. | |
333 * | |
334 * The elements in the array are in the following order: | |
335 * hour, minute, second, timezone | |
336 * | |
337 * Almost any part of the string may be omitted. It's for example legal to | |
338 * just specify seconds, leave out the hour etc. | |
339 * | |
340 * Timezone is either returned as 'Z' or as '+08:00' | |
341 * | |
342 * For any non-specified values null is returned. | |
343 * | |
344 * List of supported time formats: | |
345 * | |
346 * HH | |
347 * HHMM | |
348 * HHMMSS | |
349 * -MMSS | |
350 * --SS | |
351 * | |
352 * HH | |
353 * HH:MM | |
354 * HH:MM:SS | |
355 * -MM:SS | |
356 * --SS | |
357 * | |
358 * A full basic-format time string looks like : | |
359 * 133901 | |
360 * | |
361 * A full extended-format time string looks like : | |
362 * 13:39:01 | |
363 * | |
364 * Times may be postfixed by a timezone offset. This can be either 'Z' for | |
365 * UTC, or a string like -0500 or +11:00. | |
366 * | |
367 * @param string $date | |
368 * @return array | |
369 */ | |
370 static public function parseVCardTime($date) { | |
371 | |
372 $regex = '/^ | |
373 (?P<hour> [0-9]{2} | -) | |
374 (?P<minute> [0-9]{2} | -)? | |
375 (?P<second> [0-9]{2})? | |
376 | |
377 (?P<timezone> # timezone offset | |
378 | |
379 Z | (?: \+|-)(?: [0-9]{4}) | |
380 | |
381 )? | |
382 $/x'; | |
383 | |
384 | |
385 if (!preg_match($regex, $date, $matches)) { | |
386 | |
387 // Attempting to parse the extended format. | |
388 $regex = '/^ | |
389 (?: (?P<hour> [0-9]{2}) : | -) | |
390 (?: (?P<minute> [0-9]{2}) : | -)? | |
391 (?P<second> [0-9]{2})? | |
392 | |
393 (?P<timezone> # timezone offset | |
394 | |
395 Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2}) | |
396 | |
397 )? | |
398 $/x'; | |
399 | |
400 if (!preg_match($regex, $date, $matches)) { | |
401 throw new InvalidArgumentException('Invalid vCard time string: ' . $date); | |
402 } | |
403 | |
404 } | |
405 $parts = array( | |
406 'hour', | |
407 'minute', | |
408 'second', | |
409 'timezone' | |
410 ); | |
411 | |
412 $result = array(); | |
413 foreach($parts as $part) { | |
414 | |
415 if (empty($matches[$part])) { | |
416 $result[$part] = null; | |
417 } elseif ($matches[$part] === '-') { | |
418 $result[$part] = null; | |
419 } else { | |
420 $result[$part] = $matches[$part]; | |
421 } | |
422 | |
423 } | |
424 | |
425 return $result; | |
426 | |
427 } | |
428 } |