? files
? test
? includes/adodb-time.inc.php
? includes/date.inc
? includes/date.library.inc
? includes/date.test.inc
? profiles/date_lib.php
? sites/default/settings.php
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.660
diff -u -r1.660 common.inc
--- includes/common.inc	24 Jun 2007 10:09:50 -0000	1.660
+++ includes/common.inc	26 Jun 2007 01:57:14 -0000
@@ -1028,119 +1028,6 @@
 }
 
 /**
- * Format a time interval with the requested granularity.
- *
- * @param $timestamp
- *   The length of the interval in seconds.
- * @param $granularity
- *   How many different units to display in the string.
- * @param $langcode
- *   Optional language code to translate to a language other than
- *   what is used to display the page.
- * @return
- *   A translated string representation of the interval.
- */
-function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
-  $units = array('1 year|@count years' => 31536000, '1 week|@count weeks' => 604800, '1 day|@count days' => 86400, '1 hour|@count hours' => 3600, '1 min|@count min' => 60, '1 sec|@count sec' => 1);
-  $output = '';
-  foreach ($units as $key => $value) {
-    $key = explode('|', $key);
-    if ($timestamp >= $value) {
-      $output .= ($output ? ' ' : '') . format_plural(floor($timestamp / $value), $key[0], $key[1], array(), $langcode);
-      $timestamp %= $value;
-      $granularity--;
-    }
-
-    if ($granularity == 0) {
-      break;
-    }
-  }
-  return $output ? $output : t('0 sec', array(), $langcode);
-}
-
-/**
- * Format a date with the given configured format or a custom format string.
- *
- * Drupal allows administrators to select formatting strings for 'small',
- * 'medium' and 'large' date formats. This function can handle these formats,
- * as well as any custom format.
- *
- * @param $timestamp
- *   The exact date to format, as a UNIX timestamp.
- * @param $type
- *   The format to use. Can be "small", "medium" or "large" for the preconfigured
- *   date formats. If "custom" is specified, then $format is required as well.
- * @param $format
- *   A PHP date format string as required by date(). A backslash should be used
- *   before a character to avoid interpreting the character as part of a date
- *   format.
- * @param $timezone
- *   Time zone offset in seconds; if omitted, the user's time zone is used.
- * @param $langcode
- *   Optional language code to translate to a language other than
- *   what is used to display the page.
- * @return
- *   A translated date string in the requested format.
- */
-function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {
-  if (!isset($timezone)) {
-    global $user;
-    if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
-      $timezone = $user->timezone;
-    }
-    else {
-      $timezone = variable_get('date_default_timezone', 0);
-    }
-  }
-
-  $timestamp += $timezone;
-
-  switch ($type) {
-    case 'small':
-      $format = variable_get('date_format_short', 'm/d/Y - H:i');
-      break;
-    case 'large':
-      $format = variable_get('date_format_long', 'l, F j, Y - H:i');
-      break;
-    case 'custom':
-      // No change to format
-      break;
-    case 'medium':
-    default:
-      $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
-  }
-
-  $max = strlen($format);
-  $date = '';
-  for ($i = 0; $i < $max; $i++) {
-    $c = $format[$i];
-    if (strpos('AaDFlM', $c) !== FALSE) {
-      $date .= t(gmdate($c, $timestamp), array(), $langcode);
-    }
-    else if (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== FALSE) {
-      $date .= gmdate($c, $timestamp);
-    }
-    else if ($c == 'r') {
-      $date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone);
-    }
-    else if ($c == 'O') {
-      $date .= sprintf('%s%02d%02d', ($timezone < 0 ? '-' : '+'), abs($timezone / 3600), abs($timezone % 3600) / 60);
-    }
-    else if ($c == 'Z') {
-      $date .= $timezone;
-    }
-    else if ($c == '\\') {
-      $date .= $format[++$i];
-    }
-    else {
-      $date .= $c;
-    }
-  }
-
-  return $date;
-}
-
-/**
  * @} End of "defgroup format".
  */
 
@@ -2124,7 +2011,7 @@
 
 /**
  * Return data in JSON format.
- * 
+ *
  * This function should be used for JavaScript callback functions returning
  * data in JSON format. It sets the header for JavaScript output.
  *
@@ -2245,6 +2132,7 @@
     return;
   }
   $called = 1;
+  require_once './includes/date.inc';
   require_once './includes/theme.inc';
   require_once './includes/pager.inc';
   require_once './includes/menu.inc';
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.496
diff -u -r1.496 system.module
--- modules/system/system.module	24 Jun 2007 10:09:53 -0000	1.496
+++ modules/system/system.module	25 Jun 2007 11:06:09 -0000
@@ -878,7 +878,7 @@
     '#type' => 'select',
     '#title' => t('First day of week'),
     '#default_value' => variable_get('date_first_day', 0),
-    '#options' => array(0 => t('Sunday'), 1 => t('Monday'), 2 => t('Tuesday'), 3 => t('Wednesday'), 4 => t('Thursday'), 5 => t('Friday'), 6 => t('Saturday')),
+    '#options' => date_week_days(),
     '#description' => t('The first day of the week for calendar views.')
   );
 
--- includes/date.inc
+++ includes/date.inc
@@ -0,0 +1,539 @@
+<?php
+
+if (!defined('DATE_MAX_YEAR')) define('DATE_MAX_YEAR', 3000);
+if (!defined('DATE_MIN_YEAR')) define('DATE_MIN_YEAR', 100);
+
+/**
+ * Format a date with the given configured format or a custom format string.
+ * Reworked from original drupal format_date() function to apply to entire
+ * pre-1970 to post-2038 date range.
+ *
+ * Drupal allows administrators to select formatting strings for 'small',
+ * 'medium' and 'large' date formats. This function can handle these formats,
+ * as well as any custom format.
+ *
+ * @param $timestamp
+ *   The exact date to format, as a UNIX timestamp.
+ * @param $type
+ *   The format to use. Can be "small", "medium" or "large" for the preconfigured
+ *   date formats. If "custom" is specified, then $format is required as well.
+ * @param $format
+ *   A PHP date format string as required by date(). A backslash should be used
+ *   before a character to avoid interpreting the character as part of a date
+ *   format.
+ * @param $timezone
+ *   Time zone offset in seconds; if omitted, the user's time zone is used.
+ * @param $langcode
+ *   Optional language code to translate to a language other than
+ *   what is used to display the page.
+ * @return
+ *   A translated date string in the requested format.
+ */
+function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {
+  if (!isset($timezone)) {
+    global $user;
+    if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
+      $timezone = $user->timezone;
+    }
+    else {
+      $timezone = variable_get('date_default_timezone', 0);
+    }
+  }
+  $timestamp += $timezone;
+
+  switch ($type) {
+    case 'small':
+      $format = variable_get('date_format_short', 'm/d/Y - H:i');
+      break;
+    case 'large':
+      $format = variable_get('date_format_long', 'l, F j, Y - H:i');
+      break;
+    case 'custom':
+      // No change to format
+      break;
+    case 'medium':
+    default:
+      $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
+  }
+
+  $max = strlen($format);
+  $date = '';
+  for ($i = 0; $i < $max; $i++) {
+    $c = $format[$i];
+    if (strpos('AaDFlM', $c) !== FALSE) {
+      $date .= t(date_gmdate($c, $timestamp));
+    }
+    else if (strpos('BdgGhHiIjLmnsStTUwWYyz', $c) !== FALSE) {
+      $date .= date_gmdate($c, $timestamp);
+    }
+    else if ($c == 'r') {
+      $date .= format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone, $langcode);
+    }
+    else if ($c == 'O') {
+      $date .= sprintf('%s%02d%02d', ($timezone < 0 ? '-' : '+'), abs($timezone / 3600), abs($timezone % 3600) / 60);
+    }
+    else if ($c == 'Z') {
+      $date .= $timezone;
+    }
+    else if ($c == '\\') {
+      $date .= $format[++$i];
+    }
+    else {
+      $date .= $c;
+    }
+  }
+
+  return $date;
+}
+
+/**
+ * Format a time interval with the requested granularity.
+ *
+ * @param $timestamp
+ *   The length of the interval in seconds.
+ * @param $granularity
+ *   How many different units to display in the string.
+ * @param $langcode
+ *   Optional language code to translate to a language other than
+ *   what is used to display the page.
+ * @return
+ *   A translated string representation of the interval.
+ */
+function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
+  $units = array('1 year|@count years' => 31536000, '1 week|@count weeks' => 604800, '1 day|@count days' => 86400, '1 hour|@count hours' => 3600, '1 min|@count min' => 60, '1 sec|@count sec' => 1);
+  $output = '';
+  foreach ($units as $key => $value) {
+    $key = explode('|', $key);
+    if ($timestamp >= $value) {
+      $output .= ($output ? ' ' : '') . format_plural(floor($timestamp / $value), $key[0], $key[1], array(), $langcode);
+      $timestamp %= $value;
+      $granularity--;
+    }
+
+    if ($granularity == 0) {
+      break;
+    }
+  }
+  return $output ? $output : t('0 sec', array(), $langcode);
+}
+
+/**
+ * A translated array of month names
+ *
+ * @return
+ *   an array of month names
+ */
+function date_month_names() {
+  static $month_names;
+  if (empty($month_names)) {
+    $month_names = array(1 => t('January'), 2 => t('February'),
+      3 => t('March'), 4 => t('April'), 5 => t('May'), 6 => t('June'),
+      7 => t('July'), 8 => t('August'), 9 => t('September'),
+      10 => t('October'), 11 => t('November'), 12 => t('December'));
+  }
+  return $month_names;
+}
+
+/**
+ * A translated array of month name abbreviations
+ *
+ * @return
+ *   an array of month abbreviations
+ */
+function date_month_names_abbr() {
+  static $month_names;
+  if (empty($month_names)) {
+    $month_names = array(1 => t('Jan'), 2 => t('Feb'),
+      3 => t('Mar'), 4 => t('Apr'), 5 => t('May'), 6 => t('Jun'),
+      7 => t('Jul'), 8 => t('Aug'), 9 => t('Sep'),
+      10 => t('Oct'), 11 => t('Nov'), 12 => t('Dec'));
+  }
+  return $month_names;
+}
+
+/**
+ * A translated array of week days
+ *
+ * @return
+ *   an array of week day names
+ */
+function date_week_days() {
+  static $weekdays;
+  if (empty($weekdays)) {
+    $weekdays = array(0 => t('Sunday'), 1 => t('Monday'),
+      2 => t('Tuesday'), 3 => t('Wednesday'), 4 => t('Thursday'),
+      5 => t('Friday'), 6 => t('Saturday'));
+  }
+  return $weekdays;
+}
+
+/**
+ * An translated array of week day abbreviations
+ *
+ * @return
+ *   an array of week day abbreviations
+ */
+function date_week_days_abbr() {
+  static $weekdays;
+  if (empty($weekdays)) {
+    $weekdays = array(0 => t('Sun'), 1 => t('Mon'),
+      2 => t('Tue'), 3 => t('Wed'), 4 => t('Thu'),
+      5 => t('Fri'), 6 => t('Sat'));
+  }
+  return $weekdays;
+}
+
+/**
+ * An array of hours.
+ *
+ * @param string $format
+ * @return
+ *   an array of hours in the selected format.
+ */
+function date_hours($format = 'H') {
+  $hours = array();
+  if ($format == 'h' || $format == 'g') {
+    $min = 1;
+    $max = 12;
+  }
+  else  {
+    $min = 0;
+    $max = 23;
+  }
+  for ($i = $min; $i <= $max; $i++) {
+    $hours[$i] = $i < 10 && $format == 'H' || $format == 'h' ? "0$i" : $i;
+  }
+  return $hours;
+}
+
+/**
+ * An array of minutes.
+ *
+ * @param string $format
+ * @return
+ *   an array of minutes in the selected format.
+ */
+function date_minutes($format = 'i', $increment = 1) {
+  $minutes = array();
+  for ($i = 0; $i < 60; $i += $increment) {
+    $minutes[$i] = $i < 10 && $format == 'i' ? "0$i" : $i;
+  }
+  return $minutes;
+}
+
+/**
+ * An array of seconds.
+ *
+ * @param string $format
+ * @return array an array of seconds in the selected format.
+ */
+function date_seconds($format = 's', $increment = 1) {
+  $seconds = array();
+  for ($i = 0; $i < 60; $i += $increment) {
+    $seconds[$i] = $i < 10 && $format == 's' ? "0$i" : $i;
+  }
+  return $seconds;
+}
+
+/**
+ * @ingroup adodb
+ * @{
+ */
+/**
+ * Functions to handle dates from the year 100 to the year 3000. Uses native
+ * php date functions when possible, alterate methods when native functions
+ * won't work, parses alternate code only when needed.
+
+ * date_get_gmt_diff(), date_date(), date_gmdate(), date_mktime(),
+ * and date_gmmktime() were derived from code obtained from
+ * http://phplens.com/phpeverywhere/adodb_date.libraryrary, licensed as follows:
+ *
+ * COPYRIGHT(c) 2003-2005 John Lim
+ * All rights reserved.
+ *
+ * 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.
+*/
+
+if (!defined('DATE_ALLOW_NEGATIVE_TS')) define('DATE_NO_NEGATIVE_TS', 1);
+
+/**
+ * Get local time zone offset from GMT.
+ *
+ * @return timezone offset in seconds
+ */
+function date_get_gmt_diff() {
+static $TZ;
+	if (isset($TZ)) return $TZ;
+	$TZ = mktime(0, 0, 0, 1, 2, 1970, 0) - gmmktime(0, 0, 0, 1, 2, 1970, 0);
+	return $TZ;
+}
+
+/**
+ * Returns an array with date info.
+ *
+ * @param $timestamp
+ *   A unix timestamp.
+ * @param $fast
+ *   Whether to get a fast version that omits the dow, applies only
+ *   to pre-1970 dates that use the low level function.
+ * @param $is_gmt
+ *   Whether the timestamp is a GMT date that should not have
+ *   a timezone conversion applied.
+ * @param $test
+ *   Whether the date functions are being tested, used to force the
+ *   low level function even for current dates.
+ */
+function date_getdate($timestamp = false, $fast = false, $is_gmt = false, $test = false) {
+	if ($timestamp === false) return getdate();
+	if (!$test) {
+		// check if number in 32-bit signed range
+		if ((abs($timestamp) <= 0x7FFFFFFF)) {
+			// if windows, must be +ve integer
+			if (!defined('DATE_NO_NEGATIVE_TS') || $timestamp >= 0)
+				return @getdate($timestamp);
+		}
+	}
+	include_once './includes/date.library.inc';
+	return _date_getdate($timestamp, $fast, $is_gmt);
+}
+
+/**
+ * Like date_getdate with no GMT conversion.
+ */
+function date_gmgetdate($timestamp = false, $fast = false, $test = false) {
+  if (!$timestamp) return '';
+  $array    = date_getdate($timestamp, $fast, TRUE, $test);
+  $array[0] = $timestamp;
+  return $array;
+}
+
+/**
+ * Return formatted date based on timestamp $timestamp.
+ *
+ * @param $format
+ *   the format to be used for the returned timestamp
+ * @param $timestamp
+ *   a unix timestamp
+ * @param $is_gmt
+ *   whether the timestamp is a GMT date that should not have
+ *   a timezone conversion applied.
+ * @param $test
+ *   whether the date functions are being tested, used to force the
+ *   low level function even for current dates.
+*/
+function date_date($format, $timestamp = false, $is_gmt = false, $test = false) {
+  if ($timestamp === false) return ($is_gmt)? @gmdate($format): @date($format);
+	if (!$test) {
+		// check if number in 32-bit signed range
+		if ((abs($timestamp) <= 0x7FFFFFFF)) {
+			// if windows, must be +ve integer
+			if (!defined('DATE_NO_NEGATIVE_TS') || $timestamp >= 0)
+				return ($is_gmt)? @gmdate($format, $timestamp): @date($format, $timestamp);
+		}
+	}
+	include_once './includes/date.library.inc';
+	return _date_date($format, $timestamp, $is_gmt);
+}
+
+/**
+ * Like date_date with no GMT conversion.
+ */
+function date_gmdate($format, $timestamp = false, $test = false) {
+  if (!$timestamp) return '';
+  return date_date($format, $timestamp, true, $test);
+}
+
+/**
+ *	Return a timestamp given a local time. Originally by jackbbs.
+ *
+ * @param $hr
+ *   the hour
+ * @param $min
+ *   the minute
+ * @param $sec
+ *   the second
+ * @param $mon
+ *   the month
+ * @param $day
+ *   the day
+ * @param $year
+ *   the year
+ * @param $is_gmt
+ *   whether the timestamp is a GMT date that should not have
+ *   a timezone conversion applied.
+ * @param $test
+ *   whether the date functions are being tested, used to force the
+ *   low level function even for current dates.
+*/
+function date_mktime($hr, $min, $sec, $mon = false, $day = false,
+  $year = false, $is_gmt = false, $test = false) {
+	if (!$test) {
+		if ($mon === false) {
+			return $is_gmt? @gmmktime($hr, $min, $sec): @mktime($hr, $min, $sec);
+		}
+		// for windows, we don't check 1970 because with timezone differences,
+		// 1 Jan 1970 could generate negative timestamp, which is illegal
+		if (1971 < $year && $year < 2038
+			|| !defined('DATE_NO_NEGATIVE_TS') && (1901 < $year && $year < 2038)) {
+				return $is_gmt ?
+					@gmmktime($hr, $min, $sec, $mon, $day, $year, 0):
+					@mktime($hr, $min, $sec, $mon, $day, $year, 0);
+			}
+	}
+	include_once './includes/date.library.inc';
+	return _date_mktime($hr, $min, $sec, $mon, $day, $year, $is_gmt);
+}
+
+/**
+ * Like date_mktime with no GMT conversion.
+*/
+function date_gmmktime($hr, $min, $sec, $mon = false, $day = false,
+  $year = false, $test = false) {
+	return date_mktime($hr, $min, $sec, $mon, $day, $year, true, $test);
+}
+/**
+ * @} End of ingroup "adodb".
+ */
+/**
+ * @ingroup date_calc
+ * @{
+ */
+/**
+ * date_dow(), date_is_valid(), and date_is_leap_year()
+ * were derived from code obtained from
+ * http://pear.php.net/package/Date/Calc.php, licensed as follows:
+ *
+ * Copyright (c) 1999-2006 Monte Ohrt, Pierre-Alain Joye, Daniel Convissor
+ * All rights reserved.
+ *
+ * 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.
+*/
+
+/**
+ * Returns day of week for given date (0 = Sunday), works on pre-1970 and
+ * post 2038 dates.
+ *
+ * @param int  $day
+ *   the day of the month, default is current local day
+ * @param int  $month
+ *   the month, default is current local month
+ * @param int  $year
+ *   the year in four digit format, default is current local year
+ *
+ * @return
+ *    the number of the day in the week
+ */
+function date_dow($day = 0, $month = 0, $year = 0) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  // Gregorian correction from ADODB
+  if ($year <= 1582) {
+		if ($year < 1582 ||
+			($year == 1582 && ($month < 10 || ($month == 10 && $day < 15)))) $greg_correction = 3;
+		 else
+			$greg_correction = 0;
+	} else
+		$greg_correction = 0;
+	if($month > 2)
+	    $month -= 2;
+	else {
+	    $month += 10;
+	    $year--;
+	}
+  $day = (floor((13 * $month - 1) / 5) +
+    $day + ($year % 100) +
+    floor(($year % 100) / 4) +
+    floor(($year / 100) / 4) - 2 *
+    floor($year / 100) + 77 + $greg_correction);
+  $weekday_number = $day - 7 * floor($day / 7);
+  return $weekday_number;
+}
+
+/**
+ * Returns true for valid date, false for invalid date
+ *
+ * @param int  $day
+ *   the day of the month
+ * @param int  $month
+ *   the month
+ * @param int  $year
+ *   the year, using 2005, not 05. Do not add leading 0's for years prior to 1000.
+ *
+ * @return boolean
+ */
+function date_is_valid($day, $month, $year) {
+  if ($year < DATE_MIN_YEAR || $year > DATE_MAX_YEAR) {
+    return false;
+  }
+  if (!checkdate($month, $day, $year)) {
+    return false;
+  }
+  return true;
+}
+
+/**
+ * Returns true for a leap year, else false
+ *
+ * @param int  $year
+ *   the year.  Use the complete year instead of the abbreviated version.
+ *   E.g. use 2005, not 05. Do not add leading 0's for years prior to 1000.
+ *
+ * @return boolean
+ */
+function date_is_leap_year($year = 0) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (preg_match('/\D/', $year)) {
+    return false;
+  }
+  if ($year < 1000) {
+    return false;
+  }
+  if ($year < 1582) {
+    // pre Gregorio XIII - 1582
+    return ($year % 4 == 0);
+  }
+  else {
+    // post Gregorio XIII - 1582
+    return (($year % 4 == 0) && ($year % 100 != 0)) || ($year % 400 == 0);
+  }
+}
+/**
+ * @} End of ingroup "date_calc".
+ */

--- includes/date.library.inc
+++ includes/date.library.inc
@@ -0,0 +1,2035 @@
+<?php
+// $Id$
+
+/**
+ * Date Library, extended date functions.
+ * File is included only when needed.
+ */
+/**
+ * @ingroup adodb
+ * @{
+ */
+/**
+ * The following functions are low level functions that implements pre 1970
+ * to post 2038 versions of native php date functions. Will handle dates from
+ * the year 100 to the year 3000. Uses native php date functions when possible,
+ * alterate methods when native functions won't work.
+ *
+ * Altered the original ADODB code to split it between the high level
+ * functions which are used when pre-1970 and post-2038 dates are not needed
+ * and this large file which is only parsed for dates that are out of range
+ * for native php date handling.
+ *
+ * Replace native php functions:
+ *   getdate() with date_getdate()
+ *   date() with date_date()
+ *   gmdate() with date_gmdate()
+ *   mktime() with date_mktime()
+ *   gmmktime() with gmdate_mktime()
+ *
+ * The following functions were derived from code obtained from
+ * http://phplens.com/phpeverywhere/adodb_date_library, licensed as follows:
+ *
+ * COPYRIGHT(c) 2003-2005 John Lim
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+/**
+ * Fix 2-digit years. Works for any century. Assumes that if 2-digit is
+ * more than 30 years in future, then previous century.
+*/
+function date_year_digit_check($y) {
+  if ($y < 100) {
+    $yr = (integer) date("Y");
+    $century = (integer) ($yr /100);
+    if ($yr%100 > 50) {
+      $c1 = $century + 1;
+      $c0 = $century;
+    } else {
+      $c1 = $century;
+      $c0 = $century - 1;
+    }
+    $c1 *= 100;
+    // if 2-digit year is less than 30 years in future, set it to this century
+    // otherwise if more than 30 years in future, then we set 2-digit year to the prev century.
+    if (($y + $c1) < $yr + 30) $y = $y + $c1;
+    else $y = $y + $c0 * 100;
+  }
+  return $y;
+}
+
+/**
+ * Low-level function that returns the getdate() array for pre-1970
+ * and post-2038 dates.
+ *
+ * We have a special$fast flag, which if set to true, will return fewer
+ * array values, and is much faster as it does not calculate dow, etc.
+ *
+ * @param $timestamp a unix timestamp
+ * @param $fast whether to get a fast version that omits the dow, applies only
+ *   to pre-1970 dates that use the low level function
+ * @param $is_gmt whether the timestamp is a GMT date that should not have
+ *   a timezone conversion applied.
+ * @param $test whether the date functions are being test, used to force the
+ *   low level function even for current dates.
+*/
+function _date_getdate($origd = false, $fast = false, $is_gmt = false) {
+  static $YRS;
+  $timestamp =  $origd - ($is_gmt ? 0 : date_get_gmt_diff());
+  $_day_power = 86400;
+  $_hour_power = 3600;
+  $_min_power = 60;
+  if ($timestamp < -12219321600) $timestamp -= 86400*10; // if 15 Oct 1582 or earlier, gregorian correction
+  $_month_table_normal = array("", 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
+  $_month_table_leap = array("", 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
+  $d366 = $_day_power * 366;
+  $d365 = $_day_power * 365;
+  if ($timestamp < 0) {
+    if (empty($YRS)) $YRS = array(
+      1970 => 0,
+      1960 => -315619200,
+      1950 => -631152000,
+      1940 => -946771200,
+      1930 => -1262304000,
+      1920 => -1577923200,
+      1910 => -1893456000,
+      1900 => -2208988800,
+      1890 => -2524521600,
+      1880 => -2840140800,
+      1870 => -3155673600,
+      1860 => -3471292800,
+      1850 => -3786825600,
+      1840 => -4102444800,
+      1830 => -4417977600,
+      1820 => -4733596800,
+      1810 => -5049129600,
+      1800 => -5364662400,
+      1790 => -5680195200,
+      1780 => -5995814400,
+      1770 => -6311347200,
+      1760 => -6626966400,
+      1750 => -6942499200,
+      1740 => -7258118400,
+      1730 => -7573651200,
+      1720 => -7889270400,
+      1710 => -8204803200,
+      1700 => -8520336000,
+      1690 => -8835868800,
+      1680 => -9151488000,
+      1670 => -9467020800,
+      1660 => -9782640000,
+      1650 => -10098172800,
+      1640 => -10413792000,
+      1630 => -10729324800,
+      1620 => -11044944000,
+      1610 => -11360476800,
+      1600 => -11676096000);
+    if ($is_gmt) $origd = $timestamp;
+    // The valid range of a 32bit signed timestamp is typically from
+    // Fri, 13 Dec 1901 20:45:54 GMT to Tue, 19 Jan 2038 03:14:07 GMT
+    //
+    $lastsecs = 0;
+    $lastyear = 1970;
+    foreach($YRS as $year => $secs) {
+      if ($timestamp >= $secs) {
+        $a = $lastyear;
+        break;
+      }
+      $lastsecs = $secs;
+      $lastyear = $year;
+    }
+    $timestamp -= $lastsecs;
+    if (!isset($a)) $a = $lastyear;
+    for (; --$a >= 0;) {
+      $lastd = $timestamp;
+      if ($leap = date_is_leap_year($a)) $timestamp += $d366;
+      else $timestamp += $d365;
+
+      if ($timestamp >= 0) {
+        $year = $a;
+        break;
+      }
+    }
+    $secsInYear = 86400 * ($leap ? 366 : 365) + $lastd;
+    $timestamp = $lastd;
+    $mtab = ($leap) ? $_month_table_leap : $_month_table_normal;
+    for ($a = 13 ; --$a > 0;) {
+      $lastd = $timestamp;
+      $timestamp += $mtab[$a] * $_day_power;
+      if ($timestamp >= 0) {
+        $month = $a;
+        $ndays = $mtab[$a];
+        break;
+      }
+    }
+    $timestamp = $lastd;
+    $day = $ndays + ceil(($timestamp+1) / ($_day_power));
+    $timestamp += ($ndays - $day+1)* $_day_power;
+    $hour = floor($timestamp/$_hour_power);
+  }
+  else {
+    for ($a = 1970 ;; $a++) {
+      $lastd = $timestamp;
+      if ($leap = date_is_leap_year($a)) $timestamp -= $d366;
+      else $timestamp -= $d365;
+      if ($timestamp < 0) {
+        $year = $a;
+        break;
+      }
+    }
+    $secsInYear = $lastd;
+    $timestamp = $lastd;
+    $mtab = ($leap) ? $_month_table_leap : $_month_table_normal;
+    for ($a = 1 ; $a <= 12; $a++) {
+      $lastd = $timestamp;
+      $timestamp -= $mtab[$a] * $_day_power;
+      if ($timestamp < 0) {
+        $month = $a;
+        $ndays = $mtab[$a];
+        break;
+      }
+    }
+    $timestamp = $lastd;
+    $day = ceil(($timestamp + 1) / $_day_power);
+    $timestamp = $timestamp - ($day - 1) * $_day_power;
+    $hour = floor($timestamp / $_hour_power);
+  }
+  $timestamp -= $hour * $_hour_power;
+  $min = floor($timestamp / $_min_power);
+  $secs = $timestamp - $min * $_min_power;
+  if ($fast) {
+    return array(
+    'seconds' => $secs,
+    'minutes' => $min,
+    'hours' => $hour,
+    'mday' => $day,
+    'mon' => $month,
+    'year' => $year,
+    'yday' => floor($secsInYear / $_day_power),
+    'leap' => $leap,
+    'ndays' => $ndays
+    );
+  }
+  return array(
+    'seconds' => $secs,
+    'minutes' => $min,
+    'hours' => $hour,
+    'mday' => $day,
+    'wday' => date_dow($day, $month, $year),
+    'mon' => $month,
+    'year' => $year,
+    'yday' => floor($secsInYear / $_day_power),
+    'weekday' => gmdate('l', $_day_power * (3 + $dow)),
+    'month' => gmdate('F', mktime(0, 0, 0, $month, 2, 1971)),
+    0 => $origd
+  );
+}
+
+/**
+ * Accepts unix timestamp and iso date format in $timestamp.
+ */
+function date_date2($format,  $timestamp = false, $is_gmt = false) {
+  if ($timestamp !== false) {
+    if (!preg_match(
+      "|^([0-9]{4})[-/\.]?([0-9]{1,2})[-/\.]?([0-9]{1,2})[ -]?(([0-9]{1,2}):?([0-9]{1,2}):?([0-9\.]{1,4}))?|",
+      ($timestamp), $rr)) return date_date($format, false, $is_gmt);
+    if ($rr[1] <= 100 && $rr[2]<= 1) return date_date($format, false, $is_gmt);
+    // h-m-s-MM-DD-YY
+    if (!isset($rr[5])) $timestamp = _date_mktime(0, 0, 0, $rr[2], $rr[3], $rr[1], $is_gmt);
+    else $timestamp = @_date_mktime($rr[5], $rr[6], $rr[7], $rr[2], $rr[3], $rr[1], $is_gmt);
+  }
+  return date_date($format, $timestamp, $is_gmt);
+}
+
+/**
+ * Low level function to create date() for pre-1970 and post-2038 dates.
+ *
+ * @param $format a format string for the result
+ * @param $timestamp a unix timestamp
+ * @param $fast whether to get a fast version that omits the dow, applies only
+ *   to pre-1970 dates that use the low level function
+ * @param $is_gmt whether the timestamp is a GMT date that should not have
+ *   a timezone conversion applied.
+ * @param $test whether the date functions are being test, used to force the
+ *   low level function even for current dates.
+ */
+function _date_date($format, $timestamp = false, $is_gmt = false, $test = false) {
+  $_day_power = 86400;
+  $arr = _date_getdate($timestamp, true, $is_gmt);
+  $year = $arr['year'];
+  $month = $arr['mon'];
+  $day = $arr['mday'];
+  $hour = $arr['hours'];
+  $min = $arr['minutes'];
+  $secs = $arr['seconds'];
+  $max = strlen($format);
+  $dates = '';
+  /*
+    at this point, we have the following integer vars to manipulate:
+    $year, $month, $day, $hour, $min, $secs
+  */
+  for ($i = 0; $i < $max; $i++) {
+    switch($format[$i]) {
+    case 'T': $dates .= date('T');break;
+    // YEAR
+    case 'L': $dates .= $arr['leap'] ? '1' : '0'; break;
+    case 'r': // Thu, 21 Dec 2000 16:01:07 +0200
+      // 4.3.11 uses '04 Jun 2004'
+      // 4.3.8 uses  ' 4 Jun 2004'
+      $dates .= gmdate('D', $_day_power*(3 + date_dow($day, $month, $year))).', '
+        . ($day < 10 ? '0'. $day : $day) . ' '.date('M', mktime(0, 0, 0, $month, 2, 1971)).' '.$year.' ';
+      if ($hour < 10) $dates .= '0'.$hour; else $dates .= $hour;
+      if ($min < 10) $dates .= ':0'. $min; else $dates .= ':'.$min;
+      if ($secs < 10) $dates .= ':0'. $secs; else $dates .= ':'.$secs;
+      $gmt = date_get_gmt_diff();
+      $dates .= sprintf(' %s%04d', ($gmt < 0) ? '+' : '-', abs($gmt) / 36); break;
+    case 'Y': $dates .= $year; break;
+    case 'y': $dates .= substr($year, strlen($year)-2, 2); break;
+    // MONTH
+    case 'm': if ($month<10) $dates .= '0'.$month; else $dates .= $month; break;
+    case 'Q': $dates .= ($month + 3)>>2; break;
+    case 'n': $dates .= $month; break;
+    case 'M': $dates .= date('M', mktime(0, 0, 0, $month, 2, 1971)); break;
+    case 'F': $dates .= date('F', mktime(0, 0, 0, $month, 2, 1971)); break;
+    // DAY
+    case 't': $dates .= $arr['ndays']; break;
+    case 'z': $dates .= $arr['yday']; break;
+    case 'w': $dates .= date_dow($day, $month, $year); break;
+    case 'l': $dates .= gmdate('l', $_day_power*(3 + date_dow($day, $month, $year))); break;
+    case 'D': $dates .= gmdate('D', $_day_power*(3 + date_dow($day, $month, $year))); break;
+    case 'j': $dates .= $day; break;
+    case 'd': if ($day<10) $dates .= '0'.$day; else $dates .= $day; break;
+    case 'S':
+      $d10 = $day % 10;
+      if ($d10 == 1) $dates .= 'st';
+      else if ($d10 == 2 && $day != 12) $dates .= 'nd';
+      else if ($d10 == 3) $dates .= 'rd';
+      else $dates .= 'th';
+      break;
+    // HOUR
+    case 'Z':
+      $dates .= ($is_gmt) ? 0 : -date_get_gmt_diff(); break;
+    case 'O':
+      $gmt = ($is_gmt) ? 0 : date_get_gmt_diff();
+      $dates .= sprintf('%s%04d', ($gmt<0)?'+':'-', abs($gmt)/36); break;
+    case 'H':
+      if ($hour < 10) $dates .= '0'.$hour;
+      else $dates .= $hour;
+      break;
+    case 'h':
+      if ($hour > 12) $hh = $hour - 12;
+      else {
+        if ($hour == 0) $hh = '12';
+        else $hh = $hour;
+      }
+      if ($hh < 10) $dates .= '0'.$hh;
+      else $dates .= $hh;
+      break;
+    case 'G':
+      $dates .= $hour;
+      break;
+    case 'g':
+      if ($hour > 12) $hh = $hour - 12;
+      else {
+        if ($hour == 0) $hh = '12';
+        else $hh = $hour;
+      }
+      $dates .= $hh;
+      break;
+    // MINUTES
+    case 'i': if ($min < 10) $dates .= '0'.$min; else $dates .= $min; break;
+    // SECONDS
+    case 'U': $dates .= $timestamp; break;
+    case 's': if ($secs < 10) $dates .= '0'.$secs; else $dates .= $secs; break;
+    // AM/PM
+    // Note 00:00 to 11:59 is AM, while 12:00 to 23:59 is PM
+    case 'a':
+      if ($hour>=12) $dates .= 'pm';
+      else $dates .= 'am';
+      break;
+    case 'A':
+      if ($hour>=12) $dates .= 'PM';
+      else $dates .= 'AM';
+      break;
+    default:
+      $dates .= $format[$i]; break;
+    // ESCAPE
+    case "\\":
+      $i++;
+      if ($i < $max) $dates .= $format[$i];
+      break;
+    }
+  }
+  return $dates;
+}
+
+/**
+ * Low level function to create mktime() for pre-1970 and post 2038 dates.
+ *
+ * @param $hr the hour
+ * @param $min the minute
+ * @param $sec the second
+ * @param $mon the month
+ * @param $day the day
+ * @param $year the year
+ * @param $test whether the date functions are being test, used to force the
+ *   low level function even for current dates.
+ */
+function _date_mktime($hr, $min, $sec, $mon = false, $day = false, $year = false, $is_gmt = false, $test = false) {
+  $gmt_different = ($is_gmt) ? 0 : date_get_gmt_diff();
+  /*
+  # disabled because some people place large values in $sec.
+  # however we need it for $mon because we use an array...
+  $hr = intval($hr);
+  $min = intval($min);
+  $sec = intval($sec);
+  */
+  $mon = intval($mon);
+  $day = intval($day);
+  $year = intval($year);
+  $year = date_year_digit_check($year);
+  if ($mon > 12) {
+    $y = floor($mon / 12);
+    $year += $y;
+    $mon -= $y*12;
+  } else if ($mon < 1) {
+    $y = ceil((1-$mon) / 12);
+    $year -= $y;
+    $mon += $y*12;
+  }
+  $_day_power = 86400;
+  $_hour_power = 3600;
+  $_min_power = 60;
+  $_month_table_normal = array("", 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
+  $_month_table_leap = array("", 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
+  $_total_date = 0;
+  if ($year >= 1970) {
+    for ($a = 1970 ; $a <= $year; $a++) {
+      $leap = date_is_leap_year($a);
+      if ($leap == true) {
+        $loop_table = $_month_table_leap;
+        $_add_date = 366;
+      } else {
+        $loop_table = $_month_table_normal;
+        $_add_date = 365;
+      }
+      if ($a < $year) {
+        $_total_date += $_add_date;
+      } else {
+        for($b=1;$b<$mon;$b++) {
+          $_total_date += $loop_table[$b];
+        }
+      }
+    }
+    $_total_date +=$day-1;
+    $ret = $_total_date * $_day_power + $hr * $_hour_power + $min * $_min_power + $sec + $gmt_different;
+  } else {
+    for ($a = 1969 ; $a >= $year; $a--) {
+      $leap = date_is_leap_year($a);
+      if ($leap == true) {
+        $loop_table = $_month_table_leap;
+        $_add_date = 366;
+      } else {
+        $loop_table = $_month_table_normal;
+        $_add_date = 365;
+      }
+      if ($a > $year) { $_total_date += $_add_date;
+      } else {
+        for($b = 12;$b>$mon;$b--) {
+          $_total_date += $loop_table[$b];
+        }
+      }
+    }
+    $_total_date += $loop_table[$mon] - $day;
+    $_day_time = $hr * $_hour_power + $min * $_min_power + $sec;
+    $_day_time = $_day_power - $_day_time;
+    $ret = -( $_total_date * $_day_power + $_day_time - $gmt_different);
+    if ($ret < -12220185600) $ret += 10*86400; // if earlier than 5 Oct 1582 - gregorian correction
+    else if ($ret < -12219321600) $ret = -12219321600; // if in limbo, reset to 15 Oct 1582.
+  }
+  return $ret;
+}
+/**
+ * @} End of ingroup "adodb".
+ */
+/**
+ * @ingroup date_calc
+ * @{
+ */
+/**
+ * Calculates and manipulates dates with no reliance on 32-bit system
+ * timestamps, to work on dates before 1970 and after 2038.
+ *
+ * The functions below were derived from code obtained from
+ * http://pear.php.net/package/Date/Calc.php, licensed as follows:
+ *
+ * Copyright (c) 1999-2006 Monte Ohrt, Pierre-Alain Joye, Daniel Convissor
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+/**
+ * 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
+ */
+if (!defined('DATE_CALC_FORMAT')) {
+  define('DATE_CALC_FORMAT', '%Y%m%d');
+}
+
+/**
+ * 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.
+ *
+ * Formatting options:
+ * <pre>
+ * %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
+ *    date_calc_days_to_date() 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)
+ * %t   tab character (\t)
+ * %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)
+ * %%   literal '%'
+ * </pre>
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The format string.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_format($day, $month, $year, $format) {
+  if (!date_is_valid($day, $month, $year)) {
+    $year  = date_calc_date_now('%Y');
+    $month = date_calc_date_now('%m');
+    $day   = date_calc_date_now('%d');
+  }
+  $output = '';
+  for ($strpos = 0; $strpos < strlen($format); $strpos++) {
+    $char = substr($format, $strpos, 1);
+    if ($char == '%') {
+      $nextchar = substr($format, $strpos + 1, 1);
+      switch($nextchar) {
+        case 'a':
+          $output .= date_calc_get_weekday_abbrname($day, $month, $year);
+          break;
+        case 'A':
+          $output .= date_calc_get_weekday_fullname($day, $month, $year);
+          break;
+        case 'b':
+          $output .= date_calc_get_month_abbrname($month);
+          break;
+        case 'B':
+          $output .= date_calc_get_month_fullname($month);
+          break;
+        case 'd':
+          $output .= sprintf('%02d', $day);
+          break;
+        case 'e':
+          $output .= $day;
+          break;
+        case 'E':
+          $output .= date_calc_date_to_days($day, $month, $year);
+          break;
+        case 'j':
+          $output .= date_calc_julian_date($day, $month, $year);
+          break;
+        case 'm':
+          $output .= sprintf('%02d', $month);
+          break;
+        case 'n':
+          $output .= "\n";
+          break;
+        case 't':
+          $output .= "\t";
+          break;
+        case 'w':
+          $output .= date_dow($day, $month, $year);
+          break;
+        case 'U':
+          $output .= date_calc_week_of_year($day, $month, $year);
+          break;
+        case 'y':
+          $output .= substr($year, 2, 2);
+          break;
+        case 'Y':
+          $output .= $year;
+          break;
+        case '%':
+          $output .= '%';
+          break;
+        default:
+          $output .= $char.$nextchar;
+      }
+      $strpos++;
+    }
+    else {
+      $output .= $char;
+    }
+  }
+  return $output;
+}
+
+/**
+ * Converts a date to number of days since a distant unspecified epoch
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return integer
+ *   The number of days since the date_calc epoch.
+ */
+function date_calc_date_to_days($day, $month, $year) {
+  $century = (int)substr($year, 0, 2);
+  $year = (int)substr($year, 2, 2);
+  if ($month > 2) {
+    $month -= 3;
+  }
+  else {
+    $month += 9;
+    if ($year) {
+      $year--;
+    }
+    else {
+      $year = 99;
+      $century --;
+    }
+  }
+  return (floor((146097 * $century) / 4 ) +
+    floor((1461 * $year) / 4 ) +
+    floor((153 * $month + 2) / 5 ) +
+    $day + 1721119);
+}
+
+/**
+ * Converts number of days to a distant unspecified epoch
+ *
+ * @param int  $days
+ *   The number of days since the date_calc epoch.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_days_to_date($days, $format = DATE_CALC_FORMAT) {
+  $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);
+
+  if ($month < 10) {
+    $month +=3;
+  }
+  else {
+    $month -=9;
+    if ($year++ == 99) {
+      $year = 0;
+      $century++;
+    }
+  }
+  $century = sprintf('%02d', $century);
+  $year  = sprintf('%02d', $year);
+  return date_calc_format($day, $month, $century . $year, $format);
+}
+
+/**
+ * Converts from Gregorian Year-Month-Day to ISO Year-WeekNumber-WeekDay
+ *
+ * Uses ISO 8601 definitions.  Algorithm by Rick McCarty, 1999 at
+ * http://personal.ecu.edu/mccartyr/ISOwdALG.txt .
+ * Transcribed to PHP by Jesus M. Castagnetto.
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return string
+ *   The date in ISO Year-WeekNumber-WeekDay format.
+ */
+function date_calc_gregorian_to_ISO($day, $month, $year) {
+  $mnth = array (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
+  $y_isleap = date_is_leap_year($year);
+  $y_1_isleap = date_is_leap_year($year - 1);
+  $day_of_year_number = $day + $mnth[$month - 1];
+  if ($y_isleap && $month > 2) {
+    $day_of_year_number++;
+  }
+  // find Jan 1 weekday (monday = 1, sunday = 7)
+  $yy = ($year - 1) % 100;
+  $c = ($year - 1) - $yy;
+  $g = $yy + intval($yy / 4);
+  $jan1_weekday = 1 + intval((((($c / 100) % 4) * 5) + $g) % 7);
+  // weekday for year-month-day
+  $h = $day_of_year_number + ($jan1_weekday - 1);
+  $weekday = 1 + intval(($h - 1) % 7);
+  // find if Y M D falls in YearNumber Y-1, WeekNumber 52 or
+  if ($day_of_year_number <= (8 - $jan1_weekday) && $jan1_weekday > 4){
+    $yearnumber = $year - 1;
+    if ($jan1_weekday == 5 || ($jan1_weekday == 6 && $y_1_isleap)) {
+      $weeknumber = 53;
+    }
+    else {
+      $weeknumber = 52;
+    }
+  }
+  else {
+    $yearnumber = $year;
+  }
+  // find if Y M D falls in YearNumber Y+1, WeekNumber 1
+  if ($yearnumber == $year) {
+    if ($y_isleap) {
+      $i = 366;
+    }
+    else {
+      $i = 365;
+    }
+    if (($i - $day_of_year_number) < (4 - $weekday)) {
+      $yearnumber++;
+      $weeknumber = 1;
+    }
+  }
+  // find if Y M D falls in YearNumber Y, WeekNumber 1 through 53
+  if ($yearnumber == $year) {
+    $j = $day_of_year_number + (7 - $weekday) + ($jan1_weekday - 1);
+    $weeknumber = intval($j / 7);
+    if ($jan1_weekday > 4) {
+      $weeknumber--;
+    }
+  }
+  // put it all together
+  if ($weeknumber < 10) {
+    $weeknumber = '0'.$weeknumber;
+  }
+  return $yearnumber . '-' . $weeknumber . '-' . $weekday;
+}
+
+/**
+ * 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, or WINTERSOLSTICE.
+ * @param string $year
+ *   The year in four digit format.  Must be between -1000BC and 3000AD.
+ *
+ * @return float
+ *   The julian date the season starts on.
+ *
+ * @author James Mark Hamilton <mhamilton@qwest.net>
+ * @author Robert Butler <rob@maxwellcreek.org>
+ */
+function date_calc_date_season($season, $year = 0) {
+  if ($year == '') {
+    $year = date_calc_get_year();
+  }
+  if (($year >= -1000) && ($year <= 1000)) {
+    $y = $year / 1000.0;
+    switch ($season) {
+      case 'VERNALEQUINOX':
+        $date_calc_julian_date = (((((((-0.00071 * $y) - 0.00111) * $y) + 0.06134) * $y) + 365242.1374) * $y) + 1721139.29189;
+        break;
+      case 'SUMMERSOLSTICE':
+        $date_calc_julian_date = (((((((0.00025 * $y) + 0.00907) * $y) - 0.05323) * $y) + 365241.72562) * $y) + 1721233.25401;
+        break;
+      case 'AUTUMNALEQUINOX':
+        $date_calc_julian_date = (((((((0.00074 * $y) - 0.00297) * $y) - 0.11677) * $y) + 365242.49558) * $y) + 1721325.70455;
+        break;
+      case 'WINTERSOLSTICE':
+      default:
+        $date_calc_julian_date = (((((((-0.00006 * $y) - 0.00933) * $y) - 0.00769) * $y) + 365242.88257) * $y) + 1721414.39987;
+    }
+  }
+  elseif (($year > 1000) && ($year <= 3000)) {
+    $y = ($year - 2000) / 1000;
+    switch ($season) {
+      case 'VERNALEQUINOX':
+        $date_calc_julian_date = (((((((-0.00057 * $y) - 0.00411) * $y) + 0.05169) * $y) + 365242.37404) * $y) + 2451623.80984;
+        break;
+      case 'SUMMERSOLSTICE':
+        $date_calc_julian_date = (((((((-0.0003 * $y) + 0.00888) * $y) + 0.00325) * $y) + 365241.62603) * $y) + 2451716.56767;
+        break;
+      case 'AUTUMNALEQUINOX':
+        $date_calc_julian_date = (((((((0.00078 * $y) + 0.00337) * $y) - 0.11575) * $y) + 365242.01767) * $y) + 2451810.21715;
+        break;
+      case 'WINTERSOLSTICE':
+      default:
+        $date_calc_julian_date = (((((((0.00032 * $y) - 0.00823) * $y) - 0.06223) * $y) + 365242.74049) * $y) + 2451900.05952;
+    }
+  }
+  return $date_calc_julian_date;
+}
+
+/**
+ * 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 date_calc_date_now($format = DATE_CALC_FORMAT) {
+  return strftime($format, time());
+}
+
+/**
+ * Returns the current local year in format CCYY.
+ * Altered from original PEAR function to return either the current year
+ * or the year of a specific date.
+ *
+ * @param  int $days
+ *   An integer calculated by date_calc_date_to_days().
+ *
+ * @return string
+ *   The current year in four digit format.
+ */
+function date_calc_get_year($days = NULL) {
+  if (!empty($days)) {
+    return date_calc_days_to_date($days, '%Y');
+  }
+  else {
+    return date_calc_date_now('%Y');
+  }
+}
+
+/**
+ * Returns the current local month in format MM
+ * Altered from original PEAR function to return either the current month
+ * or the month of a specific date.
+ *
+ * @param  int $days
+ *   An integer calculated by date_calc_date_to_days().
+ *
+ * @return string
+ *   The current month in two digit format.
+ */
+function date_calc_get_month($days = NULL) {
+  if (!empty($days)) {
+    return date_calc_days_to_date($days, '%m');
+  }
+  else {
+    return date_calc_date_now('%m');
+  }
+}
+
+/**
+ * Returns the current local day in format DD
+ * Altered from original PEAR function to return either the current day
+ * or the day of a specific date.
+ *
+ * @param  int $days
+ *   An integer calculated by date_calc_date_to_days().
+ *
+ * @return string
+ *   The current day of the month in two digit format.
+ */
+function date_calc_get_day($days = NULL) {
+  if (!empty($days)) {
+    return date_calc_days_to_date($days, '%d');
+  }
+  else {
+    return date_calc_date_now('%d');
+  }
+}
+
+/**
+ * Returns number of days since 31 December of year before given date.
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return int
+ *   The julian date for the date.
+ */
+function date_calc_julian_date($day = 0, $month = 0, $year = 0) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $days = array(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
+  $julian = ($days[$month - 1] + $day);
+  if ($month > 2 && date_is_leap_year($year)) {
+    $julian++;
+  }
+  return $julian;
+}
+
+/**
+ * Returns the full weekday name for the given date
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return string
+ *   The full name of the day of the week/
+ */
+function date_calc_get_weekday_fullname($day = 0, $month = 0, $year = 0) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $weekday_names = date_week_days();
+  $weekday = date_dow($day, $month, $year);
+  return $weekday_names[$weekday];
+}
+
+/**
+ * Returns the abbreviated weekday name for the given date.
+ *
+ * @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. Do not add leading 0's for years prior to 1000.
+ * @param int  $length
+ *   The length of abbreviation.
+ *
+ * @return string
+ *   The abbreviated name of the day of the week.
+ */
+function date_calc_get_weekday_abbrname($day = 0, $month = 0, $year = 0) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $weekday_names = date_week_days_abbr();
+  $weekday = date_dow($day, $month, $year);
+  return $weekday_names[$weekday];
+}
+
+/**
+ * Returns the full month name for the given month.
+ *
+ * @param int  $month
+ *   The month.
+ *
+ * @return string
+ *   The full name of the month.
+ */
+function date_calc_get_month_fullname($month) {
+  $month = (int)$month;
+  if (empty($month)) {
+    $month = (int)date_calc_get_month();
+  }
+  $month_names = date_month_names();
+  return $month_names[$month];
+}
+
+/**
+ * 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.
+ */
+function date_calc_get_month_abbrname($month, $length = 3) {
+  $month = (int)$month;
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  return substr(date_calc_get_month_fullname($month), 0, $length);
+}
+
+/**
+ * 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. Case insensitive.
+ *
+ * @return integer
+ *   The month's number.
+ */
+function date_calc_get_month_from_fullname($month) {
+  $month = strtolower($month);
+  $months = date_month_names();
+  while(list($id, $name) = each($months)) {
+    if (ereg($month, strtolower($name))) {
+      return $id;
+    }
+  }
+  return 0;
+}
+
+/**
+ * Returns week of the year, first Sunday is first day of first week.
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return int
+ *   The number of the week in the year.
+ */
+function date_calc_week_of_year($day = 0, $month = 0, $year = 0) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $iso  = date_calc_gregorian_to_ISO($day, $month, $year);
+  $parts  = explode('-', $iso);
+  $week_number = intval($parts[1]);
+  return $week_number;
+}
+
+/**
+ * Returns quarter of the year for given date.
+ *
+  * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *
+ * @return int
+ *   The number of the quarter in the year.
+ */
+function date_calc_quarter_of_year($day = 0, $month = 0, $year = 0) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $year_quarter = intval(($month - 1) / 3 + 1);
+  return $year_quarter;
+}
+
+/**
+ * Find the number of days in the given month.
+ *
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return int
+ *   The number of days the month has.
+ */
+function date_calc_days_in_month($month = 0, $year = 0) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if ($year == 1582 && $month == 10) {
+    return 21;  // October 1582 only had 1st-4th and 15th-31st
+  }
+  if ($month == 2) {
+    if (date_is_leap_year($year)) {
+      return 29;
+     }
+     else {
+      return 28;
+    }
+  }
+  elseif ($month == 4 or $month == 6 or $month == 9 or $month == 11) {
+    return 30;
+  }
+  else {
+    return 31;
+  }
+}
+
+/**
+ * Returns the number of rows on a calendar month. Useful for determining
+ * the number of rows when displaying a typical month calendar.
+ *
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return int
+ *   The number of weeks the month has.
+ */
+function date_calc_weeks_in_month($month = 0, $year = 0) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  $FDOM = date_calc_first_of_month_weekday($month, $year);
+  if (variable_get('date_first_day', 1)==1 && $FDOM==0) {
+    $first_week_days = 7 - $FDOM + variable_get('date_first_day', 1);
+    $weeks = 1;
+  }
+  elseif (variable_get('date_first_day', 1)==0 && $FDOM == 6) {
+    $first_week_days = 7 - $FDOM + variable_get('date_first_day', 1);
+    $weeks = 1;
+  }
+  else {
+    $first_week_days = variable_get('date_first_day', 1) - $FDOM;
+    $weeks = 0;
+  }
+  $first_week_days %= 7;
+  return ceil((date_calc_days_in_month($month, $year)
+    - $first_week_days) / 7) + $weeks;
+}
+
+/**
+ * Return an array with days in week.
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return array
+ *   $week[$weekday].
+ */
+function date_calc_get_calendar_week($day = 0, $month = 0, $year = 0, $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $week_array = array();
+  // date for the column of week
+  $curr_day = date_calc_begin_of_week($day, $month, $year,'%E');
+  for ($counter = 0; $counter <= 6; $counter++) {
+    $week_array[$counter] = date_calc_days_to_date($curr_day, $format);
+    $curr_day++;
+  }
+  return $week_array;
+}
+
+/**
+ * Return a set of arrays to construct a calendar month for the given date.
+ *
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return array
+ *   $month[$row][$col].
+ */
+function date_calc_get_calendar_month($month = 0, $year = 0, $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  $month_array = array();
+  // date for the first row, first column of calendar month
+  if (variable_get('date_first_day', 1) == 1) {
+    if (date_calc_first_of_month_weekday($month, $year) == 0) {
+      $curr_day = date_calc_date_to_days('01', $month, $year) - 6;
+    }
+    else {
+      $curr_day = date_calc_date_to_days('01', $month, $year)
+        - date_calc_first_of_month_weekday($month, $year) + 1;
+    }
+  }
+  else {
+    $curr_day = (date_calc_date_to_days('01', $month, $year)
+      - date_calc_first_of_month_weekday($month, $year));
+  }
+  // number of days in this month
+  $date_calc_days_in_month = date_calc_days_in_month($month, $year);
+  $date_calc_weeks_in_month = date_calc_weeks_in_month($month, $year);
+  for ($row_counter = 0; $row_counter < $date_calc_weeks_in_month; $row_counter++) {
+    for ($column_counter = 0; $column_counter <= 6; $column_counter++) {
+      $month_array[$row_counter][$column_counter] =
+        date_calc_days_to_date($curr_day , $format);
+      $curr_day++;
+    }
+  }
+  return $month_array;
+}
+
+/**
+ * Return a set of arrays to construct a calendar year for the given date
+ *
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return array $year[$month][$row][$col]
+ */
+function date_calc_get_calendar_year($year = 0, $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  $year_array = array();
+  for ($curr_month = 0; $curr_month <= 11; $curr_month++) {
+    $year_array[$curr_month] =
+      date_calc_get_calendar_month($curr_month + 1,
+        $year, $format);
+  }
+  return $year_array;
+}
+
+/**
+ * Returns date of day before given date.
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_prev_day($day = 0, $month = 0, $year = 0, $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $days = date_calc_date_to_days($day, $month, $year);
+  return date_calc_days_to_date($days - 1, $format);
+}
+
+/**
+ * Returns date of day after given date
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_next_day($day = 0, $month = 0, $year = 0, $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $days = date_calc_date_to_days($day, $month, $year);
+  return date_calc_days_to_date($days + 1, $format);
+}
+
+/**
+ * Returns date of the previous weekday, skipping from Monday to Friday
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_prev_weekday($day = 0, $month = 0, $year = 0, $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $days = date_calc_date_to_days($day, $month, $year);
+  if (date_dow($day, $month, $year) == 1) {
+    $days -= 3;
+  } elseif (date_dow($day, $month, $year) == 0) {
+    $days -= 2;
+  } else {
+    $days -= 1;
+  }
+  return date_calc_days_to_date($days, $format);
+}
+
+/**
+ * Returns date of the next weekday of given date, skipping from
+ * Friday to Monday.
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_next_weekday($day = 0, $month = 0, $year = 0, $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $days = date_calc_date_to_days($day, $month, $year);
+  if (date_dow($day, $month, $year) == 5) {
+    $days += 3;
+  }
+  elseif (date_dow($day, $month, $year) == 6) {
+    $days += 2;
+  }
+  else {
+    $days += 1;
+  }
+  return date_calc_days_to_date($days, $format);
+}
+
+/**
+ * Returns date of the previous specific day of the week
+ * from the given date.
+ *
+ * @param int
+ *   Day of week, 0=Sunday.
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param bool   $on_or_before
+ *   If true and days are same, returns current day.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_prev_day_of_week($dow, $day = 0, $month = 0, $year = 0,
+  $format = DATE_CALC_FORMAT, $on_or_before = false) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $days = date_calc_date_to_days($day, $month, $year);
+  $curr_weekday = date_dow($day, $month, $year);
+  if ($curr_weekday == $dow) {
+    if (!$on_or_before) {
+      $days -= 7;
+    }
+  } elseif ($curr_weekday < $dow) {
+    $days -= 7 - ($dow - $curr_weekday);
+  } else {
+    $days -= $curr_weekday - $dow;
+  }
+  return date_calc_days_to_date($days, $format);
+}
+
+/**
+ * Returns date of the next specific day of the week
+ * from the given date.
+ *
+ * @param int  $dow
+ *   The day of the week (0 = Sunday).
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param bool   $on_or_after
+ *   If true and days are same, returns current day.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string  the date in the desired format
+ */
+function date_calc_next_day_of_week($dow, $day = 0, $month = 0, $year = 0,
+  $format = DATE_CALC_FORMAT, $on_or_after = false) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $days = date_calc_date_to_days($day, $month, $year);
+  $curr_weekday = date_dow($day, $month, $year);
+  if ($curr_weekday == $dow) {
+    if (!$on_or_after) {
+      $days += 7;
+    }
+  }
+  elseif ($curr_weekday > $dow) {
+    $days += 7 - ($curr_weekday - $dow);
+  }
+  else {
+    $days += $dow - $curr_weekday;
+  }
+  return date_calc_days_to_date($days, $format);
+}
+
+/**
+ * Returns date of the previous specific day of the week
+ * on or before the given date.
+ *
+ * @param int  $dow
+ *   The day of the week (0 = Sunday).
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_prev_day_of_week_on_or_before($dow, $day = 0, $month = 0, $year = 0,
+  $format = DATE_CALC_FORMAT) {
+  return date_calc_prev_day_of_week($dow, $day, $month, $year, $format, true);
+}
+
+/**
+ * Returns date of the next specific day of the week
+ * on or after the given date.
+ *
+ * @param int  $dow
+ *   The day of the week (0 = Sunday).
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_prev_day_of_week_on_or_after($dow, $day = 0, $month = 0, $year = 0,
+  $format = DATE_CALC_FORMAT) {
+  return date_calc_next_day_of_week($dow, $day, $month, $year, $format, true);
+}
+
+/**
+ * Find the month day of the beginning of week for given date,
+ * using variable_get('date_first_day', 1). Can return weekday of prev month.
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_begin_of_week($day = 0, $month = 0, $year = 0,
+  $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $this_weekday = date_dow($day, $month, $year);
+  $interval = (7 - variable_get('date_first_day', 1) + $this_weekday) % 7;
+  return date_calc_days_to_date(date_calc_date_to_days($day, $month, $year)
+    - $interval, $format);
+}
+
+/**
+ * Find the month day of the end of week for given date, using
+ * variable_get('date_first_day', 1). Can return weekday of following month.
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_end_of_week($day = 0, $month = 0, $year = 0,
+  $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $this_weekday = date_dow($day, $month, $year);
+  $interval = (6 + variable_get('date_first_day', 1) - $this_weekday) % 7;
+  return date_calc_days_to_date(date_calc_date_to_days($day, $month, $year)
+    + $interval, $format);
+}
+
+/**
+ * Find the month day of the beginning of week before given date, using
+ * variable_get('date_first_day', 1). Can return weekday of prev month.
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_begin_of_prev_week($day = 0, $month = 0, $year = 0,
+  $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $date = date_calc_days_to_date(date_calc_date_to_days($day-7, $month, $year),
+    '%Y%m%d');
+  $prev_week_year  = substr($date, 0, 4);
+  $prev_week_month = substr($date, 4, 2);
+  $prev_week_day   = substr($date, 6, 2);
+  return date_calc_begin_of_week($prev_week_day, $prev_week_month,
+    $prev_week_year, $format);
+}
+
+/**
+ * Find the month day of the beginning of week after given date, using
+ * variable_get('date_first_day', 1). Can return weekday of next month.
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string  the date in the desired format
+ */
+function date_calc_begin_of_next_week($day = 0, $month = 0, $year = 0,
+  $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if (empty($day)) {
+    $day = date_calc_get_day();
+  }
+  $date = date_calc_days_to_date(date_calc_date_to_days($day + 7, $month, $year), '%Y%m%d');
+  $next_week_year  = substr($date, 0, 4);
+  $next_week_month = substr($date, 4, 2);
+  $next_week_day   = substr($date, 6, 2);
+  return date_calc_begin_of_week($next_week_day, $next_week_month, $next_week_year, $format);
+}
+
+/**
+ * Returns date of the first day of the month in the number of months
+ * from the given date
+ *
+ * @param int  $months
+ *   The number of months from the date provided.
+ *   Positive numbers go into the future.
+ *   Negative numbers go into the past.
+ *   0 is the month presented in $month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_begin_of_month_by_span($months = 0, $month = 0, $year = 0, $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if ($months > 0) {
+    // future month
+    $tmp_mo = $month + $months;
+    $month  = $tmp_mo % 12;
+    if ($month == 0) {
+      $month = 12;
+      $year = $year + floor(($tmp_mo - 1) / 12);
+    }
+    else {
+      $year = $year + floor($tmp_mo / 12);
+    }
+  }
+  else {
+    // past or present month
+    $tmp_mo = $month + $months;
+    if ($tmp_mo > 0) {
+      // same year
+      $month = $tmp_mo;
+    }
+    elseif ($tmp_mo == 0) {
+      // prior dec
+      $month = 12;
+      $year--;
+    }
+    else {
+      // some time in a prior year
+      $month = 12 + ($tmp_mo % 12);
+      $year  = $year + floor($tmp_mo / 12);
+    }
+  }
+  return date_calc_format(1, $month, $year, $format);
+}
+
+/**
+ * Returns date of the last day of the month in the number of months
+ * from the given date.
+ *
+ * @param int  $months
+ *   The number of months from the date provided.
+ *   Positive numbers go into the future.
+ *   Negative numbers go into the past.
+ *   0 is the month presented in $month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_end_of_month_by_span($months = 0, $month = 0, $year = 0, $format = DATE_CALC_FORMAT) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  if ($months > 0) {
+    // future month
+    $tmp_mo = $month + $months;
+    $month  = $tmp_mo % 12;
+    if ($month == 0) {
+      $month = 12;
+      $year = $year + floor(($tmp_mo - 1) / 12);
+    }
+    else {
+      $year = $year + floor($tmp_mo / 12);
+    }
+  }
+  else {
+    // past or present month
+    $tmp_mo = $month + $months;
+    if ($tmp_mo > 0) {
+      // same year
+      $month = $tmp_mo;
+    }
+    elseif ($tmp_mo == 0) {
+      // prior dec
+      $month = 12;
+      $year--;
+    }
+    else {
+      // some time in a prior year
+      $month = 12 + ($tmp_mo % 12);
+      $year  = $year + floor($tmp_mo / 12);
+    }
+  }
+  return date_calc_format(date_calc_days_in_month($month, $year),
+    $month, $year, $format);
+}
+
+/**
+ * Find the day of the week for the first of the month of given date
+ *
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return int
+ *   Number of weekday for the first day, 0=Sunday.
+ */
+function date_calc_first_of_month_weekday($month = 0, $year = 0) {
+  if (empty($year)) {
+    $year = date_calc_get_year();
+  }
+  if (empty($month)) {
+    $month = date_calc_get_month();
+  }
+  return date_dow('01', $month, $year);
+}
+
+/**
+ * Calculates the date of the Nth weekday of the month,
+ * such as the second Saturday of January 2000
+ *
+ * @param int  $week
+ *   The number of the week to get (1 = first, etc.  Also can be 'last'.)
+ * @param int  $dow
+ *   The day of the week (0 = Sunday).
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param string $format
+ *   The string indicating how to format the output.
+ *
+ * @return string
+ *   The date in the desired format.
+ */
+function date_calc_n_weekday_of_month($week, $dow, $month, $year, $format = DATE_CALC_FORMAT) {
+  if (is_numeric($week)) {
+    $DOW1day = ($week - 1) * 7 + 1;
+    $DOW1  = date_dow($DOW1day, $month, $year);
+    $wdate   = ($week - 1) * 7 + 1 + (7 + $dow - $DOW1) % 7;
+    if ($wdate > date_calc_days_in_month($month, $year)) {
+      return -1;
+    }
+    else {
+      return date_calc_format($wdate, $month, $year, $format);
+    }
+  }
+  elseif ($week == 'last' && $dow < 7) {
+    $lastday = date_calc_days_in_month($month, $year);
+    $lastdow = date_dow($lastday, $month, $year);
+    $diff  = $dow - $lastdow;
+    if ($diff > 0) {
+      return date_calc_format($lastday - (7 - $diff), $month, $year, $format);
+    }
+    else {
+      return date_calc_format($lastday + $diff, $month, $year, $format);
+    }
+  }
+  else {
+    return -1;
+  }
+}
+
+/**
+ * Determines if given date is a future date from now
+ *
+ * @param int  $day
+ *   The day of the month.
+ * @param int  $month
+ *   The month.
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return boolean
+ */
+function date_calc_is_future_date($day, $month, $year) {
+  $this_year  = date_calc_get_year();
+  $this_month = date_calc_get_month();
+  $this_day   = date_calc_get_day();
+  if ($year > $this_year) {
+    return true;
+  }
+  elseif ($year == $this_year) {
+    if ($month > $this_month) {
+      return true;
+    }
+    elseif ($month == $this_month) {
+      if ($day > $this_day) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+/**
+ * Determines if given date is a past date from now
+ *
+ * @param int  $day
+ *   the day of the month
+ * @param int  $month
+ *   the month
+ * @param int  $year
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return boolean
+ */
+function date_calc_is_past_date($day, $month, $year) {
+  $this_year  = date_calc_get_year();
+  $this_month = date_calc_get_month();
+  $this_day   = date_calc_get_day();
+  if ($year < $this_year) {
+    return true;
+  }
+  elseif ($year == $this_year) {
+    if ($month < $this_month) {
+      return true;
+    }
+    elseif ($month == $this_month) {
+      if ($day < $this_day) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+/**
+ * Returns number of days between two given dates
+ *
+ * @param int  $day1
+ *   the day of the month
+ * @param int  $month1
+ *   the month
+ * @param int  $year1
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param int  $day2
+ *   the day of the month
+ * @param int  $month2
+ *   the month
+ * @param int  $year2
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ *
+ * @return int
+ *   the absolute number of days between the two dates.
+ *   If an error occurs, -1 is returned.
+ */
+function date_calc_date_diff($day1, $month1, $year1, $day2, $month2, $year2) {
+  if (!date_is_valid($day1, $month1, $year1)) {
+    return -1;
+  }
+  if (!date_is_valid($day2, $month2, $year2)) {
+    return -1;
+  }
+  return abs(date_calc_date_to_days($day1, $month1, $year1)
+    - date_calc_date_to_days($day2, $month2, $year2));
+}
+
+/**
+ * Compares two dates
+ *
+ * @param int  $day1
+ *   the day of the month
+ * @param int  $month1
+ *   the month
+ * @param int  $year1
+ *   The 4 digit year. Do not add leading 0's for years prior to 1000.
+ * @param int  $day2
+ *   the day of the month
+ * @param int  $month2
+ *   the month
+ * @param int  $year2
+ *   the year.  Use the complete year instead of the abbreviated version.
+ *   E.g. use 2005, not 05. Do not add leading 0's for years prior to 1000.
+ *
+ * @return int
+ *   0 if the dates are equal. 1 if date 1 is later, -1 if date 1 is earlier.
+ */
+function date_calc_compare_dates($day1, $month1, $year1, $day2, $month2, $year2) {
+  $ndays1 = date_calc_date_to_days($day1, $month1, $year1);
+  $ndays2 = date_calc_date_to_days($day2, $month2, $year2);
+  if ($ndays1 == $ndays2) {
+    return 0;
+  }
+  return ($ndays1 > $ndays2) ? 1 : -1;
+}
+/**
+ * @} End of ingroup "date_calc".
+ */
+/**
+ * Wrapper fuctions to accept an array of values as input for mktime().
+ * All missing values set to zero.
+ */
+function date_mktime_array($array = array()) {
+  if (!date_is_valid(intval($array['mon']), intval($array['mday']), intval($array['year']))) {
+    return FALSE;
+  }
+  $timestamp = @_date_mktime(intval($array['hours']), intval($array['minutes']),
+    intval($array['seconds']), intval($array['mon']), intval($array['mday']), intval($array['year']));
+  return $timestamp;
+}
+
+/**
+ * Wrapper fuctions to accept an array of values as input for gmmktime().
+ * All missing values set to zero.
+ */
+function date_gmmktime_array($array = array()) {
+  if (!date_is_valid(intval($array['mon']), intval($array['mday']), intval($array['year']))) {
+    return FALSE;
+  }
+  $timestamp = @date_gmmktime(intval($array['hours']), intval($array['minutes']),
+    intval($array['seconds']), intval($array['mon']), intval($array['mday']), intval($array['year']));
+  return $timestamp;
+}

