A bit of refactoring with getting rid of some GLOBALS.
[timetracker.git] / WEB-INF / lib / DateAndTime.class.php
1 <?php
2 // +----------------------------------------------------------------------+
3 // | Anuko Time Tracker
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
6 // +----------------------------------------------------------------------+
7 // | LIBERAL FREEWARE LICENSE: This source code document may be used
8 // | by anyone for any purpose, and freely redistributed alone or in
9 // | combination with other software, provided that the license is obeyed.
10 // |
11 // | There are only two ways to violate the license:
12 // |
13 // | 1. To redistribute this code in source form, with the copyright
14 // |    notice or license removed or altered. (Distributing in compiled
15 // |    forms without embedded copyright notices is permitted).
16 // |
17 // | 2. To redistribute modified versions of this code in *any* form
18 // |    that bears insufficient indications that the modifications are
19 // |    not the work of the original author(s).
20 // |
21 // | This license applies to this document only, not any other software
22 // | that it may be combined with.
23 // |
24 // +----------------------------------------------------------------------+
25 // | Contributors:
26 // | https://www.anuko.com/time_tracker/credits.htm
27 // +----------------------------------------------------------------------+
28
29 /**
30  * Parse a time/date generated with strftime().
31  *
32  * This function is the same as the original one defined by PHP (Linux/Unix only),
33  *  but now you can use it on Windows too.
34  *  Limitation : Only this format can be parsed %S, %M, %H, %d, %m, %Y
35  *
36  * @author Lionel SAURON
37  * @version 1.0
38  * @public
39  *
40  * @param $sDate(string)    The string to parse (e.g. returned from strftime()).
41  * @param $sFormat(string)  The format used in date  (e.g. the same as used in strftime()).
42  * @return (array)          Returns an array with the <code>$sDate</code> parsed, or <code>false</code> on error.
43  */
44
45 function my_strptime($sDate, $sFormat)
46 {
47     $aResult = array
48     (
49         'tm_sec'   => 0,
50         'tm_min'   => 0,
51         'tm_hour'  => 0,
52         'tm_mday'  => 1,
53         'tm_mon'   => 0,
54         'tm_year'  => 0,
55         'tm_wday'  => 0,
56         'tm_yday'  => 0,
57         'unparsed' => $sDate,
58     );
59
60     while($sFormat != "")
61     {
62         // ===== Search a %x element, Check the static string before the %x =====
63         $nIdxFound = strpos($sFormat, '%');
64         if($nIdxFound === false)
65         {
66
67             // There is no more format. Check the last static string.
68             $aResult['unparsed'] = ($sFormat == $sDate) ? "" : $sDate;
69             break;
70         }
71
72         $sFormatBefore = mb_substr($sFormat, 0, $nIdxFound);
73         $sDateBefore   = mb_substr($sDate,   0, $nIdxFound);
74
75         if($sFormatBefore != $sDateBefore) break;
76
77         // ===== Read the value of the %x found =====
78         $sFormat = mb_substr($sFormat, $nIdxFound);
79         $sDate   = mb_substr($sDate,   $nIdxFound);
80
81         $aResult['unparsed'] = $sDate;
82
83         $sFormatCurrent = mb_substr($sFormat, 0, 2);
84         $sFormatAfter   = mb_substr($sFormat, 2);
85
86         $nValue = -1;
87         $sDateAfter = "";
88         switch($sFormatCurrent)
89         {
90             case '%S': // Seconds after the minute (0-59)
91
92                 sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter);
93
94                 if(($nValue < 0) || ($nValue > 59)) return false;
95
96                 $aResult['tm_sec']  = $nValue;
97                 break;
98
99             // ----------
100             case '%M': // Minutes after the hour (0-59)
101                 sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter);
102
103                 if(($nValue < 0) || ($nValue > 59)) return false;
104
105                 $aResult['tm_min']  = $nValue;
106                 break;
107
108             // ----------
109             case '%H': // Hour since midnight (0-23)
110                 sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter);
111
112                 if(($nValue < 0) || ($nValue > 23)) return false;
113
114                 $aResult['tm_hour']  = $nValue;
115                 break;
116
117             // ----------
118             case '%d': // Day of the month (1-31)
119                 sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter);
120
121                 if(($nValue < 1) || ($nValue > 31)) return false;
122
123                 $aResult['tm_mday']  = $nValue;
124                 break;
125
126             // ----------
127             case '%m': // Months since January (0-11)
128                 sscanf($sDate, "%2d%[^\\n]", $nValue, $sDateAfter);
129
130                 if(($nValue < 1) || ($nValue > 12)) return false;
131
132                 $aResult['tm_mon']  = ($nValue - 1);
133                 break;
134
135             // ----------
136             case '%Y': // Years since 1900
137                 sscanf($sDate, "%4d%[^\\n]", $nValue, $sDateAfter);
138
139                 if($nValue < 1900) return false;
140
141                 $aResult['tm_year']  = ($nValue - 1900);
142                 break;
143
144             // ----------
145             default:
146               //sscanf($sDate, "%s%[^\\n]", $skip, $sDateAfter);
147               preg_match('/^(.+)(\s|$)/uU', $sDate, $matches);
148               if (isset($matches[1])) {
149                 $sDateAfter = mb_substr($sDate, mb_strlen($matches[1]));
150               } else {
151                 $sDateAfter = '';
152               }
153               //break 2; // Break Switch and while
154               break;
155         }
156
157         // ===== Next please =====
158         $sFormat = $sFormatAfter;
159         $sDate   = $sDateAfter;
160
161         $aResult['unparsed'] = $sDate;
162
163     } // END while($sFormat != "")
164
165
166     // ===== Create the other value of the result array =====
167     $nParsedDateTimestamp = mktime($aResult['tm_hour'], $aResult['tm_min'], $aResult['tm_sec'],
168                             $aResult['tm_mon'] + 1, $aResult['tm_mday'], $aResult['tm_year'] + 1900);
169
170     // Before PHP 5.1 return -1 when error
171     if(($nParsedDateTimestamp === false)
172     ||($nParsedDateTimestamp === -1)) return false;
173
174     $aResult['tm_wday'] = (int) strftime("%w", $nParsedDateTimestamp); // Days since Sunday (0-6)
175     $aResult['tm_yday'] = (strftime("%j", $nParsedDateTimestamp) - 1); // Days since January 1 (0-365)
176
177     return $aResult;
178 } // END of function
179
180 class DateAndTime {
181   var $mHour = 0;
182   var $mMinute = 0;
183   var $mSecond = 0;
184   var $mMonth;
185   var $mDay;   // day of week
186   var $mDate;  // day of month
187   var $mYear;
188   var $mIntrFormat = "%d.%m.%Y %H:%M:%S"; //29.02.2004 16:21:42 internal format date
189   var $mLocalFormat;
190   var $mParseResult = 0;
191   var $mAutoComplete = true;
192
193   /**
194    * Constructor
195    *
196    * @param String $format
197    * @param String $strfDateTime
198    * @return DateAndTime
199    */
200   function __construct($format="",$strfDateTime="") {
201     $this->mLocalFormat = ($format ? $format : $this->mIntrFormat);
202     $d = ($strfDateTime ? $strfDateTime : $this->do_strftime($this->mLocalFormat));
203     $this->parseVal($d);
204   }
205
206   function setFormat($format) {
207     $this->mLocalFormat = $format;
208   }
209
210   function getFormat() {
211     return $this->mLocalFormat;
212   }
213
214   //01 to 31
215   function getDate() { return $this->mDate; }
216
217   //0 (for Sunday) through 6 (for Saturday)
218   function getDay() { return $this->mDay; }
219
220   //01 through 12
221   function getMonth() { return $this->mMonth; }
222
223   //1999 or 2003
224   function getYear() { return $this->mYear; }
225
226   function setDate($value) { $this->mDate = $value; }
227   function setMonth($value) { $this->mMonth = $value; }
228   function setYear($value) { $this->mYear = $value; }
229
230   function setTimestamp($ts) {
231     $this->mDate = date("d",$ts);
232     $this->mDay = date("w",$ts);
233     $this->mMonth = date("m",$ts);
234     $this->mYear = date("Y",$ts);
235     $this->mHour = date("H",$ts);
236     $this->mMinute = date("i",$ts);
237     $this->mSecond = date("s",$ts);
238   }
239
240   /**
241    * Return UNIX timestamp
242    */
243   function getTimestamp() {
244     return @mktime($this->mHour, $this->mMinute, $this->mSecond, $this->mMonth, $this->mDate, $this->mYear);
245   }
246
247   function compare($datetime) {
248     $ts1 = $this->getTimestamp();
249     $ts2 = $datetime->getTimestamp();
250     if ($ts1<$ts2) return -1;
251     if ($ts1==$ts2) return 0;
252     if ($ts1>$ts2) return 1;
253   }
254
255   function toString($format="") {
256     if ($this->mParseResult==0) {
257       if ($format) {
258         return $this->do_strftime($format, $this->getTimestamp());
259       } else {
260         return $this->do_strftime($this->mLocalFormat, $this->getTimestamp());
261       }
262     } else {
263       if ($format) {
264         return $this->do_strftime($format);
265       } else {
266         return $this->do_strftime($this->mLocalFormat);
267       }
268     }
269   }
270
271   function parseVal($szDate, $format="") {
272     $useformat = ($format ? $format : $this->mLocalFormat);
273     $res = my_strptime($szDate, $useformat);
274     if ($res !== false) {
275       $this->mDate = $res['tm_mday'];
276       $this->mDay = $res['tm_wday'];
277       $this->mMonth = $res['tm_mon'] + 1; // tm_mon - Months since January (0-11)
278       $this->mYear = 1900 + $res['tm_year']; // tm_year - Years since 1900
279       $this->mHour = $res['tm_hour'];
280       $this->mMinute = $res['tm_min'];
281       $this->mSecond = $res['tm_sec'];
282       $this->mParseResult = 0;
283     } elseif ($this->mAutoComplete) {
284       $this->setTimestamp(time());
285       $this->mParseResult = 1;
286     }
287   }
288
289   function isError() {
290     if ($this->mParseResult != 0) return true;
291     return false;
292   }
293
294   function before(/*DateAndTime*/ $obj) {
295     if ($this->getTimestamp() < $obj->getTimestamp()) return true;
296     return false;
297   }
298
299   function after(/*DateAndTime*/ $obj) {
300     if ($this->getTimestamp() > $obj->getTimestamp()) return true;
301     return false;
302   }
303
304   function equals(/*DateAndTime*/ $obj) {
305     if ($this->getTimestamp() == $obj->getTimestamp()) return true;
306     return false;
307   }
308
309   function decDay(/*int*/$days=1) {
310     $this->setTimestamp(@mktime($this->mHour, $this->mMinute, $this->mSecond, $this->mMonth, $this->mDate - $days, $this->mYear));
311   }
312
313   function incDay(/*int*/$days=1) {
314     $this->setTimestamp(@mktime($this->mHour, $this->mMinute, $this->mSecond, $this->mMonth, $this->mDate + $days, $this->mYear));
315   }
316
317   /**
318    * @param $format string Datetime format string
319    * @return string Preprocessed string with all locale-depended format
320    *                characters replaced by localized i18n strings.
321    */
322   function preprocessFormatString($format) {
323     global $i18n;
324
325     // replace locale-dependent strings
326     $format = str_replace('%a', mb_substr($i18n->getWeekDayName($this->mDay), 0, 3, 'utf-8'), $format);
327     $format = str_replace('%A', $i18n->getWeekDayName($this->mDay), $format);
328     $abbrev_month = mb_substr($i18n->monthNames[$this->mMonth], 0, 3, 'utf-8');
329     $format = str_replace('%b', $abbrev_month, $format);
330     $format = str_replace('%h', $abbrev_month, $format);
331     $format = str_replace('%z', date('O'), $format);
332     $format = str_replace('%Z', date('O'), $format); // format as 'O' for consistency with JS strftime
333     if (strpos($format, '%c') !== false) {
334       $format = str_replace('%c', $this->preprocessFormatString('%a %d %b %Y %T %Z'), $format);
335     }
336     return $format;
337   }
338
339   function do_strftime($format, $timestamp = null)
340   {
341     if (!is_null($timestamp)) {
342       return strftime($this->preprocessFormatString($format), $timestamp);
343     } else {
344       return strftime($this->preprocessFormatString($format));
345     }
346   }
347 }