2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
7 * Generic date handling class for PEAR
9 * Handles time zones and changes from local standard to local Summer
10 * time (daylight-saving time) through the {@link Date_TimeZone} class.
11 * Supports several operations from {@link Date_Calc} on Date objects.
13 * PHP versions 4 and 5
17 * Copyright (c) 1997-2008 Baba Buehler, Pierre-Alain Joye, Firman
18 * Wandayandi, C.A. Woodcock
19 * All rights reserved.
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted under the terms of the BSD License.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
37 * @category Date and Time
39 * @author Baba Buehler <baba@babaz.com>
40 * @author Pierre-Alain Joye <pajoye@php.net>
41 * @author Firman Wandayandi <firman@php.net>
42 * @author C.A. Woodcock <c01234@netcomuk.co.uk>
43 * @copyright 1997-2007 Baba Buehler, Pierre-Alain Joye, Firman Wandayandi, C.A. Woodcock
44 * @license http://www.opensource.org/licenses/bsd-license.php
47 * @link http://pear.php.net/package/Date
52 // {{{ Error constants
54 define('DATE_ERROR_INVALIDDATE', 1);
55 define('DATE_ERROR_INVALIDTIME', 2);
56 define('DATE_ERROR_INVALIDTIMEZONE', 3);
57 define('DATE_ERROR_INVALIDDATEFORMAT', 4);
58 define('DATE_ERROR_INVALIDFORMATSTRING', 5);
64 require_once 'PEAR.php';
69 require_once 'Date/TimeZone.php';
74 require_once 'Date/Calc.php';
79 require_once 'Date/Span.php';
83 // {{{ General constants
86 * Whether to capture the micro-time (in microseconds) by default
87 * in calls to {@link Date::setNow()}. Note that this makes a call to
88 * {@link http://www.php.net/gettimeofday gettimeofday()}, which may
89 * not work on all systems.
91 * @since Constant available since Release 1.5.0
93 define('DATE_CAPTURE_MICROTIME_BY_DEFAULT', false);
96 * Whether to correct, by adding the local Summer time offset, the
97 * specified time if it falls in the 'skipped hour' (encountered
98 * when the clocks go forward).
100 * N.B. if specified as 'false', and if a time zone that adjusts
101 * for Summer time is specified, then an object of this class will
102 * be set to a semi-invalid state if an invalid time is set. That
103 * is, an error will not be returned, unless the user then calls
104 * a function, directly or indirectly, that accesses the time
105 * part of the object. So, for example, if the user calls:
107 * <code>$date_object->formatLikeSQL('HH.MI.SS');</code>
111 * <code>$date_object->addSeconds(30);</code>
113 * an error will be returned if the time is invalid. However,
116 * <code>$date_object->addDays(1);</code>
118 * for example, such that the time is no longer invalid, then the
119 * object will no longer be in this invalid state. This behaviour
120 * is intended to minimize unexpected errors when a user uses the
121 * class to do addition with days only, and does not intend to
124 * Of course, this constant will be unused if the user chooses to
125 * work in UTC or a time zone without Summer time, in which case
126 * this situation will never arise.
128 * This constant is set to 'true' by default for backwards-compatibility
129 * reasons, however, you are recommended to set it to 'false'. Note that the
130 * behaviour is not intended to match that of previous versions of the class
131 * in terms of ignoring the Summer time offset when making calculations which
132 * involve dates in both standard and Summer time - this was recognized as a
133 * bug - but in terms of returning a PEAR error object when the user sets the
134 * object to an invalid date (i.e. a time in the hour which is skipped when
135 * the clocks go forwards, which in Europe would be a time such as 01.30).
136 * Backwards compatibility here means that the behaviour is the same as it
137 * used to be, less the bug.
139 * Note that this problem is not an issue for the user if any of these
140 * conditions are satisfied:
143 * <li>the user uses a time zone that does not observe Summer time, e.g. UTC</li>
144 * <li>the user never accesses the time, that is, he never makes a call to
145 * {@link Date::getHour()} or {@link Date::formatLikeStrftime()} using
146 * format code '<b>%H</b>', for example, even if he sets the time to
147 * something invalid</li>
148 * <li>the user sets DATE_CORRECTINVALIDTIME_DEFAULT to true</li>
151 * @since Constant available since Release 1.5.0
152 * @see Date::isValidTime(), DATE_VALIDATE_DATE_BY_DEFAULT
154 define('DATE_CORRECTINVALIDTIME_DEFAULT', true);
157 * Whether to validate dates (i.e. day/month/year, ignoring the time) by
158 * disallowing invalid dates (e.g. 31st February) being set by the following
161 * - {@link Date::setYear()}
162 * - {@link Date::setMonth()}
163 * - {@link Date::setDay()}
165 * If the constant is set to 'true', then the date will be checked (by
166 * default), and if invalid, an error will be returned with the Date object
169 * This constant is set to 'false' by default for backwards-compatibility
170 * reasons, however, you are recommended to set it to 'true'.
172 * Note that {@link Date::setHour()}, {@link Date::setMinute()},
173 * {@link Date::setSecond()} and {@link Date::setPartSecond()}
174 * allow an invalid date/time to be set regardless of the value of this
177 * @see Date::isValidDate(), Date::isValidTime(), Date::isNull(),
178 * DATE_CORRECTINVALIDTIME_DEFAULT
179 * @since Constant available since Release 1.5.0
181 define('DATE_VALIDATE_DATE_BY_DEFAULT', false);
184 * Whether, by default, to accept times including leap seconds (i.e. '23.59.60')
185 * when setting the date/time, and whether to count leap seconds in the
186 * following functions:
188 * - {@link Date::addSeconds()}
189 * - {@link Date::subtractSeconds()}
190 * - {@link Date_Calc::addSeconds()}
191 * - {@link Date::round()}
192 * - {@link Date::roundSeconds()}
194 * This constant is set to 'false' by default for backwards-compatibility
195 * reasons, however, you are recommended to set it to 'true'.
197 * Note that this constant does not affect {@link Date::addSpan()} and
198 * {@link Date::subtractSpan()} which will not count leap seconds in any case.
200 * @since Constant available since Release 1.5.0
202 define('DATE_COUNT_LEAP_SECONDS', false);
205 * Method to call when user invokes {@link Date::format()}
207 * @since Constant available since Release 1.5.1
209 define('DATE_FORMAT_METHOD', 'formatLikeStrftime');
213 // {{{ Output format constants (used in {@link Date::getDate()})
216 * "YYYY-MM-DD HH:MM:SS"
218 define('DATE_FORMAT_ISO', 1);
221 * "YYYYMMDDTHHMMSS(Z|(+/-)HHMM)?"
223 define('DATE_FORMAT_ISO_BASIC', 2);
226 * "YYYY-MM-DDTHH:MM:SS(Z|(+/-)HH:MM)?"
228 define('DATE_FORMAT_ISO_EXTENDED', 3);
231 * "YYYY-MM-DDTHH:MM:SS(.S*)?(Z|(+/-)HH:MM)?"
233 define('DATE_FORMAT_ISO_EXTENDED_MICROTIME', 6);
238 define('DATE_FORMAT_TIMESTAMP', 4);
241 * long int, seconds since the unix epoch
243 define('DATE_FORMAT_UNIXTIME', 5);
250 * Generic date handling class for PEAR
252 * Supports time zones with the Date_TimeZone class. Supports several
253 * operations from Date_Calc on Date objects.
255 * Note to developers: the class stores the local time and date in the
256 * local standard time. That is, it does not store the time as the
257 * local Summer time when and if the time zone is in Summer time. It
258 * is much easier to store local standard time and remember to offset
259 * it when the user requests it.
261 * @category Date and Time
263 * @author Baba Buehler <baba@babaz.com>
264 * @author Pierre-Alain Joye <pajoye@php.net>
265 * @author Firman Wandayandi <firman@php.net>
266 * @author C.A. Woodcock <c01234@netcomuk.co.uk>
267 * @copyright 1997-2007 Baba Buehler, Pierre-Alain Joye, Firman Wandayandi, C.A. Woodcock
268 * @license http://www.opensource.org/licenses/bsd-license.php
270 * @version Release: 1.5.0a1
271 * @link http://pear.php.net/package/Date
283 * @since Property available since Release 1.0
292 * @since Property available since Release 1.0
301 * @since Property available since Release 1.0
310 * @since Property available since Release 1.0
319 * @since Property available since Release 1.0
328 * @since Property available since Release 1.0
333 * The parts of a second
337 * @since Property available since Release 1.4.3
342 * The year in local standard time
346 * @since Property available since Release 1.5.0
348 public $on_standardyear;
351 * The month in local standard time
355 * @since Property available since Release 1.5.0
357 public $on_standardmonth;
360 * The day in local standard time
364 * @since Property available since Release 1.5.0
366 public $on_standardday;
369 * The hour in local standard time
373 * @since Property available since Release 1.5.0
375 public $on_standardhour;
378 * The minute in local standard time
382 * @since Property available since Release 1.5.0
384 public $on_standardminute;
387 * The second in local standard time
391 * @since Property available since Release 1.5.0
393 public $on_standardsecond;
396 * The part-second in local standard time
400 * @since Property available since Release 1.5.0
402 public $on_standardpartsecond;
405 * Whether the object should accept and count leap seconds
409 * @since Property available since Release 1.5.0
411 public $ob_countleapseconds;
414 * Whether the time is valid as a local time (an invalid time
415 * is one that lies in the 'skipped hour' at the point that
416 * the clocks go forward)
420 * @see Date::isValidTime()
421 * @since Property available since Release 1.5.0
423 public $ob_invalidtime = null;
426 * Date_TimeZone object for this date
428 * @var object Date_TimeZone object
430 * @since Property available since Release 1.0
435 * Defines the default weekday abbreviation length
437 * Formerly used by {@link Date::formatLikeStrftime()}, but now
438 * redundant - the abbreviation for the current locale of the machine
443 * @since Property available since Release 1.4.4
445 public $getWeekdayAbbrnameLength = 3;
454 * Creates a new Date Object initialized to the current date/time in the
455 * system-default timezone by default. A date optionally
456 * passed in may be in the ISO 8601, TIMESTAMP or UNIXTIME format,
457 * or another Date object. If no date is passed, the current date/time
460 * If a date is passed and an exception is returned by {@link Date::setDate()}
461 * there is nothing that this function can do, so for this reason, it
462 * is advisable to pass no parameter and to make a separate call to
463 * Date::setDate(). A date/time should only be passed if known to be a
464 * valid ISO 8601 string or a valid Unix timestamp.
466 * @param mixed $date optional ISO 8601 date/time to initialize;
467 * or, a Unix time stamp
468 * @param bool $pb_countleapseconds whether to count leap seconds
470 * {@link DATE_COUNT_LEAP_SECONDS})
474 * @see Date::setDate()
476 public function Date(
478 $pb_countleapseconds = DATE_COUNT_LEAP_SECONDS
481 $this->ob_countleapseconds = $pb_countleapseconds;
483 if (is_a($date, 'Date')) {
486 if (!is_null($date)) {
487 // 'setDate()' expects a time zone to be already set:
489 $this->_setTZToDefault();
490 $this->setDate($date);
502 * Copy values from another Date object
504 * Makes this Date a copy of another Date object. This is a
505 * PHP4-compatible implementation of {@link Date::__clone()} in PHP5.
507 * @param object $date Date object to copy
512 public function copy($date)
514 $this->year = $date->year;
515 $this->month = $date->month;
516 $this->day = $date->day;
517 $this->hour = $date->hour;
518 $this->minute = $date->minute;
519 $this->second = $date->second;
520 $this->partsecond = $date->partsecond;
522 $this->on_standardyear = $date->on_standardyear;
523 $this->on_standardmonth = $date->on_standardmonth;
524 $this->on_standardday = $date->on_standardday;
525 $this->on_standardhour = $date->on_standardhour;
526 $this->on_standardminute = $date->on_standardminute;
527 $this->on_standardsecond = $date->on_standardsecond;
528 $this->on_standardpartsecond = $date->on_standardpartsecond;
530 $this->ob_countleapseconds = $date->ob_countleapseconds;
531 $this->ob_invalidtime = $date->ob_invalidtime;
533 $this->tz = new Date_TimeZone($date->getTZID());
535 $this->getWeekdayAbbrnameLength = $date->getWeekdayAbbrnameLength;
543 * Copy values from another Date object
545 * Makes this Date a copy of another Date object. For PHP5
552 public function __clone()
554 // This line of code would be preferable, but will only
557 // $this->tz = clone $this->tz;
559 $this->tz = new Date_TimeZone($this->getTZID());
567 * Returns whether the object is null (i.e. no date has been set)
569 * If the object is set to an invalid date, then this function will
570 * still return 'false'. To check whether the date is valid use
571 * either {@link Date::isValidDate()} (to check the day/month/year
572 * part of the object only) or {@link Date::isValidTime()} (to check
573 * the time, in addition to the day/month/year part).
577 * @see Date::setDate(), Date::isValidDate(), Date::isValidTime()
578 * @since Method available since Release 1.5.0
580 public function isNull()
582 return is_null($this->year);
590 * Returns whether the date (i.e. day/month/year) is valid
592 * It is not possible to set the object to an invalid date using
593 * {@link Date::setDate()}, but it is possible to do so using the
594 * following functions:
596 * - {@link Date::setYear()}
597 * - {@link Date::setMonth()}
598 * - {@link Date::setDay()}
600 * However you can prevent this possibility (by default) by setting
601 * {@link DATE_VALIDATE_DATE_BY_DEFAULT} to 'true', in which case
602 * these three functions will return an error if they specify an
603 * invalid date, and the object will be unmodified.
605 * Note that this function only checks the day/month/year part of
606 * the object. Even if this is valid, it is still possible for the
607 * time to be invalid (see {@link DATE_CORRECTINVALIDTIME_DEFAULT}).
608 * To check the time as well, use {@link Date::isValidTime()}.
612 * @see Date::setDate(), Date::isNull(), Date::isValidTime()
613 * @since Method available since Release 1.5.0
615 public function isValidDate()
619 Date_Calc::isValidDate($this->year, $this->month, $this->day);
627 * Sets the date/time of the object based on the input date and format
629 * Accepts a string in three possible formats, and in this order of
632 * - ISO 8601 date (see {@link http://en.wikipedia.org/wiki/ISO_8601})
633 * - Time-Stamp (i.e. 'YYYYMMDDHHMMSS')
634 * - Unix time-stamp (see {@link http://en.wikipedia.org/wiki/Unix_time})
636 * Note that if you want to pass a Unix time-stamp then you need to set
637 * the $format parameter to {@link DATE_FORMAT_UNIXTIME}, or else use the
638 * method {@link Date::setFromTime()}.
640 * The input string should be a date/time representation in one of the
641 * following general formats:
643 * - <b><date>T<time><time-zone></b>
644 * - <b><date> <time><time-zone></b> (non-ISO-standard)
645 * - <b><date><time><time-zone></b> (non-ISO-standard)
646 * - <b><date>T<time></b> i.e. without optional <time-zone> representation
647 * - <b><date> <time></b>
648 * - <b><date><time></b>
649 * - <b><date></b> i.e. without optional <time> representation
651 * that is, the representation must be comprised of a <b><date></b> part,
652 * with an optional <b><time></b> part, which itself may include an optional
653 * <time-zone> part, each of which may consist of any one of the permitted
654 * formats detailed below. The <b><date></b> and <b><time</b> representations
655 * should be divided with the time designator <b>T</b> according to the ISO 8601
656 * standard, although this method also permits representations divided by a
657 * space, or by no delimiter at all.
659 * The <b><date></b> representation should be in one of the following formats:
661 * - <b>Calendar date</b>: <b>YYYY-MM-DD</b> (extended format) or
662 * <b>YYYYMMDD</b> (basic format), where [YYYY]
663 * indicates the four-digit year (0000-9999), [MM]
664 * indicates the month (01-12) and [DD] indicates the
665 * day of the month [01-31]
666 * - <b>ISO week date</b>: <b>YYYY-Www-D</b> (extended format) or
667 * <b>YYYYWwwD</b> (basic format), where [YYYY]
668 * indicates the ISO year (slightly different from the
669 * calendar year (see below)), [Www] indicates the ISO
670 * week no prefixed by the letter 'W' (W01-W53) and
671 * [D] indicates the ISO week-day (1-7), beginning on
672 * Monday and ending on Sunday. (Also see
673 * {@link http://en.wikipedia.org/wiki/ISO_week_date}.)
674 * - <b>Ordinal date</b>: <b>YYYY-DDD</b> (extended format) or
675 * <b>YYYYDDD</b> (basic format), where [YYYY]
676 * indicates the four-digit year (0000-9999) and [DDD]
677 * indicates the day of the year (001-366)
679 * The <b><time></b> representation should be in one of the following formats:
681 * - <b>hh:mm:ss</b> (extended format) or <b>hhmmss</b> (basic format)
682 * - <b>hh:mm</b> (extended format) or <b>hhmm</b> (basic format)
683 * - <b>hh</b> (extended format) or <b>hh</b> (basic format)
685 * where [hh] represents the hour (00-24), [mm] represents the minute (00-59)
686 * and [ss] represents the second (00-60)
688 * Format parameter should be one of the specified DATE_FORMAT_* constants:
690 * - <b>{@link DATE_FORMAT_ISO}</b> - 'YYYY-MM-DD HH:MI:SS'
691 * - <b>{@link DATE_FORMAT_ISO_BASIC}</b> - 'YYYYMMDDTHHMMSS(Z|(+/-)HHMM)?'
692 * - <b>{@link DATE_FORMAT_ISO_EXTENDED}</b> - 'YYYY-MM-DDTHH:MM:SS(Z|(+/-)HH:MM)?'
693 * - <b>{@link DATE_FORMAT_ISO_EXTENDED_MICROTIME}</b> - 'YYYY-MM-DDTHH:MM:SS(.S*)?(Z|(+/-)HH:MM)?'
694 * - <b>{@link DATE_FORMAT_TIMESTAMP}</b> - 'YYYYMMDDHHMMSS'
695 * - <b>{@link DATE_FORMAT_UNIXTIME}</b> - long integer of the no of seconds since
697 * (1st January 1970 00.00.00 GMT)
699 * @param string $date input date
700 * @param int $format optional format constant
701 * (DATE_FORMAT_*) of the input date.
702 * This parameter is not needed,
703 * except to force the setting of the
704 * date from a Unix time-stamp (for
706 * {@link DATE_FORMAT_UNIXTIME}).
708 * {@link DATE_FORMAT_ISO}.)
709 * @param bool $pb_repeatedhourdefault value to return if repeated
710 * hour is specified (defaults
715 * @see Date::isNull(), Date::isValidDate(), Date::isValidTime(),
716 * Date::setFromTime()
718 public function setDate(
720 $format = DATE_FORMAT_ISO,
721 $pb_repeatedhourdefault = false
724 if ($format == DATE_FORMAT_UNIXTIME) {
725 if (is_numeric($date)) {
726 // Assume Unix time-stamp:
728 $this->setFromTime((int)$date);
730 return PEAR::raiseError("'$date' not valid Unix time-stamp");
732 } elseif (preg_match('/^([0-9]{4,4})-?(' .
733 '(0[1-9]|1[0-2])-?(0[1-9]|[12][0-9]|3[01])|' . // [mm]-[dd]
734 'W(0[1-9]|[1-4][0-9]|5[0-3])-?([1-7])|' . // ISO week date
735 '(0(0[1-9]|[1-9][0-9])|[12][0-9]{2,2}|3([0-5][0-9]|6[1-6]))' . // [ddd]
737 '([01][0-9]|2[0-3])(:?' . // [hh]
738 '([0-5][0-9])(:?' . // [mm]
739 '([0-5][0-9]|60)([,.][0-9]+)?)?)?' . // [ss]
740 '(Z|[+\-][0-9]{2,2}(:?[0-5][0-9])?)?)?$/i', // offset
743 if (substr($regs[2], 0, 1) == "W") {
744 // ISO week date (YYYY-Www-D)
747 $hs_date = Date_Calc::isoWeekToDate(
753 if (PEAR::isError($hs_date)) {
757 list($hs_year, $hs_month, $hs_day) = explode(" ", $hs_date);
758 } elseif (strlen($regs[2]) == 3) {
759 // ISO ordinal date (YYYY-DDD)
762 $hn_jd = Date_Calc::firstDayOfYear($regs[1]) + $regs[2] - 1;
763 list($hs_year, $hs_month, $hs_day) =
764 explode(" ", Date_Calc::daysToDate($hn_jd, "%Y %m %d"));
766 // ISO calendar date (YYYY-MM-DD)
768 // DATE_FORMAT_ISO, ISO_BASIC, ISO_EXTENDED, and TIMESTAMP
769 // These formats are extremely close to each other. This regex
770 // is very loose and accepts almost any butchered format you could
771 // throw at it. e.g. 2003-10-07 19:45:15 and 2003-10071945:15
772 // are the same thing in the eyes of this regex, even though the
773 // latter is not a valid ISO 8601 date.
777 $hs_month = $regs[3];
780 if (!Date_Calc::isValidDate($hs_day, $hs_month, $hs_year)) {
781 return PEAR::raiseError(
783 Date_Calc::dateFormat(
789 "' is invalid calendar date",
790 DATE_ERROR_INVALIDDATE
795 if (isset($regs[17])) {
796 if ($regs[17] == "Z") {
797 $this->tz = new Date_TimeZone("UTC");
799 $this->tz = new Date_TimeZone("UTC" . $regs[17]);
807 isset($regs[11]) && $regs[11] != "" ?
809 isset($regs[13]) && $regs[13] != "" ?
811 isset($regs[15]) && $regs[15] != "" ?
813 isset($regs[16]) && $regs[16] != "" ?
815 $pb_repeatedhourdefault
818 return PEAR::raiseError(
819 "Date '$date' not in ISO 8601 format",
820 DATE_ERROR_INVALIDDATEFORMAT
830 * Sets to local current time and time zone
832 * @param bool $pb_setmicrotime whether to set micro-time (defaults to the
833 * value of the constant
834 * {@link DATE_CAPTURE_MICROTIME_BY_DEFAULT})
838 * @since Method available since Release 1.5.0
840 public function setNow($pb_setmicrotime = DATE_CAPTURE_MICROTIME_BY_DEFAULT)
842 $this->_setTZToDefault();
844 if ($pb_setmicrotime) {
845 $ha_unixtime = gettimeofday();
847 $ha_unixtime = array("sec" => time());
850 $this->setDate(date("Y-m-d H:i:s", $ha_unixtime["sec"]) .
851 (isset($ha_unixtime["usec"]) ?
852 "." . sprintf("%06d", $ha_unixtime["usec"]) :
861 * Rounds the date according to the specified precision (defaults
864 * The precision parameter must be one of the following constants:
866 * - <b>{@link DATE_PRECISION_YEAR}</b>
867 * - <b>{@link DATE_PRECISION_MONTH}</b>
868 * - <b>{@link DATE_PRECISION_DAY}</b> (default)
869 * - <b>{@link DATE_PRECISION_HOUR}</b>
870 * - <b>{@link DATE_PRECISION_10MINUTES}</b>
871 * - <b>{@link DATE_PRECISION_MINUTE}</b>
872 * - <b>{@link DATE_PRECISION_10SECONDS}</b>
873 * - <b>{@link DATE_PRECISION_SECOND}</b>
875 * The precision can also be specified as an integral offset from
876 * one of these constants, where the offset reflects a precision
877 * of 10 to the power of the offset greater than the constant.
880 * - <b>(DATE_PRECISION_YEAR - 1)</b> - rounds the date to the nearest 10 years
881 * - <b>(DATE_PRECISION_YEAR - 3)</b> - rounds the date to the nearest 1000
883 * - <b>(DATE_PRECISION_SECOND + 1)</b> - rounds the date to 1 decimal
885 * - <b>(DATE_PRECISION_SECOND + 3)</b> - rounds the date to 3 decimal
887 * - <b>(DATE_PRECISION_SECOND - 1)</b> - rounds the date to the nearest 10
888 * seconds (thus it is equivalent to
889 * <b>DATE_PRECISION_10SECONDS</b>)
891 * @param int $pn_precision a 'DATE_PRECISION_*' constant (defaults to
892 * {@link DATE_PRECISION_DAY})
893 * @param bool $pb_correctinvalidtime whether to correct, by adding the
894 * local Summer time offset, the rounded
895 * time if it falls in the skipped hour
897 * {@link DATE_CORRECTINVALIDTIME_DEFAULT})
901 * @since Method available since Release 1.5.0
903 public function round(
904 $pn_precision = DATE_PRECISION_DAY,
905 $pb_correctinvalidtime = DATE_CORRECTINVALIDTIME_DEFAULT
908 if ($pn_precision <= DATE_PRECISION_DAY) {
922 $this->partsecond == 0.0 ?
924 $this->second + $this->partsecond,
925 $this->ob_countleapseconds
927 if (is_float($hn_secondraw)) {
928 $hn_second = intval($hn_secondraw);
929 $hn_partsecond = $hn_secondraw - $hn_second;
931 $hn_second = $hn_secondraw;
932 $hn_partsecond = 0.0;
943 true, // This is unlikely anyway, but the
944 // day starts with the repeated hour
945 // the first time around
946 $pb_correctinvalidtime
951 // ($pn_precision >= DATE_PRECISION_HOUR)
953 if ($this->tz->getDSTSavings() % 3600000 == 0 ||
954 ($this->tz->getDSTSavings() % 60000 == 0 &&
955 $pn_precision >= DATE_PRECISION_MINUTE)
965 $this->on_standardday,
966 $this->on_standardmonth,
967 $this->on_standardyear,
968 $this->on_standardhour,
969 $this->on_standardminute,
970 $this->on_standardpartsecond == 0.0 ?
971 $this->on_standardsecond :
972 $this->on_standardsecond +
973 $this->on_standardpartsecond,
974 $this->ob_countleapseconds
976 if (is_float($hn_secondraw)) {
977 $hn_second = intval($hn_secondraw);
978 $hn_partsecond = $hn_secondraw - $hn_second;
980 $hn_second = $hn_secondraw;
981 $hn_partsecond = 0.0;
984 $this->setStandardTime(
996 // Very unlikely anyway (as I write, the only time zone like this
997 // is Lord Howe Island in Australia (offset of half an hour)):
999 // (This algorithm could be better)
1014 $this->partsecond == 0.0 ?
1016 $this->second + $this->partsecond,
1017 $this->ob_countleapseconds
1019 if (is_float($hn_secondraw)) {
1020 $hn_second = intval($hn_secondraw);
1021 $hn_partsecond = $hn_secondraw - $hn_second;
1023 $hn_second = $hn_secondraw;
1024 $hn_partsecond = 0.0;
1027 $this->setLocalTime(
1035 false, // This will be right half the time
1036 $pb_correctinvalidtime
1037 ); // This will be right
1039 // (depends on Summer
1045 // {{{ roundSeconds()
1048 * Rounds seconds up or down to the nearest specified unit
1050 * N.B. this function is equivalent to calling:
1052 * <code>$date_object->round(DATE_PRECISION_SECOND + $pn_precision);</code>
1054 * @param int $pn_precision number of digits after the decimal point
1055 * @param bool $pb_correctinvalidtime whether to correct, by adding the
1056 * local Summer time offset, the rounded
1057 * time if it falls in the skipped hour
1059 * {@link DATE_CORRECTINVALIDTIME_DEFAULT})
1063 * @since Method available since Release 1.5.0
1065 public function roundSeconds(
1067 $pb_correctinvalidtime = DATE_CORRECTINVALIDTIME_DEFAULT
1071 DATE_PRECISION_SECOND + $pn_precision,
1072 $pb_correctinvalidtime
1081 * Truncates the date according to the specified precision (by
1082 * default, it truncates the time part of the date)
1084 * The precision parameter must be one of the following constants:
1086 * - {@link DATE_PRECISION_YEAR}
1087 * - {@link DATE_PRECISION_MONTH}
1088 * - {@link DATE_PRECISION_DAY} (default)
1089 * - {@link DATE_PRECISION_HOUR}
1090 * - {@link DATE_PRECISION_10MINUTES}
1091 * - {@link DATE_PRECISION_MINUTE}
1092 * - {@link DATE_PRECISION_10SECONDS}
1093 * - {@link DATE_PRECISION_SECOND}
1095 * The precision can also be specified as an integral offset from
1096 * one of these constants, where the offset reflects a precision
1097 * of 10 to the power of the offset greater than the constant.
1100 * - <b>DATE_PRECISION_YEAR</b> - truncates the month, day and time
1102 * - <b>(DATE_PRECISION_YEAR - 1)</b> - truncates the unit part of the
1103 * year, e.g. 1987 becomes 1980
1104 * - <b>(DATE_PRECISION_YEAR - 3)</b> - truncates the hundreds part of the
1105 * year, e.g. 1987 becomes 1000
1106 * - <b>(DATE_PRECISION_SECOND + 1)</b> - truncates the part of the second
1107 * less than 0.1 of a second, e.g.
1108 * 3.26301 becomes 3.2 seconds
1109 * - <b>(DATE_PRECISION_SECOND + 3)</b> - truncates the part of the second
1110 * less than 0.001 of a second, e.g.
1111 * 3.26301 becomes 3.263 seconds
1112 * - <b>(DATE_PRECISION_SECOND - 1)</b> - truncates the unit part of the
1113 * seconds (thus it is equivalent to
1114 * <b>DATE_PRECISION_10SECONDS</b>)
1116 * @param int $pn_precision a 'DATE_PRECISION_*' constant (defaults
1117 * to {@link DATE_PRECISION_DAY})
1118 * @param bool $pb_correctinvalidtime whether to correct, by adding the
1119 * local Summer time offset, the
1120 * truncated time if it falls in the
1121 * skipped hour (defaults to
1122 * {@link DATE_CORRECTINVALIDTIME_DEFAULT})
1126 * @since Method available since Release 1.5.0
1128 public function trunc(
1129 $pn_precision = DATE_PRECISION_DAY,
1130 $pb_correctinvalidtime = DATE_CORRECTINVALIDTIME_DEFAULT
1133 if ($pn_precision <= DATE_PRECISION_DAY) {
1134 if ($pn_precision <= DATE_PRECISION_YEAR) {
1140 $hn_partsecond = 0.0;
1142 $hn_invprecision = DATE_PRECISION_YEAR - $pn_precision;
1143 if ($hn_invprecision > 0) {
1144 $hn_year = intval($this->year / pow(10, $hn_invprecision)) *
1145 pow(10, $hn_invprecision);
1147 // (Conversion to int necessary for PHP <= 4.0.6)
1149 $hn_year = $this->year;
1151 } elseif ($pn_precision == DATE_PRECISION_MONTH) {
1152 $hn_year = $this->year;
1153 $hn_month = $this->month;
1158 $hn_partsecond = 0.0;
1159 } elseif ($pn_precision == DATE_PRECISION_DAY) {
1160 $hn_year = $this->year;
1161 $hn_month = $this->month;
1162 $hn_day = $this->day;
1166 $hn_partsecond = 0.0;
1169 $this->setLocalTime(
1177 true, // This is unlikely anyway, but the
1178 // day starts with the repeated
1179 // hour the first time around
1180 $pb_correctinvalidtime
1185 // Precision is at least equal to DATE_PRECISION_HOUR
1187 if ($pn_precision == DATE_PRECISION_HOUR) {
1188 $this->addSeconds($this->partsecond == 0.0 ?
1190 -$this->second - $this->partsecond);
1192 // (leap seconds irrelevant)
1194 $this->addMinutes(-$this->minute);
1195 } elseif ($pn_precision <= DATE_PRECISION_MINUTE) {
1196 if ($pn_precision == DATE_PRECISION_10MINUTES) {
1197 $this->addMinutes(-$this->minute % 10);
1200 $this->addSeconds($this->partsecond == 0.0 ?
1202 -$this->second - $this->partsecond);
1204 // (leap seconds irrelevant)
1205 } elseif ($pn_precision == DATE_PRECISION_10SECONDS) {
1206 $this->addSeconds($this->partsecond == 0.0 ?
1207 -$this->second % 10 :
1208 (-$this->second % 10) - $this->partsecond);
1210 // (leap seconds irrelevant)
1212 // Assume Summer time offset cannot be composed of part-seconds:
1214 $hn_precision = $pn_precision - DATE_PRECISION_SECOND;
1215 $hn_partsecond = intval($this->on_standardpartsecond *
1216 pow(10, $hn_precision)) /
1217 pow(10, $hn_precision);
1218 $this->setStandardTime(
1219 $this->on_standardday,
1220 $this->on_standardmonth,
1221 $this->on_standardyear,
1222 $this->on_standardhour,
1223 $this->on_standardminute,
1224 $this->on_standardsecond,
1232 // {{{ truncSeconds()
1235 * Truncates seconds according to the specified precision
1237 * N.B. this function is equivalent to calling:
1240 * $date_object->trunc(DATE_PRECISION_SECOND + $pn_precision);
1243 * @param int $pn_precision number of digits after the decimal point
1244 * @param bool $pb_correctinvalidtime whether to correct, by adding the
1245 * local Summer time offset, the
1246 * truncated time if it falls in the
1247 * skipped hour (defaults to
1248 * {@link DATE_CORRECTINVALIDTIME_DEFAULT})
1252 * @since Method available since Release 1.5.0
1254 public function truncSeconds(
1256 $pb_correctinvalidtime = DATE_CORRECTINVALIDTIME_DEFAULT
1260 DATE_PRECISION_SECOND + $pn_precision,
1261 $pb_correctinvalidtime
1270 * Gets a string (or other) representation of this date
1272 * Returns a date in the format specified by the DATE_FORMAT_* constants,
1273 * which should be one of the following:
1275 * - {@link DATE_FORMAT_ISO} (default)
1276 * - {@link DATE_FORMAT_ISO_BASIC}
1277 * - {@link DATE_FORMAT_ISO_EXTENDED}
1278 * - {@link DATE_FORMAT_ISO_EXTENDED_MICROTIME}
1279 * - {@link DATE_FORMAT_TIMESTAMP}
1280 * - {@link DATE_FORMAT_UNIXTIME}
1282 * @param int $format format constant (DATE_FORMAT_*) of the output date
1284 * @return string the date in the requested format (defaults to
1285 * {@link DATE_FORMAT_ISO})
1288 public function getDate($format = DATE_FORMAT_ISO)
1292 case DATE_FORMAT_ISO:
1293 $ret = $this->formatLikeStrftime("%Y-%m-%d %T");
1295 case DATE_FORMAT_ISO_BASIC:
1296 $format = "%Y%m%dT%H%M%S";
1297 if ($this->getTZID() == 'UTC') {
1300 $ret = $this->formatLikeStrftime($format);
1302 case DATE_FORMAT_ISO_EXTENDED:
1303 $format = "%Y-%m-%dT%H:%M:%S";
1304 if ($this->getTZID() == 'UTC') {
1307 $ret = $this->formatLikeStrftime($format);
1309 case DATE_FORMAT_ISO_EXTENDED_MICROTIME:
1310 $format = "%Y-%m-%dT%H:%M:%s";
1311 if ($this->getTZID() == 'UTC') {
1314 $ret = $this->formatLikeStrftime($format);
1316 case DATE_FORMAT_TIMESTAMP:
1317 $ret = $this->formatLikeStrftime("%Y%m%d%H%M%S");
1319 case DATE_FORMAT_UNIXTIME:
1320 $ret = $this->getTime();
1321 if (!PEAR::isError($ret)) {
1322 $ret = (string)$ret;
1335 * Formats the date according to the specified formatting code string
1337 * This function is an alias for the method specified by the constant
1338 * {@link DATE_FORMAT_METHOD} (which defaults to 'formatLikeStrftime'
1339 * for backwards-compatibility).
1341 * @return string date/time in given format
1343 * @see Date::formatLikeStrftime(), Date::formatLikeDate(),
1344 * Date::formatLikeSQL()
1346 public function format()
1348 $ha_args = func_get_args();
1349 return call_user_func_array(
1350 array(&$this, DATE_FORMAT_METHOD),
1357 // {{{ formatLikeStrftime()
1360 * Formats the date according to the specified formatting code string,
1361 * based on {@link http://www.php.net/strftime strftime()}
1363 * Formats the date in the given format, much like
1364 * strftime(). Most strftime() options are supported.
1367 * Formatting options:
1369 * - <b>%a</b> - abbreviated weekday name (Sun, Mon, Tue)
1370 * - <b>%A</b> - full weekday name (Sunday, Monday, Tuesday)
1371 * - <b>%b</b> - abbreviated month name (Jan, Feb, Mar)
1372 * - <b>%B</b> - full month name (January, February, March)
1373 * - <b>%C</b> - century number (the year divided by 100 and truncated
1374 * to an integer, range 00 to 99)
1375 * - <b>%d</b> - day of month (range 00 to 31)
1376 * - <b>%D</b> - equivalent to '<b>%m/%d/%y</b>'
1377 * - <b>%e</b> - day of month without leading noughts (range 0 to 31)
1378 * - <b>%E</b> - {@link http://en.wikipedia.org/wiki/Julian_day Julian day} -
1379 * no of days since Monday, 24th November, 4714 B.C. (in
1380 * the proleptic Gregorian calendar)
1381 * - <b>%g</b> - like '<b>%G</b>', but without the century
1382 * - <b>%G</b> - the 4-digit year corresponding to the ISO week
1383 * number (see '<b>%V</b>'). This has the same
1384 * format and value as '<b>%Y</b>', except that if
1385 * the ISO week number belongs to the previous or
1386 * next year, that year is used instead.
1387 * - <b>%h</b> - hour as decimal number without leading noughts (0
1389 * - <b>%H</b> - hour as decimal number (00 to 23)
1390 * - <b>%i</b> - hour as decimal number on 12-hour clock without
1391 * leading noughts (1 to 12)
1392 * - <b>%I</b> - hour as decimal number on 12-hour clock (01 to 12)
1393 * - <b>%j</b> - day of year (range 001 to 366)
1394 * - <b>%m</b> - month as decimal number (range 01 to 12)
1395 * - <b>%M</b> - minute as a decimal number (00 to 59)
1396 * - <b>%n</b> - newline character ("\n")
1397 * - <b>%o</b> - raw timezone offset expressed as '+/-HH:MM'
1398 * - <b>%O</b> - dst-corrected timezone offset expressed as '+/-HH:MM'
1399 * - <b>%p</b> - either 'am' or 'pm' depending on the time
1400 * - <b>%P</b> - either 'AM' or 'PM' depending on the time
1401 * - <b>%r</b> - time in am/pm notation; equivalent to
1402 * '<b>%I:%M:%S %p</b>'
1403 * - <b>%R</b> - time in 24-hour notation; equivalent to
1405 * - <b>%s</b> - seconds including the micro-time (the decimal
1406 * representation less than one second to six
1408 * - <b>%S</b> - seconds as a decimal number (00 to 59)
1409 * - <b>%t</b> - tab character ("\t")
1410 * - <b>%T</b> - current time; equivalent to '<b>%H:%M:%S</b>'
1411 * - <b>%u</b> - day of week as decimal (1 to 7; where 1 = Monday)
1412 * - <b>%U</b> - week number of the current year as a decimal
1413 * number, starting with the first Sunday as the first
1414 * day of the first week (i.e. the first full week of
1415 * the year, and the week that contains 7th January)
1417 * - <b>%V</b> - the {@link http://en.wikipedia.org/wiki/ISO_week_date ISO 8601:1988}
1418 * week number of the current year
1419 * as a decimal number, range 01 to 53, where week 1
1420 * is the first week that has at least 4 days in the
1421 * current year, and with Monday as the first day of
1422 * the week. (Use '<b>%G</b>' or '<b>%g</b>' for the
1423 * year component that corresponds to the week number
1424 * for the specified timestamp.)
1425 * - <b>%w</b> - day of week as decimal (0 to 6; where 0 = Sunday)
1426 * - <b>%W</b> - week number of the current year as a decimal
1427 * number, starting with the first Monday as the first
1428 * day of the first week (i.e. the first full week of
1429 * the year, and the week that contains 7th January)
1431 * - <b>%y</b> - year as decimal (range 00 to 99)
1432 * - <b>%Y</b> - year as decimal including century (range 0000 to
1434 * - <b>%Z</b> - Abbreviated form of time zone name, e.g. 'GMT', or
1435 * the abbreviation for Summer time if the date falls
1436 * in Summer time, e.g. 'BST'.
1437 * - <b>%%</b> - literal '%'
1440 * The following codes render a different output to that of
1441 * {@link http://www.php.net/strftime strftime()}:
1443 * - <b>%e</b> - in 'strftime()' a single digit is preceded by a space
1444 * - <b>%h</b> - in 'strftime()' is equivalent to '<b>%b</b>'
1445 * - <b>%U</b> - '<b>%U</b>' and '<b>%W</b>' are different in
1446 * 'strftime()' in that if week 1 does not start on 1st
1447 * January, '00' is returned, whereas this function
1448 * returns '53', that is, the week is counted as the
1449 * last of the previous year.
1452 * @param string $format the format string for returned date/time
1454 * @return string date/time in given format
1456 * @see Date::format(), Date::formatLikeDate(), Date::formatLikeSQL()
1457 * @since Method available since Release 1.5.1
1459 public function formatLikeStrftime($format)
1467 for ($strpos = 0; $strpos < strlen($format); $strpos++) {
1468 $char = substr($format, $strpos, 1);
1470 $nextchar = substr($format, $strpos + 1, 1);
1471 switch ($nextchar) {
1473 $output .= Date_Calc::getWeekdayAbbrname(
1477 $this->getWeekdayAbbrnameLength
1481 $output .= Date_Calc::getWeekdayFullname(
1488 $output .= Date_Calc::getMonthAbbrname($this->month);
1491 $output .= Date_Calc::getMonthFullname($this->month);
1494 $output .= sprintf("%02d", intval($this->year / 100));
1497 $output .= sprintf("%02d", $this->day);
1508 $output .= $this->day;
1511 $output .= Date_Calc::dateToDays(
1518 if (is_null($hn_isoyear)) {
1519 list($hn_isoyear, $hn_isoweek, $hn_isoday) =
1520 Date_Calc::isoWeekDate(
1527 $output .= sprintf("%02d", $hn_isoyear % 100);
1530 if (is_null($hn_isoyear)) {
1531 list($hn_isoyear, $hn_isoweek, $hn_isoday) =
1532 Date_Calc::isoWeekDate(
1539 $output .= sprintf("%04d", $hn_isoyear);
1542 if ($this->ob_invalidtime) {
1543 return $this->_getErrorInvalidTime();
1545 $output .= sprintf("%d", $this->hour);
1548 if ($this->ob_invalidtime) {
1549 return $this->_getErrorInvalidTime();
1551 $output .= sprintf("%02d", $this->hour);
1555 if ($this->ob_invalidtime) {
1556 return $this->_getErrorInvalidTime();
1558 $hour = $this->hour + 1 > 12 ?
1561 $output .= $hour == 0 ?
1565 sprintf('%02d', $hour));
1570 Date_Calc::dayOfYear(
1578 $output .= sprintf("%02d", $this->month);
1581 $output .= sprintf("%02d", $this->minute);
1587 if ($this->ob_invalidtime) {
1588 return $this->_getErrorInvalidTime();
1590 $offms = $this->getTZOffset();
1591 $direction = $offms >= 0 ? "+" : "-";
1592 $offmins = abs($offms) / 1000 / 60;
1593 $hours = $offmins / 60;
1594 $minutes = $offmins % 60;
1596 $output .= sprintf("%s%02d:%02d", $direction, $hours, $minutes);
1599 $offms = $this->tz->getRawOffset($this);
1600 $direction = $offms >= 0 ? "+" : "-";
1601 $offmins = abs($offms) / 1000 / 60;
1602 $hours = $offmins / 60;
1603 $minutes = $offmins % 60;
1605 $output .= sprintf("%s%02d:%02d", $direction, $hours, $minutes);
1608 if ($this->ob_invalidtime) {
1609 return $this->_getErrorInvalidTime();
1611 $output .= $this->hour >= 12 ? "pm" : "am";
1614 if ($this->ob_invalidtime) {
1615 return $this->_getErrorInvalidTime();
1617 $output .= $this->hour >= 12 ? "PM" : "AM";
1620 if ($this->ob_invalidtime) {
1621 return $this->_getErrorInvalidTime();
1623 $hour = $this->hour + 1 > 12 ?
1627 "%02d:%02d:%02d %s",
1628 $hour == 0 ? 12 : $hour,
1631 $this->hour >= 12 ? "PM" : "AM"
1635 if ($this->ob_invalidtime) {
1636 return $this->_getErrorInvalidTime();
1638 $output .= sprintf("%02d:%02d", $this->hour, $this->minute);
1641 $output .= str_replace(
1646 (float)((float)$this->second +
1652 $output .= sprintf("%02d", $this->second);
1658 if ($this->ob_invalidtime) {
1659 return $this->_getErrorInvalidTime();
1669 $hn_dayofweek = $this->getDayOfWeek();
1670 $output .= $hn_dayofweek == 0 ? 7 : $hn_dayofweek;
1673 $ha_week = Date_Calc::weekOfYear7th(
1679 $output .= sprintf("%02d", $ha_week[1]);
1682 if (is_null($hn_isoyear)) {
1683 list($hn_isoyear, $hn_isoweek, $hn_isoday) =
1684 Date_Calc::isoWeekDate(
1691 $output .= $hn_isoweek;
1694 $output .= $this->getDayOfWeek();
1697 $ha_week = Date_Calc::weekOfYear7th(
1703 $output .= sprintf("%02d", $ha_week[1]);
1708 ($this->year < 0 ? '3' : '2') .
1716 ($this->year < 0 ? '5' : '4') .
1722 if ($this->ob_invalidtime) {
1723 return $this->_getErrorInvalidTime();
1725 $output .= $this->getTZShortName();
1731 $output .= $char . $nextchar;
1743 // {{{ _getOrdinalSuffix()
1746 * Returns appropriate ordinal suffix (i.e. 'th', 'st', 'nd' or 'rd')
1748 * @param int $pn_num number with which to determine suffix
1749 * @param bool $pb_uppercase boolean specifying if the suffix should be
1754 * @since Method available since Release 1.5.0
1756 public function _getOrdinalSuffix($pn_num, $pb_uppercase = true)
1758 switch (($pn_numabs = abs($pn_num)) % 100) {
1765 switch ($pn_numabs % 10) {
1780 return $pb_uppercase ? strtoupper($hs_suffix) : $hs_suffix;
1785 // {{{ _spellNumber()
1788 * Converts a number to its word representation
1790 * Private helper function, particularly for {@link Date::formatLikeSQL()}.
1791 * N.B. The second argument is the 'SP' code which can be specified in the
1792 * format string for 'formatLikeSQL()' and is interpreted as follows:
1794 * - <b>SP</b> - returns upper-case spelling, e.g. 'FOUR HUNDRED'
1795 * - <b>Sp</b> - returns spelling with first character of each word
1796 * capitalized, e.g. 'Four Hundred'
1797 * - <b>sp</b> - returns lower-case spelling, e.g. 'four hundred'
1799 * @param int $pn_num number to be converted to words
1800 * @param bool $pb_ordinal boolean specifying if the number should
1802 * @param string $ps_capitalization string for specifying capitalization
1804 * @param string $ps_locale language name abbreviation used for
1805 * formatting numbers as spelled-out words
1809 * @since Method available since Release 1.5.0
1811 public function _spellNumber(
1813 $pb_ordinal = false,
1814 $ps_capitalization = "SP",
1815 $ps_locale = "en_GB"
1818 include_once "Numbers/Words.php";
1819 $hs_words = Numbers_Words::toWords($pn_num, $ps_locale);
1820 if (Pear::isError($hs_words)) {
1824 if ($pb_ordinal && substr($ps_locale, 0, 2) == "en") {
1825 if (($pn_rem = ($pn_numabs = abs($pn_num)) % 100) == 12) {
1826 $hs_words = substr($hs_words, 0, -2) . "fth";
1827 } elseif ($pn_rem >= 11 && $pn_rem <= 15) {
1830 switch ($pn_numabs % 10) {
1832 $hs_words = substr($hs_words, 0, -3) . "first";
1835 $hs_words = substr($hs_words, 0, -3) . "second";
1838 $hs_words = substr($hs_words, 0, -3) . "ird";
1841 $hs_words = substr($hs_words, 0, -2) . "fth";
1844 switch (substr($hs_words, -1)) {
1846 $hs_words = substr($hs_words, 0, -1) . "th";
1852 $hs_words = substr($hs_words, 0, -1) . "ieth";
1861 if (($hs_char = substr($ps_capitalization, 0, 1)) ==
1862 strtolower($hs_char)) {
1864 $hs_words = strtolower($hs_words);
1865 } elseif (($hs_char = substr($ps_capitalization, 1, 1)) ==
1866 strtolower($hs_char)) {
1868 $hs_words = ucwords($hs_words);
1871 $hs_words = strtoupper($hs_words);
1879 // {{{ _formatNumber()
1882 * Formats a number according to the specified format string
1884 * Private helper function, for {@link Date::formatLikeSQL()}, which
1885 * interprets the codes '<b>SP</b>' and '<b>TH</b>' and the combination
1886 * of the two as follows:
1888 * - <b>TH</b> - Ordinal number
1889 * - <b>SP</b> - Spelled cardinal number
1890 * - <b>SPTH</b> - Spelled ordinal number (combination of '<b>SP</b>'
1891 * and '<b>TH</b>' in any order)
1894 * Code '<b>SP</b>' can have the following three variations (which
1895 * can also be used in combination with '<b>TH</b>'):
1897 * - <b>SP</b> - returns upper-case spelling, e.g. 'FOUR HUNDRED'
1898 * - <b>Sp</b> - returns spelling with first character of each word
1899 * capitalized, e.g. 'Four Hundred'
1900 * - <b>sp</b> - returns lower-case spelling, e.g. 'four hundred'
1902 * Code '<b>TH</b>' can have the following two variations (although in
1903 * combination with code '<b>SP</b>', the case specification of
1904 * '<b>SP</b>' takes precedence):
1906 * - <b>TH</b> - returns upper-case ordinal suffix, e.g. 400TH
1907 * - <b>th</b> - returns lower-case ordinal suffix, e.g. 400th
1909 * N.B. The format string is passed by reference, in order to pass back
1910 * the part of the format string that matches the valid codes '<b>SP</b>'
1911 * and '<b>TH</b>'. If none of these are found, then it is set to an
1912 * empty string; If both codes are found then a string is returned with
1913 * code '<b>SP</b>' preceding code '<b>TH</b>' (i.e. '<b>SPTH</b>',
1914 * '<b>Spth</b>' or '<b>spth</b>').
1916 * @param int $pn_num integer to be converted to words
1917 * @param string &$ps_format string of formatting codes (max. length 4)
1918 * @param int $pn_numofdigits no of digits to display if displayed as
1919 * numeral (i.e. not spelled out), not
1920 * including the sign (if negative); to
1921 * allow all digits specify 0
1922 * @param bool $pb_nopad boolean specifying whether to suppress
1923 * padding with leading noughts (if displayed
1925 * @param bool $pb_nosign boolean specifying whether to suppress the
1926 * display of the sign (if negative)
1927 * @param string $ps_locale language name abbreviation used for
1929 * @param string $ps_thousandsep optional thousand-separator (e.g. a comma)
1930 * numbers as spelled-out words
1931 * @param int $pn_padtype optional integer to specify padding (if
1932 * displayed as numeral) - can be
1933 * STR_PAD_LEFT or STR_PAD_RIGHT
1937 * @since Method available since Release 1.5.0
1939 public function _formatNumber(
1945 $ps_locale = "en_GB",
1946 $ps_thousandsep = null,
1947 $pn_padtype = STR_PAD_LEFT
1950 $hs_code1 = substr($ps_format, 0, 2);
1951 $hs_code2 = substr($ps_format, 2, 2);
1955 if (strtoupper($hs_code1) == "SP") {
1957 if (strtoupper($hs_code2) == "TH") {
1960 } elseif (strtoupper($hs_code1) == "TH") {
1962 if (strtoupper($hs_code2) == "SP") {
1967 $hn_absnum = abs($pn_num);
1968 if ($pn_numofdigits > 0 && strlen($hn_absnum) > $pn_numofdigits) {
1969 $hn_absnum = intval(substr($hn_absnum, -$pn_numofdigits));
1971 $hs_num = $hn_absnum;
1973 if (!is_null($hs_sp)) {
1974 // Spell out number:
1976 $ps_format = $hs_sp .
1977 (is_null($hs_th) ? "" : ($hs_sp == "SP" ? "TH" : "th"));
1978 return $this->_spellNumber(
1979 !$pb_nosign && $pn_num < 0 ?
1987 // Display number as Arabic numeral:
1990 $hs_num = str_pad($hs_num, $pn_numofdigits, "0", $pn_padtype);
1993 if (!is_null($ps_thousandsep)) {
1994 for ($i = strlen($hs_num) - 3; $i > 0; $i -= 3) {
1995 $hs_num = substr($hs_num, 0, $i) .
1997 substr($hs_num, $i);
2003 $hs_num = "-" . $hs_num;
2004 } elseif (!$pb_nopad) {
2005 $hs_num = " " . $hs_num;
2009 if (!is_null($hs_th)) {
2010 $ps_format = $hs_th;
2012 $this->_getOrdinalSuffix(
2014 substr($hs_th, 0, 1) == "T"
2025 // {{{ formatLikeSQL()
2028 * Formats the date according to the specified formatting code string,
2029 * based on SQL date-formatting codes
2031 * Most codes reproduce the no of digits equal to the length of the
2032 * code, for example, '<b>YYY</b>' will return the last 3 digits of
2033 * the year, and so the year 2007 will produce '007', and the year 89
2034 * will produce '089', unless the no-padding code is used as in
2035 * '<b>NPYYY</b>', which will return '89'.
2037 * For negative values, the sign will be discarded, unless the
2038 * '<b>S</b>' code is used in combination, but note that for positive
2039 * values the value will be padded with a leading space unless it
2040 * is suppressed with the no-padding modifier, for example for 2007:
2042 * - <b>YYYY</b> - returns '2007'
2043 * - <b>SYYYY</b> - returns ' 2007'
2044 * - <b>NPSYYYY</b> - returns '2007'
2046 * The no-padding modifier '<b>NP</b>' can be used with numeric codes
2047 * to suppress leading (or trailing in the case of code '<b>F</b>')
2048 * noughts, and with character-returning codes such as '<b>DAY</b>'
2049 * to suppress trailing spaces, which will otherwise be padded to the
2050 * maximum possible length of the return-value of the code; for
2051 * example, for Monday:
2053 * - <b>Day</b> - returns 'Monday ' because the maximum length of
2054 * this code is 'Wednesday';
2055 * - <b>NPDay</b> - returns 'Monday'
2057 * N.B. this code affects the code immediately following only, and
2058 * without this code the default is always to apply padding.
2060 * Most character-returning codes, such as '<b>MONTH</b>', will
2061 * set the capitalization according to the code, so for example:
2063 * - <b>MONTH</b> - returns upper-case spelling, e.g. 'JANUARY'
2064 * - <b>Month</b> - returns spelling with first character of each word
2065 * capitalized, e.g. 'January'
2066 * - <b>month</b> - returns lower-case spelling, e.g. 'january'
2068 * Where it makes sense, numeric codes can be combined with a following
2069 * '<b>SP</b>' code which spells out the number, or with a '<b>TH</b>'
2070 * code, which renders the code as an ordinal ('<b>TH</b>' only works
2071 * in English), for example, for 31st December:
2073 * - <b>DD</b> - returns '31'
2074 * - <b>DDTH</b> - returns '31ST'
2075 * - <b>DDth</b> - returns '31st'
2076 * - <b>DDSP</b> - returns 'THIRTY-ONE'
2077 * - <b>DDSp</b> - returns 'Thirty-one'
2078 * - <b>DDsp</b> - returns 'thirty-one'
2079 * - <b>DDSPTH</b> - returns 'THIRTY-FIRST'
2080 * - <b>DDSpth</b> - returns 'Thirty-first'
2081 * - <b>DDspth</b> - returns 'thirty-first'
2084 * All formatting options:
2086 * - <b>-</b> (All punctuation and white-space is reproduced unchanged)
2092 * - <b>"text"</b> - Quoted text is reproduced unchanged (escape using
2094 * - <b>AD</b> - AD indicator with or without full stops
2096 * - <b>AM</b> - Meridian indicator with or without full stops
2098 * - <b>BC</b> - BC indicator with or without full stops
2100 * - <b>BCE</b> - BCE indicator with or without full stops
2102 * - <b>CC</b> - Century, i.e. the year divided by 100, discarding the
2103 * remainder; '<b>S</b>' prefixes negative years with a
2106 * - <b>CE</b> - CE indicator with or without full stops
2108 * - <b>D</b> - Day of week (0-6), where 0 represents Sunday
2109 * - <b>DAY</b> - Name of day, padded with blanks to display width of the
2110 * widest name of day in the locale of the machine
2111 * - <b>DD</b> - Day of month (1-31)
2112 * - <b>DDD</b> - Day of year (1-366)
2113 * - <b>DY</b> - Abbreviated name of day
2114 * - <b>FFF</b> - Fractional seconds; no radix character is printed. The
2115 * no of '<b>F</b>'s determines the no of digits of the
2116 * part-second to return; e.g. 'HH:MI:SS.FF'
2117 * - <b>F[integer]</b> - The integer after '<b>F</b>' specifies the
2118 * number of digits of the part-second to return.
2119 * This is an alternative to using several
2120 * '<b>F</b>'s in sequence, and '<b>F3</b>' is thus
2121 * equivalent to using '<b>FFF</b>'.
2122 * - <b>HH</b> - Hour of day (0-23)
2123 * - <b>HH12</b> - Hour of day (1-12)
2124 * - <b>HH24</b> - Hour of day (0-23)
2125 * - <b>ID</b> - Day of week (1-7) based on the ISO 8601 standard (see
2127 * - <b>IW</b> - Week of year (1-52 or 1-53) based on the
2128 * {@link http://en.wikipedia.org/wiki/ISO_week_date ISO 8601 standard}
2129 * - <b>IYYY</b> - 4-digit year based on the ISO 8601 standard (see
2130 * '<b>IW</b>'); '<b>S</b>' prefixes negative years with a
2133 * - <b>IYY</b> - Last 3, 2, or 1 digit(s) of ISO year
2136 * - <b>J</b> - {@link http://en.wikipedia.org/wiki/Julian_day Julian day} -
2137 * the number of days since Monday, 24th November, 4714 B.C.
2138 * (proleptic Gregorian calendar)
2139 * - <b>MI</b> - Minute (0-59)
2140 * - <b>MM</b> - Month (01-12; January = 01)
2141 * - <b>MON</b> - Abbreviated name of month
2142 * - <b>MONTH</b> - Name of month, padded with blanks to display width of
2143 * the widest name of month in the date language used for
2144 * - <b>PM</b> - Meridian indicator with or without full stops
2146 * - <b>Q</b> - Quarter of year (1, 2, 3, 4; January - March = 1)
2147 * - <b>RM</b> - Roman numeral month (I-XII; January = I); N.B. padded
2148 * with leading spaces.
2149 * - <b>SS</b> - Second (0-59)
2150 * - <b>SSSSS</b> - Seconds past midnight (0-86399)
2151 * - <b>TZC</b> - Abbreviated form of time zone name, e.g. 'GMT', or the
2152 * abbreviation for Summer time if the date falls in Summer
2154 * N.B. this is not a unique identifier - for this purpose
2155 * use the time zone region (code '<b>TZR</b>').
2156 * - <b>TZH</b> - Time zone hour; '<b>S</b>' prefixes the hour with the
2157 * correct sign, (+/-), which otherwise is not displayed.
2158 * Note that the leading nought can be suppressed with the
2159 * no-padding code '<b>NP</b>'). Also note that if you
2160 * combine with the '<b>SP</b>' code, the sign will not be
2161 * spelled out. (I.e. '<b>STZHSp</b>' will produce '+One',
2162 * for example, and not 'Plus One'.
2163 * '<b>TZH:TZM</b>' will produce, for example, '+05:30'.
2164 * (Also see '<b>TZM</b>' format code)
2166 * - <b>TZI</b> - Whether or not the date is in Summer time (daylight
2167 * saving time). Returns '1' if Summer time, else '0'.
2168 * - <b>TZM</b> - Time zone minute, without any +/- sign. (Also see
2169 * '<b>TZH</b>' format element)
2170 * - <b>TZN</b> - Long form of time zone name, e.g.
2171 * 'Greenwich Mean Time', or the name of the Summer time if
2172 * the date falls in Summer time, e.g.
2173 * 'British Summer Time'. N.B. this is not a unique
2174 * identifier - for this purpose use the time zone region
2175 * (code '<b>TZR</b>').
2176 * - <b>TZO</b> - Time zone offset in ISO 8601 form - that is, 'Z' if
2177 * UTC, else [+/-][hh]:[mm] (which would be equivalent
2178 * to '<b>STZH:TZM</b>'). Note that this result is right
2180 * with spaces by default, (i.e. if 'Z').
2181 * - <b>TZS</b> - Time zone offset in seconds; '<b>S</b>' prefixes
2182 * negative sign with minus sign '-' if negative, and no
2183 * sign if positive (i.e. -43200 to 50400).
2185 * - <b>TZR</b> - Time zone region, that is, the name or ID of the time
2186 * zone e.g. 'Europe/London'. This value is unique for
2188 * - <b>U</b> - Seconds since the Unix Epoch -
2189 * January 1 1970 00:00:00 GMT
2190 * - <b>W</b> - 'Absolute' week of month (1-5), counting week 1 as
2191 * 1st-7th of the year, regardless of the day
2192 * - <b>W1</b> - Week of year (1-54), counting week 1 as the week that
2193 * contains 1st January
2194 * - <b>W4</b> - Week of year (1-53), counting week 1 as the week that
2195 * contains 4th January (i.e. first week with at least 4
2197 * - <b>W7</b> - Week of year (1-53), counting week 1 as the week that
2198 * contains 7th January (i.e. first full week)
2199 * - <b>WW</b> - 'Absolute' week of year (1-53), counting week 1 as
2200 * 1st-7th of the year, regardless of the day
2201 * - <b>YEAR</b> - Year, spelled out; '<b>S</b>' prefixes negative
2202 * years with 'MINUS'; N.B. '<b>YEAR</b>' differs from
2203 * '<b>YYYYSP</b>' in that the first will render 1923,
2204 * for example, as 'NINETEEN TWENTY-THREE, and the
2205 * second as 'ONE THOUSAND NINE HUNDRED TWENTY-THREE'
2207 * - <b>YYYY</b> - 4-digit year; '<b>S</b>' prefixes negative years
2210 * - <b>YYY</b> - Last 3, 2, or 1 digit(s) of year
2213 * - <b>Y,YYY</b> - Year with thousands-separator in this position; five
2214 * possible separators
2216 * - <b>Y�YYY</b> - N.B. space-dot (mid-dot, interpunct) is valid only in
2217 * ISO 8859-1 (so take care when using UTF-8 in
2222 * In addition the following codes can be used in combination with other
2224 * Codes that modify the next code in the format string:
2226 * - <b>NP</b> - 'No Padding' - Returns a value with no trailing blanks
2227 * and no leading or trailing noughts; N.B. that the
2228 * default is to include this padding in the return string.
2229 * N.B. affects the code immediately following only.
2231 * Codes that modify the previous code in the format string (can only
2232 * be used with integral codes such as '<b>MM</b>'):
2234 * - <b>TH</b> - Ordinal number
2235 * - <b>SP</b> - Spelled cardinal number
2236 * - <b>SPTH</b> - Spelled ordinal number (combination of '<b>SP</b>'
2237 * and '<b>TH</b>' in any order)
2240 * Code '<b>SP</b>' can have the following three variations (which can
2241 * also be used in combination with '<b>TH</b>'):
2243 * - <b>SP</b> - returns upper-case spelling, e.g. 'FOUR HUNDRED'
2244 * - <b>Sp</b> - returns spelling with first character of each word
2245 * capitalized, e.g. 'Four Hundred'
2246 * - <b>sp</b> - returns lower-case spelling, e.g. 'four hundred'
2248 * Code '<b>TH</b>' can have the following two variations (although in
2249 * combination with code '<b>SP</b>', the case specification of
2250 * '<b>SP</b>' takes precedence):
2252 * - <b>TH</b> - returns upper-case ordinal suffix, e.g. 400TH
2253 * - <b>th</b> - returns lower-case ordinal suffix, e.g. 400th
2255 * @param string $ps_format format string for returned date/time
2256 * @param string $ps_locale language name abbreviation used for formatting
2257 * numbers as spelled-out words
2259 * @return string date/time in given format
2261 * @see Date::format(), Date::formatLikeStrftime(), Date::formatLikeDate()
2262 * @since Method available since Release 1.5.0
2264 public function formatLikeSQL($ps_format, $ps_locale = "en_GB")
2267 '/^("([^"\\\\]|\\\\\\\\|\\\\")*"|(D{1,3}|S?C+|' .
2268 'HH(12|24)?|I[DW]|S?IY*|J|M[IM]|Q|SS(SSS)?|S?TZ[HS]|' .
2269 'TZM|U|W[W147]?|S?Y{1,3}([,.�\' ]?YYY)*)(SP(TH)?|' .
2270 'TH(SP)?)?|AD|A\.D\.|AM|A\.M\.|BCE?|B\.C\.(E\.)?|CE|' .
2271 'C\.E\.|DAY|DY|F(F*|[1-9][0-9]*)|MON(TH)?|NP|PM|' .
2272 'P\.M\.|RM|TZ[CINOR]|S?YEAR|[^A-Z0-9"])*$/i',
2275 return PEAR::raiseError(
2276 "Invalid date format '$ps_format'",
2277 DATE_ERROR_INVALIDFORMATSTRING
2284 $hb_nopadflag = false;
2285 $hb_showsignflag = false;
2287 $hn_weekdaypad = null;
2288 $hn_monthpad = null;
2292 $hn_tzoffset = null;
2294 while ($i < strlen($ps_format)) {
2297 if ($hb_nopadflag) {
2302 if ($hb_showsignflag) {
2307 $hb_nopadflag = false;
2308 $hb_showsignflag = false;
2310 switch ($hs_char = substr($ps_format, $i, 1)) {
2323 '/(([^"\\\\]|\\\\\\\\|\\\\")*)"/',
2326 PREG_OFFSET_CAPTURE,
2329 $ret .= str_replace(
2330 array('\\\\', '\\"'),
2334 $i += strlen($ha_matches[0][0]) + 1;
2340 if (strtoupper(substr($ps_format, $i, 4)) == "A.D.") {
2341 $ret .= $this->year >= 0 ?
2342 ($hb_lower ? "a.d." : "A.D.") :
2343 ($hb_lower ? "b.c." : "B.C.");
2345 } elseif (strtoupper(substr($ps_format, $i, 2)) == "AD") {
2346 $ret .= $this->year >= 0 ?
2347 ($hb_lower ? "ad" : "AD") :
2348 ($hb_lower ? "bc" : "BC");
2351 if ($this->ob_invalidtime) {
2352 return $this->_getErrorInvalidTime();
2354 if (strtoupper(substr($ps_format, $i, 4)) == "A.M.") {
2355 $ret .= $this->hour < 12 ?
2356 ($hb_lower ? "a.m." : "A.M.") :
2357 ($hb_lower ? "p.m." : "P.M.");
2359 } elseif (strtoupper(substr($ps_format, $i, 2)) == "AM") {
2360 $ret .= $this->hour < 12 ?
2361 ($hb_lower ? "am" : "AM") :
2362 ($hb_lower ? "pm" : "PM");
2372 // Check for 'B.C.E.' first:
2374 if (strtoupper(substr($ps_format, $i, 6)) == "B.C.E.") {
2375 if ($this->year >= 0) {
2376 $hs_era = $hb_lower ? "c.e." : "C.E.";
2379 str_pad($hs_era, 6, " ", STR_PAD_RIGHT);
2381 $ret .= $hb_lower ? "b.c.e." : "B.C.E.";
2384 } elseif (strtoupper(substr($ps_format, $i, 3)) == "BCE") {
2385 if ($this->year >= 0) {
2386 $hs_era = $hb_lower ? "ce" : "CE";
2389 str_pad($hs_era, 3, " ", STR_PAD_RIGHT);
2391 $ret .= $hb_lower ? "bce" : "BCE";
2394 } elseif (strtoupper(substr($ps_format, $i, 4)) == "B.C.") {
2395 $ret .= $this->year >= 0 ?
2396 ($hb_lower ? "a.d." : "A.D.") :
2397 ($hb_lower ? "b.c." : "B.C.");
2399 } elseif (strtoupper(substr($ps_format, $i, 2)) == "BC") {
2400 $ret .= $this->year >= 0 ?
2401 ($hb_lower ? "ad" : "AD") :
2402 ($hb_lower ? "bc" : "BC");
2411 if (strtoupper(substr($ps_format, $i, 4)) == "C.E.") {
2412 if ($this->year >= 0) {
2413 $hs_era = $hb_lower ? "c.e." : "C.E.";
2416 str_pad($hs_era, 6, " ", STR_PAD_RIGHT);
2418 $ret .= $hb_lower ? "b.c.e." : "B.C.E.";
2421 } elseif (strtoupper(substr($ps_format, $i, 2)) == "CE") {
2422 if ($this->year >= 0) {
2423 $hs_era = $hb_lower ? "ce" : "CE";
2426 str_pad($hs_era, 3, " ", STR_PAD_RIGHT);
2428 $ret .= $hb_lower ? "bce" : "BCE";
2435 while (strtoupper(substr(
2443 // Check next code is not 'CE' or 'C.E.'
2445 if ($hn_codelen > 1 &&
2449 $i + $hn_codelen - 1,
2454 $i + $hn_codelen - 1,
2461 $hn_century = intval($this->year / 100);
2462 $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4);
2463 $hs_century = $this->_formatNumber(
2471 if (Pear::isError($hs_century)) {
2475 $ret .= $hs_century;
2476 $i += $hn_codelen + strlen($hs_numberformat);
2484 if (strtoupper(substr($ps_format, $i, 3)) == "DAY") {
2485 $hs_day = Date_Calc::getWeekdayFullname(
2492 if (is_null($hn_weekdaypad)) {
2493 // Set week-day padding variable:
2496 foreach (Date_Calc::getWeekDays() as $hs_weekday) {
2497 $hn_weekdaypad = max(
2512 strtolower($hs_day) :
2513 (substr($ps_format, $i + 1, 1) == "A" ?
2514 strtoupper($hs_day) :
2517 } elseif (strtoupper(substr($ps_format, $i, 2)) == "DY") {
2518 $hs_day = Date_Calc::getWeekdayAbbrname(
2524 strtolower($hs_day) :
2525 (substr($ps_format, $i + 1, 1) == "Y" ?
2526 strtoupper($hs_day) :
2529 } elseif (strtoupper(substr($ps_format, $i, 3)) == "DDD" &&
2530 strtoupper(substr($ps_format, $i + 2, 3)) != "DAY" &&
2531 strtoupper(substr($ps_format, $i + 2, 2)) != "DY"
2533 $hn_day = Date_Calc::dayOfYear(
2538 $hs_numberformat = substr($ps_format, $i + 3, 4);
2539 $hs_day = $this->_formatNumber(
2547 if (Pear::isError($hs_day)) {
2552 $i += 3 + strlen($hs_numberformat);
2553 } elseif (strtoupper(substr($ps_format, $i, 2)) == "DD" &&
2554 strtoupper(substr($ps_format, $i + 1, 3)) != "DAY" &&
2555 strtoupper(substr($ps_format, $i + 1, 2)) != "DY"
2557 $hs_numberformat = substr($ps_format, $i + 2, 4);
2558 $hs_day = $this->_formatNumber(
2566 if (Pear::isError($hs_day)) {
2571 $i += 2 + strlen($hs_numberformat);
2575 $hn_day = Date_Calc::dayOfWeek(
2580 $hs_numberformat = substr($ps_format, $i + 1, 4);
2581 $hs_day = $this->_formatNumber(
2589 if (Pear::isError($hs_day)) {
2594 $i += 1 + strlen($hs_numberformat);
2600 if ($this->ob_invalidtime) {
2601 return $this->_getErrorInvalidTime();
2604 if (is_numeric(substr($ps_format, $i + $hn_codelen, 1))) {
2606 while (is_numeric(substr($ps_format, $i + $hn_codelen, 1))) {
2610 $hn_partsecdigits = substr($ps_format, $i + 1, $hn_codelen - 1);
2612 while (strtoupper(substr(
2620 // Check next code is not F[numeric]:
2622 if ($hn_codelen > 1 &&
2623 is_numeric(substr($ps_format, $i + $hn_codelen, 1))) {
2627 $hn_partsecdigits = $hn_codelen;
2630 $hs_partsec = (string)$this->partsecond;
2632 '/^([0-9]+)(\.([0-9]+))?E-([0-9]+)$/i',
2637 str_repeat("0", $ha_matches[4] - strlen($ha_matches[1])) .
2641 $hs_partsec = substr($hs_partsec, 2);
2643 $hs_partsec = substr($hs_partsec, 0, $hn_partsecdigits);
2645 // '_formatNumber() will not work for this because the
2646 // part-second is an int, and we want it to behave like a float:
2649 $hs_partsec = rtrim($hs_partsec, "0");
2650 if ($hs_partsec == "") {
2654 $hs_partsec = str_pad(
2662 $ret .= $hs_partsec;
2667 if ($this->ob_invalidtime) {
2668 return $this->_getErrorInvalidTime();
2670 if (strtoupper(substr($ps_format, $i, 4)) == "HH12") {
2671 $hn_hour = $this->hour % 12;
2672 if ($hn_hour == 0) {
2678 // Code 'HH' or 'HH24':
2680 $hn_hour = $this->hour;
2681 $hn_codelen = strtoupper(substr(
2685 )) == "HH24" ? 4 : 2;
2688 $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4);
2689 $hs_hour = $this->_formatNumber(
2697 if (Pear::isError($hs_hour)) {
2702 $i += $hn_codelen + strlen($hs_numberformat);
2706 if (is_null($hn_isoyear)) {
2707 list($hn_isoyear, $hn_isoweek, $hn_isoday) =
2708 Date_Calc::isoWeekDate(
2715 if (strtoupper(substr($ps_format, $i, 2)) == "ID" &&
2716 strtoupper(substr($ps_format, $i + 1, 3)) != "DAY"
2718 $hs_numberformat = substr($ps_format, $i + 2, 4);
2719 $hs_isoday = $this->_formatNumber(
2727 if (Pear::isError($hs_isoday)) {
2732 $i += 2 + strlen($hs_numberformat);
2733 } elseif (strtoupper(substr($ps_format, $i, 2)) == "IW") {
2734 $hs_numberformat = substr($ps_format, $i + 2, 4);
2735 $hs_isoweek = $this->_formatNumber(
2743 if (Pear::isError($hs_isoweek)) {
2747 $ret .= $hs_isoweek;
2748 $i += 2 + strlen($hs_numberformat);
2753 while (strtoupper(substr(
2761 $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4);
2762 $hs_isoyear = $this->_formatNumber(
2770 if (Pear::isError($hs_isoyear)) {
2774 $ret .= $hs_isoyear;
2775 $i += $hn_codelen + strlen($hs_numberformat);
2781 $hn_jd = Date_Calc::dateToDays(
2786 $hs_numberformat = substr($ps_format, $i + 1, 4);
2788 // Allow sign if negative; allow all digits (specify nought);
2789 // suppress padding:
2791 $hs_jd = $this->_formatNumber(
2799 if (Pear::isError($hs_jd)) {
2804 $i += 1 + strlen($hs_numberformat);
2810 if (strtoupper(substr($ps_format, $i, 2)) == "MI") {
2811 if ($this->ob_invalidtime) {
2812 return $this->_getErrorInvalidTime();
2814 $hs_numberformat = substr($ps_format, $i + 2, 4);
2815 $hs_minute = $this->_formatNumber(
2823 if (Pear::isError($hs_minute)) {
2828 $i += 2 + strlen($hs_numberformat);
2829 } elseif (strtoupper(substr($ps_format, $i, 2)) == "MM") {
2830 $hs_numberformat = substr($ps_format, $i + 2, 4);
2831 $hs_month = $this->_formatNumber(
2839 if (Pear::isError($hs_month)) {
2844 $i += 2 + strlen($hs_numberformat);
2845 } elseif (strtoupper(substr($ps_format, $i, 5)) == "MONTH") {
2846 $hs_month = Date_Calc::getMonthFullname($this->month);
2849 if (is_null($hn_monthpad)) {
2850 // Set month padding variable:
2853 foreach (Date_Calc::getMonthNames() as $hs_monthofyear) {
2856 strlen($hs_monthofyear)
2860 $hs_month = str_pad(
2869 strtolower($hs_month) :
2870 (substr($ps_format, $i + 1, 1) == "O" ?
2871 strtoupper($hs_month) :
2874 } elseif (strtoupper(substr($ps_format, $i, 3)) == "MON") {
2875 $hs_month = Date_Calc::getMonthAbbrname($this->month);
2877 strtolower($hs_month) :
2878 (substr($ps_format, $i + 1, 1) == "O" ?
2879 strtoupper($hs_month) :
2887 // No-Padding rule 'NP' applies to the next code (either trailing
2888 // spaces or leading/trailing noughts):
2890 $hb_nopadflag = true;
2897 if ($this->ob_invalidtime) {
2898 return $this->_getErrorInvalidTime();
2900 if (strtoupper(substr($ps_format, $i, 4)) == "P.M.") {
2901 $ret .= $this->hour < 12 ?
2902 ($hb_lower ? "a.m." : "A.M.") :
2903 ($hb_lower ? "p.m." : "P.M.");
2905 } elseif (strtoupper(substr($ps_format, $i, 2)) == "PM") {
2906 $ret .= $this->hour < 12 ?
2907 ($hb_lower ? "am" : "AM") :
2908 ($hb_lower ? "pm" : "PM");
2915 // N.B. Current implementation ignores the day and year, but
2916 // it is possible that a different implementation might be
2917 // desired, so pass these parameters anyway:
2919 $hn_quarter = Date_Calc::quarterOfYear(
2924 $hs_numberformat = substr($ps_format, $i + 1, 4);
2925 $hs_quarter = $this->_formatNumber(
2933 if (Pear::isError($hs_quarter)) {
2937 $ret .= $hs_quarter;
2938 $i += 1 + strlen($hs_numberformat);
2946 switch ($this->month) {
2948 $hs_monthroman = "i";
2951 $hs_monthroman = "ii";
2954 $hs_monthroman = "iii";
2957 $hs_monthroman = "iv";
2960 $hs_monthroman = "v";
2963 $hs_monthroman = "vi";
2966 $hs_monthroman = "vii";
2969 $hs_monthroman = "viii";
2972 $hs_monthroman = "ix";
2975 $hs_monthroman = "x";
2978 $hs_monthroman = "xi";
2981 $hs_monthroman = "xii";
2985 $hs_monthroman = $hb_lower ?
2987 strtoupper($hs_monthroman);
2990 str_pad($hs_monthroman, 4, " ", STR_PAD_LEFT);
2995 // Check for 'SSSSS' before 'SS':
2997 if (strtoupper(substr($ps_format, $i, 5)) == "SSSSS") {
2998 if ($this->ob_invalidtime) {
2999 return $this->_getErrorInvalidTime();
3001 $hs_numberformat = substr($ps_format, $i + 5, 4);
3002 $hn_second = Date_Calc::secondsPastMidnight(
3007 $hs_second = $this->_formatNumber(
3015 if (Pear::isError($hs_second)) {
3020 $i += 5 + strlen($hs_numberformat);
3021 } elseif (strtoupper(substr($ps_format, $i, 2)) == "SS") {
3022 if ($this->ob_invalidtime) {
3023 return $this->_getErrorInvalidTime();
3025 $hs_numberformat = substr($ps_format, $i + 2, 4);
3026 $hs_second = $this->_formatNumber(
3034 if (Pear::isError($hs_second)) {
3039 $i += 2 + strlen($hs_numberformat);
3041 // One of the following codes:
3049 $hb_showsignflag = true;
3051 $hb_nopadflag = true;
3062 if (strtoupper(substr($ps_format, $i, 3)) == "TZR") {
3063 // This time-zone-related code can be called when the time is
3064 // invalid, but the others should return an error:
3066 $ret .= $this->getTZID();
3069 if ($this->ob_invalidtime) {
3070 return $this->_getErrorInvalidTime();
3073 if (strtoupper(substr($ps_format, $i, 3)) == "TZC") {
3074 $ret .= $this->getTZShortName();
3076 } elseif (strtoupper(substr($ps_format, $i, 3)) == "TZH") {
3077 if (is_null($hn_tzoffset)) {
3078 $hn_tzoffset = $this->getTZOffset();
3081 $hs_numberformat = substr($ps_format, $i + 3, 4);
3082 $hn_tzh = intval($hn_tzoffset / 3600000);
3084 // Suppress sign here (it is added later):
3086 $hs_tzh = $this->_formatNumber(
3094 if (Pear::isError($hs_tzh)) {
3098 // Display sign, even if positive:
3100 $ret .= ($hb_nosign ? "" : ($hn_tzh >= 0 ? '+' : '-')) .
3102 $i += 3 + strlen($hs_numberformat);
3103 } elseif (strtoupper(substr($ps_format, $i, 3)) == "TZI") {
3104 $ret .= ($this->inDaylightTime() ? '1' : '0');
3106 } elseif (strtoupper(substr($ps_format, $i, 3)) == "TZM") {
3107 if (is_null($hn_tzoffset)) {
3108 $hn_tzoffset = $this->getTZOffset();
3111 $hs_numberformat = substr($ps_format, $i + 3, 4);
3112 $hn_tzm = intval(($hn_tzoffset % 3600000) / 60000);
3116 $hs_tzm = $this->_formatNumber(
3124 if (Pear::isError($hs_tzm)) {
3129 $i += 3 + strlen($hs_numberformat);
3130 } elseif (strtoupper(substr($ps_format, $i, 3)) == "TZN") {
3131 $ret .= $this->getTZLongName();
3133 } elseif (strtoupper(substr($ps_format, $i, 3)) == "TZO") {
3134 if (is_null($hn_tzoffset)) {
3135 $hn_tzoffset = $this->getTZOffset();
3138 $hn_tzh = intval(abs($hn_tzoffset) / 3600000);
3139 $hn_tzm = intval((abs($hn_tzoffset) % 3600000) / 60000);
3141 if ($hn_tzoffset == 0) {
3142 $ret .= $hb_nopad ? "Z" : "Z ";
3144 // Display sign, even if positive:
3146 $ret .= ($hn_tzoffset >= 0 ? '+' : '-') .
3147 sprintf("%02d", $hn_tzh) .
3149 sprintf("%02d", $hn_tzm);
3152 } elseif (strtoupper(substr($ps_format, $i, 3)) == "TZS") {
3153 if (is_null($hn_tzoffset)) {
3154 $hn_tzoffset = $this->getTZOffset();
3157 $hs_numberformat = substr($ps_format, $i + 3, 4);
3158 $hn_tzs = intval($hn_tzoffset / 1000);
3159 $hs_tzs = $this->_formatNumber(
3167 if (Pear::isError($hs_tzs)) {
3172 $i += 3 + strlen($hs_numberformat);
3179 if ($this->ob_invalidtime) {
3180 return $this->_getErrorInvalidTime();
3182 $hn_unixtime = $this->getTime();
3183 $hs_numberformat = substr($ps_format, $i + 1, 4);
3185 // Allow sign if negative; allow all digits (specify nought);
3186 // suppress padding:
3188 $hs_unixtime = $this->_formatNumber(
3196 if (Pear::isError($hs_unixtime)) {
3197 return $hs_unixtime;
3200 $ret .= $hs_unixtime;
3201 $i += 1 + strlen($hs_numberformat);
3205 // Check for 'WW' before 'W':
3207 if (strtoupper(substr($ps_format, $i, 2)) == "WW") {
3208 $hn_week = Date_Calc::weekOfYearAbsolute(
3213 $hs_numberformat = substr($ps_format, $i + 2, 4);
3214 $hs_week = $this->_formatNumber(
3222 if (Pear::isError($hs_week)) {
3227 $i += 2 + strlen($hs_numberformat);
3228 } elseif (strtoupper(substr($ps_format, $i, 2)) == "W1") {
3229 $hn_week = Date_Calc::weekOfYear1st(
3234 $hs_numberformat = substr($ps_format, $i + 2, 4);
3235 $hs_week = $this->_formatNumber(
3243 if (Pear::isError($hs_week)) {
3248 $i += 2 + strlen($hs_numberformat);
3249 } elseif (strtoupper(substr($ps_format, $i, 2)) == "W4") {
3250 $ha_week = Date_Calc::weekOfYear4th(
3255 $hn_week = $ha_week[1];
3256 $hs_numberformat = substr($ps_format, $i + 2, 4);
3257 $hs_week = $this->_formatNumber(
3265 if (Pear::isError($hs_week)) {
3270 $i += 2 + strlen($hs_numberformat);
3271 } elseif (strtoupper(substr($ps_format, $i, 2)) == "W7") {
3272 $ha_week = Date_Calc::weekOfYear7th(
3277 $hn_week = $ha_week[1];
3278 $hs_numberformat = substr($ps_format, $i + 2, 4);
3279 $hs_week = $this->_formatNumber(
3287 if (Pear::isError($hs_week)) {
3292 $i += 2 + strlen($hs_numberformat);
3296 $hn_week = Date_Calc::weekOfMonthAbsolute(
3301 $hs_numberformat = substr($ps_format, $i + 1, 4);
3302 $hs_week = $this->_formatNumber(
3310 if (Pear::isError($hs_week)) {
3315 $i += 1 + strlen($hs_numberformat);
3321 // Check for 'YEAR' first:
3323 if (strtoupper(substr($ps_format, $i, 4)) == "YEAR") {
3324 switch (substr($ps_format, $i, 2)) {
3326 $hs_spformat = "SP";
3329 $hs_spformat = "Sp";
3332 $hs_spformat = "sp";
3335 if (($hn_yearabs = abs($this->year)) < 100 ||
3336 $hn_yearabs % 100 < 10) {
3337 $hs_numberformat = $hs_spformat;
3339 // Allow all digits (specify nought); padding irrelevant:
3341 $hs_year = $this->_formatNumber(
3349 if (Pear::isError($hs_year)) {
3355 // Year is spelled 'Nineteen Twelve' rather than
3356 // 'One thousand Nine Hundred Twelve':
3358 $hn_century = intval($this->year / 100);
3359 $hs_numberformat = $hs_spformat;
3361 // Allow all digits (specify nought); padding irrelevant:
3363 $hs_century = $this->_formatNumber(
3371 if (Pear::isError($hs_century)) {
3375 $ret .= $hs_century . " ";
3377 $hs_numberformat = $hs_spformat;
3379 // Discard sign; padding irrelevant:
3381 $hs_year = $this->_formatNumber(
3389 if (Pear::isError($hs_year)) {
3401 while (strtoupper(substr(
3409 $hs_thousandsep = null;
3410 $hn_thousandseps = 0;
3411 if ($hn_codelen <= 3) {
3413 '/([,.�\' ])YYY/i',
3422 $hs_thousandsep = $ha_matches[1];
3427 // Check next code is not 'YEAR'
3429 if ($hn_codelen > 1 &&
3432 $i + $hn_codelen - 1,
3438 $hs_numberformat = substr($ps_format, $i + $hn_codelen, 4);
3439 $hs_year = $this->_formatNumber(
3449 if (Pear::isError($hs_year)) {
3454 $i += $hn_codelen + strlen($hs_numberformat);
3469 // {{{ formatLikeDate()
3472 * Formats the date according to the specified formatting code string,
3473 * based on {@link http://www.php.net/date date()}
3475 * All date() formatting options are supported except '<b>B</b>'. This
3476 * function also responds to the DATE_* constants, such as DATE_COOKIE,
3477 * which are specified at:
3479 * {@link http://www.php.net/manual/en/datetime.constants.php}
3482 * Formatting options:
3486 * - <b>d</b> - Day of the month, 2 digits with leading zeros (01 to 31)
3487 * - <b>D</b> - A textual representation of a day, three letters ('Mon'
3489 * - <b>j</b> - Day of the month without leading zeros (1 to 31)
3490 * - <b>l</b> - [lowercase 'L'] A full textual representation of the day
3491 * of the week ('Sunday' to 'Saturday')
3492 * - <b>N</b> - ISO-8601 numeric representation of the day of the week
3493 * (1 (for Monday) to 7 (for Sunday)) (see '<b>W</b>')
3494 * - <b>S</b> - English ordinal suffix for the day of the month, 2
3495 * characters ('st', 'nd', 'rd' or 'th')
3496 * - <b>w</b> - Numeric representation of the day of the week (0 (for
3497 * Sunday) to 6 (for Saturday))
3498 * - <b>z</b> - The day of the year, starting from 0 (0 to 365)
3502 * - <b>W</b> - {@link http://en.wikipedia.org/wiki/ISO_week_date ISO-8601}
3503 * week number of year, weeks starting on Monday (00 to 53)
3507 * - <b>F</b> - A full textual representation of a month ('January' to
3509 * - <b>m</b> - Numeric representation of a month, with leading zeros
3511 * - <b>M</b> - A short textual representation of a month, three letters
3513 * - <b>n</b> - Numeric representation of a month, without leading zeros
3515 * - <b>t</b> - Number of days in the given month (28 to 31)
3519 * - <b>L</b> - Whether it is a leap year (1 if it is a leap year, 0
3521 * - <b>o</b> - ISO-8601 year number (see '<b>W</b>'). This has the same
3522 * value as '<b>Y</b>', except that if the ISO week number
3523 * ('<b>W</b>') belongs to the previous or next year, that
3524 * year is used instead.
3525 * - <b>Y</b> - A full numeric representation of a year, 4 digits (0000
3527 * - <b>y</b> - A two digit representation of a year (00 to 99)
3531 * - <b>a</b> - Lowercase Ante meridiem and Post meridiem ('am' or
3533 * - <b>A</b> - Uppercase Ante meridiem and Post meridiem ('AM' or
3535 * - <b>g</b> - 12-hour format of an hour without leading zeros (1 to 12)
3536 * - <b>G</b> - 24-hour format of an hour without leading zeros (0 to 23)
3537 * - <b>h</b> - 12-hour format of an hour with leading zeros (01 to 12)
3538 * - <b>H</b> - 24-hour format of an hour with leading zeros (00 to 23)
3539 * - <b>i</b> - Minutes with leading zeros (00 to 59)
3540 * - <b>s</b> - Seconds, with leading zeros (00 to 59)
3541 * - <b>u</b> - Milliseconds, e.g. '54321'
3545 * - <b>e</b> - Timezone identifier, e.g. Europe/London
3546 * - <b>I</b> - Whether or not the date is in Summer time (1 if Summer
3547 * time, 0 otherwise)
3548 * - <b>O</b> - Difference to Greenwich time (GMT) in hours, e.g. '+0200'
3549 * - <b>P</b> - Difference to Greenwich time (GMT) with colon between
3550 * hours and minutes, e.g. '+02:00'
3551 * - <b>T</b> - Timezone abbreviation, e.g. 'GMT', 'EST'
3552 * - <b>Z</b> - Timezone offset in seconds. The offset for timezones west
3553 * of UTC is always negative, and for those east of UTC is
3554 * always positive. (-43200 to 50400)
3558 * - <b>c</b> - ISO 8601 date, e.g. '2004-02-12T15:19:21+00:00'
3559 * - <b>r</b> - RFC 2822 formatted date, e.g.
3560 * 'Thu, 21 Dec 2000 16:01:07 +0200'
3561 * - <b>U</b> - Seconds since the Unix Epoch
3562 * (January 1 1970 00:00:00 GMT)
3564 * @param string $ps_format the format string for returned date/time
3566 * @return string date/time in given format
3568 * @see Date::format(), Date::formatLikeStrftime(), Date::formatLikeSQL()
3569 * @since Method available since Release 1.5.0
3571 public function formatLikeDate($ps_format)
3573 $hs_formatlikesqlstr = "";
3575 for ($i = 0; $i < strlen($ps_format); ++$i) {
3576 switch ($hs_char = substr($ps_format, $i, 1)) {
3578 $hs_formatlikesqlstr .= 'DD';
3581 $hs_formatlikesqlstr .= 'NPDy';
3584 $hs_formatlikesqlstr .= 'NPDD';
3587 $hs_formatlikesqlstr .= 'NPDay';
3590 $hs_formatlikesqlstr .= 'ID';
3593 $hs_formatlikesqlstr .= 'th';
3596 $hs_formatlikesqlstr .= 'D';
3599 $hs_formatlikesqlstr .= '"' . ($this->getDayOfYear() - 1) . '"';
3602 $hs_formatlikesqlstr .= 'IW';
3605 $hs_formatlikesqlstr .= 'NPMonth';
3608 $hs_formatlikesqlstr .= 'MM';
3611 $hs_formatlikesqlstr .= 'NPMon';
3614 $hs_formatlikesqlstr .= 'NPMM';
3617 $hs_formatlikesqlstr .= '"' . $this->getDaysInMonth() . '"';
3620 $hs_formatlikesqlstr .= '"' . ($this->isLeapYear() ? 1 : 0) . '"';
3623 $hs_formatlikesqlstr .= 'IYYY';
3626 $hs_formatlikesqlstr .= 'YYYY';
3629 $hs_formatlikesqlstr .= 'YY';
3632 $hs_formatlikesqlstr .= 'am';
3635 $hs_formatlikesqlstr .= 'AM';
3638 $hs_formatlikesqlstr .= 'NPHH12';
3641 $hs_formatlikesqlstr .= 'NPHH24';
3644 $hs_formatlikesqlstr .= 'HH12';
3647 $hs_formatlikesqlstr .= 'HH24';
3650 $hs_formatlikesqlstr .= 'MI';
3653 $hs_formatlikesqlstr .= 'SS';
3656 $hs_formatlikesqlstr .= 'SSFFF';
3659 $hs_formatlikesqlstr .= 'TZR';
3662 $hs_formatlikesqlstr .= 'TZI';
3665 $hs_formatlikesqlstr .= 'STZHTZM';
3668 $hs_formatlikesqlstr .= 'STZH:TZM';
3671 $hs_formatlikesqlstr .= 'TZC';
3674 $hs_formatlikesqlstr .= 'TZS';
3677 $hs_formatlikesqlstr .= 'YYYY-MM-DD"T"HH24:MI:SSSTZH:TZM';
3680 $hs_formatlikesqlstr .= 'Dy, DD Mon YYYY HH24:MI:SS STZHTZM';
3683 $hs_formatlikesqlstr .= 'U';
3686 $hs_char = substr($ps_format, ++$i, 1);
3687 $hs_formatlikesqlstr .= '"' .
3688 ($hs_char == '\\' ? '\\\\' : $hs_char) .
3692 $hs_formatlikesqlstr .= '"\\""';
3695 $hs_formatlikesqlstr .= '"' . $hs_char . '"';
3699 $ret = $this->formatLikeSQL($hs_formatlikesqlstr);
3700 if (PEAR::isError($ret) &&
3701 $ret->getCode() == DATE_ERROR_INVALIDFORMATSTRING) {
3702 return PEAR::raiseError(
3703 "Invalid date format '$ps_format'",
3704 DATE_ERROR_INVALIDFORMATSTRING
3713 // {{{ setFromTime()
3716 * Sets the date/time using a Unix time-stamp
3718 * This may only be valid for dates from 1970 to ~2038. N.B. this
3719 * function makes a call to {@link http://www.php.net/gmdate gmdate()}
3721 * @param int $pn_timestamp Unix time-stamp
3725 * @see Date::getTime(), Date::setDate()
3727 public function setFromTime($pn_timestamp)
3729 // Unix Time; N.B. Unix Time is defined relative to GMT,
3730 // so it needs to be adjusted for the current time zone;
3731 // however we do not know if it is in Summer time until
3732 // we have converted it from Unix time:
3735 // Get current time zone details:
3737 $hs_id = $this->getTZID();
3739 // Input Unix time as UTC:
3741 $this->tz = new Date_TimeZone("UTC");
3742 $this->setDate(gmdate("Y-m-d H:i:s", $pn_timestamp));
3744 // Convert back to correct time zone:
3746 $this->convertTZByID($hs_id);
3754 * Returns the date/time as Unix time-stamp (as returned for example by
3755 * {@link http://www.php.net/time time()})
3757 * This may only be valid for dates from 1970 to ~2038. N.B. this
3758 * function makes a call to {@link http://www.php.net/gmmktime gmmktime()}
3760 * @return int number of seconds since the Unix epoch
3763 public function getTime()
3765 if ($this->ob_invalidtime) {
3766 $ret = $this->_getErrorInvalidTime();
3768 // Use 'gmmktime()' and offset result (to get UTC):
3771 $this->on_standardhour,
3772 $this->on_standardminute,
3773 $this->on_standardsecond,
3774 $this->on_standardmonth,
3775 $this->on_standardday,
3776 $this->on_standardyear
3778 $this->tz->getRawOffset() / 1000; // N.B. Unix-time excludes
3789 * Returns the unique ID of the time zone, e.g. 'America/Chicago'
3791 * @return string the time zone ID
3793 * @see Date::setTZByID(), Date::getTZLongName(),
3794 * Date::getTZShortName(), Date_TimeZone
3795 * @since Method available since Release 1.5.0
3797 public function getTZID()
3799 return $this->tz->getID();
3804 // {{{ _setTZToDefault()
3807 * sets time zone to the default time zone
3809 * If PHP version >= 5.1.0, uses date_default_timezone_get(),
3810 * else the value returned by
3811 * '{@link http://www.php.net/date date("e")}'
3812 * if valid, else the default specified if the global
3813 * constant '$GLOBALS["_DATE_TIMEZONE_DEFAULT"]', which if itself
3814 * left unset, defaults to "UTC".
3816 * N.B. this is a private method; to set the time zone to the
3817 * default publicly you should call '{@link Date::setTZByID()}',
3818 * that is, with no parameter (or a parameter of null).
3822 * @since Method available since Release 1.5.0
3824 public function _setTZToDefault()
3826 if (function_exists('version_compare') &&
3827 version_compare(phpversion(), "5.1.0", ">=") &&
3829 Date_TimeZone::isValidID($hs_id = date_default_timezone_get()) ||
3830 Date_TimeZone::isValidID($hs_id = date("e"))
3833 $this->tz = new Date_TimeZone($hs_id);
3835 $this->tz = Date_TimeZone::getDefault();
3844 * Sets the time zone of this Date
3846 * Sets the time zone of this date with the given
3847 * Date_TimeZone object. Does not alter the date/time,
3848 * only assigns a new time zone. For conversion, use
3849 * {@link Date::convertTZ()}.
3851 * @param object $tz the Date_TimeZone object to use. If called with a
3852 * parameter that is not a Date_TimeZone object, will
3853 * fall through to setTZByID().
3857 * @see Date::setTZByID(), Date::convertTZ(),
3858 * Date_TimeZone::Date_TimeZone(), Date_TimeZone
3860 public function setTZ($tz)
3862 if (is_a($tz, 'Date_Timezone')) {
3863 $this->setTZByID($tz->getID());
3865 $res = $this->setTZByID($tz);
3866 if (PEAR::isError($res)) {
3877 * Sets the time zone of this date with the given time zone ID
3879 * The time zone IDs are drawn from the 'tz data-base' (see
3880 * {@link http://en.wikipedia.org/wiki/Zoneinfo}), which is the de facto
3881 * internet and IT standard. (There is no official standard, and
3882 * the tz data-base is not intended to be a regulating body
3883 * anyway.) Lists of valid IDs are maintained at:
3885 * - {@link http://en.wikipedia.org/wiki/List_of_zoneinfo_timezones}
3886 * - {@link http://www.php.net/manual/en/timezones.php}
3888 * If no time-zone is specified and PHP version >= 5.1.0, the time
3889 * zone is set automatically to the output of date_default_timezone_get()
3890 * if set and valid, else the value returned by
3891 * '{@link http://www.php.net/date date("e")}'
3892 * if valid, else the default specified if the global
3893 * constant '$GLOBALS["_DATE_TIMEZONE_DEFAULT"]', which if itself
3894 * left unset, defaults to "UTC".
3896 * N.B. this function preserves the local date and time, that is,
3897 * whether in local Summer time or local standard time. For example,
3898 * if the time is set to 11.00 Summer time, and the time zone is then
3899 * set to another time zone, using this function, in which the date
3900 * falls in standard time, then the time will remain set to 11.00 UTC,
3901 * and not 10.00. You can convert a date to another time zone by
3902 * calling '{@link Date::convertTZ()}', which preserves the actual
3903 * time as measured against UTC.
3905 * The ID can also be specified as a UTC offset in one of the following
3906 * forms, i.e. an offset with no geographical or political base:
3908 * - <b>UTC[+/-][h]</b> - e.g. UTC-1 (the preferred form)
3909 * - <b>UTC[+/-][hh]</b> - e.g. UTC+03
3910 * - <b>UTC[+/-][hh][mm]</b> - e.g. UTC-0530
3911 * - <b>UTC[+/-][hh]:[mm]</b> - e.g. UTC+03:00
3913 * N.B. 'UTC' seems to be technically preferred over 'GMT'. GMT-based
3914 * IDs still exist in the tz data-base, but beware of POSIX-style
3915 * offsets which are the opposite way round to what people normally
3918 * @param string $ps_id a valid time zone id, e.g. 'Europe/London'
3922 * @see Date::getTZID(), Date::setTZ(), Date::convertTZByID(),
3923 * Date_TimeZone::isValidID(), Date_TimeZone::Date_TimeZone(),
3926 public function setTZByID($ps_id = null)
3928 // Whether the date is in Summer time forms the default for
3929 // the new time zone (if needed, which is very unlikely anyway).
3930 // This is mainly to prevent unexpected (defaulting) behaviour
3931 // if the user is in the repeated hour, and switches to a time
3932 // zone that is also in the repeated hour (e.g. 'Europe/London'
3933 // and 'Europe/Lisbon').
3935 $hb_insummertime = $this->inDaylightTime();
3936 if (PEAR::isError($hb_insummertime)) {
3937 if ($hb_insummertime->getCode() == DATE_ERROR_INVALIDTIME) {
3938 $hb_insummertime = false;
3940 return $hb_insummertime;
3944 if (is_null($ps_id)) {
3945 $this->_setTZToDefault();
3946 } elseif (Date_TimeZone::isValidID($ps_id)) {
3947 $this->tz = new Date_TimeZone($ps_id);
3949 return PEAR::raiseError(
3950 "Invalid time zone ID '$ps_id'",
3951 DATE_ERROR_INVALIDTIMEZONE
3955 $this->setLocalTime(
3969 // {{{ getTZLongName()
3972 * Returns the long name of the time zone
3974 * Returns long form of time zone name, e.g. 'Greenwich Mean Time'.
3975 * N.B. if the date falls in Summer time, the Summer time name will be
3976 * returned instead, e.g. 'British Summer Time'.
3978 * N.B. this is not a unique identifier for the time zone - for this
3979 * purpose use the time zone ID.
3981 * @return string the long name of the time zone
3983 * @see Date::getTZID(), Date::getTZShortName(),
3984 * Date_TimeZone::getLongName()
3985 * @since Method available since Release 1.5.0
3987 public function getTZLongName()
3989 if ($this->ob_invalidtime) {
3990 return $this->_getErrorInvalidTime();
3993 return $this->tz->getLongName($this->inDaylightTime());
3998 // {{{ getTZShortName()
4001 * Returns the short name of the time zone
4003 * Returns abbreviated form of time zone name, e.g. 'GMT'. N.B. if the
4004 * date falls in Summer time, the Summer time name will be returned
4005 * instead, e.g. 'BST'.
4007 * N.B. this is not a unique identifier - for this purpose use the
4010 * @return string the short name of the time zone
4012 * @see Date::getTZID(), Date::getTZLongName(),
4013 * Date_TimeZone::getShortName()
4014 * @since Method available since Release 1.5.0
4016 public function getTZShortName()
4018 if ($this->ob_invalidtime) {
4019 return $this->_getErrorInvalidTime();
4022 return $this->tz->getShortName($this->inDaylightTime());
4027 // {{{ getTZOffset()
4030 * Returns the DST-corrected offset from UTC for the given date
4032 * Gets the offset to UTC for a given date/time, taking into
4033 * account daylight savings time, if the time zone observes it and if
4036 * N.B. that the offset is calculated historically
4037 * and in the future according to the current Summer time rules,
4038 * and so this function is proleptically correct, but not necessarily
4039 * historically correct. (Although if you want to be correct about
4040 * times in the distant past, this class is probably not for you
4041 * because the whole notion of time zones does not apply, and
4042 * historically there are so many time zone changes, Summer time
4043 * rule changes, name changes, calendar changes, that calculating
4044 * this sort of information is beyond the scope of this package
4047 * @return int the corrected offset to UTC in milliseconds
4049 * @see Date_TimeZone::getOffset()
4050 * @since Method available since Release 1.5.0
4052 public function getTZOffset()
4054 if ($this->ob_invalidtime) {
4055 return $this->_getErrorInvalidTime();
4058 return $this->tz->getOffset($this->inDaylightTime());
4063 // {{{ inDaylightTime()
4066 * Tests if this date/time is in DST
4068 * Returns true if daylight savings time is in effect for
4069 * this date in this date's time zone.
4071 * @param bool $pb_repeatedhourdefault value to return if repeated hour is
4072 * specified (defaults to false)
4074 * @return boolean true if DST is in effect for this date
4076 * @see Date_TimeZone::hasDaylightTime(), Date_TimeZone::inDaylightTime()
4078 public function inDaylightTime($pb_repeatedhourdefault = false)
4080 if (!$this->tz->hasDaylightTime()) {
4083 if ($this->ob_invalidtime) {
4084 return $this->_getErrorInvalidTime();
4087 // The return value is 'cached' whenever the date/time is set:
4089 return $this->hour != $this->on_standardhour ||
4090 $this->minute != $this->on_standardminute ||
4091 $this->second != $this->on_standardsecond ||
4092 $this->partsecond != $this->on_standardpartsecond ||
4093 $this->day != $this->on_standardday ||
4094 $this->month != $this->on_standardmonth ||
4095 $this->year != $this->on_standardyear;
4097 // (these last 3 conditions are theoretical
4098 // possibilities but normally will never occur)
4106 * Converts this date to a new time zone
4108 * Previously this might not have worked correctly if your system did
4109 * not allow {@link http://www.php.net/putenv putenv()} or if
4110 * {@link http://www.php.net/localtime localtime()} did not work in
4111 * your environment, but this implementation is no longer used.
4113 * @param object $tz Date_TimeZone object to convert to
4117 * @see Date::convertTZByID(), Date::toUTC(),
4118 * Date_TimeZone::Date_TimeZone(), Date_TimeZone
4120 public function convertTZ($tz)
4122 if ($this->getTZID() == $tz->getID()) {
4125 if ($this->ob_invalidtime) {
4126 return $this->_getErrorInvalidTime();
4129 $hn_rawoffset = $tz->getRawOffset() - $this->tz->getRawOffset();
4130 $this->tz = new Date_TimeZone($tz->getID());
4132 list($hn_standardyear,
4138 $hn_standardpartsecond) =
4141 $this->on_standardday,
4142 $this->on_standardmonth,
4143 $this->on_standardyear,
4144 $this->on_standardhour,
4145 $this->on_standardminute,
4146 $this->on_standardsecond,
4147 $this->on_standardpartsecond
4150 $this->setStandardTime(
4157 $hn_standardpartsecond
4166 * Converts this date to UTC and sets this date's timezone to UTC
4170 * @see Date::convertTZ(), Date::convertTZByID(), Date::toUTCbyOffset()
4172 public function toUTC()
4174 if ($this->getTZID() == "UTC") {
4177 if ($this->ob_invalidtime) {
4178 return $this->_getErrorInvalidTime();
4181 $res = $this->convertTZ(new Date_TimeZone("UTC"));
4182 if (PEAR::isError($res)) {
4189 // {{{ convertTZByID()
4192 * Converts this date to a new time zone, given a valid time zone ID
4194 * Previously this might not have worked correctly if your system did
4195 * not allow {@link http://www.php.net/putenv putenv()} or if
4196 * {@link http://www.php.net/localtime localtime()} did not work
4197 * in your environment, but this implementation is no longer used.
4199 * @param string $ps_id a valid time zone id, e.g. 'Europe/London'
4203 * @see Date::convertTZ(), Date::toUTC(), Date::setTZByID(),
4204 * Date_TimeZone::isValidID(), Date_TimeZone::Date_TimeZone(),
4207 public function convertTZByID($ps_id)
4209 if (!Date_TimeZone::isValidID($ps_id)) {
4210 return PEAR::raiseError(
4211 "Invalid time zone ID '$ps_id'",
4212 DATE_ERROR_INVALIDTIMEZONE
4216 $res = $this->convertTZ(new Date_TimeZone($ps_id));
4218 if (PEAR::isError($res)) {
4225 // {{{ toUTCbyOffset()
4228 * Converts the date/time to UTC by the offset specified
4230 * This function is no longer called from within the Date class
4231 * itself because a time zone can be set using a pure offset
4232 * (e.g. UTC+1), i.e. not a geographical time zone. However
4233 * it is retained for backwards compaibility.
4235 * @param string $ps_offset offset of the form '<b>[+/-][hh]:[mm]</b>',
4236 * '<b>[+/-][hh][mm]</b>', or '<b>Z</b>'
4240 * @see Date::toUTC(), Date::convertTZ(), Date::convertTZByID()
4242 public function toUTCbyOffset($ps_offset)
4244 if ($ps_offset == "Z" ||
4245 preg_match('/^[+\-](00:?00|0{1,2})$/', $ps_offset)) {
4247 } elseif (preg_match(
4248 '/^[+\-]([0-9]{2,2}:?[0-5][0-9]|[0-9]{1,2})$/',
4251 $hs_tzid = "UTC" . $ps_offset;
4253 return PEAR::raiseError("Invalid offset '$ps_offset'");
4256 // If the time is invalid, it does not matter here:
4258 $this->setTZByID($hs_tzid);
4260 // Now the time will be valid because it is a time zone that
4261 // does not observe Summer time:
4271 * Converts the date to the specified no of years from the given date
4273 * To subtract years use a negative value for the '$pn_years'
4276 * @param int $pn_years years to add
4280 * @since Method available since Release 1.5.0
4282 public function addYears($pn_years)
4284 list($hs_year, $hs_month, $hs_day) =
4285 explode(" ", Date_Calc::addYears(
4292 $this->setLocalTime(
4308 * Converts the date to the specified no of months from the given date
4310 * To subtract months use a negative value for the '$pn_months'
4313 * @param int $pn_months months to add
4317 * @since Method available since Release 1.5.0
4319 public function addMonths($pn_months)
4321 list($hs_year, $hs_month, $hs_day) =
4322 explode(" ", Date_Calc::addMonths(
4329 $this->setLocalTime(
4345 * Converts the date to the specified no of days from the given date
4347 * To subtract days use a negative value for the '$pn_days' parameter
4349 * @param int $pn_days days to add
4353 * @since Method available since Release 1.5.0
4355 public function addDays($pn_days)
4357 list($hs_year, $hs_month, $hs_day) =
4358 explode(" ", Date_Calc::addDays(
4365 $this->setLocalTime(
4381 * Converts the date to the specified no of hours from the given date
4383 * To subtract hours use a negative value for the '$pn_hours' parameter
4385 * @param int $pn_hours hours to add
4389 * @since Method available since Release 1.5.0
4391 public function addHours($pn_hours)
4393 if ($this->ob_invalidtime) {
4394 return $this->_getErrorInvalidTime();
4397 list($hn_standardyear,
4401 Date_Calc::addHours(
4403 $this->on_standardday,
4404 $this->on_standardmonth,
4405 $this->on_standardyear,
4406 $this->on_standardhour
4409 $this->setStandardTime(
4414 $this->on_standardminute,
4415 $this->on_standardsecond,
4416 $this->on_standardpartsecond
4425 * Converts the date to the specified no of minutes from the given date
4427 * To subtract minutes use a negative value for the '$pn_minutes' parameter
4429 * @param int $pn_minutes minutes to add
4433 * @since Method available since Release 1.5.0
4435 public function addMinutes($pn_minutes)
4437 if ($this->ob_invalidtime) {
4438 return $this->_getErrorInvalidTime();
4441 list($hn_standardyear,
4445 $hn_standardminute) =
4446 Date_Calc::addMinutes(
4448 $this->on_standardday,
4449 $this->on_standardmonth,
4450 $this->on_standardyear,
4451 $this->on_standardhour,
4452 $this->on_standardminute
4455 $this->setStandardTime(
4461 $this->on_standardsecond,
4462 $this->on_standardpartsecond
4471 * Adds a given number of seconds to the date
4473 * @param mixed $sec the no of seconds to add as integer or float
4474 * @param bool $pb_countleap whether to count leap seconds (defaults to
4475 * value of count-leap-second object property)
4480 public function addSeconds($sec, $pb_countleap = null)
4482 if ($this->ob_invalidtime) {
4483 return $this->_getErrorInvalidTime();
4485 if (!is_int($sec) && !is_float($sec)) {
4486 settype($sec, 'int');
4488 if (!is_null($pb_countleap)) {
4489 $pb_countleap = $this->ob_countleapseconds;
4492 if ($pb_countleap) {
4495 list($hn_standardyear,
4501 $hn_standardpartsecond) =
4503 $this->tz->getRawOffset() * -1,
4504 $this->on_standardday,
4505 $this->on_standardmonth,
4506 $this->on_standardyear,
4507 $this->on_standardhour,
4508 $this->on_standardminute,
4509 $this->on_standardsecond,
4510 $this->on_standardpartsecond
4512 list($hn_standardyear,
4518 Date_Calc::addSeconds(
4525 $hn_standardpartsecond == 0.0 ?
4526 $hn_standardsecond :
4527 $hn_standardsecond +
4528 $hn_standardpartsecond,
4532 if (is_float($hn_secondraw)) {
4533 $hn_standardsecond = intval($hn_secondraw);
4534 $hn_standardpartsecond = $hn_secondraw - $hn_standardsecond;
4536 $hn_standardsecond = $hn_secondraw;
4537 $hn_standardpartsecond = 0.0;
4540 list($hn_standardyear,
4546 $hn_standardpartsecond) =
4548 $this->tz->getRawOffset(),
4555 $hn_standardpartsecond
4558 // Use local standard time:
4560 list($hn_standardyear,
4566 Date_Calc::addSeconds(
4568 $this->on_standardday,
4569 $this->on_standardmonth,
4570 $this->on_standardyear,
4571 $this->on_standardhour,
4572 $this->on_standardminute,
4573 $this->on_standardpartsecond == 0.0 ?
4574 $this->on_standardsecond :
4575 $this->on_standardsecond +
4576 $this->on_standardpartsecond,
4580 if (is_float($hn_secondraw)) {
4581 $hn_standardsecond = intval($hn_secondraw);
4582 $hn_standardpartsecond = $hn_secondraw - $hn_standardsecond;
4584 $hn_standardsecond = $hn_secondraw;
4585 $hn_standardpartsecond = 0.0;
4589 $this->setStandardTime(
4596 $hn_standardpartsecond
4602 // {{{ subtractSeconds()
4605 * Subtracts a given number of seconds from the date
4607 * @param mixed $sec the no of seconds to subtract as integer or
4609 * @param bool $pb_countleap whether to count leap seconds (defaults to
4610 * value of count-leap-second object property)
4615 public function subtractSeconds($sec, $pb_countleap = null)
4617 if (is_null($pb_countleap)) {
4618 $pb_countleap = $this->ob_countleapseconds;
4621 $res = $this->addSeconds(-$sec, $pb_countleap);
4623 if (PEAR::isError($res)) {
4633 * Adds a time span to the date
4635 * A time span is defined as a unsigned no of days, hours, minutes
4636 * and seconds, where the no of minutes and seconds must be less than
4637 * 60, and the no of hours must be less than 24.
4639 * A span is added (and subtracted) according to the following logic:
4641 * Hours, minutes and seconds are added such that if they fall over
4642 * a leap second, the leap second is ignored, and not counted.
4643 * For example, if a leap second occurred at 23.59.60, the
4644 * following calculations:
4646 * - 23.59.59 + one second
4647 * - 23.59.00 + one minute
4648 * - 23.00.00 + one hour
4650 * would all produce 00.00.00 the next day.
4652 * A day is treated as equivalent to 24 hours, so if the clocks
4653 * went backwards at 01.00, and one day was added to the time
4654 * 00.30, the result would be 23.30 the same day.
4656 * This is the implementation which is thought to yield the behaviour
4657 * that the user is most likely to expect, or in another way of
4658 * looking at it, it is the implementation that produces the least
4659 * unexpected behaviour. It basically works in hours, that is, a day
4660 * is treated as exactly equivalent to 24 hours, and minutes and
4661 * seconds are treated as equivalent to 1/60th and 1/3600th of an
4662 * hour. It should be obvious that working in days is impractical;
4663 * working in seconds is problematic when it comes to adding days
4664 * that fall over leap seconds, where it would appear to most users
4665 * that the function adds only 23 hours, 59 minutes and 59 seconds.
4666 * It is also problematic to work in any kind of mixture of days,
4667 * hours, minutes, and seconds, because then the addition of a span
4668 * would sometimes depend on which order you add the constituent
4669 * parts, which undermines the concept of a span altogether.
4671 * If you want alternative functionality, you must use a mixture of
4672 * the following functions instead:
4674 * - {@link Date::addYears()}
4675 * - {@link Date::addMonths()}
4676 * - {@link Date::addDays()}
4677 * - {@link Date::addHours()}
4678 * - {@link Date::addMinutes()}
4679 * - {@link Date::addSeconds()}
4681 * @param object $span the time span to add
4687 public function addSpan($span)
4689 if (!is_a($span, 'Date_Span')) {
4690 return PEAR::raiseError("Invalid argument - not 'Date_Span' object");
4691 } elseif ($this->ob_invalidtime) {
4692 return $this->_getErrorInvalidTime();
4695 $hn_days = $span->day;
4696 $hn_standardhour = $this->on_standardhour + $span->hour;
4697 $hn_standardminute = $this->on_standardminute + $span->minute;
4698 $hn_standardsecond = $this->on_standardsecond + $span->second;
4700 if ($hn_standardsecond >= 60) {
4701 ++$hn_standardminute;
4702 $hn_standardsecond -= 60;
4705 if ($hn_standardminute >= 60) {
4707 $hn_standardminute -= 60;
4710 if ($hn_standardhour >= 24) {
4712 $hn_standardhour -= 24;
4715 list($hn_standardyear, $hn_standardmonth, $hn_standardday) =
4720 $this->on_standardday,
4721 $this->on_standardmonth,
4722 $this->on_standardyear,
4727 $this->setStandardTime(
4734 $this->on_standardpartsecond
4740 // {{{ subtractSpan()
4743 * Subtracts a time span from the date
4745 * N.B. it is impossible for this function to count leap seconds,
4746 * because the result would be dependent on which order the consituent
4747 * parts of the span are subtracted from the date. Therefore, leap
4748 * seconds are ignored by this function. If you want to count leap
4749 * seconds, use {@link Date::subtractSeconds()}.
4751 * @param object $span the time span to subtract
4757 public function subtractSpan($span)
4759 if (!is_a($span, 'Date_Span')) {
4760 return PEAR::raiseError("Invalid argument - not 'Date_Span' object");
4761 } elseif ($this->ob_invalidtime) {
4762 return $this->_getErrorInvalidTime();
4765 $hn_days = -$span->day;
4766 $hn_standardhour = $this->on_standardhour - $span->hour;
4767 $hn_standardminute = $this->on_standardminute - $span->minute;
4768 $hn_standardsecond = $this->on_standardsecond - $span->second;
4770 if ($hn_standardsecond < 0) {
4771 --$hn_standardminute;
4772 $hn_standardsecond += 60;
4775 if ($hn_standardminute < 0) {
4777 $hn_standardminute += 60;
4780 if ($hn_standardhour < 0) {
4782 $hn_standardhour += 24;
4785 list($hn_standardyear, $hn_standardmonth, $hn_standardday) =
4790 $this->on_standardday,
4791 $this->on_standardmonth,
4792 $this->on_standardyear,
4797 $this->setStandardTime(
4804 $this->on_standardpartsecond
4813 * Subtract supplied date and return answer in days
4815 * If the second parameter '$pb_ignoretime' is specified as false, the time
4816 * parts of the two dates will be ignored, and the integral no of days
4817 * between the day/month/year parts of the two dates will be returned. If
4818 * either of the two dates have an invalid time, the integral no of days
4819 * will also be returned, else the returned value will be the no of days as
4820 * a float, with each hour being treated as 1/24th of a day and so on.
4824 * - 21/11/2007 13.00 minus 21/11/2007 01.00
4828 * Note that if the passed date is in the past, a positive value will be
4829 * returned, and if it is in the future, a negative value will be returned.
4831 * @param object $po_date date to subtract
4832 * @param bool $pb_ignoretime whether to ignore the time values of the two
4833 * dates in subtraction (defaults to false)
4835 * @return mixed days between two dates as int or float
4837 * @since Method available since Release 1.5.0
4839 public function dateDiff($po_date, $pb_ignoretime = false)
4841 if ($pb_ignoretime || $this->ob_invalidtime) {
4842 return Date_Calc::dateToDays(
4847 Date_Calc::dateToDays(
4849 $po_date->getMonth(),
4854 $hn_secondscompare = $po_date->getStandardSecondsPastMidnight();
4855 if (PEAR::isError($hn_secondscompare)) {
4856 if ($hn_secondscompare->getCode() != DATE_ERROR_INVALIDTIME) {
4857 return $hn_secondscompare;
4860 return Date_Calc::dateToDays(
4865 Date_Calc::dateToDays(
4867 $po_date->getMonth(),
4872 $hn_seconds = $this->getStandardSecondsPastMidnight();
4874 // If time parts are equal, return int, else return float:
4876 return Date_Calc::dateToDays(
4877 $this->on_standardday,
4878 $this->on_standardmonth,
4879 $this->on_standardyear
4881 Date_Calc::dateToDays(
4882 $po_date->getStandardDay(),
4883 $po_date->getStandardMonth(),
4884 $po_date->getStandardYear()
4886 ($hn_seconds == $hn_secondscompare ? 0 :
4887 ($hn_seconds - $hn_secondscompare) / 86400);
4892 // {{{ inEquivalentTimeZones()
4895 * Tests whether two dates are in equivalent time zones
4897 * Equivalence in this context consists in the time zones of the two dates
4900 * - an equal offset from UTC in both standard and Summer time (if
4901 * the time zones observe Summer time)
4902 * - the same Summer time start and end rules, that is, the two time zones
4903 * must switch from standard time to Summer time, and
4904 * vice versa, on the same day and at the same time
4906 * An example of two equivalent time zones is 'Europe/London' and
4907 * 'Europe/Lisbon', which in London is known as GMT/BST, and in Lisbon as
4910 * @param object $po_date1 the first Date object to compare
4911 * @param object $po_date2 the second Date object to compare
4913 * @return bool true if the time zones are equivalent
4916 * @see Date_TimeZone::isEquivalent()
4917 * @since Method available since Release 1.5.0
4919 public function inEquivalentTimeZones($po_date1, $po_date2)
4921 return $po_date1->tz->isEquivalent($po_date2->getTZID());
4929 * Compares two dates
4931 * Suitable for use in sorting functions
4933 * @param object $od1 the first Date object to compare
4934 * @param object $od2 the second Date object to compare
4936 * @return int 0 if the dates are equal, -1 if '$od1' is
4937 * before '$od2', 1 if '$od1' is after '$od2'
4941 public function compare($od1, $od2)
4943 $d1 = new Date($od1);
4944 $d2 = new Date($od2);
4946 // If the time zones are equivalent, do nothing:
4948 if (!Date::inEquivalentTimeZones($d1, $d2)) {
4949 // Only a time zone with a valid time can be converted:
4951 if ($d2->isValidTime()) {
4952 $d2->convertTZByID($d1->getTZID());
4953 } elseif ($d1->isValidTime()) {
4954 $d1->convertTZByID($d2->getTZID());
4956 // No comparison can be made without guessing the time:
4958 return PEAR::raiseError(
4959 "Both dates have invalid time",
4960 DATE_ERROR_INVALIDTIME
4965 $days1 = Date_Calc::dateToDays(
4970 $days2 = Date_Calc::dateToDays(
4975 if ($days1 < $days2) {
4978 if ($days1 > $days2) {
4982 $hn_hour1 = $d1->getStandardHour();
4983 if (PEAR::isError($hn_hour1)) {
4986 $hn_hour2 = $d2->getStandardHour();
4987 if (PEAR::isError($hn_hour2)) {
4991 if ($hn_hour1 < $hn_hour2) {
4994 if ($hn_hour1 > $hn_hour2) {
4997 if ($d1->getStandardMinute() < $d2->getStandardMinute()) {
5000 if ($d1->getStandardMinute() > $d2->getStandardMinute()) {
5003 if ($d1->getStandardSecond() < $d2->getStandardSecond()) {
5006 if ($d1->getStandardSecond() > $d2->getStandardSecond()) {
5009 if ($d1->getStandardPartSecond() < $d2->getStandardPartSecond()) {
5012 if ($d1->getStandardPartSecond() > $d2->getStandardPartSecond()) {
5023 * Test if this date/time is before a certain date/time
5025 * @param object $when the Date object to test against
5027 * @return boolean true if this date is before $when
5030 public function before($when)
5032 $hn_compare = Date::compare($this, $when);
5033 if (PEAR::isError($hn_compare)) {
5037 if ($hn_compare == -1) {
5049 * Test if this date/time is after a certain date/time
5051 * @param object $when the Date object to test against
5053 * @return boolean true if this date is after $when
5056 public function after($when)
5058 $hn_compare = Date::compare($this, $when);
5059 if (PEAR::isError($hn_compare)) {
5063 if ($hn_compare == 1) {
5075 * Test if this date/time is exactly equal to a certain date/time
5077 * @param object $when the Date object to test against
5079 * @return boolean true if this date is exactly equal to $when
5082 public function equals($when)
5084 $hn_compare = Date::compare($this, $when);
5085 if (PEAR::isError($hn_compare)) {
5089 if ($hn_compare == 0) {
5101 * Determine if this date is in the future
5103 * @return boolean true if this date is in the future
5106 public function isFuture()
5109 return $this->after($now);
5117 * Determine if this date is in the past
5119 * @return boolean true if this date is in the past
5122 public function isPast()
5125 return $this->before($now);
5133 * Determine if the year in this date is a leap year
5135 * @return boolean true if this year is a leap year
5138 public function isLeapYear()
5140 return Date_Calc::isLeapYear($this->year);
5145 // {{{ getJulianDate()
5148 * Returns the no of days (1-366) since 31st December of the previous year
5150 * N.B. this function does not return (and never has returned) the 'Julian
5151 * Date', as described, for example, at:
5153 * - {@link http://en.wikipedia.org/wiki/Julian_day}
5155 * If you want the day of the year (0-366), use {@link Date::getDayOfYear()}
5156 * instead. If you want the true Julian Day, call one of the following:
5158 * - {@link Date::formatLikeStrftime()} using code '<b>%E</b>'
5159 * - {@link Date::formatLikeSQL()} using code '<b>J</b>'
5161 * There currently is no function that calls the Julian Date (as opposed
5162 * to the 'Julian Day'), although the Julian Day is an approximation.
5164 * @return int the Julian date
5166 * @see Date::getDayOfYear()
5167 * @deprecated Method deprecated in Release 1.5.0
5169 public function getJulianDate()
5171 return Date_Calc::julianDate($this->day, $this->month, $this->year);
5176 // {{{ getDayOfYear()
5179 * Returns the no of days (1-366) since 31st December of the previous year
5181 * @return int an integer between 1 and 366
5183 * @since Method available since Release 1.5.0
5185 public function getDayOfYear()
5187 return Date_Calc::dayOfYear($this->day, $this->month, $this->year);
5192 // {{{ getDayOfWeek()
5195 * Gets the day of the week for this date (0 = Sunday)
5197 * @return int the day of the week (0 = Sunday)
5200 public function getDayOfWeek()
5202 return Date_Calc::dayOfWeek($this->day, $this->month, $this->year);
5207 // {{{ getWeekOfYear()
5210 * Gets the week of the year for this date
5212 * @return int the week of the year
5215 public function getWeekOfYear()
5217 return Date_Calc::weekOfYear($this->day, $this->month, $this->year);
5222 // {{{ getQuarterOfYear()
5225 * Gets the quarter of the year for this date
5227 * @return int the quarter of the year (1-4)
5230 public function getQuarterOfYear()
5232 return Date_Calc::quarterOfYear($this->day, $this->month, $this->year);
5237 // {{{ getDaysInMonth()
5240 * Gets number of days in the month for this date
5242 * @return int number of days in this month
5245 public function getDaysInMonth()
5247 return Date_Calc::daysInMonth($this->month, $this->year);
5252 // {{{ getWeeksInMonth()
5255 * Gets the number of weeks in the month for this date
5257 * @return int number of weeks in this month
5260 public function getWeeksInMonth()
5262 return Date_Calc::weeksInMonth($this->month, $this->year);
5270 * Gets the full name or abbreviated name of this weekday
5272 * @param bool $abbr abbreviate the name
5273 * @param int $length length of abbreviation
5275 * @return string name of this day
5278 public function getDayName($abbr = false, $length = 3)
5281 return Date_Calc::getWeekdayAbbrname(
5288 return Date_Calc::getWeekdayFullname(
5298 // {{{ getMonthName()
5301 * Gets the full name or abbreviated name of this month
5303 * @param boolean $abbr abbreviate the name
5305 * @return string name of this month
5308 public function getMonthName($abbr = false)
5311 return Date_Calc::getMonthAbbrname($this->month);
5313 return Date_Calc::getMonthFullname($this->month);
5322 * Get a Date object for the day after this one
5324 * The time of the returned Date object is the same as this time.
5326 * @return object Date object representing the next day
5329 public function getNextDay()
5331 $ret = new Date($this);
5341 * Get a Date object for the day before this one
5343 * The time of the returned Date object is the same as this time.
5345 * @return object Date object representing the previous day
5348 public function getPrevDay()
5350 $ret = new Date($this);
5357 // {{{ getNextWeekday()
5360 * Get a Date object for the weekday after this one
5362 * The time of the returned Date object is the same as this time.
5364 * @return object Date object representing the next week-day
5367 public function getNextWeekday()
5369 $ret = new Date($this);
5370 list($hs_year, $hs_month, $hs_day) =
5371 explode(" ", Date_Calc::nextWeekday(
5377 $ret->setDayMonthYear($hs_day, $hs_month, $hs_year);
5383 // {{{ getPrevWeekday()
5386 * Get a Date object for the weekday before this one
5388 * The time of the returned Date object is the same as this time.
5390 * @return object Date object representing the previous week-day
5393 public function getPrevWeekday()
5395 $ret = new Date($this);
5396 list($hs_year, $hs_month, $hs_day) =
5397 explode(" ", Date_Calc::prevWeekday(
5403 $ret->setDayMonthYear($hs_day, $hs_month, $hs_year);
5412 * Returns the year field of the date object
5414 * @return int the year
5417 public function getYear()
5427 * Returns the month field of the date object
5429 * @return int the minute
5432 public function getMonth()
5434 return $this->month;
5442 * Returns the day field of the date object
5444 * @return int the day
5447 public function getDay()
5454 // {{{ _getErrorInvalidTime()
5457 * Returns invalid time PEAR Error
5461 * @since Method available since Release 1.5.0
5463 public function _getErrorInvalidTime()
5465 return PEAR::raiseError(
5473 "' specified for date '" .
5474 Date_Calc::dateFormat(
5480 "' and in this timezone",
5481 DATE_ERROR_INVALIDTIME
5487 // {{{ _secondsInDayIsValid()
5490 * If leap seconds are observed, checks if the seconds in the day is valid
5492 * Note that only the local standard time is accessed.
5496 * @since Method available since Release 1.5.0
5498 public function _secondsInDayIsValid()
5500 if ($this->ob_countleapseconds) {
5511 $this->tz->getRawOffset() * -1,
5512 $this->on_standardday,
5513 $this->on_standardmonth,
5514 $this->on_standardyear,
5515 $this->on_standardhour,
5516 $this->on_standardminute,
5517 $this->on_standardsecond,
5518 $this->on_standardpartsecond
5520 return Date_Calc::secondsPastMidnight(
5526 Date_Calc::getSecondsInDay($hn_day, $hn_month, $hn_year);
5528 return $this->getStandardSecondsPastMidnight() < 86400;
5534 // {{{ isValidTime()
5537 * Returns whether the stored date/time is valid, i.e as a local time
5538 * for the current time-zone.
5540 * An invalid time is one that lies in the 'skipped hour' at the point
5541 * that the clocks go forward (if the time-zone uses Summer time).
5543 * Note that the stored date (i.e. the day/month/year), is set more
5544 * strictly: it is not possible to set an invalid day/month/year
5545 * using {@link Date::setDate()} and it is only possible to do so with
5546 * {@link setYear()} etc. for backwards-compatibility (and anyway, this
5547 * can be switched off by default by setting
5548 * {@link DATE_VALIDATE_DATE_BY_DEFAULT} to 'true').
5550 * The object is able to store an invalid time because a user might
5551 * unwittingly and correctly store a valid time, and then add one day so
5552 * as to put the object in the 'skipped' hour (when the clocks go forward).
5553 * This could be corrected by a conversion to Summer time (by adding one
5554 * hour); however, if the user then added another day, and had no need for
5555 * or interest in the time anyway, the behaviour may be rather unexpected.
5556 * And anyway in this situation, the time originally specified would now,
5557 * two days on, be valid again.
5559 * So this class allows an invalid time like this so long as the user does
5560 * not in any way make use of or request the time while it is in this
5561 * semi-invalid state, in order to allow for for the fact that he might be
5562 * only interested in the date, and not the time, and in order not to behave
5563 * in an unexpected way, especially without throwing an exception to tell
5564 * the user about it.
5568 * @see Date::isValidDate(), Date::isNull(),
5569 * DATE_VALIDATE_DATE_BY_DEFAULT, DATE_CORRECTINVALIDTIME_DEFAULT
5570 * @since Method available since Release 1.5.0
5572 public function isValidTime()
5574 return !$this->ob_invalidtime;
5582 * Returns the hour field of the date object
5584 * @return int the hour
5587 public function getHour()
5589 if ($this->ob_invalidtime) {
5590 return $this->_getErrorInvalidTime();
5601 * Returns the minute field of the date object
5603 * @return int the minute
5606 public function getMinute()
5608 if ($this->ob_invalidtime) {
5609 return $this->_getErrorInvalidTime();
5612 return $this->minute;
5620 * Returns the second field of the date object
5622 * @return int the second
5625 public function getSecond()
5627 if ($this->ob_invalidtime) {
5628 return $this->_getErrorInvalidTime();
5631 return $this->second;
5636 // {{{ getSecondsPastMidnight()
5639 * Returns the no of seconds since midnight (0-86400) as float
5641 * @return float float which is at least 0 and less than 86400
5643 * @since Method available since Release 1.5.0
5645 public function getSecondsPastMidnight()
5647 if ($this->ob_invalidtime) {
5648 return $this->_getErrorInvalidTime();
5651 return Date_Calc::secondsPastMidnight(
5661 // {{{ getPartSecond()
5664 * Returns the part-second field of the date object
5666 * @return float the part-second
5668 * @since Method available since Release 1.5.0
5670 public function getPartSecond()
5672 if ($this->ob_invalidtime) {
5673 return $this->_getErrorInvalidTime();
5676 return $this->partsecond;
5681 // {{{ getStandardYear()
5684 * Returns the year field of the local standard time
5686 * @return int the year
5688 * @since Method available since Release 1.5.0
5690 public function getStandardYear()
5692 if ($this->ob_invalidtime) {
5693 return $this->_getErrorInvalidTime();
5696 return $this->on_standardyear;
5701 // {{{ getStandardMonth()
5704 * Returns the month field of the local standard time
5706 * @return int the minute
5708 * @since Method available since Release 1.5.0
5710 public function getStandardMonth()
5712 if ($this->ob_invalidtime) {
5713 return $this->_getErrorInvalidTime();
5716 return $this->on_standardmonth;
5721 // {{{ getStandardDay()
5724 * Returns the day field of the local standard time
5726 * @return int the day
5728 * @since Method available since Release 1.5.0
5730 public function getStandardDay()
5732 if ($this->ob_invalidtime) {
5733 return $this->_getErrorInvalidTime();
5736 return $this->on_standardday;
5741 // {{{ getStandardHour()
5744 * Returns the hour field of the local standard time
5746 * @return int the hour
5748 * @since Method available since Release 1.5.0
5750 public function getStandardHour()
5752 if ($this->ob_invalidtime) {
5753 return $this->_getErrorInvalidTime();
5756 return $this->on_standardhour;
5761 // {{{ getStandardMinute()
5764 * Returns the minute field of the local standard time
5766 * @return int the minute
5768 * @since Method available since Release 1.5.0
5770 public function getStandardMinute()
5772 if ($this->ob_invalidtime) {
5773 return $this->_getErrorInvalidTime();
5776 return $this->on_standardminute;
5781 // {{{ getStandardSecond()
5784 * Returns the second field of the local standard time
5786 * @return int the second
5788 * @since Method available since Release 1.5.0
5790 public function getStandardSecond()
5792 if ($this->ob_invalidtime) {
5793 return $this->_getErrorInvalidTime();
5796 return $this->on_standardsecond;
5801 // {{{ getStandardSecondsPastMidnight()
5804 * Returns the no of seconds since midnight (0-86400) of the
5805 * local standard time as float
5807 * @return float float which is at least 0 and less than 86400
5809 * @since Method available since Release 1.5.0
5811 public function getStandardSecondsPastMidnight()
5813 if ($this->ob_invalidtime) {
5814 return $this->_getErrorInvalidTime();
5817 return Date_Calc::secondsPastMidnight(
5818 $this->on_standardhour,
5819 $this->on_standardminute,
5820 $this->on_standardsecond
5822 $this->on_standardpartsecond;
5827 // {{{ getStandardPartSecond()
5830 * Returns the part-second field of the local standard time
5832 * @return float the part-second
5834 * @since Method available since Release 1.5.0
5836 public function getStandardPartSecond()
5838 if ($this->ob_invalidtime) {
5839 return $this->_getErrorInvalidTime();
5842 return $this->on_standardpartsecond;
5850 * Add a time zone offset to the passed date/time
5852 * @param int $pn_offset the offset to add in milliseconds
5853 * @param int $pn_day the day
5854 * @param int $pn_month the month
5855 * @param int $pn_year the year
5856 * @param int $pn_hour the hour
5857 * @param int $pn_minute the minute
5858 * @param int $pn_second the second
5859 * @param float $pn_partsecond the part-second
5861 * @return array array of year, month, day, hour, minute, second,
5865 * @since Method available since Release 1.5.0
5867 public function _addOffset(
5878 if ($pn_offset == 0) {
5879 return array((int)$pn_year,
5885 (float)$pn_partsecond);
5888 if ($pn_offset % 3600000 == 0) {
5893 Date_Calc::addHours(
5894 $pn_offset / 3600000,
5901 $hn_minute = (int)$pn_minute;
5902 $hn_second = (int)$pn_second;
5903 $hn_partsecond = (float)$pn_partsecond;
5904 } elseif ($pn_offset % 60000 == 0) {
5910 Date_Calc::addMinutes(
5919 $hn_second = (int)$pn_second;
5920 $hn_partsecond = (float)$pn_partsecond;
5928 Date_Calc::addSeconds(
5934 $pn_partsecond == 0.0 ?
5936 $pn_second + $pn_partsecond,
5938 ); // N.B. do not count
5941 if (is_float($hn_secondraw)) {
5942 $hn_second = intval($hn_secondraw);
5943 $hn_partsecond = $hn_secondraw - $hn_second;
5945 $hn_second = $hn_secondraw;
5946 $hn_partsecond = 0.0;
5950 return array($hn_year,
5961 // {{{ setLocalTime()
5964 * Sets local time (Summer-time-adjusted) and then calculates local
5967 * @param int $pn_day the day
5968 * @param int $pn_month the month
5969 * @param int $pn_year the year
5970 * @param int $pn_hour the hour
5971 * @param int $pn_minute the minute
5972 * @param int $pn_second the second
5973 * @param float $pn_partsecond the part-second
5974 * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
5975 * repeated hour is specified (defaults
5977 * @param bool $pb_correctinvalidtime whether to correct, by adding the
5978 * local Summer time offset, the
5979 * specified time if it falls in the
5980 * skipped hour (defaults to
5981 * {@link DATE_CORRECTINVALIDTIME_DEFAULT})
5985 * @see Date::setStandardTime()
5986 * @since Method available since Release 1.5.0
5988 public function setLocalTime(
5996 $pb_repeatedhourdefault = false,
5997 $pb_correctinvalidtime = DATE_CORRECTINVALIDTIME_DEFAULT
6000 settype($pn_day, "int");
6001 settype($pn_month, "int");
6002 settype($pn_year, "int");
6003 settype($pn_hour, "int");
6004 settype($pn_minute, "int");
6005 settype($pn_second, "int");
6006 settype($pn_partsecond, "float");
6009 $this->tz->inDaylightTime(
6011 $pn_month, $pn_year, Date_Calc::secondsPastMidnight(
6015 ) + $pn_partsecond),
6016 $pb_repeatedhourdefault
6018 if (PEAR::isError($hb_insummertime)) {
6019 if ($hb_insummertime->getCode() != DATE_ERROR_INVALIDTIME) {
6020 return $hb_insummertime;
6021 } elseif ($pb_correctinvalidtime) {
6022 // Store passed time as local standard time:
6024 $this->on_standardday = $pn_day;
6025 $this->on_standardmonth = $pn_month;
6026 $this->on_standardyear = $pn_year;
6027 $this->on_standardhour = $pn_hour;
6028 $this->on_standardminute = $pn_minute;
6029 $this->on_standardsecond = $pn_second;
6030 $this->on_standardpartsecond = $pn_partsecond;
6032 // Add Summer time offset to passed time:
6040 $this->partsecond) =
6042 $this->tz->getDSTSavings(),
6052 $this->ob_invalidtime = !$this->_secondsInDayIsValid();
6054 // Hedge bets - if the user adds/subtracts a day, then the time
6055 // will be uncorrupted, and if the user does
6056 // addition/subtraction with the time, or requests the time,
6057 // then return an error at that point:
6059 $this->day = $pn_day;
6060 $this->month = $pn_month;
6061 $this->year = $pn_year;
6062 $this->hour = $pn_hour;
6063 $this->minute = $pn_minute;
6064 $this->second = $pn_second;
6065 $this->partsecond = $pn_partsecond;
6067 $this->ob_invalidtime = true;
6072 // Passed time is valid as local time:
6074 $this->day = $pn_day;
6075 $this->month = $pn_month;
6076 $this->year = $pn_year;
6077 $this->hour = $pn_hour;
6078 $this->minute = $pn_minute;
6079 $this->second = $pn_second;
6080 $this->partsecond = $pn_partsecond;
6083 $this->ob_invalidtime = !$this->_secondsInDayIsValid();
6085 if ($hb_insummertime) {
6086 // Calculate local standard time:
6088 list($this->on_standardyear,
6089 $this->on_standardmonth,
6090 $this->on_standardday,
6091 $this->on_standardhour,
6092 $this->on_standardminute,
6093 $this->on_standardsecond,
6094 $this->on_standardpartsecond) =
6096 $this->tz->getDSTSavings() * -1,
6106 // Time is already local standard time:
6108 $this->on_standardday = $pn_day;
6109 $this->on_standardmonth = $pn_month;
6110 $this->on_standardyear = $pn_year;
6111 $this->on_standardhour = $pn_hour;
6112 $this->on_standardminute = $pn_minute;
6113 $this->on_standardsecond = $pn_second;
6114 $this->on_standardpartsecond = $pn_partsecond;
6120 // {{{ setStandardTime()
6123 * Sets local standard time and then calculates local time (i.e.
6124 * Summer-time-adjusted)
6126 * @param int $pn_day the day
6127 * @param int $pn_month the month
6128 * @param int $pn_year the year
6129 * @param int $pn_hour the hour
6130 * @param int $pn_minute the minute
6131 * @param int $pn_second the second
6132 * @param float $pn_partsecond the part-second
6136 * @see Date::setLocalTime()
6137 * @since Method available since Release 1.5.0
6139 public function setStandardTime(
6149 settype($pn_day, "int");
6150 settype($pn_month, "int");
6151 settype($pn_year, "int");
6152 settype($pn_hour, "int");
6153 settype($pn_minute, "int");
6154 settype($pn_second, "int");
6155 settype($pn_partsecond, "float");
6157 $this->on_standardday = $pn_day;
6158 $this->on_standardmonth = $pn_month;
6159 $this->on_standardyear = $pn_year;
6160 $this->on_standardhour = $pn_hour;
6161 $this->on_standardminute = $pn_minute;
6162 $this->on_standardsecond = $pn_second;
6163 $this->on_standardpartsecond = $pn_partsecond;
6165 $this->ob_invalidtime = !$this->_secondsInDayIsValid();
6167 if ($this->tz->inDaylightTimeStandard(array($pn_day, $pn_month,
6168 $pn_year, Date_Calc::secondsPastMidnight(
6172 ) + $pn_partsecond))) {
6174 // Calculate local time:
6182 $this->partsecond) =
6184 $this->tz->getDSTSavings(),
6194 // Time is already local time:
6196 $this->day = $pn_day;
6197 $this->month = $pn_month;
6198 $this->year = $pn_year;
6199 $this->hour = $pn_hour;
6200 $this->minute = $pn_minute;
6201 $this->second = $pn_second;
6202 $this->partsecond = $pn_partsecond;
6211 * Sets the year field of the date object
6213 * If specified year forms an invalid date, then PEAR error will be
6214 * returned, unless the validation is over-ridden using the second
6217 * @param int $y the year
6218 * @param bool $pb_validate whether to check that the new date is valid
6219 * (defaults to {@link DATE_VALIDATE_DATE_BY_DEFAULT})
6223 * @see Date::setDayMonthYear(), Date::setDateTime()
6225 public function setYear($y, $pb_validate = DATE_VALIDATE_DATE_BY_DEFAULT)
6227 if ($pb_validate && !Date_Calc::isValidDate($this->day, $this->month, $y)) {
6228 return PEAR::raiseError(
6230 Date_Calc::dateFormat(
6236 "' is invalid calendar date",
6237 DATE_ERROR_INVALIDDATE
6240 $this->setLocalTime(
6257 * Sets the month field of the date object
6259 * If specified year forms an invalid date, then PEAR error will be
6260 * returned, unless the validation is over-ridden using the second
6263 * @param int $m the month
6264 * @param bool $pb_validate whether to check that the new date is valid
6265 * (defaults to {@link DATE_VALIDATE_DATE_BY_DEFAULT})
6269 * @see Date::setDayMonthYear(), Date::setDateTime()
6271 public function setMonth($m, $pb_validate = DATE_VALIDATE_DATE_BY_DEFAULT)
6273 if ($pb_validate && !Date_Calc::isValidDate($this->day, $m, $this->year)) {
6274 return PEAR::raiseError(
6276 Date_Calc::dateFormat(
6282 "' is invalid calendar date",
6283 DATE_ERROR_INVALIDDATE
6286 $this->setLocalTime(
6303 * Sets the day field of the date object
6305 * If specified year forms an invalid date, then PEAR error will be
6306 * returned, unless the validation is over-ridden using the second
6309 * @param int $d the day
6310 * @param bool $pb_validate whether to check that the new date is valid
6311 * (defaults to {@link DATE_VALIDATE_DATE_BY_DEFAULT})
6315 * @see Date::setDayMonthYear(), Date::setDateTime()
6317 public function setDay($d, $pb_validate = DATE_VALIDATE_DATE_BY_DEFAULT)
6319 if ($pb_validate && !Date_Calc::isValidDate($d, $this->month, $this->year)) {
6320 return PEAR::raiseError(
6322 Date_Calc::dateFormat(
6328 "' is invalid calendar date",
6329 DATE_ERROR_INVALIDDATE
6332 $this->setLocalTime(
6346 // {{{ setDayMonthYear()
6349 * Sets the day, month and year fields of the date object
6351 * If specified year forms an invalid date, then PEAR error will be
6352 * returned. Note that setting each of these fields separately
6353 * may unintentionally return a PEAR error if a transitory date is
6354 * invalid between setting these fields.
6356 * @param int $d the day
6357 * @param int $m the month
6358 * @param int $y the year
6362 * @see Date::setDateTime()
6363 * @since Method available since Release 1.5.0
6365 public function setDayMonthYear($d, $m, $y)
6367 if (!Date_Calc::isValidDate($d, $m, $y)) {
6368 return PEAR::raiseError(
6370 Date_Calc::dateFormat(
6376 "' is invalid calendar date",
6377 DATE_ERROR_INVALIDDATE
6380 $this->setLocalTime(
6397 * Sets the hour field of the date object
6399 * Expects an hour in 24-hour format.
6401 * @param int $h the hour
6402 * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
6403 * repeated hour is specified (defaults
6408 * @see Date::setHourMinuteSecond(), Date::setDateTime()
6410 public function setHour($h, $pb_repeatedhourdefault = false)
6412 if ($h > 23 || $h < 0) {
6413 return PEAR::raiseError("Invalid hour value '$h'");
6415 $ret = $this->setHourMinuteSecond(
6418 $this->partsecond == 0.0 ?
6420 $this->second + $this->partsecond,
6421 $pb_repeatedhourdefault
6424 if (PEAR::isError($ret)) {
6435 * Sets the minute field of the date object
6437 * @param int $m the minute
6438 * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
6439 * repeated hour is specified (defaults
6444 * @see Date::setHourMinuteSecond(), Date::setDateTime()
6446 public function setMinute($m, $pb_repeatedhourdefault = false)
6448 if ($m > 59 || $m < 0) {
6449 return PEAR::raiseError("Invalid minute value '$m'");
6451 $ret = $this->setHourMinuteSecond(
6454 $this->partsecond == 0.0 ?
6456 $this->second + $this->partsecond,
6457 $pb_repeatedhourdefault
6460 if (PEAR::isError($ret)) {
6471 * Sets the second field of the date object
6473 * @param mixed $s the second as integer or float
6474 * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
6475 * repeated hour is specified
6476 * (defaults to false)
6480 * @see Date::setHourMinuteSecond(), Date::setDateTime()
6482 public function setSecond($s, $pb_repeatedhourdefault = false)
6484 if ($s > 60 || // Leap seconds possible
6486 return PEAR::raiseError("Invalid second value '$s'");
6488 $ret = $this->setHourMinuteSecond(
6492 $pb_repeatedhourdefault
6495 if (PEAR::isError($ret)) {
6503 // {{{ setPartSecond()
6506 * Sets the part-second field of the date object
6508 * @param float $pn_ps the part-second
6509 * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
6510 * repeated hour is specified (defaults
6515 * @see Date::setHourMinuteSecond(), Date::setDateTime()
6516 * @since Method available since Release 1.5.0
6518 public function setPartSecond($pn_ps, $pb_repeatedhourdefault = false)
6520 if ($pn_ps >= 1 || $pn_ps < 0) {
6521 return PEAR::raiseError("Invalid part-second value '$pn_ps'");
6523 $ret = $this->setHourMinuteSecond(
6526 $this->second + $pn_ps,
6527 $pb_repeatedhourdefault
6530 if (PEAR::isError($ret)) {
6538 // {{{ setHourMinuteSecond()
6541 * Sets the hour, minute, second and part-second fields of the date object
6543 * N.B. if the repeated hour, due to the clocks going back, is specified,
6544 * the default is to assume local standard time.
6546 * @param int $h the hour
6547 * @param int $m the minute
6548 * @param mixed $s the second as integer or float
6549 * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
6550 * repeated hour is specified
6551 * (defaults to false)
6555 * @see Date::setDateTime()
6556 * @since Method available since Release 1.5.0
6558 public function setHourMinuteSecond($h, $m, $s, $pb_repeatedhourdefault = false)
6560 // Split second into integer and part-second:
6563 $hn_second = intval($s);
6564 $hn_partsecond = $s - $hn_second;
6566 $hn_second = (int)$s;
6567 $hn_partsecond = 0.0;
6570 $this->setLocalTime(
6578 $pb_repeatedhourdefault
6584 // {{{ setDateTime()
6587 * Sets all the fields of the date object (day, month, year, hour, minute
6590 * If specified year forms an invalid date, then PEAR error will be
6591 * returned. Note that setting each of these fields separately
6592 * may unintentionally return a PEAR error if a transitory date is
6593 * invalid between setting these fields.
6595 * N.B. if the repeated hour, due to the clocks going back, is specified,
6596 * the default is to assume local standard time.
6598 * @param int $pn_day the day
6599 * @param int $pn_month the month
6600 * @param int $pn_year the year
6601 * @param int $pn_hour the hour
6602 * @param int $pn_minute the minute
6603 * @param mixed $pm_second the second as integer or float
6604 * @param bool $pb_repeatedhourdefault whether to assume Summer time if a
6605 * repeated hour is specified
6606 * (defaults to false)
6610 * @see Date::setDayMonthYear(), Date::setHourMinuteSecond()
6611 * @since Method available since Release 1.5.0
6613 public function setDateTime(
6620 $pb_repeatedhourdefault = false
6623 if (!Date_Calc::isValidDate($d, $m, $y)) {
6624 return PEAR::raiseError(
6626 Date_Calc::dateFormat(
6632 "' is invalid calendar date",
6633 DATE_ERROR_INVALIDDATE
6636 // Split second into integer and part-second:
6638 if (is_float($pm_second)) {
6639 $hn_second = intval($pm_second);
6640 $hn_partsecond = $pm_second - $hn_second;
6642 $hn_second = (int)$pm_second;
6643 $hn_partsecond = 0.0;
6646 $this->setLocalTime(
6654 $pb_repeatedhourdefault
6670 * c-hanging-comment-ender-p: nil