/** * Constructor */ public function __construct($start, $end) { if (!$start instanceof qCal_DateTime) { $start = qCal_DateTime::factory($start); } if (!$end instanceof qCal_DateTime) { $end = qCal_DateTime::factory($end); } $this->start = $start; $this->end = $end; if ($this->getSeconds() < 0) { throw new qCal_DateTime_Exception_InvalidPeriod("The start date must come before the end date."); } }
/** * Cast a string value into a qCal_DateTime_Period object */ protected function doCast($value) { $parts = explode("/", $value); if (count($parts) !== 2) { throw new qCal_DateTime_Exception_InvalidPeriod("A period must contain a start date and either an end date, or a duration of time."); } $start = qCal_DateTime::factory($parts[0]); try { $end = qCal_DateTime::factory($parts[1]); } catch (qCal_DateTime_Exception $e) { // @todo This should probably be a more specific exception // invalid date, so try duration // @todo: I might want to create a qCal_Date object to represent a duration (not tied to any points in time) // using a qCal_Value object here is sort of inconsistent. Plus, I can see value in having that functionality // within the qCal_Date subcomponent // also, there is a difference in a period and a duration in that if you say start on feb 26 and end on march 2 // that will be a different "duration" depending on the year. that goes for months with alternate amounts of days too $duration = new qCal_DateTime_Duration($parts[1]); $end = qCal_DateTime::factory($start->getUnixTimestamp() + $duration->getSeconds()); // @todo This needs to be updated once qCal_DateTime accepts timestamps } return new qCal_DateTime_Period($start, $end); }
/** * Fetches instances of the recurrence rule in the given time period. Because recurrences * could potentially go on forever, there is no way to fetch ALL instances of a recurrence rule * other than providing a date range that spans the entire length of the recurrence. * * The way this will need to work is, depending on the frequency, I will find all possible * occurrence of the rule. For instance, if this is a "monthly" rule, I'll find out which month * to start in, then find all occurrence possible. Then narrow down by the other rules I guess. * * @idea Maybe I should build classes for each of the frequency types. That way I could loop over * the object and get methods like qCal_DateTime_Recur_Monthly::isNthDay('SU') to find out what sunday * of the month it is... stuff like that... I dunno... ? * * @throws qCal_DateTime_Exception_InvalidRecur * @todo The giant switch in this method is a glaring code smell, but it works for now. I will refactor * after version 0.1 and remove the switch (probably will implement qCal_DateTime_Recur_Yearly, qCal_DateTime_Recur_Monthly, etc.) */ public function getRecurrences($start, $end) { $start = qCal_DateTime::factory($start); $end = qCal_DateTime::factory($end); if ($start->getUnixTimestamp() > $end->getUnixTimestamp()) { throw new qCal_DateTime_Exception_InvalidRecur('Start date must come before end date'); } if (!$this->interval) { throw new qCal_DateTime_Exception_InvalidRecur('You must specify an interval'); } $rules = array('bymonth' => array(), 'byweekno' => array(), 'byyearday' => array(), 'byday' => array()); // byMonth rules if (is_array($this->bymonth)) { foreach ($this->bymonth as $bymonth) { $rules['bymonth'][] = new qCal_DateTime_Recur_Rule_ByMonth($bymonth); } } // byWeekNo rules if (is_array($this->byweekno)) { foreach ($this->byweekno as $byweekno) { $rules['byweekno'][] = new qCal_DateTime_Recur_Rule_ByWeekNo($byweekno); } } // byYearDay rules if (is_array($this->byyearday)) { foreach ($this->byyearday as $byyearday) { $rules['byyearday'][] = new qCal_DateTime_Recur_Rule_ByYearDay($byyearday); } } // byMonthDay rules (these get applied to bymonth rules) if (is_array($this->bymonthday)) { foreach ($this->bymonthday as $bymonthday) { $bmdrule = new qCal_DateTime_Recur_Rule_ByMonthDay($bymonthday); foreach ($rules['bymonth'] as $bymonth) { $bymonth->attach($bmdrule); } } } // byDay rules (these get applied to bymonth rules if they exist, otherwise simply to year) if (is_array($this->byday)) { foreach ($this->byday as $byday) { $bdrule = new qCal_DateTime_Recur_Rule_ByDay($byday); if (is_array($rules['bymonth']) && !empty($rules['bymonth'])) { foreach ($rules['bymonth'] as $bymonth) { $bymonth->attach($bdrule); } } else { $rules['byday'][] = $bdrule; } } } // byHour rules (these get applied to each rule above) if (is_array($this->byhour)) { foreach ($this->byhour as $byhour) { $bhrule = new qCal_DateTime_Recur_Rule_ByHour($byhour); foreach ($rules as $type => $ruleset) { foreach ($ruleset as $rule) { $rule->attach($bhrule); } } } } // byMinute rules (these get applied to each rule above) if (is_array($this->byminute)) { foreach ($this->byminute as $byminute) { $bmrule = new qCal_DateTime_Recur_Rule_ByMinute($byminute); foreach ($rules as $type => $ruleset) { foreach ($ruleset as $rule) { $rule->attach($bmrule); } } } } // bySecond rules (these get applied to each rule above) if (is_array($this->bysecond)) { foreach ($this->bysecond as $bysecond) { $bsrule = new qCal_DateTime_Recur_Rule_BySecond($bysecond); foreach ($rules as $type => $ruleset) { foreach ($ruleset as $rule) { $rule->attach($bsrule); } } } } return $this->doGetRecurrences($rules, $start, $end); }
/** * Timezones should be accessible individually by getTimezone() */ public function testGetTimezone() { $cal = new qCal_Component_Vcalendar(); $useastern = new qCal_Component_Vtimezone(array('tzid' => 'US-Eastern')); // fake us eastern timezone $useastern->attach(new qCal_Component_Standard(array('dtstart' => qCal_DateTime::factory('20090913T000000Z'), 'offsetto' => new qCal_Property_Tzoffsetto('0200'), 'offsetfrom' => new qCal_Property_Tzoffsetfrom('0400')))); $uswestern = new qCal_Component_Vtimezone(array('tzid' => 'US-Western')); // fake us western timezone $uswestern->attach(new qCal_Component_Standard(array('dtstart' => qCal_DateTime::factory('20090913T000000Z'), 'offsetto' => new qCal_Property_Tzoffsetto('0100'), 'offsetfrom' => new qCal_Property_Tzoffsetfrom('0300')))); $cal->attach($useastern); $cal->attach($uswestern); $this->assertIdentical($cal->getTimezone('us-eastern'), $useastern); }
/** * Test conversion to UTC * @todo The entire process for UTC conversion is hacky at best. Fix it up in the next release. */ public function testUTCConversion() { $datetime = qCal_DateTime::factory("2/22/1988 5:52am", "America/Denver"); // February 22, 1988 at 5:52am Mountain Standard Time (-7 hours) // UTC is GMT time, which means that the result of this should be the time specified plus seven hours $this->assertEqual($datetime->getUtc(), "19880222T125200Z"); }
/** * Test that all of the right characters are escaped when rendered * @todo Need to make sure that when parsing the escape characters are removed. */ public function testCharactersAreEscaped() { $journal = new qCal_Component_Vjournal(array('summary' => 'The most interesting, but non-interesting journal entry ever.', 'description' => 'This is a sentence that ends with a semi-colon, which I\'m not sure needs to be escaped; I will read the RFC a bit and find out what, exactly, needs to escaped. I know commas do though, and this entry has plenty of those.', 'dtstart' => qCal_DateTime::factory('20090809T113500'))); $this->assertEqual($journal->render(), "BEGIN:VJOURNAL\r\nSUMMARY:The most interesting\\, but non-interesting journal entry ever.\r\nDESCRIPTION:This is a sentence that ends with a semi-colon\\, which I'm not \r\n sure needs to be escaped; I will read the RFC a bit and find out what\\, exa\r\n ctly\\, needs to escaped. I know commas do though\\, and this entry has plent\r\n y of those.\r\nDTSTART:20090809T113500\r\nEND:VJOURNAL\r\n"); }
/** * Test that date-time data is handled right */ public function testRawDateTime() { $value = new qCal_Value_DateTime('2009-04-23 6:00'); $this->assertEqual($value->getValue(), qCal_DateTime::factory('2009-04-23 6:00')); }
public function eventToIcal($_event) { if ($_event instanceof Tinebase_Record_RecordSet) { foreach ($_event as $event) { $this->eventToIcal($event); } return $this->_vcalendar; } // NOTE: we deliver events in originators tz $_event->setTimezone($_event->originator_tz); if (!in_array($_event->originator_tz, $this->_attachedTimezones)) { $this->_vcalendar->attach(self::getVtimezone($_event->originator_tz)); $this->_attachedTimezones[] = $_event->originator_tz; } if ($_event->is_all_day_event) { $dtstart = new qCal_Property_Dtstart($_event->dtstart->format('Ymd'), array('VALUE' => 'DATE')); $dtend = new qCal_Property_Dtend($_event->dtend->format('Ymd'), array('VALUE' => 'DATE')); } else { $dtstart = new qCal_Property_Dtstart(qCal_DateTime::factory($_event->dtstart->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); $dtend = new qCal_Property_Dtend(qCal_DateTime::factory($_event->dtend->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); } $vevent = new qCal_Component_Vevent(array('uid' => $_event->uid, 'sequence' => $_event->seq, 'summary' => $_event->summary, 'dtstart' => $dtstart, 'dtend' => $dtend)); foreach (self::$veventMap as $icalProp => $tineField) { if (isset($_event[$tineField])) { $vevent->addProperty($icalProp, $_event->{$tineField}); } } // rrule if ($_event->rrule) { $vevent->addProperty('rrule', preg_replace('/(UNTIL=)(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})/', '$1$2$3$4T$5$6$7Z', $_event->rrule)); if ($exdateArray = $_event->exdate) { // use multiple EXDATE for the moment, as apple ical uses them foreach ($_event->exdate as $exdate) { $exdates = new qCal_Property_Exdate(qCal_DateTime::factory($exdate->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); $vevent->addProperty($exdates); } // $exdates = new qCal_Property_Exdate(qCal_DateTime::factory(array_shift($exdateArray)->format('Ymd\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz)); // foreach($exdateArray as $exdate) { // $exdates->addValue(qCal_DateTime::factory($exdate->format('Ymd\THis'), $_event->originator_tz)); // } // // $vevent->addProperty($exdates); } } // recurid if ($_event->isRecurException()) { $originalDtStart = $_event->getOriginalDtStart(); $originalDtStart->setTimezone($_event->originator_tz); $vevent->addProperty(new qCal_Property_RecurrenceId(qCal_DateTime::factory($originalDtStart->format('Ymd\\THis'), $_event->originator_tz), array('TZID' => $_event->originator_tz))); } // organizer $organizerId = $_event->organizer instanceof Addressbook_Model_Contact ? array($_event->organizer->getId()) : array($_event->organizer); $organizer = Addressbook_Controller_Contact::getInstance()->getMultiple($organizerId, TRUE)->getFirstRecord(); if ($organizer && ($organizerEmail = $organizer->getPreferedEmailAddress())) { $vevent->addProperty(new qCal_Property_Organizer("mailto:{$organizerEmail}", array('CN' => $organizer->n_fileas))); } // attendee if ($_event->attendee) { Calendar_Model_Attender::resolveAttendee($_event->attendee, FALSE); foreach ($_event->attendee as $attender) { $attenderEmail = $attender->getEmail(); if ($attenderEmail) { $vevent->addProperty(new qCal_Property_Attendee("mailto:{$attenderEmail}", array('CN' => $attender->getName(), 'CUTYPE' => self::$cutypeMap[$attender->user_type], 'EMAIL' => $attenderEmail, 'PARTSTAT' => $attender->status, 'ROLE' => "{$attender->role}-PARTICIPANT", 'RSVP' => 'FALSE'))); } } } // alarms if ($_event->alarms) { foreach ($_event->alarms as $alarm) { $valarm = new qCal_Component_Valarm(array('ACTION' => 'DISPLAY', 'DESCRIPTION' => $_event->summary)); // qCal only support DURATION ;-( $diffSeconds = $_event->dtstart->php52compat_diff($alarm->alarm_time); $valarm->addProperty(new qCal_Property_Trigger($diffSeconds)); // if (is_numeric($alarm->minutes_before)) { // $valarm->addProperty(new qCal_Property_Trigger("-PT{$alarm->minutes_before}M")); // } else { // $valarm->addProperty(new qCal_Property_Trigger(qCal_DateTime::factory($alarm->alarm_time->format('Ymd\THis'), $_event->originator_tz)), array('TZID' => $_event->originator_tz)); // } $vevent->attach($valarm); } } // @todo status $this->_vcalendar->attach($vevent); return $this->_vcalendar; }
/** * This converts to a qCal_Date for internal storage */ protected function doCast($value) { // @todo This may be the wrong place to do this... if ($value instanceof qCal_DateTime) { return $value; } $date = qCal_DateTime::factory($value); return $date; }
/** * Set the end date/time for the recurrence set. No recurrences will be returned * beyond this date/time * @param mixed Either a qCal_DateTime object or a string representing one * @return $this * @access public */ public function setUntil($datetime) { $this->until = $datetime instanceof qCal_DateTime ? $datetime : qCal_DateTime::factory($datetime); return $this; }
/** * Test qCal_DateTime examples */ public function testDateTimeFactory() { $datetime1 = qCal_DateTime::factory("January 21st, 2010 3pm", "GMT"); $this->assertEqual($datetime1->__toString(), "2010-01-21T15:00:00+00:00"); // this is not very easy to test... // $datetime2 = qCal_DateTime::factory("noon tomorrow"); // will result in whatever tomorrow's date is and at noon // nor is this // $datetime3 = qCal_DateTime::factory("now"); // will result in today's date and time // nor is this // $datetime4 = qCal_DateTime::factory(time()); // will result in today's date and time $datetime5 = qCal_DateTime::factory("October 1st, 2009 9am", "America/Los_Angeles"); // will result in October 1st, 2009 at 9am in America/Los_Angeles time $this->assertEqual($datetime5->__toString(), "2009-10-01T09:00:00-08:00"); }