2 // $Id: date_api_sql.inc,v 1.9.2.3.2.35 2009/02/26 18:48:00 karens Exp $
5 * A helper function to do cross-database concatation of date parts
7 * @param $array - an array of values to be concatonated in sql
8 * @return - correct sql string for database type
10 function date_sql_concat($array) {
15 return "CONCAT(". implode(",", $array) .")";
17 return implode(" || ", $array);
22 * Helper function to do cross-database NULL replacements
24 * @param an array of values to test for NULL values
25 * @return SQL statement to return the first non-NULL value in the list.
27 function date_sql_coalesce($array) {
33 return "COALESCE(". implode(',', $array) .")";
38 * A helper function to do cross-database padding of date parts
40 * @param $str - a string to apply padding to
41 * @param $size - the size the final string should be
42 * @param $pad - the value to pad the string with
43 * @param $side - the side of the string to pad
45 function date_sql_pad($str, $size = 2, $pad = '0', $side = 'l') {
48 return "RPAD($str, $size, '$pad')";
50 return "LPAD($str, $size, '$pad')";
55 * A class to manipulate date SQL.
57 class date_sql_handler {
58 var $db_type = 'mysql';
59 var $date_type = DATE_DATETIME;
60 var $db_timezone = 'UTC'; // A string timezone name.
61 var $local_timezone = NULL; // A string timezone name.
62 var $db_timezone_field = NULL; // Use if the db timezone is stored in a field.
63 var $local_timezone_field = NULL; // Use if the local timezone is stored in a field.
64 var $offset_field = NULL; // Use if the offset is stored in a field.
66 function construct($date_type = DATE_DATETIME, $local_timezone = NULL) {
67 $this->db_type = $GLOBALS['db_type'];
68 $this->date_type = $date_type;
69 $this->db_timezone = 'UTC';
70 $this->local_timezone = isset($local_timezone) ? $local_timezone : date_default_timezone_name();
71 if (isset($this->definition['content_field'])) {
72 $this->date_handler->date_type = $this->definition['content_field']['type'];
74 date_api_set_db_timezone();
78 * See if the db has timezone name support.
80 function db_tz_support($reset = FALSE) {
81 $has_support = variable_get('date_db_tz_support', -1);
82 if ($has_support == -1 || $reset) {
83 date_api_set_db_timezone();
85 switch ($this->db_type) {
88 if (version_compare(db_version(), '4.1.3', '>=')) {
89 $test = db_result(db_query("SELECT CONVERT_TZ('2008-02-15 12:00:00', 'UTC', 'US/Central')"));
90 if ($test == '2008-02-15 06:00:00') {
96 $test = db_result(db_query("'2008-02-15 12:00:00 UTC' AT TIME ZONE 'US/Central'"));
97 if ($test == '2008-02-15 06:00:00') {
102 variable_set('date_db_tz_support', $has_support);
108 * Set the database timzone offset.
110 * Setting the db timezone to UTC is done to ensure consistency in date
111 * handling whether or not the database can do proper timezone conversion.
113 * Views filters that not exposed are cached and won't set the timezone
114 * so views date filters should add 'cacheable' => 'no' to their
115 * definitions to ensure that the database timezone gets set properly
116 * when the query is executed.
119 * An offset value to set the database timezone to. This will only
120 * set a fixed offset, not a timezone, so any value other than
121 * '+00:00' should be used with caution.
123 function set_db_timezone($offset = '+00:00') {
124 static $already_set = FALSE;
125 $type = $GLOBALS['db_type'];
127 if (($type == 'mysqli' || $type == 'mysql') && version_compare(db_version(), '4.1.3', '>=')) {
128 db_query("SET @@session.time_zone = '$offset'");
130 elseif ($type == 'pgsql') {
131 db_query("SET TIME ZONE INTERVAL '$offset' HOUR TO MINUTE");
138 * Return timezone offset for the date being processed.
140 function get_offset() {
141 if (!empty($this->db_timezone) && !empty($this->local_timezone)) {
142 if ($this->db_timezone != $this->local_timezone) {
143 $date = date_now($this->db_timezone);
144 date_timezone_set($date, timezone_open($this->local_timezone));
145 return date_offset_get($date);
152 * Helper function to create cross-database SQL dates.
155 * The real table and field name, like 'tablename.fieldname'.
157 * The name of a field that holds the timezone offset or an
158 * offset value. If NULL, the normal Drupal timezone handling
159 * will be used, if $offset = 0 no adjustment will be made.
161 * An appropriate SQL string for the db type and field type.
163 function sql_field($field, $offset = NULL) {
164 if (strtoupper($field) == 'NOW') {
165 // NOW() will be in UTC since that is what we set the db timezone to.
166 $this->local_timezone = 'UTC';
167 return $this->sql_offset('NOW()', $offset);
169 switch ($this->db_type) {
172 switch ($this->date_type) {
174 $field = "FROM_UNIXTIME($field)";
177 if (version_compare(db_version(), '4.1.1', '>=')) {
178 $field = "STR_TO_DATE($field, '%Y-%m-%%dT%T')";
181 $field = "REPLACE($field, 'T', ' ')";
189 switch ($this->date_type) {
191 $field = "$field::ABSTIME";
194 $field = "TO_DATE($field, 'FMYYYY-FMMM-FMDDTFMHH:FMMI:FMSS')";
201 // Adjust the resulting value to the right timezone/offset.
202 return $this->sql_tz($field, $offset);
206 * Adjust a field value by an offset in seconds.
208 function sql_offset($field, $offset = NULL) {
209 if (!empty($offset)) {
210 switch ($this->db_type) {
213 if (version_compare(db_version(), '4.1.1', '>=')) {
214 return "ADDTIME($field, SEC_TO_TIME($offset))";
217 return "DATE_ADD($field, INTERVAL $offset SECOND)";
220 return "($field + INTERVAL '$offset SECONDS')";;
227 * Adjust a field value by time interval.
230 * The field to be adjusted.
234 * The number of values to adjust.
235 * @param $granularity
236 * The granularity of the adjustment, should be singular,
237 * like SECOND, MINUTE, DAY, HOUR.
239 function sql_date_math($field, $direction, $count, $granularity) {
240 $granularity = strtoupper($granularity);
241 switch ($this->db_type) {
244 switch ($direction) {
246 return "DATE_ADD($field, INTERVAL $count $granularity)";
248 return "DATE_SUB($field, INTERVAL $count $granularity)";
253 switch ($direction) {
255 return "($field + INTERVAL '$count $granularity')";
257 return "($field - INTERVAL '$count $granularity')";
264 * Select a date value from the database, adjusting the value
267 * Check whether database timezone conversion is supported in
268 * this system and use it if possible, otherwise use an
272 * Set a fixed offset or offset field to use for the date.
273 * If set, no timezone conversion will be done and the
274 * offset will be used.
276 function sql_tz($field, $offset = NULL) {
277 // If the timezones are values they need to be quoted, but
278 // if they are field names they do not.
279 $db_zone = $this->db_timezone_field ? $this->db_timezone_field : "'{$this->db_timezone}'";
280 $localzone = $this->local_timezone_field ? $this->local_timezone_field : "'{$this->local_timezone}'";
282 // If a fixed offset is required, use it.
283 if ($offset !== NULL) {
284 return $this->sql_offset($field, $offset);
286 // If the db and local timezones are the same, make no adjustment.
287 elseif ($db_zone == $localzone) {
288 return $this->sql_offset($field, 0);
290 // If the db has no timezone support, adjust by the offset,
291 // could be either a field name or a value.
292 elseif (!$this->db_tz_support()) {
293 if (!empty($this->offset_field)) {
294 return $this->sql_offset($field, $this->offset_field);
297 return $this->sql_offset($field, $this->get_offset());
300 // Otherwise make a database timezone adjustment to the field.
302 switch ($this->db_type) {
305 return "CONVERT_TZ($field, $db_zone, $localzone)";
307 // WITH TIME ZONE assumes the date is using the system
308 // timezone, which should have been set to UTC.
309 return "TIMESTAMP WITH TIME ZONE $field AT TIME ZONE $localzone";
315 * Helper function to create cross-database SQL date formatting.
318 * A format string for the result, like 'Y-m-d H:i:s'.
320 * The real table and field name, like 'tablename.fieldname'.
322 * An appropriate SQL string for the db type and field type.
324 function sql_format($format, $field) {
325 switch ($this->db_type) {
329 'Y' => '%Y', 'y' => '%y',
330 'm' => '%m', 'n' => '%c',
331 'd' => '%%d', 'j' => '%e',
337 $format = strtr($format, $replace);
338 return "DATE_FORMAT($field, '$format')";
341 'Y' => 'YYYY', 'y' => 'Y',
342 'm' => 'MM', 'n' => 'M',
343 'd' => 'DD', 'j' => 'D',
347 //'\W' => // TODO, what should this be?
349 $format = strtr($format, $replace);
350 return "TO_CHAR($field, '$format')";
355 * Helper function to create cross-database SQL date extraction.
357 * @param $extract_type
358 * The type of value to extract from the date, like 'MONTH'.
360 * The real table and field name, like 'tablename.fieldname'.
362 * An appropriate SQL string for the db type and field type.
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)) {
371 return "EXTRACT(YEAR FROM($field))";
373 return "EXTRACT(MONTH FROM($field))";
375 return "EXTRACT(DAY FROM($field))";
377 return "EXTRACT(HOUR FROM($field))";
379 return "EXTRACT(MINUTE FROM($field))";
381 return "EXTRACT(SECOND FROM($field))";
382 case('WEEK'): // ISO week number for date
383 switch ($this->db_type) {
386 // WEEK using arg 3 in mysql should return the same value as postgres EXTRACT
387 return "WEEK($field, 3)";
389 return "EXTRACT(WEEK FROM($field))";
392 switch ($this->db_type) {
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)";
399 return "EXTRACT(DOW FROM($field))";
402 switch ($this->db_type) {
405 return "DAYOFYEAR($field)";
407 return "EXTRACT(DOY FROM($field))";
413 * Create a where clause to compare a complete date field to a complete date value.
415 * @param string $type
416 * The type of value we're comparing to, could be another field
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 '='.
423 * The value to compare the extracted date part to, could be a
424 * field name or a date string or NOW().
426 * SQL for the where clause for this operation.
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);
433 elseif ($type == 'FIELD') {
434 $value = $this->sql_field($value, $adjustment);
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');
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;
448 $value = "'". date_format_date($date, 'custom', DATE_FORMAT_DATETIME) ."'";
450 if ($this->local_timezone != $this->db_timezone) {
451 $field = $this->sql_field($field);
454 $field = $this->sql_field($field, 0);
456 return "$field $operator $value";
460 * Create a where clause to compare an extracted part of a field to an integer value.
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 '='.
469 * The integer value to compare the extracted date part to.
471 * SQL for the where clause for this operation.
473 function sql_where_extract($part, $field, $operator, $value) {
474 if ($this->local_timezone != $this->db_timezone) {
475 $field = $this->sql_field($field);
478 $field = $this->sql_field($field, 0);
480 return $this->sql_extract($part, $field) ." $operator $value";
484 * Create a where clause to compare a formated field to a formated value.
486 * @param string $format
487 * The format to use on the date and the value when comparing them.
488 * @param string $field
489 * The db table and field name, like "$table.$field".
490 * @param string $operator
491 * The db comparison operator to use, like '='.
492 * @param string $value
493 * The value to compare the extracted date part to, could be a
494 * field name or a date string or NOW().
496 * SQL for the where clause for this operation.
498 function sql_where_format($format, $field, $operator, $value) {
499 if ($this->local_timezone != $this->db_timezone) {
500 $field = $this->sql_field($field);
503 $field = $this->sql_field($field, 0);
505 return $this->sql_format($format, $field) ." $operator '$value'";
509 * An array of all date parts,
510 * optionally limited to an array of allowed parts.
512 function date_parts($limit = NULL) {
514 'year' => date_t('Year', 'datetime'), 'month' => date_t('Month', 'datetime'), 'day' => date_t('Day', 'datetime'),
515 'hour' => date_t('Hour', 'datetime'), 'minute' => date_t('Minute', 'datetime'), 'second' => date_t('Second', 'datetime'),
517 if (!empty($limit)) {
519 foreach ($parts as $key => $part) {
523 if ($key == $limit) {
535 * 'min', 'max', 'format', 'sep', 'empty_now', 'empty_min', 'empty_max'.
536 * Returns all info if empty.
538 * 'year', 'month', 'day', 'hour', 'minute', or 'second.
539 * returns info for all parts if empty.
541 function part_info($op = NULL, $part = NULL) {
543 $info['min'] = array(
544 'year' => 100, 'month' => 1, 'day' => 1,
545 'hour' => 0, 'minute' => 0, 'second' => 0);
546 $info['max'] = array(
547 'year' => 4000, 'month' => 12, 'day' => 31,
548 'hour' => 23, 'minute' => 59, 'second' => 59);
549 $info['format'] = array(
550 'year' => 'Y', 'month' => 'm', 'day' => 'd',
551 'hour' => 'H', 'minute' => 'i', 'second' => 's');
552 $info['sep'] = array(
553 'year' => '', 'month' => '-', 'day' => '-',
554 'hour' => ' ', 'minute' => ':', 'second' => ':');
555 $info['empty_now'] = array(
556 'year' => date('Y'), 'month' => date('m'), 'day' => min('28', date('d')),
557 'hour' => date('H'), 'minute' => date('i'), 'second' => date('s'));
558 $info['empty_min'] = array(
559 'year' => '1000', 'month' => '01', 'day' => '01',
560 'hour' => '00', 'minute' => '00', 'second' => '00');
561 $info['empty_max'] = array(
562 'year' => '9999', 'month' => '12', 'day' => '31',
563 'hour' => '23', 'minute' => '59', 'second' => '59');
566 return $info[$op][$part];
576 * Create a complete datetime value out of an
577 * incomplete array of selected values.
579 * For example, array('year' => 2008, 'month' => 05) will fill
580 * in the day, hour, minute and second with the earliest possible
581 * values if type = 'min', the latest possible values if type = 'max',
582 * and the current values if type = 'now'.
584 function complete_date($selected, $type = 'now') {
585 if (empty($selected)) {
588 // Special case for weeks.
589 if (array_key_exists('week', $selected)) {
590 $dates = date_week_range($selected['week'], $selected['year']);
595 return date_format($dates[0], 'Y-m-d H:i:s');
598 return date_format($dates[1], 'Y-m-d H:i:s');
604 $compare = array_merge($this->part_info('empty_'. $type), $selected);
605 // If this is a max date, make sure the last day of
606 // the month is the right one for this date.
607 if ($type == 'max') {
608 $compare['day'] = date_days_in_month($compare['year'], $compare['month']);
611 $separators = $this->part_info('sep');
612 foreach ($this->date_parts() as $key => $name) {
613 $value .= $separators[$key] . (!empty($selected[$key]) ? $selected[$key] : $compare[$key]);
618 * Convert a format string into help text,
619 * i.e. 'Y-m-d' becomes 'YYYY-MM-DD'.
621 * @param unknown_type $format
624 function format_help($format) {
626 'Y' => 'YYYY', 'm' => 'MM', 'd' => 'DD',
627 'H' => 'HH', 'i' => 'MM', 's' => 'SS', '\T' => 'T');
628 return strtr($format, $replace);
632 * A function to test the validity of various date parts
634 function part_is_valid($value, $type) {
635 if ( !preg_match('/^[0-9]*$/', $value) ) {
638 $value = intval($value);
639 if ($value <= 0) return false;
642 if ($value < DATE_MIN_YEAR) return false;
645 if ($value < 0 || $value > 12) return false;
648 if ($value < 0 || $value > 31) return false;
651 if ($value < 0 || $value > 53) return false;
656 function views_formats($granularity, $type = 'sql') {
657 $formats = array('display', 'sql');
658 // Start with the site long date format and add seconds to it
659 $long = str_replace(':i', ':i:s', variable_get('date_format_long', 'l, F j, Y - H:i'));
660 switch ($granularity) {
662 $formats['display'] = 'Y';
663 $formats['sql'] = 'Y';
666 $formats['display'] = date_limit_format($long, array('year', 'month'));
667 $formats['sql'] = 'Y-m';
670 $formats['display'] = date_limit_format($long, array('year', 'month', 'day'));
671 $formats['sql'] = 'Y-m-d';
674 $formats['display'] = date_limit_format($long, array('year', 'month', 'day', 'hour'));
675 $formats['sql'] = 'Y-m-d\TH';
678 $formats['display'] = date_limit_format($long, array('year', 'month', 'day', 'hour', 'minute'));
679 $formats['sql'] = 'Y-m-d\TH:i';
682 $formats['display'] = date_limit_format($long, array('year', 'month', 'day', 'hour', 'minute', 'second'));
683 $formats['sql'] = 'Y-m-d\TH:i:s';
686 $formats['display'] = 'F j Y (W)';
687 $formats['sql'] = 'Y-\WW';
690 return $formats[$type];
693 function granularity_form($granularity) {
695 '#title' => t('Granularity'),
697 '#default_value' => $granularity,
698 '#options' => $this->date_parts(),
704 * Parse date parts from an ISO date argument.
706 * Based on ISO 8601 date duration and time interval standards.
708 * See http://en.wikipedia.org/wiki/ISO_8601#Week_dates for definitions of ISO weeks.
709 * See http://en.wikipedia.org/wiki/ISO_8601#Duration for definitions of ISO duration and time interval.
711 * Parses a value like 2006-01-01--2006-01-15, or 2006-W24, or @P1W.
712 * Separate from and to dates or date and period with a double hyphen (--).
714 * The 'to' portion of the argument can be eliminated if it is the same as the 'from' portion.
715 * Use @ instead of a date to substitute in the current date and time.
717 * Use periods (P1H, P1D, P1W, P1M, P1Y) to get next hour/day/week/month/year from now.
718 * Use date before P sign to get next hour/day/week/month/year from that date.
719 * Use period then date to get a period that ends on the date.
722 function arg_parts($argument) {
724 // Keep mal-formed arguments from creating errors.
725 if (empty($argument) || is_array($argument)) {
726 return array('date' => array(), 'period' => array());
728 $fromto = explode('--', $argument);
729 foreach ($fromto as $arg) {
732 $parts['date'] = date_array(date_now());
734 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)) {
736 if (!empty($matches[1])) $date['year'] = $matches[1];
737 if (!empty($matches[3])) {
738 if (empty($matches[2])) {
739 $date['month'] = $matches[3];
742 $date['week'] = $matches[3];
745 if (!empty($matches[4])) $date['day'] = $matches[4];
746 if (!empty($matches[5])) $date['hour'] = $matches[5];
747 if (!empty($matches[6])) $date['minute'] = $matches[6];
748 if (!empty($matches[7])) $date['second'] = $matches[7];
749 $parts['date'] = $date;
751 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)) {
753 if (!empty($matches[1])) $period['year'] = str_replace('Y', '', $matches[1]);
754 if (!empty($matches[2])) $period['month'] = str_replace('M', '', $matches[2]);
755 if (!empty($matches[3])) $period['week'] = str_replace('W', '', $matches[3]);
756 if (!empty($matches[4])) $period['day'] = str_replace('D', '', $matches[4]);
757 if (!empty($matches[6])) $period['hour'] = str_replace('H', '', $matches[6]);
758 if (!empty($matches[7])) $period['minute'] = str_replace('M', '', $matches[7]);
759 if (!empty($matches[8])) $period['second'] = str_replace('S', '', $matches[8]);
760 $parts['period'] = $period;
768 * Convert strings like '+1 day' to the ISO equivalent, like 'P1D'.
770 function arg_replace($arg) {
771 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)) {
772 return str_replace('now', '@', $arg);
774 $direction = $results[1];
775 $count = $results[2];
799 $prefix = in_array($item, array('hours', 'hour', 'minutes', 'minute', 'seconds', 'second')) ? 'T' : '';
800 return $prefix . strtr($direction, $replace) . $count . strtr($item, $replace);
804 * Use the parsed values from the ISO argument to determine the
805 * granularity of this period.
807 function arg_granularity($arg) {
809 $parts = $this->arg_parts($arg);
810 $date = !empty($parts[0]['date']) ? $parts[0]['date'] : (!empty($parts[1]['date']) ? $parts[1]['date'] : array());
811 foreach ($date as $key => $part) {
818 * Use the parsed values from the ISO argument to determine the
819 * min and max date for this period.
821 function arg_range($arg) {
822 // Parse the argument to get its parts
823 $parts = $this->arg_parts($arg);
825 // Build a range from a period-only argument (assumes the min date is now.)
826 if (empty($parts[0]['date']) && !empty($parts[0]['period']) && (empty($parts[1]))) {
827 $min_date = date_now();
828 $max_date = drupal_clone($min_date);
829 foreach ($parts[0]['period'] as $part => $value) {
830 date_modify($max_date, "+$value $part");
832 date_modify($max_date, '-1 second');
833 return array($min_date, $max_date);
835 // Build a range from a period to period argument
836 if (empty($parts[0]['date']) && !empty($parts[0]['period']) && !empty($parts[1]['period'])) {
837 $min_date = date_now();
838 $max_date = drupal_clone($min_date);
839 foreach ($parts[0]['period'] as $part => $value) {
840 date_modify($min_date, "+$value $part");
842 date_modify($min_date, '-1 second');
843 foreach ($parts[1]['period'] as $part => $value) {
844 date_modify($max_date, "+$value $part");
846 date_modify($max_date, '-1 second');
847 return array($min_date, $max_date);
849 if (!empty($parts[0]['date'])) {
850 $value = date_fuzzy_datetime($this->complete_date($parts[0]['date'], 'min'));
851 $min_date = date_make_date($value, date_default_timezone_name(), DATE_ISO);
852 // Build a range from a single date-only argument.
853 if (empty($parts[1]) || (empty($parts[1]['date']) && empty($parts[1]['period']))) {
854 $value = date_fuzzy_datetime($this->complete_date($parts[0]['date'], 'max'));
855 $max_date = date_make_date($value, date_default_timezone_name(), DATE_ISO);
856 return array($min_date, $max_date);
858 // Build a range from start date + period.
859 elseif (!empty($parts[1]['period'])) {
860 foreach ($parts[1]['period'] as $part => $value) {
861 $max_date = drupal_clone($min_date);
862 date_modify($max_date, "+$value $part");
864 date_modify($max_date, '-1 second');
865 return array($min_date, $max_date);
868 // Build a range from start date and end date.
869 if (!empty($parts[1]['date'])) {
870 $value = date_fuzzy_datetime($this->complete_date($parts[1]['date'], 'max'));
871 $max_date = date_make_date($value, date_default_timezone_name(), DATE_ISO);
872 if (isset($min_date)) {
873 return array($min_date, $max_date);
876 // Build a range from period + end date.
877 if (!empty($parts[0]['period'])) {
878 $min_date = date_now();
879 foreach ($parts[0]['period'] as $part => $value) {
880 date_modify($min_date, "$value $part");
882 return array($min_date, $max_date);
884 // Intercept invalid info and fall back to the current date.
886 return array($now, $now);