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