* @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2013 PradoSoft * @license http://www.pradosoft.com/license/ * @version $Id: TSimpleDateFormatter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ * @package System.Util */ /** * TSimpleDateFormatter class. * * Formats and parses dates using the SimpleDateFormat pattern. * This pattern is compatible with the I18N and java's SimpleDateFormatter. * * Pattern | Description * ---------------------------------------------------- * d | Day of month 1 to 31, no padding * dd | Day of monath 01 to 31, zero leading * M | Month digit 1 to 12, no padding * MM | Month digit 01 to 12, zero leading * yy | 2 year digit, e.g., 96, 05 * yyyy | 4 year digit, e.g., 2005 * ---------------------------------------------------- * * * Usage example, to format a date * * $formatter = new TSimpleDateFormatter("dd/MM/yyy"); * echo $formatter->format(time()); * * * To parse the date string into a date timestamp. * * $formatter = new TSimpleDateFormatter("d-M-yyy"); * echo $formatter->parse("24-6-2005"); * * * @author Wei Zhuo * @version $Id: TSimpleDateFormatter.php 3245 2013-01-07 20:23:32Z ctrlaltca $ * @package System.Util * @since 3.0 */ class TSimpleDateFormatter { /** * Formatting pattern. * @var string */ private $pattern; /** * Charset, default is 'UTF-8' * @var string */ private $charset = 'UTF-8'; /** * Constructor, create a new date time formatter. * @param string formatting pattern. * @param string pattern and value charset */ public function __construct($pattern, $charset='UTF-8') { $this->setPattern($pattern); $this->setCharset($charset); } /** * @return string formatting pattern. */ public function getPattern() { return $this->pattern; } /** * @param string formatting pattern. */ public function setPattern($pattern) { $this->pattern = $pattern; } /** * @return string formatting charset. */ public function getCharset() { return $this->charset; } /** * @param string formatting charset. */ public function setCharset($charset) { $this->charset = $charset; } /** * Format the date according to the pattern. * @param string|int the date to format, either integer or a string readable by strtotime. * @return string formatted date. */ public function format($value) { $date = $this->getDate($value); $bits['yyyy'] = $date['year']; $bits['yy'] = substr("{$date['year']}", -2); $bits['MM'] = str_pad("{$date['mon']}", 2, '0', STR_PAD_LEFT); $bits['M'] = $date['mon']; $bits['dd'] = str_pad("{$date['mday']}", 2, '0', STR_PAD_LEFT); $bits['d'] = $date['mday']; $pattern = preg_replace('/M{3,4}/', 'MM', $this->pattern); return str_replace(array_keys($bits), $bits, $pattern); } public function getMonthPattern() { if(is_int(strpos($this->pattern, 'MMMM'))) return 'MMMM'; if(is_int(strpos($this->pattern, 'MMM'))) return 'MMM'; if(is_int(strpos($this->pattern, 'MM'))) return 'MM'; if(is_int(strpos($this->pattern, 'M'))) return 'M'; return false; } public function getDayPattern() { if(is_int(strpos($this->pattern, 'dd'))) return 'dd'; if(is_int(strpos($this->pattern, 'd'))) return 'd'; return false; } public function getYearPattern() { if(is_int(strpos($this->pattern, 'yyyy'))) return 'yyyy'; if(is_int(strpos($this->pattern, 'yy'))) return 'yy'; return false; } public function getDayMonthYearOrdering() { $ordering = array(); if(is_int($day= strpos($this->pattern, 'd'))) $ordering['day'] = $day; if(is_int($month= strpos($this->pattern, 'M'))) $ordering['month'] = $month; if(is_int($year= strpos($this->pattern, 'yy'))) $ordering['year'] = $year; asort($ordering); return array_keys($ordering); } /** * Gets the time stamp from string or integer. * @param string|int date to parse * @return array date info array */ private function getDate($value) { $s = Prado::createComponent('System.Util.TDateTimeStamp'); if(is_numeric($value)) return $s->getDate($value); else return $s->parseDate($value); } /** * @return boolean true if the given value matches with the date pattern. */ public function isValidDate($value) { if($value === null) { return false; } else { return $this->parse($value, false) !== null; } } /** * Parse the string according to the pattern. * @param string|int date string or integer to parse * @return int date time stamp * @throws TInvalidDataValueException if date string is malformed. */ public function parse($value,$defaultToCurrentTime=true) { if(is_int($value) || is_float($value)) return $value; else if(!is_string($value)) throw new TInvalidDataValueException('date_to_parse_must_be_string', $value); if(empty($this->pattern)) return time(); $date = time(); if($this->length(trim($value)) < 1) return $defaultToCurrentTime ? $date : null; $pattern = $this->pattern; $i_val = 0; $i_format = 0; $pattern_length = $this->length($pattern); $c = ''; $token=''; $x=null; $y=null; if($defaultToCurrentTime) { $year = "{$date['year']}"; $month = $date['mon']; $day = $date['mday']; } else { $year = null; $month = null; $day = null; } while ($i_format < $pattern_length) { $c = $this->charAt($pattern,$i_format); $token=''; while ($this->charEqual($pattern, $i_format, $c) && ($i_format < $pattern_length)) { $token .= $this->charAt($pattern, $i_format++); } if ($token=='yyyy' || $token=='yy' || $token=='y') { if ($token=='yyyy') { $x=4;$y=4; } if ($token=='yy') { $x=2;$y=2; } if ($token=='y') { $x=2;$y=4; } $year = $this->getInteger($value,$i_val,$x,$y); if($year === null) return null; //throw new TInvalidDataValueException('Invalid year', $value); $i_val += strlen($year); if(strlen($year) == 2) { $iYear = (int)$year; if($iYear > 70) $year = $iYear + 1900; else $year = $iYear + 2000; } $year = (int)$year; } elseif($token=='MM' || $token=='M') { $month=$this->getInteger($value,$i_val, $this->length($token),2); $iMonth = (int)$month; if($month === null || $iMonth < 1 || $iMonth > 12 ) return null; //throw new TInvalidDataValueException('Invalid month', $value); $i_val += strlen($month); $month = $iMonth; } elseif ($token=='dd' || $token=='d') { $day = $this->getInteger($value,$i_val, $this->length($token), 2); $iDay = (int)$day; if($day === null || $iDay < 1 || $iDay >31) return null; //throw new TInvalidDataValueException('Invalid day', $value); $i_val += strlen($day); $day = $iDay; } else { if($this->substring($value, $i_val, $this->length($token)) != $token) return null; //throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value); else $i_val += $this->length($token); } } if ($i_val != $this->length($value)) return null; //throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value); if(!$defaultToCurrentTime && ($month === null || $day === null || $year === null)) return null; else { if(empty($year)) { $year = date('Y'); } $day = (int)$day <= 0 ? 1 : (int)$day; $month = (int)$month <= 0 ? 1 : (int)$month; $s = Prado::createComponent('System.Util.TDateTimeStamp'); return $s->getTimeStamp(0, 0, 0, $month, $day, $year); } } /** * Calculate the length of a string, may be consider iconv_strlen? */ private function length($string) { //use iconv_strlen or just strlen? return strlen($string); } /** * Get the char at a position. */ private function charAt($string, $pos) { return $this->substring($string, $pos, 1); } /** * Gets a portion of a string, uses iconv_substr. */ private function substring($string, $start, $length) { return iconv_substr($string, $start, $length); } /** * Returns true if char at position equals a particular char. */ private function charEqual($string, $pos, $char) { return $this->charAt($string, $pos) == $char; } /** * Gets integer from part of a string, allows integers of any length. * @param string string to retrieve the integer from. * @param int starting position * @param int minimum integer length * @param int maximum integer length * @return string integer portion of the string, null otherwise */ private function getInteger($str,$i,$minlength,$maxlength) { //match for digits backwards for ($x = $maxlength; $x >= $minlength; $x--) { $token= $this->substring($str, $i,$x); if ($this->length($token) < $minlength) return null; if (preg_match('/^\d+$/', $token)) return $token; } return null; } }