'January', 2 => 'February', 3 => 'March', 4 => 'April', 5 => 'May', 6 => 'June', 7 => 'July', 8 => 'August', 9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December'); } return $month_names; } /** * A translated array of month names * * @param $required * If not required, will include a blank value at the beginning of the list. * @return * an array of month names */ function date_month_names($required = FALSE) { static $month_names; if (empty($month_names)) { $month_names = array(); foreach (date_month_names_untranslated() as $key => $month) { $month_names[$key] = t($month); } } $none = array('' => ''); return !$required ? $none + $month_names : $month_names; } /** * A translated array of month name abbreviations * * @param $required * If not required, will include a blank value at the beginning of the list. * @return * an array of month abbreviations */ function date_month_names_abbr($required = FALSE) { static $month_names; if (empty($month_names)) { $month_names = array(); foreach (date_month_names_untranslated() as $key => $month) { $month_names[$key] = t(substr($month, 0, 3)); } } $none = array('' => ''); return !$required ? $none + $month_names : $month_names; } /** * An untranslated array of week days * * Needed for css, translation functions, strtotime(), and other places * that use the English versions of these words. * * @return * an array of week day names */ function date_week_days_untranslated($refresh = TRUE) { static $weekdays; if ($refresh || empty($weekdays)) { $weekdays = array(0 => 'Sunday', 1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday'); } return $weekdays; } /** * A translated array of week days * * @param $required * If not required, will include a blank value at the beginning of the array. * @return * an array of week day names */ function date_week_days($required = FALSE, $refresh = TRUE) { static $weekdays; if ($refresh || empty($weekdays)) { $weekdays = array(); foreach (date_week_days_untranslated($refresh) as $key => $day) { $weekdays[$key] = t($day); } } $none = array('' => ''); return !$required ? $none + $weekdays : $weekdays; } /** * An translated array of week day abbreviations. * * @param $required * If not required, will include a blank value at the beginning of the array. * @return * an array of week day abbreviations */ function date_week_days_abbr($required = FALSE, $refresh = TRUE, $length = 3) { if ($refresh || empty($weekdays)) { $weekdays = array(); foreach (date_week_days_untranslated($refresh) as $key => $day) { $weekdays[$key] = t(substr($day, 0, $length)); } } $none = array('' => ''); return !$required ? $none + $weekdays : $weekdays; } /** * Order weekdays * Correct weekdays array so first day in array matches the first day of * the week. Use to create things like calendar headers. * * @param array $weekdays * @return array */ function date_week_days_ordered($weekdays) { if (variable_get('date_first_day', 1) > 0) { for ($i = 1; $i <= variable_get('date_first_day', 1); $i++) { $last = array_shift($weekdays); array_push($weekdays, $last); } } return $weekdays; } /** * An array of years. * * @param int $min * the minimum year in the array * @param int $max * the maximum year in the array * @param $required * If not required, will include a blank value at the beginning of the array. * @return * an array of years in the selected range */ function date_years($min = 0, $max = 0, $required = FALSE) { // Have to be sure $min and $max are valid values; if (empty($min)) $min = intval(date('Y', time()) - 3); if (empty($max)) $max = intval(date('Y', time()) + 3); $none = array(0 => ''); return !$required ? $none + drupal_map_assoc(range($min, $max)) : drupal_map_assoc(range($min, $max)); } /** * An array of days. * * @param $required * If not required, returned array will include a blank value. * @param integer $month (optional) * @param integer $year (optional) * @return * an array of days for the selected month. */ function date_days($required = FALSE, $month = NULL, $year = NULL) { // If we have a month and year, find the right last day of the month. if (!empty($month) && !empty($year)) { $date = date_make_date($year .'-'. $month .'-01 00:00:00', 'UTC'); $max = date_format('t', $date); } // If there is no month and year given, default to 31. if (empty($max)) $max = 31; $none = array(0 => ''); return !$required ? $none + drupal_map_assoc(range(1, $max)) : drupal_map_assoc(range(1, $max)); } /** * An array of hours. * * @param string $format * @param $required * If not required, returned array will include a blank value. * @return * an array of hours in the selected format. */ function date_hours($format = 'H', $required = FALSE) { $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; } $none = array('' => ''); return !$required ? $none + $hours : $hours; } /** * An array of minutes. * * @param string $format * @param $required * If not required, returned array will include a blank value. * @return * an array of minutes in the selected format. */ function date_minutes($format = 'i', $required = FALSE, $increment = 1) { $minutes = array(); // Have to be sure $increment has a value so we don't loop endlessly; if (empty($increment)) $increment = 1; for ($i = 0; $i < 60; $i += $increment) { $minutes[$i] = $i < 10 && $format == 'i' ? "0$i" : $i; } $none = array('' => ''); return !$required ? $none + $minutes : $minutes; } /** * An array of seconds. * * @param string $format * @param $required * If not required, returned array will include a blank value. * @return array an array of seconds in the selected format. */ function date_seconds($format = 's', $required = FALSE, $increment = 1) { $seconds = array(); // Have to be sure $increment has a value so we don't loop endlessly; if (empty($increment)) $increment = 1; for ($i = 0; $i < 60; $i += $increment) { $seconds[$i] = $i < 10 && $format == 's' ? "0$i" : $i; } $none = array('' => ''); return !$required ? $none + $seconds : $seconds; } /** * An array of am and pm options. * @param $required * If not required, returned array will include a blank value. * @return array an array of am pm options. */ function date_ampm($required = FALSE) { $none = array('' => ''); $ampm = array('am' => t('am'), 'pm' => t('pm')); return !$required ? $none + $ampm : $ampm; } /** * An array of short date formats. * * @return array an array of short date format strings. */ function date_short_formats() { return array( 'Y-m-d H:i', 'm/d/Y - H:i', 'd/m/Y - H:i', 'Y/m/d - H:i', 'd.m.Y - H:i', 'm/d/Y - g:ia', 'd/m/Y - g:ia', 'Y/m/d - g:ia', 'M j Y - H:i', 'j M Y - H:i', 'Y M j - H:i', 'M j Y - g:ia', 'j M Y - g:ia', 'Y M j - g:ia'); } /** * An array of medium date formats. * * @return array an array of medium date format strings. */ function date_medium_formats() { return array( 'D, Y-m-d H:i', 'D, m/d/Y - H:i', 'D, d/m/Y - H:i', 'D, Y/m/d - H:i', 'F j, Y - H:i', 'j F, Y - H:i', 'Y, F j - H:i', 'D, m/d/Y - g:ia', 'D, d/m/Y - g:ia', 'D, Y/m/d - g:ia', 'F j, Y - g:ia', 'j F Y - g:ia', 'Y, F j - g:ia', 'j. F Y - G:i'); } /** * An array of long date formats. * * @return array an array of long date format strings. */ function date_long_formats() { return array( 'l, F j, Y - H:i', 'l, j F, Y - H:i', 'l, Y, F j - H:i', 'l, F j, Y - g:ia', 'l, j F Y - g:ia', 'l, Y, F j - g:ia', 'l, j. F Y - G:i'); } /** * Array of regex replacement strings for date format elements. * Used to allow input in custom formats. Based on work done for * the Date module by Yves Chedemois (yched). * * @return array of date() format letters and their regex equivalents. */ function date_format_patterns() { return array( 'd' => '\d{2}', 'j' => '\d{1,2}', 'N' => '\d', 'S' => '\w{2}', 'w' => '\d', 'z' => '\d{1,3}', 'W' => '\d{1,2}', 'm' => '\d{2}', 'n' => '\d{1,2}', 't' => '\d{2}', 'L' => '\d', 'o' => '\d{4}', 'Y' => '\d{4}', 'y' => '\d{2}', 'B' => '\d{3}', 'g' => '\d{1,2}', 'G' => '\d{1,2}', 'h' => '\d{2}', 'H' => '\d{2}', 'i' => '\d{2}', 's' => '\d{2}', 'e' => '\w*', 'I' => '\d', 'T' => '\w*', 'U' => '\d*', 'z' => '[+-]?\d*', 'O' => '[+-]?\d{4}', //Using S instead of w and 3 as well as 4 to pick up non-ASCII chars like German umlaute 'D' => '\S{3,4}', 'l' => '\S*', 'M' => '\S{3,4}', 'F' => '\S*', 'P' => '[+-]?\d{2}\:\d{2}', 'c' => '(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([+-]?\d{2}\:\d{2})', 'r' => '(\w{3}), (\d{2})\s(\w{3})\s(\d{2,4})\s(\d{2}):(\d{2}):(\d{2})([+-]?\d{4})?', ); } /** * Array of granularity options and their labels * * @return array */ function date_granularity_names() { return array('year' => t('Year'), 'month' => t('Month'), 'day' => t('Day'), 'hour' => t('Hour'), 'minute' => t('Minute'), 'second' => t('Second'), ); } /** * A translated array of timezone names. * Cache the untranslated array, make the translated array a static variable. * * @param $required * If not required, returned array will include a blank value. * @return * an array of timezone names */ function date_timezone_names($required = FALSE, $refresh = FALSE) { static $zonenames; if (empty($zonenames)) { $zonenames = array(); $cached = cache_get('date_timezone_identifiers_list'); $data = isset($cached->data) ? $cached->data : array(); if (empty($data) || $refresh) { $data = timezone_identifiers_list(); cache_set('date_timezone_identifiers_list', $data); } foreach ($data as $name) { $zonenames[$name] = t($name); } } $none = array('' => ''); return !$required ? $none + $zonenames : $zonenames; } /** * An array of timezone abbreviations that the system allows. * Cache an array of just the abbreviation names because the * whole timezone_abbreviations_list is huge so we don't want * to get it more than necessary. * * @return array */ function date_timezone_abbr($refresh = FALSE) { $cached = cache_get('date_timezone_abbreviations'); $data = isset($cached->data) ? $cached->data : array(); if (empty($data) || $refresh) { $data = array_keys(timezone_abbreviations_list()); cache_set('date_timezone_abbreviations', $data); } return $data; } /** * A version of the t() function for date parts that need translation. * * Run this over results of functions which do no translation of * month and day names, like date() and date_format(). * * @param string $string * @return translated value of the string */ function date_t($string) { static $replace; if (empty($replace)) { $replace = array(); // Translate the whole name first, then look for abbreviations. foreach (date_month_names_untranslated() as $month) { $replace[$month] = t($month); $replace[substr($month, 0, 3)] = t(substr($month, 0, 3)); } foreach (date_week_days_untranslated() as $day) { $replace[$day] = t($day); $replace[substr($day, 0, 3)] = t(substr($day, 0, 3)); } } return strtr($string, $replace); } /** * An override for interval formatting that adds past and future context * * @param DateTime $date * @param integer $granularity * @return formatted string */ function date_format_interval($date, $granularity = 2) { $interval = time() - date_format($date, 'U'); if($interval > 0 ) { return t('!time ago', array('!time' => format_interval($interval, $granularity))); } else { return format_interval(abs($interval), $granularity); } } /** * Reworked from Drupal's format_date function to handle pre-1970 and * post-2038 dates and accept a date object instead of a timestamp as input. * * Translates formatted date results, unlike PHP function date_format(). * * @param $oject * A date object, could be created by date_make_date(). * @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. * @return * A translated date string in the requested format. */ function date_format_date($object, $type = 'medium', $format = '') { if (empty($object)) { return ''; } 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': $format = $format; break; case 'medium': default: $format = variable_get('date_format_medium', 'D, m/d/Y - H:i'); } return date_t(date_format($object, $format)); } /** * A date object for the current time. * * @param $timezone * Optional method to force time to a specific timezone, * defaults to user timezone, if set, otherwise site timezone. * @return object date */ function date_now($timezone = NULL) { return date_make_date('now', $timezone); } /** * Convert a date of any type or an array of date parts into a valid date * object. * @param $date * A date in any format or the string 'now'. * @param $timezone * Optional, the name of the timezone this date is in, defaults * to the user timezone, if set, otherwise the site timezone. * Accepts either a timezone name or a timezone object as input. * @param $type * The type of date provided, could be * DATE_ARRAY, DATE_UNIX, DATE_DATETIME, DATE_ISO, or DATE_OBJECT. */ function date_make_date($date, $timezone = NULL, $type = DATE_DATETIME) { if ($date != 'now' && !date_is_valid($date, $type)) { return NULL; } if (empty($timezone)) { $timezone = date_default_timezone_name(); } if (!empty($timezone) && !empty($date)) { if (!is_object($timezone)) { $timezone = timezone_open($timezone); } if ($date == 'now') { return date_create('now', $timezone); } elseif ($datetime = date_convert($date, $type, DATE_DATETIME)) { return date_create($datetime, $timezone); } } return NULL; } /** * Return a timezone name to use as a default. * * @return a timezone name * Identify the default timezone for a user, if available, otherwise the site. * May return a blank value if no timezone info has been set up. */ function date_default_timezone_name($check_user = TRUE) { global $user; if ($check_user && variable_get('configurable_timezones', 1) && !empty($user->timezone_name)) { return $user->timezone_name; } else { return variable_get('date_default_timezone_name', ''); } } /** * A timezone object for the default timezone. * * @return a timezone name * Identify the default timezone for a user, if available, otherwise the site. */ function date_default_timezone($check_user = TRUE) { return timezone_open(date_default_timezone_name($check_user)); } /** * Identify the number of days in a month for a date. * * @param mixed $date * @return integer */ function date_days_in_month($date = NULL, $type = DATE_OBJECT) { if (empty($date)) { $date = date_now(); $type = DATE_OBJECT; } $date = date_convert($date, $type, DATE_OBJECT); if (is_object($date)) { return date_format($date, 't'); } return NULL; } /** * Identify the number of days in a year for a date. * * @param mixed $date * @param string $type * @return integer */ function date_days_in_year($date = NULL, $type = DATE_OBJECT) { if (empty($date)) { $date = date_now(); $type = DATE_OBJECT; } $date = date_convert($date, $type, DATE_OBJECT); if (is_object($date)) { if (date_format($date, 'L')) { return 366; } else { return 365; } } return NULL; } /** * Identify the number of ISO weeks in a year for a date. * * December 28 is always in the last ISO week of the year. * * @param mixed $date * @param string $type * @return integer */ function date_weeks_in_year($date = NULL, $type = DATE_OBJECT) { if (empty($date)) { $date = date_now(); $type = DATE_OBJECT; } $date = date_convert($date, $type, DATE_OBJECT); if (is_object($date)) { date_date_set($date, date_format($date, 'Y'), 12, 28); return date_format($date, 'W'); } return NULL; } /** * Returns day of week for a given date (0 = Sunday). * * @param mixed $date * a date, default is current local day * @param string $type * The type of date, DATE_ISO, DATE_DATETIME, or DATE_UNIX * @return * the number of the day in the week */ function date_day_of_week($date = NULL, $type = DATE_OBJECT) { if (empty($date)) { $date = date_now(); $type = DATE_OBJECT; } $date = date_convert($date, $type, DATE_OBJECT); if (is_object($date)) { return date_format($date, 'w'); } return NULL; } /** * Returns translated name of the day of week for a given date. * * @param mixed $date * a date, default is current local day * @param string $type * The type of date, DATE_ISO, DATE_DATETIME, or DATE_UNIX * @param string $abbr * Whether to return the abbreviated name for that day * @return * the name of the day in the week for that date */ function date_day_of_week_name($date = NULL, $abbr = TRUE, $type = DATE_DATETIME) { $dow = date_day_of_week($date, $type); $days = $abbr ? date_week_days_abbr() : date_week_days(); return $days[$dow]; } /** * Compute difference between two days using a given measure. * * @param mixed $date1 * the starting date * @param mixed $date2 * the ending date * @param string $measure * 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds' * @param string $type * the type of dates provided: * DATE_OBJECT, DATE_DATETIME, DATE_ISO, DATE_UNIX, DATE_ARRAY */ function date_diff($date1_in, $date2_in, $measure = 'seconds', $type = DATE_OBJECT) { // Create cloned objects or original dates will be impacted by // the date_modify() operations done in this code. $date1 = drupal_clone(date_convert($date1_in, $type, DATE_OBJECT)); $date2 = drupal_clone(date_convert($date2_in, $type, DATE_OBJECT)); if (is_object($date1) && is_object($date2)) { $diff = date_format($date2, 'U') - date_format($date1, 'U'); if ($diff == 0 ) { return 0; } elseif ($diff < 0) { // Make sure $date1 is the smaller date. $temp = $date2; $date2 = $date1; $date1 = $temp; $diff = date_format($date2, 'U') - date_format($date1, 'U'); } $year_diff = intval(date_format($date2, 'Y') - date_format($date1, 'Y')); switch ($measure) { // The easy cases first. case 'seconds': return $diff; case 'minutes': return $diff * 60; case 'hours': return $diff * 24 * 60; case 'years': return $year_diff; case 'months': $format = 'n'; $item1 = date_format($date1, $format); $item2 = date_format($date2, $format); if ($year_diff == 0) { return intval($item2 - $item1); } else { $item_diff = 12 - $item1; $item_diff += intval(($year_diff - 1) * 12); return $item_diff + $item2; } break; case 'days': $format = 'z'; $item1 = date_format($date1, $format); $item2 = date_format($date2, $format); if ($year_diff == 0) { return intval($item2 - $item1); } else { $item_diff = date_days_in_year($date1) - $item1; for ($i = 1; $i < $year_diff; $i++) { date_modify($date1, '+1 year'); $item_diff += date_days_in_year($date1); } return $item_diff + $item2; } break; case 'weeks': $week_diff = date_format($date2, 'W') - date_format($date1, 'W'); $year_diff = date_format($date2, 'o') - date_format($date1, 'o'); for ($i = 1; $i <= $year_diff; $i++) { date_modify($date1, '+1 year'); $week_diff += date_weeks_in_year($date1); } return $week_diff; } } return NULL; } /** * Date conversion helper function. * * A variety of ways to convert dates from one type to another. * No timezone conversion is done in this operation!! * * Example: date_convert('2007-03-15 08:30', DATE_DATETIME, DATE_UNIX); * returns unix value for the supplied date. * * @param mixed $date * the date to convert * @param string $from_type * the type of date to convert from * @param string $to_type * the type of date to convert to */ function date_convert($date, $from_type, $to_type) { if (empty($date) && !$date === 0) return NULL; if (empty($from_type) || empty($to_type) || $from_type == $to_type) return $date; switch ($from_type) { case DATE_ARRAY: if (!is_array($date)) return NULL; $datetime = date_pad(intval($date['year']), 4) .'-'. date_pad(intval($date['month'])) . '-'. date_pad(intval($date['day'])) .' '. date_pad(intval($date['hour'])) . ':'. date_pad(intval($date['minute'])) .':'. date_pad(intval($date['second'])); switch ($to_type) { case DATE_ISO: return str_replace(' ', 'T', $datetime); case DATE_DATETIME: return $datetime; case DATE_ICAL: $replace = array(' ' => 'T', '-' => '', ':' => ''); return strtr($datetime, $replace); case DATE_OBJECT: return date_create($datetime, timezone_open('UTC')); case DATE_UNIX: $obj = date_create($datetime, timezone_open('UTC')); return date_format($obj, 'U'); } break; case DATE_OBJECT: if (!is_object($date)) return NULL; $obj = $date; break; case DATE_DATETIME: case DATE_ISO: if (!preg_match(DATE_REGEX_LOOSE, $date)) return NULL; $date = str_replace('T', ' ', $date); $obj = date_make_date(date_fuzzy_datetime($date), 'UTC'); break; case DATE_ICAL: if (!preg_match(DATE_REGEX_LOOSE, $date)) return NULL; preg_match(DATE_REGEX_LOOSE, $date, $regs); $datetime = date_pad($regs[1], 4) .'-'. date_pad($regs[2]) .'-'. date_pad($regs[3]) . 'T'. date_pad($regs[5]) .':'. date_pad($regs[6]) .':'. date_pad($regs[7]); $obj = date_create($datetime, timezone_open('UTC')); case DATE_UNIX: if (!is_numeric($date)) return NULL; $obj = date_create("@$date", timezone_open('UTC')); break; } switch ($to_type) { case DATE_OBJECT: return $obj; case DATE_DATETIME: return date_format($obj, DATE_FORMAT_DATETIME); case DATE_ISO: return date_format($obj, DATE_FORMAT_ISO); case DATE_ICAL: return date_format($obj, DATE_FORMAT_ICAL); case DATE_UNIX: return date_format($obj, 'U'); case DATE_ARRAY: $date_array = date_array($obj); // ISO dates may contain zero values for some date parts, // make sure they don't get lost in the conversion. if ($from_type == DATE_ISO) { $date_array = array_merge($date_array, date_iso_array($date)); } return $date_array; default: return NULL; } } /** * Create valid datetime value from incomplete ISO dates or arrays. */ function date_fuzzy_datetime($date) { if (!is_array($date)) { $date = date_iso_array($date); } if (empty($date['year'])) { $date['year'] = date('Y'); } if (empty($date['month'])) { $date['month'] = 1; } if (empty($date['day'])) { $date['day'] = 1; } $value = date_pad($date['year'], 4) .'-'. date_pad($date['month']) .'-'. date_pad($date['day']) .'T'. date_pad($date['hour']) .':'. date_pad($date['minute']) .':'. date_pad($date['second']); return $value; } /** * Create an array of date parts from an ISO date. */ function date_iso_array($date) { preg_match(DATE_REGEX_LOOSE, $date, $regs); return array( 'year' => intval($regs[1]), 'month' => intval($regs[2]), 'day' => intval($regs[3]), 'hour' => intval($regs[5]), 'minute' => intval($regs[6]), 'second' => intval($regs[7]), ); } /** * Create an array of values from a date object. Structured like the * results of getdate() but not limited to the 32-bit signed range. * * @param object $obj * @return array */ function date_array($obj) { $year = intval(date_format($obj, 'Y')); $dow = date_format($obj, 'w'); $days = date_week_days(); return array( 'second' => (integer) date_format($obj, 's'), 'minute' => (integer) date_format($obj, 'i'), 'hour' => date_format($obj, 'G'), 'day' => date_format($obj, 'j'), 'wday' => $dow, 'month' => date_format($obj, 'n'), 'year' => date_format($obj, 'Y'), 'yday' => date_format($obj, 'z'), 'weekday' => $days[$dow], 'month_name' => date_format($obj, 'F'), 0 => date_format($obj, 'U')); } /** * Extract integer value of any date part from any type of date. * * Example: * date_part_extract('2007-03-15 00:00', 'month', DATE_DATETIME) * returns: 3 * * @param mixed $date * the date value to analyze. * @param string $part * the part of the date to extract, 'year', 'month', 'day', 'hour', 'minute', 'second' * @param string $type * the type of date supplied, DATE_ISO, DATE_UNIX, DATE_DATETIME, or DATE_OBJECT; * @return integer * the integer value of the requested date part. */ function date_part_extract($date, $part, $type = DATE_DATETIME) { $formats = array('year' => 'Y', 'month' => 'n', 'day' => 'j', 'hour' => 'G', 'minute' => 'i', 'second' => 's'); $positions = array('year' => 0, 'month' => 5, 'day' => 8, 'hour' => 11, 'minute' => 14, 'second' => 17); $ipositions = array('year' => 0, 'month' => 4, 'day' => 6, 'hour' => 9, 'minute' => 11, 'second' => 13); switch ($type) { case DATE_ARRAY: return (integer) $date[$part]; case DATE_DATETIME: case DATE_ISO: return (integer) substr($date, $positions[$part], $part == 'year' ? 4 : 2); case DATE_ICAL: return (integer) substr($date, $ipositions[$part], $part == 'year' ? 4 : 2); case DATE_UNIX: $date = date_create("@$date", timezone_open('UTC')); return date_format($date, $formats[$part]); case DATE_OBJECT: return date_format($date, $formats[$part]); } } /** * Functions to test the validity of a date in various formats. * Has special case for ISO dates and arrays which can be missing * month and day and still be valid. * * @param $type * could be DATE_ARRAY, DATE_UNIX, DATE_DATETIME, DATE_ISO, or DATE_OBJECT */ function date_is_valid($date, $type = DATE_DATETIME) { if (empty($date)) return FALSE; if ($type == DATE_OBJECT && !is_object($date)) return FALSE; if (($type == DATE_ISO || $type == DATE_DATETIME) && !preg_match(DATE_REGEX_LOOSE, $date)) return FALSE; if ($type == DATE_UNIX and !is_numeric($date)) return FALSE; if ($type == DATE_ARRAY and !is_array($date)) return FALSE; // If checkdate works, no need for further tests. $year = date_part_extract($date, 'year', $type); $month = date_part_extract($date, 'month', $type); $day = date_part_extract($date, 'day', $type); if (!checkdate($month, $day, $year)) { // ISO dates and arrays can have empty date parts if ($type == DATE_ISO || $type == DATE_ARRAY) { if (variable_get('date_max_year', 4000) < $year || variable_get('date_min_year', 1) > $year || 12 < $month || 0 > $month || 31 < $day || 0 > $day) { return FALSE; } } // Unix and datetime are expected to have at least a year, month, and day. // This test is needed to test very old dates in PHP 4, where checkdate won't work. // @TODO - this can be removed once everyone is using PHP 5. elseif (variable_get('date_max_year', 4000) < $year || variable_get('date_min_year', 1) > $year || 12 < $month || 1 > $month || 31 < $day || 1 > $day) { return FALSE; } } return TRUE; } /** * Helper function to left pad date parts with zeros. * Provided because this is needed so often with dates. * * @param int $value * the value to pad * @param int $size * total size expected, usually 2 or 4 * @return string the padded value */ function date_pad($value, $size = 2) { return sprintf("%0". $size ."d", $value); } /** * Function to figure out if any time data is to be collected or displayed. * * @param granularity * an array like ('year', 'month', 'day', 'hour', 'minute', 'second'); */ function date_has_time($granularity) { if (!is_array($granularity)) $granularity = array(); return sizeof(array_intersect($granularity, array('hour', 'minute', 'second'))) > 0 ? TRUE : FALSE; } /** * Recalculate a date so it only includes elements from a granularity * array. Helps prevent errors when unwanted values round up and ensures * that unwanted date part values don't get stored in the database. * * Example: * date_limit_value('2007-05-15 04:45:59', array('year', 'month', 'day')) * returns '2007-05-15 00:00:00' * * @param $date * a date value * @param $granularity * an array of allowed date parts, like ('year', 'month', 'day', 'hour', 'minute', 'second'); * @param $type * the type of date value provided, * DATE_DATETIME, DATE_ISO, DATE_UNIX, or DATE_ARRAY * @return * the date with the unwanted parts reset to zeros (or ones if zeros are * invalid for that date type). */ function date_limit_value($date, $granularity, $type = DATE_DATETIME) { if (!date_is_valid($date, $type) || !$nongranularity = date_nongranularity($granularity)) { return $date; } else { $date = date_convert($date, $type, DATE_ARRAY); foreach ($nongranularity as $level) { switch ($level) { case 'second': $date['second'] = 0; break; case 'minute': $date['minute'] = 0; break; case 'hour': $date['hour'] = 0; break; case 'month': $date['month'] = $type != DATE_ISO ? 1 : 0; break; case 'day': $date['day'] = $type != DATE_ISO ? 1 : 0; break; } } return date_convert($date, DATE_ARRAY, $type); } } /** * Rewrite a format string so it only inludes elements from a * specified granularity array. * * Example: * date_limit_format('F j, Y - H:i', array('year', 'month', 'day')); * returns 'F j, Y' * * @param $format * a format string * @param $granularity * an array of allowed date parts, all others will be removed * array('year', 'month', 'day', 'hour', 'minute', 'second'); * @return * a format string with all other elements removed */ function date_limit_format($format, $granularity) { // Strip out timezone formatting. $regex = array('([OZPe])'); // Get rid of dash separating date and time if either is missing. if (!date_has_time($granularity) || sizeof(array_intersect($granularity, array('year', 'month', 'day')) == 0)) { $regex[] = '( -)'; } if (!date_has_time($granularity)) { $regex[] = '(a|A)'; } // Create regular expressions to remove selected values from string. foreach (date_nongranularity($granularity) as $element) { switch ($element) { case 'year': $regex[] = '([\-/\.]?[Yy][\-/\.,]?)'; break; case 'day': $regex[] = '([\-/\.]?[lDdj][\-/\.,]?)'; break; case 'month': $regex[] = '([\-/\.]?[FMmn][\-/\.,]?)'; break; case 'hour': $regex[] = '([HhGg][:]?)'; break; case 'minute': $regex[] = '([:]?[i])'; break; case 'second': $regex[] = '([:]?[s])'; break; } } // Remove selected values from string. // Don't leave any trailing punctuation behind. $format = trim(preg_replace($regex, array(), $format)); return preg_replace('([\-/\.,]$)', '', $format); } /** * Convert a format to an ordered array of granularity parts. * * Example: * date_format_order('m/d/Y H:i') * returns * array( * 0 => 'month', * 1 => 'day', * 2 => 'year', * 3 => 'hour', * 4 => 'minute', * ); * * @param string $format * @return array of ordered granularity elements in this format string */ function date_format_order($format) { $order = array(); if (empty($format)) return $order; $max = strlen($format); for ($i = 0; $i <= $max; $i++) { if (!isset($format[$i])) break; $c = $format[$i]; switch ($c) { case 'd': case 'j': $order[] = 'day'; break; case 'F': case 'M': case 'm': case 'n': $order[] = 'month'; break; case 'Y': case 'y': $order[] = 'year'; break; case 'g': case 'G': case 'h': case 'H': $order[] = 'hour'; break; case 'i': $order[] = 'minute'; break; case 's': $order[] = 'second'; break; } } return $order; } /** * An difference array of granularity elements that are NOT in the * granularity array. Used by functions that strip unwanted * granularity elements out of formats and values. * * @param $granularity * an array like ('year', 'month', 'day', 'hour', 'minute', 'second'); */ function date_nongranularity($granularity) { return array_diff(array('year', 'month', 'day', 'hour', 'minute', 'second'), (array) $granularity); } /** * Implementation of hook_simpletest(). */ function date_api_simpletest() { $dir = drupal_get_path('module', 'date_api') .'/tests'; $tests = file_scan_directory($dir, '\.test$'); return array_keys($tests); } /** * Implementation of hook_elements(). */ function date_api_elements() { include_once(drupal_get_path('module', 'date_api') .'/date_api_elements.inc'); return _date_api_elements(); } function date_api_theme() { return array( 'date_timezone' => array('arguments' => array('element' => NULL)), 'date_select' => array('arguments' => array('element' => NULL)), 'date_text' => array('arguments' => array('element' => NULL)), 'date_select_element' => array('arguments' => array('element' => NULL)), 'date_textfield_element' => array('arguments' => array('element' => NULL)), 'date_date_part_hour_prefix' => array('arguments' => array('element' => NULL)), 'date_part_minsec_prefix' => array('arguments' => array('element' => NULL)), 'date_part_label_year' => array('arguments' => array('element' => NULL)), 'date_part_label_month' => array('arguments' => array('element' => NULL)), 'date_part_label_day' => array('arguments' => array('element' => NULL)), 'date_part_label_hour' => array('arguments' => array('element' => NULL)), 'date_part_label_minute' => array('arguments' => array('element' => NULL)), 'date_part_label_second' => array('arguments' => array('element' => NULL)), 'date_part_label_ampm' => array('arguments' => array('element' => NULL)), 'date_part_label_timezone' => array('arguments' => array('element' => NULL)), ); }