* @author Pierre-Alain Joye * @author Firman Wandayandi * @author C.A. Woodcock * @copyright 1997-2007 Baba Buehler, Pierre-Alain Joye, Firman Wandayandi, C.A. Woodcock * @license http://www.opensource.org/licenses/bsd-license.php * BSD License * @version CVS: $Id$ * @link http://pear.php.net/package/Date */ // }}} // {{{ Error constants define('DATE_ERROR_INVALIDDATE', 1); define('DATE_ERROR_INVALIDTIME', 2); define('DATE_ERROR_INVALIDTIMEZONE', 3); define('DATE_ERROR_INVALIDDATEFORMAT', 4); define('DATE_ERROR_INVALIDFORMATSTRING', 5); // }}} // {{{ Includes require_once 'PEAR.php'; /** * Load Date_TimeZone */ require_once 'Date/TimeZone.php'; /** * Load Date_Calc */ require_once 'Date/Calc.php'; /** * Load Date_Span */ require_once 'Date/Span.php'; // }}} // {{{ General constants /** * Whether to capture the micro-time (in microseconds) by default * in calls to {@link Date::setNow()}. Note that this makes a call to * {@link http://www.php.net/gettimeofday gettimeofday()}, which may * not work on all systems. * * @since Constant available since Release 1.5.0 */ define('DATE_CAPTURE_MICROTIME_BY_DEFAULT', false); /** * Whether to correct, by adding the local Summer time offset, the * specified time if it falls in the 'skipped hour' (encountered * when the clocks go forward). * * N.B. if specified as 'false', and if a time zone that adjusts * for Summer time is specified, then an object of this class will * be set to a semi-invalid state if an invalid time is set. That * is, an error will not be returned, unless the user then calls * a function, directly or indirectly, that accesses the time * part of the object. So, for example, if the user calls: * * $date_object->formatLikeSQL('HH.MI.SS'); * * or: * * $date_object->addSeconds(30); * * an error will be returned if the time is invalid. However, * if the user calls: * * $date_object->addDays(1); * * for example, such that the time is no longer invalid, then the * object will no longer be in this invalid state. This behaviour * is intended to minimize unexpected errors when a user uses the * class to do addition with days only, and does not intend to * access the time. * * Of course, this constant will be unused if the user chooses to * work in UTC or a time zone without Summer time, in which case * this situation will never arise. * * This constant is set to 'true' by default for backwards-compatibility * reasons, however, you are recommended to set it to 'false'. Note that the * behaviour is not intended to match that of previous versions of the class * in terms of ignoring the Summer time offset when making calculations which * involve dates in both standard and Summer time - this was recognized as a * bug - but in terms of returning a PEAR error object when the user sets the * object to an invalid date (i.e. a time in the hour which is skipped when * the clocks go forwards, which in Europe would be a time such as 01.30). * Backwards compatibility here means that the behaviour is the same as it * used to be, less the bug. * * Note that this problem is not an issue for the user if any of these * conditions are satisfied: * *
    *
  1. the user uses a time zone that does not observe Summer time, e.g. UTC
  2. *
  3. the user never accesses the time, that is, he never makes a call to * {@link Date::getHour()} or {@link Date::formatLikeStrftime()} using * format code '%H', for example, even if he sets the time to * something invalid
  4. *
  5. the user sets DATE_CORRECTINVALIDTIME_DEFAULT to true
  6. *
* * @since Constant available since Release 1.5.0 * @see Date::isValidTime(), DATE_VALIDATE_DATE_BY_DEFAULT */ define('DATE_CORRECTINVALIDTIME_DEFAULT', true); /** * Whether to validate dates (i.e. day/month/year, ignoring the time) by * disallowing invalid dates (e.g. 31st February) being set by the following * functions: * * - {@link Date::setYear()} * - {@link Date::setMonth()} * - {@link Date::setDay()} * * If the constant is set to 'true', then the date will be checked (by * default), and if invalid, an error will be returned with the Date object * left unmodified. * * This constant is set to 'false' by default for backwards-compatibility * reasons, however, you are recommended to set it to 'true'. * * Note that {@link Date::setHour()}, {@link Date::setMinute()}, * {@link Date::setSecond()} and {@link Date::setPartSecond()} * allow an invalid date/time to be set regardless of the value of this * constant. * * @see Date::isValidDate(), Date::isValidTime(), Date::isNull(), * DATE_CORRECTINVALIDTIME_DEFAULT * @since Constant available since Release 1.5.0 */ define('DATE_VALIDATE_DATE_BY_DEFAULT', false); /** * Whether, by default, to accept times including leap seconds (i.e. '23.59.60') * when setting the date/time, and whether to count leap seconds in the * following functions: * * - {@link Date::addSeconds()} * - {@link Date::subtractSeconds()} * - {@link Date_Calc::addSeconds()} * - {@link Date::round()} * - {@link Date::roundSeconds()} * * This constant is set to 'false' by default for backwards-compatibility * reasons, however, you are recommended to set it to 'true'. * * Note that this constant does not affect {@link Date::addSpan()} and * {@link Date::subtractSpan()} which will not count leap seconds in any case. * * @since Constant available since Release 1.5.0 */ define('DATE_COUNT_LEAP_SECONDS', false); /** * Method to call when user invokes {@link Date::format()} * * @since Constant available since Release 1.5.1 */ define('DATE_FORMAT_METHOD', 'formatLikeStrftime'); // }}} // {{{ Output format constants (used in {@link Date::getDate()}) /** * "YYYY-MM-DD HH:MM:SS" */ define('DATE_FORMAT_ISO', 1); /** * "YYYYMMDDTHHMMSS(Z|(+/-)HHMM)?" */ define('DATE_FORMAT_ISO_BASIC', 2); /** * "YYYY-MM-DDTHH:MM:SS(Z|(+/-)HH:MM)?" */ define('DATE_FORMAT_ISO_EXTENDED', 3); /** * "YYYY-MM-DDTHH:MM:SS(.S*)?(Z|(+/-)HH:MM)?" */ define('DATE_FORMAT_ISO_EXTENDED_MICROTIME', 6); /** * "YYYYMMDDHHMMSS" */ define('DATE_FORMAT_TIMESTAMP', 4); /** * long int, seconds since the unix epoch */ define('DATE_FORMAT_UNIXTIME', 5); // }}} // {{{ Class: Date /** * Generic date handling class for PEAR * * Supports time zones with the Date_TimeZone class. Supports several * operations from Date_Calc on Date objects. * * Note to developers: the class stores the local time and date in the * local standard time. That is, it does not store the time as the * local Summer time when and if the time zone is in Summer time. It * is much easier to store local standard time and remember to offset * it when the user requests it. * * @category Date and Time * @package Date * @author Baba Buehler * @author Pierre-Alain Joye * @author Firman Wandayandi * @author C.A. Woodcock * @copyright 1997-2007 Baba Buehler, Pierre-Alain Joye, Firman Wandayandi, C.A. Woodcock * @license http://www.opensource.org/licenses/bsd-license.php * BSD License * @version Release: 1.5.0a1 * @link http://pear.php.net/package/Date */ class Date { // {{{ Properties /** * The year * * @var int * @access private * @since Property available since Release 1.0 */ public $year; /** * The month * * @var int * @access private * @since Property available since Release 1.0 */ public $month; /** * The day * * @var int * @access private * @since Property available since Release 1.0 */ public $day; /** * The hour * * @var int * @access private * @since Property available since Release 1.0 */ public $hour; /** * The minute * * @var int * @access private * @since Property available since Release 1.0 */ public $minute; /** * The second * * @var int * @access private * @since Property available since Release 1.0 */ public $second; /** * The parts of a second * * @var float * @access private * @since Property available since Release 1.4.3 */ public $partsecond; /** * The year in local standard time * * @var int * @access private * @since Property available since Release 1.5.0 */ public $on_standardyear; /** * The month in local standard time * * @var int * @access private * @since Property available since Release 1.5.0 */ public $on_standardmonth; /** * The day in local standard time * * @var int * @access private * @since Property available since Release 1.5.0 */ public $on_standardday; /** * The hour in local standard time * * @var int * @access private * @since Property available since Release 1.5.0 */ public $on_standardhour; /** * The minute in local standard time * * @var int * @access private * @since Property available since Release 1.5.0 */ public $on_standardminute; /** * The second in local standard time * * @var int * @access private * @since Property available since Release 1.5.0 */ public $on_standardsecond; /** * The part-second in local standard time * * @var float * @access private * @since Property available since Release 1.5.0 */ public $on_standardpartsecond; /** * Whether the object should accept and count leap seconds * * @var bool * @access private * @since Property available since Release 1.5.0 */ public $ob_countleapseconds; /** * Whether the time is valid as a local time (an invalid time * is one that lies in the 'skipped hour' at the point that * the clocks go forward) * * @var bool * @access private * @see Date::isValidTime() * @since Property available since Release 1.5.0 */ public $ob_invalidtime = null; /** * Date_TimeZone object for this date * * @var object Date_TimeZone object * @access private * @since Property available since Release 1.0 */ public $tz; /** * Defines the default weekday abbreviation length * * Formerly used by {@link Date::formatLikeStrftime()}, but now * redundant - the abbreviation for the current locale of the machine * is used. * * @var int * @access private * @since Property available since Release 1.4.4 */ public $getWeekdayAbbrnameLength = 3; // }}} // {{{ Constructor /** * Constructor * * Creates a new Date Object initialized to the current date/time in the * system-default timezone by default. A date optionally * passed in may be in the ISO 8601, TIMESTAMP or UNIXTIME format, * or another Date object. If no date is passed, the current date/time * is used. * * If a date is passed and an exception is returned by {@link Date::setDate()} * there is nothing that this function can do, so for this reason, it * is advisable to pass no parameter and to make a separate call to * Date::setDate(). A date/time should only be passed if known to be a * valid ISO 8601 string or a valid Unix timestamp. * * @param mixed $date optional ISO 8601 date/time to initialize; * or, a Unix time stamp * @param bool $pb_countleapseconds whether to count leap seconds * (defaults to * {@link DATE_COUNT_LEAP_SECONDS}) * * @return void * @access public * @see Date::setDate() */ public function Date( $date = null, $pb_countleapseconds = DATE_COUNT_LEAP_SECONDS ) { $this->ob_countleapseconds = $pb_countleapseconds; if (is_a($date, 'Date')) { $this->copy($date); } else { if (!is_null($date)) { // 'setDate()' expects a time zone to be already set: // $this->_setTZToDefault(); $this->setDate($date); } else { $this->setNow(); } } } // }}} // {{{ copy() /** * Copy values from another Date object * * Makes this Date a copy of another Date object. This is a * PHP4-compatible implementation of {@link Date::__clone()} in PHP5. * * @param object $date Date object to copy * * @return void * @access public */ public function copy($date) { $this->year = $date->year; $this->month = $date->month; $this->day = $date->day; $this->hour = $date->hour; $this->minute = $date->minute; $this->second = $date->second; $this->partsecond = $date->partsecond; $this->on_standardyear = $date->on_standardyear; $this->on_standardmonth = $date->on_standardmonth; $this->on_standardday = $date->on_standardday; $this->on_standardhour = $date->on_standardhour; $this->on_standardminute = $date->on_standardminute; $this->on_standardsecond = $date->on_standardsecond; $this->on_standardpartsecond = $date->on_standardpartsecond; $this->ob_countleapseconds = $date->ob_countleapseconds; $this->ob_invalidtime = $date->ob_invalidtime; $this->tz = new Date_TimeZone($date->getTZID()); $this->getWeekdayAbbrnameLength = $date->getWeekdayAbbrnameLength; } // }}} // {{{ __clone() /** * Copy values from another Date object * * Makes this Date a copy of another Date object. For PHP5 * only. * * @return void * @access public * @see Date::copy() */ public function __clone() { // This line of code would be preferable, but will only // compile in PHP5: // // $this->tz = clone $this->tz; $this->tz = new Date_TimeZone($this->getTZID()); } // }}} // {{{ isNull() /** * Returns whether the object is null (i.e. no date has been set) * * If the object is set to an invalid date, then this function will * still return 'false'. To check whether the date is valid use * either {@link Date::isValidDate()} (to check the day/month/year * part of the object only) or {@link Date::isValidTime()} (to check * the time, in addition to the day/month/year part). * * @return bool * @access public * @see Date::setDate(), Date::isValidDate(), Date::isValidTime() * @since Method available since Release 1.5.0 */ public function isNull() { return is_null($this->year); } // }}} // {{{ isValidDate() /** * Returns whether the date (i.e. day/month/year) is valid * * It is not possible to set the object to an invalid date using * {@link Date::setDate()}, but it is possible to do so using the * following functions: * * - {@link Date::setYear()} * - {@link Date::setMonth()} * - {@link Date::setDay()} * * However you can prevent this possibility (by default) by setting * {@link DATE_VALIDATE_DATE_BY_DEFAULT} to 'true', in which case * these three functions will return an error if they specify an * invalid date, and the object will be unmodified. * * Note that this function only checks the day/month/year part of * the object. Even if this is valid, it is still possible for the * time to be invalid (see {@link DATE_CORRECTINVALIDTIME_DEFAULT}). * To check the time as well, use {@link Date::isValidTime()}. * * @return bool * @access public * @see Date::setDate(), Date::isNull(), Date::isValidTime() * @since Method available since Release 1.5.0 */ public function isValidDate() { return !Date::isNull() && Date_Calc::isValidDate($this->year, $this->month, $this->day); } // }}} // {{{ setDate() /** * Sets the date/time of the object based on the input date and format * * Accepts a string in three possible formats, and in this order of * precedence: * * - ISO 8601 date (see {@link http://en.wikipedia.org/wiki/ISO_8601}) * - Time-Stamp (i.e. 'YYYYMMDDHHMMSS') * - Unix time-stamp (see {@link http://en.wikipedia.org/wiki/Unix_time}) * * Note that if you want to pass a Unix time-stamp then you need to set * the $format parameter to {@link DATE_FORMAT_UNIXTIME}, or else use the * method {@link Date::setFromTime()}. * * The input string should be a date/time representation in one of the * following general formats: * * - T * - (non-ISO-standard) * - (non-ISO-standard) * - T i.e. without optional representation * - * - * - i.e. without optional