Textpattern | PHP Cross Reference | Content Management Systems |
Description: Handles timezones.
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * https://textpattern.com/ 6 * 7 * Copyright (C) 2020 The Textpattern Development Team 8 * 9 * This file is part of Textpattern. 10 * 11 * Textpattern is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation, version 2. 14 * 15 * Textpattern is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with Textpattern. If not, see <https://www.gnu.org/licenses/>. 22 */ 23 24 /** 25 * Handles timezones. 26 * 27 * This method extracts information from PHP's Timezone DB, and allows 28 * configuring server's timezone information. 29 * 30 * @package Date 31 * @since 4.6.0 32 */ 33 34 namespace Textpattern\Date; 35 36 class Timezone 37 { 38 /** 39 * Stores a list of details about each timezone. 40 * 41 * @var array 42 */ 43 44 protected $details; 45 46 /** 47 * Stores a list of timezone offsets. 48 * 49 * @var array 50 */ 51 52 protected $offsets; 53 54 /** 55 * An array of accepted continents. 56 * 57 * @var array 58 */ 59 60 protected $continents = array( 61 'Africa', 62 'America', 63 'Antarctica', 64 'Arctic', 65 'Asia', 66 'Atlantic', 67 'Australia', 68 'Europe', 69 'Indian', 70 'Pacific', 71 ); 72 73 /** 74 * Gets an array of safe timezones supported on this server. 75 * 76 * The following: 77 * 78 * <code> 79 * print_r(Txp::get('\Textpattern\Date\Timezone')->getTimeZones()); 80 * </code> 81 * 82 * Returns: 83 * 84 * <code> 85 * Array 86 * ( 87 * [America/New_York] => Array 88 * ( 89 * [continent] => America 90 * [city] => New_York 91 * [subcity] => 92 * [offset] => -18000 93 * [dst] => 1 94 * ) 95 * [Europe/London] => Array 96 * ( 97 * [continent] => Europe 98 * [city] => London 99 * [subcity] => 100 * [offset] => 0 101 * [dst] => 1 102 * ) 103 * ) 104 * </code> 105 * 106 * Offset is the timezone offset from UTC excluding daylight saving time, 107 * DST is whether it's currently DST in the timezone. Identifiers are 108 * sorted alphabetically. 109 * 110 * @return array|bool An array of timezones, or FALSE on failure 111 */ 112 113 public function getTimeZones() 114 { 115 if ($this->details === null) { 116 $this->details = array(); 117 118 if (($timezones = \DateTimeZone::listIdentifiers()) === false) { 119 return false; 120 } 121 122 foreach ($timezones as $timezone) { 123 $parts = explode('/', $timezone); 124 125 if (in_array($parts[0], $this->continents, true) && $data = $this->getIdentifier($timezone)) { 126 $this->details[$timezone] = $data; 127 128 if (!isset($this->offsets[$data['offset']])) { 129 $this->offsets[$data['offset']] = array(); 130 } 131 132 $this->offsets[$data['offset']][] = $timezone; 133 } 134 } 135 136 ksort($this->details); 137 } 138 139 return $this->details; 140 } 141 142 /** 143 * Gets timezone identifiers for the given timezone offset. 144 * 145 * More than one timezone might fit any given offset, thus the returned 146 * value is ambiguous and merely useful for presentation purposes. 147 * 148 * <code> 149 * print_r(Txp::get('\Textpattern\Date\Timezone')->getOffsetIdentifiers(3600)); 150 * </code> 151 * 152 * Returns: 153 * 154 * <code> 155 * Array 156 * ( 157 * [0] => Africa/Malabo 158 * [1] => Europe/Amsterdam 159 * [2] => Europe/Berlin 160 * [3] => Europe/Zurich 161 * ) 162 * </code> 163 * 164 * @param int $offset Offset in seconds 165 * @return array|bool An array of timezone identifiers, or FALSE 166 */ 167 168 public function getOffsetIdentifiers($offset) 169 { 170 if ($this->getTimeZones() && isset($this->offsets[$offset])) { 171 return $this->offsets[$offset]; 172 } 173 174 return false; 175 } 176 177 /** 178 * Whether DST is in effect. 179 * 180 * The given timestamp can either be a date format supported by DateTime, 181 * UNIX timestamp or NULL to check current status. 182 * 183 * If timezone is NULL, checks the server default timezone. 184 * 185 * <code> 186 * echo Txp::get('\Textpattern\Date\Timezone')->isDst('2013/06/20', 'Europe/London'); 187 * </code> 188 * 189 * Returns TRUE, while this returns FALSE as the timezone does not use 190 * daylight saving time: 191 * 192 * <code> 193 * echo Txp::get('\Textpattern\Date\Timezone')->isDst('2013/06/20', 'Africa/Accra'); 194 * </code> 195 * 196 * If it's winter this returns FALSE: 197 * 198 * <code> 199 * echo Txp::get('\Textpattern\Date\Timezone')->isDst(null, 'Europe/London'); 200 * </code> 201 * 202 * @param string|int|null $timestamp Time to check 203 * @param string|null $timezone Timezone identifier 204 * @return bool TRUE if timezone is using DST 205 */ 206 207 public function isDst($timestamp = null, $timezone = null) 208 { 209 static $DTZones = array(); 210 211 if (!$timezone) { 212 $timezone = $this->getTimeZone(); 213 } 214 215 if ($timestamp === null) { 216 $timestamp = time(); 217 } else { 218 if ((string)intval($timestamp) !== (string)$timestamp) { 219 $timestamp = strtotime($timestamp); 220 } 221 } 222 223 try { 224 if (!isset($DTZones[$timezone])) { 225 $DTZones[$timezone] = new \DateTimeZone($timezone); 226 } 227 $transition = $DTZones[$timezone]->getTransitions($timestamp, $timestamp); 228 $isdst = $transition[0]['isdst']; 229 } catch (\Exception $e) { 230 $isdst = false; 231 } 232 233 return (bool)$isdst; 234 } 235 236 /** 237 * Gets the next daylight saving transition period for the given timezone. 238 * 239 * Returns FALSE if the timezone does not use DST, or will in the future 240 * drop DST. 241 * 242 * <code> 243 * print_r(Txp::get('\Textpattern\Date\Timezone')->getDstPeriod('Europe/Helsinki')); 244 * </code> 245 * 246 * Returns: 247 * 248 * <code> 249 * Array 250 * ( 251 * [0] => Array 252 * ( 253 * [ts] => 1396141200 254 * [time] => 2014-03-30T01:00:00+0000 255 * [offset] => 10800 256 * [isdst] => 1 257 * [abbr] => EEST 258 * ) 259 * [1] => Array 260 * ( 261 * [ts] => 1414285200 262 * [time] => 2014-10-26T01:00:00+0000 263 * [offset] => 7200 264 * [isdst] => 265 * [abbr] => EET 266 * ) 267 * ) 268 * </code> 269 * 270 * @param string|null $timezone The timezone identifier 271 * @param int $from Next transitions starting from when 272 * @return array|bool An array of next two transitions, or FALSE 273 * @throws \Exception 274 */ 275 276 public function getDstPeriod($timezone = null, $from = null) 277 { 278 if (!$timezone) { 279 $timezone = $this->getTimeZone(); 280 } 281 282 $timezone = new \DateTimeZone($timezone); 283 284 if ($from === null) { 285 $from = time(); 286 } 287 288 $transitions = $timezone->getTransitions(); 289 $start = null; 290 $end = null; 291 292 foreach ($transitions as $transition) { 293 if ($start !== null) { 294 $end = $transition; 295 break; 296 } 297 298 if ($transition['ts'] >= $from && $transition['isdst']) { 299 $start = $transition; 300 } 301 } 302 303 if ($start) { 304 return array($start, $end); 305 } 306 307 return false; 308 } 309 310 /** 311 * Gets timezone abbreviation. 312 * 313 * If the $timezone is NULL, uses the server default. Returns FALSE if 314 * there is no abbreviation to give. 315 * 316 * <code> 317 * echo Txp::get('\Textpattern\Date\Timezone')->getTimeZoneAbbreviation('Europe/London'); 318 * </code> 319 * 320 * Returns 'GMT', while the following returns 'FALSE': 321 * 322 * <code> 323 * echo Txp::get('\Textpattern\Date\Timezone')->getTimeZoneAbbreviation('Africa/Accra', true); 324 * </code> 325 * 326 * As according to the timezone database, the timezone does not currently 327 * use DST. 328 * 329 * @param string $timezone Timezone identifier 330 * @param bool $dst TRUE to get the abbreviation during DST 331 * @return string|bool The abbreviation, or FALSE on failure 332 */ 333 334 public function getTimeZoneAbbreviation($timezone = null, $dst = false) 335 { 336 try { 337 if ($timezone === null) { 338 $timezone = $this->getTimeZone(); 339 } 340 341 $timezone = new \DateTimeZone($timezone); 342 $time = time(); 343 344 if ($transitions = $timezone->getTransitions()) { 345 $latest = end($transitions); 346 347 if ($latest['ts'] <= $time) { 348 $latest['ts'] = $time; 349 $transitions = array($latest); 350 } 351 352 foreach ($transitions as $transition) { 353 if ($time <= $transition['ts']) { 354 if ($dst === true && $transition['isdst']) { 355 return $transition['abbr']; 356 } 357 358 if ($dst === false && !$transition['isdst']) { 359 return $transition['abbr']; 360 } 361 } 362 } 363 } 364 } catch (\Exception $e) { 365 } 366 367 return false; 368 } 369 370 /** 371 * Gets a timezone identifier. 372 * 373 * Extracts information about the given timezone. If the $timezone is NULL, 374 * uses the server's default timezone. 375 * 376 * <code> 377 * print_r(Txp::get('\Textpattern\Date\Timezone')->getIdentifier('Europe/London')); 378 * </code> 379 * 380 * Returns: 381 * 382 * <code> 383 * Array 384 * ( 385 * [continent] => Europe 386 * [city] => London 387 * [subcity] => 388 * [offset] => 0 389 * [dst] => 1 390 * ) 391 * </code> 392 * 393 * @param string|null $timezone Timezone identifier 394 * @return array|bool An array, or FALSE on failure 395 */ 396 397 public function getIdentifier($timezone = null) 398 { 399 if ($timezone === null) { 400 $timezone = $this->getTimeZone(); 401 } 402 403 if (isset($this->details[$timezone])) { 404 return $this->details[$timezone]; 405 } 406 407 try { 408 $dateTime = new \DateTime('now', new \DateTimeZone($timezone)); 409 410 $data = array( 411 'continent' => '', 412 'city' => '', 413 'subcity' => '', 414 'offset' => $dateTime->getOffset(), 415 'dst' => false, 416 ); 417 418 if (strpos($timezone, '/') !== false) { 419 $parts = array_pad(explode('/', $timezone), 3, ''); 420 $data['continent'] = $parts[0]; 421 $data['city'] = $parts[1]; 422 $data['subcity'] = $parts[2]; 423 } 424 425 if ($dateTime->format('I')) { 426 $data['offset'] -= 3600; 427 $data['dst'] = true; 428 } 429 430 return $data; 431 } catch (\Exception $e) { 432 return false; 433 } 434 } 435 436 /** 437 * Sets the server default timezone. 438 * 439 * If an array of identifiers is given, the first one supported is used. 440 * 441 * <code> 442 * echo Txp::get('\Textpattern\Date\Timezone')->setTimeZone('UTC'); 443 * </code> 444 * 445 * Throws an exception if the identifier isn't valid. 446 * 447 * @param array|string $identifiers The timezone identifier 448 * @return Timezone 449 * @throws \Exception 450 */ 451 452 public function setTimeZone($identifiers) 453 { 454 foreach ((array)$identifiers as $identifier) { 455 if (@date_default_timezone_set($identifier)) { 456 return $this; 457 } 458 } 459 460 throw new \Exception(gTxt('invalid_argument', array('{name}' => 'identifiers'))); 461 } 462 463 /** 464 * Gets the server default timezone. 465 * 466 * <code> 467 * echo Txp::get('\Textpattern\Date\Timezone')->setTimeZone('Europe/London')->getTimeZone(); 468 * </code> 469 * 470 * The above returns 'Europe/London'. 471 * 472 * @return string|bool Timezone identifier 473 */ 474 475 public function getTimeZone() 476 { 477 return @date_default_timezone_get(); 478 } 479 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title