Commit 37786b25b0e for woocommerce
commit 37786b25b0ecf63080aabcb7ec01ad7d5c65d962
Author: Mike Jolley <mike.jolley@me.com>
Date: Fri Oct 23 12:29:27 2026 +0100
Ignore empty date_query boundaries on HPOS local date columns (#69983)
An empty "after" or "before" on a local date column now behaves as it
does in WP_Date_Query and the posts data store: it is ignored instead
of resolving to the current time (or the Unix epoch). A date_query
with no boundary at all, such as a year-only query, no longer reads an
undefined "before" key and returns no orders.
diff --git a/plugins/woocommerce/changelog/fix-hpos-empty-date-query-boundary b/plugins/woocommerce/changelog/fix-hpos-empty-date-query-boundary
new file mode 100644
index 00000000000..f0c712959cd
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-hpos-empty-date-query-boundary
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix order date queries that leave the "after" or "before" date empty, so they no longer drop matching orders on HPOS stores.
diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableQuery.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableQuery.php
index 5dc56fc04c3..214233e19b8 100644
--- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableQuery.php
+++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableQuery.php
@@ -618,8 +618,14 @@ class OrdersTableQuery {
// Convert any local dates to GMT.
if ( isset( $local_to_gmt_date_keys[ $query['column'] ] ) ) {
- $query['column'] = $local_to_gmt_date_keys[ $query['column'] ];
- $op = isset( $query['after'] ) ? 'after' : 'before';
+ $query['column'] = $local_to_gmt_date_keys[ $query['column'] ];
+ $op = isset( $query['after'] ) ? 'after' : 'before';
+
+ // WP_Date_Query ignores an empty boundary, so leave it empty rather than resolving it to a date.
+ if ( empty( $query[ $op ] ) ) {
+ return $query;
+ }
+
$date_value_local = $query[ $op ];
// date_create() honors an offset in the string; wc_string_to_datetime() re-applies the site offset to formats like "10:00:00.500Z" or "+02".
$parsed_date = is_string( $date_value_local ) && ! is_numeric( $date_value_local ) ? date_create( $date_value_local, wp_timezone() ) : false;
diff --git a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php
index ef89a85c85d..d53802a2cc9 100644
--- a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php
@@ -3833,6 +3833,51 @@ class OrdersTableDataStoreTests extends \HposTestCase {
$this->assertContains( $order_id, $query->orders, 'An unparseable "after" value should still resolve to the Unix epoch.' );
}
+ /**
+ * @testdox An empty 'date_query' boundary on a local date column is ignored, as WP_Date_Query does.
+ * @testWith ["after"]
+ * ["before"]
+ *
+ * @param string $operator The date query operator, 'after' or 'before'.
+ */
+ public function test_local_date_query_ignores_empty_boundary( string $operator ): void {
+ $past_id = $this->create_order_created_at( '2024-05-01T11:00:00Z' );
+ $future_id = $this->create_order_created_at( time() + DAY_IN_SECONDS );
+
+ $query = new OrdersTableQuery(
+ array(
+ 'date_query' => array(
+ array(
+ 'column' => 'post_date',
+ $operator => '',
+ ),
+ ),
+ )
+ );
+
+ $this->assertContains( $past_id, $query->orders, "A past order should match an empty '{$operator}' boundary." );
+ $this->assertContains( $future_id, $query->orders, "A future order should match an empty '{$operator}' boundary." );
+ }
+
+ /**
+ * @testdox A 'date_query' on a local date column with no 'after' or 'before' boundary still matches orders.
+ */
+ public function test_local_date_query_without_boundary_matches_orders(): void {
+ $order_id = $this->create_order_created_at( '2024-05-01T11:00:00Z' );
+
+ $query = new OrdersTableQuery(
+ array(
+ 'date_query' => array(
+ array(
+ 'column' => 'post_date',
+ 'year' => 2024,
+ ),
+ ),
+ )
+ );
+ $this->assertContains( $order_id, $query->orders, 'A year-only query should match an order from that year.' );
+ }
+
/**
* @testdox A WC_DateTime 'date_query' value on a local date column keeps its existing behavior.
*/