Converts this object to a different timezone.
public setTimezone ( string $timezone ) : Horde_Date | ||
$timezone | string | The new timezone. |
return | Horde_Date | This object. |
/** * Return the CURRENT offset from UTC for this station as provided by the * API. * * @return integer The current offset from UTC. * @since 1.2.0 */ public function getOffset() { if (!empty($this->_properties['tz']) && is_numeric($this->_properties['tz'])) { return ($this->tz < 0 ? '-' : '') . gmdate('H:i', floor(abs($this->tz) * 60 * 60)); } elseif (!empty($this->_properties['tz'])) { try { $d = new Horde_Date(time(), 'UTC'); $d->setTimezone($this->tz); return $d->tzOffset(); } catch (Horde_Date_Exception $e) { } } return false; }
/** * Test if the weekday of the given timestamp is the nth occurence of this * weekday within its month, where '5' indicates the last occurrence even if * there is less than five occurrences. * * @param integer $timestamp The timestamp to check. * @param integer $occurence 1 to 5, where 5 indicates the final occurrence * during the month if that day of the week does * not occur 5 times * @return boolean */ protected static function _isNthOcurrenceOfWeekdayInMonth($timestamp, $occurence) { $original = new Horde_Date($timestamp); $original->setTimezone('UTC'); if ($occurence == 5) { $modified = $original->add(array('mday' => 7)); return $modified->month > $original->month; } else { $modified = $original->sub(array('mday' => 7 * $occurence)); $modified2 = $original->sub(array('mday' => 7 * ($occurence - 1))); return $modified->month < $original->month && $modified2->month == $original->month; } }
/** * Build a EAS style FB string. Essentially, each digit represents 1/2 hour. * The values are as follows: * 0 - Free * 1 - Tentative * 2 - Busy * 3 - OOF * 4 - No data available. * * Though currently we only provide a Free/Busy/Unknown differentiation. * * @param stdClass $fb The fb information. An object containing: * - s: The start of the period covered. * - e: The end of the period covered. * - b: An array of busy periods. * * @param Horde_Date $start The start of the period requested by the client. * @param Horde_Date $end The end of the period requested by the client. * * @return string The EAS freebusy string. * @since 2.4.0 */ public static function buildFbString($fb, Horde_Date $start, Horde_Date $end) { if (empty($fb)) { return false; } // Convert all to UTC $start->setTimezone('UTC'); $end->setTimezone('UTC'); // Calculate total time span (end timestamp in non-inclusive). $end_ts = $end->timestamp() - 1; $start_ts = $start->timestamp(); $sec = $end_ts - $start_ts; $fb_start = new Horde_Date($fb->s); $fb_end = new Horde_Date($fb->e); $fb_start->setTimezone('UTC'); $fb_end->setTimezone('UTC'); // Number of 30 minute periods. $period_cnt = ceil($sec / 1800); // Requested range is completely out of the available range. if ($start_ts >= $fb_end->timestamp() || $end_ts < $fb_start->timestamp()) { return str_repeat('4', $period_cnt); } // We already know we don't have any busy periods. if (empty($fb->b) && $fb_end->timestamp() <= $end_ts) { return str_repeat('0', $period_cnt); } $eas_fb = ''; // Move $start to the start of the available data. while ($start_ts < $fb_start->timestamp() && $start_ts <= $end_ts) { $eas_fb .= '4'; $start_ts += 1800; // 30 minutes } // The rest is assumed free up to $fb->e while ($start_ts <= $fb_end->timestamp() && $start_ts <= $end_ts) { $eas_fb .= '0'; $start_ts += 1800; } // The remainder is also unavailable while ($start_ts <= $end_ts) { $eas_fb .= '4'; $start_ts += 1800; } // Now put in the busy blocks. Need to convert to UTC here too since // all fb data is returned as local tz. while (list($b_start, $b_end) = each($fb->b)) { $b_start = new Horde_Date($b_start); $b_start->setTimezone('UTC'); $b_end = new Horde_Date($b_end); $b_end->setTimezone('UTC'); if ($b_start->timestamp() > $end->timestamp()) { continue; } $offset = $b_start->timestamp() - $start->timestamp(); $duration = ceil(($b_end->timestamp() - $b_start->timestamp()) / 1800); if ($offset > 0) { $eas_fb = substr_replace($eas_fb, str_repeat('2', $duration), floor($offset / 1800), $duration); } } return $eas_fb; }
/** * Imports a backend specific event object. * * @param array $event Backend specific event object that this object * will represent. */ public function fromDriver($SQLEvent) { $driver = $this->getDriver(); if (isset($SQLEvent['event_timezone'])) { $this->timezone = $SQLEvent['event_timezone']; } $tz_local = date_default_timezone_get(); $this->allday = (bool) $SQLEvent['event_allday']; if (!$this->allday && $driver->getParam('utc')) { $this->start = new Horde_Date($SQLEvent['event_start'], 'UTC'); $this->start->setTimezone($tz_local); $this->end = new Horde_Date($SQLEvent['event_end'], 'UTC'); $this->end->setTimezone($tz_local); } else { $this->start = new Horde_Date($SQLEvent['event_start']); $this->end = new Horde_Date($SQLEvent['event_end']); if ($this->end->hour == 23 && $this->end->min == 59) { $this->end->hour = $this->end->min = $this->end->sec = 0; $this->end->mday++; } } $this->durMin = ($this->end->timestamp() - $this->start->timestamp()) / 60; $this->title = $driver->convertFromDriver($SQLEvent['event_title']); $this->id = $SQLEvent['event_id']; $this->uid = $SQLEvent['event_uid']; $this->creator = $SQLEvent['event_creator_id']; $this->organizer = $SQLEvent['event_organizer']; if (!empty($SQLEvent['event_recurtype'])) { $this->recurrence = new Horde_Date_Recurrence($this->start); $this->recurrence->setRecurType((int) $SQLEvent['event_recurtype']); $this->recurrence->setRecurInterval((int) $SQLEvent['event_recurinterval']); if (isset($SQLEvent['event_recurenddate']) && $SQLEvent['event_recurenddate'] != '9999-12-31 23:59:59') { if ($driver->getParam('utc')) { $recur_end = new Horde_Date($SQLEvent['event_recurenddate'], 'UTC'); if ($recur_end->min == 0) { /* Old recurrence end date format. */ $recur_end = new Horde_Date($SQLEvent['event_recurenddate']); $recur_end->hour = 23; $recur_end->min = 59; $recur_end->sec = 59; } else { $recur_end->setTimezone(date_default_timezone_get()); } } else { $recur_end = new Horde_Date($SQLEvent['event_recurenddate']); $recur_end->hour = 23; $recur_end->min = 59; $recur_end->sec = 59; } $this->recurrence->setRecurEnd($recur_end); } if (isset($SQLEvent['event_recurcount'])) { $this->recurrence->setRecurCount((int) $SQLEvent['event_recurcount']); } if (isset($SQLEvent['event_recurdays'])) { $this->recurrence->recurData = (int) $SQLEvent['event_recurdays']; } if (!empty($SQLEvent['event_exceptions'])) { $this->recurrence->exceptions = explode(',', $SQLEvent['event_exceptions']); } } if (isset($SQLEvent['event_location'])) { $this->location = $driver->convertFromDriver($SQLEvent['event_location']); } if (isset($SQLEvent['event_url'])) { $this->url = $SQLEvent['event_url']; } if (isset($SQLEvent['event_private'])) { $this->private = (bool) $SQLEvent['event_private']; } if (isset($SQLEvent['event_status'])) { $this->status = (int) $SQLEvent['event_status']; } if (isset($SQLEvent['event_attendees'])) { $attendees = unserialize($SQLEvent['event_attendees']); if ($attendees) { if (!is_object($attendees)) { $this->attendees = new Kronolith_Attendee_List(); foreach ($attendees as $email => $attendee) { $this->attendees->add(Kronolith_Attendee::migrate($email, $driver->convertFromDriver($attendee))); } } else { $this->attendees = new Kronolith_Attendee_List(iterator_to_array($attendees)); } } } if (isset($SQLEvent['event_resources'])) { $resources = unserialize($SQLEvent['event_resources']); if ($resources) { $this->_resources = array_change_key_case($driver->convertFromDriver($resources)); } } if (isset($SQLEvent['event_description'])) { $this->description = $driver->convertFromDriver($SQLEvent['event_description']); } if (isset($SQLEvent['event_alarm'])) { $this->alarm = (int) $SQLEvent['event_alarm']; } if (isset($SQLEvent['event_alarm_methods'])) { $methods = unserialize($SQLEvent['event_alarm_methods']); if ($methods) { $this->methods = $driver->convertFromDriver($methods); } } if (isset($SQLEvent['event_baseid'])) { $this->baseid = $SQLEvent['event_baseid']; } if (isset($SQLEvent['event_exceptionoriginaldate'])) { if ($driver->getParam('utc')) { $this->exceptionoriginaldate = new Horde_Date($SQLEvent['event_exceptionoriginaldate'], 'UTC'); $this->exceptionoriginaldate->setTimezone($tz_local); } else { $this->exceptionoriginaldate = new Horde_Date($SQLEvent['event_exceptionoriginaldate']); } } $this->initialized = true; $this->stored = true; }
/** * Converts an UTC timestamp like "20061222T110000Z" into a local * timestamp like "20061222T130000" using the server timezone. * * @param array $utc Array with a datetime string in UTC. * * @return string The datetime string converted to the local timezone. */ protected function _convertUTC2LocalTime($utc) { $date = new Horde_Date($utc[0]); $date->setTimezone(date_default_timezone_get()); return $date->format("Ymd\\THis"); }
/** * Oh yeah. This is beautiful. Exchange outputs date fields differently in * calendar items and emails. We could just always send one or the other, * but unfortunately nokia's 'Mail for exchange' depends on this quirk. * So we have to send a different date type depending on where it's used. * Used when encoding a date value to send to the client. * * @param Horde_Date $dt The Horde_Date object to format * (should normally be in local tz). * @param integer $type The type to format as: One of * TYPE_DATE or TYPE_DATE_DASHES, TYPE_DATE_LOCAL * * @return string The formatted date * @throws InvalidArgumentException */ protected function _formatDate(Horde_Date $dt, $type) { switch ($type) { case self::TYPE_DATE: return $dt->setTimezone('UTC')->format('Ymd\\THis\\Z'); case self::TYPE_DATE_DASHES: return $dt->setTimezone('UTC')->format('Y-m-d\\TH:i:s\\.000\\Z'); case self::TYPE_DATE_LOCAL: return $dt->format('Y-m-d\\TH:i:s\\.000\\Z'); default: throw new InvalidArgumentException('Unidentified DATE_TYPE'); } }
/** * Garbage collects old alarms in the backend. */ protected function _gc() { $query = sprintf('DELETE FROM %s WHERE alarm_end IS NOT NULL AND alarm_end < ?', $this->_params['table']); $end = new Horde_Date(time()); $this->_db->delete($query, array($end->setTimezone('UTC')->format(Horde_Date::DATE_DEFAULT))); }
/** * Converts this event between the event's and the local timezone. * * @param boolean $to_orginal If true converts to the event's timezone. */ public function setTimezone($to_original) { if (!$this->timezone || !$this->getDriver()->supportsTimezones()) { return; } $timezone = $to_original ? $this->timezone : date_default_timezone_get(); $this->start->setTimezone($timezone); $this->end->setTimezone($timezone); if ($this->recurs() && $this->recurrence->hasRecurEnd()) { /* @todo Check if have to go through all recurrence exceptions too. */ $this->recurrence->start->setTimezone($timezone); $this->recurrence->recurEnd->setTimezone($timezone); } }
/** * Parses an iCalendar 2.0 recurrence rule. * * @link http://rfc.net/rfc2445.html#s4.3.10 * @link http://rfc.net/rfc2445.html#s4.8.5 * @link http://www.shuchow.com/vCalAddendum.html * * @param string $rrule An iCalendar 2.0 conform RRULE value. */ public function fromRRule20($rrule) { $this->reset(); // Parse the recurrence rule into keys and values. $rdata = array(); $parts = explode(';', $rrule); foreach ($parts as $part) { list($key, $value) = explode('=', $part, 2); $rdata[Horde_String::upper($key)] = $value; } if (isset($rdata['FREQ'])) { // Always default the recurInterval to 1. $this->setRecurInterval(isset($rdata['INTERVAL']) ? $rdata['INTERVAL'] : 1); switch (Horde_String::upper($rdata['FREQ'])) { case 'DAILY': $this->setRecurType(self::RECUR_DAILY); break; case 'WEEKLY': $this->setRecurType(self::RECUR_WEEKLY); if (isset($rdata['BYDAY'])) { $maskdays = array('SU' => Horde_Date::MASK_SUNDAY, 'MO' => Horde_Date::MASK_MONDAY, 'TU' => Horde_Date::MASK_TUESDAY, 'WE' => Horde_Date::MASK_WEDNESDAY, 'TH' => Horde_Date::MASK_THURSDAY, 'FR' => Horde_Date::MASK_FRIDAY, 'SA' => Horde_Date::MASK_SATURDAY); $days = explode(',', $rdata['BYDAY']); $mask = 0; foreach ($days as $day) { $mask |= $maskdays[$day]; } $this->setRecurOnDay($mask); } else { // Recur on the day of the week of the original // recurrence. $maskdays = array(Horde_Date::DATE_SUNDAY => Horde_Date::MASK_SUNDAY, Horde_Date::DATE_MONDAY => Horde_Date::MASK_MONDAY, Horde_Date::DATE_TUESDAY => Horde_Date::MASK_TUESDAY, Horde_Date::DATE_WEDNESDAY => Horde_Date::MASK_WEDNESDAY, Horde_Date::DATE_THURSDAY => Horde_Date::MASK_THURSDAY, Horde_Date::DATE_FRIDAY => Horde_Date::MASK_FRIDAY, Horde_Date::DATE_SATURDAY => Horde_Date::MASK_SATURDAY); $this->setRecurOnDay($maskdays[$this->start->dayOfWeek()]); } break; case 'MONTHLY': if (isset($rdata['BYDAY'])) { $this->setRecurType(self::RECUR_MONTHLY_WEEKDAY); } else { $this->setRecurType(self::RECUR_MONTHLY_DATE); } break; case 'YEARLY': if (isset($rdata['BYYEARDAY'])) { $this->setRecurType(self::RECUR_YEARLY_DAY); } elseif (isset($rdata['BYDAY'])) { $this->setRecurType(self::RECUR_YEARLY_WEEKDAY); } else { $this->setRecurType(self::RECUR_YEARLY_DATE); } break; } // MUST take into account the time portion if it is present. // See Bug: 12869 and Bug: 2813 if (isset($rdata['UNTIL'])) { if (preg_match('/^(\\d{4})-?(\\d{2})-?(\\d{2})T? ?(\\d{2}):?(\\d{2}):?(\\d{2})(?:\\.\\d+)?(Z?)$/', $rdata['UNTIL'], $parts)) { $until = new Horde_Date($rdata['UNTIL'], 'UTC'); $until->setTimezone($this->start->timezone); } else { list($year, $month, $mday) = sscanf($rdata['UNTIL'], '%04d%02d%02d'); $until = new Horde_Date(array('year' => $year, 'month' => $month, 'mday' => $mday + 1), $this->start->timezone); } $this->setRecurEnd($until); } if (isset($rdata['COUNT'])) { $this->setRecurCount($rdata['COUNT']); } } else { // No recurrence data - event does not recur. $this->setRecurType(self::RECUR_NONE); } }
/** * Stop a timer. Expects the following in $this->vars: * - t: The timer id. * - restart: * * @return array An array describing the current timer state. Contains: * - h: The total number of hours elapsed so far. * - n: A note to apply to the description field of a time slice. * - t: The new timer title, if restarting. */ public function stopTimer() { global $prefs, $notification; try { $timer = Hermes::getTimer($this->vars->t); } catch (Horde_Exception_NotFound $e) { $notification->push(_("Invalid timer requested"), 'horde.error'); return false; } $results = $timer; $tname = $timer['name']; $elapsed = (!$timer['paused'] ? time() - $timer['time'] : 0) + $timer['elapsed']; $results['h'] = round((double) $elapsed / 3600, 2); $started = new Horde_Date($this->vars->t, 'UTC'); $started->setTimezone(date_default_timezone_get()); $now = new Horde_Date(time(), 'UTC'); $now->setTimezone(date_default_timezone_get()); if ($prefs->getValue('add_description')) { $results['n'] = sprintf(_("Using the \"%s\" stop watch from %s %s to %s %s"), $tname, $started->strftime($prefs->getValue('date_format_mini')), $started->strftime($prefs->getValue('time_format')), $now->strftime($prefs->getValue('date_format_mini')), $now->strftime($prefs->getValue('time_format'))); } else { $results['n'] = ''; } $notification->push(sprintf(_("The stop watch \"%s\" has been stopped."), $tname), 'horde.success'); Hermes::clearTimer($this->vars->t); if ($this->vars->restart == 'true') { $now = time(); $timer['elapsed'] = 0; $timer['paused'] = $results['paused'] = true; $timer['time'] = $now; Hermes::updateTimer($this->vars->t, $timer); } return $results; }
/** * Oh yeah. This is beautiful. Exchange outputs date fields differently in * calendar items and emails. We could just always send one or the other, * but unfortunately nokia's 'Mail for exchange' depends on this quirk. * So we have to send a different date type depending on where it's used. * * @param Horde_Date $dt The datetime to format (assumed to be in local tz) * @param integer $type The type to format as (TYPE_DATE or TYPE_DATE_DASHES) * * @return string The formatted date */ protected function _formatDate(Horde_Date $dt, $type) { if ($type == Horde_ActiveSync_Message_Base::TYPE_DATE) { return $dt->setTimezone('UTC')->format('Ymd\\THis\\Z'); } elseif ($type == Horde_ActiveSync_Message_Base::TYPE_DATE_DASHES) { return $dt->setTimezone('UTC')->format('Y-m-d\\TH:i:s\\.000\\Z'); } }
/** * Return an array describing this task from the provided backend data. * * @param array $row The backend data * @param boolean $include_history Include history data. * * @return array The task data. */ protected function _buildTask($row, $include_history = true) { // Make sure tasks always have a UID. if (empty($row['task_uid'])) { $row['task_uid'] = strval(new Horde_Support_Guid()); $query = 'UPDATE ' . $this->_params['table'] . ' SET task_uid = ?' . ' WHERE task_owner = ? AND task_id = ?'; $values = array($row['task_uid'], $row['task_owner'], $row['task_id']); try { $this->_db->update($query, $values); } catch (Horde_Db_Exception $e) { } } if (!$row['task_due'] || !$row['task_recurtype']) { $recurrence = null; } else { $recurrence = new Horde_Date_Recurrence($row['task_due']); $recurrence->setRecurType((int) $row['task_recurtype']); $recurrence->setRecurInterval((int) $row['task_recurinterval']); if (isset($row['task_recurenddate']) && $row['task_recurenddate'] != '9999-12-31 23:59:59') { $recur_end = new Horde_Date($row['task_recurenddate'], 'UTC'); $recur_end->setTimezone(date_default_timezone_get()); $recurrence->setRecurEnd($recur_end); } if (isset($row['task_recurcount'])) { $recurrence->setRecurCount((int) $row['task_recurcount']); } if (isset($row['task_recurdays'])) { $recurrence->recurData = (int) $row['task_recurdays']; } if (!empty($row['task_exceptions'])) { $recurrence->exceptions = explode(',', $row['task_exceptions']); } if (!empty($row['task_completions'])) { $recurrence->completions = explode(',', $row['task_completions']); } } /* Create a new task based on $row's values. */ $task = array('tasklist_id' => $row['task_owner'], 'task_id' => $row['task_id'], 'uid' => Horde_String::convertCharset($row['task_uid'], $this->_params['charset'], 'UTF-8'), 'parent' => $row['task_parent'], 'owner' => $row['task_creator'], 'assignee' => $row['task_assignee'], 'name' => Horde_String::convertCharset($row['task_name'], $this->_params['charset'], 'UTF-8'), 'desc' => Horde_String::convertCharset($row['task_desc'], $this->_params['charset'], 'UTF-8'), 'start' => $row['task_start'], 'due' => $row['task_due'], 'priority' => $row['task_priority'], 'estimate' => (double) $row['task_estimate'], 'completed' => $row['task_completed'], 'completed_date' => isset($row['task_completed_date']) ? $row['task_completed_date'] : null, 'alarm' => $row['task_alarm'], 'methods' => Horde_String::convertCharset(@unserialize($row['task_alarm_methods']), $this->_params['charset'], 'UTF-8'), 'private' => $row['task_private'], 'recurrence' => $recurrence); if ($include_history) { try { $userId = $GLOBALS['registry']->getAuth(); $log = $GLOBALS['injector']->getInstance('Horde_History')->getHistory('nag:' . $row['task_owner'] . ':' . $row['task_uid']); foreach ($log as $entry) { switch ($entry['action']) { case 'add': $task['created'] = new Horde_Date($entry['ts']); if ($userId != $entry['who']) { $task['createdby'] = sprintf(_("by %s"), Nag::getUserName($entry['who'])); } else { $task['createdby'] = _("by me"); } break; case 'modify': $task['modified'] = new Horde_Date($entry['ts']); if ($userId != $entry['who']) { $task['modifiedby'] = sprintf(_("by %s"), Nag::getUserName($entry['who'])); } else { $task['modifiedby'] = _("by me"); } break; } } } catch (Horde_Exception $e) { } } return $task; }
public function UTC2LocalDate($s) { $date = new Horde_Date($s); $date->setTimezone(date_default_timezone_get()); return $date->format('Ymd') . 'T000000'; }
/** * Return the number of hours to offset a POOMCONTACTS:BIRTHDAY * or ANNIVERSARY field in an attempt to work around a bug in the * protocol - which doesn't define a standard time for birthdays to occur. * * WP: * Devices seem to send the birthdays at the entered date, with * a time of 00:00:00 UTC during standard time and with 01:00:00 UTC * during DST if the client's configured timezone observes it. No idea * what purpose this serves since no timezone data is transmitted for * birthday values. * * iOS: * Seems different based on version. iOS 5+, at least seems to send * the birthday as midnight at the entered date in the device's timezone * then converted to UTC. Some minor issues with offsets being off an * hour or two for some timezones though. * * iOS < 5 sends the birthday time part as the time the birthday * was entered/edited on the device, converted to UTC, so it can't be * trusted at all. The best we can do here is transform the date to * midnight on date_default_timezone() converted to UTC. * * Android: * For contacts originating on the SERVER, the following is true: * * Stock 4.3 Takes the down-synched bday value which is assumed to be * UTC, does some magic to it (converts to milliseconds, creates a * gregorian calendar object, then converts to YYYY-MM-DD). When * sending the bday value up, it sends it up as-is. No conversion * to/from UTC or local is done. * * Stock 4.4.x does the above, but before sending the bday value, * validates that it's in a correct format for sending to the server. * This really only affects date data originally entered on the device * for non-stock android clients. * * There is some strange bit of code in Android that adds 1 to the * DAY_OF_MONTH when HOUR_OF_DAY >= 12 in an attempt to "fix" * birthday handling for GMT+n users. See: * https://android.googlesource.com/platform/packages/apps/Exchange/+/32daacdd71b9de8fd5e3f59c37934e3e4a9fa972%5E!/exchange2/src/com/android/exchange/adapter/ContactsSyncAdapter.java * Not sure what to make of it, or why it's not just converted to * local tz when displaying but this probably breaks birthday handling * for people in a few timezones. * * For contacts originating on the CLIENT, the datetime is sent as * 08:00:00 UTC, and this seems to be regardless of the timezone set * in the Android system. * * Given all of this, it makes sense to me to ALWAYS send birthday * data as occuring at 08:00:00 UTC for *native* Android clients. * * BB 10+ expects it at 12:00:00 UTC * * @param Horde_Date $date The date. This should normally be in the local * timezone if encoding the date for the client. * If decoding the date from the client, it will * normally be in UTC. * @param boolean $toEas Convert from local to device if true. * DEFAULT: false * * @return Horde_Date The date of the birthday/anniversary, with * any fixes applied for the current device. The * timezone set in the object will depend on the * client detected, and whether the date is being * encoding or decoding. */ public function normalizePoomContactsDates($date, $toEas = false) { switch (Horde_String::lower($this->clientType)) { case self::TYPE_WP: case 'wp8': // Legacy. Remove in H6. // Legacy. Remove in H6. case 'wp': // Legacy. Remove in H6. if ($toEas) { return new Horde_Date($date->format('Y-m-d'), 'UTC'); } else { $date = new Horde_Date($date->format('Y-m-d')); return $date->setTimezone('UTC'); } case self::TYPE_ANDROID: // Need to protect against clients that don't send the actual Android // version in the OS field. if (stripos($this->deviceType, 'samsung') === 0) { // Samsung's native Contacts app works differently than stock // Android, always sending as 00:00:00 if ($toEas) { return new Horde_Date($date->format('Y-m-d'), 'UTC'); } $date = new Horde_Date($date->format('Y-m-d')); return $date->setTimezone('UTC'); } if ($this->getMajorVersion() >= 4 && $this->getMajorVersion() <= 10) { if ($toEas) { return new Horde_Date($date->format('Y-m-d 08:00:00'), 'UTC'); } else { $date = new Horde_Date($date->format('Y-m-d')); return $date->setTimezone('UTC'); } } else { // POOMCONTACTS:BIRTHDAY not really supported in early Android // versions. Return as is. return $date; } case self::TYPE_IPAD: case self::TYPE_IPHONE: case self::TYPE_IPOD: if ($this->getMajorVersion() >= 5) { // iOS >= 5 handles it correctly more or less. if ($toEas) { return new Horde_Date($date->format('Y-m-d 00:00:00')); } else { return $date; } } else { if ($toEas) { return new Horde_Date($date->format('Y-m-d'), 'UTC'); } else { return new Horde_Date($date->format('Y-m-d')); } } case self::TYPE_NINE: if ($toEas) { return new Horde_Date($date->format('Y-m-d 00:00:00')); } else { return $date; } case self::TYPE_BLACKBERRY: if ($toEas) { return new Horde_Date($date->format('Y-m-d 12:00:00'), 'UTC'); } else { $date = new Horde_Date($date->format('Y-m-d')); return $date->setTimezone('UTC'); } case self::TYPE_TOUCHDOWN: case self::TYPE_UNKNOWN: default: return $date; } }
/** * Accessor so we can lazy-parse the results. * * @param string $property The property name. * * @return mixed The value of requested property * @throws Horde_Service_Weather_Exception_InvalidProperty */ public function __get($property) { switch ($property) { case 'is_pm': // Wunderground only supports standard return false; case 'hour': // Wunderground supports this, but we don't. return false; case 'date': $date = new Horde_Date(array('year' => $this->_properties['date']->year, 'month' => $this->_properties['date']->month, 'mday' => $this->_properties['date']->day)); $date->hour = $this->_properties['date']->hour; $date->min = $this->_properties['date']->min; $date->setTimezone($this->_properties['date']->tz_long); return $date; case 'high': if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['high']->fahrenheit !== '' ? $this->_properties['high']->fahrenheit : Horde_Service_Weather_Translation::t("N/A"); } return $this->_properties['high']->celsius; case 'low': if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['low']->fahrenheit !== '' ? $this->_properties['low']->fahrenheit : Horde_Service_Weather_Translation::t("N/A"); } return $this->_properties['low']->celsius; case 'icon': return $this->_forecast->weather->iconMap[$this->_properties['icon']]; case 'wind_direction': return strlen($this->_properties['avewind']->dir) ? Horde_Service_Weather_Translation::t($this->_properties['avewind']->dir) : Horde_Service_Weather_Translation::t("N/A"); case 'wind_degrees': return strlen($this->_properties['avewind']->dir) ? $this->_properties['avewind']->degrees : Horde_Service_Weather_Translation::t("N/A"); case 'wind_speed': if (strlen($this->_properties['avewind']->dir)) { if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['avewind']->mph; } return $this->_properties['avewind']->kph; } else { return Horde_Service_Weather_Translation::t("N/A"); } case 'wind_gust': if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['maxwind']->mph; } return $this->_properties['maxwind']->kph; case 'rain_total': if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['qpf_allday']->in; } return $this->_properties['qpf_allday']->mm; case 'snow_total': if ($this->_forecast->weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['snow_allday']->in; } return $this->_properties['snow_allday']->cm; default: if (!empty($this->_map[$property])) { return Horde_Service_Weather_Translation::t($this->_properties[$this->_map[$property]]); } throw new Horde_Service_Weather_Exception_InvalidProperty('This provider does not support the "' . $property . '" property'); } }
/** * Adds rules from this ruleset to a VTIMEZONE component. * * @param Horde_Icalendar_Vtimezone $tz A VTIMEZONE component. * @param string $tzid The timezone ID of the component. * @param string $name A timezone name abbreviation. * May contain a placeholder that is * replaced the Rules' "Letter(s)" * entry. * @param array $startOffset An offset hash describing the * base offset of a timezone. * @param Horde_Date $start Start of the period to add rules * for. * @param Horde_Date $end End of the period to add rules * for. */ public function addRules(Horde_Icalendar_Vtimezone $tz, $tzid, $name, $startOffset, Horde_Date $start, Horde_Date $end = null) { $offset = $startOffset; foreach ($this->_rules as $rule) { $year = $rule[3]; if ($year[0] == 'o') { // TO is "only" $rule[3] = $rule[2]; } if ($rule[3][0] != 'm' && $rule[3] < $start->year) { // TO is not maximum and is before the searched period continue; } if ($end && $rule[2][0] != 'm' && $rule[2] > $end->year) { // FROM is not "minimum" and is after the searched period break; } if ($rule[2][0] != 'm' && $rule[2] < $start->year) { $rule[2] = $start->year; } if ($rule[8] == 0) { $component = new Horde_Icalendar_Standard(); $component->setAttribute('TZOFFSETFROM', $offset); $component->setAttribute('TZOFFSETTO', $startOffset); $offset = $startOffset; } else { $component = new Horde_Icalendar_Daylight(); $component->setAttribute('TZOFFSETFROM', $offset); $offset = $this->_getOffset($startOffset, $rule[8]); $component->setAttribute('TZOFFSETTO', $offset); } $month = Horde_Timezone::getMonth($rule[5]); // Retrieve time of rule start. preg_match('/(\\d+)(?::(\\d+))?(?::(\\d+))?(w|s|u)?/', $rule[7], $match); if (!isset($match[2])) { $match[2] = 0; } if ($rule[2] == $rule[3] && preg_match('/^\\d+$/', $rule[6])) { // Rule lasts only for a single year and starts on a specific // date. $rdate = new Horde_Date(array('year' => $rule[2], 'month' => Horde_Timezone::getMonth($rule[5]), 'mday' => $rule[6], 'hour' => $match[1], 'min' => $match[2], 'sec' => 0)); $component->setAttribute('DTSTART', $rdate); } elseif (substr($rule[6], 0, 4) == 'last') { // Rule starts on the last of a certain weekday of the month. $weekday = $this->_weekdays[substr($rule[6], 4, 3)]; $last = new Horde_Date(array('year' => $rule[2], 'month' => $month, 'mday' => Horde_Date_Utils::daysInMonth($month, $rule[2]), 'hour' => $match[1], 'min' => $match[2], 'sec' => 0)); while ($last->dayOfWeek() != $weekday) { $last->mday--; } $component->setAttribute('DTSTART', $last); if ($rule[3][0] == 'm') { $until = ''; } else { $last = new Horde_Date(array('year' => $rule[3], 'month' => $month, 'mday' => Horde_Date_Utils::daysInMonth($month, $rule[2]), 'hour' => $match[1], 'min' => $match[2], 'sec' => 0), $tzid); while ($last->dayOfWeek() != $weekday) { $last->mday--; } $last->setTimezone('UTC'); $until = ';UNTIL=' . $last->format('Ymd\\THIs') . 'Z'; } $component->setAttribute('RRULE', 'FREQ=YEARLY;BYDAY=-1' . Horde_String::upper(substr($rule[6], 4, 2)) . ';BYMONTH=' . $month . $until); } elseif (strpos($rule[6], '>=')) { // Rule starts on a certain weekday after a certain day of // month. list($weekday, $day) = explode('>=', $rule[6]); $weekdayInt = $this->_weekdays[substr($weekday, 0, 3)]; $first = new Horde_Date(array('year' => $rule[2], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0)); while ($first->dayOfWeek() != $weekdayInt) { $first->mday++; } $component->setAttribute('DTSTART', $first); if ($rule[3][0] == 'm') { $until = ''; } else { $last = new Horde_Date(array('year' => $rule[3], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0), $tzid); while ($last->dayOfWeek() != $weekday) { $last->mday++; } $last->setTimezone('UTC'); $until = ';UNTIL=' . $last->format('Ymd\\THIs') . 'Z'; } for ($days = array(), $i = $day, $lastDay = min(Horde_Date_Utils::daysInMonth($month, $rule[2]), $i + 6); $day > 1 && $i <= $lastDay; $i++) { $days[] = $i; } $component->setAttribute('RRULE', 'FREQ=YEARLY;BYMONTH=' . $month . ($days ? ';BYMONTHDAY=' . implode(',', $days) : '') . ';BYDAY=1' . Horde_String::upper(substr($weekday, 0, 2)) . $until); } elseif (strpos($rule[6], '<=')) { // Rule starts on a certain weekday before a certain day of // month. list($weekday, $day) = explode('>=', $rule[6]); $weekdayInt = $this->_weekdays[substr($weekday, 0, 3)]; $last = new Horde_Date(array('year' => $rule[2], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0)); while ($last->dayOfWeek() != $weekdayInt) { $last->mday--; } $component->setAttribute('DTSTART', $last); if ($rule[3][0] == 'm') { $until = ''; } else { $last = new Horde_Date(array('year' => $rule[3], 'month' => $month, 'mday' => $day, 'hour' => $match[1], 'min' => $match[2], 'sec' => 0), $tzid); while ($last->dayOfWeek() != $weekday) { $last->mday--; } $last->setTimezone('UTC'); $until = ';UNTIL=' . $last->format('Ymd\\THIs') . 'Z'; } for ($days = array(), $i = 1; $i <= $day; $i++) { $days[] = $i; } $component->setAttribute('RRULE', 'FREQ=YEARLY;BYMONTH=' . $month . ';BYMONTHDAY=' . implode(',', $days) . ';BYDAY=-1' . Horde_String::upper(substr($weekday, 0, 2)) . $until); } $component->setAttribute('TZNAME', sprintf($name, $rule[9])); $tz->addComponent($component); } }
/** * Convert an ActiveSync contact message into a hash suitable for * importing via self::add(). * * @param Horde_ActiveSync_Message_Contact $message The contact message * object. * * @return array A contact hash. */ public function fromASContact(Horde_ActiveSync_Message_Contact $message) { $hash = array(); foreach (self::$_asMap as $turbaField => $asField) { if (!$message->isGhosted($asField)) { try { $hash[$turbaField] = $message->{$asField}; } catch (InvalidArgumentException $e) { } } } /* Requires special handling */ try { if ($message->getProtocolVersion() >= Horde_ActiveSync::VERSION_TWELVE) { if (!empty($message->airsyncbasebody)) { $hash['notes'] = $message->airsyncbasebody->data; } } else { $hash['notes'] = $message->body; } } catch (InvalidArgumentException $e) { } // picture ($message->picture *should* already be base64 encdoed) if (!$message->isGhosted('picture')) { $hash['photo'] = base64_decode($message->picture); } /* Email addresses */ $hash['emails'] = array(); if (!$message->isGhosted('email1address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email1address); $hash['emails'][] = $hash['email'] = $e ? $e : ''; } if (!$message->isGhosted('email2address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email2address); $hash['emails'][] = $hash['homeEmail'] = $e ? $e : ''; } if (!$message->isGhosted('email3address')) { $e = Horde_Icalendar_Vcard::getBareEmail($message->email3address); $hash['emails'][] = $hash['workEmail'] = $e ? $e : ''; } $hash['emails'] = implode(',', $hash['emails']); /* Categories */ if (is_array($message->categories) && count($message->categories)) { $hash['__tags'] = $message->categories; } /* Children */ if (is_array($message->children) && count($message->children)) { // We use a comma as incoming delimiter as it's the most // common even though it might be used withing a name string. $hash['children'] = implode(', ', $message->children); } elseif (!$message->isGhosted('children')) { $hash['children'] = ''; } /* Birthday and Anniversary */ if (!empty($message->birthday)) { $bday = new Horde_Date($message->birthday); $bday->setTimezone(date_default_timezone_get()); $hash['birthday'] = $bday->format('Y-m-d'); } elseif (!$message->isGhosted('birthday')) { $hash['birthday'] = ''; } if (!empty($message->anniversary)) { $anniversary = new Horde_Date($message->anniversary); $anniversary->setTimezone(date_default_timezone_get()); $hash['anniversary'] = $anniversary->format('Y-m-d'); } elseif (!$message->isGhosted('anniversary')) { $hash['anniversary'] = ''; } /* Countries */ include 'Horde/Nls/Countries.php'; if (!empty($message->homecountry)) { if (!empty($this->map['homeCountryFree'])) { $hash['homeCountryFree'] = $message->homecountry; } else { $country = array_search($message->homecountry, $countries); if ($country === false) { $country = $message->homecountry; } $hash['homeCountry'] = $country; } } elseif (!$message->isGhosted('homecountry')) { $hash['homeCountry'] = ''; } if (!empty($message->businesscountry)) { if (!empty($this->map['workCountryFree'])) { $hash['workCountryFree'] = $message->businesscountry; } else { $country = array_search($message->businesscountry, $countries); if ($country === false) { $country = $message->businesscountry; } $hash['workCountry'] = $country; } } elseif (!$message->isGhosted('businesscountry')) { $hash['workCountry'] = ''; } if (!empty($message->othercountry)) { if (!empty($this->map['otherCountryFree'])) { $hash['otherCountryFree'] = $message->othercountry; } else { $country = array_search($message->othercountry, $countries); if ($country === false) { $country = $message->othercountry; } $hash['otherCountry'] = $country; } } elseif (!$message->isGhosted('othercountry')) { $hash['otherCountry'] = ''; } return $hash; }
/** * Creates a new event that represents an exception to a recurring event. * * @param Kronolith_Event $event The original recurring event. * @param Kronolith_Event $copy If present, contains a copy of $event, but * with changes from edited event form. * @param stdClass $attributes The attributes passed from the client. * Expected to contain rstart and rend or * rday that represents the original * starting/ending date of the instance. * * @return Kronolith_Event The event representing the exception */ protected function _copyEvent(Kronolith_Event $event, Kronolith_Event $copy = null, $attributes = null) { if (empty($copy)) { $copy = clone $event; } if ($attributes->rstart) { $rstart = new Horde_Date($attributes->rstart); $rstart->setTimezone($event->start->timezone); $rend = new Horde_Date($attributes->rend); $rend->setTimezone($event->end->timezone); } else { $rstart = new Horde_Date($attributes->rday); $rstart->setTimezone($event->start->timezone); $rstart->hour = $event->start->hour; $rstart->min = $event->start->min; $rend = $rstart->add($event->getDuration); $rend->setTimezone($event->end->timezone); $rend->hour = $event->end->hour; $rend->min = $event->end->min; } $uid = $event->uid; $otime = $event->start->strftime('%T'); // Create new event for the exception $nevent = $event->getDriver()->getEvent(); $nevent->baseid = $uid; $nevent->exceptionoriginaldate = new Horde_Date($rstart->strftime('%Y-%m-%d') . 'T' . $otime); $nevent->exceptionoriginaldate->setTimezone($event->start->timezone); $nevent->creator = $event->creator; $nevent->title = $copy->title; $nevent->description = $copy->description; $nevent->location = $copy->location; $nevent->private = $copy->private; $nevent->url = $copy->url; $nevent->status = $copy->status; $nevent->attendees = $copy->attendees; $nevent->setResources($copy->getResources()); $nevent->start = $rstart; $nevent->end = $rend; $nevent->initialized = true; return $nevent; }
/** * Accessor * * @param string $property Property to get * * @return mixed The property value */ public function __get($property) { // Maybe someday I can add a better $_map array with 'type' fields etc.. // for now, just as easy to manually check for these exceptions. switch ($property) { case 'logo_url': return $this->_properties['image']->url; case 'time': return new Horde_Date($this->_properties['observation_time_rfc822'], $this->_properties['local_tz_long']); case 'time_utc': $date = new Horde_Date($this->_properties['observation_time_rfc822'], $this->_properties['local_tz_long']); $date->setTimezone('UTC'); return $date; case 'temp': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return round($this->_properties['temp_f']); } return round($this->_properties['temp_c']); case 'wind_speed': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['wind_mph']; } return $this->_properties['wind_kph']; case 'wind_gust': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['wind_gust_mph']; } return $this->_properties['wind_gust_kph']; case 'dewpoint': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['dewpoint_f']; } return $this->_properties['dewpoint_c']; case 'heat_index': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['heat_index_f']; } return $this->_properties['heat_index_c']; case 'wind_chill': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['wind_chill_f']; } return $this->_properties['wind_chill_c']; case 'visibility': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return round($this->_properties['visibility_mi']); } return round($this->_properties['visibility_km']); case 'pressure': if ($this->_weather->units == Horde_Service_Weather::UNITS_STANDARD) { return $this->_properties['pressure_in']; } return $this->_properties['pressure_mb']; case 'pressure_trend': switch ($this->_properties['pressure_trend']) { case '0': return Horde_Service_Weather_Translation::t('steady'); case '+': return Horde_Service_Weather_Translation::t('rising'); case '-': return Horde_Service_Weather_Translation::t('falling'); } break; case 'icon': return $this->_weather->iconMap[$this->_properties['icon']]; default: if (empty($this->_map[$property])) { throw new Horde_Service_Weather_Exception_InvalidProperty(); } if (strpos($this->_properties[$this->_map[$property]], '-999') !== false) { return Horde_Service_Weather_Translation::t('N/A'); } return Horde_Service_Weather_Translation::t($this->_properties[$this->_map[$property]]); } }
public function testSetTimezone() { $oldTimezone = date_default_timezone_get(); date_default_timezone_set('America/New_York'); $date = new Horde_Date('20010203040506'); $this->assertEquals('2001-02-03 04:05:06', (string) $date); $date->setTimezone('Europe/Berlin'); $this->assertEquals('2001-02-03 10:05:06', (string) $date); $date = new Horde_Date('20010203040506', 'UTC'); $this->assertEquals('2001-02-03 04:05:06', (string) $date); $date->setTimezone('Europe/Berlin'); $this->assertEquals('2001-02-03 05:05:06', (string) $date); date_default_timezone_set($oldTimezone); }
public function testNextRecurrenceDifferentTimezones() { // Every 2 week recurring event that starts at 1am localtime date_default_timezone_set('America/New_York'); $rrule = new Horde_Date_Recurrence('2009-12-30 00:00:00'); $rrule->setRecurType(Horde_Date_Recurrence::RECUR_WEEKLY); $rrule->setRecurOnDay(Horde_Date::MASK_WEDNESDAY); $rrule->setRecurInterval(2); // Date to get recurrences after in UTC $after = new Horde_Date('2011-02-09'); $after->setTimezone('UTC'); $next = $rrule->nextRecurrence($after); $this->assertInstanceOf('Horde_Date', $next); $this->assertEquals($next->mday, 9); date_default_timezone_set('Europe/Berlin'); }