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