]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/framework/Util/TSimpleDateFormatter.php
baculum: New Baculum API and Baculum Web
[bacula/bacula] / gui / baculum / framework / Util / TSimpleDateFormatter.php
1 <?php
2 /**
3  * TSimpleDateFormatter class file
4  *
5  * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
6  * @link https://github.com/pradosoft/prado
7  * @copyright Copyright &copy; 2005-2016 The PRADO Group
8  * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT
9  * @package System.Util
10  */
11
12 /**
13  * TSimpleDateFormatter class.
14  *
15  * Formats and parses dates using the SimpleDateFormat pattern.
16  * This pattern is compatible with the I18N and java's SimpleDateFormatter.
17  * <code>
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  * ----------------------------------------------------
27  * </code>
28  *
29  * Usage example, to format a date
30  * <code>
31  * $formatter = new TSimpleDateFormatter("dd/MM/yyy");
32  * echo $formatter->format(time());
33  * </code>
34  *
35  * To parse the date string into a date timestamp.
36  * <code>
37  * $formatter = new TSimpleDateFormatter("d-M-yyy");
38  * echo $formatter->parse("24-6-2005");
39  * </code>
40  *
41  * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
42  * @package System.Util
43  * @since 3.0
44  */
45 class TSimpleDateFormatter
46 {
47         /**
48          * Formatting pattern.
49          * @var string
50          */
51         private $pattern;
52
53         /**
54          * Charset, default is 'UTF-8'
55          * @var string
56          */
57         private $charset = 'UTF-8';
58
59         /**
60          * Constructor, create a new date time formatter.
61          * @param string formatting pattern.
62          * @param string pattern and value charset
63          */
64         public function __construct($pattern, $charset='UTF-8')
65         {
66                 $this->setPattern($pattern);
67                 $this->setCharset($charset);
68         }
69
70         /**
71          * @return string formatting pattern.
72          */
73         public function getPattern()
74         {
75                 return $this->pattern;
76         }
77
78         /**
79          * @param string formatting pattern.
80          */
81         public function setPattern($pattern)
82         {
83                 $this->pattern = $pattern;
84         }
85
86         /**
87          * @return string formatting charset.
88          */
89         public function getCharset()
90         {
91                 return $this->charset;
92         }
93
94         /**
95          * @param string formatting charset.
96          */
97         public function setCharset($charset)
98         {
99                 $this->charset = $charset;
100         }
101
102         /**
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.
106          */
107         public function format($value)
108         {
109                 $date = $this->getDate($value);
110                 $bits['yyyy'] = $date['year'];
111                 $bits['yy'] = substr("{$date['year']}", -2);
112
113                 $bits['MM'] = str_pad("{$date['mon']}", 2, '0', STR_PAD_LEFT);
114                 $bits['M'] = $date['mon'];
115
116                 $bits['dd'] = str_pad("{$date['mday']}", 2, '0', STR_PAD_LEFT);
117                 $bits['d'] = $date['mday'];
118
119                 $pattern = preg_replace('/M{3,4}/', 'MM', $this->pattern);
120                 return str_replace(array_keys($bits), $bits, $pattern);
121         }
122
123         public function getMonthPattern()
124         {
125                 if(is_int(strpos($this->pattern, 'MMMM')))
126                         return 'MMMM';
127                 if(is_int(strpos($this->pattern, 'MMM')))
128                         return 'MMM';
129                 if(is_int(strpos($this->pattern, 'MM')))
130                         return 'MM';
131                 if(is_int(strpos($this->pattern, 'M')))
132                         return 'M';
133                 return false;
134         }
135
136         public function getDayPattern()
137         {
138                 if(is_int(strpos($this->pattern, 'dd')))
139                         return 'dd';
140                 if(is_int(strpos($this->pattern, 'd')))
141                         return 'd';
142                 return false;
143         }
144
145         public function getYearPattern()
146         {
147                 if(is_int(strpos($this->pattern, 'yyyy')))
148                         return 'yyyy';
149                 if(is_int(strpos($this->pattern, 'yy')))
150                         return 'yy';
151                 return false;
152         }
153
154         public function getDayMonthYearOrdering()
155         {
156                 $ordering = array();
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;
163                 asort($ordering);
164                 return array_keys($ordering);
165         }
166
167         /**
168          * Gets the time stamp from string or integer.
169          * @param string|int date to parse
170          * @return array date info array
171          */
172         private function getDate($value)
173         {
174                 $s = Prado::createComponent('System.Util.TDateTimeStamp');
175                 if(is_numeric($value))
176                         return $s->getDate($value);
177                 else
178                         return $s->parseDate($value);
179         }
180
181         /**
182          * @return boolean true if the given value matches with the date pattern.
183          */
184         public function isValidDate($value)
185         {
186                 if($value === null) {
187                         return false;
188                 } else {
189                         return $this->parse($value, false) !== null;
190                 }
191         }
192
193         /**
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.
198          */
199         public function parse($value,$defaultToCurrentTime=true)
200         {
201                 if(is_int($value) || is_float($value))
202                         return $value;
203                 else if(!is_string($value))
204                         throw new TInvalidDataValueException('date_to_parse_must_be_string', $value);
205
206                 if(empty($this->pattern)) return time();
207
208                 $date = time();
209
210                 if($this->length(trim($value)) < 1)
211                         return $defaultToCurrentTime ? $date : null;
212
213                 $pattern = $this->pattern;
214
215                 $i_val = 0;
216                 $i_format = 0;
217                 $pattern_length = $this->length($pattern);
218                 $c = '';
219                 $token='';
220                 $x=null; $y=null;
221
222
223                 if($defaultToCurrentTime)
224                 {
225                         $year = "{$date['year']}";
226                         $month = $date['mon'];
227                         $day = $date['mday'];
228                 }
229                 else
230                 {
231                         $year = null;
232                         $month = null;
233                         $day = null;
234                 }
235
236                 while ($i_format < $pattern_length)
237                 {
238                         $c = $this->charAt($pattern,$i_format);
239                         $token='';
240                         while ($this->charEqual($pattern, $i_format, $c)
241                                                 && ($i_format < $pattern_length))
242                         {
243                                 $token .= $this->charAt($pattern, $i_format++);
244                         }
245
246                         if ($token=='yyyy' || $token=='yy' || $token=='y')
247                         {
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);
252                                 if($year === null)
253                                         return null;
254                                         //throw new TInvalidDataValueException('Invalid year', $value);
255                                 $i_val += strlen($year);
256                                 if(strlen($year) == 2)
257                                 {
258                                         $iYear = (int)$year;
259                                         if($iYear > 70)
260                                                 $year = $iYear + 1900;
261                                         else
262                                                 $year = $iYear + 2000;
263                                 }
264                                 $year = (int)$year;
265                         }
266                         elseif($token=='MM' || $token=='M')
267                         {
268                                 $month=$this->getInteger($value,$i_val,
269                                                                         $this->length($token),2);
270                                 $iMonth = (int)$month;
271                                 if($month === null || $iMonth < 1 || $iMonth > 12 )
272                                         return null;
273                                         //throw new TInvalidDataValueException('Invalid month', $value);
274                                 $i_val += strlen($month);
275                                 $month = $iMonth;
276                         }
277                         elseif ($token=='dd' || $token=='d')
278                         {
279                                 $day = $this->getInteger($value,$i_val,
280                                                                         $this->length($token), 2);
281                                 $iDay = (int)$day;
282                                 if($day === null || $iDay < 1 || $iDay >31)
283                                         return null;
284                                         //throw new TInvalidDataValueException('Invalid day', $value);
285                                 $i_val += strlen($day);
286                                 $day = $iDay;
287                         }
288                         else
289                         {
290                                 if($this->substring($value, $i_val, $this->length($token)) != $token)
291                                         return null;
292                                         //throw new TInvalidDataValueException("Subpattern '{$this->pattern}' mismatch", $value);
293                                 else
294                                         $i_val += $this->length($token);
295                         }
296                 }
297                 if ($i_val != $this->length($value))
298                         return null;
299                         //throw new TInvalidDataValueException("Pattern '{$this->pattern}' mismatch", $value);
300                 if(!$defaultToCurrentTime && ($month === null || $day === null || $year === null))
301                         return null;
302                 else
303                 {
304                         if(empty($year)) {
305                                 $year = date('Y');
306                         }
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);
311                 }
312         }
313
314         /**
315          * Calculate the length of a string, may be consider iconv_strlen?
316          */
317         private function length($string)
318         {
319                 //use iconv_strlen or just strlen?
320                 return strlen($string);
321         }
322
323         /**
324          * Get the char at a position.
325          */
326         private function charAt($string, $pos)
327         {
328                 return $this->substring($string, $pos, 1);
329         }
330
331         /**
332          * Gets a portion of a string, uses iconv_substr.
333          */
334         private function substring($string, $start, $length)
335         {
336                 return iconv_substr($string, $start, $length);
337         }
338
339         /**
340          * Returns true if char at position equals a particular char.
341          */
342         private function charEqual($string, $pos, $char)
343         {
344                 return $this->charAt($string, $pos) == $char;
345         }
346
347         /**
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
354          */
355         private function getInteger($str,$i,$minlength,$maxlength)
356         {
357                 //match for digits backwards
358                 for ($x = $maxlength; $x >= $minlength; $x--)
359                 {
360                         $token= $this->substring($str, $i,$x);
361                         if ($this->length($token) < $minlength)
362                                 return null;
363                         if (preg_match('/^\d+$/', $token))
364                                 return $token;
365                 }
366                 return null;
367         }
368 }
369