Commit fd658dfcb20 for woocommerce

commit fd658dfcb202226ae81c4b584a1d05e147596ad2
Author: Cvetan Cvetanov <cvetan.cvetanov@automattic.com>
Date:   Thu Aug 20 13:08:00 2026 +0300

    Fix last_quarter timeframe returning an invalid September 31 end date (#67787)

    * Fix last_quarter timeframe returning an invalid September 31 end date

    TimeInterval::get_timeframe_dates() built the Q3 end bound as "-09-31".
    September has 30 days, so PHP's parser silently normalized the value to
    October 1, widening the quarter by a full day.

    The only consumer, TotalPaymentsVolumeProcessor, passes this string as the
    "before" bound of a revenue query, so remote inbox rules using
    "timeframe: last_quarter" summed an extra day of sales when evaluated
    between October and December, and could match stores they should not.

    Use "-09-30", and cover all four quarter ends with a test that parses each
    returned value. The previous test asserted the raw string and never parsed
    it, which is why it passed against an impossible date.

    Refs #67786

    * Add changelog entry for last_quarter end date fix

diff --git a/plugins/woocommerce/changelog/fix-last-quarter-end-date-rollover b/plugins/woocommerce/changelog/fix-last-quarter-end-date-rollover
new file mode 100644
index 00000000000..5cd5e3b221c
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-last-quarter-end-date-rollover
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix the last_quarter end date in TimeInterval::get_timeframe_dates(), which used an invalid September 31 and rolled over to October 1, widening the quarter by a day.
diff --git a/plugins/woocommerce/src/Admin/API/Reports/TimeInterval.php b/plugins/woocommerce/src/Admin/API/Reports/TimeInterval.php
index 9fae2a685a8..447e6d3d148 100644
--- a/plugins/woocommerce/src/Admin/API/Reports/TimeInterval.php
+++ b/plugins/woocommerce/src/Admin/API/Reports/TimeInterval.php
@@ -682,7 +682,7 @@ class TimeInterval {
 				case $current_month >= 10 && $current_month <= 12:
 					return array(
 						'start' => $current_year . '-07-01 00:00:00',
-						'end'   => $current_year . '-09-31 23:59:59',
+						'end'   => $current_year . '-09-30 23:59:59',
 					);
 			}
 		}
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/api/reports-interval.php b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/api/reports-interval.php
index f5ece349b8d..26e7eea80f5 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/api/reports-interval.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/woocommerce-admin/api/reports-interval.php
@@ -1082,7 +1082,44 @@ class WC_Admin_Tests_Reports_Interval_Stats extends WC_Unit_Test_Case {
 		$dates    = TimeInterval::get_timeframe_dates( 'last_quarter', $datetime );

 		$this->assertEquals( '2022-07-01 00:00:00', $dates['start'] );
-		$this->assertEquals( '2022-09-31 23:59:59', $dates['end'] );
+		$this->assertEquals( '2022-09-30 23:59:59', $dates['end'] );
+	}
+
+	/**
+	 * @testdox Every "last_quarter" end date should be a real calendar date that does not roll over into the next month.
+	 *
+	 * @dataProvider provider_last_quarter_end_dates
+	 *
+	 * @param string $current_date Date the timeframe is evaluated on.
+	 * @param string $expected_end Expected end of the preceding quarter.
+	 */
+	public function test_timeframes_last_quarter_end_dates_are_valid_calendar_dates( $current_date, $expected_end ) {
+		$dates = TimeInterval::get_timeframe_dates( 'last_quarter', new DateTime( $current_date ) );
+
+		$this->assertEquals( $expected_end, $dates['end'] );
+
+		// String equality alone passes for invalid dates such as "-09-31", so assert the value survives parsing.
+		$parsed = new DateTime( $dates['end'] );
+
+		$this->assertEquals(
+			$expected_end,
+			$parsed->format( 'Y-m-d H:i:s' ),
+			"{$dates['end']} is not a valid calendar date and rolls over to {$parsed->format( 'Y-m-d H:i:s' )}."
+		);
+	}
+
+	/**
+	 * Data provider for test_timeframes_last_quarter_end_dates_are_valid_calendar_dates.
+	 *
+	 * @return array
+	 */
+	public function provider_last_quarter_end_dates() {
+		return array(
+			'Q4 of the preceding year' => array( '2022-01-12', '2021-12-31 23:59:59' ),
+			'Q1'                       => array( '2022-05-26', '2022-03-31 23:59:59' ),
+			'Q2'                       => array( '2022-07-18', '2022-06-30 23:59:59' ),
+			'Q3'                       => array( '2022-11-07', '2022-09-30 23:59:59' ),
+		);
 	}

 	/**