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