From 821a5991b348be7a141653c19801c6a6c514aa2b Mon Sep 17 00:00:00 2001 From: Jason Hill Date: Thu, 19 Feb 2026 20:58:27 -0500 Subject: [PATCH 1/2] Fix metrics week calculations across DST --- core/app/models/workarea/metrics/by_week.rb | 9 ++++- core/app/models/workarea/metrics/scoring.rb | 8 +++- .../workarea/metrics/product_by_week_test.rb | 38 ++++++++++++++----- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/core/app/models/workarea/metrics/by_week.rb b/core/app/models/workarea/metrics/by_week.rb index 6ad0999093..a69ea04cd1 100644 --- a/core/app/models/workarea/metrics/by_week.rb +++ b/core/app/models/workarea/metrics/by_week.rb @@ -13,9 +13,14 @@ module ByWeek index({ reporting_on: 1 }, { expire_after_seconds: 2.years.seconds.to_i }) scope :last_week, -> do + # Use date math to avoid DST-related boundary shifts. + # We want the previous calendar week, not "now minus 7 days". + start_of_this_week = Time.current.to_date.beginning_of_week + start_of_last_week = start_of_this_week - 1.week + where( - :reporting_on.gte => Time.current.last_week, - :reporting_on.lt => Time.current.last_week.end_of_week + :reporting_on.gte => start_of_last_week.in_time_zone, + :reporting_on.lt => start_of_this_week.in_time_zone ) end end diff --git a/core/app/models/workarea/metrics/scoring.rb b/core/app/models/workarea/metrics/scoring.rb index fd2bdb7d8b..baf2eb6ff7 100644 --- a/core/app/models/workarea/metrics/scoring.rb +++ b/core/app/models/workarea/metrics/scoring.rb @@ -22,8 +22,12 @@ def score(field) end def weeks_ago - difference = Time.current.beginning_of_week - reporting_on.beginning_of_week - difference / 1.week + # Use date math instead of second-based math to avoid DST boundary issues. + # (e.g. a "week" containing a DST shift is not always 7 * 24 hours) + current_week = Time.current.to_date.beginning_of_week + reporting_week = reporting_on.to_date.beginning_of_week + + ((current_week - reporting_week) / 7).to_i end end end diff --git a/core/test/models/workarea/metrics/product_by_week_test.rb b/core/test/models/workarea/metrics/product_by_week_test.rb index 9696809b3a..bbe5e028c4 100644 --- a/core/test/models/workarea/metrics/product_by_week_test.rb +++ b/core/test/models/workarea/metrics/product_by_week_test.rb @@ -3,6 +3,10 @@ module Workarea module Metrics class ProductByWeekTest < TestCase + setup do + Metrics::ProductByWeek.delete_all + Metrics::ProductForLastWeek.delete_all + end def test_last_week two_weeks_ago = create_product_by_week(reporting_on: Time.zone.local(2018, 11, 25)) last_week = create_product_by_week(reporting_on: Time.zone.local(2018, 12, 2)) @@ -117,20 +121,34 @@ def test_score def test_weeks_ago model = create_product_by_week(reporting_on: Time.zone.local(2019, 1, 25)) - travel_to Time.zone.local(2019, 1, 25) - assert_equal(0, model.weeks_ago) + travel_to Time.zone.local(2019, 1, 25) do + assert_equal(0, model.weeks_ago) + end + + travel_to Time.zone.local(2019, 1, 27) do + assert_equal(0, model.weeks_ago) + end + + travel_to Time.zone.local(2019, 2, 8) do + assert_equal(2, model.weeks_ago) + end - travel_to Time.zone.local(2019, 1, 27) - assert_equal(0, model.weeks_ago) + travel_to Time.zone.local(2019, 2, 9) do + assert_equal(2, model.weeks_ago) + end - travel_to Time.zone.local(2019, 2, 8) - assert_equal(2, model.weeks_ago) + travel_to Time.zone.local(2019, 2, 11) do + assert_equal(3, model.weeks_ago) + end - travel_to Time.zone.local(2019, 2, 9) - assert_equal(2, model.weeks_ago) + # Regression: DST boundaries should not affect week math. + Time.use_zone('Eastern Time (US & Canada)') do + dst_model = create_product_by_week(reporting_on: Time.zone.local(2019, 3, 4, 0, 0, 0)) - travel_to Time.zone.local(2019, 2, 11) - assert_equal(3, model.weeks_ago) + travel_to Time.zone.local(2019, 3, 11, 0, 0, 0) do + assert_equal(1, dst_model.weeks_ago) + end + end end end end From c68b08d659a8e66291ae4170de08ac329d99f1e6 Mon Sep 17 00:00:00 2001 From: kitcommerce <262714811+kitcommerce@users.noreply.github.com> Date: Sat, 21 Feb 2026 14:56:43 -0500 Subject: [PATCH 2/2] Use block-form travel_to in ProductByWeek tests --- .../workarea/metrics/product_by_week_test.rb | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/core/test/models/workarea/metrics/product_by_week_test.rb b/core/test/models/workarea/metrics/product_by_week_test.rb index bbe5e028c4..c8d8215799 100644 --- a/core/test/models/workarea/metrics/product_by_week_test.rb +++ b/core/test/models/workarea/metrics/product_by_week_test.rb @@ -12,10 +12,11 @@ def test_last_week last_week = create_product_by_week(reporting_on: Time.zone.local(2018, 12, 2)) this_week = create_product_by_week(reporting_on: Time.zone.local(2018, 12, 5)) - travel_to Time.zone.local(2018, 12, 5) - refute(ProductByWeek.last_week.include?(two_weeks_ago)) - assert(ProductByWeek.last_week.include?(last_week)) - refute(ProductByWeek.last_week.include?(this_week)) + travel_to Time.zone.local(2018, 12, 5) do + refute(ProductByWeek.last_week.include?(two_weeks_ago)) + assert(ProductByWeek.last_week.include?(last_week)) + refute(ProductByWeek.last_week.include?(this_week)) + end end def test_by_views_percentile @@ -109,13 +110,13 @@ def test_score reporting_on: Time.zone.local(2018, 12, 5) ) - travel_to Time.zone.local(2018, 12, 5) - - Workarea.config.score_decay = 0.5 + travel_to Time.zone.local(2018, 12, 5) do + Workarea.config.score_decay = 0.5 - assert_equal(3, this_week.score(:orders)) - assert_equal(1, last_week.score(:orders)) - assert_equal(0.25, two_weeks_ago.score(:orders)) + assert_equal(3, this_week.score(:orders)) + assert_equal(1, last_week.score(:orders)) + assert_equal(0.25, two_weeks_ago.score(:orders)) + end end def test_weeks_ago