123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- <?php
- /**
- * CDateTimeParser class file
- *
- * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @author Tomasz Suchanek <tomasz[dot]suchanek[at]gmail[dot]com>
- * @link http://www.yiiframework.com/
- * @copyright 2008-2013 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
- /**
- * CDateTimeParser converts a date/time string to a UNIX timestamp according to the specified pattern.
- *
- * The following pattern characters are recognized:
- * <pre>
- * Pattern | Description
- * ----------------------------------------------------
- * d | Day of month 1 to 31, no padding
- * dd | Day of month 01 to 31, zero leading
- * M | Month digit 1 to 12, no padding
- * MM | Month digit 01 to 12, zero leading
- * MMM | Abbreviation representation of month (available since 1.1.11; locale aware since 1.1.13)
- * MMMM | Full name representation (available since 1.1.13; locale aware)
- * y | 4 year digit, e.g., 2005 (available since 1.1.16)
- * yy | 2 year digit, e.g., 96, 05
- * yyyy | 4 year digit, e.g., 2005
- * h | Hour in 0 to 23, no padding
- * hh | Hour in 00 to 23, zero leading
- * H | Hour in 0 to 23, no padding
- * HH | Hour in 00 to 23, zero leading
- * m | Minutes in 0 to 59, no padding
- * mm | Minutes in 00 to 59, zero leading
- * s | Seconds in 0 to 59, no padding
- * ss | Seconds in 00 to 59, zero leading
- * a | AM or PM, case-insensitive (since version 1.1.5)
- * ? | matches any character (wildcard) (since version 1.1.11)
- * ----------------------------------------------------
- * </pre>
- * All other characters must appear in the date string at the corresponding positions.
- *
- * For example, to parse a date string '21/10/2008', use the following:
- * <pre>
- * $timestamp=CDateTimeParser::parse('21/10/2008','dd/MM/yyyy');
- * </pre>
- *
- * Locale specific patterns such as MMM and MMMM uses {@link CLocale} for retrieving needed information.
- *
- * To format a timestamp to a date string, please use {@link CDateFormatter}.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @package system.utils
- * @since 1.0
- */
- class CDateTimeParser
- {
- /**
- * @var boolean whether 'mbstring' PHP extension available. This static property introduced for
- * the better overall performance of the class functionality. Checking 'mbstring' availability
- * through static property with predefined status value is much faster than direct calling
- * of function_exists('...').
- * Intended for internal use only.
- * @since 1.1.13
- */
- private static $_mbstringAvailable;
- /**
- * Converts a date string to a timestamp.
- * @param string $value the date string to be parsed
- * @param string $pattern the pattern that the date string is following
- * @param array $defaults the default values for year, month, day, hour, minute and second.
- * The default values will be used in case when the pattern doesn't specify the
- * corresponding fields. For example, if the pattern is 'MM/dd/yyyy' and this
- * parameter is array('minute'=>0, 'second'=>0), then the actual minute and second
- * for the parsing result will take value 0, while the actual hour value will be
- * the current hour obtained by date('H'). This parameter has been available since version 1.1.5.
- * @return integer timestamp for the date string. False if parsing fails.
- */
- public static function parse($value,$pattern='MM/dd/yyyy',$defaults=array())
- {
- if(self::$_mbstringAvailable===null)
- self::$_mbstringAvailable=extension_loaded('mbstring');
- $tokens=self::tokenize($pattern);
- $i=0;
- $n=self::$_mbstringAvailable ? mb_strlen($value,Yii::app()->charset) : strlen($value);
- foreach($tokens as $token)
- {
- switch($token)
- {
- case 'yyyy':
- case 'y':
- {
- if(($year=self::parseInteger($value,$i,4,4))===false)
- return false;
- $i+=4;
- break;
- }
- case 'yy':
- {
- if(($year=self::parseInteger($value,$i,1,2))===false)
- return false;
- $i+=strlen($year);
- break;
- }
- case 'MMMM':
- {
- $monthName='';
- if(($month=self::parseMonth($value,$i,'wide',$monthName))===false)
- return false;
- $i+=self::$_mbstringAvailable ? mb_strlen($monthName,Yii::app()->charset) : strlen($monthName);
- break;
- }
- case 'MMM':
- {
- $monthName='';
- if(($month=self::parseMonth($value,$i,'abbreviated',$monthName))===false)
- return false;
- $i+=self::$_mbstringAvailable ? mb_strlen($monthName,Yii::app()->charset) : strlen($monthName);
- break;
- }
- case 'MM':
- {
- if(($month=self::parseInteger($value,$i,2,2))===false)
- return false;
- $i+=2;
- break;
- }
- case 'M':
- {
- if(($month=self::parseInteger($value,$i,1,2))===false)
- return false;
- $i+=strlen($month);
- break;
- }
- case 'dd':
- {
- if(($day=self::parseInteger($value,$i,2,2))===false)
- return false;
- $i+=2;
- break;
- }
- case 'd':
- {
- if(($day=self::parseInteger($value,$i,1,2))===false)
- return false;
- $i+=strlen($day);
- break;
- }
- case 'h':
- case 'H':
- {
- if(($hour=self::parseInteger($value,$i,1,2))===false)
- return false;
- $i+=strlen($hour);
- break;
- }
- case 'hh':
- case 'HH':
- {
- if(($hour=self::parseInteger($value,$i,2,2))===false)
- return false;
- $i+=2;
- break;
- }
- case 'm':
- {
- if(($minute=self::parseInteger($value,$i,1,2))===false)
- return false;
- $i+=strlen($minute);
- break;
- }
- case 'mm':
- {
- if(($minute=self::parseInteger($value,$i,2,2))===false)
- return false;
- $i+=2;
- break;
- }
- case 's':
- {
- if(($second=self::parseInteger($value,$i,1,2))===false)
- return false;
- $i+=strlen($second);
- break;
- }
- case 'ss':
- {
- if(($second=self::parseInteger($value,$i,2,2))===false)
- return false;
- $i+=2;
- break;
- }
- case 'a':
- {
- if(($ampm=self::parseAmPm($value,$i))===false)
- return false;
- if(isset($hour))
- {
- if($hour==12 && $ampm==='am')
- $hour=0;
- elseif($hour<12 && $ampm==='pm')
- $hour+=12;
- }
- $i+=2;
- break;
- }
- default:
- {
- $tn=self::$_mbstringAvailable ? mb_strlen($token,Yii::app()->charset) : strlen($token);
- if($i>=$n || ($token{0}!='?' && (self::$_mbstringAvailable ? mb_substr($value,$i,$tn,Yii::app()->charset) : substr($value,$i,$tn))!==$token))
- return false;
- $i+=$tn;
- break;
- }
- }
- }
- if($i<$n)
- return false;
- if(!isset($year))
- $year=isset($defaults['year']) ? $defaults['year'] : date('Y');
- if(!isset($month))
- $month=isset($defaults['month']) ? $defaults['month'] : date('n');
- if(!isset($day))
- $day=isset($defaults['day']) ? $defaults['day'] : date('j');
- if(strlen($year)===2)
- {
- if($year>=70)
- $year+=1900;
- else
- $year+=2000;
- }
- $year=(int)$year;
- $month=(int)$month;
- $day=(int)$day;
- if(
- !isset($hour) && !isset($minute) && !isset($second)
- && !isset($defaults['hour']) && !isset($defaults['minute']) && !isset($defaults['second'])
- )
- $hour=$minute=$second=0;
- else
- {
- if(!isset($hour))
- $hour=isset($defaults['hour']) ? $defaults['hour'] : date('H');
- if(!isset($minute))
- $minute=isset($defaults['minute']) ? $defaults['minute'] : date('i');
- if(!isset($second))
- $second=isset($defaults['second']) ? $defaults['second'] : date('s');
- $hour=(int)$hour;
- $minute=(int)$minute;
- $second=(int)$second;
- }
- if(CTimestamp::isValidDate($year,$month,$day) && CTimestamp::isValidTime($hour,$minute,$second))
- return CTimestamp::getTimestamp($hour,$minute,$second,$month,$day,$year);
- else
- return false;
- }
- /*
- * @param string $pattern the pattern that the date string is following
- */
- private static function tokenize($pattern)
- {
- if(!($n=self::$_mbstringAvailable ? mb_strlen($pattern,Yii::app()->charset) : strlen($pattern)))
- return array();
- $tokens=array();
- $c0=self::$_mbstringAvailable ? mb_substr($pattern,0,1,Yii::app()->charset) : substr($pattern,0,1);
- for($start=0,$i=1;$i<$n;++$i)
- {
- $c=self::$_mbstringAvailable ? mb_substr($pattern,$i,1,Yii::app()->charset) : substr($pattern,$i,1);
- if($c!==$c0)
- {
- $tokens[]=self::$_mbstringAvailable ? mb_substr($pattern,$start,$i-$start,Yii::app()->charset) : substr($pattern,$start,$i-$start);
- $c0=$c;
- $start=$i;
- }
- }
- $tokens[]=self::$_mbstringAvailable ? mb_substr($pattern,$start,$n-$start,Yii::app()->charset) : substr($pattern,$start,$n-$start);
- return $tokens;
- }
- /**
- * @param string $value the date string to be parsed
- * @param integer $offset starting offset
- * @param integer $minLength minimum length
- * @param integer $maxLength maximum length
- * @return string parsed integer value
- */
- protected static function parseInteger($value,$offset,$minLength,$maxLength)
- {
- for($len=$maxLength;$len>=$minLength;--$len)
- {
- $v=self::$_mbstringAvailable ? mb_substr($value,$offset,$len,Yii::app()->charset) : substr($value,$offset,$len);
- if(ctype_digit($v) && (self::$_mbstringAvailable ? mb_strlen($v,Yii::app()->charset) : strlen($v))>=$minLength)
- return $v;
- }
- return false;
- }
- /**
- * @param string $value the date string to be parsed
- * @param integer $offset starting offset
- * @return string parsed day period value
- */
- protected static function parseAmPm($value, $offset)
- {
- $v=strtolower(self::$_mbstringAvailable ? mb_substr($value,$offset,2,Yii::app()->charset) : substr($value,$offset,2));
- return $v==='am' || $v==='pm' ? $v : false;
- }
- /**
- * @param string $value the date string to be parsed.
- * @param integer $offset starting offset.
- * @param string $width month name width. It can be 'wide', 'abbreviated' or 'narrow'.
- * @param string $monthName extracted month name. Passed by reference.
- * @return string parsed month name.
- * @since 1.1.13
- */
- protected static function parseMonth($value,$offset,$width,&$monthName)
- {
- $valueLength=self::$_mbstringAvailable ? mb_strlen($value,Yii::app()->charset) : strlen($value);
- for($len=1; $offset+$len<=$valueLength; $len++)
- {
- $monthName=self::$_mbstringAvailable ? mb_substr($value,$offset,$len,Yii::app()->charset) : substr($value,$offset,$len);
- if(!preg_match('/^[\p{L}\p{M}]+$/u',$monthName)) // unicode aware replacement for ctype_alpha($monthName)
- {
- $monthName=self::$_mbstringAvailable ? mb_substr($monthName,0,-1,Yii::app()->charset) : substr($monthName,0,-1);
- break;
- }
- }
- $monthName=self::$_mbstringAvailable ? mb_strtolower($monthName,Yii::app()->charset) : strtolower($monthName);
- $monthNames=Yii::app()->getLocale()->getMonthNames($width,false);
- foreach($monthNames as $k=>$v)
- $monthNames[$k]=rtrim(self::$_mbstringAvailable ? mb_strtolower($v,Yii::app()->charset) : strtolower($v),'.');
- $monthNamesStandAlone=Yii::app()->getLocale()->getMonthNames($width,true);
- foreach($monthNamesStandAlone as $k=>$v)
- $monthNamesStandAlone[$k]=rtrim(self::$_mbstringAvailable ? mb_strtolower($v,Yii::app()->charset) : strtolower($v),'.');
- if(($v=array_search($monthName,$monthNames))===false && ($v=array_search($monthName,$monthNamesStandAlone))===false)
- return false;
- return $v;
- }
- }
|