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