4
|
1 <?php
|
|
2
|
|
3 namespace Sabre\VObject;
|
|
4
|
|
5 /**
|
|
6 * DateTimeParser
|
|
7 *
|
|
8 * This class is responsible for parsing the several different date and time
|
|
9 * formats iCalendar and vCards have.
|
|
10 *
|
|
11 * @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
|
12 * @author Evert Pot (http://evertpot.com/)
|
|
13 * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
|
14 */
|
|
15 class DateTimeParser {
|
|
16
|
|
17 /**
|
|
18 * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
|
|
19 *
|
|
20 * Specifying a reference timezone is optional. It will only be used
|
|
21 * if the non-UTC format is used. The argument is used as a reference, the
|
|
22 * returned DateTime object will still be in the UTC timezone.
|
|
23 *
|
|
24 * @param string $dt
|
|
25 * @param DateTimeZone $tz
|
|
26 * @return DateTime
|
|
27 */
|
|
28 static public function parseDateTime($dt,\DateTimeZone $tz = null) {
|
|
29
|
|
30 // Format is YYYYMMDD + "T" + hhmmss
|
|
31 $result = preg_match('/^([1-4][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches);
|
|
32
|
|
33 if (!$result) {
|
|
34 throw new \LogicException('The supplied iCalendar datetime value is incorrect: ' . $dt);
|
|
35 }
|
|
36
|
|
37 if ($matches[7]==='Z' || is_null($tz)) {
|
|
38 $tz = new \DateTimeZone('UTC');
|
|
39 }
|
|
40 $date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz);
|
|
41
|
|
42 // Still resetting the timezone, to normalize everything to UTC
|
|
43 $date->setTimeZone(new \DateTimeZone('UTC'));
|
|
44 return $date;
|
|
45
|
|
46 }
|
|
47
|
|
48 /**
|
|
49 * Parses an iCalendar (rfc5545) formatted date and returns a DateTime object
|
|
50 *
|
|
51 * @param string $date
|
|
52 * @return DateTime
|
|
53 */
|
|
54 static public function parseDate($date) {
|
|
55
|
|
56 // Format is YYYYMMDD
|
|
57 $result = preg_match('/^([1-4][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches);
|
|
58
|
|
59 if (!$result) {
|
|
60 throw new \LogicException('The supplied iCalendar date value is incorrect: ' . $date);
|
|
61 }
|
|
62
|
|
63 $date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new \DateTimeZone('UTC'));
|
|
64 return $date;
|
|
65
|
|
66 }
|
|
67
|
|
68 /**
|
|
69 * Parses an iCalendar (RFC5545) formatted duration value.
|
|
70 *
|
|
71 * This method will either return a DateTimeInterval object, or a string
|
|
72 * suitable for strtotime or DateTime::modify.
|
|
73 *
|
|
74 * @param string $duration
|
|
75 * @param bool $asString
|
|
76 * @return DateInterval|string
|
|
77 */
|
|
78 static public function parseDuration($duration, $asString = false) {
|
|
79
|
|
80 $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);
|
|
81 if (!$result) {
|
|
82 throw new \LogicException('The supplied iCalendar duration value is incorrect: ' . $duration);
|
|
83 }
|
|
84
|
|
85 if (!$asString) {
|
|
86 $invert = false;
|
|
87 if ($matches['plusminus']==='-') {
|
|
88 $invert = true;
|
|
89 }
|
|
90
|
|
91
|
|
92 $parts = array(
|
|
93 'week',
|
|
94 'day',
|
|
95 'hour',
|
|
96 'minute',
|
|
97 'second',
|
|
98 );
|
|
99 foreach($parts as $part) {
|
|
100 $matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0;
|
|
101 }
|
|
102
|
|
103
|
|
104 // We need to re-construct the $duration string, because weeks and
|
|
105 // days are not supported by DateInterval in the same string.
|
|
106 $duration = 'P';
|
|
107 $days = $matches['day'];
|
|
108 if ($matches['week']) {
|
|
109 $days+=$matches['week']*7;
|
|
110 }
|
|
111 if ($days)
|
|
112 $duration.=$days . 'D';
|
|
113
|
|
114 if ($matches['minute'] || $matches['second'] || $matches['hour']) {
|
|
115 $duration.='T';
|
|
116
|
|
117 if ($matches['hour'])
|
|
118 $duration.=$matches['hour'].'H';
|
|
119
|
|
120 if ($matches['minute'])
|
|
121 $duration.=$matches['minute'].'M';
|
|
122
|
|
123 if ($matches['second'])
|
|
124 $duration.=$matches['second'].'S';
|
|
125
|
|
126 }
|
|
127
|
|
128 if ($duration==='P') {
|
|
129 $duration = 'PT0S';
|
|
130 }
|
|
131 $iv = new \DateInterval($duration);
|
|
132 if ($invert) $iv->invert = true;
|
|
133
|
|
134 return $iv;
|
|
135
|
|
136 }
|
|
137
|
|
138
|
|
139
|
|
140 $parts = array(
|
|
141 'week',
|
|
142 'day',
|
|
143 'hour',
|
|
144 'minute',
|
|
145 'second',
|
|
146 );
|
|
147
|
|
148 $newDur = '';
|
|
149 foreach($parts as $part) {
|
|
150 if (isset($matches[$part]) && $matches[$part]) {
|
|
151 $newDur.=' '.$matches[$part] . ' ' . $part . 's';
|
|
152 }
|
|
153 }
|
|
154
|
|
155 $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur);
|
|
156 if ($newDur === '+') { $newDur = '+0 seconds'; };
|
|
157 return $newDur;
|
|
158
|
|
159 }
|
|
160
|
|
161 /**
|
|
162 * Parses either a Date or DateTime, or Duration value.
|
|
163 *
|
|
164 * @param string $date
|
|
165 * @param DateTimeZone|string $referenceTZ
|
|
166 * @return DateTime|DateInterval
|
|
167 */
|
|
168 static public function parse($date, $referenceTZ = null) {
|
|
169
|
|
170 if ($date[0]==='P' || ($date[0]==='-' && $date[1]==='P')) {
|
|
171 return self::parseDuration($date);
|
|
172 } elseif (strlen($date)===8) {
|
|
173 return self::parseDate($date);
|
|
174 } else {
|
|
175 return self::parseDateTime($date, $referenceTZ);
|
|
176 }
|
|
177
|
|
178 }
|
|
179
|
|
180
|
|
181 }
|