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 } |
