3 * TSimpleDateFormatter class file
5 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
6 * @link https://github.com/pradosoft/prado
7 * @copyright Copyright © 2005-2016 The PRADO Group
8 * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
13 * TSimpleDateFormatter class.
15 * Formats and parses dates using the SimpleDateFormat pattern.
16 * This pattern is compatible with the I18N and java's SimpleDateFormatter.
18 * Pattern | Description
19 * ----------------------------------------------------
20 * d | Day of month 1 to 31, no padding
21 * dd | Day of monath 01 to 31, zero leading
22 * M | Month digit 1 to 12, no padding
23 * MM | Month digit 01 to 12, zero leading
24 * yy | 2 year digit, e.g., 96, 05
25 * yyyy | 4 year digit, e.g., 2005
26 * ----------------------------------------------------
29 * Usage example, to format a date
31 * $formatter = new TSimpleDateFormatter("dd/MM/yyy");
32 * echo $formatter->format(time());
35 * To parse the date string into a date timestamp.
37 * $formatter = new TSimpleDateFormatter("d-M-yyy");
38 * echo $formatter->parse("24-6-2005");
41 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
42 * @package System.Util
45 class TSimpleDateFormatter
54 * Charset, default is 'UTF-8'
57 private $charset = 'UTF-8';
60 * Constructor, create a new date time formatter.
61 * @param string formatting pattern.
62 * @param string pattern and value charset
64 public function __construct($pattern, $charset='UTF-8')
66 $this->setPattern($pattern);
67 $this->setCharset($charset);
71 * @return string formatting pattern.
73 public function getPattern()
75 return $this->pattern;
79 * @param string formatting pattern.
81 public function setPattern($pattern)
83 $this->pattern = $pattern;
87 * @return string formatting charset.
89 public function getCharset()
91 return $this->charset;
95 * @param string formatting charset.
97 public function setCharset($charset)
99 $this->charset = $charset;
103 * Format the date according to the pattern.
104 * @param string|int the date to format, either integer or a string readable by strtotime.
105 * @return string formatted date.
107 public function format($value)
109 $date = $this->getDate($value);
110 $bits['yyyy'] = $date['year'];
111 $bits['yy'] = substr("{$date['year']}", -2);
113 $bits['MM'] = str_pad("{$date['mon']}", 2, '0', STR_PAD_LEFT);
114 $bits['M'] = $date['mon'];
116 $bits['dd'] = str_pad("{$date['mday']}", 2, '0', STR_PAD_LEFT);
117 $bits['d'] = $date['mday'];
119 $pattern = preg_replace('/M{3,4}/', 'MM', $this->pattern);
120 return str_replace(array_keys($bits), $bits, $pattern);
123 public function getMonthPattern()
125 if(is_int(strpos($this->pattern, 'MMMM')))
127 if(is_int(strpos($this->pattern, 'MMM')))
129 if(is_int(strpos($this->pattern, 'MM')))
131 if(is_int(strpos($this->pattern, 'M')))
136 public function getDayPattern()
138 if(is_int(strpos($this->pattern, 'dd')))
140 if(is_int(strpos($this->pattern, 'd')))
145 public function getYearPattern()
147 if(is_int(strpos($this->pattern, 'yyyy')))
149 if(is_int(strpos($this->pattern, 'yy')))
154 public function getDayMonthYearOrdering()
157 if(is_int($day= strpos($this->pattern, 'd')))
158 $ordering['day'] = $day;
159 if(is_int($month= strpos($this->pattern, 'M')))
160 $ordering['month'] = $month;
161 if(is_int($year= strpos($this->pattern, 'yy')))
162 $ordering['year'] = $year;
164 return array_keys($ordering);
168 * Gets the time stamp from string or integer.
169 * @param string|int date to parse
170 * @return array date info array
172 private function getDate($value)
174 $s = Prado::createComponent('System.Util.TDateTimeStamp');
175 if(is_numeric($value))
176 return $s->getDate($value);
178 return $s->parseDate($value);
182 * @return boolean true if the given value matches with the date pattern.
184 public function isValidDate($value)
186 if($value === null) {
189 return $this->parse($value, false) !== null;
194 * Parse the string according to the pattern.
195 * @param string|int date string or integer to parse
196 * @return int date time stamp
197 * @throws TInvalidDataValueException if date string is malformed.
199 public function parse($value,$defaultToCurrentTime=true)
201 if(is_int($value) || is_float($value))
203 else if(!is_string($value))
204 throw new TInvalidDataValueException('date_to_parse_must_be_string', $value);
206 if(empty($this->pattern)) return time();
210 if($this->length(trim($value)) < 1)
211 return $defaultToCurrentTime ? $date : null;
213 $pattern = $this->pattern;
217 $pattern_length = $this->length($pattern);
223 if($defaultToCurrentTime)
225 $year = "{$date['year']}";
226 $month = $date['mon'];
227 $day = $date['mday'];
236 while ($i_format < $pattern_length)
238 $c = $this->charAt($pattern,$i_format);
240 while ($this->charEqual($pattern, $i_format, $c)
241 && ($i_format < $pattern_length))
243 $token .= $this->charAt($pattern, $i_format++);
246 if ($token=='yyyy' || $token=='yy' || $token=='y')
248 if ($token=='yyyy') { $x=4;$y=4; }
249 if ($token=='yy') { $x=2;$y=2; }
250 if ($token=='y') { $x=2;$y=4; }
251 $year = $this->getInteger($value,$i_val,$x,$y);
254 //throw new TInvalidDataValueException('Invalid year', $value);
255 $i_val += strlen($year);
256 if(strlen($year) == 2)
260 $year = $iYear + 1900;
262 $year = $iYear + 2000;
266 elseif($token=='MM' || $token=='M')
268 $month=$this->getInteger($value,$i_val,
269 $this->length($token),2);
270 $iMonth = (int)$month;
271 if($month === null || $iMonth < 1 || $iMonth > 12 )
273 //throw new TInvalidDataValueException('Invalid month', $value);
274 $i_val += strlen($month);
277 elseif ($token=='dd' || $token=='d')
279 $day = $this->getInteger($value,$i_val,
280 $this->length($token), 2);
282 if($day === null || $iDay < 1 || $iDay >31)
284 //throw new TInvalidDataValueException('Invalid day', $value);
285 $i_val += strlen($day);
290 if($this->substring($value, $i_val, $this->length($token)) != $token)
292 //throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value);
294 $i_val += $this->length($token);
297 if ($i_val != $this->length($value))
299 //throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value);
300 if(!$defaultToCurrentTime && ($month === null || $day === null || $year === null))
307 $day = (int)$day <= 0 ? 1 : (int)$day;
308 $month = (int)$month <= 0 ? 1 : (int)$month;
309 $s = Prado::createComponent('System.Util.TDateTimeStamp');
310 return $s->getTimeStamp(0, 0, 0, $month, $day, $year);
315 * Calculate the length of a string, may be consider iconv_strlen?
317 private function length($string)
319 //use iconv_strlen or just strlen?
320 return strlen($string);
324 * Get the char at a position.
326 private function charAt($string, $pos)
328 return $this->substring($string, $pos, 1);
332 * Gets a portion of a string, uses iconv_substr.
334 private function substring($string, $start, $length)
336 return iconv_substr($string, $start, $length);
340 * Returns true if char at position equals a particular char.
342 private function charEqual($string, $pos, $char)
344 return $this->charAt($string, $pos) == $char;
348 * Gets integer from part of a string, allows integers of any length.
349 * @param string string to retrieve the integer from.
350 * @param int starting position
351 * @param int minimum integer length
352 * @param int maximum integer length
353 * @return string integer portion of the string, null otherwise
355 private function getInteger($str,$i,$minlength,$maxlength)
357 //match for digits backwards
358 for ($x = $maxlength; $x >= $minlength; $x--)
360 $token= $this->substring($str, $i,$x);
361 if ($this->length($token) < $minlength)
363 if (preg_match('/^\d+$/', $token))