[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/simplepie/simplepie/src/Parse/ -> Date.php (source)

   1  <?php
   2  
   3  declare(strict_types=1);
   4  /**
   5   * SimplePie
   6   *
   7   * A PHP-Based RSS and Atom Feed Framework.
   8   * Takes the hard work out of managing a complete RSS/Atom solution.
   9   *
  10   * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
  11   * All rights reserved.
  12   *
  13   * Redistribution and use in source and binary forms, with or without modification, are
  14   * permitted provided that the following conditions are met:
  15   *
  16   *     * Redistributions of source code must retain the above copyright notice, this list of
  17   *       conditions and the following disclaimer.
  18   *
  19   *     * Redistributions in binary form must reproduce the above copyright notice, this list
  20   *       of conditions and the following disclaimer in the documentation and/or other materials
  21   *       provided with the distribution.
  22   *
  23   *     * Neither the name of the SimplePie Team nor the names of its contributors may be used
  24   *       to endorse or promote products derived from this software without specific prior
  25   *       written permission.
  26   *
  27   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
  28   * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  29   * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
  30   * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  32   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  34   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  35   * POSSIBILITY OF SUCH DAMAGE.
  36   *
  37   * @package SimplePie
  38   * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
  39   * @author Ryan Parman
  40   * @author Sam Sneddon
  41   * @author Ryan McCue
  42   * @link http://simplepie.org/ SimplePie
  43   * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  44   */
  45  
  46  namespace SimplePie\Parse;
  47  
  48  /**
  49   * Date Parser
  50   *
  51   * @package SimplePie
  52   * @subpackage Parsing
  53   */
  54  class Date
  55  {
  56      /**
  57       * Input data
  58       *
  59       * @access protected
  60       * @var string
  61       */
  62      public $date;
  63  
  64      /**
  65       * List of days, calendar day name => ordinal day number in the week
  66       *
  67       * @access protected
  68       * @var array
  69       */
  70      public $day = [
  71          // English
  72          'mon' => 1,
  73          'monday' => 1,
  74          'tue' => 2,
  75          'tuesday' => 2,
  76          'wed' => 3,
  77          'wednesday' => 3,
  78          'thu' => 4,
  79          'thursday' => 4,
  80          'fri' => 5,
  81          'friday' => 5,
  82          'sat' => 6,
  83          'saturday' => 6,
  84          'sun' => 7,
  85          'sunday' => 7,
  86          // Dutch
  87          'maandag' => 1,
  88          'dinsdag' => 2,
  89          'woensdag' => 3,
  90          'donderdag' => 4,
  91          'vrijdag' => 5,
  92          'zaterdag' => 6,
  93          'zondag' => 7,
  94          // French
  95          'lundi' => 1,
  96          'mardi' => 2,
  97          'mercredi' => 3,
  98          'jeudi' => 4,
  99          'vendredi' => 5,
 100          'samedi' => 6,
 101          'dimanche' => 7,
 102          // German
 103          'montag' => 1,
 104          'mo' => 1,
 105          'dienstag' => 2,
 106          'di' => 2,
 107          'mittwoch' => 3,
 108          'mi' => 3,
 109          'donnerstag' => 4,
 110          'do' => 4,
 111          'freitag' => 5,
 112          'fr' => 5,
 113          'samstag' => 6,
 114          'sa' => 6,
 115          'sonnabend' => 6,
 116          // AFAIK no short form for sonnabend
 117          'so' => 7,
 118          'sonntag' => 7,
 119          // Italian
 120          'lunedì' => 1,
 121          'martedì' => 2,
 122          'mercoledì' => 3,
 123          'giovedì' => 4,
 124          'venerdì' => 5,
 125          'sabato' => 6,
 126          'domenica' => 7,
 127          // Spanish
 128          'lunes' => 1,
 129          'martes' => 2,
 130          'miércoles' => 3,
 131          'jueves' => 4,
 132          'viernes' => 5,
 133          'sábado' => 6,
 134          'domingo' => 7,
 135          // Finnish
 136          'maanantai' => 1,
 137          'tiistai' => 2,
 138          'keskiviikko' => 3,
 139          'torstai' => 4,
 140          'perjantai' => 5,
 141          'lauantai' => 6,
 142          'sunnuntai' => 7,
 143          // Hungarian
 144          'hétfő' => 1,
 145          'kedd' => 2,
 146          'szerda' => 3,
 147          'csütörtok' => 4,
 148          'péntek' => 5,
 149          'szombat' => 6,
 150          'vasárnap' => 7,
 151          // Greek
 152          'Δευ' => 1,
 153          'Τρι' => 2,
 154          'Τετ' => 3,
 155          'Πεμ' => 4,
 156          'Παρ' => 5,
 157          'Σαβ' => 6,
 158          'Κυρ' => 7,
 159          // Russian
 160          'Пн.' => 1,
 161          'Вт.' => 2,
 162          'Ср.' => 3,
 163          'Чт.' => 4,
 164          'Пт.' => 5,
 165          'Сб.' => 6,
 166          'Вс.' => 7,
 167      ];
 168  
 169      /**
 170       * List of months, calendar month name => calendar month number
 171       *
 172       * @access protected
 173       * @var array
 174       */
 175      public $month = [
 176          // English
 177          'jan' => 1,
 178          'january' => 1,
 179          'feb' => 2,
 180          'february' => 2,
 181          'mar' => 3,
 182          'march' => 3,
 183          'apr' => 4,
 184          'april' => 4,
 185          'may' => 5,
 186          // No long form of May
 187          'jun' => 6,
 188          'june' => 6,
 189          'jul' => 7,
 190          'july' => 7,
 191          'aug' => 8,
 192          'august' => 8,
 193          'sep' => 9,
 194          'september' => 9,
 195          'oct' => 10,
 196          'october' => 10,
 197          'nov' => 11,
 198          'november' => 11,
 199          'dec' => 12,
 200          'december' => 12,
 201          // Dutch
 202          'januari' => 1,
 203          'februari' => 2,
 204          'maart' => 3,
 205          'april' => 4,
 206          'mei' => 5,
 207          'juni' => 6,
 208          'juli' => 7,
 209          'augustus' => 8,
 210          'september' => 9,
 211          'oktober' => 10,
 212          'november' => 11,
 213          'december' => 12,
 214          // French
 215          'janvier' => 1,
 216          'février' => 2,
 217          'mars' => 3,
 218          'avril' => 4,
 219          'mai' => 5,
 220          'juin' => 6,
 221          'juillet' => 7,
 222          'août' => 8,
 223          'septembre' => 9,
 224          'octobre' => 10,
 225          'novembre' => 11,
 226          'décembre' => 12,
 227          // German
 228          'januar' => 1,
 229          'jan' => 1,
 230          'februar' => 2,
 231          'feb' => 2,
 232          'märz' => 3,
 233          'mär' => 3,
 234          'april' => 4,
 235          'apr' => 4,
 236          'mai' => 5, // no short form for may
 237          'juni' => 6,
 238          'jun' => 6,
 239          'juli' => 7,
 240          'jul' => 7,
 241          'august' => 8,
 242          'aug' => 8,
 243          'september' => 9,
 244          'sep' => 9,
 245          'oktober' => 10,
 246          'okt' => 10,
 247          'november' => 11,
 248          'nov' => 11,
 249          'dezember' => 12,
 250          'dez' => 12,
 251          // Italian
 252          'gennaio' => 1,
 253          'febbraio' => 2,
 254          'marzo' => 3,
 255          'aprile' => 4,
 256          'maggio' => 5,
 257          'giugno' => 6,
 258          'luglio' => 7,
 259          'agosto' => 8,
 260          'settembre' => 9,
 261          'ottobre' => 10,
 262          'novembre' => 11,
 263          'dicembre' => 12,
 264          // Spanish
 265          'enero' => 1,
 266          'febrero' => 2,
 267          'marzo' => 3,
 268          'abril' => 4,
 269          'mayo' => 5,
 270          'junio' => 6,
 271          'julio' => 7,
 272          'agosto' => 8,
 273          'septiembre' => 9,
 274          'setiembre' => 9,
 275          'octubre' => 10,
 276          'noviembre' => 11,
 277          'diciembre' => 12,
 278          // Finnish
 279          'tammikuu' => 1,
 280          'helmikuu' => 2,
 281          'maaliskuu' => 3,
 282          'huhtikuu' => 4,
 283          'toukokuu' => 5,
 284          'kesäkuu' => 6,
 285          'heinäkuu' => 7,
 286          'elokuu' => 8,
 287          'suuskuu' => 9,
 288          'lokakuu' => 10,
 289          'marras' => 11,
 290          'joulukuu' => 12,
 291          // Hungarian
 292          'január' => 1,
 293          'február' => 2,
 294          'március' => 3,
 295          'április' => 4,
 296          'május' => 5,
 297          'június' => 6,
 298          'július' => 7,
 299          'augusztus' => 8,
 300          'szeptember' => 9,
 301          'október' => 10,
 302          'november' => 11,
 303          'december' => 12,
 304          // Greek
 305          'Ιαν' => 1,
 306          'Φεβ' => 2,
 307          'Μάώ' => 3,
 308          'Μαώ' => 3,
 309          'Απρ' => 4,
 310          'Μάι' => 5,
 311          'Μαϊ' => 5,
 312          'Μαι' => 5,
 313          'Ιούν' => 6,
 314          'Ιον' => 6,
 315          'Ιούλ' => 7,
 316          'Ιολ' => 7,
 317          'Αύγ' => 8,
 318          'Αυγ' => 8,
 319          'Σεπ' => 9,
 320          'Οκτ' => 10,
 321          'Νοέ' => 11,
 322          'Δεκ' => 12,
 323          // Russian
 324          'Янв' => 1,
 325          'января' => 1,
 326          'Фев' => 2,
 327          'февраля' => 2,
 328          'Мар' => 3,
 329          'марта' => 3,
 330          'Апр' => 4,
 331          'апреля' => 4,
 332          'Май' => 5,
 333          'мая' => 5,
 334          'Июн' => 6,
 335          'июня' => 6,
 336          'Июл' => 7,
 337          'июля' => 7,
 338          'Авг' => 8,
 339          'августа' => 8,
 340          'Сен' => 9,
 341          'сентября' => 9,
 342          'Окт' => 10,
 343          'октября' => 10,
 344          'Ноя' => 11,
 345          'ноября' => 11,
 346          'Дек' => 12,
 347          'декабря' => 12,
 348  
 349      ];
 350  
 351      /**
 352       * List of timezones, abbreviation => offset from UTC
 353       *
 354       * @access protected
 355       * @var array
 356       */
 357      public $timezone = [
 358          'ACDT' => 37800,
 359          'ACIT' => 28800,
 360          'ACST' => 34200,
 361          'ACT' => -18000,
 362          'ACWDT' => 35100,
 363          'ACWST' => 31500,
 364          'AEDT' => 39600,
 365          'AEST' => 36000,
 366          'AFT' => 16200,
 367          'AKDT' => -28800,
 368          'AKST' => -32400,
 369          'AMDT' => 18000,
 370          'AMT' => -14400,
 371          'ANAST' => 46800,
 372          'ANAT' => 43200,
 373          'ART' => -10800,
 374          'AZOST' => -3600,
 375          'AZST' => 18000,
 376          'AZT' => 14400,
 377          'BIOT' => 21600,
 378          'BIT' => -43200,
 379          'BOT' => -14400,
 380          'BRST' => -7200,
 381          'BRT' => -10800,
 382          'BST' => 3600,
 383          'BTT' => 21600,
 384          'CAST' => 18000,
 385          'CAT' => 7200,
 386          'CCT' => 23400,
 387          'CDT' => -18000,
 388          'CEDT' => 7200,
 389          'CEST' => 7200,
 390          'CET' => 3600,
 391          'CGST' => -7200,
 392          'CGT' => -10800,
 393          'CHADT' => 49500,
 394          'CHAST' => 45900,
 395          'CIST' => -28800,
 396          'CKT' => -36000,
 397          'CLDT' => -10800,
 398          'CLST' => -14400,
 399          'COT' => -18000,
 400          'CST' => -21600,
 401          'CVT' => -3600,
 402          'CXT' => 25200,
 403          'DAVT' => 25200,
 404          'DTAT' => 36000,
 405          'EADT' => -18000,
 406          'EAST' => -21600,
 407          'EAT' => 10800,
 408          'ECT' => -18000,
 409          'EDT' => -14400,
 410          'EEST' => 10800,
 411          'EET' => 7200,
 412          'EGT' => -3600,
 413          'EKST' => 21600,
 414          'EST' => -18000,
 415          'FJT' => 43200,
 416          'FKDT' => -10800,
 417          'FKST' => -14400,
 418          'FNT' => -7200,
 419          'GALT' => -21600,
 420          'GEDT' => 14400,
 421          'GEST' => 10800,
 422          'GFT' => -10800,
 423          'GILT' => 43200,
 424          'GIT' => -32400,
 425          'GST' => 14400,
 426          'GST' => -7200,
 427          'GYT' => -14400,
 428          'HAA' => -10800,
 429          'HAC' => -18000,
 430          'HADT' => -32400,
 431          'HAE' => -14400,
 432          'HAP' => -25200,
 433          'HAR' => -21600,
 434          'HAST' => -36000,
 435          'HAT' => -9000,
 436          'HAY' => -28800,
 437          'HKST' => 28800,
 438          'HMT' => 18000,
 439          'HNA' => -14400,
 440          'HNC' => -21600,
 441          'HNE' => -18000,
 442          'HNP' => -28800,
 443          'HNR' => -25200,
 444          'HNT' => -12600,
 445          'HNY' => -32400,
 446          'IRDT' => 16200,
 447          'IRKST' => 32400,
 448          'IRKT' => 28800,
 449          'IRST' => 12600,
 450          'JFDT' => -10800,
 451          'JFST' => -14400,
 452          'JST' => 32400,
 453          'KGST' => 21600,
 454          'KGT' => 18000,
 455          'KOST' => 39600,
 456          'KOVST' => 28800,
 457          'KOVT' => 25200,
 458          'KRAST' => 28800,
 459          'KRAT' => 25200,
 460          'KST' => 32400,
 461          'LHDT' => 39600,
 462          'LHST' => 37800,
 463          'LINT' => 50400,
 464          'LKT' => 21600,
 465          'MAGST' => 43200,
 466          'MAGT' => 39600,
 467          'MAWT' => 21600,
 468          'MDT' => -21600,
 469          'MESZ' => 7200,
 470          'MEZ' => 3600,
 471          'MHT' => 43200,
 472          'MIT' => -34200,
 473          'MNST' => 32400,
 474          'MSDT' => 14400,
 475          'MSST' => 10800,
 476          'MST' => -25200,
 477          'MUT' => 14400,
 478          'MVT' => 18000,
 479          'MYT' => 28800,
 480          'NCT' => 39600,
 481          'NDT' => -9000,
 482          'NFT' => 41400,
 483          'NMIT' => 36000,
 484          'NOVST' => 25200,
 485          'NOVT' => 21600,
 486          'NPT' => 20700,
 487          'NRT' => 43200,
 488          'NST' => -12600,
 489          'NUT' => -39600,
 490          'NZDT' => 46800,
 491          'NZST' => 43200,
 492          'OMSST' => 25200,
 493          'OMST' => 21600,
 494          'PDT' => -25200,
 495          'PET' => -18000,
 496          'PETST' => 46800,
 497          'PETT' => 43200,
 498          'PGT' => 36000,
 499          'PHOT' => 46800,
 500          'PHT' => 28800,
 501          'PKT' => 18000,
 502          'PMDT' => -7200,
 503          'PMST' => -10800,
 504          'PONT' => 39600,
 505          'PST' => -28800,
 506          'PWT' => 32400,
 507          'PYST' => -10800,
 508          'PYT' => -14400,
 509          'RET' => 14400,
 510          'ROTT' => -10800,
 511          'SAMST' => 18000,
 512          'SAMT' => 14400,
 513          'SAST' => 7200,
 514          'SBT' => 39600,
 515          'SCDT' => 46800,
 516          'SCST' => 43200,
 517          'SCT' => 14400,
 518          'SEST' => 3600,
 519          'SGT' => 28800,
 520          'SIT' => 28800,
 521          'SRT' => -10800,
 522          'SST' => -39600,
 523          'SYST' => 10800,
 524          'SYT' => 7200,
 525          'TFT' => 18000,
 526          'THAT' => -36000,
 527          'TJT' => 18000,
 528          'TKT' => -36000,
 529          'TMT' => 18000,
 530          'TOT' => 46800,
 531          'TPT' => 32400,
 532          'TRUT' => 36000,
 533          'TVT' => 43200,
 534          'TWT' => 28800,
 535          'UYST' => -7200,
 536          'UYT' => -10800,
 537          'UZT' => 18000,
 538          'VET' => -14400,
 539          'VLAST' => 39600,
 540          'VLAT' => 36000,
 541          'VOST' => 21600,
 542          'VUT' => 39600,
 543          'WAST' => 7200,
 544          'WAT' => 3600,
 545          'WDT' => 32400,
 546          'WEST' => 3600,
 547          'WFT' => 43200,
 548          'WIB' => 25200,
 549          'WIT' => 32400,
 550          'WITA' => 28800,
 551          'WKST' => 18000,
 552          'WST' => 28800,
 553          'YAKST' => 36000,
 554          'YAKT' => 32400,
 555          'YAPT' => 36000,
 556          'YEKST' => 21600,
 557          'YEKT' => 18000,
 558      ];
 559  
 560      /**
 561       * Cached PCRE for Date::$day
 562       *
 563       * @access protected
 564       * @var string
 565       */
 566      public $day_pcre;
 567  
 568      /**
 569       * Cached PCRE for Date::$month
 570       *
 571       * @access protected
 572       * @var string
 573       */
 574      public $month_pcre;
 575  
 576      /**
 577       * Array of user-added callback methods
 578       *
 579       * @access private
 580       * @var array
 581       */
 582      public $built_in = [];
 583  
 584      /**
 585       * Array of user-added callback methods
 586       *
 587       * @access private
 588       * @var array
 589       */
 590      public $user = [];
 591  
 592      /**
 593       * Create new Date object, and set self::day_pcre,
 594       * self::month_pcre, and self::built_in
 595       *
 596       * @access private
 597       */
 598      public function __construct()
 599      {
 600          $this->day_pcre = '(' . implode('|', array_keys($this->day)) . ')';
 601          $this->month_pcre = '(' . implode('|', array_keys($this->month)) . ')';
 602  
 603          static $cache;
 604          if (!isset($cache[get_class($this)])) {
 605              $all_methods = get_class_methods($this);
 606  
 607              foreach ($all_methods as $method) {
 608                  if (strtolower(substr($method, 0, 5)) === 'date_') {
 609                      $cache[get_class($this)][] = $method;
 610                  }
 611              }
 612          }
 613  
 614          foreach ($cache[get_class($this)] as $method) {
 615              $this->built_in[] = $method;
 616          }
 617      }
 618  
 619      /**
 620       * Get the object
 621       *
 622       * @access public
 623       */
 624      public static function get()
 625      {
 626          static $object;
 627          if (!$object) {
 628              $object = new Date();
 629          }
 630          return $object;
 631      }
 632  
 633      /**
 634       * Parse a date
 635       *
 636       * @final
 637       * @access public
 638       * @param string $date Date to parse
 639       * @return int Timestamp corresponding to date string, or false on failure
 640       */
 641      public function parse($date)
 642      {
 643          foreach ($this->user as $method) {
 644              if (($returned = call_user_func($method, $date)) !== false) {
 645                  return $returned;
 646              }
 647          }
 648  
 649          foreach ($this->built_in as $method) {
 650              if (($returned = call_user_func([$this, $method], $date)) !== false) {
 651                  return $returned;
 652              }
 653          }
 654  
 655          return false;
 656      }
 657  
 658      /**
 659       * Add a callback method to parse a date
 660       *
 661       * @final
 662       * @access public
 663       * @param callable $callback
 664       */
 665      public function add_callback($callback)
 666      {
 667          if (is_callable($callback)) {
 668              $this->user[] = $callback;
 669          } else {
 670              trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
 671          }
 672      }
 673  
 674      /**
 675       * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
 676       * well as allowing any of upper or lower case "T", horizontal tabs, or
 677       * spaces to be used as the time separator (including more than one))
 678       *
 679       * @access protected
 680       * @return int Timestamp
 681       */
 682      public function date_w3cdtf($date)
 683      {
 684          $pcre = <<<'PCRE'
 685              /
 686              ^
 687              (?P<year>[0-9]{4})
 688              (?:
 689                  -?
 690                  (?P<month>[0-9]{2})
 691                  (?:
 692                      -?
 693                      (?P<day>[0-9]{2})
 694                      (?:
 695                          [Tt\x09\x20]+
 696                          (?P<hour>[0-9]{2})
 697                          (?:
 698                              :?
 699                              (?P<minute>[0-9]{2})
 700                              (?:
 701                                  :?
 702                                  (?P<second>[0-9]{2})
 703                                  (?:
 704                                      .
 705                                      (?P<second_fraction>[0-9]*)
 706                                  )?
 707                              )?
 708                          )?
 709                          (?:
 710                              (?P<zulu>Z)
 711                              |   (?P<tz_sign>[+\-])
 712                                  (?P<tz_hour>[0-9]{1,2})
 713                                  :?
 714                                  (?P<tz_minute>[0-9]{1,2})
 715                          )
 716                      )?
 717                  )?
 718              )?
 719              $
 720              /x
 721  PCRE;
 722          if (preg_match($pcre, $date, $match)) {
 723              // Fill in empty matches and convert to proper types.
 724              $year = (int) $match['year'];
 725              $month = isset($match['month']) ? (int) $match['month'] : 1;
 726              $day = isset($match['day']) ? (int) $match['day'] : 1;
 727              $hour = isset($match['hour']) ? (int) $match['hour'] : 0;
 728              $minute = isset($match['minute']) ? (int) $match['minute'] : 0;
 729              $second = isset($match['second']) ? (int) $match['second'] : 0;
 730              $second_fraction = isset($match['second_fraction']) ? ((int) $match['second_fraction']) / (10 ** strlen($match['second_fraction'])) : 0;
 731              $tz_sign = ($match['tz_sign'] ?? '') === '-' ? -1 : 1;
 732              $tz_hour = isset($match['tz_hour']) ? (int) $match['tz_hour'] : 0;
 733              $tz_minute = isset($match['tz_minute']) ? (int) $match['tz_minute'] : 0;
 734  
 735              // Numeric timezone
 736              $timezone = $tz_hour * 3600;
 737              $timezone += $tz_minute * 60;
 738              $timezone *= $tz_sign;
 739  
 740              // Convert the number of seconds to an integer, taking decimals into account
 741              $second = (int) round($second + $second_fraction);
 742  
 743              return gmmktime($hour, $minute, $second, $month, $day, $year) - $timezone;
 744          }
 745  
 746          return false;
 747      }
 748  
 749      /**
 750       * Remove RFC822 comments
 751       *
 752       * @access protected
 753       * @param string $data Data to strip comments from
 754       * @return string Comment stripped string
 755       */
 756      public function remove_rfc2822_comments($string)
 757      {
 758          $string = (string) $string;
 759          $position = 0;
 760          $length = strlen($string);
 761          $depth = 0;
 762  
 763          $output = '';
 764  
 765          while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) {
 766              $output .= substr($string, $position, $pos - $position);
 767              $position = $pos + 1;
 768              if ($pos === 0 || $string[$pos - 1] !== '\\') {
 769                  $depth++;
 770                  while ($depth && $position < $length) {
 771                      $position += strcspn($string, '()', $position);
 772                      if ($string[$position - 1] === '\\') {
 773                          $position++;
 774                          continue;
 775                      } elseif (isset($string[$position])) {
 776                          switch ($string[$position]) {
 777                              case '(':
 778                                  $depth++;
 779                                  break;
 780  
 781                              case ')':
 782                                  $depth--;
 783                                  break;
 784                          }
 785                          $position++;
 786                      } else {
 787                          break;
 788                      }
 789                  }
 790              } else {
 791                  $output .= '(';
 792              }
 793          }
 794          $output .= substr($string, $position);
 795  
 796          return $output;
 797      }
 798  
 799      /**
 800       * Parse RFC2822's date format
 801       *
 802       * @access protected
 803       * @return int Timestamp
 804       */
 805      public function date_rfc2822($date)
 806      {
 807          static $pcre;
 808          if (!$pcre) {
 809              $wsp = '[\x09\x20]';
 810              $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
 811              $optional_fws = $fws . '?';
 812              $day_name = $this->day_pcre;
 813              $month = $this->month_pcre;
 814              $day = '([0-9]{1,2})';
 815              $hour = $minute = $second = '([0-9]{2})';
 816              $year = '([0-9]{2,4})';
 817              $num_zone = '([+\-])([0-9]{2})([0-9]{2})';
 818              $character_zone = '([A-Z]{1,5})';
 819              $zone = '(?:' . $num_zone . '|' . $character_zone . ')';
 820              $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i';
 821          }
 822          if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) {
 823              /*
 824              Capturing subpatterns:
 825              1: Day name
 826              2: Day
 827              3: Month
 828              4: Year
 829              5: Hour
 830              6: Minute
 831              7: Second
 832              8: Timezone ±
 833              9: Timezone hours
 834              10: Timezone minutes
 835              11: Alphabetic timezone
 836              */
 837  
 838              // Find the month number
 839              $month = $this->month[strtolower($match[3])];
 840  
 841              // Numeric timezone
 842              if ($match[8] !== '') {
 843                  $timezone = $match[9] * 3600;
 844                  $timezone += $match[10] * 60;
 845                  if ($match[8] === '-') {
 846                      $timezone = 0 - $timezone;
 847                  }
 848              }
 849              // Character timezone
 850              elseif (isset($this->timezone[strtoupper($match[11])])) {
 851                  $timezone = $this->timezone[strtoupper($match[11])];
 852              }
 853              // Assume everything else to be -0000
 854              else {
 855                  $timezone = 0;
 856              }
 857  
 858              // Deal with 2/3 digit years
 859              if ($match[4] < 50) {
 860                  $match[4] += 2000;
 861              } elseif ($match[4] < 1000) {
 862                  $match[4] += 1900;
 863              }
 864  
 865              // Second is optional, if it is empty set it to zero
 866              if ($match[7] !== '') {
 867                  $second = $match[7];
 868              } else {
 869                  $second = 0;
 870              }
 871  
 872              return gmmktime(intval($match[5]), intval($match[6]), intval($second), intval($month), intval($match[2]), intval($match[4])) - $timezone;
 873          }
 874  
 875          return false;
 876      }
 877  
 878      /**
 879       * Parse RFC850's date format
 880       *
 881       * @access protected
 882       * @return int Timestamp
 883       */
 884      public function date_rfc850($date)
 885      {
 886          static $pcre;
 887          if (!$pcre) {
 888              $space = '[\x09\x20]+';
 889              $day_name = $this->day_pcre;
 890              $month = $this->month_pcre;
 891              $day = '([0-9]{1,2})';
 892              $year = $hour = $minute = $second = '([0-9]{2})';
 893              $zone = '([A-Z]{1,5})';
 894              $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
 895          }
 896          if (preg_match($pcre, $date, $match)) {
 897              /*
 898              Capturing subpatterns:
 899              1: Day name
 900              2: Day
 901              3: Month
 902              4: Year
 903              5: Hour
 904              6: Minute
 905              7: Second
 906              8: Timezone
 907              */
 908  
 909              // Month
 910              $month = $this->month[strtolower($match[3])];
 911  
 912              // Character timezone
 913              if (isset($this->timezone[strtoupper($match[8])])) {
 914                  $timezone = $this->timezone[strtoupper($match[8])];
 915              }
 916              // Assume everything else to be -0000
 917              else {
 918                  $timezone = 0;
 919              }
 920  
 921              // Deal with 2 digit year
 922              if ($match[4] < 50) {
 923                  $match[4] += 2000;
 924              } else {
 925                  $match[4] += 1900;
 926              }
 927  
 928              return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
 929          }
 930  
 931          return false;
 932      }
 933  
 934      /**
 935       * Parse C99's asctime()'s date format
 936       *
 937       * @access protected
 938       * @return int Timestamp
 939       */
 940      public function date_asctime($date)
 941      {
 942          static $pcre;
 943          if (!$pcre) {
 944              $space = '[\x09\x20]+';
 945              $wday_name = $this->day_pcre;
 946              $mon_name = $this->month_pcre;
 947              $day = '([0-9]{1,2})';
 948              $hour = $sec = $min = '([0-9]{2})';
 949              $year = '([0-9]{4})';
 950              $terminator = '\x0A?\x00?';
 951              $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
 952          }
 953          if (preg_match($pcre, $date, $match)) {
 954              /*
 955              Capturing subpatterns:
 956              1: Day name
 957              2: Month
 958              3: Day
 959              4: Hour
 960              5: Minute
 961              6: Second
 962              7: Year
 963              */
 964  
 965              $month = $this->month[strtolower($match[2])];
 966              return gmmktime((int) $match[4], (int) $match[5], (int) $match[6], $month, (int) $match[3], (int) $match[7]);
 967          }
 968  
 969          return false;
 970      }
 971  
 972      /**
 973       * Parse dates using strtotime()
 974       *
 975       * @access protected
 976       * @return int Timestamp
 977       */
 978      public function date_strtotime($date)
 979      {
 980          $strtotime = strtotime($date);
 981          if ($strtotime === -1 || $strtotime === false) {
 982              return false;
 983          }
 984  
 985          return $strtotime;
 986      }
 987  }
 988  
 989  class_alias('SimplePie\Parse\Date', 'SimplePie_Parse_Date');