* usually better to store datetimes as either UTC datetime or plain unix
* timestamp. I usually go with the former - using database datetime type.
*/
+
+/**
+ * @category Horde
+ * @package Horde_Date
+ */
class Horde_Date
{
const DATE_SUNDAY = 0;
'sec' => self::MASK_SECOND,
);
- var $_formatCache = array();
+ protected $_formatCache = array();
/**
* Build a new date object. If $date contains date parts, use them to
* 03 Mar 1973)
* - unix timestamps
*/
- function __construct($date = null, $timezone = null)
+ public function __construct($date = null, $timezone = null)
{
if (!self::$_supportedSpecs) {
self::$_supportedSpecs = self::$_defaultSpecs;
*
* @return string This object converted to a string.
*/
- function __toString()
+ public function __toString()
{
try {
return $this->format($this->_defaultFormat);
*
* @return DateTime
*/
- function toDateTime()
+ public function toDateTime()
{
$date = new DateTime(null, new DateTimeZone($this->_timezone));
$date->setDate($this->_year, $this->_month, $this->_mday);
}
/**
+ * Converts a date in the proleptic Gregorian calendar to the no of days
+ * since 24th November, 4714 B.C.
+ *
+ * Returns the no of days since Monday, 24th November, 4714 B.C. in the
+ * proleptic Gregorian calendar (which is 24th November, -4713 using
+ * 'Astronomical' year numbering, and 1st January, 4713 B.C. in the
+ * proleptic Julian calendar). This is also the first day of the 'Julian
+ * Period' proposed by Joseph Scaliger in 1583, and the number of days
+ * since this date is known as the 'Julian Day'. (It is not directly
+ * to do with the Julian calendar, although this is where the name
+ * is derived from.)
+ *
+ * The algorithm is valid for all years (positive and negative), and
+ * also for years preceding 4714 B.C.
+ *
+ * Algorithm is from PEAR::Date_Calc
+ *
+ * @author Monte Ohrt <monte@ispi.net>
+ * @author Pierre-Alain Joye <pajoye@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @author C.A. Woodcock <c01234@netcomuk.co.uk>
+ *
+ * @return integer The number of days since 24th November, 4714 B.C.
+ */
+ public function toDays()
+ {
+ $day = $this->_mday;
+ $month = $this->_month;
+ $year = $this->_year;
+
+ if ($month > 2) {
+ // March = 0, April = 1, ..., December = 9,
+ // January = 10, February = 11
+ $month -= 3;
+ } else {
+ $month += 9;
+ --$year;
+ }
+
+ $hb_negativeyear = $year < 0;
+ $century = intval($year / 100);
+ $year = $year % 100;
+
+ if ($hb_negativeyear) {
+ // Subtract 1 because year 0 is a leap year;
+ // And N.B. that we must treat the leap years as occurring
+ // one year earlier than they do, because for the purposes
+ // of calculation, the year starts on 1st March:
+ //
+ return intval((14609700 * $century + ($year == 0 ? 1 : 0)) / 400) +
+ intval((1461 * $year + 1) / 4) +
+ intval((153 * $month + 2) / 5) +
+ $day + 1721118;
+ } else {
+ return intval(146097 * $century / 4) +
+ intval(1461 * $year / 4) +
+ intval((153 * $month + 2) / 5) +
+ $day + 1721119;
+ }
+ }
+
+ /**
+ * Converts number of days since 24th November, 4714 B.C. (in the proleptic
+ * Gregorian calendar, which is year -4713 using 'Astronomical' year
+ * numbering) to Gregorian calendar date.
+ *
+ * Returned date belongs to the proleptic Gregorian calendar, using
+ * 'Astronomical' year numbering.
+ *
+ * The algorithm is valid for all years (positive and negative), and
+ * also for years preceding 4714 B.C. (i.e. for negative 'Julian Days'),
+ * and so the only limitation is platform-dependent (for 32-bit systems
+ * the maximum year would be something like about 1,465,190 A.D.).
+ *
+ * N.B. Monday, 24th November, 4714 B.C. is Julian Day '0'.
+ *
+ * Algorithm is from PEAR::Date_Calc
+ *
+ * @author Monte Ohrt <monte@ispi.net>
+ * @author Pierre-Alain Joye <pajoye@php.net>
+ * @author Daniel Convissor <danielc@php.net>
+ * @author C.A. Woodcock <c01234@netcomuk.co.uk>
+ *
+ * @param int $days the number of days since 24th November, 4714 B.C.
+ * @param string $format the string indicating how to format the output
+ *
+ * @return Horde_Date A Horde_Date object representing the date.
+ */
+ public static function fromDays($days)
+ {
+ $days = intval($days);
+
+ $days -= 1721119;
+ $century = floor((4 * $days - 1) / 146097);
+ $days = floor(4 * $days - 1 - 146097 * $century);
+ $day = floor($days / 4);
+
+ $year = floor((4 * $day + 3) / 1461);
+ $day = floor(4 * $day + 3 - 1461 * $year);
+ $day = floor(($day + 4) / 4);
+
+ $month = floor((5 * $day - 3) / 153);
+ $day = floor(5 * $day - 3 - 153 * $month);
+ $day = floor(($day + 5) / 5);
+
+ $year = $century * 100 + $year;
+ if ($month < 10) {
+ $month +=3;
+ } else {
+ $month -=9;
+ ++$year;
+ }
+
+ return new Horde_Date($year, $month, $day);
+ }
+
+ /**
* Getter for the date and time properties.
*
* @param string $name One of 'year', 'month', 'mday', 'hour', 'min' or
*
* @return integer The property value, or null if not set.
*/
- function __get($name)
+ public function __get($name)
{
if ($name == 'day') {
$name = 'mday';
* 'sec'.
* @param integer $value The property value.
*/
- function __set($name, $value)
+ public function __set($name, $value)
{
if ($name == 'day') {
$name = 'mday';
*
* @return boolen True if the property exists and is set.
*/
- function __isset($name)
+ public function __isset($name)
{
if ($name == 'day') {
$name = 'mday';
/**
* Returns whether a year is a leap year.
*
- * @static
- *
* @param integer $year The year.
*
* @return boolan True if the year is a leap year.
*/
- function isLeapYear($year)
+ public static function isLeapYear($year)
{
if (strlen($year) != 4 || preg_match('/\D/', $year)) {
return false;
*
* @return Horde_Date The date of the first day of the given week.
*/
- function firstDayOfWeek($week, $year)
+ public static function firstDayOfWeek($week, $year)
{
return new Horde_Date(sprintf('%04dW%02d', $year, $week));
}
/**
* Returns the number of days in the specified month.
*
- * @static
- *
* @param integer $month The month
* @param integer $year The year.
*
* @return integer The number of days in the month.
*/
- function daysInMonth($month, $year)
+ public static function daysInMonth($month, $year)
{
static $cache = array();
if (!isset($cache[$year][$month])) {
*
* @return integer The day of the week.
*/
- function dayOfWeek()
+ public function dayOfWeek()
{
if ($this->_month > 2) {
$month = $this->_month - 2;
*
* @return integer The day of the year.
*/
- function dayOfYear()
+ public function dayOfYear()
{
return $this->format('z') + 1;
}
*
* @return integer The week number.
*/
- function weekOfMonth()
+ public function weekOfMonth()
{
return ceil($this->_mday / 7);
}
*
* @return integer The week number.
*/
- function weekOfYear()
+ public function weekOfYear()
{
return $this->format('W');
}
/**
* Return the number of weeks in the given year (52 or 53).
*
- * @static
- *
* @param integer $year The year to count the number of weeks in.
*
* @return integer $numWeeks The number of weeks in $year.
*/
- function weeksInYear($year)
+ public static function weeksInYear($year)
{
// Find the last Thursday of the year.
$date = new Horde_Date($year . '-12-31');
* @param integer $weekday The day of the week (0 = Sunday, etc).
* @param integer $nth The $nth $weekday to set to (defaults to 1).
*/
- function setNthWeekday($weekday, $nth = 1)
+ public function setNthWeekday($weekday, $nth = 1)
{
if ($weekday < self::DATE_SUNDAY || $weekday > self::DATE_SATURDAY) {
return;
*
* @return boolean Validity, counting leap years, etc.
*/
- function isValid()
+ public function isValid()
{
return ($this->_year >= 0 && $this->_year <= 9999);
}
/**
- * Correct any over- or underflows in any of the date's members.
- *
- * @param integer $mask We may not want to correct some overflows.
- */
- protected function _correct($mask = self::MASK_ALLPARTS)
- {
- if ($mask & self::MASK_SECOND) {
- if ($this->_sec < 0 || $this->_sec > 59) {
- $mask |= self::MASK_MINUTE;
-
- $this->_min += (int)($this->_sec / 60);
- $this->_sec %= 60;
- if ($this->_sec < 0) {
- $this->_min--;
- $this->_sec += 60;
- }
- }
- }
-
- if ($mask & self::MASK_MINUTE) {
- if ($this->_min < 0 || $this->_min > 59) {
- $mask |= self::MASK_HOUR;
-
- $this->_hour += (int)($this->_min / 60);
- $this->_min %= 60;
- if ($this->_min < 0) {
- $this->_hour--;
- $this->_min += 60;
- }
- }
- }
-
- if ($mask & self::MASK_HOUR) {
- if ($this->_hour < 0 || $this->_hour > 23) {
- $mask |= self::MASK_DAY;
-
- $this->_mday += (int)($this->_hour / 24);
- $this->_hour %= 24;
- if ($this->_hour < 0) {
- $this->_mday--;
- $this->_hour += 24;
- }
- }
- }
-
- if ($mask & self::MASK_MONTH) {
- $this->_year += (int)($this->_month / 12);
- $this->_month %= 12;
- if ($this->_month < 1) {
- $this->_year--;
- $this->_month += 12;
- }
- }
-
- if ($mask & self::MASK_DAY) {
- while ($this->_mday > 28 &&
- $this->_mday > Horde_Date::daysInMonth($this->_month, $this->_year)) {
- $this->_mday -= Horde_Date::daysInMonth($this->_month, $this->_year);
- ++$this->_month;
- $this->_correct(self::MASK_MONTH);
- }
- while ($this->_mday < 1) {
- --$this->_month;
- $this->_correct(self::MASK_MONTH);
- $this->_mday += Horde_Date::daysInMonth($this->_month, $this->_year);
- }
- }
- }
-
- /**
* Compare this date to another date object to see which one is
* greater (later). Assumes that the dates are in the same
* timezone.
* >= 1 if $this is greater (later)
* <= -1 if $other is greater (later)
*/
- function compareDate($other)
+ public function compareDate($other)
{
if (!is_a($other, 'Horde_Date')) {
$other = new Horde_Date($other);
*
* @return boolean True if this date is after the other.
*/
- function after($other)
+ public function after($other)
{
return $this->compareDate($other) > 0;
}
*
* @return boolean True if this date is before the other.
*/
- function before($other)
+ public function before($other)
{
return $this->compareDate($other) < 0;
}
*
* @return boolean True if this date is the same like the other.
*/
- function equals($other)
+ public function equals($other)
{
return $this->compareDate($other) == 0;
}
* >= 1 if $this is greater (later)
* <= -1 if $other is greater (later)
*/
- function compareTime($other)
+ public function compareTime($other)
{
if (!is_a($other, 'Horde_Date')) {
$other = new Horde_Date($other);
* >= 1 if $this is greater (later)
* <= -1 if $other is greater (later)
*/
- function compareDateTime($other)
+ public function compareDateTime($other)
{
if (!is_a($other, 'Horde_Date')) {
$other = new Horde_Date($other);
}
/**
+ * Returns number of days between this date and another.
+ *
+ * @param Horde_Date $other The other day to diff with.
+ *
+ * @return integer The absolute number of days between the two dates.
+ */
+ public function diff($other)
+ {
+ return abs($this->toDays() - $other->toDays());
+ }
+
+ /**
* Get the time offset for local time zone.
*
* @param boolean $colon Place a colon between hours and minutes?
*
* @return string Timezone offset as a string in the format +HH:MM.
*/
- function tzOffset($colon = true)
+ public function tzOffset($colon = true)
{
return $colon ? $this->format('P') : $this->format('O');
}
*
* @return integer A unix timestamp.
*/
- function timestamp()
+ public function timestamp()
{
if ($this->_year >= 1970 && $this->_year < 2038) {
return mktime($this->_hour, $this->_min, $this->_sec,
*
* @return integer A unix timestamp.
*/
- function datestamp()
+ public function datestamp()
{
if ($this->_year >= 1970 && $this->_year < 2038) {
return mktime(0, 0, 0, $this->_month, $this->_mday, $this->_year);
*
* @return string Date and time.
*/
- function dateString()
+ public function dateString()
{
return sprintf('%04d%02d%02d', $this->_year, $this->_month, $this->_mday);
}
*
* @return string Date and time.
*/
- function toJson()
+ public function toJson()
{
return $this->format(self::DATE_JSON);
}
*
* @return string Formatted time.
*/
- function format($format)
+ public function format($format)
{
if (!isset($this->_formatCache[$format])) {
$this->_formatCache[$format] = $this->toDateTime()->format($format);
*
* @return string strftime() formatted date and time.
*/
- function strftime($format)
+ public function strftime($format)
{
if (preg_match('/%[^' . self::$_supportedSpecs . ']/', $format)) {
return strftime($format, $this->timestamp());
*
* @return string strftime() formatted date and time.
*/
- function _strftime($format)
+ protected function _strftime($format)
{
if (preg_match('/%[bBpxX]/', $format)) {
require_once 'Horde/NLS.php';
}
/**
+ * Correct any over- or underflows in any of the date's members.
+ *
+ * @param integer $mask We may not want to correct some overflows.
+ */
+ protected function _correct($mask = self::MASK_ALLPARTS)
+ {
+ if ($mask & self::MASK_SECOND) {
+ if ($this->_sec < 0 || $this->_sec > 59) {
+ $mask |= self::MASK_MINUTE;
+
+ $this->_min += (int)($this->_sec / 60);
+ $this->_sec %= 60;
+ if ($this->_sec < 0) {
+ $this->_min--;
+ $this->_sec += 60;
+ }
+ }
+ }
+
+ if ($mask & self::MASK_MINUTE) {
+ if ($this->_min < 0 || $this->_min > 59) {
+ $mask |= self::MASK_HOUR;
+
+ $this->_hour += (int)($this->_min / 60);
+ $this->_min %= 60;
+ if ($this->_min < 0) {
+ $this->_hour--;
+ $this->_min += 60;
+ }
+ }
+ }
+
+ if ($mask & self::MASK_HOUR) {
+ if ($this->_hour < 0 || $this->_hour > 23) {
+ $mask |= self::MASK_DAY;
+
+ $this->_mday += (int)($this->_hour / 24);
+ $this->_hour %= 24;
+ if ($this->_hour < 0) {
+ $this->_mday--;
+ $this->_hour += 24;
+ }
+ }
+ }
+
+ if ($mask & self::MASK_MONTH) {
+ $this->_year += (int)($this->_month / 12);
+ $this->_month %= 12;
+ if ($this->_month < 1) {
+ $this->_year--;
+ $this->_month += 12;
+ }
+ }
+
+ if ($mask & self::MASK_DAY) {
+ while ($this->_mday > 28 &&
+ $this->_mday > Horde_Date::daysInMonth($this->_month, $this->_year)) {
+ $this->_mday -= Horde_Date::daysInMonth($this->_month, $this->_year);
+ ++$this->_month;
+ $this->_correct(self::MASK_MONTH);
+ }
+ while ($this->_mday < 1) {
+ --$this->_month;
+ $this->_correct(self::MASK_MONTH);
+ $this->_mday += Horde_Date::daysInMonth($this->_month, $this->_year);
+ }
+ }
+ }
+
+ /**
* Handle args in order: year month day hour min sec tz
*/
protected function _initializeFromArgs($args)
(string)(int)$date['minute'] == $date['minute']) {
$this->_min = (int)$date['minute'];
}
+
$this->_correct();
}