Source for file Calc.php
Documentation is available at Calc.php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
* Calculates, manipulates and retrieves dates
* It does not rely on 32-bit system time stamps, so it works dates
* before 1970 and after 2038.
* Copyright (c) 1999-2007 Monte Ohrt, Pierre-Alain Joye, Daniel Convissor,
* Redistribution and use in source and binary forms, with or without
* modification, are permitted under the terms of the BSD License.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* @category Date and Time
* @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>
* @copyright 1999-2007 Monte Ohrt, Pierre-Alain Joye, Daniel Convissor, C.A. Woodcock
* @license http://www.opensource.org/licenses/bsd-license.php
* @version CVS: $Id: Calc.php,v 1.57 2008/03/23 18:34:16 c01234 Exp $
* @link http://pear.php.net/package/Date
* @since File available since Release 1.2
// {{{ General constants:
if (!defined('DATE_CALC_BEGIN_WEEKDAY')) {
* Defines what day starts the week
* Monday (1) is the international standard.
* Redefine this to 0 if you want weeks to begin on Sunday.
define('DATE_CALC_BEGIN_WEEKDAY', 1 );
if (!defined('DATE_CALC_FORMAT')) {
* The default value for each method's $format parameter
* The default is '%Y%m%d'. To override this default, define
* this constant before including Calc.php.
* @since Constant available since Release 1.4.4
define('DATE_CALC_FORMAT', '%Y%m%d');
// {{{ Date precision constants (used in 'round()' and 'trunc()'):
define('DATE_PRECISION_YEAR', -2 );
define('DATE_PRECISION_MONTH', -1 );
define('DATE_PRECISION_DAY', 0 );
define('DATE_PRECISION_HOUR', 1 );
define('DATE_PRECISION_10MINUTES', 2 );
define('DATE_PRECISION_MINUTE', 3 );
define('DATE_PRECISION_10SECONDS', 4 );
define('DATE_PRECISION_SECOND', 5 );
* Calculates, manipulates and retrieves dates
* It does not rely on 32-bit system time stamps, so it works dates
* before 1970 and after 2038.
* @category Date and Time
* @author Monte Ohrt <monte@ispi.net>
* @author Daniel Convissor <danielc@php.net>
* @author C.A. Woodcock <c01234@netcomuk.co.uk>
* @copyright 1999-2007 Monte Ohrt, Pierre-Alain Joye, Daniel Convissor, C.A. Woodcock
* @license http://www.opensource.org/licenses/bsd-license.php
* @version Release: 1.5.0a1
* @link http://pear.php.net/package/Date
* @since Class available since Release 1.2
* Formats the date in the given format, much like strfmt()
* This function is used to alleviate the problem with 32-bit numbers for
* dates pre 1970 or post 2038, as strfmt() has on most systems.
* Most of the formatting options are compatible.
* %a abbreviated weekday name (Sun, Mon, Tue)
* %A full weekday name (Sunday, Monday, Tuesday)
* %b abbreviated month name (Jan, Feb, Mar)
* %B full month name (January, February, March)
* %d day of month (range 00 to 31)
* %e day of month, single digit (range 0 to 31)
* %E number of days since unspecified epoch (integer)
* (%E is useful for passing a date in a URL as
* an integer value. Then simply use
* daysToDate() to convert back to a date.)
* %j day of year (range 001 to 366)
* %m month as decimal number (range 1 to 12)
* %n newline character (\n)
* %w weekday as decimal (0 = Sunday)
* %U week number of current year, first sunday as first week
* %y year as decimal (range 00 to 99)
* %Y year as decimal including century (range 0000 to 9999)
* @param int $day the day of the month
* @param int $month the month
* @param int $year the year. Use the complete year instead of the
* abbreviated version. E.g. use 2005, not 05.
* @param string $format the format string
* @return string the date in the desired format
for ($strpos = 0; $strpos < strlen($format); $strpos++ ) {
$char = substr($format, $strpos, 1 );
$nextchar = substr($format, $strpos + 1 , 1 );
$output .= sprintf('%02d', $month);
($year < 0 ? '3' : '2') .
($year < 0 ? '5' : '4') .
$output .= $char. $nextchar;
* Returns the current local date
* NOTE: This function retrieves the local date using strftime(),
* which may or may not be 32-bit safe on your system.
* @param string $format the string indicating how to format the output
* @return string the current date in the specified format
function dateNow($format = DATE_CALC_FORMAT )
* Returns the current local year in format CCYY
* @return string the current year in four digit format
* Returns the current local month in format MM
* @return string the current month in two digit format
* Returns the current local day in format DD
* @return string the current day of the month in two digit format
* Turns a two digit year into a four digit year
* Return value depends on current year; the century chosen
* will be the one which forms the year that is closest
* to the current year. If the two possibilities are
* equidistant to the current year (i.e. 50 years in the past
* and 50 years in the future), then the past year is chosen.
* For example, if the current year is 2007:
* 56 - returns 2056 (closer to 2007 than 1956)
* 57 - returns 1957 (1957 and 2007 are equidistant, so previous century
* @param int $year the 2 digit year
* @return int the 4 digit year
$hn_century = intval(($hn_currentyear = date("Y")) / 100 );
$hn_currentyear = $hn_currentyear % 100;
if ($year < 0 || $year >= 100 )
if ($year - $hn_currentyear < -50 )
return ($hn_century + 1 ) * 100 + $year;
else if ($year - $hn_currentyear < 50 )
return $hn_century * 100 + $year;
return ($hn_century - 1 ) * 100 + $year;
// {{{ getSecondsInYear()
* Returns the total number of seconds in the given year
* This takes into account leap seconds.
* @param int $pn_year the year in four digit format
* @since Method available since Release 1.5.0
if (!isset ($ha_leapseconds)) {
$ha_leapseconds = array (1972 => 2 ,
if (isset ($ha_leapseconds[$pn_year])) {
return $ret + $ha_leapseconds[$pn_year];
// {{{ getSecondsInMonth()
* Returns the total number of seconds in the given month
* This takes into account leap seconds.
* @param int $pn_month the month
* @param int $pn_year the year in four digit format
* @since Method available since Release 1.5.0
$pn_month = intval($pn_month);
if (!isset ($ha_leapseconds)) {
$ha_leapseconds = array (1972 => array (6 => 1 ,
if (isset ($ha_leapseconds[$pn_year][$pn_month])) {
return $ret + $ha_leapseconds[$pn_year][$pn_month];
* Returns the total number of seconds in the day of the given date
* This takes into account leap seconds.
* @param int $pn_day the day of the month
* @param int $pn_month the month
* @param int $pn_year the year in four digit format
* @since Method available since Release 1.5.0
// The leap seconds listed here are a matter of historical fact,
// that is, it is known on which exact day they occurred.
// However, the implementation of the class as a whole depends
// on the fact that they always occur at the end of the month
// (although it is assumed that they could occur in any month,
// even though practically they only occur in June or December).
// Do not define a leap second on a day of the month other than
// the last day without altering the implementation of the
// functions that depend on this one.
// It is possible, though, to define an un-leap second (i.e. a skipped
// second (I do not know what they are called), or a number of
// consecutive leap seconds).
$pn_month = intval($pn_month);
if (!isset ($ha_leapseconds)) {
$ha_leapseconds = array (1972 => array (6 => array (30 => 1 ),
1973 => array (12 => array (31 => 1 )),
1974 => array (12 => array (31 => 1 )),
1975 => array (12 => array (31 => 1 )),
1976 => array (12 => array (31 => 1 )),
1977 => array (12 => array (31 => 1 )),
1978 => array (12 => array (31 => 1 )),
1979 => array (12 => array (31 => 1 )),
1981 => array (6 => array (30 => 1 )),
1982 => array (6 => array (30 => 1 )),
1983 => array (6 => array (30 => 1 )),
1985 => array (6 => array (30 => 1 )),
1987 => array (12 => array (31 => 1 )),
1989 => array (12 => array (31 => 1 )),
1990 => array (12 => array (31 => 1 )),
1992 => array (6 => array (30 => 1 )),
1993 => array (6 => array (30 => 1 )),
1994 => array (6 => array (30 => 1 )),
1995 => array (12 => array (31 => 1 )),
1997 => array (6 => array (30 => 1 )),
1998 => array (12 => array (31 => 1 )),
2005 => array (12 => array (31 => 1 )));
if (isset ($ha_leapseconds[$pn_year][$pn_month][$pn_day])) {
return 86400 + $ha_leapseconds[$pn_year][$pn_month][$pn_day];
// {{{ getSecondsInHour()
* Returns the total number of seconds in the hour of the given date
* This takes into account leap seconds.
* @param int $pn_day the day of the month
* @param int $pn_month the month
* @param int $pn_year the year in four digit format
* @param int $pn_hour the hour
// {{{ getSecondsInMinute()
* Returns the total number of seconds in the minute of the given hour
* This takes into account leap seconds.
* @param int $pn_day the day of the month
* @param int $pn_month the month
* @param int $pn_year the year in four digit format
* @param int $pn_hour the hour
* @param int $pn_minute the minute
* @since Method available since Release 1.5.0
if ($pn_hour < 23 || $pn_minute < 59 )
// {{{ secondsPastMidnight()
* Returns the no of seconds since midnight (0-86399)
* @param int $pn_hour the hour of the day
* @param int $pn_minute the minute
* @param mixed $pn_second the second as integer or float
* @return mixed integer or float from 0-86399
* @since Method available since Release 1.5.0
return 3600 * $pn_hour + 60 * $pn_minute + $pn_second;
// {{{ secondsPastMidnightToTime()
* Returns the time as an array (i.e. hour, minute, second)
* @param mixed $pn_seconds the no of seconds since midnight (0-86399)
* @return mixed array of hour, minute (both as integers), second (as
* integer or float, depending on parameter)
* @since Method available since Release 1.5.0
if ($pn_seconds >= 86400 ) {
return array (23 , 59 , $pn_seconds - 86340 );
$hn_hour = intval($pn_seconds / 3600 );
$hn_minute = intval(($pn_seconds - $hn_hour * 3600 ) / 60 );
return array ($hn_hour, $hn_minute, $hn_second);
// {{{ secondsPastTheHour()
* Returns the no of seconds since the last hour o'clock (0-3599)
* @param int $pn_minute the minute
* @param mixed $pn_second the second as integer or float
* @return mixed integer or float from 0-3599
* @since Method available since Release 1.5.0
return 60 * $pn_minute + $pn_second;
* Returns the date the specified no of hours from the given date
* To subtract hours use a negative value for the '$pn_hours' parameter
* @param int $pn_hours hours to add
* @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
* @return array array of year, month, day, hour
* @since Method available since Release 1.5.0
function addHours($pn_hours, $pn_day, $pn_month, $pn_year, $pn_hour)
return array ((int) $pn_year,
$hn_days = intval($pn_hours / 24 );
$hn_hour = $pn_hour + $pn_hours % 24;
} else if ($hn_hour < 0 ) {
list ($hn_year, $hn_month, $hn_day) =
return array ((int) $hn_year, (int) $hn_month, (int) $hn_day, $hn_hour);
* Returns the date the specified no of minutes from the given date
* To subtract minutes use a negative value for the '$pn_minutes' parameter
* @param int $pn_minutes minutes to add
* @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
* @return array array of year, month, day, hour, minute
* @since Method available since Release 1.5.0
return array ((int) $pn_year,
$hn_hours = intval($pn_minutes / 60 );
$hn_minute = $pn_minute + $pn_minutes % 60;
} else if ($hn_minute < 0 ) {
list ($hn_year, $hn_month, $hn_day, $hn_hour) =
return array ($hn_year, $hn_month, $hn_day, $hn_hour, $hn_minute);
* Returns the date the specified no of seconds from the given date
* If leap seconds are specified to be counted, the passed time must be UTC.
* To subtract seconds use a negative value for the '$pn_seconds' parameter.
* N.B. the return type of the second part of the date is float if
* either '$pn_seconds' or '$pn_second' is a float; otherwise, it
* @param mixed $pn_seconds seconds to add as integer or float
* @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
* @since Method available since Release 1.5.0
$pb_countleap = DATE_COUNT_LEAP_SECONDS )
return array ((int) $pn_year,
$hn_seconds = $pn_seconds;
$hn_month = (int) $pn_month;
$hn_year = (int) $pn_year;
$hn_hour = (int) $pn_hour;
$hn_minute = (int) $pn_minute;
$hn_secondsofmonth = 86400 * ($hn_days -
// Advance to end of month:
if ($hn_secondsofmonth != 0 &&
$hn_secondsofmonth + $hn_seconds >=
$hn_seconds -= $hn_secondsinmonth - $hn_secondsofmonth;
list ($hn_year, $hn_month) =
$hn_hour = $hn_minute = $hn_second = 0;
// Advance to end of year:
if ($hn_secondsofmonth == 0 &&
while ($hn_year == $pn_year &&
$hn_seconds >= ($hn_secondsinmonth =
$hn_seconds -= $hn_secondsinmonth;
list ($hn_year, $hn_month) =
if ($hn_secondsofmonth == 0 ) {
while ($hn_seconds >= ($hn_secondsinyear =
$hn_seconds -= $hn_secondsinyear;
while ($hn_seconds >= ($hn_secondsinmonth =
$hn_seconds -= $hn_secondsinmonth;
list ($hn_year, $hn_month) =
// Go back to start of month:
if ($hn_secondsofmonth != 0 &&
- $hn_seconds >= $hn_secondsofmonth) {
$hn_seconds += $hn_secondsofmonth;
$hn_hour = $hn_minute = $hn_second = 0;
// Go back to start of year:
if ($hn_secondsofmonth == 0 ) {
list ($hn_year, $hn_prevmonth) =
if (- $hn_seconds >= ($hn_secondsinmonth =
$hn_seconds += $hn_secondsinmonth;
$hn_month = $hn_prevmonth;
if ($hn_secondsofmonth == 0 ) {
while (- $hn_seconds >= ($hn_secondsinyear =
$hn_seconds += $hn_secondsinyear;
list ($hn_pmyear, $hn_prevmonth) =
while (- $hn_seconds >= ($hn_secondsinmonth =
$hn_seconds += $hn_secondsinmonth;
$hn_month = $hn_prevmonth;
list ($hn_pmyear, $hn_prevmonth) =
if ($hn_seconds < 0 && $hn_secondsofmonth == 0 ) {
list ($hn_year, $hn_month) =
$hn_daysadd = intval($hn_seconds / 86400 ) - 1;
} else if ($hn_seconds < 86400 ) {
$hn_daysadd = intval($hn_seconds / 86400 ) - 1;
list ($hn_year, $hn_month, $hn_day) =
$hn_seconds -= $hn_daysadd * 86400;
if ($hn_seconds >= $hn_secondsinday) {
list ($hn_year, $hn_month, $hn_day) =
$hn_seconds -= $hn_secondsinday;
list ($hn_hour, $hn_minute, $hn_second) =
return array ((int) $hn_year,
// Assume every day has 86400 seconds exactly (ignore leap seconds):
$hn_minutes = intval($pn_seconds / 60 );
$hn_second = $pn_second + fmod($pn_seconds, 60 );
$hn_second = $pn_second + $pn_seconds % 60;
} else if ($hn_second < 0 ) {
list ($hn_year, $hn_month, $hn_day, $hn_hour, $hn_minute) =
* 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
* The algorithm is valid for all years (positive and negative), and
* also for years preceding 4714 B.C.
* @param int $day the day of the month
* @param int $month the month
* @param int $year the year (using 'Astronomical' year numbering)
* @return int the number of days since 24th November, 4714 B.C.
// March = 0, April = 1, ..., December = 9,
// January = 10, February = 11
$hb_negativeyear = $year < 0;
$century = intval($year / 100 );
// 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 ) +
return intval(146097 * $century / 4 ) +
intval((153 * $month + 2 ) / 5 ) +
* Converts no 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'.
* @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 string the date in the desired format
function daysToDate($days, $format = DATE_CALC_FORMAT )
$century = floor((4 * $days - 1 ) / 146097 );
$days = floor(4 * $days - 1 - 146097 * $century);
$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;
* Returns array of the month numbers, in order, for the given year
* @param int $pn_year the year (using 'Astronomical' year numbering)
* @return array array of integer month numbers, in order
* @since Method available since Release 1.5.0
// N.B. Month numbers can be skipped but not duplicated:
return array (1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 );
* Returns an array of month names
* Used to take advantage of the setlocale function to return
* language specific month names.
* TODO: cache values to some global array to avoid performance
* hits when called more than once.
* @param int $pb_abbreviated whether to return the abbreviated form of the
* @return array associative array of integer month numbers, in
$ret[$i] = strftime($pb_abbreviated ? '%b' : '%B',
mktime(0 , 0 , 0 , $i, 1 , 2001 ));
* Returns month and year of previous month
* @param int $pn_month the month
* @param int $pn_year the year (using 'Astronomical' year numbering)
* @return array array of year, month as integers
* @since Method available since Release 1.5.0
return array ((int) $pn_year, $ha_months[$hn_monthkey - 1 ]);
return array ($pn_year - 1 , end($ha_months));
* Returns month and year of next month
* @param int $pn_month the month
* @param int $pn_year the year (using 'Astronomical' year numbering)
* @return array array of year, month as integers
* @since Method available since Release 1.5.0
return array ((int) $pn_year, $ha_months[$hn_monthkey + 1 ]);
return array ($pn_year + 1 , $ha_months[0 ]);
* Returns 'Julian Day' of the date the specified no of months
* To subtract months use a negative value for the '$pn_months'
* @param int $pn_months months to add
* @param int $pn_days 'Julian Day', i.e. the no of days since 1st
* @return int 'Julian Day', i.e. the no of days since 1st January,
* @since Method available since Release 1.5.0
list ($hn_year, $hn_month, $hn_day) =
$hn_retmonth = $hn_month + $pn_months % 12;
$hn_retyear = $hn_year + intval($pn_months / 12 );
} else if ($hn_retmonth > 12 ) {
// Calculate days since first of month:
$hn_dayoffset = $pn_days -
if ($hn_dayoffset > $hn_retmonthlastday - $hn_retmonthfirstday) {
return $hn_retmonthlastday;
return $hn_retmonthfirstday + $hn_dayoffset;
* Returns the date the specified no of months from the given date
* To subtract months use a negative value for the '$pn_months'
* @param int $pn_months months to add
* @param int $pn_day the day of the month, default is current local
* @param int $pn_month the month, default is current local month
* @param int $pn_year the year in four digit format, default is
* @param string $ps_format string specifying how to format the output
* @return string the date in the desired format
* @since Method available since Release 1.5.0
$ps_format = DATE_CALC_FORMAT )
* Returns 'Julian Day' of the date the specified no of years
* To subtract years use a negative value for the '$pn_years'
* @param int $pn_years years to add
* @param int $pn_days 'Julian Day', i.e. the no of days since 1st January,
* @return int 'Julian Day', i.e. the no of days since 1st January,
* @since Method available since Release 1.5.0
list ($hn_year, $hn_month, $hn_day) =
$hn_retyear = $hn_year + $pn_years;
$hn_retmonth = $hn_month;
// Calculate days since first of month:
if ($hn_dayoffset > $hn_retmonthlastday - $hn_retmonthfirstday) {
return $hn_retmonthlastday;
return $hn_retmonthfirstday + $hn_dayoffset;
// Calculate days since first of year:
if ($hn_dayoffset > $hn_retyearlastday - $hn_retyearfirstday) {
return $hn_retyearlastday;
return $hn_retyearfirstday + $hn_dayoffset;
* Returns the date the specified no of years from the given date
* To subtract years use a negative value for the '$pn_years'
* @param int $pn_years years to add
* @param int $pn_day the day of the month, default is current local
* @param int $pn_month the month, default is current local month
* @param int $pn_year the year in four digit format, default is
* @param string $ps_format string specifying how to format the output
* @return string the date in the desired format
* @since Method available since Release 1.5.0
$ps_format = DATE_CALC_FORMAT )
* Returns the date the specified no of days from the given date
* To subtract days use a negative value for the '$pn_days' parameter
* @param int $pn_days days to add
* @param int $pn_day the day of the month, default is current local
* @param int $pn_month the month, default is current local month
* @param int $pn_year the year in four digit format, default is
* @param string $ps_format string specifying how to format the output
* @return string the date in the desired format
* @since Method available since Release 1.5.0
$ps_format = DATE_CALC_FORMAT )
// {{{ getFirstDayOfMonth()
* Returns first day of the specified month of specified year as integer
* @param int $pn_month the month
* @param int $pn_year the year (using 'Astronomical' year numbering)
* @return int number of first day of month
* @since Method available since Release 1.5.0
// {{{ getLastDayOfMonth()
* Returns last day of the specified month of specified year as integer
* @param int $pn_month the month
* @param int $pn_year the year (using 'Astronomical' year numbering)
* @return int number of last day of month
* @since Method available since Release 1.5.0
* Returns the Julian Day of the first day of the month of the specified
* year (i.e. the no of days since 24th November, 4714 B.C.)
* @param int $pn_month the month
* @param int $pn_year the year (using 'Astronomical' year numbering)
* @return integer the number of days since 24th November, 4714 B.C.
* @since Method available since Release 1.5.0
* Returns the Julian Day of the last day of the month of the specified
* year (i.e. the no of days since 24th November, 4714 B.C.)
* @param int $pn_month the month
* @param int $pn_year the year (using 'Astronomical' year numbering)
* @return integer the number of days since 24th November, 4714 B.C.
* @since Method available since Release 1.5.0
// {{{ getFirstMonthOfYear()
* Returns first month of specified year as integer
* @param int $pn_year the year (using 'Astronomical' year numbering)
* @return int number of first month of year
* @since Method available since Release 1.5.0
* Returns the Julian Day of the first day of the year (i.e. the no of
* days since 24th November, 4714 B.C.)
* @param int $pn_year the year (using 'Astronomical' year numbering)
* @return integer the number of days since 24th November, 4714 B.C.
* @since Method available since Release 1.5.0
* Returns the Julian Day of the last day of the year (i.e. the no of
* days since 24th November, 4714 B.C.)
* @param int $pn_year the year (using 'Astronomical' year numbering)
* @return integer the number of days since 24th November, 4714 B.C.
* @since Method available since Release 1.5.0
// {{{ dateToDaysJulian()
* Converts a date in the proleptic Julian calendar to the no of days
* since 1st January, 4713 B.C.
* Returns the no of days since Monday, 1st January, 4713 B.C. in the
* proleptic Julian calendar (which is 1st January, -4712 using
* 'Astronomical' year numbering, and 24th November, 4713 B.C. in the
* proleptic Gregorian 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
* The algorithm is valid for all years (positive and negative), and
* also for years preceding 4713 B.C.
* @param int $day the day of the month
* @param int $month the month
* @param int $year the year (using 'Astronomical' year numbering)
* @return int the number of days since 1st January, 4713 B.C.
* @since Method available since Release 1.5.0
// March = 0, April = 1, ..., December = 9,
// January = 10, February = 11
$hb_negativeyear = $year < 0;
// 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((1461 * $year + 1 ) / 4 ) +
intval((153 * $month + 2 ) / 5 ) +
return intval(1461 * $year / 4 ) +
floor((153 * $month + 2 ) / 5 ) +
// {{{ daysToDateJulian()
* Converts no of days since 1st January, 4713 B.C. (in the proleptic
* Julian calendar, which is year -4712 using 'Astronomical' year
* numbering) to Julian calendar date
* Returned date belongs to the proleptic Julian calendar, using
* 'Astronomical' year numbering.
* @param int $days the number of days since 1st January, 4713 B.C.
* @param string $format the string indicating how to format the output
* @return string the date in the desired format
* @since Method available since Release 1.5.0
$days = floor(4 * $days - 1 );
$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 );
* Returns array defining the 'ISO Week Date' as defined in ISO 8601
* Expects a date in the proleptic Gregorian calendar using 'Astronomical'
* year numbering, that is, with a year 0. Algorithm is valid for all
* years (positive and negative).
* N.B. the ISO week day no for Sunday is defined as 7, whereas this
* class and its related functions defines Sunday as 0.
* @param int $pn_day the day of the month
* @param int $pn_month the month
* @param int $pn_year the year
* @return array array of ISO Year, ISO Week No, ISO Day No as
* @since Method available since Release 1.5.0
function isoWeekDate($pn_day = 0 , $pn_month = 0 , $pn_year = null )
$hn_day = $hn_jd - $hn_jd1 + 1;
// ISO week is the first week of the next ISO year:
$hn_isoweek = floor(($hn_day + $hn_wd1 - 2 ) / 7 ) + 1;
if ($hn_day <= 8 - $hn_wd1) {
// ISO week is the last week of the previous ISO year:
list ($hn_year, $hn_lastmonth, $hn_lastday) =
list ($hn_year, $hn_isoweek, $hn_pisoday) =
$hn_isoweek = floor(($hn_day + $hn_wd1 - 9 ) / 7 ) + 1;
return array ((int) $hn_year, (int) $hn_isoweek, (int) $hn_wd);
* Converts from Gregorian Year-Month-Day to ISO Year-WeekNumber-WeekDay
* Uses ISO 8601 definitions.
* @param int $day the day of the month
* @param int $month the month
* @param int $year the year. Use the complete year instead of the
* abbreviated version. E.g. use 2005, not 05.
* @return string the date in ISO Year-WeekNumber-WeekDay format
list ($yearnumber, $weeknumber, $weekday) =
return sprintf("%04d", $yearnumber) .
* Returns week of the year counting week 1 as the week that contains 4th
* Week 1 is determined to be the week that includes the 4th January, and
* therefore can be defined as the first week of the year that has at least
* 4 days. The previous week is counted as week 52 or 53 of the previous
* year. Note that this definition depends on which day is the first day of
* the week, and that if this is not passed as the '$pn_firstdayofweek'
* parameter, the default is assumed.
* Note also that the last day week of the year is likely to extend into
* the following year, except in the case that the last day of the week
* falls on 31st December.
* Also note that this is very similar to the ISO week returned by
* 'isoWeekDate()', the difference being that the ISO week always has
* 7 days, and if the 4th of January is a Friday, for example,
* ISO week 1 would start on Monday, 31st December in the previous year,
* whereas the week defined by this function would start on 1st January,
* but would be only 6 days long. Of course you can also set the day
* of the week, whereas the ISO week starts on a Monday by definition.
* Returned week is an integer from 1 to 53.
* @param int $pn_day the day of the month, default is current
* @param int $pn_month the month, default is current local month
* @param int $pn_year the year in four digit format, default is
* @param int $pn_firstdayofweek optional integer specifying the first day
* @return array array of year, week no as integers
* @since Method available since Release 1.5.0
$pn_firstdayofweek = DATE_CALC_BEGIN_WEEKDAY )
$hn_week = floor(($hn_day +
(10 + $hn_wd1 - $pn_firstdayofweek) % 7 +
// Week number is the last week of the previous year:
list ($hn_year, $hn_lastmonth, $hn_lastday) =
list ($hn_year, $hn_week) =
return array ((int) $hn_year, (int) $hn_week);
* Returns week of the year counting week 1 as the week that contains 7th
* Week 1 is determined to be the week that includes the 7th January, and
* therefore can be defined as the first full week of the year. The
* previous week is counted as week 52 or 53 of the previous year. Note
* that this definition depends on which day is the first day of the week,
* and that if this is not passed as the '$pn_firstdayofweek' parameter, the
* Note also that the last day week of the year is likely to extend into
* the following year, except in the case that the last day of the week
* falls on 31st December.
* Returned week is an integer from 1 to 53.
* @param int $pn_day the day of the month, default is current
* @param int $pn_month the month, default is current local month
* @param int $pn_year the year in four digit format, default is
* @param int $pn_firstdayofweek optional integer specifying the first day
* @return array array of year, week no as integers
* @since Method available since Release 1.5.0
$pn_firstdayofweek = DATE_CALC_BEGIN_WEEKDAY )
$hn_week = floor(($hn_day + (6 + $hn_wd1 - $pn_firstdayofweek) % 7 ) / 7 );
// Week number is the last week of the previous ISO year:
list ($hn_year, $hn_week) = Date_Calc::weekOfYear7th($hn_lastday, $hn_lastmonth, $hn_year, $pn_firstdayofweek);
return array ((int) $hn_year, (int) $hn_week);
* Determines julian date of the given season
* Adapted from previous work in Java by James Mark Hamilton.
* @param string $season the season to get the date for: VERNALEQUINOX,
* SUMMERSOLSTICE, AUTUMNALEQUINOX,
* @param string $year the year in four digit format. Must be between
* -1000 B.C. and 3000 A.D.
* @return float the julian date the season starts on
if (($year >= -1000 ) && ($year <= 1000 )) {
$juliandate = (((((((-0.00071 * $y) - 0.00111 ) * $y) + 0.06134 ) * $y) + 365242.1374 ) * $y) + 1721139.29189;
$juliandate = (((((((0.00025 * $y) + 0.00907 ) * $y) - 0.05323 ) * $y) + 365241.72562 ) * $y) + 1721233.25401;
$juliandate = (((((((0.00074 * $y) - 0.00297 ) * $y) - 0.11677 ) * $y) + 365242.49558 ) * $y) + 1721325.70455;
$juliandate = (((((((-0.00006 * $y) - 0.00933 ) * $y) - 0.00769 ) * $y) + 365242.88257 ) * $y) + 1721414.39987;
} elseif (($year > 1000 ) && ($year <= 3000 )) {
$y = ($year - 2000 ) / 1000;
$juliandate = (((((((-0.00057 * $y) - 0.00411 ) * $y) + 0.05169 ) * $y) + 365242.37404 ) * $y) + 2451623.80984;
$juliandate = (((((((-0.0003 * $y) + 0.00888 ) * $y) + 0.00325 ) * $y) + 365241.62603 ) * $y) + 2451716.56767;
$juliandate = (((((((0.00078 * $y) + 0.00337 ) * $y) - 0.11575 ) * $y) + 365242.01767 ) * $y) + 2451810.21715;
$juliandate = (((((((0.00032 * $y) - 0.00823 ) * $y) - 0.06223 ) * $y) + 365242.74049 ) * $y) + 2451900.05952;
* Returns number of days since 31 December of year before given date
* @param int $pn_day the day of the month, default is current local day
* @param int $pn_month the month, default is current local month
* @param int $pn_year the year in four digit format, default is current
* @since Method available since Release 1.5.0
function dayOfYear($pn_day = 0 , $pn_month = 0 , $pn_year = null )
return $hn_jd - $hn_jd1 + 1;
* Returns number of days since 31 December of year before given date
* @param int $pn_day the day of the month, default is current local day
* @param int $pn_month the month, default is current local month
* @param int $pn_year the year in four digit format, default is current
* @deprecated Method deprecated in Release 1.5.0
function julianDate($pn_day = 0 , $pn_month = 0 , $pn_year = null )
// {{{ getWeekdayFullname()
* Returns the full weekday name for the given date
* @param int $pn_day the day of the month, default is current local day
* @param int $pn_month the month, default is current local month
* @param int $pn_year the year in four digit format, default is current
* @return string the full name of the day of the week
return $weekday_names[$weekday];
// {{{ getWeekdayAbbrname()
* Returns the abbreviated weekday name for the given date
* @param int $pn_day the day of the month, default is current local day
* @param int $pn_month the month, default is current local month
* @param int $pn_year the year in four digit format, default is current
* @param int $length the length of abbreviation
* @return string the abbreviated name of the day of the week
* @see Date_Calc::getWeekdayFullname()
return $weekday_names[$weekday];
// {{{ getMonthFullname()
* Returns the full month name for the given month
* @param int $month the month
* @return string the full name of the month
return $month_names[$month];
// {{{ getMonthAbbrname()
* Returns the abbreviated month name for the given month
* @param int $month the month
* @param int $length the length of abbreviation
* @return string the abbreviated name of the month
* @see Date_Calc::getMonthFullname
return $month_names[$month];
// {{{ getMonthFromFullname()
* Returns the numeric month from the month name or an abreviation
* Both August and Aug would return 8.
* @param string $month the name of the month to examine.
* @return int the month's number
while (list ($id, $name) = each($months)) {
|