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