wp/wp-includes/date.php
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
     1 <?php
     1 <?php
     2 /**
     2 /**
     3  * Class for generating SQL clauses that filter a primary query according to date.
     3  * Class for generating SQL clauses that filter a primary query according to date.
     4  *
     4  *
     5  * `WP_Date_Query` is a helper that allows primary query classes, such as {@see WP_Query},
     5  * WP_Date_Query is a helper that allows primary query classes, such as WP_Query, to filter
     6  * to filter their results by date columns, by generating `WHERE` subclauses to be attached
     6  * their results by date columns, by generating `WHERE` subclauses to be attached to the
     7  * to the primary SQL query string.
     7  * primary SQL query string.
     8  *
     8  *
     9  * Attempting to filter by an invalid date value (eg month=13) will generate SQL that will
     9  * Attempting to filter by an invalid date value (eg month=13) will generate SQL that will
    10  * return no results. In these cases, a _doing_it_wrong() error notice is also thrown.
    10  * return no results. In these cases, a _doing_it_wrong() error notice is also thrown.
    11  * See {@link WP_Date_Query::validate_date_values()}.
    11  * See WP_Date_Query::validate_date_values().
    12  *
    12  *
    13  * @link https://codex.wordpress.org/Function_Reference/WP_Query Codex page.
    13  * @link https://codex.wordpress.org/Function_Reference/WP_Query Codex page.
    14  *
    14  *
    15  * @since 3.7.0
    15  * @since 3.7.0
    16  */
    16  */
    17 class WP_Date_Query {
    17 class WP_Date_Query {
    18 	/**
    18 	/**
    19 	 * Array of date queries.
    19 	 * Array of date queries.
    20 	 *
    20 	 *
    21 	 * See {@see WP_Date_Query::__construct()} for information on date query arguments.
    21 	 * See WP_Date_Query::__construct() for information on date query arguments.
    22 	 *
    22 	 *
    23 	 * @since 3.7.0
    23 	 * @since 3.7.0
    24 	 * @access public
       
    25 	 * @var array
    24 	 * @var array
    26 	 */
    25 	 */
    27 	public $queries = array();
    26 	public $queries = array();
    28 
    27 
    29 	/**
    28 	/**
    30 	 * The default relation between top-level queries. Can be either 'AND' or 'OR'.
    29 	 * The default relation between top-level queries. Can be either 'AND' or 'OR'.
    31 	 *
    30 	 *
    32 	 * @since 3.7.0
    31 	 * @since 3.7.0
    33 	 * @access public
       
    34 	 * @var string
    32 	 * @var string
    35 	 */
    33 	 */
    36 	public $relation = 'AND';
    34 	public $relation = 'AND';
    37 
    35 
    38 	/**
    36 	/**
    39 	 * The column to query against. Can be changed via the query arguments.
    37 	 * The column to query against. Can be changed via the query arguments.
    40 	 *
    38 	 *
    41 	 * @since 3.7.0
    39 	 * @since 3.7.0
    42 	 * @access public
       
    43 	 * @var string
    40 	 * @var string
    44 	 */
    41 	 */
    45 	public $column = 'post_date';
    42 	public $column = 'post_date';
    46 
    43 
    47 	/**
    44 	/**
    48 	 * The value comparison operator. Can be changed via the query arguments.
    45 	 * The value comparison operator. Can be changed via the query arguments.
    49 	 *
    46 	 *
    50 	 * @since 3.7.0
    47 	 * @since 3.7.0
    51 	 * @access public
       
    52 	 * @var array
    48 	 * @var array
    53 	 */
    49 	 */
    54 	public $compare = '=';
    50 	public $compare = '=';
    55 
    51 
    56 	/**
    52 	/**
    57 	 * Supported time-related parameter keys.
    53 	 * Supported time-related parameter keys.
    58 	 *
    54 	 *
    59 	 * @since 4.1.0
    55 	 * @since 4.1.0
    60 	 * @access public
       
    61 	 * @var array
    56 	 * @var array
    62 	 */
    57 	 */
    63 	public $time_keys = array( 'after', 'before', 'year', 'month', 'monthnum', 'week', 'w', 'dayofyear', 'day', 'dayofweek', 'dayofweek_iso', 'hour', 'minute', 'second' );
    58 	public $time_keys = array( 'after', 'before', 'year', 'month', 'monthnum', 'week', 'w', 'dayofyear', 'day', 'dayofweek', 'dayofweek_iso', 'hour', 'minute', 'second' );
    64 
    59 
    65 	/**
    60 	/**
    71 	 * BETWEEN', arrays of two valid values are required. See individual argument descriptions for accepted values.
    66 	 * BETWEEN', arrays of two valid values are required. See individual argument descriptions for accepted values.
    72 	 *
    67 	 *
    73 	 * @since 3.7.0
    68 	 * @since 3.7.0
    74 	 * @since 4.0.0 The $inclusive logic was updated to include all times within the date range.
    69 	 * @since 4.0.0 The $inclusive logic was updated to include all times within the date range.
    75 	 * @since 4.1.0 Introduced 'dayofweek_iso' time type parameter.
    70 	 * @since 4.1.0 Introduced 'dayofweek_iso' time type parameter.
    76 	 * @access public
       
    77 	 *
    71 	 *
    78 	 * @param array $date_query {
    72 	 * @param array $date_query {
    79 	 *     Array of date query clauses.
    73 	 *     Array of date query clauses.
    80 	 *
    74 	 *
    81 	 *     @type array {
    75 	 *     @type array {
   149 	 * @param array $default_column Optional. Default column to query against. Default 'post_date'.
   143 	 * @param array $default_column Optional. Default column to query against. Default 'post_date'.
   150 	 *                              Accepts 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt',
   144 	 *                              Accepts 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt',
   151 	 *                              'comment_date', 'comment_date_gmt'.
   145 	 *                              'comment_date', 'comment_date_gmt'.
   152 	 */
   146 	 */
   153 	public function __construct( $date_query, $default_column = 'post_date' ) {
   147 	public function __construct( $date_query, $default_column = 'post_date' ) {
   154 
       
   155 		if ( isset( $date_query['relation'] ) && 'OR' === strtoupper( $date_query['relation'] ) ) {
   148 		if ( isset( $date_query['relation'] ) && 'OR' === strtoupper( $date_query['relation'] ) ) {
   156 			$this->relation = 'OR';
   149 			$this->relation = 'OR';
   157 		} else {
   150 		} else {
   158 			$this->relation = 'AND';
   151 			$this->relation = 'AND';
   159 		}
   152 		}
   190 	 * Ensures that each query-level clause has a 'relation' key, and that
   183 	 * Ensures that each query-level clause has a 'relation' key, and that
   191 	 * each first-order clause contains all the necessary keys from
   184 	 * each first-order clause contains all the necessary keys from
   192 	 * `$defaults`.
   185 	 * `$defaults`.
   193 	 *
   186 	 *
   194 	 * @since 4.1.0
   187 	 * @since 4.1.0
   195 	 * @access public
       
   196 	 *
   188 	 *
   197 	 * @param array $queries
   189 	 * @param array $queries
   198 	 * @param array $parent_query
   190 	 * @param array $parent_query
   199 	 *
   191 	 *
   200 	 * @return array Sanitized queries.
   192 	 * @return array Sanitized queries.
   250 	 * Determine whether this is a first-order clause.
   242 	 * Determine whether this is a first-order clause.
   251 	 *
   243 	 *
   252 	 * Checks to see if the current clause has any time-related keys.
   244 	 * Checks to see if the current clause has any time-related keys.
   253 	 * If so, it's first-order.
   245 	 * If so, it's first-order.
   254 	 *
   246 	 *
       
   247 	 * @since 4.1.0
       
   248 	 *
   255 	 * @param  array $query Query clause.
   249 	 * @param  array $query Query clause.
   256 	 * @return bool True if this is a first-order clause.
   250 	 * @return bool True if this is a first-order clause.
   257 	 */
   251 	 */
   258 	protected function is_first_order_clause( $query ) {
   252 	protected function is_first_order_clause( $query ) {
   259 		$time_keys = array_intersect( $this->time_keys, array_keys( $query ) );
   253 		$time_keys = array_intersect( $this->time_keys, array_keys( $query ) );
   262 
   256 
   263 	/**
   257 	/**
   264 	 * Determines and validates what comparison operator to use.
   258 	 * Determines and validates what comparison operator to use.
   265 	 *
   259 	 *
   266 	 * @since 3.7.0
   260 	 * @since 3.7.0
   267 	 * @access public
       
   268 	 *
   261 	 *
   269 	 * @param array $query A date query or a date subquery.
   262 	 * @param array $query A date query or a date subquery.
   270 	 * @return string The comparison operator.
   263 	 * @return string The comparison operator.
   271 	 */
   264 	 */
   272 	public function get_compare( $query ) {
   265 	public function get_compare( $query ) {
   282 	 * Note that date queries with invalid date ranges are allowed to
   275 	 * Note that date queries with invalid date ranges are allowed to
   283 	 * continue (though of course no items will be found for impossible dates).
   276 	 * continue (though of course no items will be found for impossible dates).
   284 	 * This method only generates debug notices for these cases.
   277 	 * This method only generates debug notices for these cases.
   285 	 *
   278 	 *
   286 	 * @since  4.1.0
   279 	 * @since  4.1.0
   287 	 * @access public
       
   288 	 *
   280 	 *
   289 	 * @param  array $date_query The date_query array.
   281 	 * @param  array $date_query The date_query array.
   290 	 * @return bool  True if all values in the query are valid, false if one or more fail.
   282 	 * @return bool  True if all values in the query are valid, false if one or more fail.
   291 	 */
   283 	 */
   292 	public function validate_date_values( $date_query = array() ) {
   284 	public function validate_date_values( $date_query = array() ) {
   353 			'max' => 12
   345 			'max' => 12
   354 		);
   346 		);
   355 
   347 
   356 		// Weeks per year.
   348 		// Weeks per year.
   357 		if ( isset( $_year ) ) {
   349 		if ( isset( $_year ) ) {
   358 			// If we have a specific year, use it to calculate number of weeks.
   350 			/*
   359 			$date = new DateTime();
   351 			 * If we have a specific year, use it to calculate number of weeks.
   360 			$date->setISODate( $_year, 53 );
   352 			 * Note: the number of weeks in a year is the date in which Dec 28 appears.
   361 			$week_count = $date->format( "W" ) === "53" ? 53 : 52;
   353 			 */
       
   354 			$week_count = date( 'W', mktime( 0, 0, 0, 12, 28, $_year ) );
   362 
   355 
   363 		} else {
   356 		} else {
   364 			// Otherwise set the week-count to a maximum of 53.
   357 			// Otherwise set the week-count to a maximum of 53.
   365 			$week_count = 53;
   358 			$week_count = 53;
   366 		}
   359 		}
   477 	 * known tables, and then, if found, have a table prefix (such as 'wp_posts.') prepended.
   470 	 * known tables, and then, if found, have a table prefix (such as 'wp_posts.') prepended.
   478 	 * Prefixed column names (such as 'wp_posts.post_date') bypass this whitelist check,
   471 	 * Prefixed column names (such as 'wp_posts.post_date') bypass this whitelist check,
   479 	 * and are only sanitized to remove illegal characters.
   472 	 * and are only sanitized to remove illegal characters.
   480 	 *
   473 	 *
   481 	 * @since 3.7.0
   474 	 * @since 3.7.0
   482 	 * @access public
       
   483 	 *
   475 	 *
   484 	 * @param string $column The user-supplied column name.
   476 	 * @param string $column The user-supplied column name.
   485 	 * @return string A validated column name value.
   477 	 * @return string A validated column name value.
   486 	 */
   478 	 */
   487 	public function validate_column( $column ) {
   479 	public function validate_column( $column ) {
   488 		global $wpdb;
   480 		global $wpdb;
   489 
   481 
   490 		$valid_columns = array(
   482 		$valid_columns = array(
   491 			'post_date', 'post_date_gmt', 'post_modified',
   483 			'post_date', 'post_date_gmt', 'post_modified',
   492 			'post_modified_gmt', 'comment_date', 'comment_date_gmt',
   484 			'post_modified_gmt', 'comment_date', 'comment_date_gmt',
   493 			'user_registered',
   485 			'user_registered', 'registered', 'last_updated',
   494 		);
   486 		);
   495 
   487 
   496 		// Attempt to detect a table prefix.
   488 		// Attempt to detect a table prefix.
   497 		if ( false === strpos( $column, '.' ) ) {
   489 		if ( false === strpos( $column, '.' ) ) {
   498 			/**
   490 			/**
   499 			 * Filter the list of valid date query columns.
   491 			 * Filters the list of valid date query columns.
   500 			 *
   492 			 *
   501 			 * @since 3.7.0
   493 			 * @since 3.7.0
   502 			 * @since 4.1.0 Added 'user_registered' to the default recognized columns.
   494 			 * @since 4.1.0 Added 'user_registered' to the default recognized columns.
   503 			 *
   495 			 *
   504 			 * @param array $valid_columns An array of valid date query columns. Defaults
   496 			 * @param array $valid_columns An array of valid date query columns. Defaults
   522 					'comment_date_gmt',
   514 					'comment_date_gmt',
   523 				),
   515 				),
   524 				$wpdb->users => array(
   516 				$wpdb->users => array(
   525 					'user_registered',
   517 					'user_registered',
   526 				),
   518 				),
       
   519 				$wpdb->blogs => array(
       
   520 					'registered',
       
   521 					'last_updated',
       
   522 				),
   527 			);
   523 			);
   528 
   524 
   529 			// If it's a known column name, add the appropriate table prefix.
   525 			// If it's a known column name, add the appropriate table prefix.
   530 			foreach ( $known_columns as $table_name => $table_columns ) {
   526 			foreach ( $known_columns as $table_name => $table_columns ) {
   531 				if ( in_array( $column, $table_columns ) ) {
   527 				if ( in_array( $column, $table_columns ) ) {
   542 
   538 
   543 	/**
   539 	/**
   544 	 * Generate WHERE clause to be appended to a main query.
   540 	 * Generate WHERE clause to be appended to a main query.
   545 	 *
   541 	 *
   546 	 * @since 3.7.0
   542 	 * @since 3.7.0
   547 	 * @access public
       
   548 	 *
   543 	 *
   549 	 * @return string MySQL WHERE clause.
   544 	 * @return string MySQL WHERE clause.
   550 	 */
   545 	 */
   551 	public function get_sql() {
   546 	public function get_sql() {
   552 		$sql = $this->get_sql_clauses();
   547 		$sql = $this->get_sql_clauses();
   553 
   548 
   554 		$where = $sql['where'];
   549 		$where = $sql['where'];
   555 
   550 
   556 		/**
   551 		/**
   557 		 * Filter the date query WHERE clause.
   552 		 * Filters the date query WHERE clause.
   558 		 *
   553 		 *
   559 		 * @since 3.7.0
   554 		 * @since 3.7.0
   560 		 *
   555 		 *
   561 		 * @param string        $where WHERE clause of the date query.
   556 		 * @param string        $where WHERE clause of the date query.
   562 		 * @param WP_Date_Query $this  The WP_Date_Query instance.
   557 		 * @param WP_Date_Query $this  The WP_Date_Query instance.
   565 	}
   560 	}
   566 
   561 
   567 	/**
   562 	/**
   568 	 * Generate SQL clauses to be appended to a main query.
   563 	 * Generate SQL clauses to be appended to a main query.
   569 	 *
   564 	 *
   570 	 * Called by the public {@see WP_Date_Query::get_sql()}, this method
   565 	 * Called by the public WP_Date_Query::get_sql(), this method is abstracted
   571 	 * is abstracted out to maintain parity with the other Query classes.
   566 	 * out to maintain parity with the other Query classes.
   572 	 *
   567 	 *
   573 	 * @since 4.1.0
   568 	 * @since 4.1.0
   574 	 * @access protected
       
   575 	 *
   569 	 *
   576 	 * @return array {
   570 	 * @return array {
   577 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   571 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   578 	 *
   572 	 *
   579 	 *     @type string $join  SQL fragment to append to the main JOIN clause.
   573 	 *     @type string $join  SQL fragment to append to the main JOIN clause.
   595 	 *
   589 	 *
   596 	 * If nested subqueries are found, this method recurses the tree to
   590 	 * If nested subqueries are found, this method recurses the tree to
   597 	 * produce the properly nested SQL.
   591 	 * produce the properly nested SQL.
   598 	 *
   592 	 *
   599 	 * @since 4.1.0
   593 	 * @since 4.1.0
   600 	 * @access protected
       
   601 	 *
   594 	 *
   602 	 * @param array $query Query to parse.
   595 	 * @param array $query Query to parse.
   603 	 * @param int   $depth Optional. Number of tree levels deep we currently are.
   596 	 * @param int   $depth Optional. Number of tree levels deep we currently are.
   604 	 *                     Used to calculate indentation. Default 0.
   597 	 *                     Used to calculate indentation. Default 0.
   605 	 * @return array {
   598 	 * @return array {
   680 	 *
   673 	 *
   681 	 * A wrapper for get_sql_for_clause(), included here for backward
   674 	 * A wrapper for get_sql_for_clause(), included here for backward
   682 	 * compatibility while retaining the naming convention across Query classes.
   675 	 * compatibility while retaining the naming convention across Query classes.
   683 	 *
   676 	 *
   684 	 * @since  3.7.0
   677 	 * @since  3.7.0
   685 	 * @access protected
       
   686 	 *
   678 	 *
   687 	 * @param  array $query Date query arguments.
   679 	 * @param  array $query Date query arguments.
   688 	 * @return array {
   680 	 * @return array {
   689 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   681 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   690 	 *
   682 	 *
   698 
   690 
   699 	/**
   691 	/**
   700 	 * Turns a first-order date query into SQL for a WHERE clause.
   692 	 * Turns a first-order date query into SQL for a WHERE clause.
   701 	 *
   693 	 *
   702 	 * @since  4.1.0
   694 	 * @since  4.1.0
   703 	 * @access protected
       
   704 	 *
   695 	 *
   705 	 * @param  array $query        Date query clause.
   696 	 * @param  array $query        Date query clause.
   706 	 * @param  array $parent_query Parent query of the current date query.
   697 	 * @param  array $parent_query Parent query of the current date query.
   707 	 * @return array {
   698 	 * @return array {
   708 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   699 	 *     Array containing JOIN and WHERE SQL clauses to append to the main query.
   733 			$lt .= '=';
   724 			$lt .= '=';
   734 			$gt .= '=';
   725 			$gt .= '=';
   735 		}
   726 		}
   736 
   727 
   737 		// Range queries.
   728 		// Range queries.
   738 		if ( ! empty( $query['after'] ) )
   729 		if ( ! empty( $query['after'] ) ) {
   739 			$where_parts[] = $wpdb->prepare( "$column $gt %s", $this->build_mysql_datetime( $query['after'], ! $inclusive ) );
   730 			$where_parts[] = $wpdb->prepare( "$column $gt %s", $this->build_mysql_datetime( $query['after'], ! $inclusive ) );
   740 
   731 		}
   741 		if ( ! empty( $query['before'] ) )
   732 		if ( ! empty( $query['before'] ) ) {
   742 			$where_parts[] = $wpdb->prepare( "$column $lt %s", $this->build_mysql_datetime( $query['before'], $inclusive ) );
   733 			$where_parts[] = $wpdb->prepare( "$column $lt %s", $this->build_mysql_datetime( $query['before'], $inclusive ) );
   743 
   734 		}
   744 		// Specific value queries.
   735 		// Specific value queries.
   745 
   736 
   746 		if ( isset( $query['year'] ) && $value = $this->build_value( $compare, $query['year'] ) )
   737 		if ( isset( $query['year'] ) && $value = $this->build_value( $compare, $query['year'] ) )
   747 			$where_parts[] = "YEAR( $column ) $compare $value";
   738 			$where_parts[] = "YEAR( $column ) $compare $value";
   748 
   739 
   793 
   784 
   794 	/**
   785 	/**
   795 	 * Builds and validates a value string based on the comparison operator.
   786 	 * Builds and validates a value string based on the comparison operator.
   796 	 *
   787 	 *
   797 	 * @since 3.7.0
   788 	 * @since 3.7.0
   798 	 * @access public
       
   799 	 *
   789 	 *
   800 	 * @param string $compare The compare operator to use
   790 	 * @param string $compare The compare operator to use
   801 	 * @param string|array $value The value
   791 	 * @param string|array $value The value
   802 	 * @return string|false|int The value to be used in SQL or false on error.
   792 	 * @return string|false|int The value to be used in SQL or false on error.
   803 	 */
   793 	 */
   836 
   826 
   837 				$value = array_map( 'intval', $value );
   827 				$value = array_map( 'intval', $value );
   838 
   828 
   839 				return $value[0] . ' AND ' . $value[1];
   829 				return $value[0] . ' AND ' . $value[1];
   840 
   830 
   841 			default;
   831 			default:
   842 				if ( ! is_numeric( $value ) ) {
   832 				if ( ! is_numeric( $value ) ) {
   843 					return false;
   833 					return false;
   844 				}
   834 				}
   845 
   835 
   846 				return (int) $value;
   836 				return (int) $value;
   850 	/**
   840 	/**
   851 	 * Builds a MySQL format date/time based on some query parameters.
   841 	 * Builds a MySQL format date/time based on some query parameters.
   852 	 *
   842 	 *
   853 	 * You can pass an array of values (year, month, etc.) with missing parameter values being defaulted to
   843 	 * You can pass an array of values (year, month, etc.) with missing parameter values being defaulted to
   854 	 * either the maximum or minimum values (controlled by the $default_to parameter). Alternatively you can
   844 	 * either the maximum or minimum values (controlled by the $default_to parameter). Alternatively you can
   855 	 * pass a string that that will be run through strtotime().
   845 	 * pass a string that will be run through strtotime().
   856 	 *
   846 	 *
   857 	 * @since 3.7.0
   847 	 * @since 3.7.0
   858 	 * @access public
       
   859 	 *
   848 	 *
   860 	 * @param string|array $datetime       An array of parameters or a strotime() string
   849 	 * @param string|array $datetime       An array of parameters or a strotime() string
   861 	 * @param bool         $default_to_max Whether to round up incomplete dates. Supported by values
   850 	 * @param bool         $default_to_max Whether to round up incomplete dates. Supported by values
   862 	 *                                     of $datetime that are arrays, or string values that are a
   851 	 *                                     of $datetime that are arrays, or string values that are a
   863 	 *                                     subset of MySQL date format ('Y', 'Y-m', 'Y-m-d', 'Y-m-d H:i').
   852 	 *                                     subset of MySQL date format ('Y', 'Y-m', 'Y-m-d', 'Y-m-d H:i').
   941 	 * If just hour, minute, or second is set than a normal comparison will be done.
   930 	 * If just hour, minute, or second is set than a normal comparison will be done.
   942 	 * However if multiple values are passed, a pseudo-decimal time will be created
   931 	 * However if multiple values are passed, a pseudo-decimal time will be created
   943 	 * in order to be able to accurately compare against.
   932 	 * in order to be able to accurately compare against.
   944 	 *
   933 	 *
   945 	 * @since 3.7.0
   934 	 * @since 3.7.0
   946 	 * @access public
       
   947 	 *
   935 	 *
   948 	 * @param string $column The column to query against. Needs to be pre-validated!
   936 	 * @param string $column The column to query against. Needs to be pre-validated!
   949 	 * @param string $compare The comparison operator. Needs to be pre-validated!
   937 	 * @param string $compare The comparison operator. Needs to be pre-validated!
   950 	 * @param int|null $hour Optional. An hour value (0-23).
   938 	 * @param int|null $hour Optional. An hour value (0-23).
   951 	 * @param int|null $minute Optional. A minute value (0-59).
   939 	 * @param int|null $minute Optional. A minute value (0-59).
   989 			return false;
   977 			return false;
   990 
   978 
   991 		$format = $time = '';
   979 		$format = $time = '';
   992 
   980 
   993 		// Hour
   981 		// Hour
   994 		if ( $hour ) {
   982 		if ( null !== $hour ) {
   995 			$format .= '%H.';
   983 			$format .= '%H.';
   996 			$time   .= sprintf( '%02d', $hour ) . '.';
   984 			$time   .= sprintf( '%02d', $hour ) . '.';
   997 		} else {
   985 		} else {
   998 			$format .= '0.';
   986 			$format .= '0.';
   999 			$time   .= '0.';
   987 			$time   .= '0.';