3 * TSimpleDateFormatter class file
5 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
6 * @link http://www.pradosoft.com/
7 * @copyright Copyright © 2005-2013 PradoSoft
8 * @license http://www.pradosoft.com/license/
9 * @version $Id: TSimpleDateFormatter.php 3245 2013-01-07 20:23:32Z ctrlaltca $
10 * @package System.Util
14 * TSimpleDateFormatter class.
16 * Formats and parses dates using the SimpleDateFormat pattern.
17 * This pattern is compatible with the I18N and java's SimpleDateFormatter.
19 * Pattern | Description
20 * ----------------------------------------------------
21 * d | Day of month 1 to 31, no padding
22 * dd | Day of monath 01 to 31, zero leading
23 * M | Month digit 1 to 12, no padding
24 * MM | Month digit 01 to 12, zero leading
25 * yy | 2 year digit, e.g., 96, 05
26 * yyyy | 4 year digit, e.g., 2005
27 * ----------------------------------------------------
30 * Usage example, to format a date
32 * $formatter = new TSimpleDateFormatter("dd/MM/yyy");
33 * echo $formatter->format(time());
36 * To parse the date string into a date timestamp.
38 * $formatter = new TSimpleDateFormatter("d-M-yyy");
39 * echo $formatter->parse("24-6-2005");
42 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
43 * @version $Id: TSimpleDateFormatter.php 3245 2013-01-07 20:23:32Z ctrlaltca $
44 * @package System.Util
47 class TSimpleDateFormatter
56 * Charset, default is 'UTF-8'
59 private $charset = 'UTF-8';
62 * Constructor, create a new date time formatter.
63 * @param string formatting pattern.
64 * @param string pattern and value charset
66 public function __construct($pattern, $charset='UTF-8')
68 $this->setPattern($pattern);
69 $this->setCharset($charset);
73 * @return string formatting pattern.
75 public function getPattern()
77 return $this->pattern;
81 * @param string formatting pattern.
83 public function setPattern($pattern)
85 $this->pattern = $pattern;
89 * @return string formatting charset.
91 public function getCharset()
93 return $this->charset;
97 * @param string formatting charset.
99 public function setCharset($charset)
101 $this->charset = $charset;
105 * Format the date according to the pattern.
106 * @param string|int the date to format, either integer or a string readable by strtotime.
107 * @return string formatted date.
109 public function format($value)
111 $date = $this->getDate($value);
112 $bits['yyyy'] = $date['year'];
113 $bits['yy'] = substr("{$date['year']}", -2);
115 $bits['MM'] = str_pad("{$date['mon']}", 2, '0', STR_PAD_LEFT);
116 $bits['M'] = $date['mon'];
118 $bits['dd'] = str_pad("{$date['mday']}", 2, '0', STR_PAD_LEFT);
119 $bits['d'] = $date['mday'];
121 $pattern = preg_replace('/M{3,4}/', 'MM', $this->pattern);
122 return str_replace(array_keys($bits), $bits, $pattern);
125 public function getMonthPattern()
127 if(is_int(strpos($this->pattern, 'MMMM')))
129 if(is_int(strpos($this->pattern, 'MMM')))
131 if(is_int(strpos($this->pattern, 'MM')))
133 if(is_int(strpos($this->pattern, 'M')))
138 public function getDayPattern()
140 if(is_int(strpos($this->pattern, 'dd')))
142 if(is_int(strpos($this->pattern, 'd')))
147 public function getYearPattern()
149 if(is_int(strpos($this->pattern, 'yyyy')))
151 if(is_int(strpos($this->pattern, 'yy')))
156 public function getDayMonthYearOrdering()
159 if(is_int($day= strpos($this->pattern, 'd')))
160 $ordering['day'] = $day;
161 if(is_int($month= strpos($this->pattern, 'M')))
162 $ordering['month'] = $month;
163 if(is_int($year= strpos($this->pattern, 'yy')))
164 $ordering['year'] = $year;
166 return array_keys($ordering);
170 * Gets the time stamp from string or integer.
171 * @param string|int date to parse
172 * @return array date info array
174 private function getDate($value)
176 $s = Prado::createComponent('System.Util.TDateTimeStamp');
177 if(is_numeric($value))
178 return $s->getDate($value);
180 return $s->parseDate($value);
184 * @return boolean true if the given value matches with the date pattern.
186 public function isValidDate($value)
188 if($value === null) {
191 return $this->parse($value, false) !== null;
196 * Parse the string according to the pattern.
197 * @param string|int date string or integer to parse
198 * @return int date time stamp
199 * @throws TInvalidDataValueException if date string is malformed.
201 public function parse($value,$defaultToCurrentTime=true)
203 if(is_int($value) || is_float($value))
205 else if(!is_string($value))
206 throw new TInvalidDataValueException('date_to_parse_must_be_string', $value);
208 if(empty($this->pattern)) return time();
212 if($this->length(trim($value)) < 1)
213 return $defaultToCurrentTime ? $date : null;
215 $pattern = $this->pattern;
219 $pattern_length = $this->length($pattern);
225 if($defaultToCurrentTime)
227 $year = "{$date['year']}";
228 $month = $date['mon'];
229 $day = $date['mday'];
238 while ($i_format < $pattern_length)
240 $c = $this->charAt($pattern,$i_format);
242 while ($this->charEqual($pattern, $i_format, $c)
243 && ($i_format < $pattern_length))
245 $token .= $this->charAt($pattern, $i_format++);
248 if ($token=='yyyy' || $token=='yy' || $token=='y')
250 if ($token=='yyyy') { $x=4;$y=4; }
251 if ($token=='yy') { $x=2;$y=2; }
252 if ($token=='y') { $x=2;$y=4; }
253 $year = $this->getInteger($value,$i_val,$x,$y);
256 //throw new TInvalidDataValueException('Invalid year', $value);
257 $i_val += strlen($year);
258 if(strlen($year) == 2)
262 $year = $iYear + 1900;
264 $year = $iYear + 2000;
268 elseif($token=='MM' || $token=='M')
270 $month=$this->getInteger($value,$i_val,
271 $this->length($token),2);
272 $iMonth = (int)$month;
273 if($month === null || $iMonth < 1 || $iMonth > 12 )
275 //throw new TInvalidDataValueException('Invalid month', $value);
276 $i_val += strlen($month);
279 elseif ($token=='dd' || $token=='d')
281 $day = $this->getInteger($value,$i_val,
282 $this->length($token), 2);
284 if($day === null || $iDay < 1 || $iDay >31)
286 //throw new TInvalidDataValueException('Invalid day', $value);
287 $i_val += strlen($day);
292 if($this->substring($value, $i_val, $this->length($token)) != $token)
294 //throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value);
296 $i_val += $this->length($token);
299 if ($i_val != $this->length($value))
301 //throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value);
302 if(!$defaultToCurrentTime && ($month === null || $day === null || $year === null))
309 $day = (int)$day <= 0 ? 1 : (int)$day;
310 $month = (int)$month <= 0 ? 1 : (int)$month;
311 $s = Prado::createComponent('System.Util.TDateTimeStamp');
312 return $s->getTimeStamp(0, 0, 0, $month, $day, $year);
317 * Calculate the length of a string, may be consider iconv_strlen?
319 private function length($string)
321 //use iconv_strlen or just strlen?
322 return strlen($string);
326 * Get the char at a position.
328 private function charAt($string, $pos)
330 return $this->substring($string, $pos, 1);
334 * Gets a portion of a string, uses iconv_substr.
336 private function substring($string, $start, $length)
338 return iconv_substr($string, $start, $length);
342 * Returns true if char at position equals a particular char.
344 private function charEqual($string, $pos, $char)
346 return $this->charAt($string, $pos) == $char;
350 * Gets integer from part of a string, allows integers of any length.
351 * @param string string to retrieve the integer from.
352 * @param int starting position
353 * @param int minimum integer length
354 * @param int maximum integer length
355 * @return string integer portion of the string, null otherwise
357 private function getInteger($str,$i,$minlength,$maxlength)
359 //match for digits backwards
360 for ($x = $maxlength; $x >= $minlength; $x--)
362 $token= $this->substring($str, $i,$x);
363 if ($this->length($token) < $minlength)
365 if (preg_match('/^\d+$/', $token))