Textpattern PHP Cross Reference Content Management Systems

Source: /textpattern/vendors/Textpattern/Date/Timezone.php - 479 lines - 12717 bytes - Summary - Text - Print

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

title

Description

title

Description

title

title

Body