3 * DAViCal Timezone Service handler - capabilitis
6 * @subpackage tzservice
7 * @author Andrew McMillan <andrew@morphoss.com>
8 * @copyright Morphoss Ltd
9 * @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
12 require_once('vCalendar.php');
13 require_once('RRule-v2.php');
15 if ( empty($format) ) $format = 'text/calendar';
16 if ( $format != 'text/calendar' ) {
17 $request->PreconditionFailed(403, 'supported-format', 'This server currently only supports text/calendar format.', 'urn:ietf:params:xml:ns:timezone-service' );
20 if ( empty($start) ) $start = sprintf( '%04d-01-01', date('Y'));
21 if ( empty($end) ) $end = sprintf( '%04d-12-31', date('Y') +
10);
23 $sql = 'SELECT our_tzno, tzid, active, olson_name, vtimezone, etag, ';
24 $sql .= 'to_char(last_modified,\'Dy, DD Mon IYYY HH24:MI:SS "GMT"\') AS last_modified ';
25 $sql .= 'FROM timezones WHERE tzid=:tzid';
26 $params = array( ':tzid' => $tzid );
27 $qry = new AwlQuery($sql,$params);
28 if ( !$qry->Exec() ) exit(1);
29 if ( $qry->rows() < 1 ) {
30 $sql = 'SELECT our_tzno, tzid, active, olson_name, vtimezone, etag, ';
31 $sql .= 'to_char(last_modified,\'Dy, DD Mon IYYY HH24:MI:SS "GMT"\') AS last_modified ';
32 $sql .= 'FROM timezones JOIN tz_aliases USING(our_tzno) WHERE tzalias=:tzid';
33 if ( !$qry->Exec() ) exit(1);
34 if ( $qry->rows() < 1 ) $request->DoResponse(404);
39 // define( 'DEBUG_EXPAND', true);
40 define( 'DEBUG_EXPAND', false );
44 * Expand the instances for a STANDARD or DAYLIGHT component of a VTIMEZONE
46 * @param object $vResource is a VCALENDAR with a VTIMEZONE containing components needing expansion
47 * @param object $range_start A RepeatRuleDateTime which is the beginning of the range for events.
48 * @param object $range_end A RepeatRuleDateTime which is the end of the range for events.
49 * @param int $offset_from The offset from UTC in seconds at the onset time.
51 * @return array of onset datetimes with UTC from/to offsets
53 function expand_timezone_onsets( vCalendar
$vResource, RepeatRuleDateTime
$range_start, RepeatRuleDateTime
$range_end ) {
55 $vtimezones = $vResource->GetComponents();
56 $vtz = $vtimezones[0];
57 $components = $vtz->GetComponents();
63 $zone_tz = $vtz->GetPValue('TZID');
65 foreach( $components AS $k => $comp ) {
67 printf( "Starting TZ expansion for component '%s' in timezone '%s'\n", $comp->GetType(), $zone_tz);
68 foreach( $instances AS $k => $v ) {
73 $dtstart_prop = $comp->GetProperty('DTSTART');
74 if ( !isset($dtstart_prop) ) continue;
75 $dtstart = new RepeatRuleDateTime( $dtstart_prop );
76 $dtstart->setTimeZone('UTC');
77 $offset_from = $comp->GetPValue('TZOFFSETFROM');
78 $offset_from = (($offset_from / 100) * 3600) +
((abs($offset_from) %
100) * 60 * ($offset_from < 0 ?
-1 : 0));
80 $offset_from = "$offset_from seconds";
81 dbg_error_log( 'tz/update', "%s of offset\n", $offset_from);
82 $dtstart->modify($offset_from);
83 $is_date = $dtstart->isDate();
84 $instances[$dtstart->UTC('Y-m-d\TH:i:s\Z')] = $comp;
85 $rrule = $comp->GetProperty('RRULE');
86 $has_repeats = isset($rrule);
87 if ( !$has_repeats ) continue;
89 $recur = $comp->GetProperty('RRULE');
90 if ( isset($recur) ) {
91 $recur = $recur->Value();
92 $this_start = clone($dtstart);
93 $rule = new RepeatRule( $this_start, $recur, $is_date );
96 while( $date = $rule->next() ) {
97 $instances[$date->UTC('Y-m-d\TH:i:s\Z')] = $comp;
98 if ( $i++
>= $result_limit ||
$date > $range_end ) break;
100 if ( DEBUG_EXPAND
) {
101 print( "After rrule_expand");
102 foreach( $instances AS $k => $v ) {
109 $properties = $comp->GetProperties('RDATE');
110 if ( count($properties) ) {
111 foreach( $properties AS $p ) {
112 $timezone = $p->GetParameterValue('TZID');
113 $rdate = $p->Value();
114 $rdates = explode( ',', $rdate );
115 foreach( $rdates AS $k => $v ) {
116 $rdate = new RepeatRuleDateTime( $v, $timezone, $is_date);
117 if ( $return_floating_times ) $rdate->setAsFloat();
118 $instances[$rdate->UTC('Y-m-d\TH:i:s\Z')] = $comp;
119 if ( $rdate > $range_end ) break;
123 if ( DEBUG_EXPAND
) {
124 print( "After rdate_expand");
125 foreach( $instances AS $k => $v ) {
136 $start_utc = $range_start->UTC('Y-m-d\TH:i:s\Z');
137 $end_utc = $range_end->UTC('Y-m-d\TH:i:s\Z');
138 foreach( $instances AS $utc => $comp ) {
139 if ( $utc > $end_utc ) {
140 if ( DEBUG_EXPAND
) printf( "We're done: $utc is out of the range.\n");
144 if ( $utc < $start_utc ) {
147 $onsets[$utc] = array(
148 'from' => $comp->GetPValue('TZOFFSETFROM'),
149 'to' => $comp->GetPValue('TZOFFSETTO'),
150 'name' => $comp->GetPValue('TZNAME'),
151 'type' => $comp->GetType()
158 header( 'ETag: "'.$tz->etag
.'"' );
159 header( 'Last-Modified', $tz->last_modified
);
160 header('Content-Type: application/xml; charset="utf-8"');
162 $vtz = new vCalendar($tz->vtimezone
);
164 $response = new XMLDocument(array("urn:ietf:params:xml:ns:timezone-service" => ""));
165 $timezones = $response->NewXMLElement('urn:ietf:params:xml:ns:timezone-service:timezones');
166 $qry = new AwlQuery('SELECT to_char(max(last_modified),\'YYYY-MM-DD"T"HH24:MI:SS"Z"\') AS dtstamp FROM timezones');
167 if ( $qry->Exec('tz/list',__LINE__
,__FILE__
) && $qry->rows() > 0 ) {
168 $row = $qry->Fetch();
169 $timezones->NewElement('dtstamp', $row->dtstamp
);
172 $timezones->NewElement('dtstamp', gmdate('Y-m-d\TH:i:s\Z'));
175 $from = new RepeatRuleDateTime($start);
176 $until = new RepeatRuleDateTime($end);
178 $observances = expand_timezone_onsets($vtz, $from, $until);
180 $tzdata[] = new XMLElement( 'tzid', $tzid );
181 $tzdata[] = new XMLElement( 'calscale', 'Gregorian' );
183 foreach( $observances AS $onset => $details ) {
184 $tzdata[] = new XMLElement( 'observance', array(
185 new XMLElement('name', (empty($details['name']) ?
$details['type'] : $details['name'] ) ),
186 new XMLElement('onset', $onset ),
187 new XMLElement('utc-offset-from', substr($details['from'],0,-2).':'.substr($details['from'],-2) ),
188 new XMLElement('utc-offset-to', substr($details['to'],0,-2).':'.substr($details['to'],-2) )
192 $timezones->NewElement('tzdata', $tzdata );
193 echo $response->Render($timezones);