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.
 	 */