1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10:
11:
12: require_once('vCalendar.php');
13: require_once('RRule.php');
14:
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' );
18: }
19:
20: if ( empty($start) ) $start = sprintf( '%04d-01-01', date('Y'));
21: if ( empty($end) ) $end = sprintf( '%04d-12-31', date('Y') + 10);
22:
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);
35: }
36:
37: $tz = $qry->Fetch();
38:
39:
40: define( 'DEBUG_EXPAND', false );
41:
42:
43: 44: 45: 46: 47: 48: 49: 50: 51: 52:
53: function expand_timezone_onsets( vCalendar $vResource, RepeatRuleDateTime $range_start, RepeatRuleDateTime $range_end ) {
54: global $c;
55: $vtimezones = $vResource->GetComponents();
56: $vtz = $vtimezones[0];
57: $components = $vtz->GetComponents();
58:
59: $instances = array();
60: $dtstart = null;
61: $is_date = false;
62: $has_repeats = false;
63: $zone_tz = $vtz->GetPValue('TZID');
64:
65: foreach( $components AS $k => $comp ) {
66: if ( DEBUG_EXPAND ) {
67: printf( "Starting TZ expansion for component '%s' in timezone '%s'\n", $comp->GetType(), $zone_tz);
68: foreach( $instances AS $k => $v ) {
69: print ' : '.$k;
70: }
71: print "\n";
72: }
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));
79: $offset_from *= -1;
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;
88:
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 );
94: $i = 0;
95: $result_limit = 1000;
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;
99: }
100: if ( DEBUG_EXPAND ) {
101: print( "After rrule_expand");
102: foreach( $instances AS $k => $v ) {
103: print ' : '.$k;
104: }
105: print "\n";
106: }
107: }
108:
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;
120: }
121: }
122:
123: if ( DEBUG_EXPAND ) {
124: print( "After rdate_expand");
125: foreach( $instances AS $k => $v ) {
126: print ' : '.$k;
127: }
128: print "\n";
129: }
130: }
131: }
132:
133: ksort($instances);
134:
135: $onsets = array();
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");
141: break;
142: }
143:
144: if ( $utc < $start_utc ) {
145: continue;
146: }
147: $onsets[$utc] = array(
148: 'from' => $comp->GetPValue('TZOFFSETFROM'),
149: 'to' => $comp->GetPValue('TZOFFSETTO'),
150: 'name' => $comp->GetPValue('TZNAME'),
151: 'type' => $comp->GetType()
152: );
153: }
154:
155: return $onsets;
156: }
157:
158: header( 'ETag: "'.$tz->etag.'"' );
159: header( 'Last-Modified', $tz->last_modified );
160: header('Content-Type: application/xml; charset="utf-8"');
161:
162: $vtz = new vCalendar($tz->vtimezone);
163:
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);
170: }
171: else {
172: $timezones->NewElement('dtstamp', gmdate('Y-m-d\TH:i:s\Z'));
173: }
174:
175: $from = new RepeatRuleDateTime($start);
176: $until = new RepeatRuleDateTime($end);
177:
178: $observances = expand_timezone_onsets($vtz, $from, $until);
179: $tzdata = array();
180: $tzdata[] = new XMLElement( 'tzid', $tzid );
181: $tzdata[] = new XMLElement( 'calscale', 'Gregorian' );
182:
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) )
189: ));
190: }
191:
192: $timezones->NewElement('tzdata', $tzdata );
193: echo $response->Render($timezones);
194:
195: exit(0);
196: