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