source: sipes/modules_contrib/date/date_api.module @ 8a8efa8

stableversion-3.0
Last change on this file since 8a8efa8 was f3eebcf, checked in by José Gregorio Puentes <jpuentes@…>, 8 años ago

se actualizo el modulo

  • Propiedad mode establecida a 100755
File size: 91.3 KB
Línea 
1<?php
2/**
3 * @file
4 * This module will make the date API available to other modules.
5 * Designed to provide a light but flexible assortment of functions
6 * and constants, with more functionality in additional files that
7 * are not loaded unless other modules specifically include them.
8 */
9
10/**
11 * Set up some constants.
12 *
13 * Includes standard date types, format strings, strict regex strings for ISO
14 * and DATETIME formats (seconds are optional).
15 *
16 * The loose regex will find any variety of ISO date and time, with or
17 * without time, with or without dashes and colons separating the elements,
18 * and with either a 'T' or a space separating date and time.
19 */
20define('DATE_ISO',  'date');
21define('DATE_UNIX', 'datestamp');
22define('DATE_DATETIME', 'datetime');
23define('DATE_ARRAY', 'array');
24define('DATE_OBJECT', 'object');
25define('DATE_ICAL', 'ical');
26
27define('DATE_FORMAT_ISO', "Y-m-d\TH:i:s");
28define('DATE_FORMAT_UNIX', "U");
29define('DATE_FORMAT_DATETIME', "Y-m-d H:i:s");
30define('DATE_FORMAT_ICAL', "Ymd\THis");
31define('DATE_FORMAT_ICAL_DATE', "Ymd");
32define('DATE_FORMAT_DATE', 'Y-m-d');
33
34define('DATE_REGEX_ISO', '/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):?(\d{2})?/');
35define('DATE_REGEX_DATETIME', '/(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):?(\d{2})?/');
36define('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})?)?/');
37define('DATE_REGEX_ICAL_DATE', '/(\d{4})(\d{2})(\d{2})/');
38define('DATE_REGEX_ICAL_DATETIME', '/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?/');
39
40/**
41 * Implementation of hook_init().
42 */
43function date_api_init() {
44  if (function_exists('date_default_timezone_set')) {
45    date_default_timezone_set(date_default_timezone_name());
46  }
47  drupal_add_css(drupal_get_path('module', 'date_api')  .'/date.css');
48
49}
50
51/**
52 * Implementation of hook_menu().
53 */
54function date_api_menu() {
55  $items = array();
56  $items['admin/settings/date-time/formats'] = array(
57    'title' => 'Formats',
58    'description' => 'Allow users to configure date formats',
59    'type' => MENU_LOCAL_TASK,
60    'file' => 'date_api.admin.inc',
61    'page callback' => 'drupal_get_form',
62    'page arguments' => array('date_api_date_formats_form'),
63    'access arguments' => array('administer site configuration'),
64    'weight' => 1,
65  );
66  $items['admin/settings/date-time/formats/configure'] = array(
67    'title' => 'Configure',
68    'description' => 'Allow users to configure date formats',
69    'type' => MENU_DEFAULT_LOCAL_TASK,
70    'file' => 'date_api.admin.inc',
71    'page callback' => 'drupal_get_form',
72    'page arguments' => array('date_api_date_formats_form'),
73    'access arguments' => array('administer site configuration'),
74    'weight' => 1,
75  );
76  $items['admin/settings/date-time/formats/lookup'] = array(
77    'title' => 'Date and time lookup',
78    'type' => MENU_CALLBACK,
79    'page callback' => 'date_api_date_time_lookup',
80    'access arguments' => array('administer site configuration'),
81  );
82  $items['admin/settings/date-time/formats/custom'] = array(
83    'title' => 'Custom formats',
84    'description' => 'Allow users to configure custom date formats.',
85    'type' => MENU_LOCAL_TASK,
86    'file' => 'date_api.admin.inc',
87    'page callback' => 'date_api_configure_custom_date_formats',
88    'access arguments' => array('administer site configuration'),
89    'weight' => 2,
90  );
91  $items['admin/settings/date-time/formats/add'] = array(
92    'title' => 'Add format',
93    'description' => 'Allow users to add additional date formats.',
94    'type' => MENU_LOCAL_TASK,
95    'file' => 'date_api.admin.inc',
96    'page callback' => 'drupal_get_form',
97    'page arguments' => array('date_api_add_date_formats_form'),
98    'access arguments' => array('administer site configuration'),
99    'weight' => 3,
100  );
101  $items['admin/settings/date-time/formats/delete/%'] = array(
102    'title' => 'Delete date format',
103    'description' => 'Allow users to delete a configured date format.',
104    'type' => MENU_CALLBACK,
105    'file' => 'date_api.admin.inc',
106    'page callback' => 'drupal_get_form',
107    'page arguments' => array('date_api_delete_format_form', 5),
108    'access arguments' => array('administer site configuration'),
109  );
110  $items['admin/settings/date-time/delete/%'] = array(
111    'title' => 'Delete date format type',
112    'description' => 'Allow users to delete a configured date format type.',
113    'type' => MENU_CALLBACK,
114    'file' => 'date_api.admin.inc',
115    'page callback' => 'drupal_get_form',
116    'page arguments' => array('date_api_delete_format_type_form', 4),
117    'access arguments' => array('administer site configuration'),
118  );
119
120  return $items;
121}
122
123/**
124 * Implementation of hook_menu_alter().
125 */
126function date_api_menu_alter(&$callbacks) {
127  // Add a new 'admin/settings/date-time/configure' path and make it the same as
128  // the 'admin/settings/date-time'.  Also set it to be the default local task -
129  // needed to make tabs work as expected.
130  $callbacks['admin/settings/date-time/configure'] = $callbacks['admin/settings/date-time'];
131  $callbacks['admin/settings/date-time/configure']['type'] = MENU_DEFAULT_LOCAL_TASK;
132}
133
134/**
135 * Helper function for getting the format string for a date type.
136 */
137function date_type_format($type) {
138  switch ($type) {
139    case DATE_ISO:
140      return DATE_FORMAT_ISO;
141    case DATE_UNIX:
142      return DATE_FORMAT_UNIX;
143    case DATE_DATETIME:
144      return DATE_FORMAT_DATETIME;
145    case DATE_ICAL:
146      return DATE_FORMAT_ICAL;
147  }
148}
149
150/**
151 * An untranslated array of month names
152 *
153 * Needed for css, translation functions, strtotime(), and other places
154 * that use the English versions of these words.
155 *
156 * @return
157 *   an array of month names
158 */
159function date_month_names_untranslated() {
160  static $month_names;
161  if (empty($month_names)) {
162    $month_names = array(1 => 'January', 2 => 'February', 3 => 'March',
163      4 => 'April', 5 => 'May', 6 => 'June', 7 => 'July',
164      8 => 'August', 9 => 'September', 10 => 'October',
165      11 => 'November', 12 => 'December');
166  }
167  return $month_names;
168}
169
170/**
171 * A translated array of month names
172 *
173 * @param $required
174 *   If not required, will include a blank value at the beginning of the list.
175 * @return
176 *   an array of month names
177 */
178function date_month_names($required = FALSE) {
179  $month_names = array();
180  foreach (date_month_names_untranslated() as $key => $day) {
181    $month_names[$key] = date_t($day, 'month_name');
182  }
183  $none = array('' => '');
184  return !$required ? $none + $month_names : $month_names;
185}
186
187/**
188 * A translated array of month name abbreviations
189 *
190 * @param $required
191 *   If not required, will include a blank value at the beginning of the list.
192 * @return
193 *   an array of month abbreviations
194 */
195function date_month_names_abbr($required = FALSE) {
196  $month_names = array();
197  foreach (date_month_names_untranslated() as $key => $day) {
198    $month_names[$key] = date_t($day, 'month_abbr');
199  }
200  $none = array('' => '');
201  return !$required ? $none + $month_names : $month_names;
202}
203
204/**
205 * An untranslated array of week days
206 *
207 * Needed for css, translation functions, strtotime(), and other places
208 * that use the English versions of these words.
209 *
210 * @return
211 *   an array of week day names
212 */
213function date_week_days_untranslated($refresh = TRUE) {
214  static $weekdays;
215  if ($refresh || empty($weekdays)) {
216    $weekdays = array(0 => 'Sunday', 1 => 'Monday', 2 => 'Tuesday',
217      3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday',
218      6 => 'Saturday');
219  }
220  return $weekdays;
221}
222
223/**
224 * A translated array of week days
225 *
226 * @param $required
227 *   If not required, will include a blank value at the beginning of the array.
228 * @return
229 *   an array of week day names
230 */
231function date_week_days($required = FALSE, $refresh = TRUE) {
232  $weekdays = array();
233  foreach (date_week_days_untranslated() as $key => $day) {
234    $weekdays[$key] = date_t($day, 'day_name');
235  }
236  $none = array('' => '');
237  return !$required ? $none + $weekdays : $weekdays;
238}
239
240/**
241 * An translated array of week day abbreviations.
242 *
243 * @param $required
244 *   If not required, will include a blank value at the beginning of the array.
245 * @return
246 *   an array of week day abbreviations
247 */
248function date_week_days_abbr($required = FALSE, $refresh = TRUE, $length = 3) {
249  $weekdays = array();
250  switch ($length) {
251    case 1:
252      $context = 'day_abbr1';
253      break;
254    case 2:
255      $context = 'day_abbr2';
256      break;
257    default:
258      $context = 'day_abbr';
259      break;
260  }
261  foreach (date_week_days_untranslated() as $key => $day) {
262    $weekdays[$key] = date_t($day, $context);
263  }
264  $none = array('' => '');
265  return !$required ? $none + $weekdays : $weekdays;
266}
267
268/**
269 * Order weekdays
270 *   Correct weekdays array so first day in array matches the first day of
271 *   the week. Use to create things like calendar headers.
272 *
273 * @param array $weekdays
274 * @return array
275 */
276function date_week_days_ordered($weekdays) {
277  if (variable_get('date_first_day', 1) > 0) {
278    for ($i = 1; $i <= variable_get('date_first_day', 1); $i++) {
279      $last = array_shift($weekdays);
280      array_push($weekdays, $last);
281    }
282  }
283  return $weekdays;
284}
285
286/**
287 * An array of years.
288 *
289 * @param int $min
290 *   the minimum year in the array
291 * @param int $max
292 *   the maximum year in the array
293 * @param $required
294 *   If not required, will include a blank value at the beginning of the array.
295 * @return
296 *   an array of years in the selected range
297 */
298function date_years($min = 0, $max = 0, $required = FALSE) {
299  // Have to be sure $min and $max are valid values;
300  if (empty($min)) $min = intval(date('Y', time()) - 3);
301  if (empty($max)) $max = intval(date('Y', time()) + 3);
302  $none = array(0 => '');
303  return !$required ? $none + drupal_map_assoc(range($min, $max)) : drupal_map_assoc(range($min, $max));
304}
305
306/**
307 * An array of days.
308 *
309 * @param $required
310 *   If not required, returned array will include a blank value.
311 * @param integer $month (optional)
312 * @param integer $year (optional)
313 * @return
314 *   an array of days for the selected month.
315 */
316function date_days($required = FALSE, $month = NULL, $year = NULL) {
317  // If we have a month and year, find the right last day of the month.
318  if (!empty($month) && !empty($year)) {
319    $date = date_make_date($year .'-'. $month .'-01 00:00:00', 'UTC');
320    $max = date_format('t', $date);
321  }
322  // If there is no month and year given, default to 31.
323  if (empty($max)) $max = 31;
324  $none = array(0 => '');
325  return !$required ? $none + drupal_map_assoc(range(1, $max)) : drupal_map_assoc(range(1, $max));
326}
327
328/**
329 * An array of hours.
330 *
331 * @param string $format
332 * @param $required
333 *   If not required, returned array will include a blank value.
334 * @return
335 *   an array of hours in the selected format.
336 */
337function date_hours($format = 'H', $required = FALSE) {
338  $hours = array();
339  if ($format == 'h' || $format == 'g') {
340    $min = 1;
341    $max = 12;
342  }
343  else  {
344    $min = 0;
345    $max = 23;
346  }
347  for ($i = $min; $i <= $max; $i++) {
348    $hours[$i] = $i < 10 && ($format == 'H' || $format == 'h') ? "0$i" : $i;
349  }
350  $none = array('' => '');
351  return !$required ? $none + $hours : $hours;
352}
353
354/**
355 * An array of minutes.
356 *
357 * @param string $format
358 * @param $required
359 *   If not required, returned array will include a blank value.
360 * @return
361 *   an array of minutes in the selected format.
362 */
363function date_minutes($format = 'i', $required = FALSE, $increment = 1) {
364  $minutes = array();
365  // Have to be sure $increment has a value so we don't loop endlessly;
366  if (empty($increment)) $increment = 1;
367  for ($i = 0; $i < 60; $i += $increment) {
368    $minutes[$i] = $i < 10 && $format == 'i' ? "0$i" : $i;
369  }
370  $none = array('' => '');
371  return !$required ? $none + $minutes : $minutes;
372}
373
374/**
375 * An array of seconds.
376 *
377 * @param string $format
378 * @param $required
379 *   If not required, returned array will include a blank value.
380 * @return array an array of seconds in the selected format.
381 */
382function date_seconds($format = 's', $required = FALSE, $increment = 1) {
383  $seconds = array();
384  // Have to be sure $increment has a value so we don't loop endlessly;
385  if (empty($increment)) $increment = 1;
386  for ($i = 0; $i < 60; $i += $increment) {
387    $seconds[$i] = $i < 10 && $format == 's' ? "0$i" : $i;
388  }
389  $none = array('' => '');
390  return !$required ? $none + $seconds : $seconds;
391}
392
393/**
394 * An array of am and pm options.
395 * @param $required
396 *   If not required, returned array will include a blank value.
397 * @return array an array of am pm options.
398 */
399function date_ampm($required = FALSE) {
400  $none = array('' => '');
401  $ampm = array('am' => date_t('am', 'ampm'), 'pm' => date_t('pm', 'ampm'));
402  return !$required ? $none + $ampm : $ampm;
403}
404
405/**
406 * Implementation of hook_date_formats().
407 *
408 * @return
409 *   An array of date formats with attributes 'type' (short, medium or long),
410 *   'format' (the format string) and 'locales'.  The 'locales' attribute is an
411 *   array of locales, which can include both 2 character language codes like
412 *   'en', 'fr', but also 5 character language codes like 'en-gb' and 'en-us'.
413 */
414function date_api_date_formats() {
415  include_once('./'. drupal_get_path('module', 'date_api') .'/date_api_formats_list.inc');
416  return _date_api_date_formats_list();
417}
418
419/**
420 * Implementation of hook_date_format_types().
421 */
422function date_api_date_format_types() {
423  return array(
424    'long' => t('Long'),
425    'medium' => t('Medium'),
426    'short' => t('Short'),
427  );
428}
429
430/**
431 * Array of regex replacement strings for date format elements.
432 * Used to allow input in custom formats. Based on work done for
433 * the Date module by Yves Chedemois (yched).
434 *
435 * @return array of date() format letters and their regex equivalents.
436 */
437function date_format_patterns($strict = FALSE) {
438  return array(
439    'd' => '\d{'. ($strict ? '2' : '1,2') .'}',
440    'm' => '\d{'. ($strict ? '2' : '1,2') .'}',
441    'h' => '\d{'. ($strict ? '2' : '1,2') .'}',
442    'H' => '\d{'. ($strict ? '2' : '1,2') .'}',
443    'i' => '\d{'. ($strict ? '2' : '1,2') .'}',
444    's' => '\d{'. ($strict ? '2' : '1,2') .'}',
445    'j' => '\d{1,2}',    'N' => '\d',      'S' => '\w{2}',
446    'w' => '\d',       'z' => '\d{1,3}',    'W' => '\d{1,2}',
447    'n' => '\d{1,2}',  't' => '\d{2}',      'L' => '\d',      'o' => '\d{4}',
448    'Y' => '\d{4}',    'y' => '\d{2}',      'B' => '\d{3}',   'g' => '\d{1,2}',
449    'G' => '\d{1,2}',  'e' => '\w*',        'I' => '\d',      'T' => '\w*',
450    'U' => '\d*',      'z' => '[+-]?\d*',   'O' => '[+-]?\d{4}',
451    //Using S instead of w and 3 as well as 4 to pick up non-ASCII chars like German umlaute.
452    // Per http://drupal.org/node/1101294, we may need as little as 2 and as many as 5 characters
453    // in some languages.
454    'D' => '\S{2,5}',    'l' => '\S*', 'M' => '\S{2,5}', 'F' => '\S*',
455    'P' => '[+-]?\d{2}\:\d{2}',
456    'c' => '(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})([+-]?\d{2}\:\d{2})',
457    'r' => '(\w{3}), (\d{2})\s(\w{3})\s(\d{2,4})\s(\d{2}):(\d{2}):(\d{2})([+-]?\d{4})?',
458    );
459}
460
461/**
462 * Array of granularity options and their labels
463 *
464 * @return array
465 */
466function date_granularity_names() {
467  return array(
468    'year' => date_t('Year', 'datetime'),
469    'month' => date_t('Month', 'datetime'),
470    'day' => date_t('Day', 'datetime'),
471    'hour' => date_t('Hour', 'datetime'),
472    'minute' => date_t('Minute', 'datetime'),
473    'second' => date_t('Second', 'datetime'),
474    );
475}
476
477/**
478 * Sort a granularity array.
479 */
480function date_granularity_sorted($granularity) {
481  return array_intersect(array('year', 'month', 'day', 'hour', 'minute', 'second'), $granularity);
482}
483
484/**
485 * Give a granularity $precision, return an array of
486 * all the possible granularity elements.
487 */
488function date_granularity_array_from_precision($precision) {
489  $granularity_array = array('year', 'month', 'day', 'hour', 'minute', 'second');
490  switch (($precision)) {
491    case 'year':
492      return array_slice($granularity_array, -6);
493    case 'month':
494      return array_slice($granularity_array, -5);
495    case 'day':
496      return array_slice($granularity_array, -4);
497    case 'hour':
498      return array_slice($granularity_array, -3);
499    case 'minute':
500      return array_slice($granularity_array, -2);
501    default:
502      return $granularity_array;
503  }
504}
505
506/**
507 * Give a granularity array, return the highest precision.
508 */
509function date_granularity_precision($granularity_array) {
510  $input = date_granularity_sorted($granularity_array);
511  return array_pop($input);
512}
513
514/**
515 * Construct an appropriate DATETIME format string for the granularity of an item.
516 */
517function date_granularity_format($granularity) {
518  if (is_array($granularity)) {
519    $granularity = date_granularity_precision($granularity);
520  }
521  $format = 'Y-m-d H:i:s';
522  switch ($granularity) {
523    case 'year':
524      return drupal_substr($format, 0, 1);
525    case 'month':
526      return drupal_substr($format, 0, 3);
527    case 'day':
528      return drupal_substr($format, 0, 5);
529    case 'hour';
530      return drupal_substr($format, 0, 7);
531    case 'minute':
532      return drupal_substr($format, 0, 9);
533    default:
534      return $format;
535  }
536}
537
538/**
539 * A translated array of timezone names.
540 * Cache the untranslated array, make the translated array a static variable.
541 *
542 * @param $required
543 *   If not required, returned array will include a blank value.
544 * @return
545 *   an array of timezone names
546 */
547function date_timezone_names($required = FALSE, $refresh = FALSE) {
548  static $zonenames;
549  if (empty($zonenames) || $refresh) {
550    $cached = cache_get('date_timezone_identifiers_list');
551    $zonenames = !empty($cached) ? $cached->data : array();
552    if ($refresh || empty($cached) || empty($zonenames)) {
553      $data = timezone_identifiers_list();
554      asort($data);
555      // Use include instead of include once in case the function gets
556      // refreshed via devel or other API and is called more than once.
557      if (module_exists('date_php4')) {
558        include('./'. drupal_get_path('module', 'date_php4') .'/date_php4_missing_data.inc');
559      }
560      foreach ($data as $delta => $zone) {
561        // Because many time zones exist in PHP only for backward
562        // compatibility reasons and should not be used, the list is
563        // filtered by a regular expression.
564        if (preg_match('!^((Africa|America|Antarctica|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)/|UTC$)!', $zone)) {
565          $zonenames[$zone] = $zone;
566        }
567      }
568      // If using PHP4, filter the list down to only the timezones
569      // the PHP4 wrapper has data for.
570      if (module_exists('date_php4')) {
571        foreach ($missing_timezone_data as $zone) {
572          unset($zonenames[$zone]);
573        }
574      }
575
576      // If using Event, further filter the list down to only
577      // zones that exist in the event module.
578      if (module_exists('event') && db_table_exists('event_timezones')) {
579        $result = db_query("SELECT name FROM {event_timezones} ORDER BY name");
580        $names = array();
581        while ($row = db_fetch_array($result)) {
582          $names[] = str_replace(' ', '_', $row['name']);
583        }
584        foreach ($zonenames as $name => $zone) {
585          if (!in_array($name, $names)) {
586            unset($zonenames[$name]);
587          }
588        }
589      }
590      if (!empty($zonenames)) {
591        cache_set('date_timezone_identifiers_list', $zonenames);
592      }
593    }
594    foreach ($zonenames as $zone) {
595      $zonenames[$zone] = t('!timezone', array('!timezone' => t($zone)));
596    }
597  }
598  $none = array('' => '');
599  return !$required ? $none + $zonenames : $zonenames;
600}
601
602/**
603 * An array of timezone abbreviations that the system allows.
604 * Cache an array of just the abbreviation names because the
605 * whole timezone_abbreviations_list is huge so we don't want
606 * to get it more than necessary.
607 *
608 * @return array
609 */
610function date_timezone_abbr($refresh = FALSE) {
611  $cached = cache_get('date_timezone_abbreviations');
612  $data = isset($cached->data) ? $cached->data : array();
613  if (empty($data) || $refresh) {
614    $data = array_keys(timezone_abbreviations_list());
615    cache_set('date_timezone_abbreviations', $data);
616  }
617  return $data;
618}
619
620/**
621 * A function to translate ambiguous short date strings.
622 *
623 * Example: Pass in 'Monday', 'day_abbr' and get the translated
624 * abbreviation for Monday.
625 *
626 * @param string $string
627 * @param string $context
628 * @param int $langcode
629 * @return translated value of the string
630 */
631function date_t($string, $context, $langcode = NULL) {
632  static $replace = array();
633
634  if (empty($replace[$langcode])) {
635    // The function to create the date string arrays is kept separate
636    // so those arrays can be directly accessed by other functions.
637    date_t_strings($replace, $langcode);
638  }
639  switch ($context) {
640    case 'day_name':
641    case 'day_abbr':
642    case 'day_abbr1':
643    case 'day_abbr2':
644      $untranslated = array_flip(date_week_days_untranslated());
645      break;
646    case 'month_name':
647    case 'month_abbr':
648      $untranslated = array_flip(date_month_names_untranslated());
649      break;
650    case 'ampm':
651      $untranslated = array_flip(array('am', 'pm', 'AM', 'PM'));
652      break;
653    case 'datetime':
654      $untranslated = array_flip(array('Year', 'Month', 'Day', 'Week', 'Hour', 'Minute', 'Second', 'All Day', 'All day'));
655      break;
656    case 'datetime_plural':
657      $untranslated = array_flip(array('Years', 'Months', 'Days', 'Weeks', 'Hours', 'Minutes', 'Seconds'));
658      break;
659    case 'date_order':
660      $untranslated = array_flip(array('Every', 'First', 'Second', 'Third', 'Fourth', 'Fifth'));
661      break;
662    case 'date_order_reverse':
663      $untranslated = array_flip(array('', 'Last', 'Next to last', 'Third from last', 'Fourth from last', 'Fifth from last'));
664      break;
665    case 'date_nav':
666      $untranslated = array_flip(array('Prev', 'Next', 'Today'));
667      break;
668  }
669  $pos = $untranslated[$string];
670  return $replace[$langcode][$context][$pos];
671}
672
673/**
674 * Construct translation arrays from pipe-delimited strings.
675 *
676 * Combining these strings into a single t() gives them the context needed
677 * for better translation.
678 */
679function date_t_strings(&$replace, $langcode) {
680  $replace[$langcode]['day_name'] = explode('|', trim(t('!day-name Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday', array('!day-name' => ''), $langcode)));
681  $replace[$langcode]['day_abbr'] = explode('|', trim(t('!day-abbreviation Sun|Mon|Tue|Wed|Thu|Fri|Sat', array('!day-abbreviation' => ''), $langcode)));
682  $replace[$langcode]['day_abbr1'] = explode('|', trim(t('!day-abbreviation S|M|T|W|T|F|S', array('!day-abbreviation' => ''), $langcode)));
683  $replace[$langcode]['day_abbr2'] = explode('|', trim(t('!day-abbreviation SU|MO|TU|WE|TH|FR|SA', array('!day-abbreviation' => ''), $langcode)));
684  $replace[$langcode]['ampm'] = explode('|', trim(t('!ampm-abbreviation am|pm|AM|PM', array('!ampm-abbreviation' => ''), $langcode)));
685  $replace[$langcode]['datetime'] = explode('|', trim(t('!datetime Year|Month|Day|Week|Hour|Minute|Second|All Day|All day', array('!datetime' => ''), $langcode)));
686  $replace[$langcode]['datetime_plural'] = explode('|', trim(t('!datetime_plural Years|Months|Days|Weeks|Hours|Minutes|Seconds', array('!datetime_plural' => ''), $langcode)));
687  $replace[$langcode]['date_order'] = explode('|', trim(t('!date_order Every|First|Second|Third|Fourth|Fifth', array('!date_order' => ''), $langcode)));
688  $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)));
689  $replace[$langcode]['date_nav'] = explode('|', trim(t('!date_nav Prev|Next|Today', array('!date_nav' => ''), $langcode)));
690
691  // These start with a pipe so the January value will be in position 1 instead of position 0.
692  $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)));
693  $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)));
694}
695
696/**
697 * Reworked from Drupal's format_date function to handle pre-1970 and
698 * post-2038 dates and accept a date object instead of a timestamp as input.
699 *
700 * Translates formatted date results, unlike PHP function date_format().
701 *
702 * @param $date
703 *   A date object, could be created by date_make_date().
704 * @param $type
705 *   The format to use. Can be "small", "medium" or "large" for the preconfigured
706 *   date formats. If "custom" is specified, then $format is required as well.
707 * @param $format
708 *   A PHP date format string as required by date(). A backslash should be used
709 *   before a character to avoid interpreting the character as part of a date
710 *   format.
711 * @return
712 *   A translated date string in the requested format.
713 */
714function date_format_date($date, $type = 'medium', $format = '', $langcode = NULL) {
715  if (empty($date)) {
716    return '';
717  }
718
719  if (function_exists('timezone_name_from_abbr') && get_class($date) != 'DateTime') {
720    $date = date_make_date($date);
721  }
722  switch ($type) {
723    case 'small':
724    case 'short':
725      $format = variable_get('date_format_short', 'm/d/Y - H:i');
726      break;
727    case 'large':
728    case 'long':
729      $format = variable_get('date_format_long', 'l, F j, Y - H:i');
730      break;
731    case 'custom':
732      $format = $format;
733      break;
734    case 'medium':
735    default:
736      $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
737  }
738  $max = drupal_strlen($format);
739  $datestring = '';
740  for ($i = 0; $i < $max; $i++) {
741    $c = $format[$i];
742    switch ($c) {
743      // Use date_t() for ambiguous short strings that need translation.
744      // We send long day and month names to date_t(), along with context.
745      case 'l':
746        $datestring .= date_t(date_format($date, 'l'), 'day_name', $langcode);
747        break;
748      case 'D':
749        $datestring .= date_t(date_format($date, 'l'), 'day_abbr', $langcode);
750        break;
751      case 'F':
752        $datestring .= date_t(date_format($date, 'F'), 'month_name', $langcode);
753        break;
754      case 'M':
755        $datestring .= date_t(date_format($date, 'F'), 'month_abbr', $langcode);
756        break;
757      case 'A':
758      case 'a':
759        $datestring .= date_t(date_format($date, $c), 'ampm', $langcode);
760        break;
761      // The timezone name translations can use t().
762      case 'e':
763      case 'T':
764        $datestring .= t(date_format($date, $c));
765        break;
766      // Remaining date parts need no translation.
767      case 'O':
768        $datestring .= sprintf('%s%02d%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60);
769        break;
770      case 'P':
771        $datestring .= sprintf('%s%02d:%02d', (date_offset_get($date) < 0 ? '-' : '+'), abs(date_offset_get($date) / 3600), abs(date_offset_get($date) % 3600) / 60);
772        break;
773      case 'Z':
774        $datestring .= date_offset_get($date);
775        break;
776      case '\\':
777        $datestring .= $format[++$i];
778        break;
779      case 'r':
780        $datestring .= date_format_date($date, 'custom', 'D, d M Y H:i:s O', $langcode);
781        break;
782      default:
783        if (strpos('BdcgGhHiIjLmnNosStTuUwWYyz', $c) !== FALSE) {
784          $datestring .= date_format($date, $c);
785        }
786        else {
787          $datestring .= $c;
788        }
789    }
790  }
791  return $datestring;
792}
793
794/**
795 * An override for interval formatting that adds past and future context
796 *
797 * @param DateTime $date
798 * @param integer $granularity
799 * @return formatted string
800 */
801function date_format_interval($date, $granularity = 2) {
802  // If no date is sent, then return nothing
803  if (empty($date)) {
804    return NULL;
805  }
806
807  $interval = time() - date_format($date, 'U');
808  if ($interval > 0 ) {
809    return t('!time ago', array('!time' => format_interval($interval, $granularity)));
810  }
811  else {
812    return format_interval(abs($interval), $granularity);
813  }
814}
815
816/**
817 * A date object for the current time.
818 *
819 * @param $timezone
820 *   Optional method to force time to a specific timezone,
821 *   defaults to user timezone, if set, otherwise site timezone.
822 * @return object date
823 */
824function date_now($timezone = NULL) {
825  return date_make_date('now', $timezone);
826}
827
828/**
829 *  Convert a date of any type or an array of date parts into a valid date
830 *  object.
831
832 *  @param $date
833 *    A date in any format or the string 'now'.
834 *  @param $timezone
835 *    Optional, the name of the timezone this date is in, defaults
836 *    to the user timezone, if set, otherwise the site timezone.
837 *    Accepts either a timezone name or a timezone object as input.
838 *  @param $type
839 *    The type of date provided, could be
840 *    DATE_ARRAY, DATE_UNIX, DATE_DATETIME, DATE_ISO, or DATE_OBJECT.
841 *  @param $granularity
842 *    The granularity of the date value provided. Set this for partial
843 *    dates so they pass validation and don't get reset to 'now'.
844 */
845function date_make_date($date, $timezone = NULL, $type = DATE_DATETIME, $granularity = array('year', 'month', 'day', 'hour', 'minute')) {
846
847  // Make sure some value is set for the date and timezone even if the
848  // site timezone is not yet set up to avoid fatal installation
849  // errors.
850  if (empty($timezone) || !date_timezone_is_valid($timezone)) {
851    $timezone = date_default_timezone_name();
852  }
853
854  // Special handling for a unix timestamp of '0', since it will fail 'empty' tests below.
855  if ($date === 0 && $type == DATE_UNIX) {
856    $date = date_convert($date, $type, DATE_DATETIME, $timezone);
857    $type = DATE_DATETIME;
858  }
859
860  // No value or one with unexpected array keys.
861  if (empty($date) || (is_array($date) && array_diff($granularity, array_keys($date)))) {
862    return NULL;
863  }
864
865  // Special handling for partial dates that don't need precision.
866  $granularity_sorted = date_granularity_sorted($granularity);
867  $max_granularity = end($granularity_sorted);
868  if (in_array($max_granularity, array('year', 'month')) || $type == DATE_ISO || $type == DATE_ARRAY) {
869    if ($type == DATE_UNIX) {
870      $date = date_convert($date, $type, DATE_DATETIME);
871    }
872    $date = date_fuzzy_datetime($date);
873    $type = DATE_DATETIME;
874  }
875
876  if (!date_is_valid($date, $type, $granularity)) {
877    $date = 'now';
878  }
879  if (!empty($timezone) && !empty($date)) {
880    if ($date == 'now') {
881      return date_create('now', timezone_open($timezone));
882    }
883    elseif ($datetime = date_convert($date, $type, DATE_DATETIME, $timezone)) {
884      return date_create($datetime, timezone_open($timezone));
885    }
886  }
887  return NULL;
888}
889
890function date_timezone_is_valid($timezone) {
891  static $timezone_names;
892  if (empty($timezone_names)) {
893    $timezone_names = array_keys(date_timezone_names(TRUE));
894  }
895  if (!in_array($timezone, $timezone_names)) {
896    return FALSE;
897  }
898  return TRUE;
899}
900
901/**
902 * Return a timezone name to use as a default.
903 *
904 * @return a timezone name
905 *   Identify the default timezone for a user, if available, otherwise the site.
906 *   Must return a value even if no timezone info has been set up.
907 */
908function date_default_timezone_name($check_user = TRUE) {
909  global $user;
910  if ($check_user && variable_get('configurable_timezones', 1) && !empty($user->timezone_name)) {
911    return $user->timezone_name;
912  }
913  else {
914    $default = variable_get('date_default_timezone_name', '');
915    return empty($default) ? 'UTC' : $default;
916  }
917}
918
919/**
920 * A timezone object for the default timezone.
921 *
922 * @return a timezone object
923 *   Identify the default timezone for a user, if available, otherwise the site.
924 */
925function date_default_timezone($check_user = TRUE) {
926  $timezone = date_default_timezone_name($check_user);
927  return timezone_open(date_default_timezone_name($check_user));
928}
929
930/**
931 * Identify the number of days in a month for a date.
932 */
933function date_days_in_month($year, $month) {
934  // Pick a day in the middle of the month to avoid timezone shifts.
935  $datetime = date_pad($year, 4) .'-'. date_pad($month) .'-15 00:00:00';
936  $date = date_make_date($datetime);
937  return date_format($date, 't');
938}
939
940/**
941 * Identify the number of days in a year for a date.
942 *
943 * @param mixed $date
944 * @param string $type
945 * @return integer
946 */
947function date_days_in_year($date = NULL, $type = DATE_OBJECT) {
948  if (empty($date)) {
949    $date = date_now();
950  }
951  if (!is_object($date)) {
952    $date = date_convert($date, $type, DATE_OBJECT);
953  }
954  if (is_object($date)) {
955    if (date_format($date, 'L')) {
956      return 366;
957    }
958    else {
959      return 365;
960    }
961  }
962  return NULL;
963}
964
965/**
966 * Identify the number of ISO weeks in a year for a date.
967 *
968 * December 28 is always in the last ISO week of the year.
969 *
970 * @param mixed $date
971 * @param string $type
972 * @return integer
973 */
974function date_iso_weeks_in_year($date = NULL, $type = DATE_OBJECT) {
975  if (empty($date)) {
976    $date = date_now();
977  }
978  if (!is_object($date)) {
979    $date = date_convert($date, $type, DATE_OBJECT);
980  }
981  if (is_object($date)) {
982    date_date_set($date, date_format($date, 'Y'), 12, 28);
983    return date_format($date, 'W');
984  }
985  return NULL;
986}
987
988/**
989 * Returns day of week for a given date (0 = Sunday).
990 *
991 * @param mixed  $date
992 *   a date, default is current local day
993 * @param string  $type
994 *   The type of date, DATE_ISO, DATE_DATETIME, or DATE_UNIX
995 * @return
996 *    the number of the day in the week
997 */
998function date_day_of_week($date = NULL, $type = DATE_OBJECT) {
999  if (empty($date)) {
1000    $date = date_now();
1001    $type = DATE_OBJECT;
1002  }
1003  $date = date_convert($date, $type, DATE_OBJECT);
1004  if (is_object($date)) {
1005    return date_format($date, 'w');
1006  }
1007  return NULL;
1008}
1009
1010/**
1011 * Returns translated name of the day of week for a given date.
1012 *
1013 * @param mixed  $date
1014 *   a date, default is current local day
1015 * @param string  $type
1016 *   The type of date, DATE_ISO, DATE_DATETIME, or DATE_UNIX
1017 * @param string $abbr
1018 *   Whether to return the abbreviated name for that day
1019 * @return
1020 *    the name of the day in the week for that date
1021 */
1022function date_day_of_week_name($date = NULL, $abbr = TRUE, $type = DATE_DATETIME) {
1023  $dow = date_day_of_week($date, $type);
1024  $days = $abbr ? date_week_days_abbr() : date_week_days();
1025  return $days[$dow];
1026}
1027
1028/**
1029 * Compute difference between two days using a given measure.
1030 *
1031 * @param mixed $date1
1032 *   the starting date
1033 * @param mixed $date2
1034 *   the ending date
1035 * @param string $measure
1036 *   'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'
1037 * @param string $type
1038 *   the type of dates provided:
1039 *   DATE_OBJECT, DATE_DATETIME, DATE_ISO, DATE_UNIX, DATE_ARRAY
1040 */
1041function date_difference($date1_in, $date2_in, $measure = 'seconds', $type = DATE_OBJECT) {
1042  // Create cloned objects or original dates will be impacted by
1043  // the date_modify() operations done in this code.
1044  $date1 = drupal_clone(date_convert($date1_in, $type, DATE_OBJECT));
1045  $date2 = drupal_clone(date_convert($date2_in, $type, DATE_OBJECT));
1046  if (is_object($date1) && is_object($date2)) {
1047    $diff = date_format($date2, 'U') - date_format($date1, 'U');
1048    if ($diff == 0 ) {
1049      return 0;
1050    }
1051    elseif ($diff < 0) {
1052      // Make sure $date1 is the smaller date.
1053      $temp = $date2;
1054      $date2 = $date1;
1055      $date1 = $temp;
1056      $diff = date_format($date2, 'U') - date_format($date1, 'U');
1057    }
1058    $year_diff = intval(date_format($date2, 'Y') - date_format($date1, 'Y'));
1059    switch ($measure) {
1060
1061      // The easy cases first.
1062      case 'seconds':
1063        return $diff;
1064      case 'minutes':
1065        return $diff / 60;
1066      case 'hours':
1067        return $diff / 3600;
1068      case 'years':
1069        return $year_diff;
1070
1071      case 'months':
1072        $format = 'n';
1073        $item1 = date_format($date1, $format);
1074        $item2 = date_format($date2, $format);
1075        if ($year_diff == 0) {
1076          return intval($item2 - $item1);
1077        }
1078        else {
1079          $item_diff = 12 - $item1;
1080          $item_diff += intval(($year_diff - 1) * 12);
1081          return $item_diff + $item2;
1082        }
1083        break;
1084
1085      case 'days':
1086        $format = 'z';
1087        $item1 = date_format($date1, $format);
1088        $item2 = date_format($date2, $format);
1089        if ($year_diff == 0) {
1090          return intval($item2 - $item1);
1091        }
1092        else {
1093          $item_diff = date_days_in_year($date1) - $item1;
1094          for ($i = 1; $i < $year_diff; $i++) {
1095            date_modify($date1, '+1 year');
1096            $item_diff += date_days_in_year($date1);
1097          }
1098          return $item_diff + $item2;
1099        }
1100        break;
1101
1102      case 'weeks':
1103        $week_diff = date_format($date2, 'W') - date_format($date1, 'W');
1104        $year_diff = date_format($date2, 'o') - date_format($date1, 'o');
1105        for ($i = 1; $i <= $year_diff; $i++) {
1106          date_modify($date1, '+1 year');
1107          $week_diff += date_iso_weeks_in_year($date1);
1108        }
1109        return $week_diff;
1110    }
1111  }
1112  return NULL;
1113}
1114
1115/**
1116 * Start and end dates for a calendar week, adjusted to use the
1117 * chosen first day of week for this site.
1118 */
1119function date_week_range($week, $year) {
1120  if (variable_get('date_api_use_iso8601', FALSE)) {
1121    return date_iso_week_range($week, $year);
1122  }
1123
1124  $min_date = date_make_date($year .'-01-01 00:00:00', date_default_timezone_name());
1125  date_timezone_set($min_date, date_default_timezone());
1126
1127  // move to the right week
1128  date_modify($min_date, '+' . strval(7 * ($week - 1)) . ' days');
1129
1130  // move backwards to the first day of the week
1131  $first_day = variable_get('date_first_day', 1);
1132  $day_wday = date_format($min_date, 'w');
1133  date_modify($min_date, '-' . strval((7 + $day_wday - $first_day) % 7) . ' days');
1134
1135  // move forwards to the last day of the week
1136  $max_date = drupal_clone($min_date);
1137  date_modify($max_date, '+7 days');
1138
1139  if (date_format($min_date, 'Y') != $year) {
1140    $min_date = date_make_date($year .'-01-01 00:00:00', date_default_timezone());
1141  }
1142  return array($min_date, $max_date);
1143}
1144
1145/**
1146 * Start and end dates for an ISO week.
1147 */
1148function date_iso_week_range($week, $year) {
1149
1150  // Get to the last ISO week of the previous year.
1151  $min_date = date_make_date(($year - 1) .'-12-28 00:00:00', date_default_timezone_name());
1152  date_timezone_set($min_date, date_default_timezone());
1153
1154  // Find the first day of the first ISO week in the year.
1155  date_modify($min_date, '+1 Monday');
1156
1157  // Jump ahead to the desired week for the beginning of the week range.
1158  if ($week > 1) {
1159    date_modify($min_date, '+ '. ($week - 1) .' weeks');
1160  }
1161
1162  // move forwards to the last day of the week
1163  $max_date = drupal_clone($min_date);
1164  date_modify($max_date, '+7 days');
1165  return array($min_date, $max_date);
1166}
1167
1168/**
1169 * The number of calendar weeks in a year.
1170 *
1171 * PHP week functions return the ISO week, not the calendar week.
1172 *
1173 * @param int $year
1174 * @return int number of calendar weeks in selected year.
1175 */
1176function date_weeks_in_year($year) {
1177  $date = date_make_date(($year + 1) . '-01-01 12:00:00', 'UTC');
1178  date_modify($date, '-1 day');
1179  return date_week(date_format($date, 'Y-m-d'));
1180}
1181
1182/**
1183 * The calendar week number for a date.
1184 *
1185 * PHP week functions return the ISO week, not the calendar week.
1186 *
1187 * @param string $date, in the format Y-m-d
1188 * @return int calendar week number.
1189 */
1190function date_week($date) {
1191  $date = drupal_substr($date, 0, 10);
1192  $parts = explode('-', $date);
1193  $date = date_make_date($date . ' 12:00:00', 'UTC');
1194
1195  // If we are using ISO weeks, this is easy.
1196  if (variable_get('date_api_use_iso8601', FALSE)) {
1197    return intval(date_format($date, 'W'));
1198  }
1199
1200  $year_date = date_make_date($parts[0] . '-01-01 12:00:00', 'UTC');
1201  $week = intval(date_format($date, 'W'));
1202  $year_week = intval(date_format($year_date, 'W'));
1203  $date_year = intval(date_format($date, 'o'));
1204
1205  // remove the leap week if it's present
1206  if ($date_year > intval($parts[0])) {
1207    $last_date = drupal_clone($date);
1208    date_modify($last_date, '-7 days');
1209    $week = date_format($last_date, 'W') + 1;
1210  }
1211  elseif ($date_year < intval($parts[0])) {
1212    $week = 0;
1213  }
1214
1215  if ($year_week != 1) $week++;
1216
1217  // convert to ISO-8601 day number, to match weeks calculated above
1218  $iso_first_day = 1 + (variable_get('date_first_day', 1) + 6) % 7;
1219
1220  // if it's before the starting day, it's the previous week
1221  if (intval(date_format($date, 'N')) < $iso_first_day) $week--;
1222
1223  // if the year starts before, it's an extra week at the beginning
1224  if (intval(date_format($year_date, 'N')) < $iso_first_day) $week++;
1225
1226  return $week;
1227}
1228
1229/**
1230 * Date conversion helper function.
1231 *
1232 * A variety of ways to convert dates from one type to another.
1233 * No timezone conversion is done in this operation, except
1234 * when handling timestamps because create_date() assumes
1235 * timestamps hold the UTC value for the time.
1236 *
1237 * @param mixed $date
1238 *   the date to convert
1239 * @param string $from_type
1240 *   the type of date to convert from
1241 * @param string $to_type
1242 *   the type of date to convert to
1243 * @param string $tz
1244 *   the timezone of the supplied value, only needed when using timestamps
1245 *   for dates not set to UTC.
1246 */
1247function date_convert($date, $from_type, $to_type, $tz = 'UTC') {
1248  if (empty($date) && !$date === 0) return NULL;
1249  if (empty($from_type) || empty($to_type) || $from_type == $to_type) return $date;
1250  switch ($from_type) {
1251    case DATE_ARRAY:
1252      if (!is_array($date)) return NULL;
1253      // Make sure all parts exist to avoid PHP notices.
1254      foreach (array('month', 'day', 'hour', 'minute', 'second') as $part) {
1255        if (!isset($date[$part])) {
1256          $date[$part] = '';
1257        }
1258      }
1259      if (isset($date['ampm'])) {
1260        if ($date['ampm'] == 'pm' && $date['hour'] < 12) $date['hour'] += 12;
1261        if ($date['ampm'] == 'am' && $date['hour'] == 12) $date['hour'] -= 12;
1262      }
1263      $datetime = date_pad(intval($date['year']), 4) .'-'. date_pad(intval($date['month'])) .
1264            '-'. date_pad(intval($date['day'])) .' '. date_pad(intval($date['hour'])) .
1265            ':'. date_pad(intval($date['minute'])) .':'. date_pad(intval($date['second']));
1266      switch ($to_type) {
1267        case DATE_ISO:
1268          return str_replace(' ', 'T', $datetime);
1269        case DATE_DATETIME:
1270          return $datetime;
1271        case DATE_ICAL:
1272          $replace = array(' ' => 'T', '-' => '', ':' => '');
1273          return strtr($datetime, $replace);
1274        case DATE_OBJECT:
1275          return date_create($datetime, timezone_open($tz));
1276        case DATE_UNIX:
1277          $obj = date_create($datetime, timezone_open($tz));
1278          return date_format($obj, 'U');
1279      }
1280      break;
1281    case DATE_OBJECT:
1282      if (!is_object($date)) return NULL;
1283      $obj = $date;
1284      break;
1285    case DATE_DATETIME:
1286    case DATE_ISO:
1287      if (!preg_match(DATE_REGEX_LOOSE, $date)) return NULL;
1288      $date = date_fuzzy_datetime($date);
1289      $obj = date_create($date, timezone_open($tz));
1290      break;
1291    case DATE_ICAL:
1292      if (!preg_match(DATE_REGEX_LOOSE, $date)) return NULL;
1293      preg_match(DATE_REGEX_LOOSE, $date, $regs);
1294      $datetime = date_pad($regs[1], 4) .'-'. date_pad($regs[2]) .'-'. date_pad($regs[3]) .
1295        'T'. date_pad($regs[5]) .':'. date_pad($regs[6]) .':'. date_pad($regs[7]);
1296      $obj = date_create($datetime, timezone_open($tz));
1297      break;
1298    case DATE_UNIX:
1299      if (!is_numeric($date)) return NULL;
1300      // Special case when creating dates with timestamps.
1301      // The date_create() function will assume date is UTC value
1302      // and will ignore our timezone.
1303      $obj = date_create("@$date", timezone_open('UTC'));
1304      date_timezone_set($obj, timezone_open($tz));
1305      break;
1306  }
1307  switch ($to_type) {
1308    case DATE_OBJECT:
1309      return $obj;
1310    case DATE_DATETIME:
1311      return date_format($obj, DATE_FORMAT_DATETIME);
1312    case DATE_ISO:
1313      return date_format($obj, DATE_FORMAT_ISO);
1314    case DATE_ICAL:
1315      return date_format($obj, DATE_FORMAT_ICAL);
1316    case DATE_UNIX:
1317      return date_format($obj, 'U');
1318    case DATE_ARRAY:
1319      $date_array = date_array($obj);
1320      // ISO dates may contain zero values for some date parts,
1321      // make sure they don't get lost in the conversion.
1322      if ($from_type == DATE_ISO) {
1323        $date_array = array_merge($date_array, date_iso_array($date));
1324      }
1325      return $date_array;
1326    default:
1327      return NULL;
1328  }
1329}
1330
1331/**
1332 * Create valid datetime value from incomplete ISO dates or arrays.
1333 */
1334function date_fuzzy_datetime($date) {
1335  // A text ISO date, like MMMM-YY-DD HH:MM:SS
1336  if (!is_array($date)) {
1337    $date = date_iso_array($date);
1338  }
1339  // An date/time value in the format:
1340  //  array('date' => MMMM-YY-DD, 'time' => HH:MM:SS).
1341  elseif (array_key_exists('date', $date) || array_key_exists('time', $date)) {
1342    $date_part = array_key_exists('date', $date) ? $date['date'] : '';
1343    $time_part = array_key_exists('time', $date) ? $date['time'] : '';
1344    $date = date_iso_array(trim($date_part .' '. $time_part));
1345  }
1346  // Otherwise date must in in format:
1347  //  array('year' => YYYY, 'month' => MM, 'day' => DD).
1348  if (empty($date['year'])) {
1349    $date['year'] = date('Y');
1350  }
1351  if (empty($date['month'])) {
1352    $date['month'] = 1;
1353  }
1354  if (empty($date['day'])) {
1355    $date['day'] = 1;
1356  }
1357  foreach (array('hour', 'minute', 'second') as $part) {
1358    if (empty($date[$part])) {
1359      $date[$part] = 0;
1360    }
1361  }
1362  $value = date_pad($date['year'], 4) .'-'. date_pad($date['month']) .'-'.
1363    date_pad($date['day']) .' '. date_pad($date['hour']) .':'.
1364    date_pad($date['minute']) .':'. date_pad($date['second']);
1365  return $value;
1366}
1367
1368/**
1369 * Create an array of date parts from an ISO date.
1370 */
1371function date_iso_array($date) {
1372  preg_match(DATE_REGEX_LOOSE, $date, $regs);
1373  return array(
1374    'year' => isset($regs[1]) ? intval($regs[1]) : '',
1375    'month' => isset($regs[2]) ? intval($regs[2]) : '',
1376    'day' => isset($regs[3]) ? intval($regs[3]) : '',
1377    'hour' => isset($regs[5]) ? intval($regs[5]) : '',
1378    'minute' => isset($regs[6]) ? intval($regs[6]) : '',
1379    'second' => isset($regs[7]) ? intval($regs[7]) : '',
1380    );
1381}
1382
1383/**
1384 * Create an array of values from a date object. Structured like the
1385 * results of getdate() but not limited to the 32-bit signed range.
1386 *
1387 * @param object $obj
1388 * @return array
1389 */
1390function date_array($obj) {
1391  $year = intval(date_format($obj, 'Y'));
1392  $dow = date_format($obj, 'w');
1393  $days = date_week_days();
1394  return array(
1395    'second' => (integer) date_format($obj, 's'),
1396    'minute' => (integer) date_format($obj, 'i'),
1397    'hour' => date_format($obj, 'G'),
1398    'day' => date_format($obj, 'j'),
1399    'wday' => $dow,
1400    'month' => date_format($obj, 'n'),
1401    'year' => date_format($obj, 'Y'),
1402    'yday' => date_format($obj, 'z'),
1403    'weekday' => $days[$dow],
1404    'month_name' => date_format($obj, 'F'),
1405    0 => date_format($obj, 'U'));
1406}
1407
1408/**
1409 * Extract integer value of any date part from any type of date.
1410 *
1411 * Example:
1412 *   date_part_extract('2007-03-15 00:00', 'month', DATE_DATETIME)
1413 *   returns: 3
1414 *
1415 * @param mixed $date
1416 *   the date value to analyze.
1417 * @param string $part
1418 *   the part of the date to extract, 'year', 'month', 'day', 'hour', 'minute', 'second'
1419 * @param string $type
1420 *   the type of date supplied, DATE_ISO, DATE_UNIX, DATE_DATETIME, or DATE_OBJECT;
1421 * @return integer
1422 *   the integer value of the requested date part.
1423 */
1424function date_part_extract($date, $part, $type = DATE_DATETIME, $tz = 'UTC') {
1425  $formats = array('year' => 'Y', 'month' => 'n', 'day' => 'j',
1426    'hour' => 'G', 'minute' => 'i', 'second' => 's');
1427  $positions = array('year' => 0, 'month' => 5, 'day' => 8,
1428    'hour' => 11, 'minute' => 14, 'second' => 17);
1429  $ipositions = array('year' => 0, 'month' => 4, 'day' => 6,
1430    'hour' => 9, 'minute' => 11, 'second' => 13);
1431  switch ($type) {
1432    case DATE_ARRAY:
1433      return (integer) array_key_exists($part, $date) ? $date[$part] : NULL;
1434    case DATE_DATETIME:
1435    case DATE_ISO:
1436      return (integer) drupal_substr($date, $positions[$part], $part == 'year' ? 4 : 2);
1437    case DATE_ICAL:
1438      return (integer) drupal_substr($date, $ipositions[$part], $part == 'year' ? 4 : 2);
1439    case DATE_UNIX:
1440      // Special case when creating dates with timestamps.
1441      // The date_create() function will assume date is UTC value
1442      // and will ignore our timezone.
1443      $date = date_create("@$date", timezone_open('UTC'));
1444      date_timezone_set($date, timezone_open($tz));
1445      return date_format($date, $formats[$part]);
1446    case DATE_OBJECT:
1447      return date_format($date, $formats[$part]);
1448  }
1449}
1450
1451/**
1452 *  Functions to test the validity of a date in various formats.
1453 *  Has special case for ISO dates and arrays which can be missing
1454 *  month and day and still be valid.
1455 *
1456 *  @param $type
1457 *    could be DATE_ARRAY, DATE_UNIX, DATE_DATETIME, DATE_ISO, or DATE_OBJECT
1458 *  @param $granularity
1459 *    The granularity of the date value provided. Set this for partial
1460 *    dates so they pass validation.
1461 */
1462function date_is_valid($date, $type = DATE_DATETIME, $granularity = array('year', 'month', 'day', 'hour', 'minute')) {
1463
1464  // Check that the value is properly structured.
1465  // Remember that DATE_UNIX can have a valid value of '0', which is 'empty'.
1466  if (empty($date) && $type != DATE_UNIX) return FALSE;
1467  if ($type == DATE_OBJECT && !is_object($date)) return FALSE;
1468  if (($type == DATE_ISO || $type == DATE_DATETIME) && (!is_string($date) || !preg_match(DATE_REGEX_LOOSE, $date))) return FALSE;
1469  if ($type == DATE_UNIX and !is_numeric($date)) return FALSE;
1470  if ($type == DATE_ARRAY and !is_array($date)) return FALSE;
1471
1472  // Make sure integer values are sent to checkdate.
1473  $year = intval(date_part_extract($date, 'year', $type));
1474  $month = intval(date_part_extract($date, 'month', $type));
1475  $day = intval(date_part_extract($date, 'day', $type));
1476  if (checkdate($month, $day, $year)) {
1477    return TRUE;
1478  }
1479
1480  // If this is an incomplete date (year only or year and month only),
1481  // need special handling, partial dates can have empty date parts.
1482  $max_granularity = $granularity;
1483  $max_granularity = array_pop($max_granularity);
1484  if (!in_array($max_granularity, array('year', 'month'))) {
1485    if (in_array('year', $granularity) && !date_valid_year($year)) {
1486      return FALSE;
1487    }
1488    elseif (in_array('month', $granularity) && !date_valid_month($month)) {
1489      return FALSE;
1490    }
1491    elseif (in_array('day', $granularity) && !date_valid_day($day, $month, $year)) {
1492      return FALSE;
1493    }
1494  }
1495  // ISO dates and arrays can have empty date parts.
1496  elseif ($type == DATE_ISO || $type == DATE_ARRAY) {
1497    if (!date_valid_year($year)) {
1498      return FALSE;
1499    }
1500    elseif (!empty($month) && !date_valid_month($month)) {
1501      return FALSE;
1502    }
1503    elseif (!empty($day) && !date_valid_day($day, $month, $year)) {
1504      return FALSE;
1505    }
1506  }
1507  elseif (!date_valid_year($year) || !date_valid_month($month) || !date_valid_day($day, $month, $year)) {
1508    // Unix and datetime are expected to have at least a year, month, and day.
1509    return FALSE;
1510  }
1511
1512  return TRUE;
1513}
1514
1515function date_valid_year($year) {
1516  if (variable_get('date_max_year', 4000) < $year || variable_get('date_min_year', 1) > $year) {
1517    return FALSE;
1518  }
1519  else {
1520    return TRUE;
1521  }
1522}
1523
1524function date_valid_month($month) {
1525  if (12 < $month || 0 > $month) {
1526    return FALSE;
1527  }
1528  else {
1529    return TRUE;
1530  }
1531}
1532
1533function date_valid_day($day, $month = NULL, $year = NULL) {
1534  $days_in_month = !empty($month) && !empty($year) ? date_days_in_month($year, $month) : 31;
1535  if ($days_in_month < $day || 1 > $day) {
1536    return FALSE;
1537  }
1538  else {
1539    return TRUE;
1540  }
1541}
1542
1543/**
1544 * Helper function to left pad date parts with zeros.
1545 * Provided because this is needed so often with dates.
1546 *
1547 * @param int $value
1548 *   the value to pad
1549 * @param int $size
1550 *   total size expected, usually 2 or 4
1551 * @return string the padded value
1552 */
1553function date_pad($value, $size = 2) {
1554  return sprintf("%0". $size ."d", $value);
1555}
1556
1557/**
1558 *  Function to figure out if any time data is to be collected or displayed.
1559 *
1560 *  @param granularity
1561 *    an array like ('year', 'month', 'day', 'hour', 'minute', 'second');
1562 */
1563function date_has_time($granularity) {
1564  if (!is_array($granularity)) $granularity = array();
1565  return sizeof(array_intersect($granularity, array('hour', 'minute', 'second'))) > 0 ? TRUE : FALSE;
1566}
1567
1568function date_has_date($granularity) {
1569  if (!is_array($granularity)) $granularity = array();
1570  return sizeof(array_intersect($granularity, array('year', 'month', 'day'))) > 0 ? TRUE : FALSE;
1571}
1572/**
1573 * Recalculate a date so it only includes elements from a granularity
1574 * array. Helps prevent errors when unwanted values round up and ensures
1575 * that unwanted date part values don't get stored in the database.
1576 *
1577 * Example:
1578 *   date_limit_value('2007-05-15 04:45:59', array('year', 'month', 'day'))
1579 *   returns '2007-05-15 00:00:00'
1580 *
1581 * @param $date
1582 *   a date value
1583 * @param $granularity
1584 *   an array of allowed date parts, like ('year', 'month', 'day', 'hour', 'minute', 'second');
1585 * @param $type
1586 *   the type of date value provided,
1587 *   DATE_DATETIME, DATE_ISO, DATE_UNIX, or DATE_ARRAY
1588 * @return
1589 *   the date with the unwanted parts reset to zeros (or ones if zeros are
1590 *   invalid for that date type).
1591 */
1592function date_limit_value($date, $granularity, $type = DATE_DATETIME) {
1593  if (!date_is_valid($date, $type, $granularity) || !$nongranularity = date_nongranularity($granularity)) {
1594    return $date;
1595  }
1596  else {
1597    $date = date_convert($date, $type, DATE_ARRAY);
1598    foreach ($nongranularity as $level) {
1599      switch ($level) {
1600        case 'second':
1601          $date['second'] = 0;
1602          break;
1603        case 'minute':
1604          $date['minute'] = 0;
1605          break;
1606        case 'hour':
1607          $date['hour'] = 0;
1608          break;
1609        case 'month':
1610          $date['month'] = $type != DATE_ISO ? 1 : 0;
1611          break;
1612        case 'day':
1613          $date['day'] = $type != DATE_ISO ? 1 : 0;
1614          break;
1615      }
1616    }
1617    return date_convert($date, DATE_ARRAY, $type);
1618  }
1619}
1620
1621/**
1622 * Rewrite a format string so it only includes elements from a
1623 * specified granularity array.
1624 *
1625 * Example:
1626 *   date_limit_format('F j, Y - H:i', array('year', 'month', 'day'));
1627 *   returns 'F j, Y'
1628 *
1629 * @param $format
1630 *   a format string
1631 * @param $granularity
1632 *   an array of allowed date parts, all others will be removed
1633 *   array('year', 'month', 'day', 'hour', 'minute', 'second');
1634 * @return
1635 *   a format string with all other elements removed
1636 */
1637function date_limit_format($format, $granularity) {
1638  // If punctuation has been escaped, remove the escaping.
1639  // Done using strtr because it is easier than getting the
1640  // escape character extracted using preg_replace.
1641  $replace = array(
1642    '\-' => '-',
1643    '\:' => ':',
1644    "\'" => "'",
1645    '\.' => '.',
1646    '\,' => ',',
1647    );
1648  $format = strtr($format, $replace);
1649
1650  // Get the 'T' out of ISO date formats that don't have
1651  // both date and time.
1652  if (!date_has_time($granularity) || !date_has_date($granularity)) {
1653    $format = str_replace('\T', ' ', $format);
1654    $format = str_replace('T', ' ', $format);
1655  }
1656
1657  $regex = array();
1658  if (!date_has_time($granularity)) {
1659    $regex[] = '((?<!\\\\)[a|A])';
1660  }
1661  // Create regular expressions to remove selected values from string.
1662  // Use (?<!\\\\) to keep escaped letters from being removed.
1663  foreach (date_nongranularity($granularity) as $element) {
1664    switch ($element) {
1665      case 'year':
1666        $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[Yy])';
1667        break;
1668      case 'day':
1669        $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[l|D|d|dS|j|jS|N|w|W|z]{1,2})';
1670        break;
1671      case 'month':
1672        $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[FMmn])';
1673        break;
1674      case 'hour':
1675        $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[HhGg])';
1676        break;
1677      case 'minute':
1678        $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[i])';
1679        break;
1680      case 'second':
1681        $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[s])';
1682        break;
1683      case 'timezone':
1684        $regex[] = '([\-/\.,:]?\s?(?<!\\\\)[TOZPe])';
1685        break;
1686
1687    }
1688  }
1689  // Remove empty parentheses, brackets, pipes.
1690  $regex[] = '(\(\))';
1691  $regex[] = '(\[\])';
1692  $regex[] = '(\|\|)';
1693
1694  // Remove selected values from string.
1695  $format = trim(preg_replace($regex, array(), $format));
1696  // Remove orphaned punctuation at the beginning of the string.
1697  $format = preg_replace('`^([\-/\.,:\'])`', '', $format);
1698  // Remove orphaned punctuation at the end of the string.
1699  $format = preg_replace('([\-/\.,:\']$)', '', $format);
1700  $format = preg_replace('(\\$)', '', $format);
1701
1702  // Trim any whitespace from the result.
1703  $format = trim($format);
1704
1705  // After removing the non-desired parts of the format, test if the only
1706  // things left are escaped, non-date, characters. If so, return nothing.
1707  // Using S instead of w to pick up non-ASCII characters.
1708  $test = trim(preg_replace('(\\\\\S{1,3})', '', $format));
1709  if (empty($test)) {
1710    $format = '';
1711  }
1712  return $format;
1713}
1714
1715/**
1716 * Convert a format to an ordered array of granularity parts.
1717 *
1718 * Example:
1719 *   date_format_order('m/d/Y H:i')
1720 *   returns
1721 *     array(
1722 *       0 => 'month',
1723 *       1 => 'day',
1724 *       2 => 'year',
1725 *       3 => 'hour',
1726 *       4 => 'minute',
1727 *     );
1728 *
1729 * @param string $format
1730 * @return array of ordered granularity elements in this format string
1731 */
1732function date_format_order($format) {
1733  $order = array();
1734  if (empty($format)) return $order;
1735  $max = drupal_strlen($format);
1736  for ($i = 0; $i <= $max; $i++) {
1737    if (!isset($format[$i])) break;
1738    $c = $format[$i];
1739    switch ($c) {
1740      case 'd':
1741      case 'j':
1742        $order[] = 'day';
1743        break;
1744      case 'F':
1745      case 'M':
1746      case 'm':
1747      case 'n':
1748        $order[] = 'month';
1749        break;
1750      case 'Y':
1751      case 'y':
1752        $order[] = 'year';
1753        break;
1754      case 'g':
1755      case 'G':
1756      case 'h':
1757      case 'H':
1758        $order[] = 'hour';
1759        break;
1760      case 'i':
1761        $order[] = 'minute';
1762        break;
1763      case 's':
1764        $order[] = 'second';
1765        break;
1766    }
1767  }
1768  return $order;
1769}
1770
1771/**
1772 * An difference array of granularity elements that are NOT in the
1773 * granularity array. Used by functions that strip unwanted
1774 * granularity elements out of formats and values.
1775 *
1776 * @param $granularity
1777 *   an array like ('year', 'month', 'day', 'hour', 'minute', 'second');
1778 */
1779function date_nongranularity($granularity) {
1780  return array_diff(array('year', 'month', 'day', 'hour', 'minute', 'second', 'timezone'), (array) $granularity);
1781}
1782
1783/**
1784 * Implementation of hook_simpletest().
1785 */
1786function date_api_simpletest() {
1787  $dir = drupal_get_path('module', 'date_api') .'/tests';
1788  $tests = file_scan_directory($dir, '\.test$');
1789  return array_keys($tests);
1790}
1791
1792/**
1793 * Implementation of hook_elements().
1794 */
1795function date_api_elements() {
1796  require_once('./'. drupal_get_path('module', 'date_api') .'/date_api_elements.inc');
1797  return _date_api_elements();
1798}
1799
1800function date_api_theme() {
1801  $path = drupal_get_path('module', 'date_api');
1802  $base = array(
1803    'file' => 'theme.inc',
1804    'path' => "$path/theme",
1805  );
1806  return array(
1807    'date_nav_title' => $base + array('arguments' => array('type' => NULL, 'view' => NULL)),
1808    'date_vcalendar' => $base + array('arguments' => array('events' => NULL, 'calname' => NULL)),
1809    'date_vevent' => $base + array('arguments' => array('event' => NULL)),
1810    'date_valarm' => $base + array('arguments' => array('alarm' => NULL)),
1811    'date_timezone' => $base + array('arguments' => array('element' => NULL)),
1812    'date_select' => $base + array('arguments' => array('element' => NULL)),
1813    'date_text' => $base + array('arguments' => array('element' => NULL)),
1814    'date_select_element' => $base + array('arguments' => array('element' => NULL)),
1815    'date_textfield_element' => $base + array('arguments' => array('element' => NULL)),
1816    'date_date_part_hour_prefix' => $base + array('arguments' => array('element' => NULL)),
1817    'date_part_minsec_prefix' => $base + array('arguments' => array('element' => NULL)),
1818    'date_part_label_year' => $base + array('arguments' => array('element' => NULL)),
1819    'date_part_label_month' => $base + array('arguments' => array('element' => NULL)),
1820    'date_part_label_day' => $base + array('arguments' => array('element' => NULL)),
1821    'date_part_label_hour' => $base + array('arguments' => array('element' => NULL)),
1822    'date_part_label_minute' => $base + array('arguments' => array('element' => NULL)),
1823    'date_part_label_second' => $base + array('arguments' => array('element' => NULL)),
1824    'date_part_label_ampm' => $base + array('arguments' => array('element' => NULL)),
1825    'date_part_label_timezone' => $base + array('arguments' => array('element' => NULL)),
1826    'date_views_filter_form' => $base + array(
1827      'template' => 'date-views-filter-form',
1828      'arguments' => array('form' => NULL)),
1829    'date_calendar_day' => $base + array('arguments' => array('date' => NULL)),
1830    'date_time_ago' => $base + array('arguments' => array('start_date' => NULL, 'end_date' => NULL, 'interval' => NULL)));
1831}
1832
1833/**
1834 * Wrapper around date handler setting for timezone.
1835 */
1836function date_api_set_db_timezone($offset = '+00:00') {
1837  require_once('./'. drupal_get_path('module', 'date_api') .'/date_api_sql.inc');
1838  $handler = new date_sql_handler();
1839  return $handler->set_db_timezone($offset);
1840}
1841
1842/**
1843 *  Function to figure out which local timezone applies to a date and select it
1844 */
1845function date_get_timezone($handling, $timezone = '') {
1846  switch ($handling) {
1847    case ('date'):
1848      $timezone = !empty($timezone) ? $timezone : date_default_timezone_name();
1849      break;
1850    case ('utc'):
1851      $timezone = 'UTC';
1852      break;
1853    default:
1854      $timezone = date_default_timezone_name();
1855  }
1856  return $timezone > '' ? $timezone : date_default_timezone_name();
1857}
1858
1859/**
1860 *  Function to figure out which db timezone applies to a date and select it
1861 */
1862function date_get_timezone_db($handling, $timezone = '') {
1863  switch ($handling) {
1864    case ('none'):
1865      $timezone = date_default_timezone_name();
1866      break;
1867    default:
1868      $timezone = 'UTC';
1869      break;
1870  }
1871  return $timezone > '' ? $timezone : 'UTC';
1872}
1873
1874/**
1875 * Wrapper function to make sure this function will always work.
1876 */
1877function date_api_views_fetch_fields($base, $type) {
1878  if (!module_exists('views')) {
1879    return array();
1880  }
1881  require_once('./'. drupal_get_path('module', 'views') .'/includes/admin.inc');
1882  return views_fetch_fields($base, $type);
1883}
1884
1885/**
1886 * Get the list of date formats for a particular format length.
1887 *
1888 * @param $type
1889 *   The format type: 'short', 'medium', 'long', 'custom'.  If empty, then all
1890 *   available formats will be returned.
1891 * @param $reset
1892 *   Whether or not to reset this function's internal cache (defaults to FALSE).
1893 * @return
1894 *   Array of date formats.
1895 */
1896function date_get_formats($type = NULL, $reset = FALSE) {
1897  static $_date_formats;
1898
1899  if ($reset || !isset($_date_formats)) {
1900    $_date_formats = _date_formats_build();
1901  }
1902
1903  return $type ? (isset($_date_formats[$type]) ? $_date_formats[$type] : FALSE) : $_date_formats;
1904}
1905
1906/**
1907 * Get the format details for a particular id.
1908 *
1909 * @param $dfid
1910 *   Identifier of a date format string.
1911 * @return
1912 *   Array of date format details.
1913 */
1914function date_get_format($dfid) {
1915  $result = db_query('SELECT df.dfid, df.format, df.type, df.locked FROM {date_formats} df WHERE df.dfid = %d', $dfid);
1916  return db_fetch_array($result);
1917}
1918
1919/**
1920 * Get the list of available date format types and attributes.
1921 *
1922 * @param $type
1923 *   The format type, e.g. 'short', 'medium', 'long', 'custom'.  If empty, then
1924 *   all attributes for that type will be returned.
1925 * @param $reset
1926 *   Whether or not to reset this function's internal cache (defaults to FALSE).
1927 * @return
1928 *   Array of date format types.
1929 */
1930function date_get_format_types($type = NULL, $reset = FALSE) {
1931  static $_date_format_types;
1932
1933  if ($reset || !isset($_date_format_types)) {
1934    $_date_format_types = _date_format_types_build();
1935  }
1936
1937  return $type ? (isset($_date_format_types[$type]) ? $_date_format_types[$type] : FALSE) : $_date_format_types;
1938}
1939
1940/**
1941 * Implementation of hook_flush_caches().
1942 */
1943function date_api_flush_caches() {
1944  // Rebuild list of date formats.
1945  date_formats_rebuild();
1946  return array();
1947}
1948
1949/**
1950 * Resets the database cache of date formats, and saves all new date formats to
1951 * the database.
1952 */
1953function date_formats_rebuild() {
1954  $date_formats = date_get_formats(NULL, TRUE);
1955
1956  foreach ($date_formats as $format_type => $formats) {
1957    foreach ($formats as $format => $info) {
1958      date_format_save($info);
1959    }
1960  }
1961
1962  // Rebuild configured date formats locale list.
1963  date_format_locale(NULL, NULL, TRUE);
1964
1965  _date_formats_build();
1966}
1967
1968/**
1969 * Save a date format type to the database.
1970 *
1971 * @param $date_format_type
1972 *   An array of attributes for a date format type.
1973 */
1974function date_format_type_save($date_format_type) {
1975  $type = array();
1976  $type['type'] = $date_format_type['type'];
1977  $type['title'] = $date_format_type['title'];
1978  $type['locked'] = $date_format_type['locked'];
1979
1980  // Update date_format table.
1981  if (isset($date_format_type['is_new']) && !empty($date_format_type['is_new'])) {
1982    drupal_write_record('date_format_types', $type);
1983  }
1984  else {
1985    drupal_write_record('date_format_types', $type, 'type');
1986  }
1987}
1988
1989/**
1990 * Delete a date format type from the database.
1991 *
1992 * @param $date_format_type
1993 *   The date format type name.
1994 */
1995function date_format_type_delete($date_format_type) {
1996  db_query("DELETE FROM {date_formats} WHERE type = '%s'", $date_format_type);
1997  db_query("DELETE FROM {date_format_types} WHERE type = '%s'", $date_format_type);
1998  db_query("DELETE FROM {date_format_locale} WHERE type = '%s'", $date_format_type);
1999}
2000
2001/**
2002 * Save a date format to the database.
2003 *
2004 * @param $date_format
2005 *   An array of attributes for a date format.
2006 */
2007function date_format_save($date_format) {
2008  $format = array();
2009  $format['type'] = $date_format['type'];
2010  $format['format'] = $date_format['format'];
2011  $format['locked'] = $date_format['locked'];
2012
2013  // Update date_format table.
2014  if (isset($date_format['is_new']) && !empty($date_format['is_new'])) {
2015    drupal_write_record('date_formats', $format);
2016  }
2017  else {
2018    drupal_write_record('date_formats', $format, array('format', 'type'));
2019  }
2020
2021  $languages = language_list('enabled');
2022  $languages = $languages[1];
2023  // If site_country module is enabled, add country specific languages to
2024  // languages array.
2025  if (module_exists('site_country')) {
2026    $country_code = variable_get('site_country_default_country', '');
2027    if (!empty($country_code)) {
2028      foreach ($languages as $langcode => $details) {
2029        $country_language = $langcode . '-' . $country_code;
2030        if (drupal_strlen($langcode) == 2 && !in_array($country_language, array_keys($languages))) {
2031          $name = $details->name;
2032          $languages[$country_language] = "$name ($country_code)";
2033        }
2034      }
2035    }
2036  }
2037
2038  $locale_format = array();
2039  $locale_format['type'] = $date_format['type'];
2040  $locale_format['format'] = $date_format['format'];
2041
2042  // Check if the suggested language codes are configured and enabled.
2043  if (!empty($date_format['locales'])) {
2044    foreach ($date_format['locales'] as $langcode) {
2045      // Only proceed if language is enabled.
2046      if (in_array($langcode, $languages)) {
2047        $is_existing = db_result(db_query("SELECT COUNT(*) FROM {date_format_locale} WHERE type = '%s' AND language = '%s'", $date_format['type'], $langcode));
2048        if (!$is_existing) {
2049          $locale_format['language'] = $langcode;
2050          drupal_write_record('date_format_locale', $locale_format);
2051        }
2052      }
2053    }
2054  }
2055}
2056
2057/**
2058 * Delete a date format from the database.
2059 *
2060 * @param $date_format_id
2061 *   The date format string identifier.
2062 */
2063function date_format_delete($date_format_id) {
2064  db_query("DELETE FROM {date_formats} WHERE dfid = '%d'", $date_format_id);
2065}
2066
2067/**
2068 * Builds and returns the list of available date format types.
2069 *
2070 * @return
2071 *   Array of date format types.
2072 */
2073function _date_format_types_build() {
2074  $types = array();
2075
2076  // Prevent errors in the upgrade before the date_format_types table exists.
2077  if (defined('MAINTENANCE_MODE') && !db_table_exists('date_format_types')) {
2078    return $types;
2079  }
2080
2081  // Get list of modules which implement hook_date_format_types().
2082  $modules = module_implements('date_format_types');
2083
2084  foreach ($modules as $module) {
2085    $module_types = module_invoke($module, 'date_format_types');
2086    foreach ($module_types as $module_type => $type_title) {
2087      $type = array();
2088      $type['module'] = $module;
2089      $type['type'] = $module_type;
2090      $type['title'] = $type_title;
2091      $type['locked'] = 1;
2092      $type['is_new'] = TRUE; // Will be over-ridden later if in the db.
2093      $types[$module_type] = $type;
2094    }
2095  }
2096
2097  // Get custom formats added to the database by the end user.
2098  $result = db_query('SELECT dft.type, dft.title, dft.locked FROM {date_format_types} dft ORDER BY dft.title');
2099  while ($object = db_fetch_object($result)) {
2100    if (!in_array($object->type, $types)) {
2101      $type = array();
2102      $type['is_new'] = FALSE;
2103      $type['module'] = '';
2104      $type['type'] = $object->type;
2105      $type['title'] = $object->title;
2106      $type['locked'] = $object->locked;
2107      $types[$object->type] = $type;
2108    }
2109    else {
2110        $type = array();
2111        $type['is_new'] = FALSE;  // Over-riding previous setting.
2112        $types[$object->type] = array_merge($types[$object->type], $type);
2113    }
2114  }
2115
2116  // Allow other modules to modify these format types.
2117  drupal_alter('date_format_types', $types);
2118
2119  return $types;
2120}
2121
2122/**
2123 * Builds and returns the list of available date formats.
2124 *
2125 * @return
2126 *   Array of date formats.
2127 */
2128function _date_formats_build() {
2129  $date_formats = array();
2130
2131  // Prevent errors in the upgrade before the date_format table exists.
2132  if (defined('MAINTENANCE_MODE') && !db_table_exists('date_format')) {
2133    return $date_formats;
2134  }
2135
2136  // First handle hook_date_format_types().
2137  $types = _date_format_types_build();
2138  foreach ($types as $type => $info) {
2139    date_format_type_save($info);
2140  }
2141
2142  // Get formats supplied by various contrib modules.
2143  $module_formats = module_invoke_all('date_formats');
2144
2145  foreach ($module_formats as $module_format) {
2146    $module_format['locked'] = 1; // System types are locked.
2147    // If no format type is specified, assign 'custom'.
2148    if (!isset($module_format['type'])) {
2149      $module_format['type'] = 'custom';
2150    }
2151    if (!in_array($module_format['type'], array_keys($types))) {
2152      continue;
2153    }
2154    if (!isset($date_formats[$module_format['type']])) {
2155      $date_formats[$module_format['type']] = array();
2156    }
2157
2158    // If another module already set this format, merge in the new settings.
2159    if (isset($date_formats[$module_format['type']][$module_format['format']])) {
2160      $date_formats[$module_format['type']][$module_format['format']] = array_merge_recursive($date_formats[$module_format['type']][$module_format['format']], $format);
2161    }
2162    else {
2163      // This setting will be overridden later if it already exists in the db.
2164      $module_format['is_new'] = TRUE;
2165      $date_formats[$module_format['type']][$module_format['format']] = $module_format;
2166    }
2167  }
2168
2169  // Get custom formats added to the database by the end user.
2170  $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');
2171  while ($object = db_fetch_object($result)) {
2172    // If this format type isn't set, initialise the array.
2173    if (!isset($date_formats[$object->type])) {
2174      $date_formats[$object->type] = array();
2175    }
2176    // If this format not already present, add it to the array.
2177    if (!isset($date_formats[$object->type][$object->format])) {
2178      // We don't set 'is_new' as it is already in the db.
2179      $format = array();
2180      $format['module'] = '';
2181      $format['dfid'] = $object->dfid;
2182      $format['format'] = $object->format;
2183      $format['type'] = $object->type;
2184      $format['locked'] = $object->locked;
2185      $format['locales'] = array($object->language);
2186      $date_formats[$object->type][$object->format] = $format;
2187    }
2188    // Format already present, so merge in settings.
2189    else {
2190      $format = array();
2191      $format['is_new'] = FALSE; // It's in the db, so override this setting.
2192      $format['dfid'] = $object->dfid;
2193      $format['format'] = $object->format;
2194      $format['type'] = $object->type;
2195      $format['locked'] = $object->locked;
2196      if (!empty($object->language)) {
2197        $format['locales'] = array_merge($date_formats[$object->type][$object->format]['locales'], array($object->language));
2198      }
2199      $date_formats[$object->type][$object->format] = array_merge($date_formats[$object->type][$object->format], $format);
2200    }
2201  }
2202
2203  // Allow other modules to modify these formats.
2204  drupal_alter('date_formats', $date_formats);
2205
2206  return $date_formats;
2207}
2208
2209/**
2210 * Get the appropriate date format for a type and locale.
2211 *
2212 * @param $langcode
2213 *   Language code for the current locale.  This can be a 2 character language
2214 *   code like 'en', 'fr', or a longer 5 character code like 'en-gb'.
2215 * @param $type
2216 *   Date format type: short, medium, long, custom.
2217 * @param $reset
2218 *   Whether or not to reset this function's internal cache (defaults to FALSE).
2219 * @return
2220 *   The format string, or NULL if no matching format found.
2221 */
2222function date_format_locale($langcode = NULL, $type = NULL, $reset = FALSE) {
2223  static $formats;
2224
2225  if ($reset || empty($formats)) {
2226    $formats = array();
2227    $result = db_query("SELECT format, type, language FROM {date_format_locale}");
2228    while ($object = db_fetch_object($result)) {
2229      if (!isset($formats[$object->language])) {
2230        $formats[$object->language] = array();
2231      }
2232      $formats[$object->language][$object->type] = $object->format;
2233    }
2234  }
2235
2236  if ($type && $langcode && !empty($formats[$langcode][$type])) {
2237    return $formats[$langcode][$type];
2238  }
2239  elseif ($langcode && !empty($formats[$langcode])) {
2240    return $formats[$langcode];
2241  }
2242
2243  return FALSE;
2244}
2245
2246/**
2247 * Helper function for BYDAY options in Date Repeat
2248 * and for converting back and forth from '+1' to 'First'.
2249 */
2250function date_order_translated() {
2251  return array(
2252    '+1' => date_t('First', 'date_order'),
2253    '+2' => date_t('Second', 'date_order'),
2254    '+3' => date_t('Third', 'date_order'),
2255    '+4' => date_t('Fourth', 'date_order'),
2256    '+5' => date_t('Fifth', 'date_order'),
2257    '-1' => date_t('Last', 'date_order_reverse'),
2258    '-2' => date_t('Next to last', 'date_order_reverse'),
2259    '-3' => date_t('Third from last', 'date_order_reverse'),
2260    '-4' => date_t('Fourth from last', 'date_order_reverse'),
2261    '-5' => date_t('Fifth from last', 'date_order_reverse')
2262  );
2263}
2264
2265function date_order() {
2266  return array(
2267    '+1' => 'First',
2268    '+2' => 'Second',
2269    '+3' => 'Third',
2270    '+4' => 'Fourth',
2271    '+5' => 'Fifth',
2272    '-1' => 'Last',
2273    '-2' => '-2',
2274    '-3' => '-3',
2275    '-4' => '-4',
2276    '-5' => '-5'
2277  );
2278}
2279
2280/**
2281 * Implementation of hook_views_api().
2282 *
2283 * This one is used as the base to reduce errors when updating.
2284 */
2285function date_api_views_api() {
2286  return array(
2287    'api' => 2,
2288    'path' => drupal_get_path('module', 'date_api') .'/includes',
2289  );
2290}
2291
2292/**
2293 * Implementation of hook_date_api_fields().
2294 * on behalf of core fields.
2295 *
2296 * All modules that create custom fields that use the
2297 * 'views_handler_field_date' handler can provide
2298 * additional information here about the type of
2299 * date they create so the date can be used by
2300 * the Date API views date argument and date filter.
2301 */
2302function date_api_date_api_fields($field) {
2303  $values = array(
2304    // The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME.
2305    'sql_type' => DATE_UNIX,
2306    // Timezone handling options: 'none', 'site', 'date', 'utc'.
2307    'tz_handling' => 'site',
2308    // Needed only for dates that use 'date' tz_handling.
2309    'timezone_field' => '',
2310    // Needed only for dates that use 'date' tz_handling.
2311    'offset_field' => '',
2312    // Array of "table.field" values for related fields that should be
2313    // loaded automatically in the Views SQL.
2314    'related_fields' => array(),
2315    // Granularity of this date field's db data.
2316    'granularity' => array('year', 'month', 'day', 'hour', 'minute', 'second'),
2317  );
2318
2319  switch ($field) {
2320    case 'users.created':
2321    case 'users.access':
2322    case 'users.login':
2323    case 'node.created':
2324    case 'node.changed':
2325    case 'node_revisions.timestamp':
2326    case 'files.timestamp':
2327    case 'node_counter.timestamp':
2328    case 'accesslog.timestamp':
2329    case 'comments.timestamp':
2330    case 'node_comment_statistics.last_comment_timestamp':
2331      return $values;
2332  }
2333}
2334
2335/**
2336 * Rebuild the theme registry and all the caches.
2337 * needed to pick up changes created by updated Views API
2338 * and other changes to Views definitions.
2339 */
2340function date_api_views_clear() {
2341  if (db_table_exists('cache_content')) {
2342    db_query('DELETE FROM {cache_content}');
2343  }
2344  if (db_table_exists('cache_views')) {
2345    db_query('DELETE FROM {cache_views}');
2346  }
2347  if (db_table_exists('views_object_cache')) {
2348    db_query('DELETE FROM {views_object_cache}');
2349  }
2350  db_query("DELETE FROM {cache} where cid LIKE 'theme_registry%'");
2351}
2352
2353/**
2354 * Embed a view using a PHP snippet.
2355 *
2356 * This function is meant to be called from PHP snippets, should one wish to
2357 * embed a view in a node or something. It's meant to provide the simplest
2358 * solution and doesn't really offer a lot of options, but breaking the function
2359 * apart is pretty easy, and this provides a worthwhile guide to doing so.
2360 *
2361 * Note that this function does NOT display the title of the view. If you want
2362 * to do that, you will need to do what this function does manually, by
2363 * loading the view, getting the preview and then getting $view->get_title().
2364 *
2365 * @param $name
2366 *   The name of the view to embed.
2367 *
2368 * @param $display_id
2369 *   'calendar_1' will display the calendar page,
2370 *   'calendar_block_1' will display the calendar block.
2371 *
2372 * @param $settings
2373 *   an array of view settings to use to override view default values;
2374 *
2375 *   Include a setting for 'block_identifier, the identifier to use
2376 *   for this embedded view. All embedded views that use the same
2377 *   identifier will move together, or provide different identifiers
2378 *   to keep them independent. The identifier will be used in the url
2379 *   as a querystring, like: node/27?mini=calendar/2008-10.
2380 *
2381 * @param ...
2382 *   Any additional parameters will be passed as arguments.
2383 */
2384function date_embed_view($name, $display_id = 'default', $settings = array(), $args = array()) {
2385  $view = views_get_view($name);
2386  if (!$view) {
2387    return;
2388  }
2389  if (!empty($settings)) {
2390    foreach ($settings as $key => $setting) {
2391      $view->$key = $setting;
2392    }
2393  }
2394  if (!isset($view->date_info->block_identifier)) {
2395    $view->date_info->block_identifier = 'mini';
2396  }
2397  return $view->preview($display_id, $args);
2398}
2399
2400/**
2401 * Figure out the URL of the date view we're currently looking at,
2402 * adapted to various date types or specific date arguments.
2403 *
2404 * @param $date_type
2405 *  - if not empty, return the url of a specific date type.
2406 * @param $date_arg
2407 *  - if not empty, return the url for a view with a specific date argument.
2408 * @param $force_view_url
2409 *  - always use the view url, even if embedded.
2410 * @return
2411 *   return the requested view url.
2412 */
2413function date_real_url($view, $date_type = NULL, $date_arg = NULL, $force_view_url = FALSE) {
2414  $args = $view->args;
2415  $pos = $view->date_info->date_arg_pos;
2416
2417  // The View arguments array is indexed numerically but is not necessarily
2418  // in numerical order. Sort the arguments to ensure the correct order.
2419  ksort($args);
2420
2421  // If there are empty arguments before the date argument,
2422  // pad them with the wildcard so the date argument will be in
2423  // the right position.
2424  if (count($args) < $pos) {
2425    foreach ($view->argument as $name => $argument) {
2426      if ($argument->position == $pos) {
2427        break;
2428      }
2429      $args[] = $argument->options['wildcard'];
2430    }
2431  }
2432
2433  if (!empty($date_type)) {
2434    switch ($date_type) {
2435      case 'year':
2436        $args[$pos] = date_pad($view->date_info->year, 4);
2437        break;
2438      case 'week':
2439        $args[$pos] = date_pad($view->date_info->year, 4) .'-W'. date_pad($view->date_info->week);
2440        break;
2441      case 'day':
2442        $args[$pos] = date_pad($view->date_info->year, 4) .'-'. date_pad($view->date_info->month) .'-'. date_pad($view->date_info->day);
2443        break;
2444      default:
2445        $args[$pos] = date_pad($view->date_info->year, 4) .'-'. date_pad($view->date_info->month);
2446        break;
2447    }
2448  }
2449  elseif (!empty($date_arg)) {
2450    $args[$pos] = $date_arg;
2451  }
2452  else {
2453    $args = $view->args;
2454  }
2455  // Is this an embedded or a block view?
2456  if (!$force_view_url &&
2457  (!empty($view->preview) || !empty($view->date_info->block_identifier))) {
2458    $url = $view->get_url($args);
2459    $key = date_block_identifier($view);
2460    if (!empty($key)) {
2461      return url($_GET['q'], array(
2462        'query' => date_querystring($view, array($key => $url)),
2463        'absolute' => TRUE));
2464    }
2465  }
2466  // Normal views may need querystrings appended to them
2467  // if they use exposed filters.
2468  return url($view->get_url($args), array(
2469    'query' => date_querystring($view),
2470    'absolute' => TRUE));
2471}
2472
2473/**
2474 * Pick up filter and sort info from url.
2475 */
2476function date_querystring($view, $extra_params = array()) {
2477  $query_params = array_merge($_GET, $extra_params);
2478  // Allow NULL params to be removed from the query string.
2479  foreach ($extra_params AS $key => $value) {
2480    if (!isset($value)) {
2481      unset($query_params[$key]);
2482    }
2483  }
2484  // Filter the special "q" and "view" variables out of the query string.
2485  $exclude = array('q');
2486  $query = drupal_query_string_encode($query_params, $exclude);
2487  // To prevent an empty query string from adding a "?" on to the end of a URL,
2488  // we return NULL.
2489  return !empty($query) ? $query : NULL;
2490}
2491
2492function date_block_identifier($view) {
2493  if (!empty($view->block_identifier)) {
2494    return $view->block_identifier;
2495  }
2496  return isset($view->date_info->block_identifier) ? $view->date_info->block_identifier : NULL;
2497}
2498
2499/**
2500 * Implementation of hook_form_alter().
2501 *
2502 * Add new submit handler for system_modules form.
2503 */
2504function date_api_form_system_modules_alter(&$form, $form_state, $form_id = 'system_modules') {
2505  $form['#submit'][] = 'date_api_system_modules_submit';
2506}
2507
2508/**
2509 * Rebuild list of date formats when modules list is saved.
2510 */
2511function date_api_system_modules_submit($form, &$form_state) {
2512  date_formats_rebuild();
2513}
2514
2515/**
2516 * Implementation of hook_form_alter().
2517 *
2518 * Remove the 'date_formats' section from the 'admin/settings/date-time' page.
2519 * This form section is now part of the form at 'admin/settings/date-time/formats'.
2520 * We add the formats as values to the form to avoid errors on submission
2521 * of the form when expected values are missing in system_date_time_settings_submit().
2522 *
2523 * Add a form element to configure whether or not week numbers are ISO-8601 (default: FALSE == US/UK/AUS norm).
2524 */
2525function date_api_form_system_date_time_settings_alter(&$form, $form_state, $form_id = 'system_date_time_settings') {
2526  include_once(drupal_get_path('module', 'date_api') .'/date_api.admin.inc');
2527  $formats_form = date_api_date_formats_form($form_state);
2528  $form['date_formats'] = $formats_form['date_formats'];
2529  foreach ($form['date_formats'] as $key => $value) {
2530    if (drupal_substr($key, 0, 1) != '#') {
2531      $form['date_formats'][$key]['#type'] = 'value';
2532    }
2533    else {
2534      unset($form['date_formats'][$key]);
2535    }
2536  }
2537  $form['locale']['date_api_use_iso8601'] = array(
2538    '#type'          => 'checkbox',
2539    '#title'         => t('Use ISO-8601 week numbers'),
2540    '#default_value' => variable_get('date_api_use_iso8601', FALSE),
2541    '#description'   => t('IMPORTANT! If checked, First day of week MUST be set to Monday'),
2542  );
2543  $form['#validate'][] = 'date_api_form_system_settings_validate';
2544}
2545
2546/**
2547 * Validate that the option to use ISO weeks matches first day of week choice.
2548 */
2549function date_api_form_system_settings_validate(&$form, &$form_state) {
2550  $form_values = $form_state['values'];
2551  if ($form_values['date_api_use_iso8601'] && $form_values['date_first_day'] != 1) {
2552    form_set_error('date_first_day', t('When using ISO-8601 week numbers, the first day of the week must be set to Monday.'));
2553  }
2554}
2555
2556/**
2557 * Helper function; add system.js and javascript settings.
2558 */
2559function date_api_add_system_javascript() {
2560  drupal_add_js(drupal_get_path('module', 'date_api') .'/date_api.js', 'module');
2561  drupal_add_js(array('dateDateTime' => array('lookup' => url('admin/settings/date-time/formats/lookup'))), 'setting');
2562}
2563
2564/**
2565 * Return the date for a given format string via Ajax.
2566 */
2567function date_api_date_time_lookup() {
2568  $result = date_format_date(date_now(), 'custom', $_GET['format']);
2569  echo drupal_to_js($result);
2570  exit;
2571}
2572
2573/*
2574 * Test validity of a date range string.
2575 */
2576function date_range_valid($string) {
2577  $matches = preg_match('@^(\-[0-9]+|[0-9]{4}):([\+|\-][0-9]+|[0-9]{4})$@', $string);
2578  return $matches < 1 ? FALSE : TRUE;
2579}
2580
2581/**
2582 * Split a string like -3:+3 or 2001:2010 into
2583 * an array of min and max years.
2584 *
2585 * Center the range around the current year, if any, but expand it far
2586 * enough so it will pick up the year value in the field in case
2587 * the value in the field is outside the initial range.
2588 */
2589function date_range_years($string, $date = NULL) {
2590  $this_year = date_format(date_now(), 'Y');
2591  list($min_year, $max_year) = explode(':', $string);
2592
2593  // Valid patterns would be -5:+5, 0:+1, 2008:2010.
2594  $plus_pattern = '@[\+|\-][0-9]{1,4}@';
2595  $year_pattern = '@[0-9]{4}@';
2596  if (!preg_match($year_pattern, $min_year, $matches)) {
2597    if (preg_match($plus_pattern, $min_year, $matches)) {
2598      $min_year = $this_year + $matches[0];
2599    }
2600    else {
2601      $min_year = $this_year;
2602    }
2603  }
2604  if (!preg_match($year_pattern, $max_year, $matches)) {
2605    if (preg_match($plus_pattern, $max_year, $matches)) {
2606      $max_year = $this_year + $matches[0];
2607    }
2608    else {
2609      $max_year = $this_year;
2610    }
2611  }
2612  // We expect the $min year to be less than the $max year.
2613  // Some custom values for -99:+99 might not obey that.
2614  if ($min_year > $max_year) {
2615    $temp = $max_year;
2616    $max_year = $min_year;
2617    $min_year = $temp;
2618  }
2619  // If there is a current value, stretch the range to include it.
2620  $value_year = is_object($date) ? date_format($date, 'Y') : '';
2621  if (!empty($value_year)) {
2622    $min_year = min($value_year, $min_year);
2623    $max_year = max($value_year, $max_year);
2624  }
2625  return array($min_year, $max_year);
2626}
2627
2628/**
2629 * Convert a min and max year into a string like '-3:+1'.
2630 *
2631 * @param unknown_type $years
2632 * @return unknown
2633 */
2634function date_range_string($years) {
2635  $this_year = date_format(date_now(), 'Y');
2636  if ($years[0] < $this_year) {
2637    $min = '-'. ($this_year - $years[0]);
2638  }
2639  else {
2640    $min = '+'. ($years[0] - $this_year);
2641  }
2642  if ($years[1] < $this_year) {
2643    $max = '-'. ($this_year - $years[1]);
2644  }
2645  else {
2646    $max = '+'. ($years[1] - $this_year);
2647  }
2648  return $min .':'. $max;
2649}
2650
2651/**
2652 * Implement hook_date_api_tables().
2653 */
2654function date_api_date_api_tables() {
2655  return array('node', 'comments', 'users');
2656}
2657
2658/**
2659 * Determine if a from/to date combination qualify as 'All day'.
2660 *
2661 * @param object $date1, a string date in datetime format for the 'from' date.
2662 * @param object $date2, a string date in datetime format for the 'to' date.
2663 * @return TRUE or FALSE.
2664 */
2665function date_is_all_day($string1, $string2, $granularity = 'second', $increment = 1) {
2666  if (empty($string1) || empty($string2)) {
2667    return FALSE;
2668  }
2669  elseif (!in_array($granularity, array('hour', 'minute', 'second'))) {
2670    return FALSE;
2671  }
2672
2673  preg_match('/([0-9]{4}-[0-9]{2}-[0-9]{2}) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/', $string1, $matches);
2674  $count = count($matches);
2675  $date1 = $count > 1 ? $matches[1] : '';
2676  $time1 = $count > 2 ? $matches[2] : '';
2677  $hour1 = $count > 3 ? intval($matches[3]) : 0;
2678  $min1 = $count > 4 ? intval($matches[4]) : 0;
2679  $sec1 = $count > 5 ? intval($matches[5]) : 0;
2680  preg_match('/([0-9]{4}-[0-9]{2}-[0-9]{2}) (([0-9]{2}):([0-9]{2}):([0-9]{2}))/', $string2, $matches);
2681  $count = count($matches);
2682  $date2 = $count > 1 ? $matches[1] : '';
2683  $time2 = $count > 2 ? $matches[2] : '';
2684  $hour2 = $count > 3 ? intval($matches[3]) : 0;
2685  $min2 = $count > 4 ? intval($matches[4]) : 0;
2686  $sec2 = $count > 5 ? intval($matches[5]) : 0;
2687  if (empty($date1) || empty($date2)) {
2688    return FALSE;
2689  }
2690  if (empty($time1) || empty($time2)) {
2691    return FALSE;
2692  }
2693
2694  $tmp = date_seconds('s', TRUE, $increment);
2695  $max_seconds = intval(array_pop($tmp));
2696  $tmp = date_minutes('i', TRUE, $increment);
2697  $max_minutes = intval(array_pop($tmp));
2698
2699  // See if minutes and seconds are the maximum allowed for an increment or the
2700  // maximum possible (59), or 0.
2701  switch ($granularity) {
2702    case 'second':
2703      $min_match = $time1 == '00:00:00'
2704        || ($hour1 == 0 && $min1 == 0 && $sec1 == 0);
2705      $max_match = $time2 == '00:00:00'
2706        || ($hour2 == 23 && in_array($min2, array($max_minutes, 59)) && in_array($sec2, array($max_seconds, 59)))
2707        || ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0 && $sec1 == 0 && $sec2 == 0);
2708      break;
2709    case 'minute':
2710      $min_match = $time1 == '00:00:00'
2711        || ($hour1 == 0 && $min1 == 0);
2712      $max_match = $time2 == '00:00:00'
2713        || ($hour2 == 23 && in_array($min2, array($max_minutes, 59)))
2714        || ($hour1 == 0 && $hour2 == 0 && $min1 == 0 && $min2 == 0);
2715      break;
2716    case 'hour':
2717      $min_match = $time1 == '00:00:00'
2718        || ($hour1 == 0);
2719      $max_match = $time2 == '00:00:00'
2720        || ($hour2 == 23)
2721        || ($hour1 == 0 && $hour2 == 0);
2722      break;
2723    default:
2724      $min_match = TRUE;
2725      $max_match = FALSE;
2726  }
2727
2728  if ($min_match && $max_match) {
2729    return TRUE;
2730  }
2731
2732  return FALSE;
2733}
2734
2735
2736/**
2737 * Helper function to round minutes and seconds to requested value.
2738 */
2739function date_increment_round(&$date, $increment) {
2740  // Round minutes and seconds, if necessary.
2741  if (is_object($date) && $increment > 1) {
2742    $day = intval(date_format($date, 'j'));
2743    $hour = intval(date_format($date, 'H'));
2744    $second = intval(round(intval(date_format($date, 's')) / $increment) * $increment);
2745    $minute = intval(date_format($date, 'i'));
2746    if ($second == 60) {
2747      $minute += 1;
2748      $second = 0;
2749    }
2750    $minute = intval(round($minute / $increment) * $increment);
2751    if ($minute == 60) {
2752      $hour += 1;
2753      $minute = 0;
2754    }
2755    date_time_set($date, $hour, $minute, $second);
2756    if ($hour == 24) {
2757      $day += 1;
2758      $hour = 0;
2759      $year = date_format($date, 'Y');
2760      $month = date_format($date, 'n');
2761      date_date_set($date, $year, $month, $day);
2762    }
2763  }
2764  return $date;
2765}
2766
2767/**
2768 * Return the nested form elements for a field by name.
2769 * This can be used either to retrieve the entire sub-element
2770 * for a field by name, no matter how deeply nested it is within
2771 * fieldgroups or multigroups, or to find the multiple value
2772 * sub-elements within a field element by name (i.e. 'value' or
2773 * 'rrule'). You can also use this function to see if an item exists
2774 * in a form (the return will be an empty array if it does not exist).
2775 *
2776 * The function returns an array of results. A field will generally
2777 * only exist once in a form but the function can also be used to
2778 * locate all the 'value' elements within a multiple value field,
2779 * so the result is always returned as an array of values.
2780 *
2781 * For example, for a field named field_custom,  the following will
2782 * pluck out the form elements for this field from the node form,
2783 * no matter how deeply it is nested within fieldgroups or fieldsets:
2784 *
2785 * $elements = content_get_nested_elements($node_form, 'field_custom');
2786 *
2787 * You can prefix the function with '&' to retrieve the element by
2788 * reference to alter it directly:
2789 *
2790 * $elements = &content_get_nested_elements($form, 'field_custom');
2791 * foreach ($elements as $element) {
2792 *   $element['#after_build'][] = 'my_field_afterbuild';
2793 * }
2794 *
2795 * During the #after_build you could then do something like the
2796 * following to alter each individual part of a multiple value field:
2797 *
2798 * $sub_elements = &content_get_nested_elements($element, 'value');
2799 * foreach ($sub_elements as $sub_element) {
2800 *   $sub_element['#element_validate'][] = 'custom_validation';
2801 * }
2802 *
2803 * @param $form
2804 *   The form array to search.
2805 * @param $field_name
2806 *   The name or key of the form elements to return.
2807 * @return
2808 *   An array of all matching form elements, returned by reference.
2809 */
2810function &date_get_nested_elements(&$form, $field_name) {
2811  $elements = array();
2812
2813  foreach (element_children($form) as $key) {
2814    if ($key === $field_name) {
2815      $elements[] = &$form[$key];
2816    }
2817    elseif (is_array($form[$key])) {
2818      $nested_form = &$form[$key];
2819      if ($sub_elements = &date_get_nested_elements($nested_form, $field_name)) {
2820        $elements = array_merge($elements, $sub_elements);
2821      }
2822    }
2823  }
2824
2825  return $elements;
2826}
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.