1<?php 2/********************************************************************* 3 class.timezone.php 4 5 Database time zone get utils. 6 7 Peter Rotich <peter@osticket.com> 8 Copyright (c) 2006-2013 osTicket 9 http://www.osticket.com 10 11 Released under the GNU General Public License WITHOUT ANY WARRANTY. 12 See LICENSE.TXT for details. 13 14 vim: expandtab sw=4 ts=4 sts=4: 15**********************************************************************/ 16 17// This class adopted from jstimezone 18 19class DbTimezone { 20 const HEMISPHERE_SOUTH = 's'; 21 const DAY = 86400; 22 const HOUR = 3600; 23 const MINUTE = 60; 24 const SECOND = 1; 25 const BASELINE_YEAR = 2014; 26 const MAX_SCORE = 864000; // 10 days 27 28 static $AMBIGUITIES = array( 29 'America/Denver' => array('America/Mazatlan'), 30 'America/Chicago' => array('America/Mexico_City'), 31 'America/Santiago' => array('America/Asuncion', 'America/Campo_Grande'), 32 'America/Montevideo' => array('America/Sao_Paulo'), 33 // Europe/Minsk should not be in this list... but Windows. 34 'Asia/Beirut' => array('Asia/Amman', 'Asia/Jerusalem', 'Europe/Helsinki', 'Asia/Damascus', 'Africa/Cairo', 'Asia/Gaza', 'Europe/Minsk'), 35 'Pacific/Auckland' => array('Pacific/Fiji'), 36 'America/Los_Angeles' => array('America/Santa_Isabel'), 37 'America/New_York' => array('America/Havana'), 38 'America/Halifax' => array('America/Goose_Bay'), 39 'America/Godthab' => array('America/Miquelon'), 40 'Asia/Dubai' => array('Asia/Yerevan'), 41 'Asia/Jakarta' => array('Asia/Krasnoyarsk'), 42 'Asia/Shanghai' => array('Asia/Irkutsk', 'Australia/Perth'), 43 'Australia/Sydney' => array('Australia/Lord_Howe'), 44 'Asia/Tokyo' => array('Asia/Yakutsk'), 45 'Asia/Dhaka' => array('Asia/Omsk'), 46 // In the real world Yerevan is not ambigous for Baku... but Windows. 47 'Asia/Baku' => array('Asia/Yerevan'), 48 'Australia/Brisbane' => array('Asia/Vladivostok'), 49 'Pacific/Noumea' => array('Asia/Vladivostok'), 50 'Pacific/Majuro' => array('Asia/Kamchatka', 'Pacific/Fiji'), 51 'Pacific/Tongatapu' => array('Pacific/Apia'), 52 'Asia/Baghdad' => array('Europe/Minsk', 'Europe/Moscow'), 53 'Asia/Karachi' => array('Asia/Yekaterinburg'), 54 'Africa/Johannesburg' => array('Asia/Gaza', 'Africa/Cairo') 55 ); 56 57 static $olsonTimezones = array( 58 '-720,0' => 'Etc/GMT+12', 59 '-660,0' => 'Pacific/Pago_Pago', 60 '-660,1,s' => 'Pacific/Apia', // Why? Because windows... cry! 61 '-600,1' => 'America/Adak', 62 '-600,0' => 'Pacific/Honolulu', 63 '-570,0' => 'Pacific/Marquesas', 64 '-540,0' => 'Pacific/Gambier', 65 '-540,1' => 'America/Anchorage', 66 '-480,1' => 'America/Los_Angeles', 67 '-480,0' => 'Pacific/Pitcairn', 68 '-420,0' => 'America/Phoenix', 69 '-420,1' => 'America/Denver', 70 '-360,0' => 'America/Guatemala', 71 '-360,1' => 'America/Chicago', 72 '-360,1,s' => 'Pacific/Easter', 73 '-300,0' => 'America/Bogota', 74 '-300,1' => 'America/New_York', 75 '-270,0' => 'America/Caracas', 76 '-240,1' => 'America/Halifax', 77 '-240,0' => 'America/Santo_Domingo', 78 '-240,1,s' => 'America/Santiago', 79 '-210,1' => 'America/St_Johns', 80 '-180,1' => 'America/Godthab', 81 '-180,0' => 'America/Argentina/Buenos_Aires', 82 '-180,1,s' => 'America/Montevideo', 83 '-120,0' => 'America/Noronha', 84 '-120,1' => 'America/Noronha', 85 '-60,1' => 'Atlantic/Azores', 86 '-60,0' => 'Atlantic/Cape_Verde', 87 '0,0' => 'UTC', 88 '0,1' => 'Europe/London', 89 '60,1' => 'Europe/Berlin', 90 '60,0' => 'Africa/Lagos', 91 '60,1,s' => 'Africa/Windhoek', 92 '120,1' => 'Asia/Beirut', 93 '120,0' => 'Africa/Johannesburg', 94 '180,0' => 'Asia/Baghdad', 95 '180,1' => 'Europe/Moscow', 96 '210,1' => 'Asia/Tehran', 97 '240,0' => 'Asia/Dubai', 98 '240,1' => 'Asia/Baku', 99 '270,0' => 'Asia/Kabul', 100 '300,1' => 'Asia/Yekaterinburg', 101 '300,0' => 'Asia/Karachi', 102 '330,0' => 'Asia/Kolkata', 103 '345,0' => 'Asia/Kathmandu', 104 '360,0' => 'Asia/Dhaka', 105 '360,1' => 'Asia/Omsk', 106 '390,0' => 'Asia/Rangoon', 107 '420,1' => 'Asia/Krasnoyarsk', 108 '420,0' => 'Asia/Jakarta', 109 '480,0' => 'Asia/Shanghai', 110 '480,1' => 'Asia/Irkutsk', 111 '525,0' => 'Australia/Eucla', 112 '525,1,s' => 'Australia/Eucla', 113 '540,1' => 'Asia/Yakutsk', 114 '540,0' => 'Asia/Tokyo', 115 '570,0' => 'Australia/Darwin', 116 '570,1,s' => 'Australia/Adelaide', 117 '600,0' => 'Australia/Brisbane', 118 '600,1' => 'Asia/Vladivostok', 119 '600,1,s' => 'Australia/Sydney', 120 '630,1,s' => 'Australia/Lord_Howe', 121 '660,1' => 'Asia/Kamchatka', 122 '660,0' => 'Pacific/Noumea', 123 '690,0' => 'Pacific/Norfolk', 124 '720,1,s' => 'Pacific/Auckland', 125 '720,0' => 'Pacific/Majuro', 126 '765,1,s' => 'Pacific/Chatham', 127 '780,0' => 'Pacific/Tongatapu', 128 '780,1,s' => 'Pacific/Apia', 129 '840,0' => 'Pacific/Kiritimati' 130 ); 131 132 133 function get_date_offset($checks) { 134 static $fragment = 135 "time_to_sec(timediff('%s', convert_tz('%s', @@session.time_zone, '+00:00'))) DIV 60"; 136 137 if (!is_array($checks)) 138 $checks = func_get_args(); 139 $dates = array(); 140 foreach ($checks as $time) { 141 $date = date('Y-m-d h:i:s', $time); 142 $dates[] = sprintf($fragment, $date, $date); 143 } 144 145 $sql = 'SELECT '.implode(',', $dates); 146 return db_fetch_row(db_query($sql)); 147 } 148 149 function lookup_key() { 150 list($january_offset, $june_offset) = 151 $this->get_date_offset( 152 mktime(0, 0, 0, 1, 2, self::BASELINE_YEAR), 153 mktime(0, 0, 0, 6, 2, self::BASELINE_YEAR)); 154 $diff = $january_offset - $june_offset; 155 156 if ($diff < 0) { 157 return $january_offset . ",1"; 158 } else if ($diff > 0) { 159 return $june_offset . ",1," . self::HEMISPHERE_SOUTH; 160 } 161 162 return $january_offset . ",0"; 163 } 164 165 function get_from_database() { 166 // Attempt to fetch timezone direct from the database 167 $TZ = db_timezone(); 168 169 // Translate ambiguous 'GMT' timezone 170 if ($TZ === 'GMT') { 171 // PHP assumes GMT == UTC, MySQL assumes GMT == Europe/London. 172 // To shore up the difference, assuming use of MySQL, use the 173 // timezone in PHP which honors BST (British Summer Time) 174 return 'Europe/London'; 175 } 176 177 return Format::timezone($TZ); 178 } 179 180 function dst_dates($year) { 181 $yearstart = mktime(0, 0, 1, 1, 1, $year); 182 $yearend = mktime(23, 59, 59, 12, 31, $year); 183 $current = $yearstart; 184 list($date_offset) = $this->get_date_offset($current); 185 $dst_start = null; 186 $dst_end = null; 187 188 $checks = array(); 189 while ($current < $yearend - 86400) { 190 $checks[] = $current; 191 $current += 86400; 192 } 193 194 foreach ($this->get_date_offset($checks) as $i=>$offset) { 195 if ($offset !== $date_offset) { 196 if ($offset < $date_offset) { 197 $dst_start = $checks[$i]; 198 } 199 if ($offset > $date_offset) { 200 $dst_end = $checks[$i]; 201 } 202 } 203 } 204 // $offset will remain the last item in ::get_date_offset($checks) 205 206 if ($dst_start && $dst_end) { 207 return array( 208 's' => $this->find_dst_fold($dst_start), 209 'e' => $this->find_dst_fold($dst_end), 210 ); 211 } 212 213 return false; 214 } 215 216 function find_dst_fold($a_date, $padding=self::DAY, $iterator=self::HOUR) { 217 $date_start = $a_date - $padding; 218 $date_end = $a_date + $padding; 219 list($date_offset) = $this->get_date_offset($date_start); 220 221 $current = $date_start; 222 223 $dst_change = null; 224 while ($current < $date_end - $iterator) { 225 $checks = array(); 226 for ($i=0; $i<12; $i++) { 227 $checks[] = $current; 228 $current += $iterator; 229 } 230 231 foreach ($this->get_date_offset($checks) as $i=>$offset) { 232 if ($offset !== $date_offset) { 233 $dst_change = $checks[$i]; 234 break; 235 } 236 } 237 if ($dst_change) 238 break; 239 } 240 241 if ($padding === self::DAY) { 242 return $this->find_dst_fold($dst_change, self::HOUR, self::MINUTE); 243 } 244 245 if ($padding === self::HOUR) { 246 return $this->find_dst_fold($dst_change, self::MINUTE, self::SECOND); 247 } 248 249 return $dst_change; 250 } 251 252 function windows7_adaptations($rule_list, $preliminary_timezone, $score, $sample) { 253 if ($score !== 'N/A') { 254 return $score; 255 } 256 if ($preliminary_timezone === 'Asia/Beirut') { 257 if ($sample['name'] === 'Africa/Cairo') { 258 if ($rule_list[6]['s'] === 1398376800 && $rule_list[6]['e'] === 1411678800) { 259 return 0; 260 } 261 } 262 if ($sample['name'] === 'Asia/Jerusalem') { 263 if ($rule_list[6]['s'] === 1395964800 && $rule_list[6]['e'] === 1411858800) { 264 return 0; 265 } 266 } 267 } else if ($preliminary_timezone === 'America/Santiago') { 268 if ($sample['name'] === 'America/Asuncion') { 269 if ($rule_list[6]['s'] === 1412481600 && $rule_list[6]['e'] === 1397358000) { 270 return 0; 271 } 272 } 273 if ($sample['name'] === 'America/Campo_Grande') { 274 if ($rule_list[6]['s'] === 1413691200 && $rule_list[6]['e'] === 1392519600) { 275 return 0; 276 } 277 } 278 } else if ($preliminary_timezone === 'America/Montevideo') { 279 if ($sample['name'] === 'America/Sao_Paulo') { 280 if ($rule_list[6]['s'] === 1413687600 && $rule_list[6]['e'] === 1392516000) { 281 return 0; 282 } 283 } 284 } else if ($preliminary_timezone === 'Pacific/Auckland') { 285 if ($sample['name'] === 'Pacific/Fiji') { 286 if ($rule_list[6]['s'] === 1414245600 && $rule_list[6]['e'] === 1396101600) { 287 return 0; 288 } 289 } 290 } 291 292 return $score; 293 } 294 295 function best_dst_match($rule_list, $preliminary_timezone) { 296 $self = $this; 297 $score_sample = function ($sample) use ($rule_list, $self, $preliminary_timezone) { 298 $score = 0; 299 300 for ($j = 0; $j < count($rule_list); $j++) { 301 302 // Both sample and current time zone report DST during the year. 303 if (!!$sample['rules'][$j] && !!$rule_list[$j]) { 304 305 // The current time zone's DST rules are inside the sample's. Include. 306 if ($rule_list[$j]['s'] >= $sample['rules'][$j]['s'] && $rule_list[$j]['e'] <= $sample['rules'][$j]['e']) { 307 $score = 0; 308 $score += abs($rule_list[$j]['s'] - $sample['rules'][$j]['s']); 309 $score += abs($sample['rules'][$j]['e'] - $rule_list[$j]['e']); 310 311 // The current time zone's DST rules are outside the sample's. Discard. 312 } else { 313 $score = 'N/A'; 314 break; 315 } 316 317 // The max score has been reached. Discard. 318 if ($score > self::MAX_SCORE) { 319 $score = 'N/A'; 320 break; 321 } 322 } 323 } 324 325 $score = $self->windows7_adaptations($rule_list, $preliminary_timezone, $score, $sample); 326 327 return $score; 328 }; 329 $scoreboard = array(); 330 $dst_zones = self::$dst_rules['zones']; 331 $ambiguities = @self::$AMBIGUITIES[$preliminary_timezone]; 332 333 foreach ($dst_zones as $sample) { 334 $score = $score_sample($sample); 335 336 if ($score !== 'N/A') { 337 $scoreboard[$sample['name']] = $score; 338 } 339 } 340 341 foreach ($scoreboard as $tz) { 342 if (in_array($tz, $ambiguities)) { 343 return $tz; 344 } 345 } 346 347 return $preliminary_timezone; 348 } 349 350 function get_by_dst($preliminary_timezone) { 351 $rules = array(); 352 foreach (self::$dst_rules['years'] as $Y) { 353 $rules[] = $this->dst_dates($Y); 354 } 355 $has_dst = false; 356 foreach ($rules as $R) { 357 if ($R !== false) { 358 $has_dst = true; break; 359 } 360 } 361 362 if ($has_dst) { 363 return $this->best_dst_match($rules, $preliminary_timezone); 364 } 365 366 return $preliminary_timezone; 367 } 368 369 static function determine() { 370 $self = new static(); 371 $preliminary_tz = $self->get_from_database(); 372 373 if (!$preliminary_tz) { 374 $preliminary_tz = self::$olsonTimezones[$self->lookup_key()]; 375 376 if (isset(self::$AMBIGUITIES[$preliminary_tz])) { 377 $preliminary_tz = $self->get_by_dst($preliminary_tz); 378 } 379 } 380 381 return $preliminary_tz; 382 } 383 384 // Rules compiled from jstz rules.js file by 385 // str_replace('000,', ',', var_export(json_decode('...', true))); 386 static $dst_rules = array('years'=>array(0=>2008,1=>2009,2=>2010,3=>2011,4=>2012,5=>2013,6=>2014,),'zones'=>array(0=>array('name'=>'Africa/Cairo','rules'=>array(0=>array('e'=>1219957200,'s'=>1209074400,),1=>array('e'=>1250802000,'s'=>1240524000,),2=>array('e'=>1285880400,'s'=>1284069600,),3=>false,4=>false,5=>false,6=>array('e'=>1411678800,'s'=>1406844000,),),),1=>array('name'=>'America/Asuncion','rules'=>array(0=>array('e'=>1205031600,'s'=>1224388800,),1=>array('e'=>1236481200,'s'=>1255838400,),2=>array('e'=>1270954800,'s'=>1286078400,),3=>array('e'=>1302404400,'s'=>1317528000,),4=>array('e'=>1333854000,'s'=>1349582400,),5=>array('e'=>1364094000,'s'=>1381032000,),6=>array('e'=>1395543600,'s'=>1412481600,),),),2=>array('name'=>'America/Campo_Grande','rules'=>array(0=>array('e'=>1203217200,'s'=>1224388800,),1=>array('e'=>1234666800,'s'=>1255838400,),2=>array('e'=>1266721200,'s'=>1287288000,),3=>array('e'=>1298170800,'s'=>1318737600,),4=>array('e'=>1330225200,'s'=>1350792000,),5=>array('e'=>1361070000,'s'=>1382241600,),6=>array('e'=>1392519600,'s'=>1413691200,),),),3=>array('name'=>'America/Goose_Bay','rules'=>array(0=>array('e'=>1225594860,'s'=>1205035260,),1=>array('e'=>1257044460,'s'=>1236484860,),2=>array('e'=>1289098860,'s'=>1268539260,),3=>array('e'=>1320555600,'s'=>1299988860,),4=>array('e'=>1352005200,'s'=>1331445600,),5=>array('e'=>1383454800,'s'=>1362895200,),6=>array('e'=>1414904400,'s'=>1394344800,),),),4=>array('name'=>'America/Havana','rules'=>array(0=>array('e'=>1224997200,'s'=>1205643600,),1=>array('e'=>1256446800,'s'=>1236488400,),2=>array('e'=>1288501200,'s'=>1268542800,),3=>array('e'=>1321160400,'s'=>1300597200,),4=>array('e'=>1352005200,'s'=>1333256400,),5=>array('e'=>1383454800,'s'=>1362891600,),6=>array('e'=>1414904400,'s'=>1394341200,),),),5=>array('name'=>'America/Mazatlan','rules'=>array(0=>array('e'=>1225008000,'s'=>1207472400,),1=>array('e'=>1256457600,'s'=>1238922000,),2=>array('e'=>1288512000,'s'=>1270371600,),3=>array('e'=>1319961600,'s'=>1301821200,),4=>array('e'=>1351411200,'s'=>1333270800,),5=>array('e'=>1382860800,'s'=>1365325200,),6=>array('e'=>1414310400,'s'=>1396774800,),),),6=>array('name'=>'America/Mexico_City','rules'=>array(0=>array('e'=>1225004400,'s'=>1207468800,),1=>array('e'=>1256454000,'s'=>1238918400,),2=>array('e'=>1288508400,'s'=>1270368000,),3=>array('e'=>1319958000,'s'=>1301817600,),4=>array('e'=>1351407600,'s'=>1333267200,),5=>array('e'=>1382857200,'s'=>1365321600,),6=>array('e'=>1414306800,'s'=>1396771200,),),),7=>array('name'=>'America/Miquelon','rules'=>array(0=>array('e'=>1225598400,'s'=>1205038800,),1=>array('e'=>1257048000,'s'=>1236488400,),2=>array('e'=>1289102400,'s'=>1268542800,),3=>array('e'=>1320552000,'s'=>1299992400,),4=>array('e'=>1352001600,'s'=>1331442000,),5=>array('e'=>1383451200,'s'=>1362891600,),6=>array('e'=>1414900800,'s'=>1394341200,),),),8=>array('name'=>'America/Santa_Isabel','rules'=>array(0=>array('e'=>1225011600,'s'=>1207476000,),1=>array('e'=>1256461200,'s'=>1238925600,),2=>array('e'=>1288515600,'s'=>1270375200,),3=>array('e'=>1319965200,'s'=>1301824800,),4=>array('e'=>1351414800,'s'=>1333274400,),5=>array('e'=>1382864400,'s'=>1365328800,),6=>array('e'=>1414314000,'s'=>1396778400,),),),9=>array('name'=>'America/Sao_Paulo','rules'=>array(0=>array('e'=>1203213600,'s'=>1224385200,),1=>array('e'=>1234663200,'s'=>1255834800,),2=>array('e'=>1266717600,'s'=>1287284400,),3=>array('e'=>1298167200,'s'=>1318734000,),4=>array('e'=>1330221600,'s'=>1350788400,),5=>array('e'=>1361066400,'s'=>1382238000,),6=>array('e'=>1392516000,'s'=>1413687600,),),),10=>array('name'=>'Asia/Amman','rules'=>array(0=>array('e'=>1225404000,'s'=>1206655200,),1=>array('e'=>1256853600,'s'=>1238104800,),2=>array('e'=>1288303200,'s'=>1269554400,),3=>array('e'=>1319752800,'s'=>1301608800,),4=>false,5=>false,6=>array('e'=>1414706400,'s'=>1395957600,),),),11=>array('name'=>'Asia/Damascus','rules'=>array(0=>array('e'=>1225486800,'s'=>1207260000,),1=>array('e'=>1256850000,'s'=>1238104800,),2=>array('e'=>1288299600,'s'=>1270159200,),3=>array('e'=>1319749200,'s'=>1301608800,),4=>array('e'=>1351198800,'s'=>1333058400,),5=>array('e'=>1382648400,'s'=>1364508000,),6=>array('e'=>1414702800,'s'=>1395957600,),),),12=>array('name'=>'Asia/Dubai','rules'=>array(0=>false,1=>false,2=>false,3=>false,4=>false,5=>false,6=>false,),),13=>array('name'=>'Asia/Gaza','rules'=>array(0=>array('e'=>1219957200,'s'=>1206655200,),1=>array('e'=>1252015200,'s'=>1238104800,),2=>array('e'=>1281474000,'s'=>1269640860,),3=>array('e'=>1312146000,'s'=>1301608860,),4=>array('e'=>1348178400,'s'=>1333058400,),5=>array('e'=>1380229200,'s'=>1364508000,),6=>array('e'=>1411678800,'s'=>1395957600,),),),14=>array('name'=>'Asia/Irkutsk','rules'=>array(0=>array('e'=>1224957600,'s'=>1206813600,),1=>array('e'=>1256407200,'s'=>1238263200,),2=>array('e'=>1288461600,'s'=>1269712800,),3=>false,4=>false,5=>false,6=>false,),),15=>array('name'=>'Asia/Jerusalem','rules'=>array(0=>array('e'=>1223161200,'s'=>1206662400,),1=>array('e'=>1254006000,'s'=>1238112000,),2=>array('e'=>1284246000,'s'=>1269561600,),3=>array('e'=>1317510000,'s'=>1301616000,),4=>array('e'=>1348354800,'s'=>1333065600,),5=>array('e'=>1382828400,'s'=>1364515200,),6=>array('e'=>1414278000,'s'=>1395964800,),),),16=>array('name'=>'Asia/Kamchatka','rules'=>array(0=>array('e'=>1224943200,'s'=>1206799200,),1=>array('e'=>1256392800,'s'=>1238248800,),2=>array('e'=>1288450800,'s'=>1269698400,),3=>false,4=>false,5=>false,6=>false,),),17=>array('name'=>'Asia/Krasnoyarsk','rules'=>array(0=>array('e'=>1224961200,'s'=>1206817200,),1=>array('e'=>1256410800,'s'=>1238266800,),2=>array('e'=>1288465200,'s'=>1269716400,),3=>false,4=>false,5=>false,6=>false,),),18=>array('name'=>'Asia/Omsk','rules'=>array(0=>array('e'=>1224964800,'s'=>1206820800,),1=>array('e'=>1256414400,'s'=>1238270400,),2=>array('e'=>1288468800,'s'=>1269720000,),3=>false,4=>false,5=>false,6=>false,),),19=>array('name'=>'Asia/Vladivostok','rules'=>array(0=>array('e'=>1224950400,'s'=>1206806400,),1=>array('e'=>1256400000,'s'=>1238256000,),2=>array('e'=>1288454400,'s'=>1269705600,),3=>false,4=>false,5=>false,6=>false,),),20=>array('name'=>'Asia/Yakutsk','rules'=>array(0=>array('e'=>1224954000,'s'=>1206810000,),1=>array('e'=>1256403600,'s'=>1238259600,),2=>array('e'=>1288458000,'s'=>1269709200,),3=>false,4=>false,5=>false,6=>false,),),21=>array('name'=>'Asia/Yekaterinburg','rules'=>array(0=>array('e'=>1224968400,'s'=>1206824400,),1=>array('e'=>1256418000,'s'=>1238274000,),2=>array('e'=>1288472400,'s'=>1269723600,),3=>false,4=>false,5=>false,6=>false,),),22=>array('name'=>'Asia/Yerevan','rules'=>array(0=>array('e'=>1224972000,'s'=>1206828000,),1=>array('e'=>1256421600,'s'=>1238277600,),2=>array('e'=>1288476000,'s'=>1269727200,),3=>array('e'=>1319925600,'s'=>1301176800,),4=>false,5=>false,6=>false,),),23=>array('name'=>'Australia/Lord_Howe','rules'=>array(0=>array('e'=>1207407600,'s'=>1223134200,),1=>array('e'=>1238857200,'s'=>1254583800,),2=>array('e'=>1270306800,'s'=>1286033400,),3=>array('e'=>1301756400,'s'=>1317483000,),4=>array('e'=>1333206000,'s'=>1349537400,),5=>array('e'=>1365260400,'s'=>1380987000,),6=>array('e'=>1396710000,'s'=>1412436600,),),),24=>array('name'=>'Australia/Perth','rules'=>array(0=>array('e'=>1206813600,'s'=>1224957600,),1=>false,2=>false,3=>false,4=>false,5=>false,6=>false,),),25=>array('name'=>'Europe/Helsinki','rules'=>array(0=>array('e'=>1224982800,'s'=>1206838800,),1=>array('e'=>1256432400,'s'=>1238288400,),2=>array('e'=>1288486800,'s'=>1269738000,),3=>array('e'=>1319936400,'s'=>1301187600,),4=>array('e'=>1351386000,'s'=>1332637200,),5=>array('e'=>1382835600,'s'=>1364691600,),6=>array('e'=>1414285200,'s'=>1396141200,),),),26=>array('name'=>'Europe/Minsk','rules'=>array(0=>array('e'=>1224979200,'s'=>1206835200,),1=>array('e'=>1256428800,'s'=>1238284800,),2=>array('e'=>1288483200,'s'=>1269734400,),3=>false,4=>false,5=>false,6=>false,),),27=>array('name'=>'Europe/Moscow','rules'=>array(0=>array('e'=>1224975600,'s'=>1206831600,),1=>array('e'=>1256425200,'s'=>1238281200,),2=>array('e'=>1288479600,'s'=>1269730800,),3=>false,4=>false,5=>false,6=>false,),),28=>array('name'=>'Pacific/Apia','rules'=>array(0=>false,1=>false,2=>false,3=>array('e'=>1301752800,'s'=>1316872800,),4=>array('e'=>1333202400,'s'=>1348927200,),5=>array('e'=>1365256800,'s'=>1380376800,),6=>array('e'=>1396706400,'s'=>1411826400,),),),29=>array('name'=>'Pacific/Fiji','rules'=>array(0=>false,1=>false,2=>array('e'=>1269698400,'s'=>1287842400,),3=>array('e'=>1327154400,'s'=>1319292000,),4=>array('e'=>1358604000,'s'=>1350741600,),5=>array('e'=>1390050000,'s'=>1382796000,),6=>array('e'=>1421503200,'s'=>1414850400,),),),),); 387} 388 389?> 390