/** * Rounds the date according to the specified precision * * The precision parameter must be one of the following constants: * * <code>DATE_PRECISION_YEAR</code> * <code>DATE_PRECISION_MONTH</code> * <code>DATE_PRECISION_DAY</code> * <code>DATE_PRECISION_HOUR</code> * <code>DATE_PRECISION_10MINUTES</code> * <code>DATE_PRECISION_MINUTE</code> * <code>DATE_PRECISION_10SECONDS</code> * <code>DATE_PRECISION_SECOND</code> * * The precision can also be specified as an integral offset from * one of these constants, where the offset reflects a precision * of 10 to the power of the offset greater than the constant. * For example: * * <code>DATE_PRECISION_YEAR - 1</code> rounds the date to the nearest 10 * years * <code>DATE_PRECISION_YEAR - 3</code> rounds the date to the nearest 1000 * years * <code>DATE_PRECISION_SECOND + 1</code> rounds the date to 1 decimal * point of a second * <code>DATE_PRECISION_SECOND + 1</code> rounds the date to 3 decimal * points of a second * <code>DATE_PRECISION_SECOND + 1</code> rounds the date to the nearest 10 * seconds (thus it is equivalent to * DATE_PRECISION_10SECONDS) * * N.B. This function requires a time in UTC if both the precision is at * least DATE_PRECISION_SECOND and leap seconds are being counted, otherwise * any local time is acceptable. * * @param int $pn_precision a 'DATE_PRECISION_*' constant * @param int $pn_day the day of the month * @param int $pn_month the month * @param int $pn_year the year * @param int $pn_hour the hour * @param int $pn_minute the minute * @param mixed $pn_second the second as integer or float * @param bool $pb_countleap whether to count leap seconds (defaults to * DATE_COUNT_LEAP_SECONDS) * * @return array array of year, month, day, hour, minute, second * @access public * @static * @since Method available since Release 1.5.0 */ function round($pn_precision, $pn_day, $pn_month, $pn_year, $pn_hour = 0, $pn_minute = 0, $pn_second = 0, $pb_countleap = DATE_COUNT_LEAP_SECONDS) { if ($pn_precision <= DATE_PRECISION_YEAR) { $hn_month = 0; $hn_day = 0; $hn_hour = 0; $hn_minute = 0; $hn_second = 0; if ($pn_precision < DATE_PRECISION_YEAR) { $hn_year = round($pn_year, $pn_precision - DATE_PRECISION_YEAR); } else { // Check part-year: // $hn_midyear = (Date_Calc::firstDayOfYear($pn_year + 1) - Date_Calc::firstDayOfYear($pn_year)) / 2; if (($hn_days = Date_Calc::dayOfYear($pn_day, $pn_month, $pn_year)) <= $hn_midyear - 1) { $hn_year = $pn_year; } else { if ($hn_days >= $hn_midyear) { // Round up: // $hn_year = $pn_year + 1; } else { // Take time into account: // $hn_partday = Date_Calc::secondsPastMidnight($pn_hour, $pn_minute, $pn_second) / 86400; if ($hn_partday >= $hn_midyear - $hn_days) { // Round up: // $hn_year = $pn_year + 1; } else { $hn_year = $pn_year; } } } } } else { if ($pn_precision == DATE_PRECISION_MONTH) { $hn_year = $pn_year; $hn_day = 0; $hn_hour = 0; $hn_minute = 0; $hn_second = 0; $hn_firstofmonth = Date_Calc::firstDayOfMonth($pn_month, $pn_year); $hn_midmonth = (Date_Calc::lastDayOfMonth($pn_month, $pn_year) + 1 - $hn_firstofmonth) / 2; if (($hn_days = Date_Calc::dateToDays($pn_day, $pn_month, $pn_year) - $hn_firstofmonth) <= $hn_midmonth - 1) { $hn_month = $pn_month; } else { if ($hn_days >= $hn_midmonth) { // Round up: // list($hn_year, $hn_month) = Date_Calc::nextMonth($pn_month, $pn_year); } else { // Take time into account: // $hn_partday = Date_Calc::secondsPastMidnight($pn_hour, $pn_minute, $pn_second) / 86400; if ($hn_partday >= $hn_midmonth - $hn_days) { // Round up: // list($hn_year, $hn_month) = Date_Calc::nextMonth($pn_month, $pn_year); } else { $hn_month = $pn_month; } } } } else { if ($pn_precision == DATE_PRECISION_DAY) { $hn_year = $pn_year; $hn_month = $pn_month; $hn_hour = 0; $hn_minute = 0; $hn_second = 0; if (Date_Calc::secondsPastMidnight($pn_hour, $pn_minute, $pn_second) >= 43200) { // Round up: // list($hn_year, $hn_month, $hn_day) = explode(" ", Date_Calc::nextDay($pn_day, $pn_month, $pn_year, "%Y %m %d")); } else { $hn_day = $pn_day; } } else { if ($pn_precision == DATE_PRECISION_HOUR) { $hn_year = $pn_year; $hn_month = $pn_month; $hn_day = $pn_day; $hn_minute = 0; $hn_second = 0; if (Date_Calc::secondsPastTheHour($pn_minute, $pn_second) >= 1800) { // Round up: // list($hn_year, $hn_month, $hn_day, $hn_hour) = Date_Calc::addHours(1, $pn_day, $pn_month, $pn_year, $pn_hour); } else { $hn_hour = $pn_hour; } } else { if ($pn_precision <= DATE_PRECISION_MINUTE) { $hn_year = $pn_year; $hn_month = $pn_month; $hn_day = $pn_day; $hn_hour = $pn_hour; $hn_second = 0; if ($pn_precision < DATE_PRECISION_MINUTE) { $hn_minute = round($pn_minute, $pn_precision - DATE_PRECISION_MINUTE); } else { // Check seconds: // if ($pn_second >= 30) { // Round up: // list($hn_year, $hn_month, $hn_day, $hn_hour, $hn_minute) = Date_Calc::addMinutes(1, $pn_day, $pn_month, $pn_year, $pn_hour, $pn_minute); } else { $hn_minute = $pn_minute; } } } else { // Precision is at least (DATE_PRECISION_SECOND - 1): // $hn_year = $pn_year; $hn_month = $pn_month; $hn_day = $pn_day; $hn_hour = $pn_hour; $hn_minute = $pn_minute; $hn_second = round($pn_second, $pn_precision - DATE_PRECISION_SECOND); if (fmod($hn_second, 1) == 0.0) { $hn_second = (int) $hn_second; if ($hn_second != intval($pn_second)) { list($hn_year, $hn_month, $hn_day, $hn_hour, $hn_minute, $hn_second) = Date_Calc::addSeconds($hn_second - intval($pn_second), $pn_day, $pn_month, $pn_year, $pn_hour, $pn_minute, intval($pn_second), $pn_precision >= DATE_PRECISION_SECOND && $pb_countleap); // // (N.B. if rounded to nearest 10 seconds, // user does not expect seconds to be '60') } } } } } } } return array((int) $hn_year, (int) $hn_month, (int) $hn_day, (int) $hn_hour, (int) $hn_minute, $hn_second); }
/** * 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: * * - <b><date>T<time><time-zone></b> * - <b><date> <time><time-zone></b> (non-ISO-standard) * - <b><date><time><time-zone></b> (non-ISO-standard) * - <b><date>T<time></b> i.e. without optional <time-zone> representation * - <b><date> <time></b> * - <b><date><time></b> * - <b><date></b> i.e. without optional <time> representation * * that is, the representation must be comprised of a <b><date></b> part, * with an optional <b><time></b> part, which itself may include an optional * <time-zone> part, each of which may consist of any one of the permitted * formats detailed below. The <b><date></b> and <b><time</b> representations * should be divided with the time designator <b>T</b> according to the ISO 8601 * standard, although this method also permits representations divided by a * space, or by no delimiter at all. * * The <b><date></b> representation should be in one of the following formats: * * - <b>Calendar date</b>: <b>YYYY-MM-DD</b> (extended format) or * <b>YYYYMMDD</b> (basic format), where [YYYY] * indicates the four-digit year (0000-9999), [MM] * indicates the month (01-12) and [DD] indicates the * day of the month [01-31] * - <b>ISO week date</b>: <b>YYYY-Www-D</b> (extended format) or * <b>YYYYWwwD</b> (basic format), where [YYYY] * indicates the ISO year (slightly different from the * calendar year (see below)), [Www] indicates the ISO * week no prefixed by the letter 'W' (W01-W53) and * [D] indicates the ISO week-day (1-7), beginning on * Monday and ending on Sunday. (Also see * {@link http://en.wikipedia.org/wiki/ISO_week_date}.) * - <b>Ordinal date</b>: <b>YYYY-DDD</b> (extended format) or * <b>YYYYDDD</b> (basic format), where [YYYY] * indicates the four-digit year (0000-9999) and [DDD] * indicates the day of the year (001-366) * * The <b><time></b> representation should be in one of the following formats: * * - <b>hh:mm:ss</b> (extended format) or <b>hhmmss</b> (basic format) * - <b>hh:mm</b> (extended format) or <b>hhmm</b> (basic format) * - <b>hh</b> (extended format) or <b>hh</b> (basic format) * * where [hh] represents the hour (00-24), [mm] represents the minute (00-59) * and [ss] represents the second (00-60) * * Format parameter should be one of the specified DATE_FORMAT_* constants: * * - <b>{@link DATE_FORMAT_ISO}</b> - 'YYYY-MM-DD HH:MI:SS' * - <b>{@link DATE_FORMAT_ISO_BASIC}</b> - 'YYYYMMDDTHHMMSS(Z|(+/-)HHMM)?' * - <b>{@link DATE_FORMAT_ISO_EXTENDED}</b> - 'YYYY-MM-DDTHH:MM:SS(Z|(+/-)HH:MM)?' * - <b>{@link DATE_FORMAT_ISO_EXTENDED_MICROTIME}</b> - 'YYYY-MM-DDTHH:MM:SS(.S*)?(Z|(+/-)HH:MM)?' * - <b>{@link DATE_FORMAT_TIMESTAMP}</b> - 'YYYYMMDDHHMMSS' * - <b>{@link DATE_FORMAT_UNIXTIME}</b> - long integer of the no of seconds since * the Unix Epoch * (1st January 1970 00.00.00 GMT) * * @param string $date input date * @param int $format optional format constant * (DATE_FORMAT_*) of the input date. * This parameter is not needed, * except to force the setting of the * date from a Unix time-stamp (for * which use * {@link DATE_FORMAT_UNIXTIME}). * (Defaults to * {@link DATE_FORMAT_ISO}.) * @param bool $pb_repeatedhourdefault value to return if repeated * hour is specified (defaults * to false) * * @return void * @access public * @see Date::isNull(), Date::isValidDate(), Date::isValidTime(), * Date::setFromTime() */ function setDate($date, $format = DATE_FORMAT_ISO, $pb_repeatedhourdefault = false) { if ($format == DATE_FORMAT_UNIXTIME) { if (is_numeric($date)) { // Assume Unix time-stamp: // $this->setFromTime((int) $date); } else { return PEAR::raiseError("'{$date}' not valid Unix time-stamp"); } } else { if (preg_match('/^([0-9]{4,4})-?(' . '(0[1-9]|1[0-2])-?(0[1-9]|[12][0-9]|3[01])|' . 'W(0[1-9]|[1-4][0-9]|5[0-3])-?([1-7])|' . '(0(0[1-9]|[1-9][0-9])|[12][0-9]{2,2}|3([0-5][0-9]|6[1-6]))' . ')([T\\s]?' . '([01][0-9]|2[0-3])(:?' . '([0-5][0-9])(:?' . '([0-5][0-9]|60)([,.][0-9]+)?)?)?' . '(Z|[+\\-][0-9]{2,2}(:?[0-5][0-9])?)?)?$/i', $date, $regs)) { if (substr($regs[2], 0, 1) == "W") { // ISO week date (YYYY-Www-D) // $hs_date = Date_Calc::isoWeekToDate($regs[6], $regs[5], $regs[1], "%Y %m %d"); if (PEAR::isError($hs_date)) { return $hs_date; } list($hs_year, $hs_month, $hs_day) = explode(" ", $hs_date); } else { if (strlen($regs[2]) == 3) { // ISO ordinal date (YYYY-DDD) // $hn_jd = Date_Calc::firstDayOfYear($regs[1]) + $regs[2] - 1; list($hs_year, $hs_month, $hs_day) = explode(" ", Date_Calc::daysToDate($hn_jd, "%Y %m %d")); } else { // ISO calendar date (YYYY-MM-DD) // // DATE_FORMAT_ISO, ISO_BASIC, ISO_EXTENDED, and TIMESTAMP // These formats are extremely close to each other. This regex // is very loose and accepts almost any butchered format you could // throw at it. e.g. 2003-10-07 19:45:15 and 2003-10071945:15 // are the same thing in the eyes of this regex, even though the // latter is not a valid ISO 8601 date. // $hs_year = $regs[1]; $hs_month = $regs[3]; $hs_day = $regs[4]; if (!Date_Calc::isValidDate($hs_day, $hs_month, $hs_year)) { return PEAR::raiseError("'" . Date_Calc::dateFormat($hs_year, $hs_month, $hs_day, "%Y-%m-%d") . "' is invalid calendar date", DATE_ERROR_INVALIDDATE); } } } if (isset($regs[17])) { if ($regs[17] == "Z") { $this->tz = new Date_TimeZone("UTC"); } else { $this->tz = new Date_TimeZone("UTC" . $regs[17]); } } $this->setLocalTime($hs_day, $hs_month, $hs_year, isset($regs[11]) && $regs[11] != "" ? $regs[11] : 0, isset($regs[13]) && $regs[13] != "" ? $regs[13] : 0, isset($regs[15]) && $regs[15] != "" ? $regs[15] : 0, isset($regs[16]) && $regs[16] != "" ? $regs[16] : 0.0, $pb_repeatedhourdefault); } else { return PEAR::raiseError("Date '{$date}' not in ISO 8601 format", DATE_ERROR_INVALIDDATEFORMAT); } } }