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