comparison vendor/sabre/vobject/lib/Property/ICalendar/DateTime.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\Property\ICalendar;
4
5 use DateTimeZone;
6 use Sabre\VObject\Property;
7 use Sabre\VObject\DateTimeParser;
8 use Sabre\VObject\TimeZoneUtil;
9
10 /**
11 * DateTime property
12 *
13 * This object represents DATE-TIME values, as defined here:
14 *
15 * http://tools.ietf.org/html/rfc5545#section-3.3.4
16 *
17 * This particular object has a bit of hackish magic that it may also in some
18 * cases represent a DATE value. This is because it's a common usecase to be
19 * able to change a DATE-TIME into a DATE.
20 *
21 * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/).
22 * @author Evert Pot (http://evertpot.com/)
23 * @license http://sabre.io/license/ Modified BSD License
24 */
25 class DateTime extends Property {
26
27 /**
28 * In case this is a multi-value property. This string will be used as a
29 * delimiter.
30 *
31 * @var string|null
32 */
33 public $delimiter = ',';
34
35 /**
36 * Sets a multi-valued property.
37 *
38 * You may also specify DateTime objects here.
39 *
40 * @param array $parts
41 * @return void
42 */
43 public function setParts(array $parts) {
44
45 if (isset($parts[0]) && $parts[0] instanceof \DateTime) {
46 $this->setDateTimes($parts);
47 } else {
48 parent::setParts($parts);
49 }
50
51 }
52
53 /**
54 * Updates the current value.
55 *
56 * This may be either a single, or multiple strings in an array.
57 *
58 * Instead of strings, you may also use DateTime here.
59 *
60 * @param string|array|\DateTime $value
61 * @return void
62 */
63 public function setValue($value) {
64
65 if (is_array($value) && isset($value[0]) && $value[0] instanceof \DateTime) {
66 $this->setDateTimes($value);
67 } elseif ($value instanceof \DateTime) {
68 $this->setDateTimes(array($value));
69 } else {
70 parent::setValue($value);
71 }
72
73 }
74
75 /**
76 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
77 *
78 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
79 * not yet done, but parameters are not included.
80 *
81 * @param string $val
82 * @return void
83 */
84 public function setRawMimeDirValue($val) {
85
86 $this->setValue(explode($this->delimiter, $val));
87
88 }
89
90 /**
91 * Returns a raw mime-dir representation of the value.
92 *
93 * @return string
94 */
95 public function getRawMimeDirValue() {
96
97 return implode($this->delimiter, $this->getParts());
98
99 }
100
101 /**
102 * Returns true if this is a DATE-TIME value, false if it's a DATE.
103 *
104 * @return bool
105 */
106 public function hasTime() {
107
108 return strtoupper((string)$this['VALUE']) !== 'DATE';
109
110 }
111
112 /**
113 * Returns true if this is a floating DATE or DATE-TIME.
114 *
115 * Note that DATE is always floating.
116 */
117 public function isFloating() {
118
119 return
120 !$this->hasTime() ||
121 (
122 !isset($this['TZID']) &&
123 strpos($this->getValue(),'Z')===false
124 );
125
126 }
127
128 /**
129 * Returns a date-time value.
130 *
131 * Note that if this property contained more than 1 date-time, only the
132 * first will be returned. To get an array with multiple values, call
133 * getDateTimes.
134 *
135 * If no timezone information is known, because it's either an all-day
136 * property or floating time, we will use the DateTimeZone argument to
137 * figure out the exact date.
138 *
139 * @param DateTimeZone $timeZone
140 * @return \DateTime
141 */
142 public function getDateTime(DateTimeZone $timeZone = null) {
143
144 $dt = $this->getDateTimes($timeZone);
145 if (!$dt) return null;
146
147 return $dt[0];
148
149 }
150
151 /**
152 * Returns multiple date-time values.
153 *
154 * If no timezone information is known, because it's either an all-day
155 * property or floating time, we will use the DateTimeZone argument to
156 * figure out the exact date.
157 *
158 * @param DateTimeZone $timeZone
159 * @return \DateTime[]
160 */
161 public function getDateTimes(DateTimeZone $timeZone = null) {
162
163 // Does the property have a TZID?
164 $tzid = $this['TZID'];
165
166 if ($tzid) {
167 $timeZone = TimeZoneUtil::getTimeZone((string)$tzid, $this->root);
168 }
169
170 $dts = array();
171 foreach($this->getParts() as $part) {
172 $dts[] = DateTimeParser::parse($part, $timeZone);
173 }
174 return $dts;
175
176 }
177
178 /**
179 * Sets the property as a DateTime object.
180 *
181 * @param \DateTime $dt
182 * @param bool isFloating If set to true, timezones will be ignored.
183 * @return void
184 */
185 public function setDateTime(\DateTime $dt, $isFloating = false) {
186
187 $this->setDateTimes(array($dt), $isFloating);
188
189 }
190
191 /**
192 * Sets the property as multiple date-time objects.
193 *
194 * The first value will be used as a reference for the timezones, and all
195 * the otehr values will be adjusted for that timezone
196 *
197 * @param \DateTime[] $dt
198 * @param bool isFloating If set to true, timezones will be ignored.
199 * @return void
200 */
201 public function setDateTimes(array $dt, $isFloating = false) {
202
203 $values = array();
204
205 if($this->hasTime()) {
206
207 $tz = null;
208 $isUtc = false;
209
210 foreach($dt as $d) {
211
212 if ($isFloating) {
213 $values[] = $d->format('Ymd\\THis');
214 continue;
215 }
216 if (is_null($tz)) {
217 $tz = $d->getTimeZone();
218 $isUtc = in_array($tz->getName() , array('UTC', 'GMT', 'Z'));
219 if (!$isUtc) {
220 $this->offsetSet('TZID', $tz->getName());
221 }
222 } else {
223 $d->setTimeZone($tz);
224 }
225
226 if ($isUtc) {
227 $values[] = $d->format('Ymd\\THis\\Z');
228 } else {
229 $values[] = $d->format('Ymd\\THis');
230 }
231
232 }
233 if ($isUtc || $isFloating) {
234 $this->offsetUnset('TZID');
235 }
236
237 } else {
238
239 foreach($dt as $d) {
240
241 $values[] = $d->format('Ymd');
242
243 }
244 $this->offsetUnset('TZID');
245
246 }
247
248 $this->value = $values;
249
250 }
251
252 /**
253 * Returns the type of value.
254 *
255 * This corresponds to the VALUE= parameter. Every property also has a
256 * 'default' valueType.
257 *
258 * @return string
259 */
260 public function getValueType() {
261
262 return $this->hasTime()?'DATE-TIME':'DATE';
263
264 }
265
266 /**
267 * Returns the value, in the format it should be encoded for json.
268 *
269 * This method must always return an array.
270 *
271 * @return array
272 */
273 public function getJsonValue() {
274
275 $dts = $this->getDateTimes();
276 $hasTime = $this->hasTime();
277 $isFloating = $this->isFloating();
278
279 $tz = $dts[0]->getTimeZone();
280 $isUtc = $isFloating ? false : in_array($tz->getName() , array('UTC', 'GMT', 'Z'));
281
282 return array_map(
283 function($dt) use ($hasTime, $isUtc) {
284
285 if ($hasTime) {
286 return $dt->format('Y-m-d\\TH:i:s') . ($isUtc?'Z':'');
287 } else {
288 return $dt->format('Y-m-d');
289 }
290
291 },
292 $dts
293 );
294
295 }
296
297 /**
298 * Sets the json value, as it would appear in a jCard or jCal object.
299 *
300 * The value must always be an array.
301 *
302 * @param array $value
303 * @return void
304 */
305 public function setJsonValue(array $value) {
306
307 // dates and times in jCal have one difference to dates and times in
308 // iCalendar. In jCal date-parts are separated by dashes, and
309 // time-parts are separated by colons. It makes sense to just remove
310 // those.
311 $this->setValue(
312 array_map(
313 function($item) {
314
315 return strtr($item, array(':'=>'', '-'=>''));
316
317 },
318 $value
319 )
320 );
321
322 }
323 /**
324 * We need to intercept offsetSet, because it may be used to alter the
325 * VALUE from DATE-TIME to DATE or vice-versa.
326 *
327 * @param string $name
328 * @param mixed $value
329 * @return void
330 */
331 public function offsetSet($name, $value) {
332
333 parent::offsetSet($name, $value);
334 if (strtoupper($name)!=='VALUE') {
335 return;
336 }
337
338 // This will ensure that dates are correctly encoded.
339 $this->setDateTimes($this->getDateTimes());
340
341 }
342
343 /**
344 * Validates the node for correctness.
345 *
346 * The following options are supported:
347 * Node::REPAIR - May attempt to automatically repair the problem.
348 *
349 * This method returns an array with detected problems.
350 * Every element has the following properties:
351 *
352 * * level - problem level.
353 * * message - A human-readable string describing the issue.
354 * * node - A reference to the problematic node.
355 *
356 * The level means:
357 * 1 - The issue was repaired (only happens if REPAIR was turned on)
358 * 2 - An inconsequential issue
359 * 3 - A severe issue.
360 *
361 * @param int $options
362 * @return array
363 */
364 public function validate($options = 0) {
365
366 $messages = parent::validate($options);
367 $valueType = $this->getValueType();
368 $value = $this->getValue();
369 try {
370 switch($valueType) {
371 case 'DATE' :
372 $foo = DateTimeParser::parseDate($value);
373 break;
374 case 'DATE-TIME' :
375 $foo = DateTimeParser::parseDateTime($value);
376 break;
377 }
378 } catch (\LogicException $e) {
379 $messages[] = array(
380 'level' => 3,
381 'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType,
382 'node' => $this,
383 );
384 }
385 return $messages;
386
387 }
388 }