5
* A PHP-Based RSS and Atom Feed Framework.
6
* Takes the hard work out of managing a complete RSS/Atom solution.
8
* Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
11
* Redistribution and use in source and binary forms, with or without modification, are
12
* permitted provided that the following conditions are met:
14
* * Redistributions of source code must retain the above copyright notice, this list of
15
* conditions and the following disclaimer.
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.
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
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.
37
* @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue
39
* @author Geoffrey Sneddon
41
* @link http://simplepie.org/ SimplePie
42
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
52
class SimplePie_Parse_Date
63
* List of days, calendar day name => ordinal day number in the week
152
* List of months, calendar month name => calendar month number
168
// No long form of May
297
* List of timezones, abbreviation => offset from UTC
302
var $timezone = array(
505
* Cached PCRE for SimplePie_Parse_Date::$day
513
* Cached PCRE for SimplePie_Parse_Date::$month
521
* Array of user-added callback methods
526
var $built_in = array();
529
* Array of user-added callback methods
537
* Create new SimplePie_Parse_Date object, and set self::day_pcre,
538
* self::month_pcre, and self::built_in
542
public function __construct()
544
$this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')';
545
$this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')';
548
if (!isset($cache[get_class($this)]))
550
$all_methods = get_class_methods($this);
552
foreach ($all_methods as $method)
554
if (strtolower(substr($method, 0, 5)) === 'date_')
556
$cache[get_class($this)][] = $method;
561
foreach ($cache[get_class($this)] as $method)
563
$this->built_in[] = $method;
572
public static function get()
577
$object = new SimplePie_Parse_Date;
587
* @param string $date Date to parse
588
* @return int Timestamp corresponding to date string, or false on failure
590
public function parse($date)
592
foreach ($this->user as $method)
594
if (($returned = call_user_func($method, $date)) !== false)
600
foreach ($this->built_in as $method)
602
if (($returned = call_user_func(array($this, $method), $date)) !== false)
612
* Add a callback method to parse a date
616
* @param callback $callback
618
public function add_callback($callback)
620
if (is_callable($callback))
622
$this->user[] = $callback;
626
trigger_error('User-supplied function must be a valid callback', E_USER_WARNING);
631
* Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as
632
* well as allowing any of upper or lower case "T", horizontal tabs, or
633
* spaces to be used as the time seperator (including more than one))
636
* @return int Timestamp
638
public function date_w3cdtf($date)
643
$year = '([0-9]{4})';
644
$month = $day = $hour = $minute = $second = '([0-9]{2})';
645
$decimal = '([0-9]*)';
646
$zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))';
647
$pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/';
649
if (preg_match($pcre, $date, $match))
652
Capturing subpatterns:
659
7: Decimal fraction of a second
666
// Fill in empty matches
667
for ($i = count($match); $i <= 3; $i++)
672
for ($i = count($match); $i <= 7; $i++)
678
if (isset($match[9]) && $match[9] !== '')
680
$timezone = $match[10] * 3600;
681
$timezone += $match[11] * 60;
682
if ($match[9] === '-')
684
$timezone = 0 - $timezone;
692
// Convert the number of seconds to an integer, taking decimals into account
693
$second = round($match[6] + $match[7] / pow(10, strlen($match[7])));
695
return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone;
704
* Remove RFC822 comments
707
* @param string $data Data to strip comments from
708
* @return string Comment stripped string
710
public function remove_rfc2822_comments($string)
712
$string = (string) $string;
714
$length = strlen($string);
719
while ($position < $length && ($pos = strpos($string, '(', $position)) !== false)
721
$output .= substr($string, $position, $pos - $position);
722
$position = $pos + 1;
723
if ($string[$pos - 1] !== '\\')
726
while ($depth && $position < $length)
728
$position += strcspn($string, '()', $position);
729
if ($string[$position - 1] === '\\')
734
elseif (isset($string[$position]))
736
switch ($string[$position])
759
$output .= substr($string, $position);
765
* Parse RFC2822's date format
768
* @return int Timestamp
770
public function date_rfc2822($date)
776
$fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)';
777
$optional_fws = $fws . '?';
778
$day_name = $this->day_pcre;
779
$month = $this->month_pcre;
780
$day = '([0-9]{1,2})';
781
$hour = $minute = $second = '([0-9]{2})';
782
$year = '([0-9]{2,4})';
783
$num_zone = '([+\-])([0-9]{2})([0-9]{2})';
784
$character_zone = '([A-Z]{1,5})';
785
$zone = '(?:' . $num_zone . '|' . $character_zone . ')';
786
$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';
788
if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match))
791
Capturing subpatterns:
802
11: Alphabetic timezone
805
// Find the month number
806
$month = $this->month[strtolower($match[3])];
809
if ($match[8] !== '')
811
$timezone = $match[9] * 3600;
812
$timezone += $match[10] * 60;
813
if ($match[8] === '-')
815
$timezone = 0 - $timezone;
818
// Character timezone
819
elseif (isset($this->timezone[strtoupper($match[11])]))
821
$timezone = $this->timezone[strtoupper($match[11])];
823
// Assume everything else to be -0000
829
// Deal with 2/3 digit years
834
elseif ($match[4] < 1000)
839
// Second is optional, if it is empty set it to zero
840
if ($match[7] !== '')
849
return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone;
858
* Parse RFC850's date format
861
* @return int Timestamp
863
public function date_rfc850($date)
868
$space = '[\x09\x20]+';
869
$day_name = $this->day_pcre;
870
$month = $this->month_pcre;
871
$day = '([0-9]{1,2})';
872
$year = $hour = $minute = $second = '([0-9]{2})';
873
$zone = '([A-Z]{1,5})';
874
$pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i';
876
if (preg_match($pcre, $date, $match))
879
Capturing subpatterns:
891
$month = $this->month[strtolower($match[3])];
893
// Character timezone
894
if (isset($this->timezone[strtoupper($match[8])]))
896
$timezone = $this->timezone[strtoupper($match[8])];
898
// Assume everything else to be -0000
904
// Deal with 2 digit year
914
return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone;
923
* Parse C99's asctime()'s date format
926
* @return int Timestamp
928
public function date_asctime($date)
933
$space = '[\x09\x20]+';
934
$wday_name = $this->day_pcre;
935
$mon_name = $this->month_pcre;
936
$day = '([0-9]{1,2})';
937
$hour = $sec = $min = '([0-9]{2})';
938
$year = '([0-9]{4})';
939
$terminator = '\x0A?\x00?';
940
$pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i';
942
if (preg_match($pcre, $date, $match))
945
Capturing subpatterns:
955
$month = $this->month[strtolower($match[2])];
956
return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]);
965
* Parse dates using strtotime()
968
* @return int Timestamp
970
public function date_strtotime($date)
972
$strtotime = strtotime($date);
973
if ($strtotime === -1 || $strtotime === false)