1<?php 2 3namespace Sabre\VObject\Property\VCard; 4 5use 6 Sabre\VObject\DateTimeParser, 7 Sabre\VObject\Property\Text, 8 Sabre\VObject\Property, 9 DateTime; 10 11/** 12 * DateAndOrTime property 13 * 14 * This object encodes DATE-AND-OR-TIME values. 15 * 16 * @copyright Copyright (C) fruux GmbH (https://fruux.com/) 17 * @author Evert Pot (http://evertpot.com/) 18 * @license http://sabre.io/license/ Modified BSD License 19 */ 20class DateAndOrTime extends Property { 21 22 /** 23 * Field separator 24 * 25 * @var null|string 26 */ 27 public $delimiter = null; 28 29 /** 30 * Returns the type of value. 31 * 32 * This corresponds to the VALUE= parameter. Every property also has a 33 * 'default' valueType. 34 * 35 * @return string 36 */ 37 public function getValueType() { 38 39 return "DATE-AND-OR-TIME"; 40 41 } 42 43 /** 44 * Sets a multi-valued property. 45 * 46 * You may also specify DateTime objects here. 47 * 48 * @param array $parts 49 * @return void 50 */ 51 public function setParts(array $parts) { 52 53 if (count($parts)>1) { 54 throw new \InvalidArgumentException('Only one value allowed'); 55 } 56 if (isset($parts[0]) && $parts[0] instanceof \DateTime) { 57 $this->setDateTime($parts[0]); 58 } else { 59 parent::setParts($parts); 60 } 61 62 } 63 64 /** 65 * Updates the current value. 66 * 67 * This may be either a single, or multiple strings in an array. 68 * 69 * Instead of strings, you may also use DateTime here. 70 * 71 * @param string|array|\DateTime $value 72 * @return void 73 */ 74 public function setValue($value) { 75 76 if ($value instanceof \DateTime) { 77 $this->setDateTime($value); 78 } else { 79 parent::setValue($value); 80 } 81 82 } 83 84 /** 85 * Sets the property as a DateTime object. 86 * 87 * @param \DateTime $dt 88 * @return void 89 */ 90 public function setDateTime(\DateTime $dt) { 91 92 $values = array(); 93 94 $tz = null; 95 $isUtc = false; 96 97 $tz = $dt->getTimeZone(); 98 $isUtc = in_array($tz->getName() , array('UTC', 'GMT', 'Z')); 99 100 if ($isUtc) { 101 $value = $dt->format('Ymd\\THis\\Z'); 102 } else { 103 // Calculating the offset. 104 $value = $dt->format('Ymd\\THisO'); 105 } 106 107 $this->value = $value; 108 109 } 110 111 /** 112 * Returns a date-time value. 113 * 114 * Note that if this property contained more than 1 date-time, only the 115 * first will be returned. To get an array with multiple values, call 116 * getDateTimes. 117 * 118 * If no time was specified, we will always use midnight (in the default 119 * timezone) as the time. 120 * 121 * If parts of the date were omitted, such as the year, we will grab the 122 * current values for those. So at the time of writing, if the year was 123 * omitted, we would have filled in 2014. 124 * 125 * @return \DateTime 126 */ 127 public function getDateTime() { 128 129 $dts = array(); 130 $now = new DateTime(); 131 132 $tzFormat = $now->getTimezone()->getOffset($now)===0?'\\Z':'O'; 133 $nowParts = DateTimeParser::parseVCardDateTime($now->format('Ymd\\This' . $tzFormat)); 134 135 $value = $this->getValue(); 136 137 $dateParts = DateTimeParser::parseVCardDateTime($this->getValue()); 138 139 // This sets all the missing parts to the current date/time. 140 // So if the year was missing for a birthday, we're making it 'this 141 // year'. 142 foreach($dateParts as $k=>$v) { 143 if (is_null($v)) { 144 $dateParts[$k] = $nowParts[$k]; 145 } 146 } 147 return new DateTime("$dateParts[year]-$dateParts[month]-$dateParts[date] $dateParts[hour]:$dateParts[minute]:$dateParts[second] $dateParts[timezone]"); 148 149 } 150 151 /** 152 * Returns the value, in the format it should be encoded for json. 153 * 154 * This method must always return an array. 155 * 156 * @return array 157 */ 158 public function getJsonValue() { 159 160 $parts = DateTimeParser::parseVCardDateTime($this->getValue()); 161 162 $dateStr = ''; 163 164 // Year 165 if (!is_null($parts['year'])) { 166 $dateStr.=$parts['year']; 167 168 if (!is_null($parts['month'])) { 169 // If a year and a month is set, we need to insert a separator 170 // dash. 171 $dateStr.='-'; 172 } 173 174 } else { 175 176 if (!is_null($parts['month']) || !is_null($parts['date'])) { 177 // Inserting two dashes 178 $dateStr.='--'; 179 } 180 181 } 182 183 // Month 184 185 if (!is_null($parts['month'])) { 186 $dateStr.=$parts['month']; 187 188 if (isset($parts['date'])) { 189 // If month and date are set, we need the separator dash. 190 $dateStr.='-'; 191 } 192 } else { 193 if (isset($parts['date'])) { 194 // If the month is empty, and a date is set, we need a 'empty 195 // dash' 196 $dateStr.='-'; 197 } 198 } 199 200 // Date 201 if (!is_null($parts['date'])) { 202 $dateStr.=$parts['date']; 203 } 204 205 206 // Early exit if we don't have a time string. 207 if (is_null($parts['hour']) && is_null($parts['minute']) && is_null($parts['second'])) { 208 return array($dateStr); 209 } 210 211 $dateStr.='T'; 212 213 // Hour 214 if (!is_null($parts['hour'])) { 215 $dateStr.=$parts['hour']; 216 217 if (!is_null($parts['minute'])) { 218 $dateStr.=':'; 219 } 220 } else { 221 // We know either minute or second _must_ be set, so we insert a 222 // dash for an empty value. 223 $dateStr.='-'; 224 } 225 226 // Minute 227 if (!is_null($parts['minute'])) { 228 $dateStr.=$parts['minute']; 229 230 if (!is_null($parts['second'])) { 231 $dateStr.=':'; 232 } 233 } else { 234 if (isset($parts['second'])) { 235 // Dash for empty minute 236 $dateStr.='-'; 237 } 238 } 239 240 // Second 241 if (!is_null($parts['second'])) { 242 $dateStr.=$parts['second']; 243 } 244 245 // Timezone 246 if (!is_null($parts['timezone'])) { 247 $dateStr.=$parts['timezone']; 248 } 249 250 return array($dateStr); 251 252 } 253 254 /** 255 * Sets a raw value coming from a mimedir (iCalendar/vCard) file. 256 * 257 * This has been 'unfolded', so only 1 line will be passed. Unescaping is 258 * not yet done, but parameters are not included. 259 * 260 * @param string $val 261 * @return void 262 */ 263 public function setRawMimeDirValue($val) { 264 265 $this->setValue($val); 266 267 } 268 269 /** 270 * Returns a raw mime-dir representation of the value. 271 * 272 * @return string 273 */ 274 public function getRawMimeDirValue() { 275 276 return implode($this->delimiter, $this->getParts()); 277 278 } 279 280 /** 281 * Validates the node for correctness. 282 * 283 * The following options are supported: 284 * Node::REPAIR - May attempt to automatically repair the problem. 285 * 286 * This method returns an array with detected problems. 287 * Every element has the following properties: 288 * 289 * * level - problem level. 290 * * message - A human-readable string describing the issue. 291 * * node - A reference to the problematic node. 292 * 293 * The level means: 294 * 1 - The issue was repaired (only happens if REPAIR was turned on) 295 * 2 - An inconsequential issue 296 * 3 - A severe issue. 297 * 298 * @param int $options 299 * @return array 300 */ 301 public function validate($options = 0) { 302 303 $messages = parent::validate($options); 304 $value = $this->getValue(); 305 try { 306 DateTimeParser::parseVCardDateTime($value); 307 } catch (\InvalidArgumentException $e) { 308 $messages[] = array( 309 'level' => 3, 310 'message' => 'The supplied value (' . $value . ') is not a correct DATE-AND-OR-TIME property', 311 'node' => $this, 312 ); 313 } 314 return $messages; 315 316 } 317} 318