* @param int $expected_interval_count Expected number of intervals in total.
* @param int $db_records Total number of records for given period in the database.
* @param int $items_per_page Number of items per page.
* @param int $page_no Page number.
* @param string $order asc or desc.
* @param string $order_by Column by which the result will be sorted.
* @param int $intervals_count Number of records for given (possibly shortened) time interval.
public static function intervals_missing( $expected_interval_count, $db_records, $items_per_page, $page_no, $order, $order_by, $intervals_count ) {
if ( $expected_interval_count <= $db_records ) {
if ( 'date' === $order_by ) {
$expected_intervals_on_page = self::expected_intervals_on_page( $expected_interval_count, $items_per_page, $page_no );
return $intervals_count < $expected_intervals_on_page;
if ( 'desc' === $order ) {
return $page_no > floor( $db_records / $items_per_page );
if ( 'asc' === $order ) {
return $page_no <= ceil( ( $expected_interval_count - $db_records ) / $items_per_page );
* Normalize "*_between" parameters to "*_min" and "*_max" for numeric values
* and "*_after" and "*_before" for date values.
* @param array $request Query params from REST API request.
* @param string|array $param_names One or more param names to handle. Should not include "_between" suffix.
* @param bool $is_date Boolean if the param is date is related.
* @return array Normalized query values.
public static function normalize_between_params( $request, $param_names, $is_date ) {
if ( ! is_array( $param_names ) ) {
$param_names = array( $param_names );
foreach ( $param_names as $param_name ) {
if ( ! is_array( $request[ $param_name . '_between' ] ) ) {
$range = $request[ $param_name . '_between' ];
if ( 2 !== count( $range ) ) {
$min = $is_date ? '_after' : '_min';
$max = $is_date ? '_before' : '_max';
if ( $range[0] < $range[1] ) {
$normalized[ $param_name . $min ] = $range[0];
$normalized[ $param_name . $max ] = $range[1];
$normalized[ $param_name . $min ] = $range[1];
$normalized[ $param_name . $max ] = $range[0];
* Validate a "*_between" range argument (an array with 2 numeric items).
* @param mixed $value Parameter value.
* @param WP_REST_Request $request REST Request.
* @param string $param Parameter name.
* @return WP_Error|boolean
public static function rest_validate_between_numeric_arg( $value, $request, $param ) {
if ( ! wp_is_numeric_array( $value ) ) {
/* translators: 1: parameter name */
sprintf( __( '%1$s is not a numerically indexed array.', 'woocommerce' ), $param )
! is_numeric( $value[0] ) ||
! is_numeric( $value[1] )
/* translators: %s: parameter name */
sprintf( __( '%s must contain 2 numbers.', 'woocommerce' ), $param )
* Validate a "*_between" range argument (an array with 2 date items).
* @param mixed $value Parameter value.
* @param WP_REST_Request $request REST Request.
* @param string $param Parameter name.
* @return WP_Error|boolean
public static function rest_validate_between_date_arg( $value, $request, $param ) {
if ( ! wp_is_numeric_array( $value ) ) {
/* translators: 1: parameter name */
sprintf( __( '%1$s is not a numerically indexed array.', 'woocommerce' ), $param )
! rest_parse_date( $value[0] ) ||
! rest_parse_date( $value[1] )
/* translators: %s: parameter name */
sprintf( __( '%s must contain 2 valid dates.', 'woocommerce' ), $param )
* Get dates from a timeframe string.
* @param int $timeframe Timeframe to use. One of: last_week|last_month|last_quarter|last_6_months|last_year.
* @param DateTime|null $current_date DateTime of current date to compare.
public static function get_timeframe_dates( $timeframe, $current_date = null ) {
$current_date = new \DateTime();
$current_year = $current_date->format( 'Y' );
$current_month = $current_date->format( 'm' );
if ( 'last_week' === $timeframe ) {
'start' => $current_date->modify( 'last week monday' )->format( 'Y-m-d 00:00:00' ),
'end' => $current_date->modify( 'this sunday' )->format( 'Y-m-d 23:59:59' ),
if ( 'last_month' === $timeframe ) {
'start' => $current_date->modify( 'first day of previous month' )->format( 'Y-m-d 00:00:00' ),
'end' => $current_date->modify( 'last day of this month' )->format( 'Y-m-d 23:59:59' ),
if ( 'last_quarter' === $timeframe ) {
switch ( $current_month ) {
case $current_month >= 1 && $current_month <= 3:
'start' => ( $current_year - 1 ) . '-10-01 00:00:00',
'end' => ( $current_year - 1 ) . '-12-31 23:59:59',
case $current_month >= 4 && $current_month <= 6:
'start' => $current_year . '-01-01 00:00:00',
'end' => $current_year . '-03-31 23:59:59',
case $current_month >= 7 && $current_month <= 9:
'start' => $current_year . '-04-01 00:00:00',
'end' => $current_year . '-06-30 23:59:59',
case $current_month >= 10 && $current_month <= 12:
'start' => $current_year . '-07-01 00:00:00',
'end' => $current_year . '-09-31 23:59:59',
if ( 'last_6_months' === $timeframe ) {
if ( $current_month >= 1 && $current_month <= 6 ) {
'start' => ( $current_year - 1 ) . '-07-01 00:00:00',
'end' => ( $current_year - 1 ) . '-12-31 23:59:59',
'start' => $current_year . '-01-01 00:00:00',
'end' => $current_year . '-06-30 23:59:59',
if ( 'last_year' === $timeframe ) {
'start' => ( $current_year - 1 ) . '-01-01 00:00:00',
'end' => ( $current_year - 1 ) . '-12-31 23:59:59',