1 <?php
2 
3 /**
4  * SimplePie
5  *
6  * A PHP-Based RSS and Atom Feed Framework.
7  * Takes the hard work out of managing a complete RSS/Atom solution.
8  *
9  * Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without modification, are
13  * permitted provided that the following conditions are met:
14  *
15  * 	* Redistributions of source code must retain the above copyright notice, this list of
16  * 	  conditions and the following disclaimer.
17  *
18  * 	* Redistributions in binary form must reproduce the above copyright notice, this list
19  * 	  of conditions and the following disclaimer in the documentation and/or other materials
20  * 	  provided with the distribution.
21  *
22  * 	* Neither the name of the SimplePie Team nor the names of its contributors may be used
23  * 	  to endorse or promote products derived from this software without specific prior
24  * 	  written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
27  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
29  * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
33  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  * @package SimplePie
37  * @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
38  * @author Ryan Parman
39  * @author Sam Sneddon
40  * @author Ryan McCue
41  * @link http://simplepie.org/ SimplePie
42  * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43  */
44 
45 namespace SimplePie\Parse;
46 
47 /**
48  * Date Parser
49  *
50  * @package SimplePie
51  * @subpackage Parsing
52  */
53 class Date
54 {
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
720 PCRE;
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     }
986 }
987 
988 class_alias('SimplePie\Parse\Date', 'SimplePie_Parse_Date');
989