Merged latest changes from stable branch.
[fa-stable.git] / includes / date_functions.inc
1 <?php
2 /**********************************************************************
3     Copyright (C) FrontAccounting, LLC.
4         Released under the terms of the GNU General Public License, GPL, 
5         as published by the Free Software Foundation, either version 3 
6         of the License, or (at your option) any later version.
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
10     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
12 /*
13 date validation and parsing functions
14
15 These functions refer to the global variable defining the date format
16 The date format is defined in config.php called dateformats
17 this can be a string either "d/m/Y" for UK/Australia/New Zealand dates or
18 "m/d/Y" for US/Canada format dates depending on setting in preferences.
19
20 */
21
22 function __date($year, $month, $day)
23 {
24         global $dateseps, $tmonths;
25         
26         $how = user_date_format();
27         $sep = $dateseps[user_date_sep()];
28         $day = (int)$day;
29         $month = (int)$month;
30         if ($how < 3)
31         {
32                 if ($day < 10)
33                         $day = "0".$day;
34                 if ($month < 10)
35                         $month = "0".$month;
36         }               
37         if ($how == 0)
38                 return $month.$sep.$day.$sep.$year;
39         elseif ($how == 1)
40                 return $day.$sep.$month.$sep.$year;
41         elseif ($how == 2)
42                 return $year.$sep.$month.$sep.$day;
43         elseif ($how == 3)
44                 return $tmonths[$month].$sep.$day.$sep.$year;
45         elseif ($how == 4)
46                 return $day.$sep.$tmonths[$month].$sep.$year;
47         else
48                 return $year.$sep.$tmonths[$month].$sep.$day;
49 }
50
51 function is_date($date_) 
52 {
53         global $dateseps;
54
55         if ($date_ == null || $date_ == "")
56                 return 0;
57         $how = user_date_format();
58         $sep = $dateseps[user_date_sep()];
59
60         $date_ = trim($date_);
61         $date = str_replace($sep, "", $date_);
62         
63         if ($how > 2)
64         {
65                 global $tmonths;
66                 $dd = explode($sep, $date_);
67                 if ($how == 3)
68                 {
69                         $day = $dd[1];
70                         $month = array_search($dd[0], $tmonths);
71                         $year = $dd[2];
72                 } 
73                 elseif ($how == 4)
74                 {
75                         $day = $dd[0];
76                         $month = array_search($dd[1], $tmonths);
77                         $year = $dd[2];
78                 } 
79                 else
80                 {
81                         $day = $dd[2];
82                         $month = array_search($dd[1], $tmonths);
83                         $year = $dd[0];
84                 }
85                 if ($year < 1000)
86                         return 0;
87         }
88         elseif (strlen($date) == 6)
89         {
90                 if ($how == 0)
91                 {
92                         $day = substr($date,2,2);
93                         $month = substr($date,0,2);
94                         $year = substr($date,4,2);
95                 } 
96                 elseif ($how == 1)
97                 {
98                         $day = substr($date,0,2);
99                         $month = substr($date,2,2);
100                         $year = substr($date,4,2);
101                 } 
102                 else
103                 {
104                         $day = substr($date,4,2);
105                         $month = substr($date,2,2);
106                         $year = substr($date,0,2);
107                 }
108         }
109         elseif (strlen($date) == 8)
110         {
111                 if ($how == 0)
112                 {
113                         $day = substr($date,2,2);
114                         $month = substr($date,0,2);
115                         $year = substr($date,4,4);
116                 } 
117                 elseif ($how == 1)
118                 {
119                         $day = substr($date,0,2);
120                         $month = substr($date,2,2);
121                         $year = substr($date,4,4);
122                 } 
123                 else
124                 {
125                         $day = substr($date,6,2);
126                         $month = substr($date,4,2);
127                         $year = substr($date,0,4);
128                 }
129         }
130         if (!isset($year)|| (int)$year > 9999) 
131         {
132                 return 0;
133         }
134
135         if (is_long((int)$day) && is_long((int)$month) && is_long((int)$year))
136         {
137                 global $date_system;
138                 if ($date_system == 1)
139                         list($year, $month, $day) = jalali_to_gregorian($year, $month, $day);  
140                 elseif ($date_system == 2)      
141                         list($year, $month, $day) = islamic_to_gregorian($year, $month, $day);  
142                 if (checkdate((int)$month, (int)$day, (int)$year))
143                 {
144                         return 1;
145                 }
146                 else
147                 {
148                         return 0;
149                 }
150         }
151         else
152         { /*Can't be in an appropriate DefaultDateFormat */
153                 return 0;
154         }
155 } //end of is_date function
156
157 function Today() 
158 {
159         global $date_system;
160
161         $year = date("Y");
162         $month = date("n");
163         $day = date("j");
164         if ($date_system == 1)
165                 list($year, $month, $day) = gregorian_to_jalali($year, $month, $day);
166         elseif ($date_system == 2)      
167                 list($year, $month, $day) = gregorian_to_islamic($year, $month, $day);
168         return __date($year, $month, $day);     
169 }
170
171 function Now() 
172 {
173         if (user_date_format() == 0)
174                 return date("h:i a");
175         else
176                 return date("H:i");
177 }
178 //
179 //      Retrieve and optionaly set default date for new document.
180 //
181 function new_doc_date($date=null)
182 {
183         if (isset($date) && $date != '')
184                 $_SESSION['_default_date'] = $date;
185
186         if (!isset($_SESSION['_default_date']) || !sticky_doc_date())
187                 $_SESSION['_default_date'] = Today();
188
189         return $_SESSION['_default_date'];
190 }
191
192 function is_date_in_fiscalyear($date, $convert=false)
193 {
194         global $path_to_root;
195         include_once($path_to_root . "/admin/db/fiscalyears_db.inc");
196
197         if ($convert)
198                 $date2 = sql2date($date);
199         else
200                 $date2 = $date;
201
202         if (user_check_access('SA_MULTIFISCALYEARS')) // allow all open years for this one
203                 return is_date_in_fiscalyears($date2, false);
204
205         if (is_date_closed($date2))
206                 return 0;
207         $myrow = get_current_fiscalyear();
208         $begin = sql2date($myrow['begin']);
209         $end = sql2date($myrow['end']);
210         if (date1_greater_date2($begin, $date2) || date1_greater_date2($date2, $end))
211         {
212                 return 0;
213         }
214         return 1;
215 }
216
217 function is_date_closed($date)
218 {
219         return !date1_greater_date2($date, sql2date(get_company_pref('gl_closing_date')));
220 }
221
222 function begin_fiscalyear()
223 {
224         global $path_to_root;
225         include_once($path_to_root . "/admin/db/fiscalyears_db.inc");
226
227         $myrow = get_current_fiscalyear();
228         return sql2date($myrow['begin']);
229 }
230
231 function end_fiscalyear()
232 {
233         global $path_to_root;
234         include_once($path_to_root . "/admin/db/fiscalyears_db.inc");
235
236         $myrow = get_current_fiscalyear();
237         return sql2date($myrow['end']);
238 }
239
240 function begin_month($date)
241 {
242         global $date_system;
243     list($day, $month, $year) = explode_date_to_dmy($date);
244     if ($date_system == 1)
245         list($year, $month, $day) = gregorian_to_jalali($year, $month, $day);
246     elseif ($date_system == 2)  
247         list($year, $month, $day) = gregorian_to_islamic($year, $month, $day);
248         return __date($year, $month, 1);
249 }
250
251 function days_in_month($month, $year)
252 {
253         global $date_system;
254
255         if ($date_system == 1)
256         {
257                 $days_in_month = array(31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, ((((((($year - (($year > 0) ? 474 : 473)) % 2820) + 474) + 38) * 682) % 2816) < 682 ? 30 : 29));
258         }
259         elseif ($date_system == 2)
260         {
261                 $days_in_month = array(30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, (((((11 * $year) + 14) % 30) < 11) ? 30 : 29));
262         }
263         else // gregorian date
264                 $days_in_month = array(31, ((!($year % 4 ) && (($year % 100) || !($year % 400)))?29:28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
265
266         return $days_in_month[$month - 1];
267 }
268
269 function end_month($date)
270 {
271         global $date_system;
272
273     list($day, $month, $year) = explode_date_to_dmy($date);
274         if ($date_system == 1)
275         {
276                 list($year, $month, $day) = gregorian_to_jalali($year, $month, $day);
277         }
278         elseif ($date_system == 2)
279         {
280                 list($year, $month, $day) = gregorian_to_islamic($year, $month, $day);
281         }
282
283         return __date($year, $month, days_in_month($month, $year));
284 }
285
286 function add_days($date, $days) // accepts negative values as well
287 {
288         global $date_system;
289     list($day, $month, $year) = explode_date_to_dmy($date);
290         $timet = mktime(0,0,0, $month, $day + $days, $year);
291     if ($date_system == 1 || $date_system == 2)
292     {
293         if ($date_system == 1)
294                 list($year, $month, $day) = gregorian_to_jalali(date("Y", $timet), date("n", $timet), date("j", $timet));
295         elseif ($date_system == 2)      
296                 list($year, $month, $day) = gregorian_to_islamic(date("Y", $timet), date("n", $timet), date("j", $timet));
297         return __date($year, $month, $day);
298     }
299     list($year, $month, $day) = explode("-", date("Y-m-d", $timet));
300         return __date($year, $month, $day);
301 }
302
303 function add_months($date, $months) // accepts negative values as well
304 {
305         global $date_system;
306     list($day, $month, $year) = explode_date_to_dmy($date);
307
308         $months += $year*12+$month;
309         $month = ($months-1)%12+1;
310         $year = ($months-$month)/12;
311
312         $timet = mktime(0,0,0, $month, min($day, days_in_month($month, $year)), $year);
313
314     if ($date_system == 1 || $date_system == 2)
315     {
316         if ($date_system == 1)
317                 list($year, $month, $day) = gregorian_to_jalali(date("Y", $timet), date("n", $timet), date("j", $timet));
318         elseif ($date_system == 2)      
319                 list($year, $month, $day) = gregorian_to_islamic(date("Y", $timet), date("n", $timet), date("j", $timet));
320         return __date($year, $month, $day);
321     }
322     list($year, $month, $day) = explode("-", date("Y-m-d", $timet));
323         return __date($year, $month, $day);
324 }
325
326 function add_years($date, $years) // accepts negative values as well
327 {
328         global $date_system;
329     list($day, $month, $year) = explode_date_to_dmy($date);
330         $timet = Mktime(0,0,0, $month, $day, $year + $years);
331     if ($date_system == 1 || $date_system == 2)
332     {
333         if ($date_system == 1)
334                 list($year, $month, $day) = gregorian_to_jalali(date("Y", $timet), date("n", $timet), date("j", $timet));
335         elseif ($date_system == 2)      
336                 list($year, $month, $day) = gregorian_to_islamic(date("Y", $timet), date("n", $timet), date("j", $timet));
337         return __date($year, $month, $day);
338     }
339     list($year, $month, $day) = explode("-", date("Y-m-d", $timet));
340         return __date($year, $month, $day);
341 }
342
343 //_______________________________________________________________
344
345 function sql2date($date_) 
346 {
347         global $date_system;
348
349         //for MySQL dates are in the format YYYY-mm-dd
350         if ($date_ == null || strlen($date_) == 0)
351                 return "";
352
353         if (strpos($date_, "/")) 
354         { // In MySQL it could be either / or -
355                 list($year, $month, $day) = explode("/", $date_);
356         } 
357         elseif (strpos ($date_, "-")) 
358         {
359                 list($year, $month, $day) = explode("-", $date_);
360         }
361
362         if (strlen($day) > 4) 
363         {  /*chop off the time stuff */
364                 $day = substr($day, 0, 2);
365         }
366         if ($date_system == 1)
367                 list($year, $month, $day) = gregorian_to_jalali($year, $month, $day);
368         elseif ($date_system == 2)
369                 list($year, $month, $day) = gregorian_to_islamic($year, $month, $day);
370         return __date($year, $month, $day);     
371 } // end function sql2date
372
373
374 function date2sql($date_)
375 {
376         global $dateseps, $date_system, $tmonths;
377 /* takes a date in a the format specified in $DefaultDateFormat
378 and converts to a yyyy/mm/dd format */
379
380         $how = user_date_format();
381         $sep = $dateseps[user_date_sep()];
382
383         if ($date_ == null || strlen($date_) == 0)
384                 return "";
385
386         $date_ = trim($date_);
387     $year = $month = $day = 0;
388     // Split up the date by the separator based on "how" to split it
389     if ($how == 0 || $how == 3) // MMDDYYYY or MmmDDYYYY
390         list($month, $day, $year) = explode($sep, $date_);
391     elseif ($how == 1 || $how == 4) // DDMMYYYY or DDMmYYYY
392         list($day, $month, $year) = explode($sep, $date_);
393     else // $how == 2 || $how == 5, YYYYMMDD or YYYYMmmDD
394         list($year, $month, $day) = explode($sep, $date_);
395         if ($how > 2)
396         {
397                 global $tmonths;
398                 $month = array_search($month, $tmonths);
399         }       
400 //to modify assumption in 2030
401         if ($date_system == 0 || $date_system == 3)
402         {
403                 if ((int)$year < 60)
404                 {
405                         $year = "20".$year;
406                 } 
407                 elseif ((int)$year > 59 && (int)$year < 100)
408                 {
409                         $year = "19".$year;
410                 }
411         }       
412         if ((int)$year > 9999)
413         {
414                 return 0;
415         }
416         if ($date_system == 1)
417                 list($year, $month, $day) = jalali_to_gregorian($year, $month, $day); 
418         elseif ($date_system == 2)
419                 list($year, $month, $day) = islamic_to_gregorian($year, $month, $day); 
420
421         return sprintf("%04d-%02d-%02d", $year, $month, $day);
422 }// end of function
423
424 function date1_greater_date2 ($date1, $date2) 
425 {
426
427 /* returns 1 true if date1 is greater than date_ 2 */
428
429         $date1 = date2sql($date1);
430         $date2 = date2sql($date2);
431
432         @list($year1, $month1, $day1) = explode("-", $date1);
433         @list($year2, $month2, $day2) = explode("-", $date2);
434
435         if ($year1 > $year2)
436         {
437                 return 1;
438         }
439         elseif ($year1 == $year2)
440         {
441                 if ($month1 > $month2)
442                 {
443                         return 1;
444                 }
445                 elseif ($month1 == $month2)
446                 {
447                         if ($day1 > $day2)
448                         {
449                                 return 1;
450                         }
451                 }
452         }
453         return 0;
454 }
455
456
457 function date_diff2 ($date1, $date2, $period) 
458 {
459
460 /* expects dates in the format specified in $DefaultDateFormat - period can be one of 'd','w','y','m'
461 months are assumed to be 30 days and years 365.25 days This only works
462 provided that both dates are after 1970. Also only works for dates up to the year 2035 ish */
463
464         $date1 = date2sql($date1);
465         $date2 = date2sql($date2);
466         list($year1, $month1, $day1) = explode("-", $date1);
467         list($year2, $month2, $day2) = explode("-", $date2);
468
469         $stamp1 = mktime(0,0,0, (int)$month1, (int)$day1, (int)$year1);
470         $stamp2 = mktime(0,0,0, (int)$month2, (int)$day2, (int)$year2);
471         $difference = $stamp1 - $stamp2;
472
473 /* difference is the number of seconds between each date negative if date_ 2 > date_ 1 */
474
475         switch ($period) 
476         {
477                 case "d":
478                         return (int)($difference / (24 * 60 * 60));
479                 case "w":
480                         return (int)($difference / (24 * 60 * 60 * 7));
481                 case "m":
482                         return (int)($difference / (24 * 60 * 60 * 30));
483                 case "s":
484                         return $difference;
485                 case "y":
486                         return (int)($difference / (24 * 60 * 60 * 365.25));
487                 default:
488                         Return 0;
489         }
490 }
491
492 function explode_date_to_dmy($date_)
493 {
494         $date = date2sql($date_);
495         if ($date == "") 
496         {
497                 return array(0,0,0);
498         }
499         list($year, $month, $day) = explode("-", $date);
500         return array($day, $month, $year);
501 }
502
503 function div($a, $b) 
504 {
505     return (int) ($a / $b);
506 }
507 /* Based on convertor to and from Gregorian and Jalali calendars.
508    Copyright (C) 2000  Roozbeh Pournader and Mohammad Toossi 
509    Released under GNU General Public License */
510
511 function gregorian_to_jalali ($g_y, $g_m, $g_d)
512 {
513     $g_days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
514     $j_days_in_month = array(31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29);
515
516         $gy = $g_y - 1600;
517         $gm = $g_m - 1;
518         $gd = $g_d - 1;
519
520         $g_day_no = 365 * $gy + div($gy + 3, 4) - div($gy + 99, 100) + div($gy + 399, 400);
521
522         for ($i = 0; $i < $gm; ++$i)
523         $g_day_no += $g_days_in_month[$i];
524         if ($gm > 1 && (($gy % 4 == 0 && $gy % 100 != 0) || ($gy % 400 == 0)))
525         /* leap and after Feb */
526         $g_day_no++;
527         $g_day_no += $gd;
528         $j_day_no = $g_day_no - 79;
529
530         $j_np = div($j_day_no, 12053); /* 12053 = 365*33 + 32/4 */
531         $j_day_no %= 12053;
532
533         $jy = 979 + 33 * $j_np + 4 * div($j_day_no, 1461); /* 1461 = 365*4 + 4/4 */
534
535         $j_day_no %= 1461;
536
537         if ($j_day_no >= 366) 
538         {
539         $jy += div($j_day_no - 1, 365);
540         $j_day_no = ($j_day_no - 1) % 365;
541         }
542
543         for ($i = 0; $i < 11 && $j_day_no >= $j_days_in_month[$i]; ++$i)
544         $j_day_no -= $j_days_in_month[$i];
545         $jm = $i + 1;
546         $jd = $j_day_no + 1;
547
548         return array($jy, $jm, $jd);
549 }
550
551 function jalali_to_gregorian($j_y, $j_m, $j_d)
552 {
553     $g_days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
554     $j_days_in_month = array(31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29);
555
556         $jy = $j_y - 979;
557         $jm = $j_m - 1;
558         $jd = $j_d - 1;
559
560         $j_day_no = 365 * $jy + div($jy, 33) * 8 + div($jy % 33 + 3, 4);
561         for ($i = 0; $i < $jm; ++$i)
562         $j_day_no += $j_days_in_month[$i];
563
564         $j_day_no += $jd;
565
566         $g_day_no = $j_day_no + 79;
567
568         $gy = 1600 + 400 * div($g_day_no, 146097); /* 146097 = 365*400 + 400/4 - 400/100 + 400/400 */
569         $g_day_no %= 146097;
570
571         $leap = true;
572         if ($g_day_no >= 36525) /* 36525 = 365*100 + 100/4 */
573         {
574         $g_day_no--;
575         $gy += 100 * div($g_day_no,  36524); /* 36524 = 365*100 + 100/4 - 100/100 */
576         $g_day_no %= 36524;
577
578         if ($g_day_no >= 365)
579                 $g_day_no++;
580         else
581                 $leap = false;
582         }
583
584         $gy += 4 * div($g_day_no, 1461); /* 1461 = 365*4 + 4/4 */
585         $g_day_no %= 1461;
586
587         if ($g_day_no >= 366) 
588         {
589         $leap = false;
590
591         $g_day_no--;
592         $gy += div($g_day_no, 365);
593         $g_day_no %= 365;
594         }
595
596         for ($i = 0; $g_day_no >= $g_days_in_month[$i] + ($i == 1 && $leap); $i++)
597         $g_day_no -= $g_days_in_month[$i] + ($i == 1 && $leap);
598         $gm = $i + 1;
599         $gd = $g_day_no + 1;
600
601         return array($gy, $gm, $gd);
602 }
603 /* Based on Hidri Date Script 
604    Released under GNU General Public License */
605 function gregorian_to_islamic($g_y, $g_m, $g_d)
606 {
607         $y = $g_y;   
608         $m = $g_m;
609         $d = $g_d;
610         if (($y > 1582) || (($y == 1582) && ($m > 10)) || (($y == 1582) && 
611                 ($m == 10) && ($d > 14))) 
612         {
613                 $jd = (int)((1461 * ($y + 4800 + (int)(($m - 14) / 12)))/ 4) + 
614                         (int)((367 * ($m - 2 - 12 * ((int)(($m - 14) / 12)))) / 12) - 
615                         (int)((3 * ((int)(($y + 4900 + (int)(($m - 14) / 12)) / 100))) / 4) + $d - 32075;
616         } 
617         else 
618         {
619                 $jd = 367 * $y - (int)((7 * ($y + 5001 + (int)(($m - 9) / 7))) / 4) + 
620                         (int)((275 * $m) / 9) + $d + 1729777;
621         }
622         $l = $jd - 1948440 + 10632;
623         $n = (int)(($l - 1) / 10631);
624         $l = $l - 10631 * $n + 354;
625         $j = ((int)((10985 - $l) / 5316)) * ((int)((50 * $l) / 17719)) + 
626                 ((int)($l / 5670)) * ((int)((43 * $l) / 15238));
627         $l = $l - ((int)((30 - $j) / 15)) * ((int)((17719 * $j) / 50)) - 
628                 ((int)($j / 16)) * ((int)((15238 * $j) / 43)) + 29;
629         $m = (int)((24 * $l) / 709);
630         $d = $l - (int)((709 * $m) / 24);
631         $y = 30 * $n + $j - 30;
632         return array($y, $m, $d);
633 }
634
635 function islamic_to_gregorian($i_y, $i_m, $i_d)
636 {
637         $y = $i_y;   
638         $m = $i_m;
639         $d = $i_d;
640
641         $jd = (int)((11 * $y + 3) / 30) + 354 * $y + 30 * $m - (int)(($m - 1) / 2) + $d + 1948440 - 385;
642         if ($jd > 2299160)
643         {
644                 $l = $jd + 68569;
645                 $n = (int)((4 * $l) / 146097);
646                 $l = $l - (int)((146097 * $n + 3) / 4);
647                 $i = (int)((4000 * ($l + 1)) / 1461001);
648                 $l = $l - (int)((1461 * $i) / 4) + 31;
649                 $j = (int)((80 * $l) / 2447);
650                 $d = $l - (int)((2447 * $j) / 80);
651                 $l= (int)($j / 11);
652                 $m = $j + 2 - 12 * $l;
653                 $y = 100 * ($n - 49) + $i + $l;
654         } 
655         else 
656         {
657                 $j = $jd + 1402;
658                 $k = (int)(($j - 1) / 1461);
659                 $l = $j - 1461 * $k;
660                 $n = (int)(($l - 1) / 365) - (int)($l / 1461);
661                 $i = $l - 365 * $n + 30;
662                 $j = (int)((80 * $i) / 2447);
663                 $d = $i - (int)((2447 * $j) / 80);
664                 $i = (int)($j / 11);
665                 $m = $j + 2 - 12 * $i;
666                 $y = 4 * $k + $n + $i - 4716;
667         }
668         return array($y, $m, $d);
669 }
670 ?>