]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/Date/Calc.php
[ROUTES] Allow accept-header specification during router creation
[quix0rs-gnu-social.git] / extlib / Date / Calc.php
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
3
4 // {{{ Header
5
6 /**
7  * Calculates, manipulates and retrieves dates
8  *
9  * It does not rely on 32-bit system time stamps, so it works dates
10  * before 1970 and after 2038.
11  *
12  * PHP versions 4 and 5
13  *
14  * LICENSE:
15  *
16  * Copyright (c) 1999-2007 Monte Ohrt, Pierre-Alain Joye, Daniel Convissor,
17  * C.A. Woodcock
18  * All rights reserved.
19  *
20  * Redistribution and use in source and binary forms, with or without
21  * modification, are permitted under the terms of the BSD License.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  * @category   Date and Time
37  * @package    Date
38  * @author     Monte Ohrt <monte@ispi.net>
39  * @author     Pierre-Alain Joye <pajoye@php.net>
40  * @author     Daniel Convissor <danielc@php.net>
41  * @author     C.A. Woodcock <c01234@netcomuk.co.uk>
42  * @copyright  1999-2007 Monte Ohrt, Pierre-Alain Joye, Daniel Convissor, C.A. Woodcock
43  * @license    http://www.opensource.org/licenses/bsd-license.php
44  *             BSD License
45  * @version    CVS: $Id$
46  * @link       http://pear.php.net/package/Date
47  * @since      File available since Release 1.2
48  */
49
50
51 // }}}
52
53 /**
54  * PEAR
55  */
56 require_once 'PEAR.php';
57
58
59 // {{{ General constants:
60
61 if (!defined('DATE_CALC_BEGIN_WEEKDAY')) {
62     /**
63      * Defines what day starts the week
64      *
65      * Monday (1) is the international standard.
66      * Redefine this to 0 if you want weeks to begin on Sunday.
67      */
68     define('DATE_CALC_BEGIN_WEEKDAY', 1);
69 }
70
71 if (!defined('DATE_CALC_FORMAT')) {
72     /**
73      * The default value for each method's $format parameter
74      *
75      * The default is '%Y%m%d'.  To override this default, define
76      * this constant before including Calc.php.
77      *
78      * @since Constant available since Release 1.4.4
79      */
80     define('DATE_CALC_FORMAT', '%Y%m%d');
81 }
82
83
84 // {{{ Date precision constants (used in 'round()' and 'trunc()'):
85
86 define('DATE_PRECISION_YEAR', -2);
87 define('DATE_PRECISION_MONTH', -1);
88 define('DATE_PRECISION_DAY', 0);
89 define('DATE_PRECISION_HOUR', 1);
90 define('DATE_PRECISION_10MINUTES', 2);
91 define('DATE_PRECISION_MINUTE', 3);
92 define('DATE_PRECISION_10SECONDS', 4);
93 define('DATE_PRECISION_SECOND', 5);
94
95
96 // }}}
97 // {{{ Class: Date_Calc
98
99 /**
100  * Calculates, manipulates and retrieves dates
101  *
102  * It does not rely on 32-bit system time stamps, so it works dates
103  * before 1970 and after 2038.
104  *
105  * @category  Date and Time
106  * @package   Date
107  * @author    Monte Ohrt <monte@ispi.net>
108  * @author    Daniel Convissor <danielc@php.net>
109  * @author    C.A. Woodcock <c01234@netcomuk.co.uk>
110  * @copyright 1999-2007 Monte Ohrt, Pierre-Alain Joye, Daniel Convissor, C.A. Woodcock
111  * @license   http://www.opensource.org/licenses/bsd-license.php
112  *            BSD License
113  * @version   Release: 1.5.0a1
114  * @link      http://pear.php.net/package/Date
115  * @since     Class available since Release 1.2
116  */
117 class Date_Calc
118 {
119
120     // {{{ dateFormat()
121
122     /**
123      * Formats the date in the given format, much like
124      * {@link http://www.php.net/strftime strftime()}
125      *
126      * This function is used to alleviate the problem with 32-bit numbers for
127      * dates pre 1970 or post 2038, as strftime() has on most systems.
128      * Most of the formatting options are compatible.
129      *
130      * Formatting options:
131      *
132      *  - <b>%a</b> - abbreviated weekday name (Sun, Mon, Tue)
133      *  - <b>%A</b> - full weekday name (Sunday, Monday, Tuesday)
134      *  - <b>%b</b> - abbreviated month name (Jan, Feb, Mar)
135      *  - <b>%B</b> - full month name (January, February, March)
136      *  - <b>%d</b> - day of month (range 00 to 31)
137      *  - <b>%e</b> - day of month, single digit (range 0 to 31)
138      *  - <b>%E</b> - number of days since unspecified epoch (integer).
139      *                ('<b>%E</b>' is useful for passing a date in a URL as an
140      *                integer value.  Then simply use {@link Date_Calc::daysToDate()}
141      *                to convert back to a date.)
142      *  - <b>%j</b> - day of year (range 001 to 366)
143      *  - <b>%m</b> - month as decimal number (range 1 to 12)
144      *  - <b>%n</b> - newline character (\n)
145      *  - <b>%t</b> - tab character (\t)
146      *  - <b>%w</b> - weekday as decimal (0 = Sunday)
147      *  - <b>%U</b> - week number of current year, first sunday as first week
148      *  - <b>%y</b> - year as decimal (range 00 to 99)
149      *  - <b>%Y</b> - year as decimal including century (range 0000 to 9999)
150      *  - <b>%%</b> - literal '%'
151      *
152      * @param int $day the day of the month
153      * @param int $month the month
154      * @param int $year the year.  Use the complete year instead of the
155      *                        abbreviated version.  E.g. use 2005, not 05.
156      * @param string $format the format string
157      *
158      * @return   string     the date in the desired format
159      * @access   public
160      * @static
161      */
162     public function dateFormat($day, $month, $year, $format)
163     {
164         if (!Date_Calc::isValidDate($day, $month, $year)) {
165             $year = Date_Calc::dateNow('%Y');
166             $month = Date_Calc::dateNow('%m');
167             $day = Date_Calc::dateNow('%d');
168         }
169
170         $output = '';
171
172         for ($strpos = 0; $strpos < strlen($format); $strpos++) {
173             $char = substr($format, $strpos, 1);
174             if ($char == '%') {
175                 $nextchar = substr($format, $strpos + 1, 1);
176                 switch ($nextchar) {
177                     case 'a':
178                         $output .= Date_Calc::getWeekdayAbbrname($day, $month, $year);
179                         break;
180                     case 'A':
181                         $output .= Date_Calc::getWeekdayFullname($day, $month, $year);
182                         break;
183                     case 'b':
184                         $output .= Date_Calc::getMonthAbbrname($month);
185                         break;
186                     case 'B':
187                         $output .= Date_Calc::getMonthFullname($month);
188                         break;
189                     case 'd':
190                         $output .= sprintf('%02d', $day);
191                         break;
192                     case 'e':
193                         $output .= $day;
194                         break;
195                     case 'E':
196                         $output .= Date_Calc::dateToDays($day, $month, $year);
197                         break;
198                     case 'j':
199                         $output .= Date_Calc::dayOfYear($day, $month, $year);
200                         break;
201                     case 'm':
202                         $output .= sprintf('%02d', $month);
203                         break;
204                     case 'n':
205                         $output .= "\n";
206                         break;
207                     case 't':
208                         $output .= "\t";
209                         break;
210                     case 'w':
211                         $output .= Date_Calc::dayOfWeek($day, $month, $year);
212                         break;
213                     case 'U':
214                         $output .= Date_Calc::weekOfYear($day, $month, $year);
215                         break;
216                     case 'y':
217                         $output .= sprintf(
218                             '%0' .
219                             ($year < 0 ? '3' : '2') .
220                             'd',
221                             $year % 100
222                         );
223                         break;
224                     case "Y":
225                         $output .= sprintf(
226                             '%0' .
227                             ($year < 0 ? '5' : '4') .
228                             'd',
229                             $year
230                         );
231                         break;
232                     case '%':
233                         $output .= '%';
234                         break;
235                     default:
236                         $output .= $char . $nextchar;
237                 }
238                 $strpos++;
239             } else {
240                 $output .= $char;
241             }
242         }
243         return $output;
244     }
245
246
247     // }}}
248     // {{{ dateNow()
249
250     /**
251      * Returns the current local date
252      *
253      * NOTE: This function retrieves the local date using
254      * {@link http://www.php.net/strftime strftime()}, which may or may
255      * not be 32-bit safe on your system.
256      *
257      * @param string $format the string indicating how to format the output
258      *
259      * @return   string     the current date in the specified format
260      * @access   public
261      * @static
262      */
263     public function dateNow($format = DATE_CALC_FORMAT)
264     {
265         return strftime($format, time());
266     }
267
268
269     // }}}
270     // {{{ getYear()
271
272     /**
273      * Returns the current local year in format CCYY
274      *
275      * @return   string     the current year in four digit format
276      * @access   public
277      * @static
278      */
279     public function getYear()
280     {
281         return Date_Calc::dateNow('%Y');
282     }
283
284
285     // }}}
286     // {{{ getMonth()
287
288     /**
289      * Returns the current local month in format MM
290      *
291      * @return   string     the current month in two digit format
292      * @access   public
293      * @static
294      */
295     public function getMonth()
296     {
297         return Date_Calc::dateNow('%m');
298     }
299
300
301     // }}}
302     // {{{ getDay()
303
304     /**
305      * Returns the current local day in format DD
306      *
307      * @return   string     the current day of the month in two digit format
308      * @access   public
309      * @static
310      */
311     public function getDay()
312     {
313         return Date_Calc::dateNow('%d');
314     }
315
316
317     // }}}
318     // {{{ defaultCentury()
319
320     /**
321      * Turns a two digit year into a four digit year
322      *
323      * Return value depends on current year; the century chosen
324      * will be the one which forms the year that is closest
325      * to the current year.  If the two possibilities are
326      * equidistant to the current year (i.e. 50 years in the past
327      * and 50 years in the future), then the past year is chosen.
328      *
329      * For example, if the current year is 2007:
330      *  - <b>"03"</b> - returns 2003
331      *  - <b>"09"</b> - returns 2009
332      *  - <b>"56"</b> - returns 2056 (closer to 2007 than 1956)
333      *  - <b>"57"</b> - returns 1957 (1957 and 2007 are equidistant,
334      *                   so previous century chosen)
335      *  - <b>"58"</b> - returns 1958
336      *
337      * @param int $year the 2 digit year
338      *
339      * @return   int        the 4 digit year
340      * @access   public
341      * @static
342      */
343     public function defaultCentury($year)
344     {
345         $hn_century = intval(($hn_currentyear = date("Y")) / 100);
346         $hn_currentyear = $hn_currentyear % 100;
347
348         if ($year < 0 || $year >= 100) {
349             $year = $year % 100;
350         }
351
352         if ($year - $hn_currentyear < -50) {
353             return ($hn_century + 1) * 100 + $year;
354         } elseif ($year - $hn_currentyear < 50) {
355             return $hn_century * 100 + $year;
356         } else {
357             return ($hn_century - 1) * 100 + $year;
358         }
359     }
360
361
362     // }}}
363     // {{{ getSecondsInYear()
364
365     /**
366      * Returns the total number of seconds in the given year
367      *
368      * This takes into account leap seconds.
369      *
370      * @param int $pn_year the year in four digit format
371      *
372      * @return   int
373      * @access   public
374      * @static
375      * @since    Method available since Release 1.5.0
376      */
377     public function getSecondsInYear($pn_year)
378     {
379         $pn_year = intval($pn_year);
380
381         static $ha_leapseconds;
382         if (!isset($ha_leapseconds)) {
383             $ha_leapseconds = array(1972 => 2,
384                 1973 => 1,
385                 1974 => 1,
386                 1975 => 1,
387                 1976 => 1,
388                 1977 => 1,
389                 1978 => 1,
390                 1979 => 1,
391                 1981 => 1,
392                 1982 => 1,
393                 1983 => 1,
394                 1985 => 1,
395                 1987 => 1,
396                 1989 => 1,
397                 1990 => 1,
398                 1992 => 1,
399                 1993 => 1,
400                 1994 => 1,
401                 1995 => 1,
402                 1997 => 1,
403                 1998 => 1,
404                 2005 => 1);
405         }
406
407         $ret = Date_Calc::daysInYear($pn_year) * 86400;
408
409         if (isset($ha_leapseconds[$pn_year])) {
410             return $ret + $ha_leapseconds[$pn_year];
411         } else {
412             return $ret;
413         }
414     }
415
416
417     // }}}
418     // {{{ getSecondsInMonth()
419
420     /**
421      * Returns the total number of seconds in the given month
422      *
423      * This takes into account leap seconds.
424      *
425      * @param int $pn_month the month
426      * @param int $pn_year the year in four digit format
427      *
428      * @return   int
429      * @access   public
430      * @static
431      * @since    Method available since Release 1.5.0
432      */
433     public function getSecondsInMonth($pn_month, $pn_year)
434     {
435         $pn_month = intval($pn_month);
436         $pn_year = intval($pn_year);
437
438         static $ha_leapseconds;
439         if (!isset($ha_leapseconds)) {
440             $ha_leapseconds = array(1972 => array(6 => 1,
441                 12 => 1),
442                 1973 => array(12 => 1),
443                 1974 => array(12 => 1),
444                 1975 => array(12 => 1),
445                 1976 => array(12 => 1),
446                 1977 => array(12 => 1),
447                 1978 => array(12 => 1),
448                 1979 => array(12 => 1),
449                 1981 => array(6 => 1),
450                 1982 => array(6 => 1),
451                 1983 => array(6 => 1),
452                 1985 => array(6 => 1),
453                 1987 => array(12 => 1),
454                 1989 => array(12 => 1),
455                 1990 => array(12 => 1),
456                 1992 => array(6 => 1),
457                 1993 => array(6 => 1),
458                 1994 => array(6 => 1),
459                 1995 => array(12 => 1),
460                 1997 => array(6 => 1),
461                 1998 => array(12 => 1),
462                 2005 => array(12 => 1));
463         }
464
465         $ret = Date_Calc::daysInMonth($pn_month, $pn_year) * 86400;
466
467         if (isset($ha_leapseconds[$pn_year][$pn_month])) {
468             return $ret + $ha_leapseconds[$pn_year][$pn_month];
469         } else {
470             return $ret;
471         }
472     }
473
474
475     // }}}
476     // {{{ getSecondsInDay()
477
478     /**
479      * Returns the total number of seconds in the day of the given date
480      *
481      * This takes into account leap seconds.
482      *
483      * @param int $pn_day the day of the month
484      * @param int $pn_month the month
485      * @param int $pn_year the year in four digit format
486      *
487      * @return   int
488      * @access   public
489      * @static
490      * @since    Method available since Release 1.5.0
491      */
492     public function getSecondsInDay($pn_day, $pn_month, $pn_year)
493     {
494         // Note to developers:
495         //
496         // The leap seconds listed here are a matter of historical fact,
497         // that is, it is known on which exact day they occurred.
498         // However, the implementation of the class as a whole depends
499         // on the fact that they always occur at the end of the month
500         // (although it is assumed that they could occur in any month,
501         // even though practically they only occur in June or December).
502         //
503         // Do not define a leap second on a day of the month other than
504         // the last day without altering the implementation of the
505         // functions that depend on this one.
506         //
507         // It is possible, though, to define an un-leap second (i.e. a skipped
508         // second (I do not know what they are called), or a number of
509         // consecutive leap seconds).
510
511         $pn_day = intval($pn_day);
512         $pn_month = intval($pn_month);
513         $pn_year = intval($pn_year);
514
515         static $ha_leapseconds;
516         if (!isset($ha_leapseconds)) {
517             $ha_leapseconds = array(1972 => array(6 => array(30 => 1),
518                 12 => array(31 => 1)),
519                 1973 => array(12 => array(31 => 1)),
520                 1974 => array(12 => array(31 => 1)),
521                 1975 => array(12 => array(31 => 1)),
522                 1976 => array(12 => array(31 => 1)),
523                 1977 => array(12 => array(31 => 1)),
524                 1978 => array(12 => array(31 => 1)),
525                 1979 => array(12 => array(31 => 1)),
526                 1981 => array(6 => array(30 => 1)),
527                 1982 => array(6 => array(30 => 1)),
528                 1983 => array(6 => array(30 => 1)),
529                 1985 => array(6 => array(30 => 1)),
530                 1987 => array(12 => array(31 => 1)),
531                 1989 => array(12 => array(31 => 1)),
532                 1990 => array(12 => array(31 => 1)),
533                 1992 => array(6 => array(30 => 1)),
534                 1993 => array(6 => array(30 => 1)),
535                 1994 => array(6 => array(30 => 1)),
536                 1995 => array(12 => array(31 => 1)),
537                 1997 => array(6 => array(30 => 1)),
538                 1998 => array(12 => array(31 => 1)),
539                 2005 => array(12 => array(31 => 1)));
540         }
541
542         if (isset($ha_leapseconds[$pn_year][$pn_month][$pn_day])) {
543             return 86400 + $ha_leapseconds[$pn_year][$pn_month][$pn_day];
544         } else {
545             return 86400;
546         }
547     }
548
549
550     // }}}
551     // {{{ getSecondsInHour()
552
553     /**
554      * Returns the total number of seconds in the hour of the given date
555      *
556      * This takes into account leap seconds.
557      *
558      * @param int $pn_day the day of the month
559      * @param int $pn_month the month
560      * @param int $pn_year the year in four digit format
561      * @param int $pn_hour the hour
562      *
563      * @return   int
564      * @access   public
565      * @static
566      */
567     public function getSecondsInHour($pn_day, $pn_month, $pn_year, $pn_hour)
568     {
569         if ($pn_hour < 23) {
570             return 3600;
571         } else {
572             return Date_Calc::getSecondsInDay($pn_day, $pn_month, $pn_year) -
573                 82800;
574         }
575     }
576
577
578     // }}}
579     // {{{ getSecondsInMinute()
580
581     /**
582      * Returns the total number of seconds in the minute of the given hour
583      *
584      * This takes into account leap seconds.
585      *
586      * @param int $pn_day the day of the month
587      * @param int $pn_month the month
588      * @param int $pn_year the year in four digit format
589      * @param int $pn_hour the hour
590      * @param int $pn_minute the minute
591      *
592      * @return   int
593      * @access   public
594      * @static
595      * @since    Method available since Release 1.5.0
596      */
597     public function getSecondsInMinute(
598         $pn_day,
599         $pn_month,
600         $pn_year,
601         $pn_hour,
602         $pn_minute
603     )
604     {
605         if ($pn_hour < 23 || $pn_minute < 59) {
606             return 60;
607         } else {
608             return Date_Calc::getSecondsInDay($pn_day, $pn_month, $pn_year) -
609                 86340;
610         }
611     }
612
613
614     // }}}
615     // {{{ secondsPastMidnight()
616
617     /**
618      * Returns the no of seconds since midnight (0-86399)
619      *
620      * @param int $pn_hour the hour of the day
621      * @param int $pn_minute the minute
622      * @param mixed $pn_second the second as integer or float
623      *
624      * @return   mixed      integer or float from 0-86399
625      * @access   public
626      * @static
627      * @since    Method available since Release 1.5.0
628      */
629     public function secondsPastMidnight($pn_hour, $pn_minute, $pn_second)
630     {
631         return 3600 * $pn_hour + 60 * $pn_minute + $pn_second;
632     }
633
634
635     // }}}
636     // {{{ secondsPastMidnightToTime()
637
638     /**
639      * Returns the time as an array (i.e. hour, minute, second)
640      *
641      * @param mixed $pn_seconds the no of seconds since midnight (0-86399)
642      *
643      * @return   mixed      array of hour, minute (both as integers), second (as
644      *                       integer or float, depending on parameter)
645      * @access   public
646      * @static
647      * @since    Method available since Release 1.5.0
648      */
649     public function secondsPastMidnightToTime($pn_seconds)
650     {
651         if ($pn_seconds >= 86400) {
652             return array(23, 59, $pn_seconds - 86340);
653         }
654
655         $hn_hour = intval($pn_seconds / 3600);
656         $hn_minute = intval(($pn_seconds - $hn_hour * 3600) / 60);
657         $hn_second = is_float($pn_seconds) ?
658             fmod($pn_seconds, 60) :
659             $pn_seconds % 60;
660
661         return array($hn_hour, $hn_minute, $hn_second);
662     }
663
664
665     // }}}
666     // {{{ secondsPastTheHour()
667
668     /**
669      * Returns the no of seconds since the last hour o'clock (0-3599)
670      *
671      * @param int $pn_minute the minute
672      * @param mixed $pn_second the second as integer or float
673      *
674      * @return   mixed      integer or float from 0-3599
675      * @access   public
676      * @static
677      * @since    Method available since Release 1.5.0
678      */
679     public function secondsPastTheHour($pn_minute, $pn_second)
680     {
681         return 60 * $pn_minute + $pn_second;
682     }
683
684
685     // }}}
686     // {{{ addHours()
687
688     /**
689      * Returns the date the specified no of hours from the given date
690      *
691      * To subtract hours use a negative value for the '$pn_hours' parameter
692      *
693      * @param int $pn_hours hours to add
694      * @param int $pn_day the day of the month
695      * @param int $pn_month the month
696      * @param int $pn_year the year
697      * @param int $pn_hour the hour
698      *
699      * @return   array      array of year, month, day, hour
700      * @access   public
701      * @static
702      * @since    Method available since Release 1.5.0
703      */
704     public function addHours($pn_hours, $pn_day, $pn_month, $pn_year, $pn_hour)
705     {
706         if ($pn_hours == 0) {
707             return array((int)$pn_year,
708                 (int)$pn_month,
709                 (int)$pn_day,
710                 (int)$pn_hour);
711         }
712
713         $hn_days = intval($pn_hours / 24);
714         $hn_hour = $pn_hour + $pn_hours % 24;
715
716         if ($hn_hour >= 24) {
717             ++$hn_days;
718             $hn_hour -= 24;
719         } elseif ($hn_hour < 0) {
720             --$hn_days;
721             $hn_hour += 24;
722         }
723
724         if ($hn_days == 0) {
725             $hn_year = $pn_year;
726             $hn_month = $pn_month;
727             $hn_day = $pn_day;
728         } else {
729             list($hn_year, $hn_month, $hn_day) =
730                 explode(
731                     " ",
732                     Date_Calc::addDays(
733                         $hn_days,
734                         $pn_day,
735                         $pn_month,
736                         $pn_year,
737                         "%Y %m %d"
738                     )
739                 );
740         }
741
742         return array((int)$hn_year, (int)$hn_month, (int)$hn_day, $hn_hour);
743     }
744
745
746     // }}}
747     // {{{ addMinutes()
748
749     /**
750      * Returns the date the specified no of minutes from the given date
751      *
752      * To subtract minutes use a negative value for the '$pn_minutes' parameter
753      *
754      * @param int $pn_minutes minutes to add
755      * @param int $pn_day the day of the month
756      * @param int $pn_month the month
757      * @param int $pn_year the year
758      * @param int $pn_hour the hour
759      * @param int $pn_minute the minute
760      *
761      * @return   array      array of year, month, day, hour, minute
762      * @access   public
763      * @static
764      * @since    Method available since Release 1.5.0
765      */
766     public function addMinutes(
767         $pn_minutes,
768         $pn_day,
769         $pn_month,
770         $pn_year,
771         $pn_hour,
772         $pn_minute
773     )
774     {
775         if ($pn_minutes == 0) {
776             return array((int)$pn_year,
777                 (int)$pn_month,
778                 (int)$pn_day,
779                 (int)$pn_hour,
780                 (int)$pn_minute);
781         }
782
783         $hn_hours = intval($pn_minutes / 60);
784         $hn_minute = $pn_minute + $pn_minutes % 60;
785
786         if ($hn_minute >= 60) {
787             ++$hn_hours;
788             $hn_minute -= 60;
789         } elseif ($hn_minute < 0) {
790             --$hn_hours;
791             $hn_minute += 60;
792         }
793
794         if ($hn_hours == 0) {
795             $hn_year = $pn_year;
796             $hn_month = $pn_month;
797             $hn_day = $pn_day;
798             $hn_hour = $pn_hour;
799         } else {
800             list($hn_year, $hn_month, $hn_day, $hn_hour) =
801                 Date_Calc::addHours(
802                     $hn_hours,
803                     $pn_day,
804                     $pn_month,
805                     $pn_year,
806                     $pn_hour
807                 );
808         }
809
810         return array($hn_year, $hn_month, $hn_day, $hn_hour, $hn_minute);
811     }
812
813
814     // }}}
815     // {{{ addSeconds()
816
817     /**
818      * Returns the date the specified no of seconds from the given date
819      *
820      * If leap seconds are specified to be counted, the passed time must be UTC.
821      * To subtract seconds use a negative value for the '$pn_seconds' parameter.
822      *
823      * N.B. the return type of the second part of the date is float if
824      * either '$pn_seconds' or '$pn_second' is a float; otherwise, it
825      * is integer.
826      *
827      * @param mixed $pn_seconds seconds to add as integer or float
828      * @param int $pn_day the day of the month
829      * @param int $pn_month the month
830      * @param int $pn_year the year
831      * @param int $pn_hour the hour
832      * @param int $pn_minute the minute
833      * @param mixed $pn_second the second as integer or float
834      * @param bool $pb_countleap whether to count leap seconds (defaults to
835      *                             DATE_COUNT_LEAP_SECONDS)
836      *
837      * @return   array      array of year, month, day, hour, minute, second
838      * @access   public
839      * @static
840      * @since    Method available since Release 1.5.0
841      */
842     public function addSeconds(
843         $pn_seconds,
844         $pn_day,
845         $pn_month,
846         $pn_year,
847         $pn_hour,
848         $pn_minute,
849         $pn_second,
850         $pb_countleap = DATE_COUNT_LEAP_SECONDS
851     )
852     {
853         if ($pn_seconds == 0) {
854             return array((int)$pn_year,
855                 (int)$pn_month,
856                 (int)$pn_day,
857                 (int)$pn_hour,
858                 (int)$pn_minute,
859                 $pn_second);
860         }
861
862         if ($pb_countleap) {
863             $hn_seconds = $pn_seconds;
864
865             $hn_day = (int)$pn_day;
866             $hn_month = (int)$pn_month;
867             $hn_year = (int)$pn_year;
868             $hn_hour = (int)$pn_hour;
869             $hn_minute = (int)$pn_minute;
870             $hn_second = $pn_second;
871
872             $hn_days = Date_Calc::dateToDays(
873                 $pn_day,
874                 $pn_month,
875                 $pn_year
876             );
877             $hn_secondsofmonth = 86400 * ($hn_days -
878                     Date_Calc::firstDayOfMonth(
879                         $pn_month,
880                         $pn_year
881                     )) +
882                 Date_Calc::secondsPastMidnight(
883                     $pn_hour,
884                     $pn_minute,
885                     $pn_second
886                 );
887
888             if ($hn_seconds > 0) {
889                 // Advance to end of month:
890                 //
891                 if ($hn_secondsofmonth != 0 &&
892                     $hn_secondsofmonth + $hn_seconds >=
893                     ($hn_secondsinmonth =
894                         Date_Calc::getSecondsInMonth($hn_month, $hn_year))) {
895                     $hn_seconds -= $hn_secondsinmonth - $hn_secondsofmonth;
896                     $hn_secondsofmonth = 0;
897                     list($hn_year, $hn_month) =
898                         Date_Calc::nextMonth($hn_month, $hn_year);
899                     $hn_day = Date_Calc::getFirstDayOfMonth(
900                         $hn_month,
901                         $hn_year
902                     );
903                     $hn_hour = $hn_minute = $hn_second = 0;
904                 }
905
906                 // Advance to end of year:
907                 //
908                 if ($hn_secondsofmonth == 0 &&
909                     $hn_month != Date_Calc::getFirstMonthOfYear($hn_year)) {
910                     while ($hn_year == $pn_year &&
911                         $hn_seconds >= ($hn_secondsinmonth =
912                             Date_Calc::getSecondsInMonth(
913                                 $hn_month,
914                                 $hn_year
915                             ))) {
916                         $hn_seconds -= $hn_secondsinmonth;
917                         list($hn_year, $hn_month) =
918                             Date_Calc::nextMonth($hn_month, $hn_year);
919                         $hn_day = Date_Calc::getFirstDayOfMonth(
920                             $hn_month,
921                             $hn_year
922                         );
923                     }
924                 }
925
926                 if ($hn_secondsofmonth == 0) {
927                     // Add years:
928                     //
929                     if ($hn_month == Date_Calc::getFirstMonthOfYear($hn_year)) {
930                         while ($hn_seconds >= ($hn_secondsinyear =
931                                 Date_Calc::getSecondsInYear($hn_year))) {
932                             $hn_seconds -= $hn_secondsinyear;
933                             $hn_month = Date_Calc::getFirstMonthOfYear(++$hn_year);
934                             $hn_day = Date_Calc::getFirstDayOfMonth(
935                                 $hn_month,
936                                 $hn_year
937                             );
938                         }
939                     }
940
941                     // Add months:
942                     //
943                     while ($hn_seconds >= ($hn_secondsinmonth =
944                             Date_Calc::getSecondsInMonth($hn_month, $hn_year))) {
945                         $hn_seconds -= $hn_secondsinmonth;
946                         list($hn_year, $hn_month) =
947                             Date_Calc::nextMonth($hn_month, $hn_year);
948                         $hn_day = Date_Calc::getFirstDayOfMonth($hn_month, $hn_year);
949                     }
950                 }
951             } else {
952                 //
953                 // (if $hn_seconds < 0)
954
955                 // Go back to start of month:
956                 //
957                 if ($hn_secondsofmonth != 0 &&
958                     -$hn_seconds >= $hn_secondsofmonth) {
959                     $hn_seconds += $hn_secondsofmonth;
960                     $hn_secondsofmonth = 0;
961                     $hn_day = Date_Calc::getFirstDayOfMonth(
962                         $hn_month,
963                         $hn_year
964                     );
965                     $hn_hour = $hn_minute = $hn_second = 0;
966                 }
967
968                 // Go back to start of year:
969                 //
970                 if ($hn_secondsofmonth == 0) {
971                     while ($hn_month !=
972                         Date_Calc::getFirstMonthOfYear($hn_year)) {
973                         list($hn_year, $hn_prevmonth) =
974                             Date_Calc::prevMonth($hn_month, $hn_year);
975
976                         if (-$hn_seconds >= ($hn_secondsinmonth =
977                                 Date_Calc::getSecondsInMonth(
978                                     $hn_prevmonth,
979                                     $hn_year
980                                 ))) {
981                             $hn_seconds += $hn_secondsinmonth;
982                             $hn_month = $hn_prevmonth;
983                             $hn_day = Date_Calc::getFirstDayOfMonth(
984                                 $hn_month,
985                                 $hn_year
986                             );
987                         } else {
988                             break;
989                         }
990                     }
991                 }
992
993                 if ($hn_secondsofmonth == 0) {
994                     // Subtract years:
995                     //
996                     if ($hn_month == Date_Calc::getFirstMonthOfYear($hn_year)) {
997                         while (-$hn_seconds >= ($hn_secondsinyear =
998                                 Date_Calc::getSecondsInYear($hn_year - 1))) {
999                             $hn_seconds += $hn_secondsinyear;
1000                             $hn_month = Date_Calc::getFirstMonthOfYear(--$hn_year);
1001                             $hn_day = Date_Calc::getFirstDayOfMonth(
1002                                 $hn_month,
1003                                 $hn_year
1004                             );
1005                         }
1006                     }
1007
1008                     // Subtract months:
1009                     //
1010                     list($hn_pmyear, $hn_prevmonth) =
1011                         Date_Calc::prevMonth($hn_month, $hn_year);
1012                     while (-$hn_seconds >= ($hn_secondsinmonth =
1013                             Date_Calc::getSecondsInMonth(
1014                                 $hn_prevmonth,
1015                                 $hn_pmyear
1016                             ))) {
1017                         $hn_seconds += $hn_secondsinmonth;
1018                         $hn_year = $hn_pmyear;
1019                         $hn_month = $hn_prevmonth;
1020                         $hn_day = Date_Calc::getFirstDayOfMonth(
1021                             $hn_month,
1022                             $hn_year
1023                         );
1024                         list($hn_pmyear, $hn_prevmonth) =
1025                             Date_Calc::prevMonth($hn_month, $hn_year);
1026                     }
1027                 }
1028             }
1029
1030             if ($hn_seconds < 0 && $hn_secondsofmonth == 0) {
1031                 list($hn_year, $hn_month) =
1032                     Date_Calc::prevMonth($hn_month, $hn_year);
1033                 $hn_day = Date_Calc::getFirstDayOfMonth($hn_month, $hn_year);
1034                 $hn_seconds += Date_Calc::getSecondsInMonth($hn_month, $hn_year);
1035             }
1036
1037             $hn_seconds += Date_Calc::secondsPastMidnight(
1038                 $hn_hour,
1039                 $hn_minute,
1040                 $hn_second
1041             );
1042             if ($hn_seconds < 0) {
1043                 $hn_daysadd = intval($hn_seconds / 86400) - 1;
1044             } elseif ($hn_seconds < 86400) {
1045                 $hn_daysadd = 0;
1046             } else {
1047                 $hn_daysadd = intval($hn_seconds / 86400) - 1;
1048             }
1049
1050             if ($hn_daysadd != 0) {
1051                 list($hn_year, $hn_month, $hn_day) =
1052                     explode(
1053                         " ",
1054                         Date_Calc::addDays(
1055                             $hn_daysadd,
1056                             $hn_day,
1057                             $hn_month,
1058                             $hn_year,
1059                             "%Y %m %d"
1060                         )
1061                     );
1062                 $hn_seconds -= $hn_daysadd * 86400;
1063             }
1064
1065             $hn_secondsinday = Date_Calc::getSecondsInDay(
1066                 $hn_day,
1067                 $hn_month,
1068                 $hn_year
1069             );
1070             if ($hn_seconds >= $hn_secondsinday) {
1071                 list($hn_year, $hn_month, $hn_day) =
1072                     explode(
1073                         " ",
1074                         Date_Calc::addDays(
1075                             1,
1076                             $hn_day,
1077                             $hn_month,
1078                             $hn_year,
1079                             "%Y %m %d"
1080                         )
1081                     );
1082                 $hn_seconds -= $hn_secondsinday;
1083             }
1084
1085             list($hn_hour, $hn_minute, $hn_second) =
1086                 Date_Calc::secondsPastMidnightToTime($hn_seconds);
1087
1088             return array((int)$hn_year,
1089                 (int)$hn_month,
1090                 (int)$hn_day,
1091                 $hn_hour,
1092                 $hn_minute,
1093                 $hn_second);
1094         } else {
1095             // Assume every day has 86400 seconds exactly (ignore leap seconds):
1096             //
1097             $hn_minutes = intval($pn_seconds / 60);
1098
1099             if (is_float($pn_seconds)) {
1100                 $hn_second = $pn_second + fmod($pn_seconds, 60);
1101             } else {
1102                 $hn_second = $pn_second + $pn_seconds % 60;
1103             }
1104
1105             if ($hn_second >= 60) {
1106                 ++$hn_minutes;
1107                 $hn_second -= 60;
1108             } elseif ($hn_second < 0) {
1109                 --$hn_minutes;
1110                 $hn_second += 60;
1111             }
1112
1113             if ($hn_minutes == 0) {
1114                 $hn_year = $pn_year;
1115                 $hn_month = $pn_month;
1116                 $hn_day = $pn_day;
1117                 $hn_hour = $pn_hour;
1118                 $hn_minute = $pn_minute;
1119             } else {
1120                 list($hn_year, $hn_month, $hn_day, $hn_hour, $hn_minute) =
1121                     Date_Calc::addMinutes(
1122                         $hn_minutes,
1123                         $pn_day,
1124                         $pn_month,
1125                         $pn_year,
1126                         $pn_hour,
1127                         $pn_minute
1128                     );
1129             }
1130
1131             return array($hn_year,
1132                 $hn_month,
1133                 $hn_day,
1134                 $hn_hour,
1135                 $hn_minute,
1136                 $hn_second);
1137         }
1138     }
1139
1140
1141     // }}}
1142     // {{{ dateToDays()
1143
1144     /**
1145      * Converts a date in the proleptic Gregorian calendar to the no of days
1146      * since 24th November, 4714 B.C.
1147      *
1148      * Returns the no of days since Monday, 24th November, 4714 B.C. in the
1149      * proleptic Gregorian calendar (which is 24th November, -4713 using
1150      * 'Astronomical' year numbering, and 1st January, 4713 B.C. in the
1151      * proleptic Julian calendar).  This is also the first day of the 'Julian
1152      * Period' proposed by Joseph Scaliger in 1583, and the number of days
1153      * since this date is known as the 'Julian Day'.  (It is not directly
1154      * to do with the Julian calendar, although this is where the name
1155      * is derived from.)
1156      *
1157      * The algorithm is valid for all years (positive and negative), and
1158      * also for years preceding 4714 B.C.
1159      *
1160      * @param int $day the day of the month
1161      * @param int $month the month
1162      * @param int $year the year (using 'Astronomical' year numbering)
1163      *
1164      * @return   int        the number of days since 24th November, 4714 B.C.
1165      * @access   public
1166      * @static
1167      */
1168     public function dateToDays($day, $month, $year)
1169     {
1170         if ($month > 2) {
1171             // March = 0, April = 1, ..., December = 9,
1172             // January = 10, February = 11
1173             $month -= 3;
1174         } else {
1175             $month += 9;
1176             --$year;
1177         }
1178
1179         $hb_negativeyear = $year < 0;
1180         $century = intval($year / 100);
1181         $year = $year % 100;
1182
1183         if ($hb_negativeyear) {
1184             // Subtract 1 because year 0 is a leap year;
1185             // And N.B. that we must treat the leap years as occurring
1186             // one year earlier than they do, because for the purposes
1187             // of calculation, the year starts on 1st March:
1188             //
1189             return intval((14609700 * $century + ($year == 0 ? 1 : 0)) / 400) +
1190                 intval((1461 * $year + 1) / 4) +
1191                 intval((153 * $month + 2) / 5) +
1192                 $day + 1721118;
1193         } else {
1194             return intval(146097 * $century / 4) +
1195                 intval(1461 * $year / 4) +
1196                 intval((153 * $month + 2) / 5) +
1197                 $day + 1721119;
1198         }
1199     }
1200
1201
1202     // }}}
1203     // {{{ daysToDate()
1204
1205     /**
1206      * Converts no of days since 24th November, 4714 B.C. (in the proleptic
1207      * Gregorian calendar, which is year -4713 using 'Astronomical' year
1208      * numbering) to Gregorian calendar date
1209      *
1210      * Returned date belongs to the proleptic Gregorian calendar, using
1211      * 'Astronomical' year numbering.
1212      *
1213      * The algorithm is valid for all years (positive and negative), and
1214      * also for years preceding 4714 B.C. (i.e. for negative 'Julian Days'),
1215      * and so the only limitation is platform-dependent (for 32-bit systems
1216      * the maximum year would be something like about 1,465,190 A.D.).
1217      *
1218      * N.B. Monday, 24th November, 4714 B.C. is Julian Day '0'.
1219      *
1220      * @param int $days the number of days since 24th November, 4714 B.C.
1221      * @param string $format the string indicating how to format the output
1222      *
1223      * @return   string     the date in the desired format
1224      * @access   public
1225      * @static
1226      */
1227     public function daysToDate($days, $format = DATE_CALC_FORMAT)
1228     {
1229         $days = intval($days);
1230
1231         $days -= 1721119;
1232         $century = floor((4 * $days - 1) / 146097);
1233         $days = floor(4 * $days - 1 - 146097 * $century);
1234         $day = floor($days / 4);
1235
1236         $year = floor((4 * $day + 3) / 1461);
1237         $day = floor(4 * $day + 3 - 1461 * $year);
1238         $day = floor(($day + 4) / 4);
1239
1240         $month = floor((5 * $day - 3) / 153);
1241         $day = floor(5 * $day - 3 - 153 * $month);
1242         $day = floor(($day + 5) / 5);
1243
1244         $year = $century * 100 + $year;
1245         if ($month < 10) {
1246             $month += 3;
1247         } else {
1248             $month -= 9;
1249             ++$year;
1250         }
1251
1252         return Date_Calc::dateFormat($day, $month, $year, $format);
1253     }
1254
1255
1256     // }}}
1257     // {{{ getMonths()
1258
1259     /**
1260      * Returns array of the month numbers, in order, for the given year
1261      *
1262      * @param int $pn_year the year (using 'Astronomical' year numbering)
1263      *
1264      * @return   array      array of integer month numbers, in order
1265      * @access   public
1266      * @static
1267      * @since    Method available since Release 1.5.0
1268      */
1269     public function getMonths($pn_year)
1270     {
1271         // N.B. Month numbers can be skipped but not duplicated:
1272         //
1273         return array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
1274     }
1275
1276
1277     // }}}
1278     // {{{ getMonthNames()
1279
1280     /**
1281      * Returns an array of month names
1282      *
1283      * Used to take advantage of the setlocale function to return
1284      * language specific month names.
1285      *
1286      * TODO: cache values to some global array to avoid performance
1287      * hits when called more than once.
1288      *
1289      * @param int $pb_abbreviated whether to return the abbreviated form of the
1290      *                             months
1291      *
1292      * @return  array       associative array of integer month numbers, in
1293      *                       order, to month names
1294      * @access  public
1295      * @static
1296      */
1297     public function getMonthNames($pb_abbreviated = false)
1298     {
1299         $ret = array();
1300         foreach (Date_Calc::getMonths(2001) as $i) {
1301             $ret[$i] = strftime(
1302                 $pb_abbreviated ? '%b' : '%B',
1303                 mktime(0, 0, 0, $i, 1, 2001)
1304             );
1305         }
1306         return $ret;
1307     }
1308
1309
1310     // }}}
1311     // {{{ prevMonth()
1312
1313     /**
1314      * Returns month and year of previous month
1315      *
1316      * @param int $pn_month the month
1317      * @param int $pn_year the year (using 'Astronomical' year numbering)
1318      *
1319      * @return   array      array of year, month as integers
1320      * @access   public
1321      * @static
1322      * @since    Method available since Release 1.5.0
1323      */
1324     public function prevMonth($pn_month, $pn_year)
1325     {
1326         $ha_months = Date_Calc::getMonths($pn_year);
1327         $hn_monthkey = array_search($pn_month, $ha_months);
1328         if (array_key_exists($hn_monthkey - 1, $ha_months)) {
1329             return array((int)$pn_year, $ha_months[$hn_monthkey - 1]);
1330         } else {
1331             $ha_months = Date_Calc::getMonths($pn_year - 1);
1332             return array($pn_year - 1, end($ha_months));
1333         }
1334     }
1335
1336
1337     // }}}
1338     // {{{ nextMonth()
1339
1340     /**
1341      * Returns month and year of next month
1342      *
1343      * @param int $pn_month the month
1344      * @param int $pn_year the year (using 'Astronomical' year numbering)
1345      *
1346      * @return   array      array of year, month as integers
1347      * @access   public
1348      * @static
1349      * @since    Method available since Release 1.5.0
1350      */
1351     public function nextMonth($pn_month, $pn_year)
1352     {
1353         $ha_months = Date_Calc::getMonths($pn_year);
1354         $hn_monthkey = array_search($pn_month, $ha_months);
1355         if (array_key_exists($hn_monthkey + 1, $ha_months)) {
1356             return array((int)$pn_year, $ha_months[$hn_monthkey + 1]);
1357         } else {
1358             $ha_months = Date_Calc::getMonths($pn_year + 1);
1359             return array($pn_year + 1, $ha_months[0]);
1360         }
1361     }
1362
1363
1364     // }}}
1365     // {{{ addMonthsToDays()
1366
1367     /**
1368      * Returns 'Julian Day' of the date the specified no of months
1369      * from the given date
1370      *
1371      * To subtract months use a negative value for the '$pn_months'
1372      * parameter
1373      *
1374      * @param int $pn_months months to add
1375      * @param int $pn_days 'Julian Day', i.e. the no of days since 1st
1376      *                        January, 4713 B.C.
1377      *
1378      * @return   int        'Julian Day', i.e. the no of days since 1st January,
1379      *                       4713 B.C.
1380      * @access   public
1381      * @static
1382      * @since    Method available since Release 1.5.0
1383      */
1384     public function addMonthsToDays($pn_months, $pn_days)
1385     {
1386         if ($pn_months == 0) {
1387             return (int)$pn_days;
1388         }
1389
1390         list($hn_year, $hn_month, $hn_day) =
1391             explode(" ", Date_Calc::daysToDate($pn_days, "%Y %m %d"));
1392
1393         $hn_retmonth = $hn_month + $pn_months % 12;
1394         $hn_retyear = $hn_year + intval($pn_months / 12);
1395         if ($hn_retmonth < 1) {
1396             $hn_retmonth += 12;
1397             --$hn_retyear;
1398         } elseif ($hn_retmonth > 12) {
1399             $hn_retmonth -= 12;
1400             ++$hn_retyear;
1401         }
1402
1403         if (Date_Calc::isValidDate($hn_day, $hn_retmonth, $hn_retyear)) {
1404             return Date_Calc::dateToDays($hn_day, $hn_retmonth, $hn_retyear);
1405         }
1406
1407         // Calculate days since first of month:
1408         //
1409         $hn_dayoffset = $pn_days -
1410             Date_Calc::firstDayOfMonth($hn_month, $hn_year);
1411
1412         $hn_retmonthfirstday = Date_Calc::firstDayOfMonth(
1413             $hn_retmonth,
1414             $hn_retyear
1415         );
1416         $hn_retmonthlastday = Date_Calc::lastDayOfMonth(
1417             $hn_retmonth,
1418             $hn_retyear
1419         );
1420
1421         if ($hn_dayoffset > $hn_retmonthlastday - $hn_retmonthfirstday) {
1422             return $hn_retmonthlastday;
1423         } else {
1424             return $hn_retmonthfirstday + $hn_dayoffset;
1425         }
1426     }
1427
1428
1429     // }}}
1430     // {{{ addMonths()
1431
1432     /**
1433      * Returns the date the specified no of months from the given date
1434      *
1435      * To subtract months use a negative value for the '$pn_months'
1436      * parameter
1437      *
1438      * @param int $pn_months months to add
1439      * @param int $pn_day the day of the month, default is current local
1440      *                           day
1441      * @param int $pn_month the month, default is current local month
1442      * @param int $pn_year the year in four digit format, default is
1443      *                           current local year
1444      * @param string $ps_format string specifying how to format the output
1445      *
1446      * @return   string     the date in the desired format
1447      * @access   public
1448      * @static
1449      * @since    Method available since Release 1.5.0
1450      */
1451     public function addMonths(
1452         $pn_months,
1453         $pn_day,
1454         $pn_month,
1455         $pn_year,
1456         $ps_format = DATE_CALC_FORMAT
1457     )
1458     {
1459         if (is_null($pn_year)) {
1460             $pn_year = Date_Calc::dateNow('%Y');
1461         }
1462         if (empty($pn_month)) {
1463             $pn_month = Date_Calc::dateNow('%m');
1464         }
1465         if (empty($pn_day)) {
1466             $pn_day = Date_Calc::dateNow('%d');
1467         }
1468
1469         if ($pn_months == 0) {
1470             return Date_Calc::dateFormat(
1471                 $pn_day,
1472                 $pn_month,
1473                 $pn_year,
1474                 $ps_format
1475             );
1476         }
1477
1478         $hn_days = Date_Calc::dateToDays($pn_day, $pn_month, $pn_year);
1479         return Date_Calc::daysToDate(
1480             Date_Calc::addMonthsToDays(
1481                 $pn_months,
1482                 $hn_days
1483             ),
1484             $ps_format
1485         );
1486     }
1487
1488
1489     // }}}
1490     // {{{ addYearsToDays()
1491
1492     /**
1493      * Returns 'Julian Day' of the date the specified no of years
1494      * from the given date
1495      *
1496      * To subtract years use a negative value for the '$pn_years'
1497      * parameter
1498      *
1499      * @param int $pn_years years to add
1500      * @param int $pn_days 'Julian Day', i.e. the no of days since 1st January,
1501      *                       4713 B.C.
1502      *
1503      * @return   int        'Julian Day', i.e. the no of days since 1st January,
1504      *                       4713 B.C.
1505      * @access   public
1506      * @static
1507      * @since    Method available since Release 1.5.0
1508      */
1509     public function addYearsToDays($pn_years, $pn_days)
1510     {
1511         if ($pn_years == 0) {
1512             return (int)$pn_days;
1513         }
1514
1515         list($hn_year, $hn_month, $hn_day) =
1516             explode(" ", Date_Calc::daysToDate($pn_days, "%Y %m %d"));
1517
1518         $hn_retyear = $hn_year + $pn_years;
1519         if (Date_Calc::isValidDate($hn_day, $hn_month, $hn_retyear)) {
1520             return Date_Calc::dateToDays($hn_day, $hn_month, $hn_retyear);
1521         }
1522
1523         $ha_months = Date_Calc::getMonths($hn_retyear);
1524         if (in_array($hn_month, $ha_months)) {
1525             $hn_retmonth = $hn_month;
1526
1527             // Calculate days since first of month:
1528             //
1529             $hn_dayoffset = $pn_days - Date_Calc::firstDayOfMonth(
1530                     $hn_month,
1531                     $hn_year
1532                 );
1533
1534             $hn_retmonthfirstday = Date_Calc::firstDayOfMonth(
1535                 $hn_retmonth,
1536                 $hn_retyear
1537             );
1538             $hn_retmonthlastday = Date_Calc::lastDayOfMonth(
1539                 $hn_retmonth,
1540                 $hn_retyear
1541             );
1542
1543             if ($hn_dayoffset > $hn_retmonthlastday - $hn_retmonthfirstday) {
1544                 return $hn_retmonthlastday;
1545             } else {
1546                 return $hn_retmonthfirstday + $hn_dayoffset;
1547             }
1548         } else {
1549             // Calculate days since first of year:
1550             //
1551             $hn_dayoffset = $pn_days - Date_Calc::firstDayOfYear($hn_year);
1552
1553             $hn_retyearfirstday = Date_Calc::firstDayOfYear($hn_retyear);
1554             $hn_retyearlastday = Date_Calc::lastDayOfYear($hn_retyear);
1555
1556             if ($hn_dayoffset > $hn_retyearlastday - $hn_retyearfirstday) {
1557                 return $hn_retyearlastday;
1558             } else {
1559                 return $hn_retyearfirstday + $hn_dayoffset;
1560             }
1561         }
1562     }
1563
1564
1565     // }}}
1566     // {{{ addYears()
1567
1568     /**
1569      * Returns the date the specified no of years from the given date
1570      *
1571      * To subtract years use a negative value for the '$pn_years'
1572      * parameter
1573      *
1574      * @param int $pn_years years to add
1575      * @param int $pn_day the day of the month, default is current local
1576      *                           day
1577      * @param int $pn_month the month, default is current local month
1578      * @param int $pn_year the year in four digit format, default is
1579      *                           current local year
1580      * @param string $ps_format string specifying how to format the output
1581      *
1582      * @return   string     the date in the desired format
1583      * @access   public
1584      * @static
1585      * @since    Method available since Release 1.5.0
1586      */
1587     public function addYears(
1588         $pn_years,
1589         $pn_day,
1590         $pn_month,
1591         $pn_year,
1592         $ps_format = DATE_CALC_FORMAT
1593     )
1594     {
1595         if (is_null($pn_year)) {
1596             $pn_year = Date_Calc::dateNow('%Y');
1597         }
1598         if (empty($pn_month)) {
1599             $pn_month = Date_Calc::dateNow('%m');
1600         }
1601         if (empty($pn_day)) {
1602             $pn_day = Date_Calc::dateNow('%d');
1603         }
1604
1605         if ($pn_years == 0) {
1606             return Date_Calc::dateFormat(
1607                 $pn_day,
1608                 $pn_month,
1609                 $pn_year,
1610                 $ps_format
1611             );
1612         }
1613
1614         $hn_days = Date_Calc::dateToDays($pn_day, $pn_month, $pn_year);
1615         return Date_Calc::daysToDate(
1616             Date_Calc::addYearsToDays(
1617                 $pn_years,
1618                 $hn_days
1619             ),
1620             $ps_format
1621         );
1622     }
1623
1624
1625     // }}}
1626     // {{{ addDays()
1627
1628     /**
1629      * Returns the date the specified no of days from the given date
1630      *
1631      * To subtract days use a negative value for the '$pn_days' parameter
1632      *
1633      * @param int $pn_days days to add
1634      * @param int $pn_day the day of the month, default is current local
1635      *                           day
1636      * @param int $pn_month the month, default is current local month
1637      * @param int $pn_year the year in four digit format, default is
1638      *                           current local year
1639      * @param string $ps_format string specifying how to format the output
1640      *
1641      * @return   string     the date in the desired format
1642      * @access   public
1643      * @static
1644      * @since    Method available since Release 1.5.0
1645      */
1646     public function addDays(
1647         $pn_days,
1648         $pn_day,
1649         $pn_month,
1650         $pn_year,
1651         $ps_format = DATE_CALC_FORMAT
1652     )
1653     {
1654         if (is_null($pn_year)) {
1655             $pn_year = Date_Calc::dateNow('%Y');
1656         }
1657         if (empty($pn_month)) {
1658             $pn_month = Date_Calc::dateNow('%m');
1659         }
1660         if (empty($pn_day)) {
1661             $pn_day = Date_Calc::dateNow('%d');
1662         }
1663
1664         if ($pn_days == 0) {
1665             return Date_Calc::dateFormat(
1666                 $pn_day,
1667                 $pn_month,
1668                 $pn_year,
1669                 $ps_format
1670             );
1671         }
1672
1673         return Date_Calc::daysToDate(
1674             Date_Calc::dateToDays(
1675                 $pn_day,
1676                 $pn_month,
1677                 $pn_year
1678             ) +
1679             $pn_days,
1680             $ps_format
1681         );
1682     }
1683
1684
1685     // }}}
1686     // {{{ getFirstDayOfMonth()
1687
1688     /**
1689      * Returns first day of the specified month of specified year as integer
1690      *
1691      * @param int $pn_month the month
1692      * @param int $pn_year the year (using 'Astronomical' year numbering)
1693      *
1694      * @return   int        number of first day of month
1695      * @access   public
1696      * @static
1697      * @since    Method available since Release 1.5.0
1698      */
1699     public function getFirstDayOfMonth($pn_month, $pn_year)
1700     {
1701         return 1;
1702     }
1703
1704
1705     // }}}
1706     // {{{ getLastDayOfMonth()
1707
1708     /**
1709      * Returns last day of the specified month of specified year as integer
1710      *
1711      * @param int $pn_month the month
1712      * @param int $pn_year the year (using 'Astronomical' year numbering)
1713      *
1714      * @return   int        number of last day of month
1715      * @access   public
1716      * @static
1717      * @since    Method available since Release 1.5.0
1718      */
1719     public function getLastDayOfMonth($pn_month, $pn_year)
1720     {
1721         return Date_Calc::daysInMonth($pn_month, $pn_year);
1722     }
1723
1724
1725     // }}}
1726     // {{{ firstDayOfMonth()
1727
1728     /**
1729      * Returns the Julian Day of the first day of the month of the specified
1730      * year (i.e. the no of days since 24th November, 4714 B.C.)
1731      *
1732      * @param int $pn_month the month
1733      * @param int $pn_year the year (using 'Astronomical' year numbering)
1734      *
1735      * @return   integer    the number of days since 24th November, 4714 B.C.
1736      * @access   public
1737      * @static
1738      * @since    Method available since Release 1.5.0
1739      */
1740     public function firstDayOfMonth($pn_month, $pn_year)
1741     {
1742         return Date_Calc::dateToDays(
1743             Date_Calc::getFirstDayOfMonth(
1744                 $pn_month,
1745                 $pn_year
1746             ),
1747             $pn_month,
1748             $pn_year
1749         );
1750     }
1751
1752
1753     // }}}
1754     // {{{ lastDayOfMonth()
1755
1756     /**
1757      * Returns the Julian Day of the last day of the month of the specified
1758      * year (i.e. the no of days since 24th November, 4714 B.C.)
1759      *
1760      * @param int $pn_month the month
1761      * @param int $pn_year the year (using 'Astronomical' year numbering)
1762      *
1763      * @return   integer    the number of days since 24th November, 4714 B.C.
1764      * @access   public
1765      * @static
1766      * @since    Method available since Release 1.5.0
1767      */
1768     public function lastDayOfMonth($pn_month, $pn_year)
1769     {
1770         list($hn_nmyear, $hn_nextmonth) = Date_Calc::nextMonth(
1771             $pn_month,
1772             $pn_year
1773         );
1774         return Date_Calc::firstDayOfMonth($hn_nextmonth, $hn_nmyear) - 1;
1775     }
1776
1777
1778     // }}}
1779     // {{{ getFirstMonthOfYear()
1780
1781     /**
1782      * Returns first month of specified year as integer
1783      *
1784      * @param int $pn_year the year (using 'Astronomical' year numbering)
1785      *
1786      * @return   int        number of first month of year
1787      * @access   public
1788      * @static
1789      * @since    Method available since Release 1.5.0
1790      */
1791     public function getFirstMonthOfYear($pn_year)
1792     {
1793         $ha_months = Date_Calc::getMonths($pn_year);
1794         return $ha_months[0];
1795     }
1796
1797
1798     // }}}
1799     // {{{ firstDayOfYear()
1800
1801     /**
1802      * Returns the Julian Day of the first day of the year (i.e. the no of
1803      * days since 24th November, 4714 B.C.)
1804      *
1805      * @param int $pn_year the year (using 'Astronomical' year numbering)
1806      *
1807      * @return   integer    the number of days since 24th November, 4714 B.C.
1808      * @access   public
1809      * @static
1810      * @since    Method available since Release 1.5.0
1811      */
1812     public function firstDayOfYear($pn_year)
1813     {
1814         return Date_Calc::firstDayOfMonth(
1815             Date_Calc::getFirstMonthOfYear($pn_year),
1816             $pn_year
1817         );
1818     }
1819
1820
1821     // }}}
1822     // {{{ lastDayOfYear()
1823
1824     /**
1825      * Returns the Julian Day of the last day of the year (i.e. the no of
1826      * days since 24th November, 4714 B.C.)
1827      *
1828      * @param int $pn_year the year (using 'Astronomical' year numbering)
1829      *
1830      * @return   integer    the number of days since 24th November, 4714 B.C.
1831      * @access   public
1832      * @static
1833      * @since    Method available since Release 1.5.0
1834      */
1835     public function lastDayOfYear($pn_year)
1836     {
1837         return Date_Calc::firstDayOfYear($pn_year + 1) - 1;
1838     }
1839
1840
1841     // }}}
1842     // {{{ dateToDaysJulian()
1843
1844     /**
1845      * Converts a date in the proleptic Julian calendar to the no of days
1846      * since 1st January, 4713 B.C.
1847      *
1848      * Returns the no of days since Monday, 1st January, 4713 B.C. in the
1849      * proleptic Julian calendar (which is 1st January, -4712 using
1850      * 'Astronomical' year numbering, and 24th November, 4713 B.C. in the
1851      * proleptic Gregorian calendar).  This is also the first day of the 'Julian
1852      * Period' proposed by Joseph Scaliger in 1583, and the number of days
1853      * since this date is known as the 'Julian Day'.  (It is not directly
1854      * to do with the Julian calendar, although this is where the name
1855      * is derived from.)
1856      *
1857      * The algorithm is valid for all years (positive and negative), and
1858      * also for years preceding 4713 B.C.
1859      *
1860      * @param int $day the day of the month
1861      * @param int $month the month
1862      * @param int $year the year (using 'Astronomical' year numbering)
1863      *
1864      * @return   int        the number of days since 1st January, 4713 B.C.
1865      * @access   public
1866      * @static
1867      * @since    Method available since Release 1.5.0
1868      */
1869     public function dateToDaysJulian($day, $month, $year)
1870     {
1871         if ($month > 2) {
1872             // March = 0, April = 1, ..., December = 9,
1873             // January = 10, February = 11
1874             $month -= 3;
1875         } else {
1876             $month += 9;
1877             --$year;
1878         }
1879
1880         $hb_negativeyear = $year < 0;
1881
1882         if ($hb_negativeyear) {
1883             // Subtract 1 because year 0 is a leap year;
1884             // And N.B. that we must treat the leap years as occurring
1885             // one year earlier than they do, because for the purposes
1886             // of calculation, the year starts on 1st March:
1887             //
1888             return intval((1461 * $year + 1) / 4) +
1889                 intval((153 * $month + 2) / 5) +
1890                 $day + 1721116;
1891         } else {
1892             return intval(1461 * $year / 4) +
1893                 floor((153 * $month + 2) / 5) +
1894                 $day + 1721117;
1895         }
1896     }
1897
1898
1899     // }}}
1900     // {{{ daysToDateJulian()
1901
1902     /**
1903      * Converts no of days since 1st January, 4713 B.C. (in the proleptic
1904      * Julian calendar, which is year -4712 using 'Astronomical' year
1905      * numbering) to Julian calendar date
1906      *
1907      * Returned date belongs to the proleptic Julian calendar, using
1908      * 'Astronomical' year numbering.
1909      *
1910      * @param int $days the number of days since 1st January, 4713 B.C.
1911      * @param string $format the string indicating how to format the output
1912      *
1913      * @return   string     the date in the desired format
1914      * @access   public
1915      * @static
1916      * @since    Method available since Release 1.5.0
1917      */
1918     public function daysToDateJulian($days, $format = DATE_CALC_FORMAT)
1919     {
1920         $days = intval($days);
1921
1922         $days -= 1721117;
1923         $days = floor(4 * $days - 1);
1924         $day = floor($days / 4);
1925
1926         $year = floor((4 * $day + 3) / 1461);
1927         $day = floor(4 * $day + 3 - 1461 * $year);
1928         $day = floor(($day + 4) / 4);
1929
1930         $month = floor((5 * $day - 3) / 153);
1931         $day = floor(5 * $day - 3 - 153 * $month);
1932         $day = floor(($day + 5) / 5);
1933
1934         if ($month < 10) {
1935             $month += 3;
1936         } else {
1937             $month -= 9;
1938             ++$year;
1939         }
1940
1941         return Date_Calc::dateFormat($day, $month, $year, $format);
1942     }
1943
1944
1945     // }}}
1946     // {{{ isoWeekDate()
1947
1948     /**
1949      * Returns array defining the 'ISO Week Date' as defined in ISO 8601
1950      *
1951      * Expects a date in the proleptic Gregorian calendar using 'Astronomical'
1952      * year numbering, that is, with a year 0.  Algorithm is valid for all
1953      * years (positive and negative).
1954      *
1955      * N.B. the ISO week day no for Sunday is defined as 7, whereas this
1956      * class and its related functions defines Sunday as 0.
1957      *
1958      * @param int $pn_day the day of the month
1959      * @param int $pn_month the month
1960      * @param int $pn_year the year
1961      *
1962      * @return   array      array of ISO Year, ISO Week No, ISO Day No as
1963      *                       integers
1964      * @access   public
1965      * @static
1966      * @since    Method available since Release 1.5.0
1967      */
1968     public function isoWeekDate($pn_day = 0, $pn_month = 0, $pn_year = null)
1969     {
1970         if (is_null($pn_year)) {
1971             $pn_year = Date_Calc::dateNow('%Y');
1972         }
1973         if (empty($pn_month)) {
1974             $pn_month = Date_Calc::dateNow('%m');
1975         }
1976         if (empty($pn_day)) {
1977             $pn_day = Date_Calc::dateNow('%d');
1978         }
1979
1980         $hn_jd = Date_Calc::dateToDays($pn_day, $pn_month, $pn_year);
1981         $hn_wd = Date_Calc::daysToDayOfWeek($hn_jd);
1982         if ($hn_wd == 0) {
1983             $hn_wd = 7;
1984         }
1985
1986         $hn_jd1 = Date_Calc::firstDayOfYear($pn_year);
1987         $hn_day = $hn_jd - $hn_jd1 + 1;
1988
1989         if ($hn_wd <= $hn_jd - Date_Calc::lastDayOfYear($pn_year) + 3) {
1990             // ISO week is the first week of the next ISO year:
1991             //
1992             $hn_year = $pn_year + 1;
1993             $hn_isoweek = 1;
1994         } else {
1995             switch ($hn_wd1 = Date_Calc::daysToDayOfWeek($hn_jd1)) {
1996                 case 1:
1997                 case 2:
1998                 case 3:
1999                 case 4:
2000                     // Monday - Thursday:
2001                     //
2002                     $hn_year = $pn_year;
2003                     $hn_isoweek = floor(($hn_day + $hn_wd1 - 2) / 7) + 1;
2004                     break;
2005                 case 0:
2006                     $hn_wd1 = 7;
2007                 // no break
2008                 case 5:
2009                 case 6:
2010                     // Friday - Sunday:
2011                     //
2012                     if ($hn_day <= 8 - $hn_wd1) {
2013                         // ISO week is the last week of the previous ISO year:
2014                         //
2015                         list($hn_year, $hn_lastmonth, $hn_lastday) =
2016                             explode(
2017                                 " ",
2018                                 Date_Calc::daysToDate($hn_jd1 - 1, "%Y %m %d")
2019                             );
2020                         list($hn_year, $hn_isoweek, $hn_pisoday) =
2021                             Date_Calc::isoWeekDate(
2022                                 $hn_lastday,
2023                                 $hn_lastmonth,
2024                                 $hn_year
2025                             );
2026                     } else {
2027                         $hn_year = $pn_year;
2028                         $hn_isoweek = floor(($hn_day + $hn_wd1 - 9) / 7) + 1;
2029                     }
2030
2031                     break;
2032             }
2033         }
2034
2035         return array((int)$hn_year, (int)$hn_isoweek, (int)$hn_wd);
2036     }
2037
2038
2039     // }}}
2040     // {{{ gregorianToISO()
2041
2042     /**
2043      * Converts from Gregorian Year-Month-Day to ISO Year-WeekNumber-WeekDay
2044      *
2045      * Uses ISO 8601 definitions.
2046      *
2047      * @param int $day the day of the month
2048      * @param int $month the month
2049      * @param int $year the year.  Use the complete year instead of the
2050      *                    abbreviated version.  E.g. use 2005, not 05.
2051      *
2052      * @return   string     the date in ISO Year-WeekNumber-WeekDay format
2053      * @access   public
2054      * @static
2055      */
2056     public function gregorianToISO($day, $month, $year)
2057     {
2058         list($yearnumber, $weeknumber, $weekday) =
2059             Date_Calc::isoWeekDate($day, $month, $year);
2060         return sprintf("%04d", $yearnumber) .
2061             '-' .
2062             sprintf("%02d", $weeknumber) .
2063             '-' .
2064             $weekday;
2065     }
2066
2067
2068     // }}}
2069     // {{{ weekOfYear4th()
2070
2071     /**
2072      * Returns week of the year counting week 1 as the week that contains 4th
2073      * January
2074      *
2075      * Week 1 is determined to be the week that includes the 4th January, and
2076      * therefore can be defined as the first week of the year that has at least
2077      * 4 days.  The previous week is counted as week 52 or 53 of the previous
2078      * year.  Note that this definition depends on which day is the first day of
2079      * the week, and that if this is not passed as the '$pn_firstdayofweek'
2080      * parameter, the default is assumed.
2081      *
2082      * Note also that the last day week of the year is likely to extend into
2083      * the following year, except in the case that the last day of the week
2084      * falls on 31st December.
2085      *
2086      * Also note that this is very similar to the ISO week returned by
2087      * {@link Date::isoWeekDate()}, the difference being that the ISO week
2088      * always has 7 days, and if the 4th of January is a Friday, for example,
2089      * ISO week 1 would start on Monday, 31st December in the previous year,
2090      * whereas the week defined by this function would start on 1st January,
2091      * but would be only 6 days long.  Of course you can also set the day
2092      * of the week, whereas the ISO week starts on a Monday by definition.
2093      *
2094      * Returned week is an integer from 1 to 53.
2095      *
2096      * @param int $pn_day the day of the month, default is current
2097      *                                local day
2098      * @param int $pn_month the month, default is current local month
2099      * @param int $pn_year the year in four digit format, default is
2100      *                                current local year
2101      * @param int $pn_firstdayofweek optional integer specifying the first day
2102      *                                of the week
2103      *
2104      * @return   array      array of year, week no as integers
2105      * @access   public
2106      * @static
2107      * @since    Method available since Release 1.5.0
2108      */
2109     public function weekOfYear4th(
2110         $pn_day = 0,
2111         $pn_month = 0,
2112         $pn_year = null,
2113         $pn_firstdayofweek = DATE_CALC_BEGIN_WEEKDAY
2114     )
2115     {
2116         if (is_null($pn_year)) {
2117             $pn_year = Date_Calc::dateNow('%Y');
2118         }
2119         if (empty($pn_month)) {
2120             $pn_month = Date_Calc::dateNow('%m');
2121         }
2122         if (empty($pn_day)) {
2123             $pn_day = Date_Calc::dateNow('%d');
2124         }
2125
2126         $hn_wd1 = Date_Calc::daysToDayOfWeek(Date_Calc::firstDayOfYear($pn_year));
2127         $hn_day = Date_Calc::dayOfYear($pn_day, $pn_month, $pn_year);
2128         $hn_week = floor(($hn_day +
2129                 (10 + $hn_wd1 - $pn_firstdayofweek) % 7 +
2130                 3) / 7);
2131
2132         if ($hn_week > 0) {
2133             $hn_year = $pn_year;
2134         } else {
2135             // Week number is the last week of the previous year:
2136             //
2137             list($hn_year, $hn_lastmonth, $hn_lastday) =
2138                 explode(
2139                     " ",
2140                     Date_Calc::daysToDate(
2141                         Date_Calc::lastDayOfYear($pn_year - 1),
2142                         "%Y %m %d"
2143                     )
2144                 );
2145             list($hn_year, $hn_week) =
2146                 Date_Calc::weekOfYear4th(
2147                     $hn_lastday,
2148                     $hn_lastmonth,
2149                     $hn_year,
2150                     $pn_firstdayofweek
2151                 );
2152         }
2153
2154         return array((int)$hn_year, (int)$hn_week);
2155     }
2156
2157
2158     // }}}
2159     // {{{ weekOfYear7th()
2160
2161     /**
2162      * Returns week of the year counting week 1 as the week that contains 7th
2163      * January
2164      *
2165      * Week 1 is determined to be the week that includes the 7th January, and
2166      * therefore can be defined as the first full week of the year.  The
2167      * previous week is counted as week 52 or 53 of the previous year.  Note
2168      * that this definition depends on which day is the first day of the week,
2169      * and that if this is not passed as the '$pn_firstdayofweek' parameter, the
2170      * default is assumed.
2171      *
2172      * Note also that the last day week of the year is likely to extend into
2173      * the following year, except in the case that the last day of the week
2174      * falls on 31st December.
2175      *
2176      * Returned week is an integer from 1 to 53.
2177      *
2178      * @param int $pn_day the day of the month, default is current
2179      *                                local day
2180      * @param int $pn_month the month, default is current local month
2181      * @param int $pn_year the year in four digit format, default is
2182      *                                current local year
2183      * @param int $pn_firstdayofweek optional integer specifying the first day
2184      *                                of the week
2185      *
2186      * @return   array      array of year, week no as integers
2187      * @access   public
2188      * @static
2189      * @since    Method available since Release 1.5.0
2190      */
2191     public function weekOfYear7th(
2192         $pn_day = 0,
2193         $pn_month = 0,
2194         $pn_year = null,
2195         $pn_firstdayofweek = DATE_CALC_BEGIN_WEEKDAY
2196     )
2197     {
2198         if (is_null($pn_year)) {
2199             $pn_year = Date_Calc::dateNow('%Y');
2200         }
2201         if (empty($pn_month)) {
2202             $pn_month = Date_Calc::dateNow('%m');
2203         }
2204         if (empty($pn_day)) {
2205             $pn_day = Date_Calc::dateNow('%d');
2206         }
2207
2208         $hn_wd1 = Date_Calc::daysToDayOfWeek(Date_Calc::firstDayOfYear($pn_year));
2209         $hn_day = Date_Calc::dayOfYear($pn_day, $pn_month, $pn_year);
2210         $hn_week = floor(($hn_day + (6 + $hn_wd1 - $pn_firstdayofweek) % 7) / 7);
2211
2212         if ($hn_week > 0) {
2213             $hn_year = $pn_year;
2214         } else {
2215             // Week number is the last week of the previous ISO year:
2216             //
2217             list($hn_year, $hn_lastmonth, $hn_lastday) = explode(" ", Date_Calc::daysToDate(Date_Calc::lastDayOfYear($pn_year - 1), "%Y %m %d"));
2218             list($hn_year, $hn_week) = Date_Calc::weekOfYear7th($hn_lastday, $hn_lastmonth, $hn_year, $pn_firstdayofweek);
2219         }
2220
2221         return array((int)$hn_year, (int)$hn_week);
2222     }
2223
2224
2225     // }}}
2226     // {{{ dateSeason()
2227
2228     /**
2229      * Determines julian date of the given season
2230      *
2231      * @param string $season the season to get the date for: VERNALEQUINOX,
2232      *                        SUMMERSOLSTICE, AUTUMNALEQUINOX,
2233      *                        or WINTERSOLSTICE
2234      * @param string $year the year in four digit format.  Must be between
2235      *                        -1000 B.C. and 3000 A.D.
2236      *
2237      * @return   float      the julian date the season starts on
2238      * @access   public
2239      * @static
2240      */
2241     public function dateSeason($season, $year = 0)
2242     {
2243         if ($year == '') {
2244             $year = Date_Calc::dateNow('%Y');
2245         }
2246         if (($year >= -1000) && ($year <= 1000)) {
2247             $y = $year / 1000.0;
2248             switch ($season) {
2249                 case 'VERNALEQUINOX':
2250                     $juliandate = (((((((-0.00071 * $y) - 0.00111) * $y) + 0.06134) * $y) + 365242.1374) * $y) + 1721139.29189;
2251                     break;
2252                 case 'SUMMERSOLSTICE':
2253                     $juliandate = (((((((0.00025 * $y) + 0.00907) * $y) - 0.05323) * $y) + 365241.72562) * $y) + 1721233.25401;
2254                     break;
2255                 case 'AUTUMNALEQUINOX':
2256                     $juliandate = (((((((0.00074 * $y) - 0.00297) * $y) - 0.11677) * $y) + 365242.49558) * $y) + 1721325.70455;
2257                     break;
2258                 case 'WINTERSOLSTICE':
2259                 default:
2260                     $juliandate = (((((((-0.00006 * $y) - 0.00933) * $y) - 0.00769) * $y) + 365242.88257) * $y) + 1721414.39987;
2261             }
2262         } elseif (($year > 1000) && ($year <= 3000)) {
2263             $y = ($year - 2000) / 1000;
2264             switch ($season) {
2265                 case 'VERNALEQUINOX':
2266                     $juliandate = (((((((-0.00057 * $y) - 0.00411) * $y) + 0.05169) * $y) + 365242.37404) * $y) + 2451623.80984;
2267                     break;
2268                 case 'SUMMERSOLSTICE':
2269                     $juliandate = (((((((-0.0003 * $y) + 0.00888) * $y) + 0.00325) * $y) + 365241.62603) * $y) + 2451716.56767;
2270                     break;
2271                 case 'AUTUMNALEQUINOX':
2272                     $juliandate = (((((((0.00078 * $y) + 0.00337) * $y) - 0.11575) * $y) + 365242.01767) * $y) + 2451810.21715;
2273                     break;
2274                 case 'WINTERSOLSTICE':
2275                 default:
2276                     $juliandate = (((((((0.00032 * $y) - 0.00823) * $y) - 0.06223) * $y) + 365242.74049) * $y) + 2451900.05952;
2277             }
2278         }
2279         return $juliandate;
2280     }
2281
2282
2283     // }}}
2284     // {{{ dayOfYear()
2285
2286     /**
2287      * Returns number of days since 31 December of year before given date
2288      *
2289      * @param int $pn_day the day of the month, default is current local day
2290      * @param int $pn_month the month, default is current local month
2291      * @param int $pn_year the year in four digit format, default is current
2292      *                       local year
2293      *
2294      * @return   int
2295      * @access   public
2296      * @static
2297      * @since    Method available since Release 1.5.0
2298      */
2299     public function dayOfYear($pn_day = 0, $pn_month = 0, $pn_year = null)
2300     {
2301         if (is_null($pn_year)) {
2302             $pn_year = Date_Calc::dateNow('%Y');
2303         }
2304         if (empty($pn_month)) {
2305             $pn_month = Date_Calc::dateNow('%m');
2306         }
2307         if (empty($pn_day)) {
2308             $pn_day = Date_Calc::dateNow('%d');
2309         }
2310
2311         $hn_jd = Date_Calc::dateToDays($pn_day, $pn_month, $pn_year);
2312         $hn_jd1 = Date_Calc::firstDayOfYear($pn_year);
2313         return $hn_jd - $hn_jd1 + 1;
2314     }
2315
2316
2317     // }}}
2318     // {{{ julianDate()
2319
2320     /**
2321      * Returns number of days since 31 December of year before given date
2322      *
2323      * @param int $pn_day the day of the month, default is current local day
2324      * @param int $pn_month the month, default is current local month
2325      * @param int $pn_year the year in four digit format, default is current
2326      *                       local year
2327      *
2328      * @return     int
2329      * @access     public
2330      * @static
2331      * @deprecated Method deprecated in Release 1.5.0
2332      */
2333     public function julianDate($pn_day = 0, $pn_month = 0, $pn_year = null)
2334     {
2335         return Date_Calc::dayOfYear($pn_day, $pn_month, $pn_year);
2336     }
2337
2338
2339     // }}}
2340     // {{{ getWeekdayFullname()
2341
2342     /**
2343      * Returns the full weekday name for the given date
2344      *
2345      * @param int $pn_day the day of the month, default is current local day
2346      * @param int $pn_month the month, default is current local month
2347      * @param int $pn_year the year in four digit format, default is current
2348      *                       local year
2349      *
2350      * @return   string     the full name of the day of the week
2351      * @access   public
2352      * @static
2353      */
2354     public function getWeekdayFullname($pn_day = 0, $pn_month = 0, $pn_year = null)
2355     {
2356         if (is_null($pn_year)) {
2357             $pn_year = Date_Calc::dateNow('%Y');
2358         }
2359         if (empty($pn_month)) {
2360             $pn_month = Date_Calc::dateNow('%m');
2361         }
2362         if (empty($pn_day)) {
2363             $pn_day = Date_Calc::dateNow('%d');
2364         }
2365
2366         $weekday_names = Date_Calc::getWeekDays();
2367         $weekday = Date_Calc::dayOfWeek($pn_day, $pn_month, $pn_year);
2368         return $weekday_names[$weekday];
2369     }
2370
2371
2372     // }}}
2373     // {{{ getWeekdayAbbrname()
2374
2375     /**
2376      * Returns the abbreviated weekday name for the given date
2377      *
2378      * @param int $pn_day the day of the month, default is current local day
2379      * @param int $pn_month the month, default is current local month
2380      * @param int $pn_year the year in four digit format, default is current
2381      *                       local year
2382      * @param int $length the length of abbreviation
2383      *
2384      * @return   string     the abbreviated name of the day of the week
2385      * @access   public
2386      * @static
2387      * @see      Date_Calc::getWeekdayFullname()
2388      */
2389     public function getWeekdayAbbrname(
2390         $pn_day = 0,
2391         $pn_month = 0,
2392         $pn_year = null,
2393         $length = 3
2394     )
2395     {
2396         if (is_null($pn_year)) {
2397             $pn_year = Date_Calc::dateNow('%Y');
2398         }
2399         if (empty($pn_month)) {
2400             $pn_month = Date_Calc::dateNow('%m');
2401         }
2402         if (empty($pn_day)) {
2403             $pn_day = Date_Calc::dateNow('%d');
2404         }
2405
2406         $weekday_names = Date_Calc::getWeekDays(true);
2407         $weekday = Date_Calc::dayOfWeek($pn_day, $pn_month, $pn_year);
2408         return $weekday_names[$weekday];
2409     }
2410
2411
2412     // }}}
2413     // {{{ getMonthFullname()
2414
2415     /**
2416      * Returns the full month name for the given month
2417      *
2418      * @param int $month the month
2419      *
2420      * @return   string     the full name of the month
2421      * @access   public
2422      * @static
2423      */
2424     public function getMonthFullname($month)
2425     {
2426         $month = (int)$month;
2427         if (empty($month)) {
2428             $month = (int)Date_Calc::dateNow('%m');
2429         }
2430
2431         $month_names = Date_Calc::getMonthNames();
2432         return $month_names[$month];
2433     }
2434
2435
2436     // }}}
2437     // {{{ getMonthAbbrname()
2438
2439     /**
2440      * Returns the abbreviated month name for the given month
2441      *
2442      * @param int $month the month
2443      * @param int $length the length of abbreviation
2444      *
2445      * @return   string     the abbreviated name of the month
2446      * @access   public
2447      * @static
2448      * @see      Date_Calc::getMonthFullname
2449      */
2450     public function getMonthAbbrname($month, $length = 3)
2451     {
2452         $month = (int)$month;
2453         if (empty($month)) {
2454             $month = Date_Calc::dateNow('%m');
2455         }
2456
2457         $month_names = Date_Calc::getMonthNames(true);
2458         return $month_names[$month];
2459     }
2460
2461
2462     // }}}
2463     // {{{ getMonthFromFullname()
2464
2465     /**
2466      * Returns the numeric month from the month name or an abreviation
2467      *
2468      * Both August and Aug would return 8.
2469      *
2470      * @param string $month the name of the month to examine.
2471      *                       Case insensitive.
2472      *
2473      * @return   int        the month's number
2474      * @access   public
2475      * @static
2476      */
2477     public function getMonthFromFullName($month)
2478     {
2479         $month = strtolower($month);
2480         $months = Date_Calc::getMonthNames();
2481         while (list($id, $name) = each($months)) {
2482             if (preg_match('/' . $month . '/', strtolower($name))) {
2483                 return $id;
2484             }
2485         }
2486         return 0;
2487     }
2488
2489
2490     // }}}
2491     // {{{ getWeekDays()
2492
2493     /**
2494      * Returns an array of week day names
2495      *
2496      * Used to take advantage of the setlocale function to return language
2497      * specific week days.
2498      *
2499      * @param int $pb_abbreviated whether to return the abbreviated form of the
2500      *                             days
2501      *
2502      * @return   array      an array of week-day names
2503      * @access   public
2504      * @static
2505      */
2506     public function getWeekDays($pb_abbreviated = false)
2507     {
2508         for ($i = 0; $i < 7; $i++) {
2509             $weekdays[$i] = strftime(
2510                 $pb_abbreviated ? '%a' : '%A',
2511                 mktime(0, 0, 0, 1, $i, 2001)
2512             );
2513         }
2514         return $weekdays;
2515     }
2516
2517
2518     // }}}
2519     // {{{ daysToDayOfWeek()
2520
2521     /**
2522      * Returns day of week for specified 'Julian Day'
2523      *
2524      * The algorithm is valid for all years (positive and negative), and
2525      * also for years preceding 4714 B.C. (i.e. for negative 'Julian Days'),
2526      * and so the only limitation is platform-dependent (for 32-bit systems
2527      * the maximum year would be something like about 1,465,190 A.D.).
2528      *
2529      * N.B. Monday, 24th November, 4714 B.C. is Julian Day '0'.
2530      *
2531      * @param int $pn_days the number of days since 24th November, 4714 B.C.
2532      *
2533      * @return   int        integer from 0 to 7 where 0 represents Sunday
2534      * @access   public
2535      * @static
2536      * @since    Method available since Release 1.5.0
2537      */
2538     public function daysToDayOfWeek($pn_days)
2539     {
2540         // On Julian day 0 the day is Monday (PHP day 1):
2541         //
2542         $ret = ($pn_days + 1) % 7;
2543         return $ret < 0 ? $ret + 7 : $ret;
2544     }
2545
2546
2547     // }}}
2548     // {{{ dayOfWeek()
2549
2550     /**
2551      * Returns day of week for given date (0 = Sunday)
2552      *
2553      * The algorithm is valid for all years (positive and negative).
2554      *
2555      * @param int $day the day of the month, default is current local day
2556      * @param int $month the month, default is current local month
2557      * @param int $year the year in four digit format, default is current
2558      *                    local year
2559      *
2560      * @return   int        the number of the day in the week
2561      * @access   public
2562      * @static
2563      */
2564     public function dayOfWeek($day = null, $month = null, $year = null)
2565     {
2566         if (is_null($year)) {
2567             $year = Date_Calc::dateNow('%Y');
2568         }
2569         if (empty($month)) {
2570             $month = Date_Calc::dateNow('%m');
2571         }
2572         if (empty($day)) {
2573             $day = Date_Calc::dateNow('%d');
2574         }
2575
2576         // if ($month <= 2) {
2577         //     $month += 12;
2578         //     --$year;
2579         // }
2580
2581         // $wd = ($day +
2582         //        intval((13 * $month + 3) / 5) +
2583         //        $year +
2584         //        floor($year / 4) -
2585         //        floor($year / 100) +
2586         //        floor($year / 400) +
2587         //        1) % 7;
2588
2589         // return (int) ($wd < 0 ? $wd + 7 : $wd);
2590
2591         return Date_Calc::daysToDayOfWeek(Date_Calc::dateToDays(
2592             $day,
2593             $month,
2594             $year
2595         ));
2596     }
2597
2598
2599     // }}}
2600     // {{{ weekOfYearAbsolute()
2601
2602     /**
2603      * Returns week of the year counting week 1 as 1st-7th January,
2604      * regardless of what day 1st January falls on
2605      *
2606      * Returned value is an integer from 1 to 53.  Week 53 will start on
2607      * 31st December and have only one day, except in a leap year, in
2608      * which it will start a day earlier and contain two days.
2609      *
2610      * @param int $pn_day the day of the month, default is current local day
2611      * @param int $pn_month the month, default is current local month
2612      * @param int $pn_year the year in four digit format, default is current
2613      *                       local year
2614      *
2615      * @return   int        integer from 1 to 53
2616      * @access   public
2617      * @static
2618      * @since    Method available since Release 1.5.0
2619      */
2620     public function weekOfYearAbsolute($pn_day = 0, $pn_month = 0, $pn_year = null)
2621     {
2622         if (is_null($pn_year)) {
2623             $pn_year = Date_Calc::dateNow('%Y');
2624         }
2625         if (empty($pn_month)) {
2626             $pn_month = Date_Calc::dateNow('%m');
2627         }
2628         if (empty($pn_day)) {
2629             $pn_day = Date_Calc::dateNow('%d');
2630         }
2631
2632         $hn_day = Date_Calc::dayOfYear($pn_day, $pn_month, $pn_year);
2633         return intval(($hn_day + 6) / 7);
2634     }
2635
2636
2637     // }}}
2638     // {{{ weekOfYear1st()
2639
2640     /**
2641      * Returns week of the year counting week 1 as the week that contains 1st
2642      * January
2643      *
2644      * Week 1 is determined to be the week that includes the 1st January, even
2645      * if this week extends into the previous year, in which case the week will
2646      * only contain between 1 and 6 days of the current year.  Note that this
2647      * definition depends on which day is the first day of the week, and that if
2648      * this is not passed as the '$pn_firstdayofweek' parameter, the default is
2649      * assumed.
2650      *
2651      * Note also that the last day week of the year is also likely to contain
2652      * less than seven days, except in the case that the last day of the week
2653      * falls on 31st December.
2654      *
2655      * Returned value is an integer from 1 to 54.  The year will only contain
2656      * 54 weeks in the case of a leap year in which 1st January is the last day
2657      * of the week, and 31st December is the first day of the week.  In this
2658      * case, both weeks 1 and 54 will contain one day only.
2659      *
2660      * @param int $pn_day the day of the month, default is current
2661      *                                local day
2662      * @param int $pn_month the month, default is current local month
2663      * @param int $pn_year the year in four digit format, default is
2664      *                                current local year
2665      * @param int $pn_firstdayofweek optional integer specifying the first day
2666      *                                of the week
2667      *
2668      * @return   int        integer from 1 to 54
2669      * @access   public
2670      * @static
2671      * @since    Method available since Release 1.5.0
2672      */
2673     public function weekOfYear1st(
2674         $pn_day = 0,
2675         $pn_month = 0,
2676         $pn_year = null,
2677         $pn_firstdayofweek = DATE_CALC_BEGIN_WEEKDAY
2678     )
2679     {
2680         if (is_null($pn_year)) {
2681             $pn_year = Date_Calc::dateNow('%Y');
2682         }
2683         if (empty($pn_month)) {
2684             $pn_month = Date_Calc::dateNow('%m');
2685         }
2686         if (empty($pn_day)) {
2687             $pn_day = Date_Calc::dateNow('%d');
2688         }
2689
2690         $hn_wd1 = Date_Calc::daysToDayOfWeek(Date_Calc::firstDayOfYear($pn_year));
2691         $hn_day = Date_Calc::dayOfYear($pn_day, $pn_month, $pn_year);
2692         return floor(($hn_day + (7 + $hn_wd1 - $pn_firstdayofweek) % 7 + 6) / 7);
2693     }
2694
2695
2696     // }}}
2697     // {{{ weekOfYear()
2698
2699     /**
2700      * Returns week of the year, where first Sunday is first day of first week
2701      *
2702      * N.B. this function is equivalent to calling:
2703      *
2704      *  <code>Date_Calc::weekOfYear7th($day, $month, $year, 0);</code>
2705      *
2706      * Returned week is an integer from 1 to 53.
2707      *
2708      * @param int $pn_day the day of the month, default is current local day
2709      * @param int $pn_month the month, default is current local month
2710      * @param int $pn_year the year in four digit format, default is current
2711      *                       local year
2712      *
2713      * @return     int        integer from 1 to 53
2714      * @access     public
2715      * @static
2716      * @see        Date_Calc::weekOfYear7th
2717      * @deprecated Method deprecated in Release 1.5.0
2718      */
2719     public function weekOfYear($pn_day = 0, $pn_month = 0, $pn_year = null)
2720     {
2721         $ha_week = Date_Calc::weekOfYear7th($pn_day, $pn_month, $pn_year, 0);
2722         return $ha_week[1];
2723     }
2724
2725
2726     // }}}
2727     // {{{ weekOfMonthAbsolute()
2728
2729     /**
2730      * Returns week of the month counting week 1 as 1st-7th of the month,
2731      * regardless of what day the 1st falls on
2732      *
2733      * Returned value is an integer from 1 to 5.  Week 5 will start on
2734      * the 29th of the month and have between 1 and 3 days, except
2735      * in February in a non-leap year, when there will be 4 weeks only.
2736      *
2737      * @param int $pn_day the day of the month, default is current local day
2738      *
2739      * @return   int        integer from 1 to 5
2740      * @access   public
2741      * @static
2742      * @since    Method available since Release 1.5.0
2743      */
2744     public function weekOfMonthAbsolute($pn_day = 0)
2745     {
2746         if (empty($pn_day)) {
2747             $pn_day = Date_Calc::dateNow('%d');
2748         }
2749         return intval(($pn_day + 6) / 7);
2750     }
2751
2752
2753     // }}}
2754     // {{{ weekOfMonth()
2755
2756     /**
2757      * Alias for 'weekOfMonthAbsolute()'
2758      *
2759      * @param int $pn_day the day of the month, default is current local day
2760      *
2761      * @return   int        integer from 1 to 5
2762      * @access   public
2763      * @static
2764      * @since    Method available since Release 1.5.0
2765      */
2766     public function weekOfMonth($pn_day = 0)
2767     {
2768         return Date_Calc::weekOfMonthAbsolute($pn_day);
2769     }
2770
2771
2772     // }}}
2773     // {{{ quarterOfYear()
2774
2775     /**
2776      * Returns quarter of the year for given date
2777      *
2778      * @param int $day the day of the month, default is current local day
2779      * @param int $month the month, default is current local month
2780      * @param int $year the year in four digit format, default is current
2781      *                    local year
2782      *
2783      * @return   int        the number of the quarter in the year
2784      * @access   public
2785      * @static
2786      */
2787     public function quarterOfYear($day = 0, $month = 0, $year = null)
2788     {
2789         if (empty($month)) {
2790             $month = Date_Calc::dateNow('%m');
2791         }
2792         return intval(($month - 1) / 3 + 1);
2793     }
2794
2795
2796     // }}}
2797     // {{{ daysInMonth()
2798
2799     /**
2800      * Returns the number of days in the given month
2801      *
2802      * @param int $month the month, default is current local month
2803      * @param int $year the year in four digit format, default is current
2804      *                    local year
2805      *
2806      * @return   int        the number of days the month has
2807      * @access   public
2808      * @static
2809      */
2810     public function daysInMonth($month = 0, $year = null)
2811     {
2812         if (is_null($year)) {
2813             $year = Date_Calc::dateNow('%Y');
2814         }
2815         if (empty($month)) {
2816             $month = Date_Calc::dateNow('%m');
2817         }
2818
2819         return Date_Calc::lastDayOfMonth($month, $year) -
2820             Date_Calc::firstDayOfMonth($month, $year) +
2821             1;
2822     }
2823
2824
2825     // }}}
2826     // {{{ daysInYear()
2827
2828     /**
2829      * Returns the number of days in the given year
2830      *
2831      * @param int $year the year in four digit format, default is current local
2832      *                   year
2833      *
2834      * @return   int        the number of days the year has
2835      * @access   public
2836      * @static
2837      */
2838     public function daysInYear($year = null)
2839     {
2840         if (is_null($year)) {
2841             $year = Date_Calc::dateNow('%Y');
2842         }
2843
2844         return Date_Calc::firstDayOfYear($year + 1) -
2845             Date_Calc::firstDayOfYear($year);
2846     }
2847
2848
2849     // }}}
2850     // {{{ weeksInMonth()
2851
2852     /**
2853      * Returns the number of rows on a calendar month
2854      *
2855      * Useful for determining the number of rows when displaying a typical
2856      * month calendar.
2857      *
2858      * @param int $month the month, default is current local month
2859      * @param int $year the year in four digit format, default is current
2860      *                    local year
2861      *
2862      * @return   int        the number of weeks the month has
2863      * @access   public
2864      * @static
2865      */
2866     public function weeksInMonth($month = 0, $year = null)
2867     {
2868         if (is_null($year)) {
2869             $year = Date_Calc::dateNow('%Y');
2870         }
2871         if (empty($month)) {
2872             $month = Date_Calc::dateNow('%m');
2873         }
2874         $FDOM = Date_Calc::firstOfMonthWeekday($month, $year);
2875         if (DATE_CALC_BEGIN_WEEKDAY == 1 && $FDOM == 0) {
2876             $first_week_days = 7 - $FDOM + DATE_CALC_BEGIN_WEEKDAY;
2877             $weeks = 1;
2878         } elseif (DATE_CALC_BEGIN_WEEKDAY == 0 && $FDOM == 6) {
2879             $first_week_days = 7 - $FDOM + DATE_CALC_BEGIN_WEEKDAY;
2880             $weeks = 1;
2881         } else {
2882             $first_week_days = DATE_CALC_BEGIN_WEEKDAY - $FDOM;
2883             $weeks = 0;
2884         }
2885         $first_week_days %= 7;
2886         return ceil((Date_Calc::daysInMonth($month, $year)
2887                     - $first_week_days) / 7) + $weeks;
2888     }
2889
2890
2891     // }}}
2892     // {{{ getCalendarWeek()
2893
2894     /**
2895      * Return an array with days in week
2896      *
2897      * @param int $day the day of the month, default is current local day
2898      * @param int $month the month, default is current local month
2899      * @param int $year the year in four digit format, default is current
2900      *                        local year
2901      * @param string $format the string indicating how to format the output
2902      *
2903      * @return   array      $week[$weekday]
2904      * @access   public
2905      * @static
2906      */
2907     public function getCalendarWeek(
2908         $day = 0,
2909         $month = 0,
2910         $year = null,
2911         $format = DATE_CALC_FORMAT
2912     )
2913     {
2914         if (is_null($year)) {
2915             $year = Date_Calc::dateNow('%Y');
2916         }
2917         if (empty($month)) {
2918             $month = Date_Calc::dateNow('%m');
2919         }
2920         if (empty($day)) {
2921             $day = Date_Calc::dateNow('%d');
2922         }
2923
2924         $week_array = array();
2925
2926         // date for the column of week
2927
2928         $curr_day = Date_Calc::beginOfWeek($day, $month, $year, '%E');
2929
2930         for ($counter = 0; $counter <= 6; $counter++) {
2931             $week_array[$counter] = Date_Calc::daysToDate($curr_day, $format);
2932             $curr_day++;
2933         }
2934         return $week_array;
2935     }
2936
2937
2938     // }}}
2939     // {{{ getCalendarMonth()
2940
2941     /**
2942      * Return a set of arrays to construct a calendar month for the given date
2943      *
2944      * @param int $month the month, default is current local month
2945      * @param int $year the year in four digit format, default is current
2946      *                        local year
2947      * @param string $format the string indicating how to format the output
2948      *
2949      * @return   array      $month[$row][$col]
2950      * @access   public
2951      * @static
2952      */
2953     public function getCalendarMonth(
2954         $month = 0,
2955         $year = null,
2956         $format = DATE_CALC_FORMAT
2957     )
2958     {
2959         if (is_null($year)) {
2960             $year = Date_Calc::dateNow('%Y');
2961         }
2962         if (empty($month)) {
2963             $month = Date_Calc::dateNow('%m');
2964         }
2965
2966         $month_array = array();
2967
2968         // date for the first row, first column of calendar month
2969         if (DATE_CALC_BEGIN_WEEKDAY == 1) {
2970             if (Date_Calc::firstOfMonthWeekday($month, $year) == 0) {
2971                 $curr_day = Date_Calc::firstDayOfMonth($month, $year) - 6;
2972             } else {
2973                 $curr_day = Date_Calc::firstDayOfMonth($month, $year)
2974                     - Date_Calc::firstOfMonthWeekday($month, $year) + 1;
2975             }
2976         } else {
2977             $curr_day = (Date_Calc::firstDayOfMonth($month, $year)
2978                 - Date_Calc::firstOfMonthWeekday($month, $year));
2979         }
2980
2981         // number of days in this month
2982         $daysInMonth = Date_Calc::daysInMonth($month, $year);
2983
2984         $weeksInMonth = Date_Calc::weeksInMonth($month, $year);
2985         for ($row_counter = 0; $row_counter < $weeksInMonth; $row_counter++) {
2986             for ($column_counter = 0; $column_counter <= 6; $column_counter++) {
2987                 $month_array[$row_counter][$column_counter] =
2988                     Date_Calc::daysToDate($curr_day, $format);
2989                 $curr_day++;
2990             }
2991         }
2992
2993         return $month_array;
2994     }
2995
2996
2997     // }}}
2998     // {{{ getCalendarYear()
2999
3000     /**
3001      * Return a set of arrays to construct a calendar year for the given date
3002      *
3003      * @param int $year the year in four digit format, default current
3004      *                        local year
3005      * @param string $format the string indicating how to format the output
3006      *
3007      * @return   array      $year[$month][$row][$col]
3008      * @access   public
3009      * @static
3010      */
3011     public function getCalendarYear($year = null, $format = DATE_CALC_FORMAT)
3012     {
3013         if (is_null($year)) {
3014             $year = Date_Calc::dateNow('%Y');
3015         }
3016
3017         $year_array = array();
3018
3019         for ($curr_month = 0; $curr_month <= 11; $curr_month++) {
3020             $year_array[$curr_month] =
3021                 Date_Calc::getCalendarMonth(
3022                     $curr_month + 1,
3023                     $year,
3024                     $format
3025                 );
3026         }
3027
3028         return $year_array;
3029     }
3030
3031
3032     // }}}
3033     // {{{ prevDay()
3034
3035     /**
3036      * Returns date of day before given date
3037      *
3038      * @param int $day the day of the month, default is current local day
3039      * @param int $month the month, default is current local month
3040      * @param int $year the year in four digit format, default is current
3041      *                        local year
3042      * @param string $format the string indicating how to format the output
3043      *
3044      * @return   string     the date in the desired format
3045      * @access   public
3046      * @static
3047      */
3048     public function prevDay(
3049         $day = 0,
3050         $month = 0,
3051         $year = null,
3052         $format = DATE_CALC_FORMAT
3053     )
3054     {
3055         if (is_null($year)) {
3056             $year = Date_Calc::dateNow('%Y');
3057         }
3058         if (empty($month)) {
3059             $month = Date_Calc::dateNow('%m');
3060         }
3061         if (empty($day)) {
3062             $day = Date_Calc::dateNow('%d');
3063         }
3064
3065         return Date_Calc::addDays(-1, $day, $month, $year, $format);
3066     }
3067
3068
3069     // }}}
3070     // {{{ nextDay()
3071
3072     /**
3073      * Returns date of day after given date
3074      *
3075      * @param int $day the day of the month, default is current local day
3076      * @param int $month the month, default is current local month
3077      * @param int $year the year in four digit format, default is current
3078      *                        local year
3079      * @param string $format the string indicating how to format the output
3080      *
3081      * @return   string     the date in the desired format
3082      * @access   public
3083      * @static
3084      */
3085     public function nextDay(
3086         $day = 0,
3087         $month = 0,
3088         $year = null,
3089         $format = DATE_CALC_FORMAT
3090     )
3091     {
3092         if (is_null($year)) {
3093             $year = Date_Calc::dateNow('%Y');
3094         }
3095         if (empty($month)) {
3096             $month = Date_Calc::dateNow('%m');
3097         }
3098         if (empty($day)) {
3099             $day = Date_Calc::dateNow('%d');
3100         }
3101
3102         return Date_Calc::addDays(1, $day, $month, $year, $format);
3103     }
3104
3105
3106     // }}}
3107     // {{{ prevWeekday()
3108
3109     /**
3110      * Returns date of the previous weekday, skipping from Monday to Friday
3111      *
3112      * @param int $day the day of the month, default is current local day
3113      * @param int $month the month, default is current local month
3114      * @param int $year the year in four digit format, default is current
3115      *                        local year
3116      * @param string $format the string indicating how to format the output
3117      *
3118      * @return   string     the date in the desired format
3119      * @access   public
3120      * @static
3121      */
3122     public function prevWeekday(
3123         $day = 0,
3124         $month = 0,
3125         $year = null,
3126         $format = DATE_CALC_FORMAT
3127     )
3128     {
3129         if (is_null($year)) {
3130             $year = Date_Calc::dateNow('%Y');
3131         }
3132         if (empty($month)) {
3133             $month = Date_Calc::dateNow('%m');
3134         }
3135         if (empty($day)) {
3136             $day = Date_Calc::dateNow('%d');
3137         }
3138
3139         $days = Date_Calc::dateToDays($day, $month, $year);
3140         if (Date_Calc::dayOfWeek($day, $month, $year) == 1) {
3141             $days -= 3;
3142         } elseif (Date_Calc::dayOfWeek($day, $month, $year) == 0) {
3143             $days -= 2;
3144         } else {
3145             $days -= 1;
3146         }
3147
3148         return Date_Calc::daysToDate($days, $format);
3149     }
3150
3151
3152     // }}}
3153     // {{{ nextWeekday()
3154
3155     /**
3156      * Returns date of the next weekday of given date, skipping from
3157      * Friday to Monday
3158      *
3159      * @param int $day the day of the month, default is current local day
3160      * @param int $month the month, default is current local month
3161      * @param int $year the year in four digit format, default is current
3162      *                        local year
3163      * @param string $format the string indicating how to format the output
3164      *
3165      * @return   string     the date in the desired format
3166      * @access   public
3167      * @static
3168      */
3169     public function nextWeekday(
3170         $day = 0,
3171         $month = 0,
3172         $year = null,
3173         $format = DATE_CALC_FORMAT
3174     )
3175     {
3176         if (is_null($year)) {
3177             $year = Date_Calc::dateNow('%Y');
3178         }
3179         if (empty($month)) {
3180             $month = Date_Calc::dateNow('%m');
3181         }
3182         if (empty($day)) {
3183             $day = Date_Calc::dateNow('%d');
3184         }
3185
3186         $days = Date_Calc::dateToDays($day, $month, $year);
3187         if (Date_Calc::dayOfWeek($day, $month, $year) == 5) {
3188             $days += 3;
3189         } elseif (Date_Calc::dayOfWeek($day, $month, $year) == 6) {
3190             $days += 2;
3191         } else {
3192             $days += 1;
3193         }
3194
3195         return Date_Calc::daysToDate($days, $format);
3196     }
3197
3198
3199     // }}}
3200     // {{{ daysToPrevDayOfWeek()
3201
3202     /**
3203      * Returns 'Julian Day' of the previous specific day of the week
3204      * from the given date.
3205      *
3206      * @param int $dow the day of the week (0 = Sunday)
3207      * @param int $days 'Julian Day', i.e. the no of days since 1st
3208      *                          January, 4713 B.C.
3209      * @param bool $onorbefore if true and days are same, returns current day
3210      *
3211      * @return   int        'Julian Day', i.e. the no of days since 1st January,
3212      *                       4713 B.C.
3213      * @access   public
3214      * @static
3215      * @since    Method available since Release 1.5.0
3216      */
3217     public function daysToPrevDayOfWeek($dow, $days, $onorbefore = false)
3218     {
3219         $curr_weekday = Date_Calc::daysToDayOfWeek($days);
3220         if ($curr_weekday == $dow) {
3221             if ($onorbefore) {
3222                 return $days;
3223             } else {
3224                 return $days - 7;
3225             }
3226         } elseif ($curr_weekday < $dow) {
3227             return $days - 7 + $dow - $curr_weekday;
3228         } else {
3229             return $days - $curr_weekday + $dow;
3230         }
3231     }
3232
3233
3234     // }}}
3235     // {{{ prevDayOfWeek()
3236
3237     /**
3238      * Returns date of the previous specific day of the week
3239      * from the given date
3240      *
3241      * @param int $dow the day of the week (0 = Sunday)
3242      * @param int $day the day of the month, default is current local
3243      *                            day
3244      * @param int $month the month, default is current local month
3245      * @param int $year the year in four digit format, default is
3246      *                            current local year
3247      * @param string $format the string indicating how to format the output
3248      * @param bool $onorbefore if true and days are same, returns current day
3249      *
3250      * @return   string     the date in the desired format
3251      * @access   public
3252      * @static
3253      */
3254     public function prevDayOfWeek(
3255         $dow,
3256         $day = 0,
3257         $month = 0,
3258         $year = null,
3259         $format = DATE_CALC_FORMAT,
3260         $onorbefore = false
3261     )
3262     {
3263         if (is_null($year)) {
3264             $year = Date_Calc::dateNow('%Y');
3265         }
3266         if (empty($month)) {
3267             $month = Date_Calc::dateNow('%m');
3268         }
3269         if (empty($day)) {
3270             $day = Date_Calc::dateNow('%d');
3271         }
3272
3273         $days = Date_Calc::dateToDays($day, $month, $year);
3274         $days = Date_Calc::daysToPrevDayOfWeek($dow, $days, $onorbefore);
3275         return Date_Calc::daysToDate($days, $format);
3276     }
3277
3278
3279     // }}}
3280     // {{{ daysToNextDayOfWeek()
3281
3282     /**
3283      * Returns 'Julian Day' of the next specific day of the week
3284      * from the given date.
3285      *
3286      * @param int $dow the day of the week (0 = Sunday)
3287      * @param int $days 'Julian Day', i.e. the no of days since 1st
3288      *                         January, 4713 B.C.
3289      * @param bool $onorafter if true and days are same, returns current day
3290      *
3291      * @return   int        'Julian Day', i.e. the no of days since 1st January,
3292      *                       4713 B.C.
3293      * @access   public
3294      * @static
3295      * @since    Method available since Release 1.5.0
3296      */
3297     public function daysToNextDayOfWeek($dow, $days, $onorafter = false)
3298     {
3299         $curr_weekday = Date_Calc::daysToDayOfWeek($days);
3300         if ($curr_weekday == $dow) {
3301             if ($onorafter) {
3302                 return $days;
3303             } else {
3304                 return $days + 7;
3305             }
3306         } elseif ($curr_weekday > $dow) {
3307             return $days + 7 - $curr_weekday + $dow;
3308         } else {
3309             return $days + $dow - $curr_weekday;
3310         }
3311     }
3312
3313
3314     // }}}
3315     // {{{ nextDayOfWeek()
3316
3317     /**
3318      * Returns date of the next specific day of the week
3319      * from the given date
3320      *
3321      * @param int $dow the day of the week (0 = Sunday)
3322      * @param int $day the day of the month, default is current local
3323      *                           day
3324      * @param int $month the month, default is current local month
3325      * @param int $year the year in four digit format, default is
3326      *                           current local year
3327      * @param string $format the string indicating how to format the output
3328      * @param bool $onorafter if true and days are same, returns current day
3329      *
3330      * @return   string     the date in the desired format
3331      * @access   public
3332      * @static
3333      */
3334     public function nextDayOfWeek(
3335         $dow,
3336         $day = 0,
3337         $month = 0,
3338         $year = null,
3339         $format = DATE_CALC_FORMAT,
3340         $onorafter = false
3341     )
3342     {
3343         if (is_null($year)) {
3344             $year = Date_Calc::dateNow('%Y');
3345         }
3346         if (empty($month)) {
3347             $month = Date_Calc::dateNow('%m');
3348         }
3349         if (empty($day)) {
3350             $day = Date_Calc::dateNow('%d');
3351         }
3352
3353         $days = Date_Calc::dateToDays($day, $month, $year);
3354         $days = Date_Calc::daysToNextDayOfWeek($dow, $days, $onorafter);
3355         return Date_Calc::daysToDate($days, $format);
3356     }
3357
3358
3359     // }}}
3360     // {{{ prevDayOfWeekOnOrBefore()
3361
3362     /**
3363      * Returns date of the previous specific day of the week
3364      * on or before the given date
3365      *
3366      * @param int $dow the day of the week (0 = Sunday)
3367      * @param int $day the day of the month, default is current local day
3368      * @param int $month the month, default is current local month
3369      * @param int $year the year in four digit format, default is current
3370      *                        local year
3371      * @param string $format the string indicating how to format the output
3372      *
3373      * @return   string     the date in the desired format
3374      * @access   public
3375      * @static
3376      */
3377     public function prevDayOfWeekOnOrBefore(
3378         $dow,
3379         $day = 0,
3380         $month = 0,
3381         $year = null,
3382         $format = DATE_CALC_FORMAT
3383     )
3384     {
3385         return Date_Calc::prevDayOfWeek(
3386             $dow,
3387             $day,
3388             $month,
3389             $year,
3390             $format,
3391             true
3392         );
3393     }
3394
3395
3396     // }}}
3397     // {{{ nextDayOfWeekOnOrAfter()
3398
3399     /**
3400      * Returns date of the next specific day of the week
3401      * on or after the given date
3402      *
3403      * @param int $dow the day of the week (0 = Sunday)
3404      * @param int $day the day of the month, default is current local day
3405      * @param int $month the month, default is current local month
3406      * @param int $year the year in four digit format, default is current
3407      *                        local year
3408      * @param string $format the string indicating how to format the output
3409      *
3410      * @return   string     the date in the desired format
3411      * @access   public
3412      * @static
3413      */
3414     public function nextDayOfWeekOnOrAfter(
3415         $dow,
3416         $day = 0,
3417         $month = 0,
3418         $year = null,
3419         $format = DATE_CALC_FORMAT
3420     )
3421     {
3422         return Date_Calc::nextDayOfWeek(
3423             $dow,
3424             $day,
3425             $month,
3426             $year,
3427             $format,
3428             true
3429         );
3430     }
3431
3432
3433     // }}}
3434     // {{{ beginOfWeek()
3435
3436     /**
3437      * Find the month day of the beginning of week for given date,
3438      * using {@link DATE_CALC_BEGIN_WEEKDAY}
3439      *
3440      * Can return weekday of prev month.
3441      *
3442      * @param int $day the day of the month, default is current local day
3443      * @param int $month the month, default is current local month
3444      * @param int $year the year in four digit format, default is current
3445      *                        local year
3446      * @param string $format the string indicating how to format the output
3447      *
3448      * @return   string     the date in the desired format
3449      * @access   public
3450      * @static
3451      */
3452     public function beginOfWeek(
3453         $day = 0,
3454         $month = 0,
3455         $year = null,
3456         $format = DATE_CALC_FORMAT
3457     )
3458     {
3459         if (is_null($year)) {
3460             $year = Date_Calc::dateNow('%Y');
3461         }
3462         if (empty($month)) {
3463             $month = Date_Calc::dateNow('%m');
3464         }
3465         if (empty($day)) {
3466             $day = Date_Calc::dateNow('%d');
3467         }
3468
3469         $hn_days = Date_Calc::dateToDays($day, $month, $year);
3470         $this_weekday = Date_Calc::daysToDayOfWeek($hn_days);
3471         $interval = (7 - DATE_CALC_BEGIN_WEEKDAY + $this_weekday) % 7;
3472         return Date_Calc::daysToDate($hn_days - $interval, $format);
3473     }
3474
3475
3476     // }}}
3477     // {{{ endOfWeek()
3478
3479     /**
3480      * Find the month day of the end of week for given date,
3481      * using {@link DATE_CALC_BEGIN_WEEKDAY}
3482      *
3483      * Can return weekday of following month.
3484      *
3485      * @param int $day the day of the month, default is current local day
3486      * @param int $month the month, default is current local month
3487      * @param int $year the year in four digit format, default is current
3488      *                        local year
3489      * @param string $format the string indicating how to format the output
3490      *
3491      * @return   string     the date in the desired format
3492      * @access   public
3493      * @static
3494      */
3495     public function endOfWeek(
3496         $day = 0,
3497         $month = 0,
3498         $year = null,
3499         $format = DATE_CALC_FORMAT
3500     )
3501     {
3502         if (is_null($year)) {
3503             $year = Date_Calc::dateNow('%Y');
3504         }
3505         if (empty($month)) {
3506             $month = Date_Calc::dateNow('%m');
3507         }
3508         if (empty($day)) {
3509             $day = Date_Calc::dateNow('%d');
3510         }
3511
3512         $hn_days = Date_Calc::dateToDays($day, $month, $year);
3513         $this_weekday = Date_Calc::daysToDayOfWeek($hn_days);
3514         $interval = (6 + DATE_CALC_BEGIN_WEEKDAY - $this_weekday) % 7;
3515         return Date_Calc::daysToDate($hn_days + $interval, $format);
3516     }
3517
3518
3519     // }}}
3520     // {{{ beginOfPrevWeek()
3521
3522     /**
3523      * Find the month day of the beginning of week before given date,
3524      * using {@link DATE_CALC_BEGIN_WEEKDAY}
3525      *
3526      * Can return weekday of prev month.
3527      *
3528      * @param int $day the day of the month, default is current local day
3529      * @param int $month the month, default is current local month
3530      * @param int $year the year in four digit format, default is current
3531      *                        local year
3532      * @param string $format the string indicating how to format the output
3533      *
3534      * @return   string     the date in the desired format
3535      * @access   public
3536      * @static
3537      */
3538     public function beginOfPrevWeek(
3539         $day = 0,
3540         $month = 0,
3541         $year = null,
3542         $format = DATE_CALC_FORMAT
3543     )
3544     {
3545         if (is_null($year)) {
3546             $year = Date_Calc::dateNow('%Y');
3547         }
3548         if (empty($month)) {
3549             $month = Date_Calc::dateNow('%m');
3550         }
3551         if (empty($day)) {
3552             $day = Date_Calc::dateNow('%d');
3553         }
3554
3555         list($hn_pwyear, $hn_pwmonth, $hn_pwday) =
3556             explode(" ", Date_Calc::daysToDate(
3557                 Date_Calc::dateToDays(
3558                     $day,
3559                     $month,
3560                     $year
3561                 ) - 7,
3562                 '%Y %m %d'
3563             ));
3564         return Date_Calc::beginOfWeek(
3565             $hn_pwday,
3566             $hn_pwmonth,
3567             $hn_pwyear,
3568             $format
3569         );
3570     }
3571
3572
3573     // }}}
3574     // {{{ beginOfNextWeek()
3575
3576     /**
3577      * Find the month day of the beginning of week after given date,
3578      * using {@link DATE_CALC_BEGIN_WEEKDAY}
3579      *
3580      * Can return weekday of prev month.
3581      *
3582      * @param int $day the day of the month, default is current local day
3583      * @param int $month the month, default is current local month
3584      * @param int $year the year in four digit format, default is current
3585      *                        local year
3586      * @param string $format the string indicating how to format the output
3587      *
3588      * @return   string     the date in the desired format
3589      * @access   public
3590      * @static
3591      */
3592     public function beginOfNextWeek(
3593         $day = 0,
3594         $month = 0,
3595         $year = null,
3596         $format = DATE_CALC_FORMAT
3597     )
3598     {
3599         if (is_null($year)) {
3600             $year = Date_Calc::dateNow('%Y');
3601         }
3602         if (empty($month)) {
3603             $month = Date_Calc::dateNow('%m');
3604         }
3605         if (empty($day)) {
3606             $day = Date_Calc::dateNow('%d');
3607         }
3608
3609         list($hn_pwyear, $hn_pwmonth, $hn_pwday) =
3610             explode(
3611                 " ",
3612                 Date_Calc::daysToDate(
3613                     Date_Calc::dateToDays(
3614                         $day,
3615                         $month,
3616                         $year
3617                     ) + 7,
3618                     '%Y %m %d'
3619                 )
3620             );
3621         return Date_Calc::beginOfWeek(
3622             $hn_pwday,
3623             $hn_pwmonth,
3624             $hn_pwyear,
3625             $format
3626         );
3627     }
3628
3629
3630     // }}}
3631     // {{{ beginOfMonth()
3632
3633     /**
3634      * Return date of first day of month of given date
3635      *
3636      * @param int $month the month, default is current local month
3637      * @param int $year the year in four digit format, default is current
3638      *                        local year
3639      * @param string $format the string indicating how to format the output
3640      *
3641      * @return     string     the date in the desired format
3642      * @access     public
3643      * @static
3644      * @see        Date_Calc::beginOfMonthBySpan()
3645      * @deprecated Method deprecated in Release 1.4.4
3646      */
3647     public function beginOfMonth($month = 0, $year = null, $format = DATE_CALC_FORMAT)
3648     {
3649         if (is_null($year)) {
3650             $year = Date_Calc::dateNow('%Y');
3651         }
3652         if (empty($month)) {
3653             $month = Date_Calc::dateNow('%m');
3654         }
3655
3656         return Date_Calc::dateFormat(
3657             Date_Calc::getFirstDayOfMonth(
3658                 $month,
3659                 $year
3660             ),
3661             $month,
3662             $year,
3663             $format
3664         );
3665     }
3666
3667
3668     // }}}
3669     // {{{ endOfMonth()
3670
3671     /**
3672      * Return date of last day of month of given date
3673      *
3674      * @param int $month the month, default is current local month
3675      * @param int $year the year in four digit format, default is current
3676      *                        local year
3677      * @param string $format the string indicating how to format the output
3678      *
3679      * @return     string  the date in the desired format
3680      * @access     public
3681      * @static
3682      * @see        Date_Calc::beginOfMonthBySpan()
3683      * @since      Method available since Release 1.5.0
3684      * @deprecated Method deprecated in Release 1.5.0
3685      */
3686     public function endOfMonth($month = 0, $year = null, $format = DATE_CALC_FORMAT)
3687     {
3688         if (is_null($year)) {
3689             $year = Date_Calc::dateNow('%Y');
3690         }
3691         if (empty($month)) {
3692             $month = Date_Calc::dateNow('%m');
3693         }
3694
3695         return Date_Calc::daysToDate(
3696             Date_Calc::lastDayOfMonth($month, $year),
3697             $format
3698         );
3699     }
3700
3701
3702     // }}}
3703     // {{{ beginOfPrevMonth()
3704
3705     /**
3706      * Returns date of the first day of previous month of given date
3707      *
3708      * @param mixed $dummy irrelevant parameter
3709      * @param int $month the month, default is current local month
3710      * @param int $year the year in four digit format, default is current
3711      *                        local year
3712      * @param string $format the string indicating how to format the output
3713      *
3714      * @return     string     the date in the desired format
3715      * @access     public
3716      * @static
3717      * @see        Date_Calc::beginOfMonthBySpan()
3718      * @deprecated Method deprecated in Release 1.4.4
3719      */
3720     public function beginOfPrevMonth(
3721         $dummy = null,
3722         $month = 0,
3723         $year = null,
3724         $format = DATE_CALC_FORMAT
3725     )
3726     {
3727         if (is_null($year)) {
3728             $year = Date_Calc::dateNow('%Y');
3729         }
3730         if (empty($month)) {
3731             $month = Date_Calc::dateNow('%m');
3732         }
3733
3734         list($hn_pmyear, $hn_prevmonth) = Date_Calc::prevMonth($month, $year);
3735         return Date_Calc::dateFormat(
3736             Date_Calc::getFirstDayOfMonth(
3737                 $hn_prevmonth,
3738                 $hn_pmyear
3739             ),
3740             $hn_prevmonth,
3741             $hn_pmyear,
3742             $format
3743         );
3744     }
3745
3746
3747     // }}}
3748     // {{{ endOfPrevMonth()
3749
3750     /**
3751      * Returns date of the last day of previous month for given date
3752      *
3753      * @param mixed $dummy irrelevant parameter
3754      * @param int $month the month, default is current local month
3755      * @param int $year the year in four digit format, default is current
3756      *                        local year
3757      * @param string $format the string indicating how to format the output
3758      *
3759      * @return     string     the date in the desired format
3760      * @access     public
3761      * @static
3762      * @see        Date_Calc::endOfMonthBySpan()
3763      * @deprecated Method deprecated in Release 1.4.4
3764      */
3765     public function endOfPrevMonth(
3766         $dummy = null,
3767         $month = 0,
3768         $year = null,
3769         $format = DATE_CALC_FORMAT
3770     )
3771     {
3772         if (is_null($year)) {
3773             $year = Date_Calc::dateNow('%Y');
3774         }
3775         if (empty($month)) {
3776             $month = Date_Calc::dateNow('%m');
3777         }
3778
3779         return Date_Calc::daysToDate(
3780             Date_Calc::firstDayOfMonth(
3781                 $month,
3782                 $year
3783             ) - 1,
3784             $format
3785         );
3786     }
3787
3788
3789     // }}}
3790     // {{{ beginOfNextMonth()
3791
3792     /**
3793      * Returns date of begin of next month of given date
3794      *
3795      * @param mixed $dummy irrelevant parameter
3796      * @param int $month the month, default is current local month
3797      * @param int $year the year in four digit format, default is current
3798      *                        local year
3799      * @param string $format the string indicating how to format the output
3800      *
3801      * @return     string     the date in the desired format
3802      * @access     public
3803      * @static
3804      * @see        Date_Calc::beginOfMonthBySpan()
3805      * @deprecated Method deprecated in Release 1.4.4
3806      */
3807     public function beginOfNextMonth(
3808         $dummy = null,
3809         $month = 0,
3810         $year = null,
3811         $format = DATE_CALC_FORMAT
3812     )
3813     {
3814         if (is_null($year)) {
3815             $year = Date_Calc::dateNow('%Y');
3816         }
3817         if (empty($month)) {
3818             $month = Date_Calc::dateNow('%m');
3819         }
3820
3821         list($hn_nmyear, $hn_nextmonth) = Date_Calc::nextMonth($month, $year);
3822         return Date_Calc::dateFormat(
3823             Date_Calc::getFirstDayOfMonth(
3824                 $hn_nextmonth,
3825                 $hn_nmyear
3826             ),
3827             $hn_nextmonth,
3828             $hn_nmyear,
3829             $format
3830         );
3831     }
3832
3833
3834     // }}}
3835     // {{{ endOfNextMonth()
3836
3837     /**
3838      * Returns date of the last day of next month of given date
3839      *
3840      * @param mixed $dummy irrelevant parameter
3841      * @param int $month the month, default is current local month
3842      * @param int $year the year in four digit format, default is current
3843      *                        local year
3844      * @param string $format the string indicating how to format the output
3845      *
3846      * @return     string     the date in the desired format
3847      * @access     public
3848      * @static
3849      * @see        Date_Calc::endOfMonthBySpan()
3850      * @deprecated Method deprecated in Release 1.4.4
3851      */
3852     public function endOfNextMonth(
3853         $dummy = null,
3854         $month = 0,
3855         $year = null,
3856         $format = DATE_CALC_FORMAT
3857     )
3858     {
3859         if (is_null($year)) {
3860             $year = Date_Calc::dateNow('%Y');
3861         }
3862         if (empty($month)) {
3863             $month = Date_Calc::dateNow('%m');
3864         }
3865
3866         list($hn_nmyear, $hn_nextmonth) = Date_Calc::nextMonth($month, $year);
3867         return Date_Calc::daysToDate(
3868             Date_Calc::lastDayOfMonth(
3869                 $hn_nextmonth,
3870                 $hn_nmyear
3871             ),
3872             $format
3873         );
3874     }
3875
3876
3877     // }}}
3878     // {{{ beginOfMonthBySpan()
3879
3880     /**
3881      * Returns date of the first day of the month in the number of months
3882      * from the given date
3883      *
3884      * @param int $months the number of months from the date provided.
3885      *                        Positive numbers go into the future.
3886      *                        Negative numbers go into the past.
3887      *                        Nought is the month presented in $month.
3888      * @param string $month the month, default is current local month
3889      * @param string $year the year in four digit format, default is the
3890      *                        current local year
3891      * @param string $format the string indicating how to format the output
3892      *
3893      * @return   string     the date in the desired format
3894      * @access   public
3895      * @static
3896      * @since    Method available since Release 1.4.4
3897      */
3898     public function beginOfMonthBySpan(
3899         $months = 0,
3900         $month = 0,
3901         $year = null,
3902         $format = DATE_CALC_FORMAT
3903     )
3904     {
3905         if (is_null($year)) {
3906             $year = Date_Calc::dateNow('%Y');
3907         }
3908         if (empty($month)) {
3909             $month = Date_Calc::dateNow('%m');
3910         }
3911
3912         return Date_Calc::addMonths(
3913             $months,
3914             Date_Calc::getFirstDayOfMonth($month, $year),
3915             $month,
3916             $year,
3917             $format
3918         );
3919     }
3920
3921
3922     // }}}
3923     // {{{ endOfMonthBySpan()
3924
3925     /**
3926      * Returns date of the last day of the month in the number of months
3927      * from the given date
3928      *
3929      * @param int $months the number of months from the date provided.
3930      *                        Positive numbers go into the future.
3931      *                        Negative numbers go into the past.
3932      *                        Nought is the month presented in $month.
3933      * @param string $month the month, default is current local month
3934      * @param string $year the year in four digit format, default is the
3935      *                        current local year
3936      * @param string $format the string indicating how to format the output
3937      *
3938      * @return   string  the date in the desired format
3939      * @access   public
3940      * @static
3941      * @since    Method available since Release 1.4.4
3942      */
3943     public function endOfMonthBySpan(
3944         $months = 0,
3945         $month = 0,
3946         $year = null,
3947         $format = DATE_CALC_FORMAT
3948     )
3949     {
3950         if (is_null($year)) {
3951             $year = Date_Calc::dateNow('%Y');
3952         }
3953         if (empty($month)) {
3954             $month = Date_Calc::dateNow('%m');
3955         }
3956
3957         $hn_days = Date_Calc::addMonthsToDays(
3958                 $months + 1,
3959                 Date_Calc::firstDayOfMonth($month, $year)
3960             ) - 1;
3961         return Date_Calc::daysToDate($hn_days, $format);
3962     }
3963
3964
3965     // }}}
3966     // {{{ firstOfMonthWeekday()
3967
3968     /**
3969      * Find the day of the week for the first of the month of given date
3970      *
3971      * @param int $month the month, default is current local month
3972      * @param int $year the year in four digit format, default is current
3973      *                    local year
3974      *
3975      * @return   int        number of weekday for the first day, 0=Sunday
3976      * @access   public
3977      * @static
3978      */
3979     public function firstOfMonthWeekday($month = 0, $year = null)
3980     {
3981         if (is_null($year)) {
3982             $year = Date_Calc::dateNow('%Y');
3983         }
3984         if (empty($month)) {
3985             $month = Date_Calc::dateNow('%m');
3986         }
3987         return Date_Calc::daysToDayOfWeek(Date_Calc::firstDayOfMonth(
3988             $month,
3989             $year
3990         ));
3991     }
3992
3993
3994     // }}}
3995     // {{{ nWeekdayOfMonth()
3996
3997     /**
3998      * Calculates the date of the Nth weekday of the month,
3999      * such as the second Saturday of January 2000
4000      *
4001      * @param int $week the number of the week to get
4002      *                       (1 to 5.  Also can be 'last'.)
4003      * @param int $dow the day of the week (0 = Sunday)
4004      * @param int $month the month
4005      * @param int $year the year.  Use the complete year instead of the
4006      *                        abbreviated version.  E.g. use 2005, not 05.
4007      * @param string $format the string indicating how to format the output
4008      *
4009      * @return   string     the date in the desired format
4010      * @access   public
4011      * @static
4012      */
4013     public function nWeekDayOfMonth(
4014         $week,
4015         $dow,
4016         $month,
4017         $year,
4018         $format = DATE_CALC_FORMAT
4019     )
4020     {
4021         if ((is_numeric($week) && ($week < 1 || $week > 5)) ||
4022             (!is_numeric($week) && $week != "last")
4023         ) {
4024             return PEAR::raiseError("Invalid week value '$week', only 1-5 or 'last' accepted");
4025         }
4026
4027         if ($dow < 0 || $dow > 6) {
4028             return PEAR::raiseError("Invalid dow value '$dow', only 0-6 accepted");
4029         }
4030
4031         if ($month < 1 || $month > 12) {
4032             return PEAR::raiseError("Invalid month value '$month'");
4033         }
4034
4035         if (is_numeric($week)) {
4036             // the weekday of first day of month "1"
4037             $DOW1 = Date_Calc::dayOfWeek(1, $month, $year);
4038
4039             // finds the sunday
4040             $sunday = ($week - 1) * 7 + 1;
4041             if ($DOW1 > 0) {
4042                 $sunday += (7 - $DOW1);
4043             }
4044
4045             // adjust the sunday with dow addition
4046             $wdate = $sunday + $dow;
4047             if ($wdate > Date_Calc::daysInMonth($month, $year)) {
4048                 return -1;
4049             } else {
4050                 return Date_Calc::dateFormat($wdate, $month, $year, $format);
4051             }
4052         } elseif ($week == 'last' && $dow < 7) {
4053             $lastday = Date_Calc::daysInMonth($month, $year);
4054             $lastdow = Date_Calc::dayOfWeek($lastday, $month, $year);
4055             $diff = $dow - $lastdow;
4056             if ($diff > 0) {
4057                 return Date_Calc::dateFormat(
4058                     $lastday - (7 - $diff),
4059                     $month,
4060                     $year,
4061                     $format
4062                 );
4063             } else {
4064                 return Date_Calc::dateFormat(
4065                     $lastday + $diff,
4066                     $month,
4067                     $year,
4068                     $format
4069                 );
4070             }
4071         } else {
4072             return -1;
4073         }
4074     }
4075
4076
4077     // }}}
4078     // {{{ isValidDate()
4079
4080     /**
4081      * Returns true for valid date, false for invalid date
4082      *
4083      * Uses the proleptic Gregorian calendar, with the year 0 (1 B.C.)
4084      * assumed to be valid and also assumed to be a leap year.
4085      *
4086      * @param int $day the day of the month
4087      * @param int $month the month
4088      * @param int $year the year.  Use the complete year instead of the
4089      *                    abbreviated version.  E.g. use 2005, not 05.
4090      *
4091      * @return   bool
4092      * @access   public
4093      * @static
4094      */
4095     public function isValidDate($day, $month, $year)
4096     {
4097         if ($day < 1 || $month < 1 || $month > 12) {
4098             return false;
4099         }
4100         if ($month == 2) {
4101             if (Date_Calc::isLeapYearGregorian($year)) {
4102                 return $day <= 29;
4103             } else {
4104                 return $day <= 28;
4105             }
4106         } elseif ($month == 4 || $month == 6 || $month == 9 || $month == 11) {
4107             return $day <= 30;
4108         } else {
4109             return $day <= 31;
4110         }
4111     }
4112
4113
4114     // }}}
4115     // {{{ isLeapYearGregorian()
4116
4117     /**
4118      * Returns true for a leap year, else false
4119      *
4120      * Uses the proleptic Gregorian calendar.  The year 0 (1 B.C.) is
4121      * assumed in this algorithm to be a leap year.  The function is
4122      * valid for all years, positive and negative.
4123      *
4124      * @param int $year the year.  Use the complete year instead of the
4125      *                   abbreviated version.  E.g. use 2005, not 05.
4126      *
4127      * @return   bool
4128      * @access   public
4129      * @static
4130      * @since    Method available since Release 1.5.0
4131      */
4132     public function isLeapYearGregorian($year = null)
4133     {
4134         if (is_null($year)) {
4135             $year = Date_Calc::dateNow('%Y');
4136         }
4137         return (($year % 4 == 0) &&
4138                 ($year % 100 != 0)) ||
4139             ($year % 400 == 0);
4140     }
4141
4142
4143     // }}}
4144     // {{{ isLeapYearJulian()
4145
4146     /**
4147      * Returns true for a leap year, else false
4148      *
4149      * Uses the proleptic Julian calendar.  The year 0 (1 B.C.) is
4150      * assumed in this algorithm to be a leap year.  The function is
4151      * valid for all years, positive and negative.
4152      *
4153      * @param int $year the year.  Use the complete year instead of the
4154      *                   abbreviated version.  E.g. use 2005, not 05.
4155      *
4156      * @return   boolean
4157      * @access   public
4158      * @static
4159      * @since    Method available since Release 1.5.0
4160      */
4161     public function isLeapYearJulian($year = null)
4162     {
4163         if (is_null($year)) {
4164             $year = Date_Calc::dateNow('%Y');
4165         }
4166         return $year % 4 == 0;
4167     }
4168
4169
4170     // }}}
4171     // {{{ isLeapYear()
4172
4173     /**
4174      * Returns true for a leap year, else false
4175      *
4176      * @param int $year the year.  Use the complete year instead of the
4177      *                   abbreviated version.  E.g. use 2005, not 05.
4178      *
4179      * @return   boolean
4180      * @access   public
4181      * @static
4182      */
4183     public function isLeapYear($year = null)
4184     {
4185         if (is_null($year)) {
4186             $year = Date_Calc::dateNow('%Y');
4187         }
4188         if ($year < 1582) {
4189             // pre Gregorio XIII - 1582
4190             return Date_Calc::isLeapYearJulian($year);
4191         } else {
4192             // post Gregorio XIII - 1582
4193             return Date_Calc::isLeapYearGregorian($year);
4194         }
4195     }
4196
4197
4198     // }}}
4199     // {{{ isFutureDate()
4200
4201     /**
4202      * Determines if given date is a future date from now
4203      *
4204      * @param int $day the day of the month
4205      * @param int $month the month
4206      * @param int $year the year.  Use the complete year instead of the
4207      *                    abbreviated version.  E.g. use 2005, not 05.
4208      *
4209      * @return   bool
4210      * @access   public
4211      * @static
4212      */
4213     public function isFutureDate($day, $month, $year)
4214     {
4215         $this_year = Date_Calc::dateNow('%Y');
4216         $this_month = Date_Calc::dateNow('%m');
4217         $this_day = Date_Calc::dateNow('%d');
4218
4219         if ($year > $this_year) {
4220             return true;
4221         } elseif ($year == $this_year) {
4222             if ($month > $this_month) {
4223                 return true;
4224             } elseif ($month == $this_month) {
4225                 if ($day > $this_day) {
4226                     return true;
4227                 }
4228             }
4229         }
4230         return false;
4231     }
4232
4233
4234     // }}}
4235     // {{{ isPastDate()
4236
4237     /**
4238      * Determines if given date is a past date from now
4239      *
4240      * @param int $day the day of the month
4241      * @param int $month the month
4242      * @param int $year the year.  Use the complete year instead of the
4243      *                    abbreviated version.  E.g. use 2005, not 05.
4244      *
4245      * @return   boolean
4246      * @access   public
4247      * @static
4248      */
4249     public function isPastDate($day, $month, $year)
4250     {
4251         $this_year = Date_Calc::dateNow('%Y');
4252         $this_month = Date_Calc::dateNow('%m');
4253         $this_day = Date_Calc::dateNow('%d');
4254
4255         if ($year < $this_year) {
4256             return true;
4257         } elseif ($year == $this_year) {
4258             if ($month < $this_month) {
4259                 return true;
4260             } elseif ($month == $this_month) {
4261                 if ($day < $this_day) {
4262                     return true;
4263                 }
4264             }
4265         }
4266         return false;
4267     }
4268
4269
4270     // }}}
4271     // {{{ dateDiff()
4272
4273     /**
4274      * Returns number of days between two given dates
4275      *
4276      * @param int $day1 the day of the month
4277      * @param int $month1 the month
4278      * @param int $year1 the year.  Use the complete year instead of the
4279      *                     abbreviated version.  E.g. use 2005, not 05.
4280      * @param int $day2 the day of the month
4281      * @param int $month2 the month
4282      * @param int $year2 the year.  Use the complete year instead of the
4283      *                     abbreviated version.  E.g. use 2005, not 05.
4284      *
4285      * @return   int        the absolute number of days between the two dates.
4286      *                       If an error occurs, -1 is returned.
4287      * @access   public
4288      * @static
4289      */
4290     public function dateDiff($day1, $month1, $year1, $day2, $month2, $year2)
4291     {
4292         if (!Date_Calc::isValidDate($day1, $month1, $year1)) {
4293             return -1;
4294         }
4295         if (!Date_Calc::isValidDate($day2, $month2, $year2)) {
4296             return -1;
4297         }
4298         return abs(Date_Calc::dateToDays($day1, $month1, $year1)
4299             - Date_Calc::dateToDays($day2, $month2, $year2));
4300     }
4301
4302
4303     // }}}
4304     // {{{ compareDates()
4305
4306     /**
4307      * Compares two dates
4308      *
4309      * @param int $day1 the day of the month
4310      * @param int $month1 the month
4311      * @param int $year1 the year.  Use the complete year instead of the
4312      *                     abbreviated version.  E.g. use 2005, not 05.
4313      * @param int $day2 the day of the month
4314      * @param int $month2 the month
4315      * @param int $year2 the year.  Use the complete year instead of the
4316      *                     abbreviated version.  E.g. use 2005, not 05.
4317      *
4318      * @return   int        0 if the dates are equal. 1 if date 1 is later, -1
4319      *                       if date 1 is earlier.
4320      * @access   public
4321      * @static
4322      */
4323     public static function compareDates($day1, $month1, $year1, $day2, $month2, $year2)
4324     {
4325         $ndays1 = Date_Calc::dateToDays($day1, $month1, $year1);
4326         $ndays2 = Date_Calc::dateToDays($day2, $month2, $year2);
4327         if ($ndays1 == $ndays2) {
4328             return 0;
4329         }
4330         return ($ndays1 > $ndays2) ? 1 : -1;
4331     }
4332
4333
4334     // }}}
4335     // {{{ round()
4336
4337     /**
4338      * Rounds the date according to the specified precision
4339      *
4340      * The precision parameter must be one of the following constants:
4341      *
4342      *   - {@link DATE_PRECISION_YEAR}
4343      *   - {@link DATE_PRECISION_MONTH}
4344      *   - {@link DATE_PRECISION_DAY}
4345      *   - {@link DATE_PRECISION_HOUR}
4346      *   - {@link DATE_PRECISION_10MINUTES}
4347      *   - {@link DATE_PRECISION_MINUTE}
4348      *   - {@link DATE_PRECISION_10SECONDS}
4349      *   - {@link DATE_PRECISION_SECOND}
4350      *
4351      * The precision can also be specified as an integral offset from
4352      * one of these constants, where the offset reflects a precision
4353      * of 10 to the power of the offset greater than the constant.
4354      * For example:
4355      *
4356      *   - <b>(DATE_PRECISION_YEAR - 1)</b> rounds the date to the nearest 10
4357      *                                      years
4358      *   - <b>(DATE_PRECISION_YEAR - 3)</b> rounds the date to the nearest 1000
4359      *                                      years
4360      *   - <b>(DATE_PRECISION_SECOND + 1)</b> rounds the date to 1 decimal
4361      *                                        point of a second
4362      *   - <b>(DATE_PRECISION_SECOND + 1)</b> rounds the date to 3 decimal
4363      *                                        points of a second
4364      *   - <b>(DATE_PRECISION_SECOND + 1)</b> rounds the date to the nearest 10
4365      *                                        seconds (thus it is equivalent to
4366      *                                        <b>DATE_PRECISION_10SECONDS</b>)
4367      *
4368      * N.B. This function requires a time in UTC if both the precision is at
4369      * least DATE_PRECISION_SECOND and leap seconds are being counted, otherwise
4370      * any local time is acceptable.
4371      *
4372      * @param int $pn_precision a 'DATE_PRECISION_*' constant (defaults to
4373      *                             {@link DATE_PRECISION_DAY})
4374      * @param int $pn_day the day of the month
4375      * @param int $pn_month the month
4376      * @param int $pn_year the year
4377      * @param int $pn_hour the hour
4378      * @param int $pn_minute the minute
4379      * @param mixed $pn_second the second as integer or float
4380      * @param bool $pb_countleap whether to count leap seconds (defaults to
4381      *                             {@link DATE_COUNT_LEAP_SECONDS})
4382      *
4383      * @return   array      array of year, month, day, hour, minute, second
4384      * @access   public
4385      * @static
4386      * @since    Method available since Release 1.5.0
4387      */
4388     public function round(
4389         $pn_precision,
4390         $pn_day,
4391         $pn_month,
4392         $pn_year,
4393         $pn_hour = 0,
4394         $pn_minute = 0,
4395         $pn_second = 0,
4396         $pb_countleap = DATE_COUNT_LEAP_SECONDS
4397     )
4398     {
4399         if ($pn_precision <= DATE_PRECISION_YEAR) {
4400             $hn_month = 0;
4401             $hn_day = 0;
4402             $hn_hour = 0;
4403             $hn_minute = 0;
4404             $hn_second = 0;
4405
4406             if ($pn_precision < DATE_PRECISION_YEAR) {
4407                 $hn_year = round($pn_year, $pn_precision - DATE_PRECISION_YEAR);
4408             } else {
4409                 // Check part-year:
4410                 //
4411                 $hn_midyear = (Date_Calc::firstDayOfYear($pn_year + 1) -
4412                         Date_Calc::firstDayOfYear($pn_year)) / 2;
4413                 if (($hn_days = Date_Calc::dayOfYear(
4414                         $pn_day,
4415                         $pn_month,
4416                         $pn_year
4417                     )) <=
4418                     $hn_midyear - 1) {
4419                     $hn_year = $pn_year;
4420                 } elseif ($hn_days >= $hn_midyear) {
4421                     // Round up:
4422                     //
4423                     $hn_year = $pn_year + 1;
4424                 } else {
4425                     // Take time into account:
4426                     //
4427                     $hn_partday = Date_Calc::secondsPastMidnight(
4428                             $pn_hour,
4429                             $pn_minute,
4430                             $pn_second
4431                         ) /
4432                         86400;
4433                     if ($hn_partday >= $hn_midyear - $hn_days) {
4434                         // Round up:
4435                         //
4436                         $hn_year = $pn_year + 1;
4437                     } else {
4438                         $hn_year = $pn_year;
4439                     }
4440                 }
4441             }
4442         } elseif ($pn_precision == DATE_PRECISION_MONTH) {
4443             $hn_year = $pn_year;
4444             $hn_day = 0;
4445             $hn_hour = 0;
4446             $hn_minute = 0;
4447             $hn_second = 0;
4448
4449             $hn_firstofmonth = Date_Calc::firstDayOfMonth($pn_month, $pn_year);
4450             $hn_midmonth = (Date_Calc::lastDayOfMonth($pn_month, $pn_year) +
4451                     1 -
4452                     $hn_firstofmonth) / 2;
4453             if (($hn_days = Date_Calc::dateToDays(
4454                         $pn_day,
4455                         $pn_month,
4456                         $pn_year
4457                     ) -
4458                     $hn_firstofmonth) <= $hn_midmonth - 1) {
4459                 $hn_month = $pn_month;
4460             } elseif ($hn_days >= $hn_midmonth) {
4461                 // Round up:
4462                 //
4463                 list($hn_year, $hn_month) = Date_Calc::nextMonth(
4464                     $pn_month,
4465                     $pn_year
4466                 );
4467             } else {
4468                 // Take time into account:
4469                 //
4470                 $hn_partday = Date_Calc::secondsPastMidnight(
4471                         $pn_hour,
4472                         $pn_minute,
4473                         $pn_second
4474                     ) /
4475                     86400;
4476                 if ($hn_partday >= $hn_midmonth - $hn_days) {
4477                     // Round up:
4478                     //
4479                     list($hn_year, $hn_month) = Date_Calc::nextMonth(
4480                         $pn_month,
4481                         $pn_year
4482                     );
4483                 } else {
4484                     $hn_month = $pn_month;
4485                 }
4486             }
4487         } elseif ($pn_precision == DATE_PRECISION_DAY) {
4488             $hn_year = $pn_year;
4489             $hn_month = $pn_month;
4490             $hn_hour = 0;
4491             $hn_minute = 0;
4492             $hn_second = 0;
4493
4494             if (Date_Calc::secondsPastMidnight(
4495                     $pn_hour,
4496                     $pn_minute,
4497                     $pn_second
4498                 ) >= 43200) {
4499                 // Round up:
4500                 //
4501                 list($hn_year, $hn_month, $hn_day) =
4502                     explode(" ", Date_Calc::nextDay(
4503                         $pn_day,
4504                         $pn_month,
4505                         $pn_year,
4506                         "%Y %m %d"
4507                     ));
4508             } else {
4509                 $hn_day = $pn_day;
4510             }
4511         } elseif ($pn_precision == DATE_PRECISION_HOUR) {
4512             $hn_year = $pn_year;
4513             $hn_month = $pn_month;
4514             $hn_day = $pn_day;
4515             $hn_minute = 0;
4516             $hn_second = 0;
4517
4518             if (Date_Calc::secondsPastTheHour($pn_minute, $pn_second) >= 1800) {
4519                 // Round up:
4520                 //
4521                 list($hn_year, $hn_month, $hn_day, $hn_hour) =
4522                     Date_Calc::addHours(
4523                         1,
4524                         $pn_day,
4525                         $pn_month,
4526                         $pn_year,
4527                         $pn_hour
4528                     );
4529             } else {
4530                 $hn_hour = $pn_hour;
4531             }
4532         } elseif ($pn_precision <= DATE_PRECISION_MINUTE) {
4533             $hn_year = $pn_year;
4534             $hn_month = $pn_month;
4535             $hn_day = $pn_day;
4536             $hn_hour = $pn_hour;
4537             $hn_second = 0;
4538
4539             if ($pn_precision < DATE_PRECISION_MINUTE) {
4540                 $hn_minute = round(
4541                     $pn_minute,
4542                     $pn_precision - DATE_PRECISION_MINUTE
4543                 );
4544             } else {
4545                 // Check seconds:
4546                 //
4547                 if ($pn_second >= 30) {
4548                     // Round up:
4549                     //
4550                     list($hn_year,
4551                         $hn_month,
4552                         $hn_day,
4553                         $hn_hour,
4554                         $hn_minute) =
4555                         Date_Calc::addMinutes(
4556                             1,
4557                             $pn_day,
4558                             $pn_month,
4559                             $pn_year,
4560                             $pn_hour,
4561                             $pn_minute
4562                         );
4563                 } else {
4564                     $hn_minute = $pn_minute;
4565                 }
4566             }
4567         } else {
4568             // Precision is at least (DATE_PRECISION_SECOND - 1):
4569             //
4570             $hn_year = $pn_year;
4571             $hn_month = $pn_month;
4572             $hn_day = $pn_day;
4573             $hn_hour = $pn_hour;
4574             $hn_minute = $pn_minute;
4575
4576             $hn_second = round(
4577                 $pn_second,
4578                 $pn_precision - DATE_PRECISION_SECOND
4579             );
4580
4581             if (fmod($hn_second, 1) == 0.0) {
4582                 $hn_second = (int)$hn_second;
4583
4584                 if ($hn_second != intval($pn_second)) {
4585                     list($hn_year,
4586                         $hn_month,
4587                         $hn_day,
4588                         $hn_hour,
4589                         $hn_minute,
4590                         $hn_second) =
4591                         Date_Calc::addSeconds(
4592                             $hn_second - intval($pn_second),
4593                             $pn_day,
4594                             $pn_month,
4595                             $pn_year,
4596                             $pn_hour,
4597                             $pn_minute,
4598                             intval($pn_second),
4599                             $pn_precision >=
4600                             DATE_PRECISION_SECOND &&
4601                             $pb_countleap
4602                         );
4603                     //
4604                     // (N.B. if rounded to nearest 10 seconds,
4605                     // user does not expect seconds to be '60')
4606                 }
4607             }
4608         }
4609
4610         return array((int)$hn_year,
4611             (int)$hn_month,
4612             (int)$hn_day,
4613             (int)$hn_hour,
4614             (int)$hn_minute,
4615             $hn_second);
4616     }
4617
4618
4619     // }}}
4620     // {{{ roundSeconds()
4621
4622     /**
4623      * Rounds seconds up or down to the nearest specified unit
4624      *
4625      * @param int $pn_precision number of digits after the decimal point
4626      * @param int $pn_day the day of the month
4627      * @param int $pn_month the month
4628      * @param int $pn_year the year
4629      * @param int $pn_hour the hour
4630      * @param int $pn_minute the minute
4631      * @param mixed $pn_second the second as integer or float
4632      * @param bool $pb_countleap whether to count leap seconds (defaults to
4633      *                             DATE_COUNT_LEAP_SECONDS)
4634      *
4635      * @return   array      array of year, month, day, hour, minute, second
4636      * @access   public
4637      * @static
4638      * @since    Method available since Release 1.5.0
4639      */
4640     public function roundSeconds(
4641         $pn_precision,
4642         $pn_day,
4643         $pn_month,
4644         $pn_year,
4645         $pn_hour,
4646         $pn_minute,
4647         $pn_second,
4648         $pb_countleap = DATE_COUNT_LEAP_SECONDS
4649     )
4650     {
4651         return Date_Calc::round(
4652             DATE_PRECISION_SECOND + $pn_precision,
4653             $pn_day,
4654             $pn_month,
4655             $pn_year,
4656             $pn_hour,
4657             $pn_minute,
4658             $pn_second
4659         );
4660     }
4661
4662
4663     // }}}
4664     // {{{ isoWeekToDate()
4665
4666     /**
4667      * Converts the Week number and Day-of-Week to Date
4668      *
4669      * Calculation algorithm taken from
4670      * {@link http://www.merlyn.demon.co.uk/weekcalc.htm}.
4671      *
4672      * @param int $dow day of week from 1 (Monday) to 7 (Sunday)
4673      * @param int $week number of week from 1 to 53
4674      * @param int $year four digits of year
4675      * @param string $format the output format
4676      *
4677      * @return  string formatted date
4678      * @access  public
4679      * @static
4680      * @since   Method available since Release 1.5.0a2
4681      */
4682     public function isoWeekToDate($dow, $week, $year, $format = DATE_CALC_FORMAT)
4683     {
4684         // validates the week number
4685         list(, $nweeks) = Date_Calc::isoWeekDate(28, 12, $year);
4686         if ($week > $nweeks) {
4687             return PEAR::raiseError(
4688                 "ISO week number for $year cannot be greater than $nweeks",
4689                 DATE_ERROR_INVALIDDATE
4690             );
4691         }
4692
4693         // validates the day of week
4694         if ($dow < 1 || $dow > 7) {
4695             return PEAR::raiseError(
4696                 "ISO day of week must be between 1 and 7",
4697                 DATE_ERROR_INVALIDDATE
4698             );
4699         }
4700
4701         // finds the day of week of January 4th.
4702         $jan4th = Date_Calc::dayOfWeek(4, 1, $year);
4703         if ($jan4th == 0) {
4704             $jan4th = 7;
4705         }
4706
4707         // offset to the monday of that week
4708         $offset = -($jan4th - 1);
4709
4710         // increment the days starting from january 4th.
4711         $days = Date_Calc::dateToDays(1, 1, $year) + $offset + 7 * ($week - 1) + ($dow - 1) + 3;
4712
4713         return Date_Calc::daysToDate($days, $format);
4714     }
4715
4716     // }}}
4717 }
4718
4719 // }}}
4720
4721 /*
4722  * Local variables:
4723  * mode: php
4724  * tab-width: 4
4725  * c-basic-offset: 4
4726  * c-hanging-comment-ender-p: nil
4727  * End:
4728  */