Make WordPress Core


Ignore:
Timestamp:
09/30/2024 10:48:16 PM (8 months ago)
Author:
SergeyBiryukov
Message:

External Libraries: Update the SimplePie library to version 1.8.0.

The most notable change in this update is that all code is now namespaced and uses PSR-4 classes, though there is a compatibility layer available for extenders using the older class names, so plugin or theme authors directly using SimplePie can decide for themselves when they want to change to using the namespaced names for SimplePie classes.

Note: This commit includes additional fixes for PHP 8.4 compatibility (PR 875, PR 888) from the one-dot-eight branch of SimplePie, which is expected to be released as SimplePie 1.8.1 soon.

References:

Follow-up to [47733], [49176], [52393], [52413].

Props jrf, peterwilsoncc, chaion07, cu121, markparnell, audrasjb, costdev, Presskopp, desrosj, faisal03, mukesh27, SergeyBiryukov.
See #55604.

Location:
trunk/src/wp-includes/SimplePie/src
Files:
1 added
1 edited
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/SimplePie/src/Parse/Date.php

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