| [ Index ] |
PHP Cross Reference of Drupal 6 (yi-drupal) |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * A helper function to do cross-database concatation of date parts 5 * 6 * @param $array - an array of values to be concatonated in sql 7 * @return - correct sql string for database type 8 */ 9 function date_sql_concat($array) { 10 global $db_type; 11 switch ($db_type) { 12 case ('mysql'): 13 case ('mysqli'): 14 return "CONCAT(". implode(",", $array) .")"; 15 case ('pgsql'): 16 return implode(" || ", $array); 17 } 18 } 19 20 /** 21 * Helper function to do cross-database NULL replacements 22 * 23 * @param an array of values to test for NULL values 24 * @return SQL statement to return the first non-NULL value in the list. 25 */ 26 function date_sql_coalesce($array) { 27 global $db_type; 28 switch ($db_type) { 29 case ('mysql'): 30 case ('mysqli'): 31 case ('pgsql'): 32 return "COALESCE(". implode(',', $array) .")"; 33 } 34 } 35 36 /** 37 * A helper function to do cross-database padding of date parts 38 * 39 * @param $str - a string to apply padding to 40 * @param $size - the size the final string should be 41 * @param $pad - the value to pad the string with 42 * @param $side - the side of the string to pad 43 */ 44 function date_sql_pad($str, $size = 2, $pad = '0', $side = 'l') { 45 switch ($side) { 46 case ('r'): 47 return "RPAD($str, $size, '$pad')"; 48 default: 49 return "LPAD($str, $size, '$pad')"; 50 } 51 } 52 53 /** 54 * A class to manipulate date SQL. 55 */ 56 class date_sql_handler { 57 var $db_type = 'mysql'; 58 var $date_type = DATE_DATETIME; 59 var $db_timezone = 'UTC'; // A string timezone name. 60 var $local_timezone = NULL; // A string timezone name. 61 var $db_timezone_field = NULL; // Use if the db timezone is stored in a field. 62 var $local_timezone_field = NULL; // Use if the local timezone is stored in a field. 63 var $offset_field = NULL; // Use if the offset is stored in a field. 64 65 function construct($date_type = DATE_DATETIME, $local_timezone = NULL) { 66 $this->db_type = $GLOBALS['db_type']; 67 $this->date_type = $date_type; 68 $this->db_timezone = 'UTC'; 69 $this->local_timezone = isset($local_timezone) ? $local_timezone : date_default_timezone_name(); 70 if (isset($this->definition['content_field'])) { 71 $this->date_handler->date_type = $this->definition['content_field']['type']; 72 } 73 date_api_set_db_timezone(); 74 } 75 76 /** 77 * See if the db has timezone name support. 78 */ 79 function db_tz_support($reset = FALSE) { 80 $has_support = variable_get('date_db_tz_support', -1); 81 if ($has_support == -1 || $reset) { 82 date_api_set_db_timezone(); 83 $has_support = FALSE; 84 switch ($this->db_type) { 85 case 'mysql': 86 case 'mysqli': 87 if (version_compare(db_version(), '4.1.3', '>=')) { 88 $test = db_result(db_query("SELECT CONVERT_TZ('2008-02-15 12:00:00', 'UTC', 'US/Central')")); 89 if ($test == '2008-02-15 06:00:00') { 90 $has_support = TRUE; 91 } 92 } 93 break; 94 case 'pgsql': 95 $test = db_result(db_query("SELECT '2008-02-15 12:00:00 UTC' AT TIME ZONE 'US/Central'")); 96 if ($test == '2008-02-15 06:00:00') { 97 $has_support = TRUE; 98 } 99 break; 100 } 101 variable_set('date_db_tz_support', $has_support); 102 } 103 return $has_support; 104 } 105 106 /** 107 * Set the database timzone offset. 108 * 109 * Setting the db timezone to UTC is done to ensure consistency in date 110 * handling whether or not the database can do proper timezone conversion. 111 * 112 * Views filters that not exposed are cached and won't set the timezone 113 * so views date filters should add 'cacheable' => 'no' to their 114 * definitions to ensure that the database timezone gets set properly 115 * when the query is executed. 116 * 117 * @param $offset 118 * An offset value to set the database timezone to. This will only 119 * set a fixed offset, not a timezone, so any value other than 120 * '+00:00' should be used with caution. 121 */ 122 function set_db_timezone($offset = '+00:00') { 123 static $already_set = FALSE; 124 $type = $GLOBALS['db_type']; 125 if (!$already_set) { 126 if (($type == 'mysqli' || $type == 'mysql') && version_compare(db_version(), '4.1.3', '>=')) { 127 db_query("SET @@session.time_zone = '$offset'"); 128 } 129 elseif ($type == 'pgsql') { 130 db_query("SET TIME ZONE INTERVAL '$offset' HOUR TO MINUTE"); 131 } 132 $already_set = true; 133 } 134 } 135 136 /** 137 * Return timezone offset for the date being processed. 138 */ 139 function get_offset() { 140 if (!empty($this->db_timezone) && !empty($this->local_timezone)) { 141 if ($this->db_timezone != $this->local_timezone) { 142 $date = date_now($this->db_timezone); 143 date_timezone_set($date, timezone_open($this->local_timezone)); 144 return date_offset_get($date); 145 } 146 } 147 return 0; 148 } 149 150 /** 151 * Helper function to create cross-database SQL dates. 152 * 153 * @param $field 154 * The real table and field name, like 'tablename.fieldname'. 155 * @param $offset 156 * The name of a field that holds the timezone offset or an 157 * offset value. If NULL, the normal Drupal timezone handling 158 * will be used, if $offset = 0 no adjustment will be made. 159 * @return 160 * An appropriate SQL string for the db type and field type. 161 */ 162 function sql_field($field, $offset = NULL) { 163 if (strtoupper($field) == 'NOW') { 164 // NOW() will be in UTC since that is what we set the db timezone to. 165 $this->local_timezone = 'UTC'; 166 return $this->sql_offset('NOW()', $offset); 167 } 168 switch ($this->db_type) { 169 case 'mysql': 170 case 'mysqli': 171 switch ($this->date_type) { 172 case DATE_UNIX: 173 $field = "FROM_UNIXTIME($field)"; 174 break; 175 case DATE_ISO: 176 if (version_compare(db_version(), '4.1.1', '>=')) { 177 $field = "STR_TO_DATE($field, '%Y-%m-%%dT%T')"; 178 } 179 else { 180 $field = "REPLACE($field, 'T', ' ')"; 181 } 182 break; 183 case DATE_DATETIME: 184 break; 185 } 186 break; 187 case 'pgsql': 188 switch ($this->date_type) { 189 case DATE_UNIX: 190 $field = "$field::ABSTIME"; 191 break; 192 case DATE_ISO: 193 $field = "TO_DATE($field, 'FMYYYY-FMMM-FMDDTFMHH24:FMMI:FMSS')"; 194 break; 195 case DATE_DATETIME: 196 break; 197 } 198 break; 199 } 200 // Adjust the resulting value to the right timezone/offset. 201 return $this->sql_tz($field, $offset); 202 } 203 204 /** 205 * Adjust a field value by an offset in seconds. 206 */ 207 function sql_offset($field, $offset = NULL) { 208 if (!empty($offset)) { 209 switch ($this->db_type) { 210 case 'mysql': 211 case 'mysqli': 212 if (version_compare(db_version(), '4.1.1', '>=')) { 213 return "ADDTIME($field, SEC_TO_TIME($offset))"; 214 } 215 else { 216 return "DATE_ADD($field, INTERVAL $offset SECOND)"; 217 } 218 case 'pgsql': 219 return "($field + INTERVAL '$offset SECONDS')";; 220 } 221 } 222 return $field; 223 } 224 225 /** 226 * Adjust a field value by time interval. 227 * 228 * @param $field 229 * The field to be adjusted. 230 * @param $direction 231 * Either ADD or SUB. 232 * @param $count 233 * The number of values to adjust. 234 * @param $granularity 235 * The granularity of the adjustment, should be singular, 236 * like SECOND, MINUTE, DAY, HOUR. 237 */ 238 function sql_date_math($field, $direction, $count, $granularity) { 239 $granularity = strtoupper($granularity); 240 switch ($this->db_type) { 241 case 'mysql': 242 case 'mysqli': 243 switch ($direction) { 244 case 'ADD': 245 return "DATE_ADD($field, INTERVAL $count $granularity)"; 246 case 'SUB': 247 return "DATE_SUB($field, INTERVAL $count $granularity)"; 248 } 249 250 case 'pgsql': 251 $granularity .= 'S'; 252 switch ($direction) { 253 case 'ADD': 254 return "($field + INTERVAL '$count $granularity')"; 255 case 'SUB': 256 return "($field - INTERVAL '$count $granularity')"; 257 } 258 } 259 return $field; 260 } 261 262 /** 263 * Select a date value from the database, adjusting the value 264 * for the timezone. 265 * 266 * Check whether database timezone conversion is supported in 267 * this system and use it if possible, otherwise use an 268 * offset. 269 * 270 * @param $offset 271 * Set a fixed offset or offset field to use for the date. 272 * If set, no timezone conversion will be done and the 273 * offset will be used. 274 */ 275 function sql_tz($field, $offset = NULL) { 276 // If the timezones are values they need to be quoted, but 277 // if they are field names they do not. 278 $db_zone = $this->db_timezone_field ? $this->db_timezone_field : "'{$this->db_timezone}'"; 279 $localzone = $this->local_timezone_field ? $this->local_timezone_field : "'{$this->local_timezone}'"; 280 281 // If a fixed offset is required, use it. 282 if ($offset !== NULL) { 283 return $this->sql_offset($field, $offset); 284 } 285 // If the db and local timezones are the same, make no adjustment. 286 elseif ($db_zone == $localzone) { 287 return $this->sql_offset($field, 0); 288 } 289 // If the db has no timezone support, adjust by the offset, 290 // could be either a field name or a value. 291 elseif (!$this->db_tz_support()) { 292 if (!empty($this->offset_field)) { 293 return $this->sql_offset($field, $this->offset_field); 294 } 295 else { 296 return $this->sql_offset($field, $this->get_offset()); 297 } 298 } 299 // Otherwise make a database timezone adjustment to the field. 300 else { 301 switch ($this->db_type) { 302 case 'mysql': 303 case 'mysqli': 304 return "CONVERT_TZ($field, $db_zone, $localzone)"; 305 case 'pgsql': 306 // WITH TIME ZONE assumes the date is using the system 307 // timezone, which should have been set to UTC. 308 return "$field::timestamp with time zone AT TIME ZONE $localzone"; 309 } 310 } 311 } 312 313 /** 314 * Helper function to create cross-database SQL date formatting. 315 * 316 * @param $format 317 * A format string for the result, like 'Y-m-d H:i:s'. 318 * @param $field 319 * The real table and field name, like 'tablename.fieldname'. 320 * @return 321 * An appropriate SQL string for the db type and field type. 322 */ 323 function sql_format($format, $field) { 324 switch ($this->db_type) { 325 case 'mysql': 326 case 'mysqli': 327 $replace = array( 328 'Y' => '%Y', 'y' => '%y', 329 'm' => '%m', 'n' => '%c', 330 'd' => '%%d', 'j' => '%e', 331 'H' => '%H', 332 'i' => '%i', 333 's' => '%%s', 334 '\WW' => 'W%U', 335 ); 336 $format = strtr($format, $replace); 337 return "DATE_FORMAT($field, '$format')"; 338 case 'pgsql': 339 $replace = array( 340 'Y' => 'YYYY', 'y' => 'Y', 341 'm' => 'MM', 'n' => 'M', 342 'd' => 'DD', 'j' => 'D', 343 'H' => 'HH24', 344 'i' => 'MI', 345 's' => 'SS', 346 '\T' => '"T"', 347 //'\W' => // TODO, what should this be? 348 ); 349 $format = strtr($format, $replace); 350 return "TO_CHAR($field, '$format')"; 351 } 352 } 353 354 /** 355 * Helper function to create cross-database SQL date extraction. 356 * 357 * @param $extract_type 358 * The type of value to extract from the date, like 'MONTH'. 359 * @param $field 360 * The real table and field name, like 'tablename.fieldname'. 361 * @return 362 * An appropriate SQL string for the db type and field type. 363 */ 364 function sql_extract($extract_type, $field) { 365 // Note there is no space after FROM to avoid db_rewrite problems 366 // see http://drupal.org/node/79904. 367 switch (strtoupper($extract_type)) { 368 case ('DATE'): 369 return $field; 370 case ('YEAR'): 371 return "EXTRACT(YEAR FROM($field))"; 372 case ('MONTH'): 373 return "EXTRACT(MONTH FROM($field))"; 374 case ('DAY'): 375 return "EXTRACT(DAY FROM($field))"; 376 case ('HOUR'): 377 return "EXTRACT(HOUR FROM($field))"; 378 case ('MINUTE'): 379 return "EXTRACT(MINUTE FROM($field))"; 380 case ('SECOND'): 381 return "EXTRACT(SECOND FROM($field))"; 382 case ('WEEK'): // ISO week number for date 383 switch ($this->db_type) { 384 case ('mysql'): 385 case ('mysqli'): 386 // WEEK using arg 3 in mysql should return the same value as postgres EXTRACT 387 return "WEEK($field, 3)"; 388 case ('pgsql'): 389 return "EXTRACT(WEEK FROM($field))"; 390 } 391 case ('DOW'): 392 switch ($this->db_type) { 393 case ('mysql'): 394 case ('mysqli'): 395 // mysql returns 1 for Sunday through 7 for Saturday 396 // php date functions and postgres use 0 for Sunday and 6 for Saturday 397 return "INTEGER(DAYOFWEEK($field) - 1)"; 398 case ('pgsql'): 399 return "EXTRACT(DOW FROM($field))"; 400 } 401 case ('DOY'): 402 switch ($this->db_type) { 403 case ('mysql'): 404 case ('mysqli'): 405 return "DAYOFYEAR($field)"; 406 case ('pgsql'): 407 return "EXTRACT(DOY FROM($field))"; 408 } 409 } 410 } 411 412 /** 413 * Create a where clause to compare a complete date field to a complete date value. 414 * 415 * @param string $type 416 * The type of value we're comparing to, could be another field 417 * or a date value. 418 * @param string $field 419 * The db table and field name, like "$table.$field". 420 * @param string $operator 421 * The db comparison operator to use, like '='. 422 * @param int $value 423 * The value to compare the extracted date part to, could be a 424 * field name or a date string or NOW(). 425 * @return 426 * SQL for the where clause for this operation. 427 */ 428 function sql_where_date($type, $field, $operator, $value, $adjustment = 0) { 429 $type = strtoupper($type); 430 if (strtoupper($value) == 'NOW') { 431 $value = $this->sql_field('NOW', $adjustment); 432 } 433 elseif ($type == 'FIELD') { 434 $value = $this->sql_field($value, $adjustment); 435 } 436 elseif ($type == 'DATE') { 437 $date = date_make_date($value, date_default_timezone_name(), DATE_DATETIME); 438 if (!empty($adjustment)) { 439 date_modify($date, $adjustment .' seconds'); 440 } 441 // When comparing a field to a date we can avoid doing timezone 442 // conversion by altering the comparison date to the db timezone. 443 // This won't work if the timezone is a field instead of a value. 444 if (empty($this->db_timezone_field) && empty($this->local_timezone_field) && $this->db_timezone_field != $this->local_timezone_field) { 445 date_timezone_set($date, timezone_open($this->db_timezone)); 446 $this->local_timezone = $this->db_timezone; 447 } 448 $value = "'". date_format_date($date, 'custom', DATE_FORMAT_DATETIME) ."'"; 449 } 450 if ($this->local_timezone != $this->db_timezone) { 451 $field = $this->sql_field($field); 452 } 453 else { 454 $field = $this->sql_field($field, 0); 455 } 456 return "$field $operator $value"; 457 } 458 459 /** 460 * Create a where clause to compare an extracted part of a field to an integer value. 461 * 462 * @param string $part 463 * The part to extract, YEAR, MONTH, DAY, etc. 464 * @param string $field 465 * The db table and field name, like "$table.$field". 466 * @param string $operator 467 * The db comparison operator to use, like '='. 468 * @param int $value 469 * The integer value to compare the extracted date part to. 470 * @return 471 * SQL for the where clause for this operation. 472 */ 473 function sql_where_extract($part, $field, $operator, $value, $adjustment = 0) { 474 $field = $this->sql_field($field, $adjustment); 475 return $this->sql_extract($part, $field) ." $operator $value"; 476 } 477 478 /** 479 * Create a where clause to compare a formated field to a formated value. 480 * 481 * @param string $format 482 * The format to use on the date and the value when comparing them. 483 * @param string $field 484 * The db table and field name, like "$table.$field". 485 * @param string $operator 486 * The db comparison operator to use, like '='. 487 * @param string $value 488 * The value to compare the extracted date part to, could be a 489 * field name or a date string or NOW(). 490 * @return 491 * SQL for the where clause for this operation. 492 */ 493 function sql_where_format($format, $field, $operator, $value, $adjustment = 0) { 494 $field = $this->sql_field($field, $adjustment); 495 return $this->sql_format($format, $field) ." $operator '$value'"; 496 } 497 498 /** 499 * An array of all date parts, 500 * optionally limited to an array of allowed parts. 501 */ 502 function date_parts($limit = NULL) { 503 $parts = array( 504 'year' => date_t('Year', 'datetime'), 'month' => date_t('Month', 'datetime'), 'day' => date_t('Day', 'datetime'), 505 'hour' => date_t('Hour', 'datetime'), 'minute' => date_t('Minute', 'datetime'), 'second' => date_t('Second', 'datetime'), 506 ); 507 if (!empty($limit)) { 508 $last = FALSE; 509 foreach ($parts as $key => $part) { 510 if ($last) { 511 unset($parts[$key]); 512 } 513 if ($key == $limit) { 514 $last = TRUE; 515 } 516 } 517 } 518 return $parts; 519 } 520 521 /** 522 * Part information. 523 * 524 * @param $op 525 * 'min', 'max', 'format', 'sep', 'empty_now', 'empty_min', 'empty_max'. 526 * Returns all info if empty. 527 * @param $part 528 * 'year', 'month', 'day', 'hour', 'minute', or 'second. 529 * returns info for all parts if empty. 530 */ 531 function part_info($op = NULL, $part = NULL) { 532 $info = array(); 533 $info['min'] = array( 534 'year' => 100, 'month' => 1, 'day' => 1, 535 'hour' => 0, 'minute' => 0, 'second' => 0); 536 $info['max'] = array( 537 'year' => 4000, 'month' => 12, 'day' => 31, 538 'hour' => 23, 'minute' => 59, 'second' => 59); 539 $info['format'] = array( 540 'year' => 'Y', 'month' => 'm', 'day' => 'd', 541 'hour' => 'H', 'minute' => 'i', 'second' => 's'); 542 $info['sep'] = array( 543 'year' => '', 'month' => '-', 'day' => '-', 544 'hour' => ' ', 'minute' => ':', 'second' => ':'); 545 $info['empty_now'] = array( 546 'year' => date('Y'), 'month' => date('m'), 'day' => min('28', date('d')), 547 'hour' => date('H'), 'minute' => date('i'), 'second' => date('s')); 548 $info['empty_min'] = array( 549 'year' => '1000', 'month' => '01', 'day' => '01', 550 'hour' => '00', 'minute' => '00', 'second' => '00'); 551 $info['empty_max'] = array( 552 'year' => '9999', 'month' => '12', 'day' => '31', 553 'hour' => '23', 'minute' => '59', 'second' => '59'); 554 if (!empty($op)) { 555 if (!empty($part)) { 556 return $info[$op][$part]; 557 } 558 else { 559 return $info[$op]; 560 } 561 } 562 return $info; 563 } 564 565 /** 566 * Create a complete datetime value out of an 567 * incomplete array of selected values. 568 * 569 * For example, array('year' => 2008, 'month' => 05) will fill 570 * in the day, hour, minute and second with the earliest possible 571 * values if type = 'min', the latest possible values if type = 'max', 572 * and the current values if type = 'now'. 573 */ 574 function complete_date($selected, $type = 'now') { 575 if (empty($selected)) { 576 return ''; 577 } 578 // Special case for weeks. 579 if (array_key_exists('week', $selected)) { 580 $dates = date_week_range($selected['week'], $selected['year']); 581 switch ($type) { 582 case 'empty_now': 583 case 'empty_min': 584 case 'min': 585 return date_format($dates[0], 'Y-m-d H:i:s'); 586 case 'empty_max': 587 case 'max': 588 return date_format($dates[1], 'Y-m-d H:i:s'); 589 default: 590 return; 591 } 592 } 593 594 $compare = array_merge($this->part_info('empty_'. $type), $selected); 595 // If this is a max date, make sure the last day of 596 // the month is the right one for this date. 597 if ($type == 'max') { 598 $compare['day'] = date_days_in_month($compare['year'], $compare['month']); 599 } 600 $value = ''; 601 $separators = $this->part_info('sep'); 602 foreach ($this->date_parts() as $key => $name) { 603 $value .= $separators[$key] . (!empty($selected[$key]) ? $selected[$key] : $compare[$key]); 604 } 605 return $value; 606 } 607 /** 608 * Convert a format string into help text, 609 * i.e. 'Y-m-d' becomes 'YYYY-MM-DD'. 610 * 611 * @param unknown_type $format 612 * @return unknown 613 */ 614 function format_help($format) { 615 $replace = array( 616 'Y' => 'YYYY', 'm' => 'MM', 'd' => 'DD', 617 'H' => 'HH', 'i' => 'MM', 's' => 'SS', '\T' => 'T'); 618 return strtr($format, $replace); 619 } 620 621 /** 622 * A function to test the validity of various date parts 623 */ 624 function part_is_valid($value, $type) { 625 if ( !preg_match('/^[0-9]*$/', $value) ) { 626 return false; 627 } 628 $value = intval($value); 629 if ($value <= 0) return false; 630 switch ($type) { 631 case 'year': 632 if ($value < DATE_MIN_YEAR) return false; 633 break; 634 case 'month': 635 if ($value < 0 || $value > 12) return false; 636 break; 637 case 'day': 638 if ($value < 0 || $value > 31) return false; 639 break; 640 case 'week': 641 if ($value < 0 || $value > 53) return false; 642 } 643 return true; 644 } 645 646 function views_formats($granularity, $type = 'sql') { 647 $formats = array('display', 'sql'); 648 // Start with the site long date format and add seconds to it 649 $long = str_replace(':i', ':i:s', variable_get('date_format_long', 'l, F j, Y - H:i')); 650 switch ($granularity) { 651 case ('year'): 652 $formats['display'] = 'Y'; 653 $formats['sql'] = 'Y'; 654 break; 655 case ('month'): 656 $formats['display'] = date_limit_format($long, array('year', 'month')); 657 $formats['sql'] = 'Y-m'; 658 break; 659 case ('day'): 660 $formats['display'] = date_limit_format($long, array('year', 'month', 'day')); 661 $formats['sql'] = 'Y-m-d'; 662 break; 663 case ('hour'): 664 $formats['display'] = date_limit_format($long, array('year', 'month', 'day', 'hour')); 665 $formats['sql'] = 'Y-m-d\TH'; 666 break; 667 case ('minute'): 668 $formats['display'] = date_limit_format($long, array('year', 'month', 'day', 'hour', 'minute')); 669 $formats['sql'] = 'Y-m-d\TH:i'; 670 break; 671 case ('second'): 672 $formats['display'] = date_limit_format($long, array('year', 'month', 'day', 'hour', 'minute', 'second')); 673 $formats['sql'] = 'Y-m-d\TH:i:s'; 674 break; 675 case ('week'): 676 $formats['display'] = 'F j Y (W)'; 677 $formats['sql'] = 'Y-\WW'; 678 break; 679 } 680 return $formats[$type]; 681 } 682 683 function granularity_form($granularity) { 684 $form = array( 685 '#title' => t('Granularity'), 686 '#type' => 'radios', 687 '#default_value' => $granularity, 688 '#options' => $this->date_parts(), 689 ); 690 return $form; 691 } 692 693 /** 694 * Parse date parts from an ISO date argument. 695 * 696 * Based on ISO 8601 date duration and time interval standards. 697 * 698 * See http://en.wikipedia.org/wiki/ISO_8601#Week_dates for definitions of ISO weeks. 699 * See http://en.wikipedia.org/wiki/ISO_8601#Duration for definitions of ISO duration and time interval. 700 * 701 * Parses a value like 2006-01-01--2006-01-15, or 2006-W24, or @P1W. 702 * Separate from and to dates or date and period with a double hyphen (--). 703 * 704 * The 'to' portion of the argument can be eliminated if it is the same as the 'from' portion. 705 * Use @ instead of a date to substitute in the current date and time. 706 * 707 * Use periods (P1H, P1D, P1W, P1M, P1Y) to get next hour/day/week/month/year from now. 708 * Use date before P sign to get next hour/day/week/month/year from that date. 709 * Use period then date to get a period that ends on the date. 710 * 711 */ 712 function arg_parts($argument) { 713 $values = array(); 714 // Keep mal-formed arguments from creating errors. 715 if (empty($argument) || is_array($argument)) { 716 return array('date' => array(), 'period' => array()); 717 } 718 $fromto = explode('--', $argument); 719 foreach ($fromto as $arg) { 720 $parts = array(); 721 if ($arg == '@') { 722 $parts['date'] = date_array(date_now()); 723 } 724 elseif (preg_match('/(\d{4})?-?(W)?(\d{1,2})?-?(\d{1,2})?[T\s]?(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?/', $arg, $matches)) { 725 $date = array(); 726 if (!empty($matches[1])) $date['year'] = $matches[1]; 727 if (!empty($matches[3])) { 728 if (empty($matches[2])) { 729 $date['month'] = $matches[3]; 730 } 731 else { 732 $date['week'] = $matches[3]; 733 } 734 } 735 if (!empty($matches[4])) $date['day'] = $matches[4]; 736 if (!empty($matches[5])) $date['hour'] = $matches[5]; 737 if (!empty($matches[6])) $date['minute'] = $matches[6]; 738 if (!empty($matches[7])) $date['second'] = $matches[7]; 739 $parts['date'] = $date; 740 } 741 if (preg_match('/^P(\d{1,4}[Y])?(\d{1,2}[M])?(\d{1,2}[W])?(\d{1,2}[D])?([T]{0,1})?(\d{1,2}[H])?(\d{1,2}[M])?(\d{1,2}[S])?/', $arg, $matches)) { 742 $period = array(); 743 if (!empty($matches[1])) $period['year'] = str_replace('Y', '', $matches[1]); 744 if (!empty($matches[2])) $period['month'] = str_replace('M', '', $matches[2]); 745 if (!empty($matches[3])) $period['week'] = str_replace('W', '', $matches[3]); 746 if (!empty($matches[4])) $period['day'] = str_replace('D', '', $matches[4]); 747 if (!empty($matches[6])) $period['hour'] = str_replace('H', '', $matches[6]); 748 if (!empty($matches[7])) $period['minute'] = str_replace('M', '', $matches[7]); 749 if (!empty($matches[8])) $period['second'] = str_replace('S', '', $matches[8]); 750 $parts['period'] = $period; 751 } 752 $values[] = $parts; 753 } 754 return $values; 755 } 756 757 /** 758 * Convert strings like '+1 day' to the ISO equivalent, like 'P1D'. 759 */ 760 function arg_replace($arg) { 761 if (!preg_match('/([+|-])\s?([0-9]{1,32})\s?([day(s)?|week(s)?|month(s)?|year(s)?|hour(s)?|minute(s)?|second(s)?]{1,10})/', $arg, $results)) { 762 return str_replace('now', '@', $arg); 763 } 764 $direction = $results[1]; 765 $count = $results[2]; 766 $item = $results[3]; 767 768 $replace = array( 769 'now' => '@', 770 '+' => 'P', 771 '-' => 'P-', 772 'years' => 'Y', 773 'year' => 'Y', 774 'months' => 'M', 775 'month' => 'M', 776 'weeks' => 'W', 777 'week' => 'W', 778 'days' => 'D', 779 'day' => 'D', 780 'hours' => 'H', 781 'hour' => 'H', 782 'minutes' => 'M', 783 'minute' => 'M', 784 'seconds' => 'S', 785 'second' => 'S', 786 ' ' => '', 787 ' ' => '', 788 ); 789 $prefix = in_array($item, array('hours', 'hour', 'minutes', 'minute', 'seconds', 'second')) ? 'T' : ''; 790 return $prefix . strtr($direction, $replace) . $count . strtr($item, $replace); 791 } 792 793 /** 794 * Use the parsed values from the ISO argument to determine the 795 * granularity of this period. 796 */ 797 function arg_granularity($arg) { 798 $granularity = ''; 799 $parts = $this->arg_parts($arg); 800 $date = !empty($parts[0]['date']) ? $parts[0]['date'] : (!empty($parts[1]['date']) ? $parts[1]['date'] : array()); 801 foreach ($date as $key => $part) { 802 $granularity = $key; 803 } 804 return $granularity; 805 } 806 807 /** 808 * Use the parsed values from the ISO argument to determine the 809 * min and max date for this period. 810 */ 811 function arg_range($arg) { 812 // Parse the argument to get its parts 813 $parts = $this->arg_parts($arg); 814 815 // Build a range from a period-only argument (assumes the min date is now.) 816 if (empty($parts[0]['date']) && !empty($parts[0]['period']) && (empty($parts[1]))) { 817 $min_date = date_now(); 818 $max_date = drupal_clone($min_date); 819 foreach ($parts[0]['period'] as $part => $value) { 820 date_modify($max_date, "+$value $part"); 821 } 822 date_modify($max_date, '-1 second'); 823 return array($min_date, $max_date); 824 } 825 // Build a range from a period to period argument 826 if (empty($parts[0]['date']) && !empty($parts[0]['period']) && !empty($parts[1]['period'])) { 827 $min_date = date_now(); 828 $max_date = drupal_clone($min_date); 829 foreach ($parts[0]['period'] as $part => $value) { 830 date_modify($min_date, "+$value $part"); 831 } 832 date_modify($min_date, '-1 second'); 833 foreach ($parts[1]['period'] as $part => $value) { 834 date_modify($max_date, "+$value $part"); 835 } 836 date_modify($max_date, '-1 second'); 837 return array($min_date, $max_date); 838 } 839 if (!empty($parts[0]['date'])) { 840 $value = date_fuzzy_datetime($this->complete_date($parts[0]['date'], 'min')); 841 $min_date = date_make_date($value, date_default_timezone_name(), DATE_ISO); 842 // Build a range from a single date-only argument. 843 if (empty($parts[1]) || (empty($parts[1]['date']) && empty($parts[1]['period']))) { 844 $value = date_fuzzy_datetime($this->complete_date($parts[0]['date'], 'max')); 845 $max_date = date_make_date($value, date_default_timezone_name(), DATE_ISO); 846 return array($min_date, $max_date); 847 } 848 // Build a range from start date + period. 849 elseif (!empty($parts[1]['period'])) { 850 foreach ($parts[1]['period'] as $part => $value) { 851 $max_date = drupal_clone($min_date); 852 date_modify($max_date, "+$value $part"); 853 } 854 date_modify($max_date, '-1 second'); 855 return array($min_date, $max_date); 856 } 857 } 858 // Build a range from start date and end date. 859 if (!empty($parts[1]['date'])) { 860 $value = date_fuzzy_datetime($this->complete_date($parts[1]['date'], 'max')); 861 $max_date = date_make_date($value, date_default_timezone_name(), DATE_ISO); 862 if (isset($min_date)) { 863 return array($min_date, $max_date); 864 } 865 } 866 // Build a range from period + end date. 867 if (!empty($parts[0]['period'])) { 868 $min_date = date_now(); 869 foreach ($parts[0]['period'] as $part => $value) { 870 date_modify($min_date, "$value $part"); 871 } 872 return array($min_date, $max_date); 873 } 874 // Intercept invalid info and fall back to the current date. 875 $now = date_now(); 876 return array($now, $now); 877 } 878 }
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 |