2826 lines
91 KiB
Text
2826 lines
91 KiB
Text
<?php
|
|
/**
|
|
* @file
|
|
* This module will make the date API available to other modules.
|
|
* Designed to provide a light but flexible assortment of functions
|
|
* and constants, with more functionality in additional files that
|
|
* are not loaded unless other modules specifically include them.
|
|
*/
|
|
|
|
/**
|
|
* Set up some constants.
|
|
*
|
|
* Includes standard date types, format strings, strict regex strings for ISO
|
|
* and DATETIME formats (seconds are optional).
|
|
*
|
|
* The loose regex will find any variety of ISO date and time, with or
|
|
* without time, with or without dashes and colons separating the elements,
|
|
* and with either a 'T' or a space separating date and time.
|
|
*/
|
|
define('DATE_ISO', 'date');
|
|
define('DATE_UNIX', 'datestamp');
|
|
define('DATE_DATETIME', 'datetime');
|
|
define('DATE_ARRAY', 'array');
|
|
define('DATE_OBJECT', 'object');
|
|
define('DATE_ICAL', 'ical');
|
|
|
|
define('DATE_FORMAT_ISO', "Y-m-d\TH:i:s");
|
|
define('DATE_FORMAT_UNIX', "U");
|
|
define('DATE_FORMAT_DATETIME', "Y-m-d H:i:s");
|
|
define('DATE_FORMAT_ICAL', "Ymd\THis");
|
|
define('DATE_FORMAT_ICAL_DATE', "Ymd");
|
|
define('DATE_FORMAT_DATE', 'Y-m-d');
|
|
|
|
define('DATE_REGEX_ISO', '/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):?(\d{2})?/');
|
|
define('DATE_REGEX_DATETIME', '/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):?(\d{2})?/');
|
|
define('DATE_REGEX_LOOSE', '/(\d{4})-?(\d{1,2})-?(\d{1,2})([T\s]?(\d{2}):?(\d{2}):?(\d{2})?(\.\d+)?(Z|[\+\-]\d{2}:?\d{2})?)?/');
|
|
define('DATE_REGEX_ICAL_DATE', '/(\d{4})(\d{2})(\d{2})/');
|
|
define('DATE_REGEX_ICAL_DATETIME', '/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?/');
|
|
|
|
/**
|
|
* Implementation of hook_init().
|
|
*/
|
|
function date_api_init() {
|
|
if (function_exists('date_default_timezone_set')) {
|
|
date_default_timezone_set(date_default_timezone_name());
|
|
}
|
|
drupal_add_css(drupal_get_path('module', 'date_api') .'/date.css');
|
|
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_menu().
|
|
*/
|
|
function date_api_menu() {
|
|
$items = array();
|
|
$items['admin/settings/date-time/formats'] = array(
|
|
'title' => 'Formats',
|
|
'description' => 'Allow users to configure date formats',
|
|
'type' => MENU_LOCAL_TASK,
|
|
'file' => 'date_api.admin.inc',
|
|
'page callback' => 'drupal_get_form',
|
|
'page arguments' => array('date_api_date_formats_form'),
|
|
'access arguments' => array('administer site configuration'),
|
|
'weight' => 1,
|
|
);
|
|
$items['admin/settings/date-time/formats/configure'] = array(
|
|
'title' => 'Configure',
|
|
'description' => 'Allow users to configure date formats',
|
|
'type' => MENU_DEFAULT_LOCAL_TASK,
|
|
'file' => 'date_api.admin.inc',
|
|
'page callback' => 'drupal_get_form',
|
|
'page arguments' => array('date_api_date_formats_form'),
|
|
'access arguments' => array('administer site configuration'),
|
|
'weight' => 1,
|
|
);
|
|
$items['admin/settings/date-time/formats/lookup'] = array(
|
|
'title' => 'Date and time lookup',
|
|
'type' => MENU_CALLBACK,
|
|
'page callback' => 'date_api_date_time_lookup',
|
|
'access arguments' => array('administer site configuration'),
|
|
);
|
|
$items['admin/settings/date-time/formats/custom'] = array(
|
|
'title' => 'Custom formats',
|
|
'description' => 'Allow users to configure custom date formats.',
|
|
'type' => MENU_LOCAL_TASK,
|
|
'file' => 'date_api.admin.inc',
|
|
'page callback' => 'date_api_configure_custom_date_formats',
|
|
'access arguments' => array('administer site configuration'),
|
|
'weight' => 2,
|
|
);
|
|
$items['admin/settings/date-time/formats/add'] = array(
|
|
'title' => 'Add format',
|
|
'description' => 'Allow users to add additional date formats.',
|
|
'type' => MENU_LOCAL_TASK,
|
|
'file' => 'date_api.admin.inc',
|
|
'page callback' => 'drupal_get_form',
|
|
'page arguments' => array('date_api_add_date_formats_form'),
|
|
'access arguments' => array('administer site configuration'),
|
|
'weight' => 3,
|
|
);
|
|
$items['admin/settings/date-time/formats/delete/%'] = array(
|
|
'title' => 'Delete date format',
|
|
'description' => 'Allow users to delete a configured date format.',
|
|
'type' => MENU_CALLBACK,
|
|
'file' => 'date_api.admin.inc',
|
|
'page callback' => 'drupal_get_form',
|
|
'page arguments' => array('date_api_delete_format_form', 5),
|
|
'access arguments' => array('administer site configuration'),
|
|
);
|
|
$items['admin/settings/date-time/delete/%'] = array(
|
|
'title' => 'Delete date format type',
|
|
'description' => 'Allow users to delete a configured date format type.',
|
|
'type' => MENU_CALLBACK,
|
|
'file' => 'date_api.admin.inc',
|
|
'page callback' => 'drupal_get_form',
|
|
'page arguments' => array('date_api_delete_format_type_form', 4),
|
|
'access arguments' => array('administer site configuration'),
|
|
);
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_menu_alter().
|
|
*/
|
|
function date_api_menu_alter(&$callbacks) {
|
|
// Add a new 'admin/settings/date-time/configure' path and make it the same as
|
|
// the 'admin/settings/date-time'. Also set it to be the default local task -
|
|
// needed to make tabs work as expected.
|
|
$callbacks['admin/settings/date-time/configure'] = $callbacks['admin/settings/date-time'];
|
|
$callbacks['admin/settings/date-time/configure']['type'] = MENU_DEFAULT_LOCAL_TASK;
|
|
}
|
|
|
|
/**
|
|
* Helper function for getting the format string for a date type.
|
|
*/
|
|
function date_type_format($type) {
|
|
switch ($type) {
|
|
case DATE_ISO:
|
|
return DATE_FORMAT_ISO;
|
|
case DATE_UNIX:
|
|
return DATE_FORMAT_UNIX;
|
|
case DATE_DATETIME:
|
|
return DATE_FORMAT_DATETIME;
|
|
case DATE_ICAL:
|
|
return DATE_FORMAT_ICAL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An untranslated array of month names
|
|
*
|
|
* Needed for css, translation functions, strtotime(), and other places
|
|
* that use the English versions of these words.
|
|
*
|
|
* @return
|
|
* an array of month names
|
|
*/
|
|
function date_month_names_untranslated() {
|
|
static $month_names;
|
|
if (empty($month_names)) {
|
|
$month_names = array(1 => '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) {
|
|
$month_names = array();
|
|
foreach (date_month_names_untranslated() as $key => $day) {
|
|
$month_names[$key] = date_t($day, 'month_name');
|
|
}
|
|
$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) {
|
|
$month_names = array();
|
|
foreach (date_month_names_untranslated() as $key => $day) {
|
|
$month_names[$key] = date_t($day, 'month_abbr');
|
|
}
|
|
$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) {
|
|
$weekdays = array();
|
|
foreach (date_week_days_untranslated() as $key => $day) {
|
|
$weekdays[$key] = date_t($day, 'day_name');
|
|
}
|
|
$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) {
|
|
$weekdays = array();
|
|
switch ($length) {
|
|
case 1:
|
|
$context = 'day_abbr1';
|
|
break;
|
|
case 2:
|
|
$context = 'day_abbr2';
|
|
break;
|
|
default:
|
|
$context = 'day_abbr';
|
|
break;
|
|
}
|
|
foreach (date_week_days_untranslated() as $key => $day) {
|
|
$weekdays[$key] = date_t($day, $context);
|
|
}
|
|
$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' => date_t('am', 'ampm'), 'pm' => date_t('pm', 'ampm'));
|
|
return !$required ? $none + $ampm : $ampm;
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_date_formats().
|
|
*
|
|
* @return
|
|
* An array of date formats with attributes 'type' (short, medium or long),
|
|
* 'format' (the format string) and 'locales'. The 'locales' attribute is an
|
|
* array of locales, which can include both 2 character language codes like
|
|
* 'en', 'fr', but also 5 character language codes like 'en-gb' and 'en-us'.
|
|
*/
|
|
function date_api_date_formats() {
|
|
include_once('./'. drupal_get_path('module', 'date_api') .'/date_api_formats_list.inc');
|
|
return _date_api_date_formats_list();
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_date_format_types().
|
|
*/
|
|
function date_api_date_format_types() {
|
|
return array(
|
|
'long' => t('Long'),
|
|
'medium' => t('Medium'),
|
|
'short' => t('Short'),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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($strict = FALSE) {
|
|
return array(
|
|
'd' => '\d{'. ($strict ? '2' : '1,2') .'}',
|
|
'm' => '\d{'. ($strict ? '2' : '1,2') .'}',
|
|
'h' => '\d{'. ($strict ? '2' : '1,2') .'}',
|
|
'H' => '\d{'. ($strict ? '2' : '1,2') .'}',
|
|
'i' => '\d{'. ($strict ? '2' : '1,2') .'}',
|
|
's' => '\d{'. ($strict ? '2' : '1,2') .'}',
|
|
'j' => '\d{1,2}', 'N' => '\d', 'S' => '\w{2}',
|
|
'w' => '\d', 'z' => '\d{1,3}', 'W' => '\d{1,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}', '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.
|
|
// Per http://drupal.org/node/1101294, we may need as little as 2 and as many as 5 characters
|
|
// in some languages.
|
|
'D' => '\S{2,5}', 'l' => '\S*', 'M' => '\S{2,5}', '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' => date_t('Year', 'datetime'),
|
|
'month' => date_t('Month', 'datetime'),
|
|
'day' => date_t('Day', 'datetime'),
|
|
'hour' => date_t('Hour', 'datetime'),
|
|
'minute' => date_t('Minute', 'datetime'),
|
|
'second' => date_t('Second', 'datetime'),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Sort a granularity array.
|
|
*/
|
|
function date_granularity_sorted($granularity) {
|
|
return array_intersect(array('year', 'month', 'day', 'hour', 'minute', 'second'), $granularity);
|
|
}
|
|
|
|
/**
|
|
* Give a granularity $precision, return an array of
|
|
* all the possible granularity elements.
|
|
*/
|
|
function date_granularity_array_from_precision($precision) {
|
|
$granularity_array = array('year', 'month', 'day', 'hour', 'minute', 'second');
|
|
switch (($precision)) {
|
|
case 'year':
|
|
return array_slice($granularity_array, -6);
|
|
case 'month':
|
|
return array_slice($granularity_array, -5);
|
|
case 'day':
|
|
return array_slice($granularity_array, -4);
|
|
case 'hour':
|
|
return array_slice($granularity_array, -3);
|
|
case 'minute':
|
|
return array_slice($granularity_array, -2);
|
|
default:
|
|
return $granularity_array;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Give a granularity array, return the highest precision.
|
|
*/
|
|
function date_granularity_precision($granularity_array) {
|
|
$input = date_granularity_sorted($granularity_array);
|
|
return array_pop($input);
|
|
}
|
|
|
|
/**
|
|
* Construct an appropriate DATETIME format string for the granularity of an item.
|
|
*/
|
|
function date_granularity_format($granularity) {
|
|
if (is_array($granularity)) {
|
|
$granularity = date_granularity_precision($granularity);
|
|
}
|
|
$format = 'Y-m-d H:i:s';
|
|
switch ($granularity) {
|
|
case 'year':
|
|
return drupal_substr($format, 0, 1);
|
|
case 'month':
|
|
return drupal_substr($format, 0, 3);
|
|
case 'day':
|
|
return drupal_substr($format, 0, 5);
|
|
case 'hour';
|
|
return drupal_substr($format, 0, 7);
|
|
case 'minute':
|
|
return drupal_substr($format, 0, 9);
|
|
default:
|
|
return $format;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) || $refresh) {
|
|
$cached = cache_get('date_timezone_identifiers_list');
|
|
$zonenames = !empty($cached) ? $cached->data : array();
|
|
if ($refresh || empty($cached) || empty($zonenames)) {
|
|
$data = timezone_identifiers_list();
|
|
asort($data);
|
|
// Use include instead of include once in case the function gets
|
|
// refreshed via devel or other API and is called more than once.
|
|
if (module_exists('date_php4')) {
|
|
include('./'. drupal_get_path('module', 'date_php4') .'/date_php4_missing_data.inc');
|
|
}
|
|
foreach ($data as $delta => $zone) {
|
|
// Because many time zones exist in PHP only for backward
|
|
// compatibility reasons and should not be used, the list is
|
|
// filtered by a regular expression.
|
|
if (preg_match('!^((Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)/|UTC$)!', $zone)) {
|
|
$zonenames[$zone] = $zone;
|
|
}
|
|
}
|
|
// If using PHP4, filter the list down to only the timezones
|
|
// the PHP4 wrapper has data for.
|
|
if (module_exists('date_php4')) {
|
|
foreach ($missing_timezone_data as $zone) {
|
|
unset($zonenames[$zone]);
|
|
}
|
|
}
|
|
|
|
// If using Event, further filter the list down to only
|
|
// zones that exist in the event module.
|
|
if (module_exists('event') && db_table_exists('event_timezones')) {
|
|
$result = db_query("SELECT name FROM {event_timezones} ORDER BY name");
|
|
$names = array();
|
|
while ($row = db_fetch_array($result)) {
|
|
$names[] = str_replace(' ', '_', $row['name']);
|
|
}
|
|
foreach ($zonenames as $name => $zone) {
|
|
if (!in_array($name, $names)) {
|
|
unset($zonenames[$name]);
|
|
}
|
|
}
|
|
}
|
|
if (!empty($zonenames)) {
|
|
cache_set('date_timezone_identifiers_list', $zonenames);
|
|
}
|
|
}
|
|
foreach ($zonenames as $zone) {
|
|
$zonenames[$zone] = t('!timezone', array('!timezone' => t($zone)));
|
|
}
|
|
}
|
|
$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 function to translate ambiguous short date strings.
|
|
*
|
|
* Example: Pass in 'Monday', 'day_abbr' and get the translated
|
|
* abbreviation for Monday.
|
|
*
|
|
* @param string $string
|
|
* @param string $context
|
|
* @param int $langcode
|
|
* @return translated value of the string
|
|
*/
|
|
function date_t($string, $context, $langcode = NULL) {
|
|
static $replace = array();
|
|
|
|
if (empty($replace[$langcode])) {
|
|
// The function to create the date string arrays is kept separate
|
|
// so those arrays can be directly accessed by other functions.
|
|
date_t_strings($replace, $langcode);
|
|
}
|
|
switch ($context) {
|
|
case 'day_name':
|
|
case 'day_abbr':
|
|
case 'day_abbr1':
|
|
case 'day_abbr2':
|
|
$untranslated = array_flip(date_week_days_untranslated());
|
|
break;
|
|
case 'month_name':
|
|
case 'month_abbr':
|
|
$untranslated = array_flip(date_month_names_untranslated());
|
|
break;
|
|
case 'ampm':
|
|
$untranslated = array_flip(array('am', 'pm', 'AM', 'PM'));
|
|
break;
|
|
case 'datetime':
|
|
$untranslated = array_flip(array('Year', 'Month', 'Day', 'Week', 'Hour', 'Minute', 'Second', 'All Day', 'All day'));
|
|
break;
|
|
case 'datetime_plural':
|
|
$untranslated = array_flip(array('Years', 'Months', 'Days', 'Weeks', 'Hours', 'Minutes', 'Seconds'));
|
|
break;
|
|
case 'date_order':
|
|
$untranslated = array_flip(array('Every', 'First', 'Second', 'Third', 'Fourth', 'Fifth'));
|
|
break;
|
|
case 'date_order_reverse':
|
|
$untranslated = array_flip(array('', 'Last', 'Next to last', 'Third from last', 'Fourth from last', 'Fifth from last'));
|
|
break;
|
|
case 'date_nav':
|
|
$untranslated = array_flip(array('Prev', 'Next', 'Today'));
|
|
break;
|
|
}
|
|
$pos = $untranslated[$string];
|
|
return $replace[$langcode][$context][$pos];
|
|
}
|
|
|
|
/**
|
|
* Construct translation arrays from pipe-delimited strings.
|
|
*
|
|
* Combining these strings into a single t() gives them the context needed
|
|
* for better translation.
|
|
*/
|
|
function date_t_strings(&$replace, $langcode) {
|
|
$replace[$langcode]['day_name'] = explode('|', trim(t('!day-name Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday', array('!day-name' => ''), $langcode)));
|
|
$replace[$langcode]['day_abbr'] = explode('|', trim(t('!day-abbreviation Sun|Mon|Tue|Wed|Thu|Fri|Sat', array('!day-abbreviation' => ''), $langcode)));
|
|
$replace[$langcode]['day_abbr1'] = explode('|', trim(t('!day-abbreviation S|M|T|W|T|F|S', array('!day-abbreviation' => ''), $langcode)));
|
|
$replace[$langcode]['day_abbr2'] = explode('|', trim(t('!day-abbreviation SU|MO|TU|WE|TH|FR|SA', array('!day-abbreviation' => ''), $langcode)));
|
|
$replace[$langcode]['ampm'] = explode('|', trim(t('!ampm-abbreviation am|pm|AM|PM', array('!ampm-abbreviation' => ''), $langcode)));
|
|
$replace[$langcode]['datetime'] = explode('|', trim(t('!datetime Year|Month|Day|Week|Hour|Minute|Second|All Day|All day', array('!datetime' => ''), $langcode)));
|
|
$replace[$langcode]['datetime_plural'] = explode('|', trim(t('!datetime_plural Years|Months|Days|Weeks|Hours|Minutes|Seconds', array('!datetime_plural' => ''), $langcode)));
|
|
$replace[$langcode]['date_order'] = explode('|', trim(t('!date_order Every|First|Second|Third|Fourth|Fifth', array('!date_order' => ''), $langcode)));
|
|
$replace[$langcode]['date_order_reverse'] = explode('|', trim(t('!date_order |Last|Next to last|Third from last|Fourth from last|Fifth from last', array('!date_order' => ''), $langcode)));
|
|
$replace[$langcode]['date_nav'] = explode('|', trim(t('!date_nav Prev|Next|Today', array('!date_nav' => ''), $langcode)));
|
|
|
|
// These start with a pipe so the January value will be in position 1 instead of position 0.
|
|
$replace[$langcode]['month_name'] = explode('|', trim(t('!month-name |January|February|March|April|May|June|July|August|September|October|November|December', array('!month-name' => ''), $langcode)));
|
|
$replace[$langcode]['month_abbr'] = explode('|', trim(t('!month-abbreviation |Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec', array('!month-abbreviation' => ''), $langcode)));
|
|
}
|
|
|
|
/**
|
|
* 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 $date
|
|
* 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($date, $type = 'medium', $format = '', $langcode = NULL) {
|
|
if (empty($date)) {
|
|
return '';
|
|
}
|
|
|
|
if (function_exists('timezone_name_from_abbr') && get_class($date) != 'DateTime') {
|
|
$date = date_make_date($date);
|
|
}
|
|
switch ($type) {
|
|
case 'small':
|
|
case 'short':
|
|
$format = variable_get('date_format_short', 'm/d/Y - H:i');
|
|
break;
|
|
case 'large':
|
|
case 'long':
|
|
$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');
|
|
}
|
|
$max = drupal_strlen($format);
|
|
$datestring = '';
|
|
for ($i = 0; $i < $max; $i++) {
|
|
$c = $format[$i];
|
|
switch ($c) {
|
|
// Use date_t() for ambiguous short strings that need translation.
|
|
// We send long day and month names to date_t(), along with context.
|
|
case 'l':
|
|
$datestring .= date_t(date_format($date, 'l'), 'day_name', $langcode);
|
|
break;
|
|
case 'D':
|
|
$datestring .= date_t(date_format($date, 'l'), 'day_abbr', $langcode);
|
|
break;
|
|
case 'F':
|
|
$datestring .= date_t(date_format($date, 'F'), 'month_name', $langcode);
|
|
break;
|
|
case 'M':
|
|
$datestring .= date_t(date_format($date, 'F'), 'month_abbr', $langcode);
|
|
break;
|
|
case 'A':
|
|
case 'a':
|
|
$datestring .= date_t(date_format($date, $c), 'ampm', $langcode);
|
|
break;
|
|
// The timezone name translations can use t().
|
|
case 'e':
|
|
case 'T':
|
|
$datestring .= t(date_format($date, $c));
|
|
break;
|
|
// Remaining date parts need no translation.
|
|
case 'O':
|
|
$datestring .= sprintf('%s%02d%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60);
|
|
break;
|
|
case 'P':
|
|
$datestring .= sprintf('%s%02d:%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60);
|
|
break;
|
|
case 'Z':
|
|
$datestring .= date_offset_get($date);
|
|
break;
|
|
case '\\':
|
|
$datestring .= $format[++$i];
|
|
break;
|
|
case 'r':
|
|
$datestring .= date_format_date($date, 'custom', 'D, d M Y H:i:s O', $langcode);
|
|
break;
|
|
default:
|
|
if (strpos('BdcgGhHiIjLmnNosStTuUwWYyz', $c) !== FALSE) {
|
|
$datestring .= date_format($date, $c);
|
|
}
|
|
else {
|
|
$datestring .= $c;
|
|
}
|
|
}
|
|
}
|
|
return $datestring;
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
// If no date is sent, then return nothing
|
|
if (empty($date)) {
|
|
return NULL;
|
|
}
|
|
|
|
$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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
* @param $granularity
|
|
* The granularity of the date value provided. Set this for partial
|
|
* dates so they pass validation and don't get reset to 'now'.
|
|
*/
|
|
function date_make_date($date, $timezone = NULL, $type = DATE_DATETIME, $granularity = array('year', 'month', 'day', 'hour', 'minute')) {
|
|
|
|
// Make sure some value is set for the date and timezone even if the
|
|
// site timezone is not yet set up to avoid fatal installation
|
|
// errors.
|
|
if (empty($timezone) || !date_timezone_is_valid($timezone)) {
|
|
$timezone = date_default_timezone_name();
|
|
}
|
|
|
|
// Special handling for a unix timestamp of '0', since it will fail 'empty' tests below.
|
|
if ($date === 0 && $type == DATE_UNIX) {
|
|
$date = date_convert($date, $type, DATE_DATETIME, $timezone);
|
|
$type = DATE_DATETIME;
|
|
}
|
|
|
|
// No value or one with unexpected array keys.
|
|
if (empty($date) || (is_array($date) && array_diff($granularity, array_keys($date)))) {
|
|
return NULL;
|
|
}
|
|
|
|
// Special handling for partial dates that don't need precision.
|
|
$granularity_sorted = date_granularity_sorted($granularity);
|
|
$max_granularity = end($granularity_sorted);
|
|
if (in_array($max_granularity, array('year', 'month')) || $type == DATE_ISO || $type == DATE_ARRAY) {
|
|
if ($type == DATE_UNIX) {
|
|
$date = date_convert($date, $type, DATE_DATETIME);
|
|
}
|
|
$date = date_fuzzy_datetime($date);
|
|
$type = DATE_DATETIME;
|
|
}
|
|
|
|
if (!date_is_valid($date, $type, $granularity)) {
|
|
$date = 'now';
|
|
}
|
|
if (!empty($timezone) && !empty($date)) {
|
|
if ($date == 'now') {
|
|
return date_create('now', timezone_open($timezone));
|
|
}
|
|
elseif ($datetime = date_convert($date, $type, DATE_DATETIME, $timezone)) {
|
|
return date_create($datetime, timezone_open($timezone));
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
function date_timezone_is_valid($timezone) {
|
|
static $timezone_names;
|
|
if (empty($timezone_names)) {
|
|
$timezone_names = array_keys(date_timezone_names(TRUE));
|
|
}
|
|
if (!in_array($timezone, $timezone_names)) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
* Must return a value even 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 {
|
|
$default = variable_get('date_default_timezone_name', '');
|
|
return empty($default) ? 'UTC' : $default;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A timezone object for the default timezone.
|
|
*
|
|
* @return a timezone object
|
|
* Identify the default timezone for a user, if available, otherwise the site.
|
|
*/
|
|
function date_default_timezone($check_user = TRUE) {
|
|
$timezone = date_default_timezone_name($check_user);
|
|
return timezone_open(date_default_timezone_name($check_user));
|
|
}
|
|
|
|
/**
|
|
* Identify the number of days in a month for a date.
|
|
*/
|
|
function date_days_in_month($year, $month) {
|
|
// Pick a day in the middle of the month to avoid timezone shifts.
|
|
$datetime = date_pad($year, 4) .'-'. date_pad($month) .'-15 00:00:00';
|
|
$date = date_make_date($datetime);
|
|
return date_format($date, 't');
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
}
|
|
if (!is_object($date)) {
|
|
$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_iso_weeks_in_year($date = NULL, $type = DATE_OBJECT) {
|
|
if (empty($date)) {
|
|
$date = date_now();
|
|
}
|
|
if (!is_object($date)) {
|
|
$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_difference($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 / 3600;
|
|
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_iso_weeks_in_year($date1);
|
|
}
|
|
return $week_diff;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Start and end dates for a calendar week, adjusted to use the
|
|
* chosen first day of week for this site.
|
|
*/
|
|
function date_week_range($week, $year) {
|
|
if (variable_get('date_api_use_iso8601', FALSE)) {
|
|
return date_iso_week_range($week, $year);
|
|
}
|
|
|
|
$min_date = date_make_date($year .'-01-01 00:00:00', date_default_timezone_name());
|
|
date_timezone_set($min_date, date_default_timezone());
|
|
|
|
// move to the right week
|
|
date_modify($min_date, '+' . strval(7 * ($week - 1)) . ' days');
|
|
|
|
// move backwards to the first day of the week
|
|
$first_day = variable_get('date_first_day', 1);
|
|
$day_wday = date_format($min_date, 'w');
|
|
date_modify($min_date, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
|
|
|
|
// move forwards to the last day of the week
|
|
$max_date = drupal_clone($min_date);
|
|
date_modify($max_date, '+7 days');
|
|
|
|
if (date_format($min_date, 'Y') != $year) {
|
|
$min_date = date_make_date($year .'-01-01 00:00:00', date_default_timezone());
|
|
}
|
|
return array($min_date, $max_date);
|
|
}
|
|
|
|
/**
|
|
* Start and end dates for an ISO week.
|
|
*/
|
|
function date_iso_week_range($week, $year) {
|
|
|
|
// Get to the last ISO week of the previous year.
|
|
$min_date = date_make_date(($year - 1) .'-12-28 00:00:00', date_default_timezone_name());
|
|
date_timezone_set($min_date, date_default_timezone());
|
|
|
|
// Find the first day of the first ISO week in the year.
|
|
date_modify($min_date, '+1 Monday');
|
|
|
|
// Jump ahead to the desired week for the beginning of the week range.
|
|
if ($week > 1) {
|
|
date_modify($min_date, '+ '. ($week - 1) .' weeks');
|
|
}
|
|
|
|
// move forwards to the last day of the week
|
|
$max_date = drupal_clone($min_date);
|
|
date_modify($max_date, '+7 days');
|
|
return array($min_date, $max_date);
|
|
}
|
|
|
|
/**
|
|
* The number of calendar weeks in a year.
|
|
*
|
|
* PHP week functions return the ISO week, not the calendar week.
|
|
*
|
|
* @param int $year
|
|
* @return int number of calendar weeks in selected year.
|
|
*/
|
|
function date_weeks_in_year($year) {
|
|
$date = date_make_date(($year + 1) . '-01-01 12:00:00', 'UTC');
|
|
date_modify($date, '-1 day');
|
|
return date_week(date_format($date, 'Y-m-d'));
|
|
}
|
|
|
|
/**
|
|
* The calendar week number for a date.
|
|
*
|
|
* PHP week functions return the ISO week, not the calendar week.
|
|
*
|
|
* @param string $date, in the format Y-m-d
|
|
* @return int calendar week number.
|
|
*/
|
|
function date_week($date) {
|
|
$date = drupal_substr($date, 0, 10);
|
|
$parts = explode('-', $date);
|
|
$date = date_make_date($date . ' 12:00:00', 'UTC');
|
|
|
|
// If we are using ISO weeks, this is easy.
|
|
if (variable_get('date_api_use_iso8601', FALSE)) {
|
|
return intval(date_format($date, 'W'));
|
|
}
|
|
|
|
$year_date = date_make_date($parts[0] . '-01-01 12:00:00', 'UTC');
|
|
$week = intval(date_format($date, 'W'));
|
|
$year_week = intval(date_format($year_date, 'W'));
|
|
$date_year = intval(date_format($date, 'o'));
|
|
|
|
// remove the leap week if it's present
|
|
if ($date_year > intval($parts[0])) {
|
|
$last_date = drupal_clone($date);
|
|
date_modify($last_date, '-7 days');
|
|
$week = date_format($last_date, 'W') + 1;
|
|
}
|
|
elseif ($date_year < intval($parts[0])) {
|
|
$week = 0;
|
|
}
|
|
|
|
if ($year_week != 1) $week++;
|
|
|
|
// convert to ISO-8601 day number, to match weeks calculated above
|
|
$iso_first_day = 1 + (variable_get('date_first_day', 1) + 6) % 7;
|
|
|
|
// if it's before the starting day, it's the previous week
|
|
if (intval(date_format($date, 'N')) < $iso_first_day) $week--;
|
|
|
|
// if the year starts before, it's an extra week at the beginning
|
|
if (intval(date_format($year_date, 'N')) < $iso_first_day) $week++;
|
|
|
|
return $week;
|
|
}
|
|
|
|
/**
|
|
* Date conversion helper function.
|
|
*
|
|
* A variety of ways to convert dates from one type to another.
|
|
* No timezone conversion is done in this operation, except
|
|
* when handling timestamps because create_date() assumes
|
|
* timestamps hold the UTC value for the time.
|
|
*
|
|
* @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
|
|
* @param string $tz
|
|
* the timezone of the supplied value, only needed when using timestamps
|
|
* for dates not set to UTC.
|
|
*/
|
|
function date_convert($date, $from_type, $to_type, $tz = 'UTC') {
|
|
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;
|
|
// Make sure all parts exist to avoid PHP notices.
|
|
foreach (array('month', 'day', 'hour', 'minute', 'second') as $part) {
|
|
if (!isset($date[$part])) {
|
|
$date[$part] = '';
|
|
}
|
|
}
|
|
if (isset($date['ampm'])) {
|
|
if ($date['ampm'] == 'pm' && $date['hour'] < 12) $date['hour'] += 12;
|
|
if ($date['ampm'] == 'am' && $date['hour'] == 12) $date['hour'] -= 12;
|
|
}
|
|
$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($tz));
|
|
case DATE_UNIX:
|
|
$obj = date_create($datetime, timezone_open($tz));
|
|
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 = date_fuzzy_datetime($date);
|
|
$obj = date_create($date, timezone_open($tz));
|
|
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($tz));
|
|
break;
|
|
case DATE_UNIX:
|
|
if (!is_numeric($date)) return NULL;
|
|
// Special case when creating dates with timestamps.
|
|
// The date_create() function will assume date is UTC value
|
|
// and will ignore our timezone.
|
|
$obj = date_create("@$date", timezone_open('UTC'));
|
|
date_timezone_set($obj, timezone_open($tz));
|
|
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) {
|
|
// A text ISO date, like MMMM-YY-DD HH:MM:SS
|
|
if (!is_array($date)) {
|
|
$date = date_iso_array($date);
|
|
}
|
|
// An date/time value in the format:
|
|
// array('date' => MMMM-YY-DD, 'time' => HH:MM:SS).
|
|
elseif (array_key_exists('date', $date) || array_key_exists('time', $date)) {
|
|
$date_part = array_key_exists('date', $date) ? $date['date'] : '';
|
|
$time_part = array_key_exists('time', $date) ? $date['time'] : '';
|
|
$date = date_iso_array(trim($date_part .' '. $time_part));
|
|
}
|
|
// Otherwise date must in in format:
|
|
// array('year' => YYYY, 'month' => MM, 'day' => DD).
|
|
if (empty($date['year'])) {
|
|
$date['year'] = date('Y');
|
|
}
|
|
if (empty($date['month'])) {
|
|
$date['month'] = 1;
|
|
}
|
|
if (empty($date['day'])) {
|
|
$date['day'] = 1;
|
|
}
|
|
foreach (array('hour', 'minute', 'second') as $part) {
|
|
if (empty($date[$part])) {
|
|
$date[$part] = 0;
|
|
}
|
|
}
|
|
$value = date_pad($date['year'], 4) .'-'. date_pad($date['month']) .'-'.
|
|
date_pad($date['day']) .' '. 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' => isset($regs[1]) ? intval($regs[1]) : '',
|
|
'month' => isset($regs[2]) ? intval($regs[2]) : '',
|
|
'day' => isset($regs[3]) ? intval($regs[3]) : '',
|
|
'hour' => isset($regs[5]) ? intval($regs[5]) : '',
|
|
'minute' => isset($regs[6]) ? intval($regs[6]) : '',
|
|
'second' => isset($regs[7]) ? 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, $tz = 'UTC') {
|
|
$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) array_key_exists($part, $date) ? $date[$part] : NULL;
|
|
case DATE_DATETIME:
|
|
case DATE_ISO:
|
|
return (integer) drupal_substr($date, $positions[$part], $part == 'year' ? 4 : 2);
|
|
case DATE_ICAL:
|
|
return (integer) drupal_substr($date, $ipositions[$part], $part == 'year' ? 4 : 2);
|
|
case DATE_UNIX:
|
|
// Special case when creating dates with timestamps.
|
|
// The date_create() function will assume date is UTC value
|
|
// and will ignore our timezone.
|
|
$date = date_create("@$date", timezone_open('UTC'));
|
|
date_timezone_set($date, timezone_open($tz));
|
|
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
|
|
* @param $granularity
|
|
* The granularity of the date value provided. Set this for partial
|
|
* dates so they pass validation.
|
|
*/
|
|
function date_is_valid($date, $type = DATE_DATETIME, $granularity = array('year', 'month', 'day', 'hour', 'minute')) {
|
|
|
|
// Check that the value is properly structured.
|
|
// Remember that DATE_UNIX can have a valid value of '0', which is 'empty'.
|
|
if (empty($date) && $type != DATE_UNIX) return FALSE;
|
|
if ($type == DATE_OBJECT && !is_object($date)) return FALSE;
|
|
if (($type == DATE_ISO || $type == DATE_DATETIME) && (!is_string($date) || !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;
|
|
|
|
// Make sure integer values are sent to checkdate.
|
|
$year = intval(date_part_extract($date, 'year', $type));
|
|
$month = intval(date_part_extract($date, 'month', $type));
|
|
$day = intval(date_part_extract($date, 'day', $type));
|
|
if (checkdate($month, $day, $year)) {
|
|
return TRUE;
|
|
}
|
|
|
|
// If this is an incomplete date (year only or year and month only),
|
|
// need special handling, partial dates can have empty date parts.
|
|
$max_granularity = $granularity;
|
|
$max_granularity = array_pop($max_granularity);
|
|
if (!in_array($max_granularity, array('year', 'month'))) {
|
|
if (in_array('year', $granularity) && !date_valid_year($year)) {
|
|
return FALSE;
|
|
}
|
|
elseif (in_array('month', $granularity) && !date_valid_month($month)) {
|
|
return FALSE;
|
|
}
|
|
elseif (in_array('day', $granularity) && !date_valid_day($day, $month, $year)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
// ISO dates and arrays can have empty date parts.
|
|
elseif ($type == DATE_ISO || $type == DATE_ARRAY) {
|
|
if (!date_valid_year($year)) {
|
|
return FALSE;
|
|
}
|
|
elseif (!empty($month) && !date_valid_month($month)) {
|
|
return FALSE;
|
|
}
|
|
elseif (!empty($day) && !date_valid_day($day, $month, $year)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
elseif (!date_valid_year($year) || !date_valid_month($month) || !date_valid_day($day, $month, $year)) {
|
|
// Unix and datetime are expected to have at least a year, month, and day.
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
function date_valid_year($year) {
|
|
if (variable_get('date_max_year', 4000) < $year || variable_get('date_min_year', 1) > $year) {
|
|
return FALSE;
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
function date_valid_month($month) {
|
|
if (12 < $month || 0 > $month) {
|
|
return FALSE;
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
function date_valid_day($day, $month = NULL, $year = NULL) {
|
|
$days_in_month = !empty($month) && !empty($year) ? date_days_in_month($year, $month) : 31;
|
|
if ($days_in_month < $day || 1 > $day) {
|
|
return FALSE;
|
|
}
|
|
else {
|
|
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;
|
|
}
|
|
|
|
function date_has_date($granularity) {
|
|
if (!is_array($granularity)) $granularity = array();
|
|
return sizeof(array_intersect($granularity, array('year', 'month', 'day'))) > 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, $granularity) || !$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 includes 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) {
|
|
// If punctuation has been escaped, remove the escaping.
|
|
// Done using strtr because it is easier than getting the
|
|
// escape character extracted using preg_replace.
|
|
$replace = array(
|
|
'\-' => '-',
|
|
'\:' => ':',
|
|
"\'" => "'",
|
|
'\.' => '.',
|
|
'\,' => ',',
|
|
);
|
|
$format = strtr($format, $replace);
|
|
|
|
// Get the 'T' out of ISO date formats that don't have
|
|
// both date and time.
|
|
if (!date_has_time($granularity) || !date_has_date($granularity)) {
|
|
$format = str_replace('\T', ' ', $format);
|
|
$format = str_replace('T', ' ', $format);
|
|
}
|
|
|
|
$regex = array();
|
|
if (!date_has_time($granularity)) {
|
|
$regex[] = '((?<!\\\\)[a|A])';
|
|
}
|
|
// Create regular expressions to remove selected values from string.
|
|
// Use (?<!\\\\) to keep escaped letters from being removed.
|
|
foreach (date_nongranularity($granularity) as $element) {
|
|
switch ($element) {
|
|
case 'year':
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[Yy])';
|
|
break;
|
|
case 'day':
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[l|D|d|dS|j|jS|N|w|W|z]{1,2})';
|
|
break;
|
|
case 'month':
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[FMmn])';
|
|
break;
|
|
case 'hour':
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[HhGg])';
|
|
break;
|
|
case 'minute':
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[i])';
|
|
break;
|
|
case 'second':
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[s])';
|
|
break;
|
|
case 'timezone':
|
|
$regex[] = '([\-/\.,:]?\s?(?<!\\\\)[TOZPe])';
|
|
break;
|
|
|
|
}
|
|
}
|
|
// Remove empty parentheses, brackets, pipes.
|
|
$regex[] = '(\(\))';
|
|
$regex[] = '(\[\])';
|
|
$regex[] = '(\|\|)';
|
|
|
|
// Remove selected values from string.
|
|
$format = trim(preg_replace($regex, array(), $format));
|
|
// Remove orphaned punctuation at the beginning of the string.
|
|
$format = preg_replace('`^([\-/\.,:\'])`', '', $format);
|
|
// Remove orphaned punctuation at the end of the string.
|
|
$format = preg_replace('([\-/\.,:\']$)', '', $format);
|
|
$format = preg_replace('(\\$)', '', $format);
|
|
|
|
// Trim any whitespace from the result.
|
|
$format = trim($format);
|
|
|
|
// After removing the non-desired parts of the format, test if the only
|
|
// things left are escaped, non-date, characters. If so, return nothing.
|
|
// Using S instead of w to pick up non-ASCII characters.
|
|
$test = trim(preg_replace('(\\\\\S{1,3})', '', $format));
|
|
if (empty($test)) {
|
|
$format = '';
|
|
}
|
|
return $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 = drupal_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', 'timezone'), (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() {
|
|
require_once('./'. drupal_get_path('module', 'date_api') .'/date_api_elements.inc');
|
|
return _date_api_elements();
|
|
}
|
|
|
|
function date_api_theme() {
|
|
$path = drupal_get_path('module', 'date_api');
|
|
$base = array(
|
|
'file' => 'theme.inc',
|
|
'path' => "$path/theme",
|
|
);
|
|
return array(
|
|
'date_nav_title' => $base + array('arguments' => array('type' => NULL, 'view' => NULL)),
|
|
'date_vcalendar' => $base + array('arguments' => array('events' => NULL, 'calname' => NULL)),
|
|
'date_vevent' => $base + array('arguments' => array('event' => NULL)),
|
|
'date_valarm' => $base + array('arguments' => array('alarm' => NULL)),
|
|
'date_timezone' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_select' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_text' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_select_element' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_textfield_element' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_date_part_hour_prefix' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_part_minsec_prefix' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_part_label_year' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_part_label_month' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_part_label_day' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_part_label_hour' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_part_label_minute' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_part_label_second' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_part_label_ampm' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_part_label_timezone' => $base + array('arguments' => array('element' => NULL)),
|
|
'date_views_filter_form' => $base + array(
|
|
'template' => 'date-views-filter-form',
|
|
'arguments' => array('form' => NULL)),
|
|
'date_calendar_day' => $base + array('arguments' => array('date' => NULL)),
|
|
'date_time_ago' => $base + array('arguments' => array('start_date' => NULL, 'end_date' => NULL, 'interval' => NULL)));
|
|
}
|
|
|
|
/**
|
|
* Wrapper around date handler setting for timezone.
|
|
*/
|
|
function date_api_set_db_timezone($offset = '+00:00') {
|
|
require_once('./'. drupal_get_path('module', 'date_api') .'/date_api_sql.inc');
|
|
$handler = new date_sql_handler();
|
|
return $handler->set_db_timezone($offset);
|
|
}
|
|
|
|
/**
|
|
* Function to figure out which local timezone applies to a date and select it
|
|
*/
|
|
function date_get_timezone($handling, $timezone = '') {
|
|
switch ($handling) {
|
|
case ('date'):
|
|
$timezone = !empty($timezone) ? $timezone : date_default_timezone_name();
|
|
break;
|
|
case ('utc'):
|
|
$timezone = 'UTC';
|
|
break;
|
|
default:
|
|
$timezone = date_default_timezone_name();
|
|
}
|
|
return $timezone > '' ? $timezone : date_default_timezone_name();
|
|
}
|
|
|
|
/**
|
|
* Function to figure out which db timezone applies to a date and select it
|
|
*/
|
|
function date_get_timezone_db($handling, $timezone = '') {
|
|
switch ($handling) {
|
|
case ('none'):
|
|
$timezone = date_default_timezone_name();
|
|
break;
|
|
default:
|
|
$timezone = 'UTC';
|
|
break;
|
|
}
|
|
return $timezone > '' ? $timezone : 'UTC';
|
|
}
|
|
|
|
/**
|
|
* Wrapper function to make sure this function will always work.
|
|
*/
|
|
function date_api_views_fetch_fields($base, $type) {
|
|
if (!module_exists('views')) {
|
|
return array();
|
|
}
|
|
require_once('./'. drupal_get_path('module', 'views') .'/includes/admin.inc');
|
|
return views_fetch_fields($base, $type);
|
|
}
|
|
|
|
/**
|
|
* Get the list of date formats for a particular format length.
|
|
*
|
|
* @param $type
|
|
* The format type: 'short', 'medium', 'long', 'custom'. If empty, then all
|
|
* available formats will be returned.
|
|
* @param $reset
|
|
* Whether or not to reset this function's internal cache (defaults to FALSE).
|
|
* @return
|
|
* Array of date formats.
|
|
*/
|
|
function date_get_formats($type = NULL, $reset = FALSE) {
|
|
static $_date_formats;
|
|
|
|
if ($reset || !isset($_date_formats)) {
|
|
$_date_formats = _date_formats_build();
|
|
}
|
|
|
|
return $type ? (isset($_date_formats[$type]) ? $_date_formats[$type] : FALSE) : $_date_formats;
|
|
}
|
|
|
|
/**
|
|
* Get the format details for a particular id.
|
|
*
|
|
* @param $dfid
|
|
* Identifier of a date format string.
|
|
* @return
|
|
* Array of date format details.
|
|
*/
|
|
function date_get_format($dfid) {
|
|
$result = db_query('SELECT df.dfid, df.format, df.type, df.locked FROM {date_formats} df WHERE df.dfid = %d', $dfid);
|
|
return db_fetch_array($result);
|
|
}
|
|
|
|
/**
|
|
* Get the list of available date format types and attributes.
|
|
*
|
|
* @param $type
|
|
* The format type, e.g. 'short', 'medium', 'long', 'custom'. If empty, then
|
|
* all attributes for that type will be returned.
|
|
* @param $reset
|
|
* Whether or not to reset this function's internal cache (defaults to FALSE).
|
|
* @return
|
|
* Array of date format types.
|
|
*/
|
|
function date_get_format_types($type = NULL, $reset = FALSE) {
|
|
static $_date_format_types;
|
|
|
|
if ($reset || !isset($_date_format_types)) {
|
|
$_date_format_types = _date_format_types_build();
|
|
}
|
|
|
|
return $type ? (isset($_date_format_types[$type]) ? $_date_format_types[$type] : FALSE) : $_date_format_types;
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_flush_caches().
|
|
*/
|
|
function date_api_flush_caches() {
|
|
// Rebuild list of date formats.
|
|
date_formats_rebuild();
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Resets the database cache of date formats, and saves all new date formats to
|
|
* the database.
|
|
*/
|
|
function date_formats_rebuild() {
|
|
$date_formats = date_get_formats(NULL, TRUE);
|
|
|
|
foreach ($date_formats as $format_type => $formats) {
|
|
foreach ($formats as $format => $info) {
|
|
date_format_save($info);
|
|
}
|
|
}
|
|
|
|
// Rebuild configured date formats locale list.
|
|
date_format_locale(NULL, NULL, TRUE);
|
|
|
|
_date_formats_build();
|
|
}
|
|
|
|
/**
|
|
* Save a date format type to the database.
|
|
*
|
|
* @param $date_format_type
|
|
* An array of attributes for a date format type.
|
|
*/
|
|
function date_format_type_save($date_format_type) {
|
|
$type = array();
|
|
$type['type'] = $date_format_type['type'];
|
|
$type['title'] = $date_format_type['title'];
|
|
$type['locked'] = $date_format_type['locked'];
|
|
|
|
// Update date_format table.
|
|
if (isset($date_format_type['is_new']) && !empty($date_format_type['is_new'])) {
|
|
drupal_write_record('date_format_types', $type);
|
|
}
|
|
else {
|
|
drupal_write_record('date_format_types', $type, 'type');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a date format type from the database.
|
|
*
|
|
* @param $date_format_type
|
|
* The date format type name.
|
|
*/
|
|
function date_format_type_delete($date_format_type) {
|
|
db_query("DELETE FROM {date_formats} WHERE type = '%s'", $date_format_type);
|
|
db_query("DELETE FROM {date_format_types} WHERE type = '%s'", $date_format_type);
|
|
db_query("DELETE FROM {date_format_locale} WHERE type = '%s'", $date_format_type);
|
|
}
|
|
|
|
/**
|
|
* Save a date format to the database.
|
|
*
|
|
* @param $date_format
|
|
* An array of attributes for a date format.
|
|
*/
|
|
function date_format_save($date_format) {
|
|
$format = array();
|
|
$format['type'] = $date_format['type'];
|
|
$format['format'] = $date_format['format'];
|
|
$format['locked'] = $date_format['locked'];
|
|
|
|
// Update date_format table.
|
|
if (isset($date_format['is_new']) && !empty($date_format['is_new'])) {
|
|
drupal_write_record('date_formats', $format);
|
|
}
|
|
else {
|
|
drupal_write_record('date_formats', $format, array('format', 'type'));
|
|
}
|
|
|
|
$languages = language_list('enabled');
|
|
$languages = $languages[1];
|
|
// If site_country module is enabled, add country specific languages to
|
|
// languages array.
|
|
if (module_exists('site_country')) {
|
|
$country_code = variable_get('site_country_default_country', '');
|
|
if (!empty($country_code)) {
|
|
foreach ($languages as $langcode => $details) {
|
|
$country_language = $langcode . '-' . $country_code;
|
|
if (drupal_strlen($langcode) == 2 && !in_array($country_language, array_keys($languages))) {
|
|
$name = $details->name;
|
|
$languages[$country_language] = "$name ($country_code)";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$locale_format = array();
|
|
$locale_format['type'] = $date_format['type'];
|
|
$locale_format['format'] = $date_format['format'];
|
|
|
|
// Check if the suggested language codes are configured and enabled.
|
|
if (!empty($date_format['locales'])) {
|
|
foreach ($date_format['locales'] as $langcode) {
|
|
// Only proceed if language is enabled.
|
|
if (in_array($langcode, $languages)) {
|
|
$is_existing = db_result(db_query("SELECT COUNT(*) FROM {date_format_locale} WHERE type = '%s' AND language = '%s'", $date_format['type'], $langcode));
|
|
if (!$is_existing) {
|
|
$locale_format['language'] = $langcode;
|
|
drupal_write_record('date_format_locale', $locale_format);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a date format from the database.
|
|
*
|
|
* @param $date_format_id
|
|
* The date format string identifier.
|
|
*/
|
|
function date_format_delete($date_format_id) {
|
|
db_query("DELETE FROM {date_formats} WHERE dfid = '%d'", $date_format_id);
|
|
}
|
|
|
|
/**
|
|
* Builds and returns the list of available date format types.
|
|
*
|
|
* @return
|
|
* Array of date format types.
|
|
*/
|
|
function _date_format_types_build() {
|
|
$types = array();
|
|
|
|
// Prevent errors in the upgrade before the date_format_types table exists.
|
|
if (defined('MAINTENANCE_MODE') && !db_table_exists('date_format_types')) {
|
|
return $types;
|
|
}
|
|
|
|
// Get list of modules which implement hook_date_format_types().
|
|
$modules = module_implements('date_format_types');
|
|
|
|
foreach ($modules as $module) {
|
|
$module_types = module_invoke($module, 'date_format_types');
|
|
foreach ($module_types as $module_type => $type_title) {
|
|
$type = array();
|
|
$type['module'] = $module;
|
|
$type['type'] = $module_type;
|
|
$type['title'] = $type_title;
|
|
$type['locked'] = 1;
|
|
$type['is_new'] = TRUE; // Will be over-ridden later if in the db.
|
|
$types[$module_type] = $type;
|
|
}
|
|
}
|
|
|
|
// Get custom formats added to the database by the end user.
|
|
$result = db_query('SELECT dft.type, dft.title, dft.locked FROM {date_format_types} dft ORDER BY dft.title');
|
|
while ($object = db_fetch_object($result)) {
|
|
if (!in_array($object->type, $types)) {
|
|
$type = array();
|
|
$type['is_new'] = FALSE;
|
|
$type['module'] = '';
|
|
$type['type'] = $object->type;
|
|
$type['title'] = $object->title;
|
|
$type['locked'] = $object->locked;
|
|
$types[$object->type] = $type;
|
|
}
|
|
else {
|
|
$type = array();
|
|
$type['is_new'] = FALSE; // Over-riding previous setting.
|
|
$types[$object->type] = array_merge($types[$object->type], $type);
|
|
}
|
|
}
|
|
|
|
// Allow other modules to modify these format types.
|
|
drupal_alter('date_format_types', $types);
|
|
|
|
return $types;
|
|
}
|
|
|
|
/**
|
|
* Builds and returns the list of available date formats.
|
|
*
|
|
* @return
|
|
* Array of date formats.
|
|
*/
|
|
function _date_formats_build() {
|
|
$date_formats = array();
|
|
|
|
// Prevent errors in the upgrade before the date_format table exists.
|
|
if (defined('MAINTENANCE_MODE') && !db_table_exists('date_format')) {
|
|
return $date_formats;
|
|
}
|
|
|
|
// First handle hook_date_format_types().
|
|
$types = _date_format_types_build();
|
|
foreach ($types as $type => $info) {
|
|
date_format_type_save($info);
|
|
}
|
|
|
|
// Get formats supplied by various contrib modules.
|
|
$module_formats = module_invoke_all('date_formats');
|
|
|
|
foreach ($module_formats as $module_format) {
|
|
$module_format['locked'] = 1; // System types are locked.
|
|
// If no format type is specified, assign 'custom'.
|
|
if (!isset($module_format['type'])) {
|
|
$module_format['type'] = 'custom';
|
|
}
|
|
if (!in_array($module_format['type'], array_keys($types))) {
|
|
continue;
|
|
}
|
|
if (!isset($date_formats[$module_format['type']])) {
|
|
$date_formats[$module_format['type']] = array();
|
|
}
|
|
|
|
// If another module already set this format, merge in the new settings.
|
|
if (isset($date_formats[$module_format['type']][$module_format['format']])) {
|
|
$date_formats[$module_format['type']][$module_format['format']] = array_merge_recursive($date_formats[$module_format['type']][$module_format['format']], $format);
|
|
}
|
|
else {
|
|
// This setting will be overridden later if it already exists in the db.
|
|
$module_format['is_new'] = TRUE;
|
|
$date_formats[$module_format['type']][$module_format['format']] = $module_format;
|
|
}
|
|
}
|
|
|
|
// Get custom formats added to the database by the end user.
|
|
$result = db_query('SELECT df.dfid, df.format, df.type, df.locked, dfl.language FROM {date_formats} df LEFT JOIN {date_format_types} dft ON df.type = dft.type LEFT JOIN {date_format_locale} dfl ON df.format = dfl.format AND df.type = dfl.type ORDER BY df.type, df.format');
|
|
while ($object = db_fetch_object($result)) {
|
|
// If this format type isn't set, initialise the array.
|
|
if (!isset($date_formats[$object->type])) {
|
|
$date_formats[$object->type] = array();
|
|
}
|
|
// If this format not already present, add it to the array.
|
|
if (!isset($date_formats[$object->type][$object->format])) {
|
|
// We don't set 'is_new' as it is already in the db.
|
|
$format = array();
|
|
$format['module'] = '';
|
|
$format['dfid'] = $object->dfid;
|
|
$format['format'] = $object->format;
|
|
$format['type'] = $object->type;
|
|
$format['locked'] = $object->locked;
|
|
$format['locales'] = array($object->language);
|
|
$date_formats[$object->type][$object->format] = $format;
|
|
}
|
|
// Format already present, so merge in settings.
|
|
else {
|
|
$format = array();
|
|
$format['is_new'] = FALSE; // It's in the db, so override this setting.
|
|
$format['dfid'] = $object->dfid;
|
|
$format['format'] = $object->format;
|
|
$format['type'] = $object->type;
|
|
$format['locked'] = $object->locked;
|
|
if (!empty($object->language)) {
|
|
$format['locales'] = array_merge($date_formats[$object->type][$object->format]['locales'], array($object->language));
|
|
}
|
|
$date_formats[$object->type][$object->format] = array_merge($date_formats[$object->type][$object->format], $format);
|
|
}
|
|
}
|
|
|
|
// Allow other modules to modify these formats.
|
|
drupal_alter('date_formats', $date_formats);
|
|
|
|
return $date_formats;
|
|
}
|
|
|
|
/**
|
|
* Get the appropriate date format for a type and locale.
|
|
*
|
|
* @param $langcode
|
|
* Language code for the current locale. This can be a 2 character language
|
|
* code like 'en', 'fr', or a longer 5 character code like 'en-gb'.
|
|
* @param $type
|
|
* Date format type: short, medium, long, custom.
|
|
* @param $reset
|
|
* Whether or not to reset this function's internal cache (defaults to FALSE).
|
|
* @return
|
|
* The format string, or NULL if no matching format found.
|
|
*/
|
|
function date_format_locale($langcode = NULL, $type = NULL, $reset = FALSE) {
|
|
static $formats;
|
|
|
|
if ($reset || empty($formats)) {
|
|
$formats = array();
|
|
$result = db_query("SELECT format, type, language FROM {date_format_locale}");
|
|
while ($object = db_fetch_object($result)) {
|
|
if (!isset($formats[$object->language])) {
|
|
$formats[$object->language] = array();
|
|
}
|
|
$formats[$object->language][$object->type] = $object->format;
|
|
}
|
|
}
|
|
|
|
if ($type && $langcode && !empty($formats[$langcode][$type])) {
|
|
return $formats[$langcode][$type];
|
|
}
|
|
elseif ($langcode && !empty($formats[$langcode])) {
|
|
return $formats[$langcode];
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Helper function for BYDAY options in Date Repeat
|
|
* and for converting back and forth from '+1' to 'First'.
|
|
*/
|
|
function date_order_translated() {
|
|
return array(
|
|
'+1' => date_t('First', 'date_order'),
|
|
'+2' => date_t('Second', 'date_order'),
|
|
'+3' => date_t('Third', 'date_order'),
|
|
'+4' => date_t('Fourth', 'date_order'),
|
|
'+5' => date_t('Fifth', 'date_order'),
|
|
'-1' => date_t('Last', 'date_order_reverse'),
|
|
'-2' => date_t('Next to last', 'date_order_reverse'),
|
|
'-3' => date_t('Third from last', 'date_order_reverse'),
|
|
'-4' => date_t('Fourth from last', 'date_order_reverse'),
|
|
'-5' => date_t('Fifth from last', 'date_order_reverse')
|
|
);
|
|
}
|
|
|
|
function date_order() {
|
|
return array(
|
|
'+1' => 'First',
|
|
'+2' => 'Second',
|
|
'+3' => 'Third',
|
|
'+4' => 'Fourth',
|
|
'+5' => 'Fifth',
|
|
'-1' => 'Last',
|
|
'-2' => '-2',
|
|
'-3' => '-3',
|
|
'-4' => '-4',
|
|
'-5' => '-5'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_views_api().
|
|
*
|
|
* This one is used as the base to reduce errors when updating.
|
|
*/
|
|
function date_api_views_api() {
|
|
return array(
|
|
'api' => 2,
|
|
'path' => drupal_get_path('module', 'date_api') .'/includes',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_date_api_fields().
|
|
* on behalf of core fields.
|
|
*
|
|
* All modules that create custom fields that use the
|
|
* 'views_handler_field_date' handler can provide
|
|
* additional information here about the type of
|
|
* date they create so the date can be used by
|
|
* the Date API views date argument and date filter.
|
|
*/
|
|
function date_api_date_api_fields($field) {
|
|
$values = array(
|
|
// The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME.
|
|
'sql_type' => DATE_UNIX,
|
|
// Timezone handling options: 'none', 'site', 'date', 'utc'.
|
|
'tz_handling' => 'site',
|
|
// Needed only for dates that use 'date' tz_handling.
|
|
'timezone_field' => '',
|
|
// Needed only for dates that use 'date' tz_handling.
|
|
'offset_field' => '',
|
|
// Array of "table.field" values for related fields that should be
|
|
// loaded automatically in the Views SQL.
|
|
'related_fields' => array(),
|
|
// Granularity of this date field's db data.
|
|
'granularity' => array('year', 'month', 'day', 'hour', 'minute', 'second'),
|
|
);
|
|
|
|
switch ($field) {
|
|
case 'users.created':
|
|
case 'users.access':
|
|
case 'users.login':
|
|
case 'node.created':
|
|
case 'node.changed':
|
|
case 'node_revisions.timestamp':
|
|
case 'files.timestamp':
|
|
case 'node_counter.timestamp':
|
|
case 'accesslog.timestamp':
|
|
case 'comments.timestamp':
|
|
case 'node_comment_statistics.last_comment_timestamp':
|
|
return $values;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rebuild the theme registry and all the caches.
|
|
* needed to pick up changes created by updated Views API
|
|
* and other changes to Views definitions.
|
|
*/
|
|
function date_api_views_clear() {
|
|
if (db_table_exists('cache_content')) {
|
|
db_query('DELETE FROM {cache_content}');
|
|
}
|
|
if (db_table_exists('cache_views')) {
|
|
db_query('DELETE FROM {cache_views}');
|
|
}
|
|
if (db_table_exists('views_object_cache')) {
|
|
db_query('DELETE FROM {views_object_cache}');
|
|
}
|
|
db_query("DELETE FROM {cache} where cid LIKE 'theme_registry%'");
|
|
}
|
|
|
|
/**
|
|
* Embed a view using a PHP snippet.
|
|
*
|
|
* This function is meant to be called from PHP snippets, should one wish to
|
|
* embed a view in a node or something. It's meant to provide the simplest
|
|
* solution and doesn't really offer a lot of options, but breaking the function
|
|
* apart is pretty easy, and this provides a worthwhile guide to doing so.
|
|
*
|
|
* Note that this function does NOT display the title of the view. If you want
|
|
* to do that, you will need to do what this function does manually, by
|
|
* loading the view, getting the preview and then getting $view->get_title().
|
|
*
|
|
* @param $name
|
|
* The name of the view to embed.
|
|
*
|
|
* @param $display_id
|
|
* 'calendar_1' will display the calendar page,
|
|
* 'calendar_block_1' will display the calendar block.
|
|
*
|
|
* @param $settings
|
|
* an array of view settings to use to override view default values;
|
|
*
|
|
* Include a setting for 'block_identifier, the identifier to use
|
|
* for this embedded view. All embedded views that use the same
|
|
* identifier will move together, or provide different identifiers
|
|
* to keep them independent. The identifier will be used in the url
|
|
* as a querystring, like: node/27?mini=calendar/2008-10.
|
|
*
|
|
* @param ...
|
|
* Any additional parameters will be passed as arguments.
|
|
*/
|
|
function date_embed_view($name, $display_id = 'default', $settings = array(), $args = array()) {
|
|
$view = views_get_view($name);
|
|
if (!$view) {
|
|
return;
|
|
}
|
|
if (!empty($settings)) {
|
|
foreach ($settings as $key => $setting) {
|
|
$view->$key = $setting;
|
|
}
|
|
}
|
|
if (!isset($view->date_info->block_identifier)) {
|
|
$view->date_info->block_identifier = 'mini';
|
|
}
|
|
return $view->preview($display_id, $args);
|
|
}
|
|
|
|
/**
|
|
* Figure out the URL of the date view we're currently looking at,
|
|
* adapted to various date types or specific date arguments.
|
|
*
|
|
* @param $date_type
|
|
* - if not empty, return the url of a specific date type.
|
|
* @param $date_arg
|
|
* - if not empty, return the url for a view with a specific date argument.
|
|
* @param $force_view_url
|
|
* - always use the view url, even if embedded.
|
|
* @return
|
|
* return the requested view url.
|
|
*/
|
|
function date_real_url($view, $date_type = NULL, $date_arg = NULL, $force_view_url = FALSE) {
|
|
$args = $view->args;
|
|
$pos = $view->date_info->date_arg_pos;
|
|
|
|
// The View arguments array is indexed numerically but is not necessarily
|
|
// in numerical order. Sort the arguments to ensure the correct order.
|
|
ksort($args);
|
|
|
|
// If there are empty arguments before the date argument,
|
|
// pad them with the wildcard so the date argument will be in
|
|
// the right position.
|
|
if (count($args) < $pos) {
|
|
foreach ($view->argument as $name => $argument) {
|
|
if ($argument->position == $pos) {
|
|
break;
|
|
}
|
|
$args[] = $argument->options['wildcard'];
|
|
}
|
|
}
|
|
|
|
if (!empty($date_type)) {
|
|
switch ($date_type) {
|
|
case 'year':
|
|
$args[$pos] = date_pad($view->date_info->year, 4);
|
|
break;
|
|
case 'week':
|
|
$args[$pos] = date_pad($view->date_info->year, 4) .'-W'. date_pad($view->date_info->week);
|
|
break;
|
|
case 'day':
|
|
$args[$pos] = date_pad($view->date_info->year, 4) .'-'. date_pad($view->date_info->month) .'-'. date_pad($view->date_info->day);
|
|
break;
|
|
default:
|
|
$args[$pos] = date_pad($view->date_info->year, 4) .'-'. date_pad($view->date_info->month);
|
|
break;
|
|
}
|
|
}
|
|
elseif (!empty($date_arg)) {
|
|
$args[$pos] = $date_arg;
|
|
}
|
|
else {
|
|
$args = $view->args;
|
|
}
|
|
// Is this an embedded or a block view?
|
|
if (!$force_view_url &&
|
|
(!empty($view->preview) || !empty($view->date_info->block_identifier))) {
|
|
$url = $view->get_url($args);
|
|
$key = date_block_identifier($view);
|
|
if (!empty($key)) {
|
|
return url($_GET['q'], array(
|
|
'query' => date_querystring($view, array($key => $url)),
|
|
'absolute' => TRUE));
|
|
}
|
|
}
|
|
// Normal views may need querystrings appended to them
|
|
// if they use exposed filters.
|
|
return url($view->get_url($args), array(
|
|
'query' => date_querystring($view),
|
|
'absolute' => TRUE));
|
|
}
|
|
|
|
/**
|
|
* Pick up filter and sort info from url.
|
|
*/
|
|
function date_querystring($view, $extra_params = array()) {
|
|
$query_params = array_merge($_GET, $extra_params);
|
|
// Allow NULL params to be removed from the query string.
|
|
foreach ($extra_params AS $key => $value) {
|
|
if (!isset($value)) {
|
|
unset($query_params[$key]);
|
|
}
|
|
}
|
|
// Filter the special "q" and "view" variables out of the query string.
|
|
$exclude = array('q');
|
|
$query = drupal_query_string_encode($query_params, $exclude);
|
|
// To prevent an empty query string from adding a "?" on to the end of a URL,
|
|
// we return NULL.
|
|
return !empty($query) ? $query : NULL;
|
|
}
|
|
|
|
function date_block_identifier($view) {
|
|
if (!empty($view->block_identifier)) {
|
|
return $view->block_identifier;
|
|
}
|
|
return isset($view->date_info->block_identifier) ? $view->date_info->block_identifier : NULL;
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_form_alter().
|
|
*
|
|
* Add new submit handler for system_modules form.
|
|
*/
|
|
function date_api_form_system_modules_alter(&$form, $form_state, $form_id = 'system_modules') {
|
|
$form['#submit'][] = 'date_api_system_modules_submit';
|
|
}
|
|
|
|
/**
|
|
* Rebuild list of date formats when modules list is saved.
|
|
*/
|
|
function date_api_system_modules_submit($form, &$form_state) {
|
|
date_formats_rebuild();
|
|
}
|
|
|
|
/**
|
|
* Implementation of hook_form_alter().
|
|
*
|
|
* Remove the 'date_formats' section from the 'admin/settings/date-time' page.
|
|
* This form section is now part of the form at 'admin/settings/date-time/formats'.
|
|
* We add the formats as values to the form to avoid errors on submission
|
|
* of the form when expected values are missing in system_date_time_settings_submit().
|
|
*
|
|
* Add a form element to configure whether or not week numbers are ISO-8601 (default: FALSE == US/UK/AUS norm).
|
|
*/
|
|
function date_api_form_system_date_time_settings_alter(&$form, $form_state, $form_id = 'system_date_time_settings') {
|
|
include_once(drupal_get_path('module', 'date_api') .'/date_api.admin.inc');
|
|
$formats_form = date_api_date_formats_form($form_state);
|
|
$form['date_formats'] = $formats_form['date_formats'];
|
|
foreach ($form['date_formats'] as $key => $value) {
|
|
if (drupal_substr($key, 0, 1) != '#') {
|
|
$form['date_formats'][$key]['#type'] = 'value';
|
|
}
|
|
else {
|
|
unset($form['date_formats'][$key]);
|
|
}
|
|
}
|
|
$form['locale']['date_api_use_iso8601'] = array(
|
|
'#type' => 'checkbox',
|
|
'#title' => t('Use ISO-8601 week numbers'),
|
|
'#default_value' => variable_get('date_api_use_iso8601', FALSE),
|
|
'#description' => t('IMPORTANT! If checked, First day of week MUST be set to Monday'),
|
|
);
|
|
$form['#validate'][] = 'date_api_form_system_settings_validate';
|
|
}
|
|
|
|
/**
|
|
* Validate that the option to use ISO weeks matches first day of week choice.
|
|
*/
|
|
function date_api_form_system_settings_validate(&$form, &$form_state) {
|
|
$form_values = $form_state['values'];
|
|
if ($form_values['date_api_use_iso8601'] && $form_values['date_first_day'] != 1) {
|
|
form_set_error('date_first_day', t('When using ISO-8601 week numbers, the first day of the week must be set to Monday.'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper function; add system.js and javascript settings.
|
|
*/
|
|
function date_api_add_system_javascript() {
|
|
drupal_add_js(drupal_get_path('module', 'date_api') .'/date_api.js', 'module');
|
|
drupal_add_js(array('dateDateTime' => array('lookup' => url('admin/settings/date-time/formats/lookup'))), 'setting');
|
|
}
|
|
|
|
/**
|
|
* Return the date for a given format string via Ajax.
|
|
*/
|
|
function date_api_date_time_lookup() {
|
|
$result = date_format_date(date_now(), 'custom', $_GET['format']);
|
|
echo drupal_to_js($result);
|
|
exit;
|
|
}
|
|
|
|
/*
|
|
* Test validity of a date range string.
|
|
*/
|
|
function date_range_valid($string) {
|
|
$matches = preg_match('@^(\-[0-9]+|[0-9]{4}):([\+|\-][0-9]+|[0-9]{4})$@', $string);
|
|
return $matches < 1 ? FALSE : TRUE;
|
|
}
|
|
|
|
/**
|
|
* Split a string like -3:+3 or 2001:2010 into
|
|
* an array of min and max years.
|
|
*
|
|
* Center the range around the current year, if any, but expand it far
|
|
* enough so it will pick up the year value in the field in case
|
|
* the value in the field is outside the initial range.
|
|
*/
|
|
function date_range_years($string, $date = NULL) {
|
|
$this_year = date_format(date_now(), 'Y');
|
|
list($min_year, $max_year) = explode(':', $string);
|
|
|
|
// Valid patterns would be -5:+5, 0:+1, 2008:2010.
|
|
$plus_pattern = '@[\+|\-][0-9]{1,4}@';
|
|
$year_pattern = '@[0-9]{4}@';
|
|
if (!preg_match($year_pattern, $min_year, $matches)) {
|
|
if (preg_match($plus_pattern, $min_year, $matches)) {
|
|
$min_year = $this_year + $matches[0];
|
|
}
|
|
else {
|
|
$min_year = $this_year;
|
|
}
|
|
}
|
|
if (!preg_match($year_pattern, $max_year, $matches)) {
|
|
if (preg_match($plus_pattern, $max_year, $matches)) {
|
|
$max_year = $this_year + $matches[0];
|
|
}
|
|
else {
|
|
$max_year = $this_year;
|
|
}
|
|
}
|
|
// We expect the $min year to be less than the $max year.
|
|
// Some custom values for -99:+99 might not obey that.
|
|
if ($min_year > $max_year) {
|
|
$temp = $max_year;
|
|
$max_year = $min_year;
|
|
$min_year = $temp;
|
|
}
|
|
// If there is a current value, stretch the range to include it.
|
|
$value_year = is_object($date) ? date_format($date, 'Y') : '';
|
|
if (!empty($value_year)) {
|
|
$min_year = min($value_year, $min_year);
|
|
$max_year = max($value_year, $max_year);
|
|
}
|
|
return array($min_year, $max_year);
|
|
}
|
|
|
|
/**
|
|
* Convert a min and max year into a string like '-3:+1'.
|
|
*
|
|
* @param unknown_type $years
|
|
* @return unknown
|
|
*/
|
|
function date_range_string($years) {
|
|
$this_year = date_format(date_now(), 'Y');
|
|
if ($years[0] < $this_year) {
|
|
$min = '-'. ($this_year - $years[0]);
|
|
}
|
|
else {
|
|
$min = '+'. ($years[0] - $this_year);
|
|
}
|
|
if ($years[1] < $this_year) {
|
|
$max = '-'. ($this_year - $years[1]);
|
|
}
|
|
else {
|
|
$max = '+'. ($years[1] - $this_year);
|
|
}
|
|
return $min .':'. $max;
|
|
}
|
|
|
|
/**
|
|
* Implement hook_date_api_tables().
|
|
*/
|
|
function date_api_date_api_tables() {
|
|
return array('node', 'comments', 'users');
|
|
}
|
|
|
|
/**
|
|
* Determine if a from/to date combination qualify as 'All day'.
|
|
*
|
|
* @param object $date1, a string date in datetime format for the 'from' date.
|
|
* @param object $date2, a string date in datetime format for the 'to' date.
|
|
* @return TRUE or FALSE.
|
|
*/
|
|
function date_is_all_day($string1, $string2, $granularity = 'second', $increment = 1) {
|
|
if (empty($string1) || empty($string2)) {
|
|
return FALSE;
|
|
}
|
|
elseif (!in_array($granularity, array('hour', 'minute', 'second'))) {
|
|
return FALSE;
|
|
}
|
|
|
|
preg_match('/([0-9]{4}-[0-9]{2}-[0-9]{2}) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/', $string1, $matches);
|
|
$count = count($matches);
|
|
$date1 = $count > 1 ? $matches[1] : '';
|
|
$time1 = $count > 2 ? $matches[2] : '';
|
|
$hour1 = $count > 3 ? intval($matches[3]) : 0;
|
|
$min1 = $count > 4 ? intval($matches[4]) : 0;
|
|
$sec1 = $count > 5 ? intval($matches[5]) : 0;
|
|
preg_match('/([0-9]{4}-[0-9]{2}-[0-9]{2}) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/', $string2, $matches);
|
|
$count = count($matches);
|
|
$date2 = $count > 1 ? $matches[1] : '';
|
|
$time2 = $count > 2 ? $matches[2] : '';
|
|
$hour2 = $count > 3 ? intval($matches[3]) : 0;
|
|
$min2 = $count > 4 ? intval($matches[4]) : 0;
|
|
$sec2 = $count > 5 ? intval($matches[5]) : 0;
|
|
if (empty($date1) || empty($date2)) {
|
|
return FALSE;
|
|
}
|
|
if (empty($time1) || empty($time2)) {
|
|
return FALSE;
|
|
}
|
|
|
|
$tmp = date_seconds('s', TRUE, $increment);
|
|
$max_seconds = intval(array_pop($tmp));
|
|
$tmp = date_minutes('i', TRUE, $increment);
|
|
$max_minutes = intval(array_pop($tmp));
|
|
|
|
// See if minutes and seconds are the maximum allowed for an increment or the
|
|
// maximum possible (59), or 0.
|
|
switch ($granularity) {
|
|
case 'second':
|
|
$min_match = $time1 == '00:00:00'
|
|
|| ($hour1 == 0 && $min1 == 0 && $sec1 == 0);
|
|
$max_match = $time2 == '00:00:00'
|
|
|| ($hour2 == 23 && in_array($min2, array($max_minutes, 59)) && in_array($sec2, array($max_seconds, 59)))
|
|
|| ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0 && $sec1 == 0 && $sec2 == 0);
|
|
break;
|
|
case 'minute':
|
|
$min_match = $time1 == '00:00:00'
|
|
|| ($hour1 == 0 && $min1 == 0);
|
|
$max_match = $time2 == '00:00:00'
|
|
|| ($hour2 == 23 && in_array($min2, array($max_minutes, 59)))
|
|
|| ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0);
|
|
break;
|
|
case 'hour':
|
|
$min_match = $time1 == '00:00:00'
|
|
|| ($hour1 == 0);
|
|
$max_match = $time2 == '00:00:00'
|
|
|| ($hour2 == 23)
|
|
|| ($hour1 == 0 && $hour2 == 0);
|
|
break;
|
|
default:
|
|
$min_match = TRUE;
|
|
$max_match = FALSE;
|
|
}
|
|
|
|
if ($min_match && $max_match) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper function to round minutes and seconds to requested value.
|
|
*/
|
|
function date_increment_round(&$date, $increment) {
|
|
// Round minutes and seconds, if necessary.
|
|
if (is_object($date) && $increment > 1) {
|
|
$day = intval(date_format($date, 'j'));
|
|
$hour = intval(date_format($date, 'H'));
|
|
$second = intval(round(intval(date_format($date, 's')) / $increment) * $increment);
|
|
$minute = intval(date_format($date, 'i'));
|
|
if ($second == 60) {
|
|
$minute += 1;
|
|
$second = 0;
|
|
}
|
|
$minute = intval(round($minute / $increment) * $increment);
|
|
if ($minute == 60) {
|
|
$hour += 1;
|
|
$minute = 0;
|
|
}
|
|
date_time_set($date, $hour, $minute, $second);
|
|
if ($hour == 24) {
|
|
$day += 1;
|
|
$hour = 0;
|
|
$year = date_format($date, 'Y');
|
|
$month = date_format($date, 'n');
|
|
date_date_set($date, $year, $month, $day);
|
|
}
|
|
}
|
|
return $date;
|
|
}
|
|
|
|
/**
|
|
* Return the nested form elements for a field by name.
|
|
* This can be used either to retrieve the entire sub-element
|
|
* for a field by name, no matter how deeply nested it is within
|
|
* fieldgroups or multigroups, or to find the multiple value
|
|
* sub-elements within a field element by name (i.e. 'value' or
|
|
* 'rrule'). You can also use this function to see if an item exists
|
|
* in a form (the return will be an empty array if it does not exist).
|
|
*
|
|
* The function returns an array of results. A field will generally
|
|
* only exist once in a form but the function can also be used to
|
|
* locate all the 'value' elements within a multiple value field,
|
|
* so the result is always returned as an array of values.
|
|
*
|
|
* For example, for a field named field_custom, the following will
|
|
* pluck out the form elements for this field from the node form,
|
|
* no matter how deeply it is nested within fieldgroups or fieldsets:
|
|
*
|
|
* $elements = content_get_nested_elements($node_form, 'field_custom');
|
|
*
|
|
* You can prefix the function with '&' to retrieve the element by
|
|
* reference to alter it directly:
|
|
*
|
|
* $elements = &content_get_nested_elements($form, 'field_custom');
|
|
* foreach ($elements as $element) {
|
|
* $element['#after_build'][] = 'my_field_afterbuild';
|
|
* }
|
|
*
|
|
* During the #after_build you could then do something like the
|
|
* following to alter each individual part of a multiple value field:
|
|
*
|
|
* $sub_elements = &content_get_nested_elements($element, 'value');
|
|
* foreach ($sub_elements as $sub_element) {
|
|
* $sub_element['#element_validate'][] = 'custom_validation';
|
|
* }
|
|
*
|
|
* @param $form
|
|
* The form array to search.
|
|
* @param $field_name
|
|
* The name or key of the form elements to return.
|
|
* @return
|
|
* An array of all matching form elements, returned by reference.
|
|
*/
|
|
function &date_get_nested_elements(&$form, $field_name) {
|
|
$elements = array();
|
|
|
|
foreach (element_children($form) as $key) {
|
|
if ($key === $field_name) {
|
|
$elements[] = &$form[$key];
|
|
}
|
|
elseif (is_array($form[$key])) {
|
|
$nested_form = &$form[$key];
|
|
if ($sub_elements = &date_get_nested_elements($nested_form, $field_name)) {
|
|
$elements = array_merge($elements, $sub_elements);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $elements;
|
|
}
|