diff --git a/plugins/woocommerce-admin/changelogs/fix-7748-analytics-crashing-daylight-saving b/plugins/woocommerce-admin/changelogs/fix-7748-analytics-crashing-daylight-saving new file mode 100644 index 00000000000..b59381ddef4 --- /dev/null +++ b/plugins/woocommerce-admin/changelogs/fix-7748-analytics-crashing-daylight-saving @@ -0,0 +1,4 @@ +Significance: patch +Type: Fix + +Fix analytics crashing on daylight saving #7763 diff --git a/plugins/woocommerce-admin/src/API/Reports/TimeInterval.php b/plugins/woocommerce-admin/src/API/Reports/TimeInterval.php index ffd85fd061b..fb044b5f2e4 100644 --- a/plugins/woocommerce-admin/src/API/Reports/TimeInterval.php +++ b/plugins/woocommerce-admin/src/API/Reports/TimeInterval.php @@ -302,24 +302,18 @@ class TimeInterval { * @return DateTime */ public static function next_day_start( $datetime, $reversed = false ) { - $seconds_into_day = (int) $datetime->format( 'H' ) * HOUR_IN_SECONDS + (int) $datetime->format( 'i' ) * MINUTE_IN_SECONDS + (int) $datetime->format( 's' ); + $oneday = new \DateInterval( 'P1D' ); + $new_datetime = clone $datetime; - // The day boundary is actually next midnight when going in reverse, so set it to day -1 at 23:59:59. if ( $reversed ) { - $timestamp = (int) $datetime->format( 'U' ); - $next_day_timestamp = $timestamp - ( $seconds_into_day + 1 ); + $new_datetime->sub( $oneday ); + $new_datetime->setTime( 23, 59, 59 ); } else { - $day_increment = new \DateInterval( 'P1D' ); // Plus 1 Day. - $next_datetime = clone $datetime; - $next_datetime->add( $day_increment ); - $timestamp = (int) $next_datetime->format( 'U' ); - $next_day_timestamp = $timestamp - $seconds_into_day; + $new_datetime->add( $oneday ); + $new_datetime->setTime( 0, 0, 0 ); } - $next_day = new \DateTime(); - $next_day->setTimestamp( $next_day_timestamp ); - $next_day->setTimezone( new \DateTimeZone( wc_timezone_string() ) ); - return $next_day; + return $new_datetime; } /** @@ -332,8 +326,12 @@ class TimeInterval { public static function next_week_start( $datetime, $reversed = false ) { $first_day_of_week = absint( get_option( 'start_of_week' ) ); $initial_week_no = self::week_number( $datetime, $first_day_of_week ); + $failsafe_count = 0; do { + if ( $failsafe_count++ >= 7 ) { + break; + } $datetime = self::next_day_start( $datetime, $reversed ); $current_week_no = self::week_number( $datetime, $first_day_of_week ); } while ( $current_week_no === $initial_week_no ); diff --git a/plugins/woocommerce-admin/tests/api/reports-interval.php b/plugins/woocommerce-admin/tests/api/reports-interval.php index 65c0b751daf..f59d02eef2a 100644 --- a/plugins/woocommerce-admin/tests/api/reports-interval.php +++ b/plugins/woocommerce-admin/tests/api/reports-interval.php @@ -670,6 +670,40 @@ class WC_Tests_Reports_Interval_Stats extends WC_Unit_Test_Case { } } + /** + * Test function that returns beginning of next day with daylight saving. Auckland's 2021 daylight saving + * starts on 26th September 2021 at 02:00:00 and ends on 3rd April 2022 at 03:00:00. + */ + public function test_next_day_start_daylight_saving() { + update_option( 'timezone_string', 'Pacific/Auckland' ); + $settings = array( + '2021-09-26T00:00:00' => array( // Before daylight saving starts. + 0 => '2021-09-27T00:00:00', + 1 => '2021-09-25T23:59:59', + ), + '2021-09-26T03:00:00' => array( // After daylight saving starts. + 0 => '2021-09-27T00:00:00', + 1 => '2021-09-25T23:59:59', + ), + '2022-04-03T00:00:00' => array( // Before daylight saving ends. + 0 => '2022-04-04T00:00:00', + 1 => '2022-04-02T23:59:59', + ), + '2022-04-03T23:59:00' => array( // After daylight saving ends. + 0 => '2022-04-04T00:00:00', + 1 => '2022-04-02T23:59:59', + ), + ); + + foreach ( $settings as $datetime_s => $setting ) { + $datetime = new DateTime( $datetime_s, new DateTimeZone( 'Pacific/Auckland' ) ); + foreach ( $setting as $reversed => $exp_value ) { + $result_dt = TimeInterval::next_day_start( $datetime, $reversed ); + $this->assertEquals( $exp_value, $result_dt->format( TimeInterval::$iso_datetime_format ), __FUNCTION__ . ": DT: $datetime_s; R: $reversed" ); + } + } + } + /** * Test function that returns beginning of next week, for weeks starting on Monday. */