Skip to content

Commit dcb92e5

Browse files
authored
fix match_day_of_week for year_over_year comparisons (#6300)
1 parent 53569d7 commit dcb92e5

2 files changed

Lines changed: 93 additions & 2 deletions

File tree

lib/plausible/stats/comparisons.ex

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,28 @@ defmodule Plausible.Stats.Comparisons do
156156
defp get_comparison_datetime_range(
157157
%Query{
158158
input_date_range: input_range,
159-
include: %{compare: :year_over_year}
159+
include: %{compare: :year_over_year} = include
160160
} = source_query
161161
)
162162
when input_range in [:"24h", :day] do
163163
comparison_start = DateTime.shift(source_query.utc_time_range.first, year: -1)
164164
comparison_end = DateTime.shift(source_query.utc_time_range.last, year: -1)
165165

166-
DateTimeRange.new!(comparison_start, comparison_end)
166+
if include.compare_match_day_of_week do
167+
source_first_date = Times.to_date(source_query.utc_time_range.first, source_query.timezone)
168+
comparison_first_date = Times.to_date(comparison_start, source_query.timezone)
169+
170+
day_to_match = Date.day_of_week(source_first_date)
171+
matched_date = shift_to_nearest(day_to_match, comparison_first_date, source_first_date)
172+
days_shifted = Date.diff(matched_date, comparison_first_date)
173+
174+
DateTimeRange.new!(
175+
DateTime.shift(comparison_start, day: days_shifted),
176+
DateTime.shift(comparison_end, day: days_shifted)
177+
)
178+
else
179+
DateTimeRange.new!(comparison_start, comparison_end)
180+
end
167181
end
168182

169183
defp get_comparison_datetime_range(

test/plausible/stats/comparisons_test.exs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,65 @@ defmodule Plausible.Stats.ComparisonsTest do
442442
|> Enum.count()
443443
end
444444

445+
describe "with period set to today" do
446+
test "handles YoY comparison mode matching exact date", %{site: site} do
447+
query =
448+
QueryBuilder.build!(site,
449+
metrics: [:visitors],
450+
input_date_range: :day,
451+
include: [compare: :year_over_year],
452+
now: ~U[2023-03-15 18:30:00Z]
453+
)
454+
455+
comparison_query = Comparisons.get_comparison_query(query)
456+
457+
assert comparison_query.utc_time_range.first == ~U[2022-03-15 00:00:00Z]
458+
assert comparison_query.utc_time_range.last == ~U[2022-03-15 18:30:00Z]
459+
end
460+
461+
test "handles YoY comparison mode with match_day_of_week enabled",
462+
%{site: site} do
463+
query =
464+
QueryBuilder.build!(site,
465+
metrics: [:visitors],
466+
input_date_range: :day,
467+
include: [compare: :year_over_year, compare_match_day_of_week: true],
468+
now: ~U[2023-03-15 18:30:00Z]
469+
)
470+
471+
comparison_query = Comparisons.get_comparison_query(query)
472+
473+
# today range: 2023-03-15 (Wednesday) 00:00:00 to 18:30:00 UTC
474+
# Year ago: 2022-03-15 (Tuesday); nearest Wednesday is 2022-03-16 (+1 day)
475+
assert comparison_query.utc_time_range.first == ~U[2022-03-16 00:00:00Z]
476+
assert comparison_query.utc_time_range.last == ~U[2022-03-16 18:30:00Z]
477+
end
478+
479+
test "handles YoY + match_day_of_week comparison for a non-UTC timezone" do
480+
site = insert(:site, timezone: "US/Eastern")
481+
482+
# 2023-03-14 20:30:00 (Tuesday) in US/Eastern
483+
utc_now = ~U[2023-03-15 00:30:00Z]
484+
485+
query =
486+
QueryBuilder.build!(site,
487+
metrics: [:visitors],
488+
input_date_range: :day,
489+
include: [compare: :year_over_year, compare_match_day_of_week: true],
490+
now: utc_now
491+
)
492+
493+
comparison_query = Comparisons.get_comparison_query(query)
494+
495+
# Year ago: March 14, 2022 is Monday
496+
# Nearest Tuesday: March 15, 2022 (+1 day)
497+
# Comparison start in US/Eastern: 2022-03-15T00:00:00
498+
assert comparison_query.utc_time_range.first == ~U[2022-03-15 04:00:00Z]
499+
# Comparison end in US/Eastern: 2022-03-15T20:30:00
500+
assert comparison_query.utc_time_range.last == ~U[2022-03-16 00:30:00Z]
501+
end
502+
end
503+
445504
describe "with period set to 24h" do
446505
test "shifts back 24h period when mode is previous_period", %{site: site} do
447506
query =
@@ -477,6 +536,24 @@ defmodule Plausible.Stats.ComparisonsTest do
477536
assert comparison_query.utc_time_range.last == ~U[2022-03-15 18:30:00Z]
478537
end
479538

539+
test "handles YoY with match_day_of_week enabled", %{site: site} do
540+
query =
541+
QueryBuilder.build!(site,
542+
metrics: [:visitors],
543+
input_date_range: :"24h",
544+
include: [compare: :year_over_year, compare_match_day_of_week: true],
545+
now: ~U[2023-03-15 18:30:00Z]
546+
)
547+
548+
comparison_query = Comparisons.get_comparison_query(query)
549+
550+
# Main 24h range: 2023-03-14 18:30:00 (Tue) -> 2023-03-15 18:30:00 (Wed)
551+
# Year ago: 2022-03-14 18:30:00 (Mon) -> 2022-03-15 18:30:00 (Tue)
552+
# Nearest Tuesday to Monday: March 15, 2022 (+1 day shift)
553+
assert comparison_query.utc_time_range.first == ~U[2022-03-15 18:30:00Z]
554+
assert comparison_query.utc_time_range.last == ~U[2022-03-16 18:30:00Z]
555+
end
556+
480557
test "custom time zone works with 24h comparison" do
481558
site = insert(:site, timezone: "US/Eastern")
482559

0 commit comments

Comments
 (0)