PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.4
Booking for Appointments and Events Calendar – Amelia v2.4.4
2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / vendor / sabre / vobject / lib / DateTimeParser.php
ameliabooking / vendor / sabre / vobject / lib Last commit date
Component 6 months ago ITip 6 months ago Parser 6 months ago Property 6 months ago Recur 6 months ago Splitter 6 months ago TimezoneGuesser 6 months ago timezonedata 1 year ago BirthdayCalendarGenerator.php 6 months ago Cli.php 6 months ago Component.php 6 months ago DateTimeParser.php 6 months ago Document.php 6 months ago ElementList.php 6 months ago EofException.php 6 months ago FreeBusyData.php 6 months ago FreeBusyGenerator.php 6 months ago InvalidDataException.php 6 months ago Node.php 6 months ago PHPUnitAssertions.php 6 months ago Parameter.php 6 months ago ParseException.php 6 months ago Property.php 6 months ago Reader.php 6 months ago Settings.php 6 months ago StringUtil.php 6 months ago TimeZoneUtil.php 6 months ago UUIDUtil.php 6 months ago VCardConverter.php 6 months ago Version.php 6 months ago Writer.php 6 months ago
DateTimeParser.php
561 lines
1 <?php
2
3 namespace AmeliaVendor\Sabre\VObject;
4
5 use DateInterval;
6 use DateTimeImmutable;
7 use DateTimeZone;
8
9 /**
10 * DateTimeParser.
11 *
12 * This class is responsible for parsing the several different date and time
13 * formats iCalendar and vCards have.
14 *
15 * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
16 * @author Evert Pot (http://evertpot.com/)
17 * @license http://sabre.io/license/ Modified BSD License
18 */
19 class DateTimeParser
20 {
21 /**
22 * Parses an iCalendar (rfc5545) formatted datetime and returns a
23 * DateTimeImmutable object.
24 *
25 * Specifying a reference timezone is optional. It will only be used
26 * if the non-UTC format is used. The argument is used as a reference, the
27 * returned DateTimeImmutable object will still be in the UTC timezone.
28 *
29 * @param string $dt
30 * @param DateTimeZone $tz
31 *
32 * @return DateTimeImmutable
33 */
34 public static 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 InvalidDataException('The supplied iCalendar datetime value is incorrect: '.$dt);
41 }
42
43 if ('Z' === $matches[7] || is_null($tz)) {
44 $tz = new DateTimeZone('UTC');
45 }
46
47 try {
48 $date = new DateTimeImmutable($matches[1].'-'.$matches[2].'-'.$matches[3].' '.$matches[4].':'.$matches[5].':'.$matches[6], $tz);
49 } catch (\Exception $e) {
50 throw new InvalidDataException('The supplied iCalendar datetime value is incorrect: '.$dt);
51 }
52
53 return $date;
54 }
55
56 /**
57 * Parses an iCalendar (rfc5545) formatted date and returns a DateTimeImmutable object.
58 *
59 * @param string $date
60 * @param DateTimeZone $tz
61 *
62 * @return DateTimeImmutable
63 */
64 public static function parseDate($date, ?DateTimeZone $tz = null)
65 {
66 // Format is YYYYMMDD
67 $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])$/', $date, $matches);
68
69 if (!$result) {
70 throw new InvalidDataException('The supplied iCalendar date value is incorrect: '.$date);
71 }
72
73 if (is_null($tz)) {
74 $tz = new DateTimeZone('UTC');
75 }
76
77 try {
78 $date = new DateTimeImmutable($matches[1].'-'.$matches[2].'-'.$matches[3], $tz);
79 } catch (\Exception $e) {
80 throw new InvalidDataException('The supplied iCalendar date value is incorrect: '.$date);
81 }
82
83 return $date;
84 }
85
86 /**
87 * Parses an iCalendar (RFC5545) formatted duration value.
88 *
89 * This method will either return a DateTimeInterval object, or a string
90 * suitable for strtotime or DateTime::modify.
91 *
92 * @param string $duration
93 * @param bool $asString
94 *
95 * @return DateInterval|string
96 */
97 public static function parseDuration($duration, $asString = false)
98 {
99 $result = preg_match('/^(?<plusminus>\+|-)?P((?<week>\d+)W)?((?<day>\d+)D)?(T((?<hour>\d+)H)?((?<minute>\d+)M)?((?<second>\d+)S)?)?$/', $duration, $matches);
100 if (!$result) {
101 throw new InvalidDataException('The supplied iCalendar duration value is incorrect: '.$duration);
102 }
103
104 if (!$asString) {
105 $invert = false;
106
107 if (isset($matches['plusminus']) && '-' === $matches['plusminus']) {
108 $invert = true;
109 }
110
111 $parts = [
112 'week',
113 'day',
114 'hour',
115 'minute',
116 'second',
117 ];
118
119 foreach ($parts as $part) {
120 $matches[$part] = isset($matches[$part]) && $matches[$part] ? (int) $matches[$part] : 0;
121 }
122
123 // We need to re-construct the $duration string, because weeks and
124 // days are not supported by DateInterval in the same string.
125 $duration = 'P';
126 $days = $matches['day'];
127
128 if ($matches['week']) {
129 $days += $matches['week'] * 7;
130 }
131
132 if ($days) {
133 $duration .= $days.'D';
134 }
135
136 if ($matches['minute'] || $matches['second'] || $matches['hour']) {
137 $duration .= 'T';
138
139 if ($matches['hour']) {
140 $duration .= $matches['hour'].'H';
141 }
142
143 if ($matches['minute']) {
144 $duration .= $matches['minute'].'M';
145 }
146
147 if ($matches['second']) {
148 $duration .= $matches['second'].'S';
149 }
150 }
151
152 if ('P' === $duration) {
153 $duration = 'PT0S';
154 }
155
156 $iv = new DateInterval($duration);
157
158 if ($invert) {
159 $iv->invert = true;
160 }
161
162 return $iv;
163 }
164
165 $parts = [
166 'week',
167 'day',
168 'hour',
169 'minute',
170 'second',
171 ];
172
173 $newDur = '';
174
175 foreach ($parts as $part) {
176 if (isset($matches[$part]) && $matches[$part]) {
177 $newDur .= ' '.$matches[$part].' '.$part.'s';
178 }
179 }
180
181 $newDur = ('-' === $matches['plusminus'] ? '-' : '+').trim($newDur);
182
183 if ('+' === $newDur) {
184 $newDur = '+0 seconds';
185 }
186
187 return $newDur;
188 }
189
190 /**
191 * Parses either a Date or DateTime, or Duration value.
192 *
193 * @param string $date
194 * @param DateTimeZone|string $referenceTz
195 *
196 * @return DateTimeImmutable|DateInterval
197 */
198 public static function parse($date, $referenceTz = null)
199 {
200 if ('P' === $date[0] || ('-' === $date[0] && 'P' === $date[1])) {
201 return self::parseDuration($date);
202 } elseif (8 === strlen($date)) {
203 return self::parseDate($date, $referenceTz);
204 } else {
205 return self::parseDateTime($date, $referenceTz);
206 }
207 }
208
209 /**
210 * This method parses a vCard date and or time value.
211 *
212 * This can be used for the DATE, DATE-TIME, TIMESTAMP and
213 * DATE-AND-OR-TIME value.
214 *
215 * This method returns an array, not a DateTime value.
216 *
217 * The elements in the array are in the following order:
218 * year, month, date, hour, minute, second, timezone
219 *
220 * Almost any part of the string may be omitted. It's for example legal to
221 * just specify seconds, leave out the year, etc.
222 *
223 * Timezone is either returned as 'Z' or as '+0800'
224 *
225 * For any non-specified values null is returned.
226 *
227 * List of date formats that are supported:
228 * YYYY
229 * YYYY-MM
230 * YYYYMMDD
231 * --MMDD
232 * ---DD
233 *
234 * YYYY-MM-DD
235 * --MM-DD
236 * ---DD
237 *
238 * List of supported time formats:
239 *
240 * HH
241 * HHMM
242 * HHMMSS
243 * -MMSS
244 * --SS
245 *
246 * HH
247 * HH:MM
248 * HH:MM:SS
249 * -MM:SS
250 * --SS
251 *
252 * A full basic-format date-time string looks like :
253 * 20130603T133901
254 *
255 * A full extended-format date-time string looks like :
256 * 2013-06-03T13:39:01
257 *
258 * Times may be postfixed by a timezone offset. This can be either 'Z' for
259 * UTC, or a string like -0500 or +1100.
260 *
261 * @param string $date
262 *
263 * @return array
264 */
265 public static function parseVCardDateTime($date)
266 {
267 $regex = '/^
268 (?: # date part
269 (?:
270 (?: (?<year> [0-9]{4}) (?: -)?| --)
271 (?<month> [0-9]{2})?
272 |---)
273 (?<date> [0-9]{2})?
274 )?
275 (?:T # time part
276 (?<hour> [0-9]{2} | -)
277 (?<minute> [0-9]{2} | -)?
278 (?<second> [0-9]{2})?
279
280 (?: \.[0-9]{3})? # milliseconds
281 (?P<timezone> # timezone offset
282
283 Z | (?: \+|-)(?: [0-9]{4})
284
285 )?
286
287 )?
288 $/x';
289
290 if (!preg_match($regex, $date, $matches)) {
291 // Attempting to parse the extended format.
292 $regex = '/^
293 (?: # date part
294 (?: (?<year> [0-9]{4}) - | -- )
295 (?<month> [0-9]{2}) -
296 (?<date> [0-9]{2})
297 )?
298 (?:T # time part
299
300 (?: (?<hour> [0-9]{2}) : | -)
301 (?: (?<minute> [0-9]{2}) : | -)?
302 (?<second> [0-9]{2})?
303
304 (?: \.[0-9]{3})? # milliseconds
305 (?P<timezone> # timezone offset
306
307 Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2})
308
309 )?
310
311 )?
312 $/x';
313
314 if (!preg_match($regex, $date, $matches)) {
315 throw new InvalidDataException('Invalid vCard date-time string: '.$date);
316 }
317 }
318 $parts = [
319 'year',
320 'month',
321 'date',
322 'hour',
323 'minute',
324 'second',
325 'timezone',
326 ];
327
328 $result = [];
329 foreach ($parts as $part) {
330 if (empty($matches[$part])) {
331 $result[$part] = null;
332 } elseif ('-' === $matches[$part] || '--' === $matches[$part]) {
333 $result[$part] = null;
334 } else {
335 $result[$part] = $matches[$part];
336 }
337 }
338
339 return $result;
340 }
341
342 /**
343 * This method parses a vCard TIME value.
344 *
345 * This method returns an array, not a DateTime value.
346 *
347 * The elements in the array are in the following order:
348 * hour, minute, second, timezone
349 *
350 * Almost any part of the string may be omitted. It's for example legal to
351 * just specify seconds, leave out the hour etc.
352 *
353 * Timezone is either returned as 'Z' or as '+08:00'
354 *
355 * For any non-specified values null is returned.
356 *
357 * List of supported time formats:
358 *
359 * HH
360 * HHMM
361 * HHMMSS
362 * -MMSS
363 * --SS
364 *
365 * HH
366 * HH:MM
367 * HH:MM:SS
368 * -MM:SS
369 * --SS
370 *
371 * A full basic-format time string looks like :
372 * 133901
373 *
374 * A full extended-format time string looks like :
375 * 13:39:01
376 *
377 * Times may be postfixed by a timezone offset. This can be either 'Z' for
378 * UTC, or a string like -0500 or +11:00.
379 *
380 * @param string $date
381 *
382 * @return array
383 */
384 public static function parseVCardTime($date)
385 {
386 $regex = '/^
387 (?<hour> [0-9]{2} | -)
388 (?<minute> [0-9]{2} | -)?
389 (?<second> [0-9]{2})?
390
391 (?: \.[0-9]{3})? # milliseconds
392 (?P<timezone> # timezone offset
393
394 Z | (?: \+|-)(?: [0-9]{4})
395
396 )?
397 $/x';
398
399 if (!preg_match($regex, $date, $matches)) {
400 // Attempting to parse the extended format.
401 $regex = '/^
402 (?: (?<hour> [0-9]{2}) : | -)
403 (?: (?<minute> [0-9]{2}) : | -)?
404 (?<second> [0-9]{2})?
405
406 (?: \.[0-9]{3})? # milliseconds
407 (?P<timezone> # timezone offset
408
409 Z | (?: \+|-)(?: [0-9]{2}:[0-9]{2})
410
411 )?
412 $/x';
413
414 if (!preg_match($regex, $date, $matches)) {
415 throw new InvalidDataException('Invalid vCard time string: '.$date);
416 }
417 }
418 $parts = [
419 'hour',
420 'minute',
421 'second',
422 'timezone',
423 ];
424
425 $result = [];
426 foreach ($parts as $part) {
427 if (empty($matches[$part])) {
428 $result[$part] = null;
429 } elseif ('-' === $matches[$part]) {
430 $result[$part] = null;
431 } else {
432 $result[$part] = $matches[$part];
433 }
434 }
435
436 return $result;
437 }
438
439 /**
440 * This method parses a vCard date and or time value.
441 *
442 * This can be used for the DATE, DATE-TIME and
443 * DATE-AND-OR-TIME value.
444 *
445 * This method returns an array, not a DateTime value.
446 * The elements in the array are in the following order:
447 * year, month, date, hour, minute, second, timezone
448 * Almost any part of the string may be omitted. It's for example legal to
449 * just specify seconds, leave out the year, etc.
450 *
451 * Timezone is either returned as 'Z' or as '+0800'
452 *
453 * For any non-specified values null is returned.
454 *
455 * List of date formats that are supported:
456 * 20150128
457 * 2015-01
458 * --01
459 * --0128
460 * ---28
461 *
462 * List of supported time formats:
463 * 13
464 * 1353
465 * 135301
466 * -53
467 * -5301
468 * --01 (unreachable, see the tests)
469 * --01Z
470 * --01+1234
471 *
472 * List of supported date-time formats:
473 * 20150128T13
474 * --0128T13
475 * ---28T13
476 * ---28T1353
477 * ---28T135301
478 * ---28T13Z
479 * ---28T13+1234
480 *
481 * See the regular expressions for all the possible patterns.
482 *
483 * Times may be postfixed by a timezone offset. This can be either 'Z' for
484 * UTC, or a string like -0500 or +1100.
485 *
486 * @param string $date
487 *
488 * @return array
489 */
490 public static function parseVCardDateAndOrTime($date)
491 {
492 // \d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d
493 $valueDate = '/^(?J)(?:'.
494 '(?<year>\d{4})(?<month>\d\d)(?<date>\d\d)'.
495 '|(?<year>\d{4})-(?<month>\d\d)'.
496 '|--(?<month>\d\d)(?<date>\d\d)?'.
497 '|---(?<date>\d\d)'.
498 ')$/';
499
500 // (\d\d(\d\d(\d\d)?)?|-\d\d(\d\d)?|--\d\d)(Z|[+\-]\d\d(\d\d)?)?
501 $valueTime = '/^(?J)(?:'.
502 '((?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?'.
503 '|-(?<minute>\d\d)(?<second>\d\d)?'.
504 '|--(?<second>\d\d))'.
505 '(?<timezone>(Z|[+\-]\d\d(\d\d)?))?'.
506 ')$/';
507
508 // (\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?(Z|[+\-]\d\d(\d\d?)?
509 $valueDateTime = '/^(?:'.
510 '((?<year0>\d{4})(?<month0>\d\d)(?<date0>\d\d)'.
511 '|--(?<month1>\d\d)(?<date1>\d\d)'.
512 '|---(?<date2>\d\d))'.
513 'T'.
514 '(?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?'.
515 '(?<timezone>(Z|[+\-]\d\d(\d\d?)))?'.
516 ')$/';
517
518 // date-and-or-time is date | date-time | time
519 // in this strict order.
520
521 if (0 === preg_match($valueDate, $date, $matches)
522 && 0 === preg_match($valueDateTime, $date, $matches)
523 && 0 === preg_match($valueTime, $date, $matches)) {
524 throw new InvalidDataException('Invalid vCard date-time string: '.$date);
525 }
526
527 $parts = [
528 'year' => null,
529 'month' => null,
530 'date' => null,
531 'hour' => null,
532 'minute' => null,
533 'second' => null,
534 'timezone' => null,
535 ];
536
537 // The $valueDateTime expression has a bug with (?J) so we simulate it.
538 $parts['date0'] = &$parts['date'];
539 $parts['date1'] = &$parts['date'];
540 $parts['date2'] = &$parts['date'];
541 $parts['month0'] = &$parts['month'];
542 $parts['month1'] = &$parts['month'];
543 $parts['year0'] = &$parts['year'];
544
545 foreach ($parts as $part => &$value) {
546 if (!empty($matches[$part])) {
547 $value = $matches[$part];
548 }
549 }
550
551 unset($parts['date0']);
552 unset($parts['date1']);
553 unset($parts['date2']);
554 unset($parts['month0']);
555 unset($parts['month1']);
556 unset($parts['year0']);
557
558 return $parts;
559 }
560 }
561