123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- <?php
- /**
- * CDateFormatter class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.yiiframework.com/
- * @copyright 2008-2013 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
- /**
- * CDateFormatter provides date/time localization functionalities.
- *
- * CDateFormatter allows you to format dates and times in a locale-sensitive manner.
- * Patterns are interpreted in the locale that the CDateFormatter instance
- * is associated with. For example, month names and weekday names may vary
- * under different locales, which yields different formatting results.
- * The patterns that CDateFormatter recognizes are as defined in
- * {@link http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns CLDR}.
- *
- * CDateFormatter supports predefined patterns as well as customized ones:
- * <ul>
- * <li>The method {@link formatDateTime()} formats date or time or both using
- * predefined patterns which include 'full', 'long', 'medium' (default) and 'short'.</li>
- * <li>The method {@link format()} formats datetime using the specified pattern.
- * See {@link http://www.unicode.org/reports/tr35/#Date_Format_Patterns} for
- * details about the recognized pattern characters.</li>
- * </ul>
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @package system.i18n
- * @since 1.0
- */
- class CDateFormatter extends CComponent
- {
- /**
- * @var array pattern characters mapping to the corresponding translator methods
- */
- private static $_formatters=array(
- 'G'=>'formatEra',
- 'y'=>'formatYear',
- 'M'=>'formatMonth',
- 'L'=>'formatMonth',
- 'd'=>'formatDay',
- 'h'=>'formatHour12',
- 'H'=>'formatHour24',
- 'm'=>'formatMinutes',
- 's'=>'formatSeconds',
- 'E'=>'formatDayInWeek',
- 'c'=>'formatDayInWeek',
- 'e'=>'formatDayInWeek',
- 'D'=>'formatDayInYear',
- 'F'=>'formatDayInMonth',
- 'w'=>'formatWeekInYear',
- 'W'=>'formatWeekInMonth',
- 'a'=>'formatPeriod',
- 'k'=>'formatHourInDay',
- 'K'=>'formatHourInPeriod',
- 'z'=>'formatTimeZone',
- 'Z'=>'formatTimeZone',
- 'v'=>'formatTimeZone',
- );
- private $_locale;
- /**
- * Constructor.
- * @param mixed $locale locale ID (string) or CLocale instance
- */
- public function __construct($locale)
- {
- if(is_string($locale))
- $this->_locale=CLocale::getInstance($locale);
- else
- $this->_locale=$locale;
- }
- /**
- * Formats a date according to a customized pattern.
- * @param string $pattern the pattern (See {@link http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns})
- * @param mixed $time UNIX timestamp or a string in strtotime format
- * @return string formatted date time. Null if $time is null. (the null value check is available since Yii 1.1.11)
- */
- public function format($pattern,$time)
- {
- if($time===null)
- return null;
- if(is_string($time))
- {
- if(ctype_digit($time) || ($time{0}=='-' && ctype_digit(substr($time, 1))))
- $time=(int)$time;
- else
- $time=strtotime($time);
- }
- $date=CTimestamp::getDate($time,false,false);
- $tokens=$this->parseFormat($pattern);
- foreach($tokens as &$token)
- {
- if(is_array($token)) // a callback: method name, sub-pattern
- $token=$this->{$token[0]}($token[1],$date);
- }
- return implode('',$tokens);
- }
- /**
- * Formats a date according to a predefined pattern.
- * The predefined pattern is determined based on the date pattern width and time pattern width.
- * @param mixed $timestamp UNIX timestamp or a string in strtotime format
- * @param string $dateWidth width of the date pattern. It can be 'full', 'long', 'medium' and 'short'.
- * If null, it means the date portion will NOT appear in the formatting result
- * @param string $timeWidth width of the time pattern. It can be 'full', 'long', 'medium' and 'short'.
- * If null, it means the time portion will NOT appear in the formatting result
- * @return string formatted date time.
- */
- public function formatDateTime($timestamp,$dateWidth='medium',$timeWidth='medium')
- {
- if(!empty($dateWidth))
- $date=$this->format($this->_locale->getDateFormat($dateWidth),$timestamp);
- if(!empty($timeWidth))
- $time=$this->format($this->_locale->getTimeFormat($timeWidth),$timestamp);
- if(isset($date) && isset($time))
- {
- $dateTimePattern=$this->_locale->getDateTimeFormat();
- return strtr($dateTimePattern,array('{0}'=>$time,'{1}'=>$date));
- }
- elseif(isset($date))
- return $date;
- elseif(isset($time))
- return $time;
- }
- /**
- * Parses the datetime format pattern.
- * @param string $pattern the pattern to be parsed
- * @return array tokenized parsing result
- */
- protected function parseFormat($pattern)
- {
- static $formats=array(); // cache
- if(isset($formats[$pattern]))
- return $formats[$pattern];
- $tokens=array();
- $n=strlen($pattern);
- $isLiteral=false;
- $literal='';
- for($i=0;$i<$n;++$i)
- {
- $c=$pattern[$i];
- if($c==="'")
- {
- if($i<$n-1 && $pattern[$i+1]==="'")
- {
- $tokens[]="'";
- $i++;
- }
- elseif($isLiteral)
- {
- $tokens[]=$literal;
- $literal='';
- $isLiteral=false;
- }
- else
- {
- $isLiteral=true;
- $literal='';
- }
- }
- elseif($isLiteral)
- $literal.=$c;
- else
- {
- for($j=$i+1;$j<$n;++$j)
- {
- if($pattern[$j]!==$c)
- break;
- }
- $p=str_repeat($c,$j-$i);
- if(isset(self::$_formatters[$c]))
- $tokens[]=array(self::$_formatters[$c],$p);
- else
- $tokens[]=$p;
- $i=$j-1;
- }
- }
- if($literal!=='')
- $tokens[]=$literal;
- return $formats[$pattern]=$tokens;
- }
- /**
- * Get the year.
- * "yy" will return the last two digits of year.
- * "y...y" will pad the year with 0 in the front, e.g. "yyyyy" will generate "02008" for year 2008.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @return string formatted year
- */
- protected function formatYear($pattern,$date)
- {
- $year=$date['year'];
- if($pattern==='yy')
- return str_pad($year%100,2,'0',STR_PAD_LEFT);
- else
- return str_pad($year,strlen($pattern),'0',STR_PAD_LEFT);
- }
- /**
- * Get the month.
- * "M" will return integer 1 through 12;
- * "MM" will return two digits month number with necessary zero padding, e.g. 05;
- * "MMM" will return the abrreviated month name, e.g. "Jan";
- * "MMMM" will return the full month name, e.g. "January";
- * "MMMMM" will return the narrow month name, e.g. "J";
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "month" pattern is unknown
- * @return string month name
- */
- protected function formatMonth($pattern,$date)
- {
- $month=$date['mon'];
- switch($pattern)
- {
- case 'M':
- return $month;
- case 'MM':
- return str_pad($month,2,'0',STR_PAD_LEFT);
- case 'MMM':
- return $this->_locale->getMonthName($month,'abbreviated');
- case 'MMMM':
- return $this->_locale->getMonthName($month,'wide');
- case 'MMMMM':
- return $this->_locale->getMonthName($month,'narrow');
- case 'L':
- return $month;
- case 'LL':
- return str_pad($month,2,'0',STR_PAD_LEFT);
- case 'LLL':
- return $this->_locale->getMonthName($month,'abbreviated', true);
- case 'LLLL':
- return $this->_locale->getMonthName($month,'wide', true);
- case 'LLLLL':
- return $this->_locale->getMonthName($month,'narrow', true);
- default:
- throw new CException(Yii::t('yii','The pattern for month must be "M", "MM", "MMM", "MMMM", "L", "LL", "LLL" or "LLLL".'));
- }
- }
- /**
- * Get the day of the month.
- * "d" for non-padding, "dd" will always return 2 digits day numbers, e.g. 05.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "day" pattern is unknown
- * @return string day of the month
- */
- protected function formatDay($pattern,$date)
- {
- $day=$date['mday'];
- if($pattern==='d')
- return $day;
- elseif($pattern==='dd')
- return str_pad($day,2,'0',STR_PAD_LEFT);
- else
- throw new CException(Yii::t('yii','The pattern for day of the month must be "d" or "dd".'));
- }
- /**
- * Get the day in the year, e.g. [1-366]
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException is "dayInYear" pattern is unknown
- * @return integer hours in AM/PM format.
- */
- protected function formatDayInYear($pattern,$date)
- {
- $day=$date['yday'];
- if(($n=strlen($pattern))<=3)
- return str_pad($day,$n,'0',STR_PAD_LEFT);
- else
- throw new CException(Yii::t('yii','The pattern for day in year must be "D", "DD" or "DDD".'));
- }
- /**
- * Get day of week in the month, e.g. 2nd Wed in July.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "dayInMonth" pattern is unknown
- * @return integer day in month
- * @see http://www.unicode.org/reports/tr35/#Date_Format_Patterns
- */
- protected function formatDayInMonth($pattern,$date)
- {
- if($pattern==='F')
- return (int)(($date['mday']+6)/7);
- else
- throw new CException(Yii::t('yii','The pattern for day in month must be "F".'));
- }
- /**
- * Get the day of the week.
- * "E", "EE", "EEE" will return abbreviated week day name, e.g. "Tues";
- * "EEEE" will return full week day name;
- * "EEEEE" will return the narrow week day name, e.g. "T";
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "dayInWeek" pattern is unknown
- * @return string day of the week.
- * @see http://www.unicode.org/reports/tr35/#Date_Format_Patterns
- */
- protected function formatDayInWeek($pattern,$date)
- {
- $day=$date['wday'];
- switch($pattern)
- {
- case 'E':
- case 'EE':
- case 'EEE':
- case 'eee':
- return $this->_locale->getWeekDayName($day,'abbreviated');
- case 'EEEE':
- case 'eeee':
- return $this->_locale->getWeekDayName($day,'wide');
- case 'EEEEE':
- case 'eeeee':
- return $this->_locale->getWeekDayName($day,'narrow');
- case 'e':
- case 'ee':
- case 'c':
- return $day ? $day : 7;
- case 'ccc':
- return $this->_locale->getWeekDayName($day,'abbreviated',true);
- case 'cccc':
- return $this->_locale->getWeekDayName($day,'wide',true);
- case 'ccccc':
- return $this->_locale->getWeekDayName($day,'narrow',true);
- default:
- throw new CException(Yii::t('yii','The pattern for day of the week must be "E", "EE", "EEE", "EEEE", "EEEEE", "e", "ee", "eee", "eeee", "eeeee", "c", "cccc" or "ccccc".'));
- }
- }
- /**
- * Get the AM/PM designator, 12 noon is PM, 12 midnight is AM.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "period" pattern is unknown
- * @return string AM or PM designator
- */
- protected function formatPeriod($pattern,$date)
- {
- if($pattern==='a')
- {
- if(intval($date['hours']/12))
- return $this->_locale->getPMName();
- else
- return $this->_locale->getAMName();
- }
- else
- throw new CException(Yii::t('yii','The pattern for AM/PM marker must be "a".'));
- }
- /**
- * Get the hours in 24 hour format, i.e. [0-23].
- * "H" for non-padding, "HH" will always return 2 characters.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "hour24" pattern is unknown
- * @return string hours in 24 hour format.
- */
- protected function formatHour24($pattern,$date)
- {
- $hour=$date['hours'];
- if($pattern==='H')
- return $hour;
- elseif($pattern==='HH')
- return str_pad($hour,2,'0',STR_PAD_LEFT);
- else
- throw new CException(Yii::t('yii','The pattern for 24 hour format must be "H" or "HH".'));
- }
- /**
- * Get the hours in 12 hour format, i.e., [1-12]
- * "h" for non-padding, "hh" will always return 2 characters.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "hour12" pattern is unknown
- * @return string hours in 12 hour format.
- */
- protected function formatHour12($pattern,$date)
- {
- $hour=$date['hours'];
- $hour=($hour==12|$hour==0)?12:($hour)%12;
- if($pattern==='h')
- return $hour;
- elseif($pattern==='hh')
- return str_pad($hour,2,'0',STR_PAD_LEFT);
- else
- throw new CException(Yii::t('yii','The pattern for 12 hour format must be "h" or "hh".'));
- }
- /**
- * Get the hours [1-24].
- * 'k' for non-padding, and 'kk' with 2 characters padding.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "hourInDay" pattern is unknown
- * @return integer hours [1-24]
- */
- protected function formatHourInDay($pattern,$date)
- {
- $hour=$date['hours']==0?24:$date['hours'];
- if($pattern==='k')
- return $hour;
- elseif($pattern==='kk')
- return str_pad($hour,2,'0',STR_PAD_LEFT);
- else
- throw new CException(Yii::t('yii','The pattern for hour in day must be "k" or "kk".'));
- }
- /**
- * Get the hours in AM/PM format, e.g [0-11]
- * "K" for non-padding, "KK" will always return 2 characters.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "hourInPeriod" pattern is unknown
- * @return integer hours in AM/PM format.
- */
- protected function formatHourInPeriod($pattern,$date)
- {
- $hour=$date['hours']%12;
- if($pattern==='K')
- return $hour;
- elseif($pattern==='KK')
- return str_pad($hour,2,'0',STR_PAD_LEFT);
- else
- throw new CException(Yii::t('yii','The pattern for hour in AM/PM must be "K" or "KK".'));
- }
- /**
- * Get the minutes.
- * "m" for non-padding, "mm" will always return 2 characters.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "minutes" pattern is unknown
- * @return string minutes.
- */
- protected function formatMinutes($pattern,$date)
- {
- $minutes=$date['minutes'];
- if($pattern==='m')
- return $minutes;
- elseif($pattern==='mm')
- return str_pad($minutes,2,'0',STR_PAD_LEFT);
- else
- throw new CException(Yii::t('yii','The pattern for minutes must be "m" or "mm".'));
- }
- /**
- * Get the seconds.
- * "s" for non-padding, "ss" will always return 2 characters.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "seconds" pattern is unknown
- * @return string seconds
- */
- protected function formatSeconds($pattern,$date)
- {
- $seconds=$date['seconds'];
- if($pattern==='s')
- return $seconds;
- elseif($pattern==='ss')
- return str_pad($seconds,2,'0',STR_PAD_LEFT);
- else
- throw new CException(Yii::t('yii','The pattern for seconds must be "s" or "ss".'));
- }
- /**
- * Get the week in the year.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "weekInYear" pattern is unknown
- * @return integer week in year
- */
- protected function formatWeekInYear($pattern,$date)
- {
- if($pattern==='w')
- return @date('W',@mktime(0,0,0,$date['mon'],$date['mday'],$date['year']));
- else
- throw new CException(Yii::t('yii','The pattern for week in year must be "w".'));
- }
- /**
- * Get week in the month.
- * @param array $pattern result of {@link CTimestamp::getdate}.
- * @param string $date a pattern.
- * @throws CException if "weekInMonth" pattern is unknown
- * @return integer week in month
- */
- protected function formatWeekInMonth($pattern,$date)
- {
- if($pattern==='W')
- {
- $weekDay=date('N',mktime(0,0,0,$date['mon'],1,$date['year']));
- return floor(($weekDay+$date['mday']-2)/7)+1;
- }
- else
- throw new CException(Yii::t('yii','The pattern for week in month must be "W".'));
- }
- /**
- * Get the timezone of the server machine.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "timeZone" pattern is unknown
- * @return string time zone
- * @todo How to get the timezone for a different region?
- */
- protected function formatTimeZone($pattern,$date)
- {
- if($pattern[0]==='z' || $pattern[0]==='v')
- return @date('T', @mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']));
- elseif($pattern[0]==='Z')
- return @date('P', @mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']));
- else
- throw new CException(Yii::t('yii','The pattern for time zone must be "z" or "v".'));
- }
- /**
- * Get the era. i.e. in gregorian, year > 0 is AD, else BC.
- * @param string $pattern a pattern.
- * @param array $date result of {@link CTimestamp::getdate}.
- * @throws CException if "era" pattern is unknown
- * @return string era
- * @todo How to support multiple Eras?, e.g. Japanese.
- */
- protected function formatEra($pattern,$date)
- {
- $era=$date['year']>0 ? 1 : 0;
- switch($pattern)
- {
- case 'G':
- case 'GG':
- case 'GGG':
- return $this->_locale->getEraName($era,'abbreviated');
- case 'GGGG':
- return $this->_locale->getEraName($era,'wide');
- case 'GGGGG':
- return $this->_locale->getEraName($era,'narrow');
- default:
- throw new CException(Yii::t('yii','The pattern for era must be "G", "GG", "GGG", "GGGG" or "GGGGG".'));
- }
- }
- }
|