From b8bd80321a5121b1256835359708c0b5004f1f94 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 11:06:44 +0200 Subject: [PATCH 01/20] Replace usages of `Timex.to_unix` with native API --- lib/plausible/auth/totp.ex | 4 +++- test/plausible/auth/totp_test.exs | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/plausible/auth/totp.ex b/lib/plausible/auth/totp.ex index e12f6ebc7194..d6b6c95dd823 100644 --- a/lib/plausible/auth/totp.ex +++ b/lib/plausible/auth/totp.ex @@ -336,7 +336,9 @@ defmodule Plausible.Auth.TOTP do |> Repo.one() if datetime do - Timex.to_unix(datetime) + datetime + |> DateTime.from_naive!("Etc/UTC") + |> DateTime.to_unix() end end diff --git a/test/plausible/auth/totp_test.exs b/test/plausible/auth/totp_test.exs index 4b2d7bf778fc..e73268f98522 100644 --- a/test/plausible/auth/totp_test.exs +++ b/test/plausible/auth/totp_test.exs @@ -330,7 +330,12 @@ defmodule Plausible.Auth.TOTPTest do assert {:ok, user} = TOTP.validate_code(user, new_code, allow_reuse?: true) - assert_in_delta Timex.to_unix(user.totp_last_used_at), System.os_time(:second), 2 + totp_last_used_at = + user.totp_last_used_at + |> DateTime.from_naive!("Etc/UTC") + |> DateTime.to_unix() + + assert_in_delta totp_last_used_at, System.os_time(:second), 2 end test "fails when trying to reuse the same code twice" do From f12d185f91dcd164205388ccf90f7404a952c21a Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 11:13:44 +0200 Subject: [PATCH 02/20] Wrap call to `Timex.is_valid_timezone?` --- lib/plausible/site.ex | 2 +- lib/plausible/timezones.ex | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/plausible/site.ex b/lib/plausible/site.ex index 8a235582a892..7aec34d6d182 100644 --- a/lib/plausible/site.ex +++ b/lib/plausible/site.ex @@ -222,7 +222,7 @@ defmodule Plausible.Site do defp validate_timezone(changeset) do tz = get_field(changeset, :timezone) - if Timex.is_valid_timezone?(tz) do + if Plausible.Timezones.valid?(tz) do changeset else add_error(changeset, :timezone, "is invalid") diff --git a/lib/plausible/timezones.ex b/lib/plausible/timezones.ex index 3ebbf7c55c24..030c41c95b42 100644 --- a/lib/plausible/timezones.ex +++ b/lib/plausible/timezones.ex @@ -1,4 +1,13 @@ defmodule Plausible.Timezones do + @moduledoc """ + API for working with timezones wrapping around external libraries where necessary. + """ + + @spec valid?(String.t()) :: boolean() + def valid?(tz) do + Timex.is_valid_timezone?(tz) + end + @spec options(DateTime.t()) :: [{:key, String.t()}, {:value, String.t()}, {:offset, integer()}] def options(now \\ DateTime.utc_now()) do Tzdata.zone_list() From 74e514f4bb588c9ed9aa9f46d905d3152dadb506 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 11:53:17 +0200 Subject: [PATCH 03/20] Wrap calls to `Timex.today(tz)` --- lib/plausible/times.ex | 12 ++++++++++++ lib/plausible_web/live/csv_import.ex | 2 +- test/plausible/site/sites_test.exs | 8 ++++---- 3 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 lib/plausible/times.ex diff --git a/lib/plausible/times.ex b/lib/plausible/times.ex new file mode 100644 index 000000000000..0414da05d54b --- /dev/null +++ b/lib/plausible/times.ex @@ -0,0 +1,12 @@ +defmodule Plausible.Times do + @moduledoc """ + API for working with time wrapping around external libraries where necessary. + """ + + @spec today(String.t()) :: Date.t() + def today(tz) do + tz + |> DateTime.now!() + |> DateTime.to_date() + end +end diff --git a/lib/plausible_web/live/csv_import.ex b/lib/plausible_web/live/csv_import.ex index 4b41b97abaa0..3242276de6fe 100644 --- a/lib/plausible_web/live/csv_import.ex +++ b/lib/plausible_web/live/csv_import.ex @@ -311,7 +311,7 @@ defmodule PlausibleWeb.Live.CSVImport do native_stats_start_date: native_stats_start_date } = socket.assigns - cutoff_date = native_stats_start_date || Timex.today(site.timezone) + cutoff_date = native_stats_start_date || Plausible.Times.today(site.timezone) case Imported.clamp_dates(occupied_ranges, cutoff_date, start_date, end_date) do {:ok, start_date, end_date} -> Date.range(start_date, end_date) diff --git a/test/plausible/site/sites_test.exs b/test/plausible/site/sites_test.exs index 662ff9257df2..75d5fb79f45a 100644 --- a/test/plausible/site/sites_test.exs +++ b/test/plausible/site/sites_test.exs @@ -159,7 +159,7 @@ defmodule Plausible.SitesTest do build(:pageview) ]) - assert Sites.stats_start_date(site) == Timex.today(site.timezone) + assert Sites.stats_start_date(site) == Plausible.Times.today(site.timezone) end test "memoizes value of start date" do @@ -171,8 +171,8 @@ defmodule Plausible.SitesTest do build(:pageview) ]) - assert Sites.stats_start_date(site) == Timex.today(site.timezone) - assert Repo.reload!(site).stats_start_date == Timex.today(site.timezone) + assert Sites.stats_start_date(site) == Plausible.Times.today(site.timezone) + assert Repo.reload!(site).stats_start_date == Plausible.Times.today(site.timezone) end end @@ -190,7 +190,7 @@ defmodule Plausible.SitesTest do build(:pageview) ]) - assert Sites.native_stats_start_date(site) == Timex.today(site.timezone) + assert Sites.native_stats_start_date(site) == Plausible.Times.today(site.timezone) end test "ignores imported stats" do From a0cff3f25a2b9854f27f532b8a9964b157777fd5 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 11:56:23 +0200 Subject: [PATCH 04/20] Replace `Timex.today()` with `Date.utc_today()` --- test/plausible/billing/billing_test.exs | 7 ++-- test/plausible/billing/site_locker_test.exs | 23 +++++++++---- test/plausible/purge_test.exs | 2 +- .../teams/invitations/accept_test.exs | 4 +-- .../breakdown_test.exs | 8 ++--- .../query_imported_test.exs | 6 ++-- .../api/stats_controller/conversions_test.exs | 4 +-- .../api/stats_controller/imported_test.exs | 2 +- .../api/stats_controller/main_graph_test.exs | 16 +++++----- .../controllers/settings_controller_test.exs | 8 +++-- .../controllers/site_controller_test.exs | 6 ++-- test/support/factory.ex | 26 +++++++-------- test/support/test_utils.ex | 2 +- test/workers/check_usage_test.exs | 28 ++++++++-------- test/workers/import_analytics_test.exs | 32 +++++++++---------- test/workers/notify_annual_renewal_test.exs | 2 +- 16 files changed, 96 insertions(+), 80 deletions(-) diff --git a/test/plausible/billing/billing_test.exs b/test/plausible/billing/billing_test.exs index fd8377d34380..91ab5addca6f 100644 --- a/test/plausible/billing/billing_test.exs +++ b/test/plausible/billing/billing_test.exs @@ -437,7 +437,10 @@ defmodule Plausible.BillingTest do end test "if teams's grace period has ended, upgrading will unlock sites and remove grace period" do - grace_period = %Plausible.Teams.GracePeriod{end_date: Timex.shift(Timex.today(), days: -1)} + grace_period = %Plausible.Teams.GracePeriod{ + end_date: Timex.shift(Date.utc_today(), days: -1) + } + user = new_user(team: [grace_period: grace_period]) subscribe_to_growth_plan(user) @@ -571,7 +574,7 @@ defmodule Plausible.BillingTest do Billing.subscription_payment_succeeded(%{ "alert_name" => "subscription_payment_succeeded", "subscription_id" => "nonexistent_subscription_id", - "next_bill_date" => Timex.shift(Timex.today(), days: 30), + "next_bill_date" => Timex.shift(Date.utc_today(), days: 30), "unit_price" => "12.00" }) diff --git a/test/plausible/billing/site_locker_test.exs b/test/plausible/billing/site_locker_test.exs index 9fcb4820a54d..d105492ae432 100644 --- a/test/plausible/billing/site_locker_test.exs +++ b/test/plausible/billing/site_locker_test.exs @@ -52,7 +52,9 @@ defmodule Plausible.Billing.SiteLockerTest do end test "does not lock team which has an active subscription and is on grace period" do - grace_period = %Plausible.Teams.GracePeriod{end_date: Timex.shift(Timex.today(), days: 1)} + grace_period = %Plausible.Teams.GracePeriod{ + end_date: Timex.shift(Date.utc_today(), days: 1) + } user = new_user(team: [grace_period: grace_period]) @@ -101,7 +103,10 @@ defmodule Plausible.Billing.SiteLockerTest do end test "locks all sites if team has an active subscription but grace period has ended (still over limits)" do - grace_period = %Plausible.Teams.GracePeriod{end_date: Timex.shift(Timex.today(), days: -1)} + grace_period = %Plausible.Teams.GracePeriod{ + end_date: Timex.shift(Date.utc_today(), days: -1) + } + user = new_user(team: [grace_period: grace_period]) subscribe_to_plan(user, @v4_growth_plan_id) site = new_site(owner: user) @@ -116,7 +121,10 @@ defmodule Plausible.Billing.SiteLockerTest do end test "does not lock sites (and removes grace period), when on active subscription and grace period ended, but usage now within limits" do - grace_period = %Plausible.Teams.GracePeriod{end_date: Timex.shift(Timex.today(), days: -1)} + grace_period = %Plausible.Teams.GracePeriod{ + end_date: Timex.shift(Date.utc_today(), days: -1) + } + user = new_user(team: [grace_period: grace_period]) subscribe_to_plan(user, @v4_growth_plan_id) site = new_site(owner: user) @@ -130,7 +138,10 @@ defmodule Plausible.Billing.SiteLockerTest do end test "sends email to all billing members if grace period has ended and still over limits" do - grace_period = %Plausible.Teams.GracePeriod{end_date: Timex.shift(Timex.today(), days: -1)} + grace_period = %Plausible.Teams.GracePeriod{ + end_date: Timex.shift(Date.utc_today(), days: -1) + } + user = new_user(team: [grace_period: grace_period]) subscribe_to_plan(user, @v4_growth_plan_id) new_site(owner: user) @@ -159,7 +170,7 @@ defmodule Plausible.Billing.SiteLockerTest do test "does not send grace period email if site is already locked" do grace_period = %Plausible.Teams.GracePeriod{ - end_date: Timex.shift(Timex.today(), days: -1), + end_date: Timex.shift(Date.utc_today(), days: -1), is_over: false } @@ -193,7 +204,7 @@ defmodule Plausible.Billing.SiteLockerTest do test "unlocks already ended grace periods when they still have an active subscription and went within limits again" do grace_period = %Plausible.Teams.GracePeriod{ - end_date: Timex.shift(Timex.today(), days: -7), + end_date: Timex.shift(Date.utc_today(), days: -7), is_over: true } diff --git a/test/plausible/purge_test.exs b/test/plausible/purge_test.exs index e34ceadfeb21..5641fe11b6a9 100644 --- a/test/plausible/purge_test.exs +++ b/test/plausible/purge_test.exs @@ -9,7 +9,7 @@ defmodule Plausible.PurgeTest do import_params = %{ source: :universal_analytics, start_date: ~D[2005-01-01], - end_date: Timex.today() + end_date: Date.utc_today() } [site_import1, site_import2] = diff --git a/test/plausible/teams/invitations/accept_test.exs b/test/plausible/teams/invitations/accept_test.exs index 9530e27b9501..7f467e720fd8 100644 --- a/test/plausible/teams/invitations/accept_test.exs +++ b/test/plausible/teams/invitations/accept_test.exs @@ -471,7 +471,7 @@ defmodule Plausible.Teams.Invitations.AcceptTest do new_user() |> subscribe_to_growth_plan( status: Plausible.Billing.Subscription.Status.deleted(), - next_bill_date: Timex.shift(Timex.today(), days: -1) + next_bill_date: Timex.shift(Date.utc_today(), days: -1) ) user_on_paused_subscription = @@ -572,7 +572,7 @@ defmodule Plausible.Teams.Invitations.AcceptTest do new_user() |> subscribe_to_growth_plan( status: Plausible.Billing.Subscription.Status.deleted(), - next_bill_date: Timex.shift(Timex.today(), days: -1) + next_bill_date: Timex.shift(Date.utc_today(), days: -1) ) user_on_paused_subscription = diff --git a/test/plausible_web/controllers/api/external_stats_controller/breakdown_test.exs b/test/plausible_web/controllers/api/external_stats_controller/breakdown_test.exs index 7251682ebc6f..87acbfda5199 100644 --- a/test/plausible_web/controllers/api/external_stats_controller/breakdown_test.exs +++ b/test/plausible_web/controllers/api/external_stats_controller/breakdown_test.exs @@ -361,7 +361,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do insert(:site_import, site: site, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), source: :universal_analytics ) @@ -554,7 +554,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do insert(:site_import, site: site, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), source: :universal_analytics ) @@ -879,7 +879,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do insert(:site_import, site: site, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), source: :universal_analytics ) @@ -2521,7 +2521,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do insert(:site_import, site: site, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), source: :universal_analytics ) diff --git a/test/plausible_web/controllers/api/external_stats_controller/query_imported_test.exs b/test/plausible_web/controllers/api/external_stats_controller/query_imported_test.exs index ca48404cb08d..98941b1350f9 100644 --- a/test/plausible_web/controllers/api/external_stats_controller/query_imported_test.exs +++ b/test/plausible_web/controllers/api/external_stats_controller/query_imported_test.exs @@ -344,7 +344,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.QueryImportedTest do insert(:site_import, site: site, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), source: :universal_analytics ) @@ -401,7 +401,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.QueryImportedTest do insert(:site_import, site: site, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), source: :universal_analytics ) @@ -460,7 +460,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.QueryImportedTest do insert(:site_import, site: site, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), source: :universal_analytics ) diff --git a/test/plausible_web/controllers/api/stats_controller/conversions_test.exs b/test/plausible_web/controllers/api/stats_controller/conversions_test.exs index 2ccd26ddc0d6..194cd32a5a4f 100644 --- a/test/plausible_web/controllers/api/stats_controller/conversions_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/conversions_test.exs @@ -1205,7 +1205,7 @@ defmodule PlausibleWeb.Api.StatsController.ConversionsTest do site_import = insert(:site_import, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), source: :universal_analytics ) @@ -1244,7 +1244,7 @@ defmodule PlausibleWeb.Api.StatsController.ConversionsTest do insert(:site_import, site: site, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), source: :universal_analytics ) diff --git a/test/plausible_web/controllers/api/stats_controller/imported_test.exs b/test/plausible_web/controllers/api/stats_controller/imported_test.exs index 6c029b424c74..aeac5a1c70b2 100644 --- a/test/plausible_web/controllers/api/stats_controller/imported_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/imported_test.exs @@ -19,7 +19,7 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do %{ source: :google_analytics_4, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), legacy: unquote(import_type) == :new_and_legacy } diff --git a/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs b/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs index e703430afb58..a3f007dd807f 100644 --- a/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs @@ -412,8 +412,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=30d&metric=visitors") assert %{"labels" => labels} = json_response(conn, 200) - {:ok, first} = Timex.today() |> Timex.shift(days: -30) |> Timex.format("{ISOdate}") - {:ok, last} = Timex.today() |> Timex.shift(days: -1) |> Timex.format("{ISOdate}") + {:ok, first} = Date.utc_today() |> Timex.shift(days: -30) |> Timex.format("{ISOdate}") + {:ok, last} = Date.utc_today() |> Timex.shift(days: -1) |> Timex.format("{ISOdate}") assert List.first(labels) == first assert List.last(labels) == last end @@ -422,8 +422,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=7d&metric=visitors") assert %{"labels" => labels} = json_response(conn, 200) - {:ok, first} = Timex.today() |> Timex.shift(days: -7) |> Timex.format("{ISOdate}") - {:ok, last} = Timex.today() |> Timex.shift(days: -1) |> Timex.format("{ISOdate}") + {:ok, first} = Date.utc_today() |> Timex.shift(days: -7) |> Timex.format("{ISOdate}") + {:ok, last} = Date.utc_today() |> Timex.shift(days: -1) |> Timex.format("{ISOdate}") assert List.first(labels) == first assert List.last(labels) == last end @@ -1444,14 +1444,14 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do assert %{"labels" => labels, "comparison_labels" => comparison_labels} = json_response(conn, 200) - {:ok, first} = Timex.today() |> Timex.shift(days: -30) |> Timex.format("{ISOdate}") - {:ok, last} = Timex.today() |> Timex.shift(days: -1) |> Timex.format("{ISOdate}") + {:ok, first} = Date.utc_today() |> Timex.shift(days: -30) |> Timex.format("{ISOdate}") + {:ok, last} = Date.utc_today() |> Timex.shift(days: -1) |> Timex.format("{ISOdate}") assert List.first(labels) == first assert List.last(labels) == last - {:ok, first} = Timex.today() |> Timex.shift(days: -60) |> Timex.format("{ISOdate}") - {:ok, last} = Timex.today() |> Timex.shift(days: -31) |> Timex.format("{ISOdate}") + {:ok, first} = Date.utc_today() |> Timex.shift(days: -60) |> Timex.format("{ISOdate}") + {:ok, last} = Date.utc_today() |> Timex.shift(days: -31) |> Timex.format("{ISOdate}") assert List.first(comparison_labels) == first assert List.last(comparison_labels) == last diff --git a/test/plausible_web/controllers/settings_controller_test.exs b/test/plausible_web/controllers/settings_controller_test.exs index b9f2f4630d83..c2de4e520863 100644 --- a/test/plausible_web/controllers/settings_controller_test.exs +++ b/test/plausible_web/controllers/settings_controller_test.exs @@ -276,7 +276,7 @@ defmodule PlausibleWeb.SettingsControllerTest do build(:event, name: "customevent", timestamp: Timex.shift(Timex.now(), days: -50)) ]) - last_bill_date = Timex.shift(Timex.today(), days: -10) + last_bill_date = Timex.shift(Date.utc_today(), days: -10) subscribe_to_plan(user, @v4_plan_id, last_bill_date: last_bill_date, status: :deleted) @@ -387,7 +387,7 @@ defmodule PlausibleWeb.SettingsControllerTest do build(:event, name: "customevent", timestamp: Timex.shift(Timex.now(), days: -20)) ]) - last_bill_date = Timex.shift(Timex.today(), days: -10) + last_bill_date = Timex.shift(Date.utc_today(), days: -10) subscribe_to_plan(user, @v4_plan_id, last_bill_date: last_bill_date) @@ -407,7 +407,9 @@ defmodule PlausibleWeb.SettingsControllerTest do conn: conn, user: user } do - subscribe_to_plan(user, @v4_plan_id, last_bill_date: Timex.shift(Timex.today(), days: -1)) + subscribe_to_plan(user, @v4_plan_id, + last_bill_date: Timex.shift(Date.utc_today(), days: -1) + ) html = conn diff --git a/test/plausible_web/controllers/site_controller_test.exs b/test/plausible_web/controllers/site_controller_test.exs index 894bd9866f13..ffc2ee503f26 100644 --- a/test/plausible_web/controllers/site_controller_test.exs +++ b/test/plausible_web/controllers/site_controller_test.exs @@ -1758,7 +1758,7 @@ defmodule PlausibleWeb.SiteControllerTest do site, user, start_date: ~D[2022-01-01], - end_date: Timex.today() + end_date: Date.utc_today() ) %{args: %{import_id: import_id}} = job @@ -1829,7 +1829,7 @@ defmodule PlausibleWeb.SiteControllerTest do site, user, start_date: ~D[2022-01-01], - end_date: Timex.today() + end_date: Date.utc_today() ) populate_stats(site, [ @@ -1850,7 +1850,7 @@ defmodule PlausibleWeb.SiteControllerTest do site, user, start_date: ~D[2022-01-01], - end_date: Timex.today() + end_date: Date.utc_today() ) populate_stats(site, [ diff --git a/test/support/factory.ex b/test/support/factory.ex index ff17bdb3d55b..16cc7468aa3d 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -6,7 +6,7 @@ defmodule Plausible.Factory do def team_factory do %Plausible.Teams.Team{ name: Plausible.Teams.default_name(), - trial_expiry_date: Timex.today() |> Timex.shift(days: 30), + trial_expiry_date: Date.utc_today() |> Timex.shift(days: 30), setup_complete: true, setup_at: NaiveDateTime.utc_now() } @@ -185,8 +185,8 @@ defmodule Plausible.Factory do update_url: "cancel.com", status: Subscription.Status.active(), next_bill_amount: "6.00", - next_bill_date: Timex.today(), - last_bill_date: Timex.today(), + next_bill_date: Date.utc_today(), + last_bill_date: Date.utc_today(), currency_code: "USD" } end @@ -252,7 +252,7 @@ defmodule Plausible.Factory do def imported_visitors_factory do %{ table: "imported_visitors", - date: Timex.today(), + date: Date.utc_today(), visitors: 1, pageviews: 1, bounces: 0, @@ -264,7 +264,7 @@ defmodule Plausible.Factory do def imported_sources_factory do %{ table: "imported_sources", - date: Timex.today(), + date: Date.utc_today(), source: "", visitors: 1, visits: 1, @@ -276,7 +276,7 @@ defmodule Plausible.Factory do def imported_pages_factory do %{ table: "imported_pages", - date: Timex.today(), + date: Date.utc_today(), page: "", visitors: 1, pageviews: 1, @@ -289,7 +289,7 @@ defmodule Plausible.Factory do def imported_entry_pages_factory do %{ table: "imported_entry_pages", - date: Timex.today(), + date: Date.utc_today(), entry_page: "", visitors: 1, entrances: 1, @@ -301,7 +301,7 @@ defmodule Plausible.Factory do def imported_exit_pages_factory do %{ table: "imported_exit_pages", - date: Timex.today(), + date: Date.utc_today(), exit_page: "", visitors: 1, exits: 1 @@ -311,7 +311,7 @@ defmodule Plausible.Factory do def imported_custom_events_factory do %{ table: "imported_custom_events", - date: Timex.today(), + date: Date.utc_today(), name: "", link_url: "", visitors: 1, @@ -322,7 +322,7 @@ defmodule Plausible.Factory do def imported_locations_factory do %{ table: "imported_locations", - date: Timex.today(), + date: Date.utc_today(), country: "", region: "", city: 0, @@ -336,7 +336,7 @@ defmodule Plausible.Factory do def imported_devices_factory do %{ table: "imported_devices", - date: Timex.today(), + date: Date.utc_today(), device: "", visitors: 1, visits: 1, @@ -348,7 +348,7 @@ defmodule Plausible.Factory do def imported_browsers_factory do %{ table: "imported_browsers", - date: Timex.today(), + date: Date.utc_today(), browser: "", visitors: 1, visits: 1, @@ -360,7 +360,7 @@ defmodule Plausible.Factory do def imported_operating_systems_factory do %{ table: "imported_operating_systems", - date: Timex.today(), + date: Date.utc_today(), operating_system: "", visitors: 1, visits: 1, diff --git a/test/support/test_utils.ex b/test/support/test_utils.ex index cfaa46b63368..d67614b14062 100644 --- a/test/support/test_utils.ex +++ b/test/support/test_utils.ex @@ -75,7 +75,7 @@ defmodule Plausible.TestUtils do Factory.insert(:site_import, site: site, start_date: ~D[2005-01-01], - end_date: Timex.today(), + end_date: Date.utc_today(), source: :universal_analytics, legacy: opts[:create_legacy_import?] == true ) diff --git a/test/workers/check_usage_test.exs b/test/workers/check_usage_test.exs index df45b43d21aa..cf9f82a4bb82 100644 --- a/test/workers/check_usage_test.exs +++ b/test/workers/check_usage_test.exs @@ -10,7 +10,7 @@ defmodule Plausible.Workers.CheckUsageTest do setup [:create_user, :create_site] @paddle_id_10k "558018" - @date_range Date.range(Timex.today(), Timex.today()) + @date_range Date.range(Date.utc_today(), Date.utc_today()) @accepted_status_values [ Plausible.Billing.Subscription.Status.active(), @@ -144,7 +144,7 @@ defmodule Plausible.Workers.CheckUsageTest do subscribe_to_plan( user, @paddle_id_10k, - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: Plausible.Billing.Subscription.Status.paused() ) @@ -157,7 +157,7 @@ defmodule Plausible.Workers.CheckUsageTest do describe "#{status} subscription, regular customers" do test "ignores user with subscription but no usage", %{user: user} do subscribe_to_plan(user, @paddle_id_10k, - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status) ) @@ -180,7 +180,7 @@ defmodule Plausible.Workers.CheckUsageTest do end) subscribe_to_plan(user, @paddle_id_10k, - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status) ) @@ -203,7 +203,7 @@ defmodule Plausible.Workers.CheckUsageTest do end) subscribe_to_plan(user, @paddle_id_10k, - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status) ) @@ -241,7 +241,7 @@ defmodule Plausible.Workers.CheckUsageTest do ) assert Repo.reload(team_of(user)).grace_period.end_date == - Timex.shift(Timex.today(), days: 7) + Timex.shift(Date.utc_today(), days: 7) end test "sends an email suggesting enterprise plan when usage is greater than 10M ", %{ @@ -259,7 +259,7 @@ defmodule Plausible.Workers.CheckUsageTest do subscribe_to_plan( user, @paddle_id_10k, - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status) ) @@ -284,7 +284,7 @@ defmodule Plausible.Workers.CheckUsageTest do end) subscribe_to_plan(user, @paddle_id_10k, - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status) ) @@ -309,7 +309,7 @@ defmodule Plausible.Workers.CheckUsageTest do subscribe_to_plan( user, @paddle_id_10k, - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status) ) @@ -375,7 +375,7 @@ defmodule Plausible.Workers.CheckUsageTest do subscribe_to_enterprise_plan(user, monthly_pageview_limit: 1_000_000, subscription: [ - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status) ] ) @@ -403,7 +403,7 @@ defmodule Plausible.Workers.CheckUsageTest do user, monthly_pageview_limit: 1_000_000, subscription: [ - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status) ] ) @@ -433,7 +433,7 @@ defmodule Plausible.Workers.CheckUsageTest do user, monthly_pageview_limit: 1_000_000, subscription: [ - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status), # non-matching ID paddle_plan_id: @paddle_id_10k @@ -464,7 +464,7 @@ defmodule Plausible.Workers.CheckUsageTest do subscribe_to_enterprise_plan(user, site_limit: 2, subscription: [ - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status) ] ) @@ -495,7 +495,7 @@ defmodule Plausible.Workers.CheckUsageTest do user, monthly_pageview_limit: 1_000_000, subscription: [ - last_bill_date: Timex.shift(Timex.today(), days: -1), + last_bill_date: Timex.shift(Date.utc_today(), days: -1), status: unquote(status) ] ) diff --git a/test/workers/import_analytics_test.exs b/test/workers/import_analytics_test.exs index f50e618f348e..6d3c1a2c75de 100644 --- a/test/workers/import_analytics_test.exs +++ b/test/workers/import_analytics_test.exs @@ -14,8 +14,8 @@ defmodule Plausible.Workers.ImportAnalyticsTest do setup do %{ import_opts: [ - start_date: Timex.today() |> Timex.shift(days: -7), - end_date: Timex.today() + start_date: Date.utc_today() |> Timex.shift(days: -7), + end_date: Date.utc_today() ] } end @@ -23,7 +23,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do test "updates site import after successful import", %{ import_opts: import_opts } do - user = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: user) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) @@ -48,7 +48,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do test "clears stats_start_date field for the site after successful import", %{ import_opts: import_opts } do - user = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: user, stats_start_date: ~D[2005-01-01]) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) @@ -65,7 +65,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do end test "sends email to owner after successful import", %{import_opts: import_opts} do - user = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: user) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) @@ -84,7 +84,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do test "send email after successful import only to the user who ran the import", %{ import_opts: import_opts } do - owner = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + owner = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: owner) importing_user = new_user() @@ -110,7 +110,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do end test "updates site import record after failed import", %{import_opts: import_opts} do - user = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: user) import_opts = Keyword.put(import_opts, :error, true) @@ -125,7 +125,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do end test "clears any orphaned data during import", %{import_opts: import_opts} do - user = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: user) import_opts = Keyword.put(import_opts, :error, true) @@ -150,7 +150,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do end test "sends email to owner after failed import", %{import_opts: import_opts} do - user = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: user) import_opts = Keyword.put(import_opts, :error, true) @@ -170,7 +170,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do test "sends email after failed import only to the user who ran the import", %{ import_opts: import_opts } do - owner = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + owner = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: owner) import_opts = Keyword.put(import_opts, :error, true) @@ -211,8 +211,8 @@ defmodule Plausible.Workers.ImportAnalyticsTest do %{ import_opts: [ - start_date: Timex.today() |> Timex.shift(days: -7), - end_date: Timex.today() + start_date: Date.utc_today() |> Timex.shift(days: -7), + end_date: Date.utc_today() ] } end @@ -221,7 +221,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> - user = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: user) site_id = site.id import_opts = Keyword.put(import_opts, :listen?, true) @@ -242,7 +242,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> - user = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: user) site_id = site.id @@ -267,7 +267,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> - user = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: user) site_id = site.id @@ -304,7 +304,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> - user = new_user(trial_expiry_date: Timex.today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) site = new_site(owner: user) site_id = site.id diff --git a/test/workers/notify_annual_renewal_test.exs b/test/workers/notify_annual_renewal_test.exs index 04dc557d1ac6..1d627b516ef8 100644 --- a/test/workers/notify_annual_renewal_test.exs +++ b/test/workers/notify_annual_renewal_test.exs @@ -137,7 +137,7 @@ defmodule Plausible.Workers.NotifyAnnualRenewalTest do Repo.insert_all("sent_renewal_notifications", [ %{ user_id: user.id, - timestamp: Timex.shift(Timex.today(), years: -1) |> Timex.to_naive_datetime() + timestamp: Timex.shift(Date.utc_today(), years: -1) |> Timex.to_naive_datetime() } ]) From 5e3254d257f3f7925047fe7c0c94975d3279eab5 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 12:13:32 +0200 Subject: [PATCH 05/20] Replace `Timex.now()` with `DateTime.utc_now()` --- test/plausible/session/cache_store_test.exs | 12 +++++----- .../controllers/settings_controller_test.exs | 22 +++++++++---------- test/plausible_web/live/choose_plan_test.exs | 2 +- test/support/factory.ex | 6 ++--- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/test/plausible/session/cache_store_test.exs b/test/plausible/session/cache_store_test.exs index 7ca4aa74538e..6b76d53e647b 100644 --- a/test/plausible/session/cache_store_test.exs +++ b/test/plausible/session/cache_store_test.exs @@ -247,7 +247,7 @@ defmodule Plausible.Session.CacheStoreTest do end test "updates session counters", %{buffer: buffer} do - timestamp = Timex.now() + timestamp = DateTime.utc_now() event1 = build(:event, name: "pageview", timestamp: timestamp |> Timex.shift(seconds: -10)) event2 = %{ @@ -352,7 +352,7 @@ defmodule Plausible.Session.CacheStoreTest do site_id: site_id, pathname: "/path/1", hostname: "whatever.example.com", - timestamp: Timex.shift(Timex.now(), seconds: -5), + timestamp: Timex.shift(DateTime.utc_now(), seconds: -5), user_id: 1 ), build(:event, @@ -379,7 +379,7 @@ defmodule Plausible.Session.CacheStoreTest do site_id: site_id, pathname: "/landing", hostname: "example.com", - timestamp: Timex.shift(Timex.now(), seconds: -5), + timestamp: Timex.shift(DateTime.utc_now(), seconds: -5), user_id: 1 ), build(:event, @@ -406,7 +406,7 @@ defmodule Plausible.Session.CacheStoreTest do site_id: site_id, pathname: "/landing", hostname: "example.com", - timestamp: Timex.shift(Timex.now(), seconds: -5), + timestamp: Timex.shift(DateTime.utc_now(), seconds: -5), user_id: 1 ), build(:event, @@ -414,7 +414,7 @@ defmodule Plausible.Session.CacheStoreTest do site_id: site_id, pathname: "/path/1", hostname: "analytics.example.com", - timestamp: Timex.shift(Timex.now(), seconds: -3), + timestamp: Timex.shift(DateTime.utc_now(), seconds: -3), user_id: 1 ), build(:event, @@ -497,7 +497,7 @@ defmodule Plausible.Session.CacheStoreTest do end test "calculates duration correctly for out-of-order events", %{buffer: buffer} do - timestamp = Timex.now() + timestamp = DateTime.utc_now() event1 = build(:event, name: "pageview", timestamp: timestamp |> Timex.shift(seconds: 10)) event2 = %{event1 | timestamp: timestamp} diff --git a/test/plausible_web/controllers/settings_controller_test.exs b/test/plausible_web/controllers/settings_controller_test.exs index c2de4e520863..83352d4d8722 100644 --- a/test/plausible_web/controllers/settings_controller_test.exs +++ b/test/plausible_web/controllers/settings_controller_test.exs @@ -270,10 +270,10 @@ defmodule PlausibleWeb.SettingsControllerTest do site = new_site(owner: user) populate_stats(site, [ - build(:event, name: "pageview", timestamp: Timex.shift(Timex.now(), days: -5)), - build(:event, name: "customevent", timestamp: Timex.shift(Timex.now(), days: -20)), - build(:event, name: "pageview", timestamp: Timex.shift(Timex.now(), days: -50)), - build(:event, name: "customevent", timestamp: Timex.shift(Timex.now(), days: -50)) + build(:event, name: "pageview", timestamp: Timex.shift(DateTime.utc_now(), days: -5)), + build(:event, name: "customevent", timestamp: Timex.shift(DateTime.utc_now(), days: -20)), + build(:event, name: "pageview", timestamp: Timex.shift(DateTime.utc_now(), days: -50)), + build(:event, name: "customevent", timestamp: Timex.shift(DateTime.utc_now(), days: -50)) ]) last_bill_date = Timex.shift(Date.utc_today(), days: -10) @@ -340,7 +340,7 @@ defmodule PlausibleWeb.SettingsControllerTest do subscribe_to_plan(user, @v4_plan_id, status: :active, - last_bill_date: Timex.shift(Timex.now(), months: -6) + last_bill_date: Timex.shift(DateTime.utc_now(), months: -6) ) subscription = @@ -368,7 +368,7 @@ defmodule PlausibleWeb.SettingsControllerTest do subscription |> Plausible.Billing.Subscription.changeset(%{ status: :deleted, - next_bill_date: Timex.shift(Timex.now(), months: 6) + next_bill_date: Timex.shift(DateTime.utc_now(), months: 6) }) |> Repo.update!() @@ -383,8 +383,8 @@ defmodule PlausibleWeb.SettingsControllerTest do site = new_site(owner: user) populate_stats(site, [ - build(:event, name: "pageview", timestamp: Timex.shift(Timex.now(), days: -5)), - build(:event, name: "customevent", timestamp: Timex.shift(Timex.now(), days: -20)) + build(:event, name: "pageview", timestamp: Timex.shift(DateTime.utc_now(), days: -5)), + build(:event, name: "customevent", timestamp: Timex.shift(DateTime.utc_now(), days: -20)) ]) last_bill_date = Timex.shift(Date.utc_today(), days: -10) @@ -429,9 +429,9 @@ defmodule PlausibleWeb.SettingsControllerTest do site = new_site(owner: user) populate_stats(site, [ - build(:event, name: "pageview", timestamp: Timex.shift(Timex.now(), days: -1)), - build(:event, name: "customevent", timestamp: Timex.shift(Timex.now(), days: -10)), - build(:event, name: "customevent", timestamp: Timex.shift(Timex.now(), days: -20)) + build(:event, name: "pageview", timestamp: Timex.shift(DateTime.utc_now(), days: -1)), + build(:event, name: "customevent", timestamp: Timex.shift(DateTime.utc_now(), days: -10)), + build(:event, name: "customevent", timestamp: Timex.shift(DateTime.utc_now(), days: -20)) ]) assert_usage = fn doc -> diff --git a/test/plausible_web/live/choose_plan_test.exs b/test/plausible_web/live/choose_plan_test.exs index b95366d0e251..4d76f3f7ccd0 100644 --- a/test/plausible_web/live/choose_plan_test.exs +++ b/test/plausible_web/live/choose_plan_test.exs @@ -1052,7 +1052,7 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do |> team_of() |> Repo.preload(:subscription) |> Map.fetch!(:subscription) - |> Subscription.changeset(%{next_bill_date: Timex.shift(Timex.now(), months: -2)}) + |> Subscription.changeset(%{next_bill_date: Timex.shift(DateTime.utc_now(), months: -2)}) |> Repo.update!() :ok diff --git a/test/support/factory.ex b/test/support/factory.ex index 16cc7468aa3d..63e7558c7ed7 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -114,8 +114,8 @@ defmodule Plausible.Factory do entry_page: "/", pageviews: 1, events: 1, - start: Timex.now(), - timestamp: Timex.now(), + start: DateTime.utc_now(), + timestamp: DateTime.utc_now(), is_bounce: false } end @@ -219,7 +219,7 @@ defmodule Plausible.Factory do email: sequence(:google_auth_email, &"email-#{&1}@example.com"), refresh_token: "123", access_token: "123", - expires: Timex.now() |> Timex.shift(days: 1) + expires: DateTime.utc_now() |> Timex.shift(days: 1) } end From b215c348a1ec1a2db389c1f6c8f79cdae7bcaa36 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 12:22:01 +0200 Subject: [PATCH 06/20] Replace `Timex.compare` with `Date.compare` --- lib/plausible/billing/subscriptions.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plausible/billing/subscriptions.ex b/lib/plausible/billing/subscriptions.ex index 129319654540..3c835c0446e6 100644 --- a/lib/plausible/billing/subscriptions.ex +++ b/lib/plausible/billing/subscriptions.ex @@ -27,7 +27,7 @@ defmodule Plausible.Billing.Subscriptions do def expired?(%Subscription{next_bill_date: next_bill_date} = subscription) do deleted? = Subscription.Status.deleted?(subscription) - expired? = Timex.compare(next_bill_date, Date.utc_today()) < 0 + expired? = Date.compare(next_bill_date, Date.utc_today()) == :lt deleted? && expired? end From 1c8c9f07cf8352e19bdce8463ffd992aebf7fd63 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 12:33:08 +0200 Subject: [PATCH 07/20] Wrap `Timex.diff` calls --- lib/plausible/stats/interval.ex | 6 +++--- lib/plausible/stats/query_optimizer.ex | 2 +- lib/plausible/stats/time.ex | 8 ++++---- lib/plausible/teams/billing.ex | 2 +- lib/plausible/times.ex | 11 +++++++++++ 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/plausible/stats/interval.ex b/lib/plausible/stats/interval.ex index 34018037fcb9..64d607f4826e 100644 --- a/lib/plausible/stats/interval.ex +++ b/lib/plausible/stats/interval.ex @@ -42,7 +42,7 @@ defmodule Plausible.Stats.Interval do """ def default_for_date_range(%DateTimeRange{first: first, last: last}) do cond do - Timex.diff(last, first, :months) > 0 -> + Plausible.Times.diff(last, first, :month) > 0 -> "month" DateTime.diff(last, first, :day) > 0 -> @@ -75,7 +75,7 @@ defmodule Plausible.Stats.Interval do table = with %Date{} = from <- Keyword.get(opts, :from), %Date{} = to <- Keyword.get(opts, :to), - true <- abs(Timex.diff(from, to, :months)) > 12 do + true <- abs(Plausible.Times.diff(from, to, :month)) > 12 do Map.replace(@valid_by_period, "custom", ["week", "month"]) else _ -> @@ -83,7 +83,7 @@ defmodule Plausible.Stats.Interval do end with %Date{} = stats_start <- Plausible.Sites.stats_start_date(site), - true <- abs(Timex.diff(Date.utc_today(), stats_start, :months)) > 12 do + true <- abs(Plausible.Times.diff(Date.utc_today(), stats_start, :month)) > 12 do Map.replace(table, "all", ["week", "month"]) else _ -> diff --git a/lib/plausible/stats/query_optimizer.ex b/lib/plausible/stats/query_optimizer.ex index 30e43bcef9d3..23ebae1baa4a 100644 --- a/lib/plausible/stats/query_optimizer.ex +++ b/lib/plausible/stats/query_optimizer.ex @@ -95,7 +95,7 @@ defmodule Plausible.Stats.QueryOptimizer do cond do DateTime.diff(last, first, :hour) <= 48 -> "time:hour" DateTime.diff(last, first, :day) <= 40 -> "time:day" - Timex.diff(last, first, :weeks) <= 52 -> "time:week" + Plausible.Times.diff(last, first, :week) <= 52 -> "time:week" true -> "time:month" end end diff --git a/lib/plausible/stats/time.ex b/lib/plausible/stats/time.ex index cf5d7ea3c3e4..91758784f818 100644 --- a/lib/plausible/stats/time.ex +++ b/lib/plausible/stats/time.ex @@ -53,10 +53,10 @@ defmodule Plausible.Stats.Time do date_range = Query.date_range(query) n_buckets = - Timex.diff( + Plausible.Times.diff( date_range.last, Date.beginning_of_month(date_range.first), - :months + :month ) Enum.map(n_buckets..0//-1, fn shift -> @@ -71,10 +71,10 @@ defmodule Plausible.Stats.Time do date_range = Query.date_range(query) n_buckets = - Timex.diff( + Plausible.Times.diff( date_range.last, Date.beginning_of_week(date_range.first), - :weeks + :week ) Enum.map(0..n_buckets, fn shift -> diff --git a/lib/plausible/teams/billing.ex b/lib/plausible/teams/billing.ex index 7bd75713cdb3..02af0ff63726 100644 --- a/lib/plausible/teams/billing.ex +++ b/lib/plausible/teams/billing.ex @@ -435,7 +435,7 @@ defmodule Plausible.Teams.Billing do last_bill_date = team.subscription.last_bill_date normalized_last_bill_date = - Date.shift(last_bill_date, month: Timex.diff(today, last_bill_date, :months)) + Date.shift(last_bill_date, month: Plausible.Times.diff(today, last_bill_date, :month)) date_range = case cycle do diff --git a/lib/plausible/times.ex b/lib/plausible/times.ex index 0414da05d54b..b3d508120875 100644 --- a/lib/plausible/times.ex +++ b/lib/plausible/times.ex @@ -9,4 +9,15 @@ defmodule Plausible.Times do |> DateTime.now!() |> DateTime.to_date() end + + @spec diff(Date.t() | DateTime.t(), Date.t() | DateTime.t(), :month | :week) :: integer() + def diff(a, b, unit) do + unit = + case unit do + :week -> :weeks + :month -> :months + end + + Timex.diff(a, b, unit) + end end From aa09a693ed05e922f303eaeefbc5d7773b161bd6 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 12:39:07 +0200 Subject: [PATCH 08/20] Replace `Timex.Timezone.convert` with `DateTime.shift_zone!` --- test/workers/send_email_report_test.exs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/workers/send_email_report_test.exs b/test/workers/send_email_report_test.exs index 37fa29b36208..ceb69f440448 100644 --- a/test/workers/send_email_report_test.exs +++ b/test/workers/send_email_report_test.exs @@ -5,7 +5,6 @@ defmodule Plausible.Workers.SendEmailReportTest do use Oban.Testing, repo: Plausible.Repo import Plausible.Test.Support.HTML alias Plausible.Workers.SendEmailReport - alias Timex.Timezone @green "#15803d" @red "#b91c1c" @@ -54,17 +53,17 @@ defmodule Plausible.Workers.SendEmailReportTest do populate_stats(site, [ # Sunday before last, not counted - build(:pageview, timestamp: Timezone.convert(sunday_before_last, "UTC")), + build(:pageview, timestamp: DateTime.shift_zone!(sunday_before_last, "UTC")), # Sunday before last, not counted - build(:pageview, timestamp: Timezone.convert(sunday_before_last, "UTC")), + build(:pageview, timestamp: DateTime.shift_zone!(sunday_before_last, "UTC")), # Last monday, counted - build(:pageview, timestamp: Timezone.convert(last_monday, "UTC")), + build(:pageview, timestamp: DateTime.shift_zone!(last_monday, "UTC")), # Last sunday, counted - build(:pageview, timestamp: Timezone.convert(last_sunday, "UTC")), + build(:pageview, timestamp: DateTime.shift_zone!(last_sunday, "UTC")), # This monday, not counted - build(:pageview, timestamp: Timezone.convert(this_monday, "UTC")), + build(:pageview, timestamp: DateTime.shift_zone!(this_monday, "UTC")), # This monday, not counted - build(:pageview, timestamp: Timezone.convert(this_monday, "UTC")) + build(:pageview, timestamp: DateTime.shift_zone!(this_monday, "UTC")) ]) perform_job(SendEmailReport, %{"site_id" => site.id, "interval" => "weekly"}) From 5459fbfc0a7082947c038eb45b2a6fde77d97edc Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 12:57:36 +0200 Subject: [PATCH 09/20] Wrap `Timex.parse!` --- lib/plausible/exports.ex | 2 +- lib/plausible/google/ga4/http.ex | 2 +- lib/plausible/imported/google_analytics4.ex | 2 +- lib/plausible/times.ex | 11 +++++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/plausible/exports.ex b/lib/plausible/exports.ex index a701b1a02460..ba6bdcfbcd8e 100644 --- a/lib/plausible/exports.ex +++ b/lib/plausible/exports.ex @@ -154,7 +154,7 @@ defmodule Plausible.Exports do ["expiry-date=", expiry_date, ", rule-id=", _rule_id] = String.split(x_amz_expiration, "\"", trim: true) - Timex.parse!(expiry_date, "{RFC1123}") + Plausible.Times.parse!(expiry_date, "{RFC1123}") end %{ diff --git a/lib/plausible/google/ga4/http.ex b/lib/plausible/google/ga4/http.ex index aedbf5f92922..5f3a1fccc8a9 100644 --- a/lib/plausible/google/ga4/http.ex +++ b/lib/plausible/google/ga4/http.ex @@ -269,7 +269,7 @@ defmodule Plausible.Google.GA4.HTTP do date = case report["rows"] do [%{"dimensionValues" => [%{"value" => date_str}]}] -> - Timex.parse!(date_str, "%Y%m%d", :strftime) |> NaiveDateTime.to_date() + Plausible.Times.parse!(date_str, "%Y%m%d", :strftime) |> NaiveDateTime.to_date() _ -> nil diff --git a/lib/plausible/imported/google_analytics4.ex b/lib/plausible/imported/google_analytics4.ex index ab9838479296..31a5166620b2 100644 --- a/lib/plausible/imported/google_analytics4.ex +++ b/lib/plausible/imported/google_analytics4.ex @@ -337,7 +337,7 @@ defmodule Plausible.Imported.GoogleAnalytics4 do defp get_date(%{dimensions: %{"date" => date}}) do date - |> Timex.parse!("%Y%m%d", :strftime) + |> Plausible.Times.parse!("%Y%m%d", :strftime) |> NaiveDateTime.to_date() end diff --git a/lib/plausible/times.ex b/lib/plausible/times.ex index b3d508120875..4e6412434264 100644 --- a/lib/plausible/times.ex +++ b/lib/plausible/times.ex @@ -20,4 +20,15 @@ defmodule Plausible.Times do Timex.diff(a, b, unit) end + + @spec parse!(String.t(), String.t(), :default | :strftime) :: DateTime.t() | NaiveDateTime.t() + def parse!(str, format, tokenizer \\ :default) + + def parse!(str, "{RFC1123}" = format, :default) do + Timex.parse!(str, format) + end + + def parse!(str, format, :strftime) do + Timex.parse!(str, format, :strftime) + end end From 5020a4bf93cb176fa49dba85ddba7f832f759029 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 13:04:28 +0200 Subject: [PATCH 10/20] Replace `Timex.to_date` with native API calls --- lib/plausible/stats/legacy/legacy_query_builder.ex | 4 ++-- lib/plausible_web/controllers/api/stats_controller.ex | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/plausible/stats/legacy/legacy_query_builder.ex b/lib/plausible/stats/legacy/legacy_query_builder.ex index b0140b99e197..29c87d89831b 100644 --- a/lib/plausible/stats/legacy/legacy_query_builder.ex +++ b/lib/plausible/stats/legacy/legacy_query_builder.ex @@ -304,12 +304,12 @@ defmodule Plausible.Stats.Legacy.QueryBuilder do end defp today(query) do - query.now |> Timex.to_date() + query.now |> DateTime.to_date() end defp parse_single_date(query, params) do case params["date"] do - "today" -> query.now |> Timex.to_date() + "today" -> query.now |> DateTime.to_date() date when is_binary(date) -> Date.from_iso8601!(date) _ -> today(query) end diff --git a/lib/plausible_web/controllers/api/stats_controller.ex b/lib/plausible_web/controllers/api/stats_controller.ex index 7b88f1398593..ab21ac724e92 100644 --- a/lib/plausible_web/controllers/api/stats_controller.ex +++ b/lib/plausible_web/controllers/api/stats_controller.ex @@ -243,7 +243,7 @@ defmodule PlausibleWeb.Api.StatsController do "day" -> current_date = DateTime.now!(site.timezone) - |> Timex.to_date() + |> DateTime.to_date() |> Date.to_string() Enum.find_index(dates, &(&1 == current_date)) @@ -253,7 +253,7 @@ defmodule PlausibleWeb.Api.StatsController do current_date = DateTime.now!(site.timezone) - |> Timex.to_date() + |> DateTime.to_date() |> Time.date_or_weekstart(date_range) |> Date.to_string() @@ -262,7 +262,7 @@ defmodule PlausibleWeb.Api.StatsController do "month" -> current_date = DateTime.now!(site.timezone) - |> Timex.to_date() + |> DateTime.to_date() |> Timex.beginning_of_month() |> Date.to_string() From 488a61ccd888b94ed35ab82d817ff25e083aa77f Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 13:40:20 +0200 Subject: [PATCH 11/20] Replace `Timex.beginning|end_of...` with native API calls for Date --- lib/plausible_web/controllers/api/stats_controller.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/plausible_web/controllers/api/stats_controller.ex b/lib/plausible_web/controllers/api/stats_controller.ex index ab21ac724e92..8f33d6570eab 100644 --- a/lib/plausible_web/controllers/api/stats_controller.ex +++ b/lib/plausible_web/controllers/api/stats_controller.ex @@ -148,7 +148,7 @@ defmodule PlausibleWeb.Api.StatsController do labels ) do date_range = Query.date_range(query) - build_intervals(labels, date_range, &Timex.beginning_of_week/1, &Timex.end_of_week/1) + build_intervals(labels, date_range, &Date.beginning_of_week/1, &Date.end_of_week/1) end defp build_full_intervals( @@ -156,7 +156,7 @@ defmodule PlausibleWeb.Api.StatsController do labels ) do date_range = Query.date_range(query) - build_intervals(labels, date_range, &Timex.beginning_of_month/1, &Timex.end_of_month/1) + build_intervals(labels, date_range, &Date.beginning_of_month/1, &Date.end_of_month/1) end defp build_full_intervals(_query, _labels) do @@ -263,7 +263,7 @@ defmodule PlausibleWeb.Api.StatsController do current_date = DateTime.now!(site.timezone) |> DateTime.to_date() - |> Timex.beginning_of_month() + |> Date.beginning_of_month() |> Date.to_string() Enum.find_index(dates, &(&1 == current_date)) From 89993bc66a2ae2703f66cecf0511159d820a7f23 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 13:42:51 +0200 Subject: [PATCH 12/20] Wrap `Timex.beginning|end_of...` for DateTimes and Dates for years --- lib/plausible/stats/filters/query_parser.ex | 4 +-- .../stats/legacy/legacy_query_builder.ex | 4 +-- lib/plausible/times.ex | 30 +++++++++++++++++++ lib/workers/schedule_email_reports.ex | 4 +-- test/workers/send_email_report_test.exs | 8 ++--- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/lib/plausible/stats/filters/query_parser.ex b/lib/plausible/stats/filters/query_parser.ex index a7411880a149..b1f05365f45b 100644 --- a/lib/plausible/stats/filters/query_parser.ex +++ b/lib/plausible/stats/filters/query_parser.ex @@ -231,8 +231,8 @@ defmodule Plausible.Stats.Filters.QueryParser do end defp parse_time_range(site, "year", date, _now) do - last = date |> Timex.end_of_year() - first = last |> Timex.beginning_of_year() + last = date |> Plausible.Times.end_of_year() + first = last |> Plausible.Times.beginning_of_year() {:ok, DateTimeRange.new!(first, last, site.timezone)} end diff --git a/lib/plausible/stats/legacy/legacy_query_builder.ex b/lib/plausible/stats/legacy/legacy_query_builder.ex index 29c87d89831b..fdb643462f32 100644 --- a/lib/plausible/stats/legacy/legacy_query_builder.ex +++ b/lib/plausible/stats/legacy/legacy_query_builder.ex @@ -157,9 +157,9 @@ defmodule Plausible.Stats.Legacy.QueryBuilder do defp put_input_date_range(query, site, %{"period" => "year"} = params) do end_date = parse_single_date(query, params) - |> Timex.end_of_year() + |> Plausible.Times.end_of_year() - start_date = Timex.beginning_of_year(end_date) + start_date = Plausible.Times.beginning_of_year(end_date) datetime_range = DateTimeRange.new!(start_date, end_date, site.timezone) diff --git a/lib/plausible/times.ex b/lib/plausible/times.ex index 4e6412434264..8aedaf195b54 100644 --- a/lib/plausible/times.ex +++ b/lib/plausible/times.ex @@ -31,4 +31,34 @@ defmodule Plausible.Times do def parse!(str, format, :strftime) do Timex.parse!(str, format, :strftime) end + + @spec beginning_of_week(DateTime.t()) :: DateTime.t() + def beginning_of_week(dt) do + Timex.beginning_of_week(dt) + end + + @spec beginning_of_month(DateTime.t()) :: DateTime.t() + def beginning_of_month(dt) do + Timex.beginning_of_month(dt) + end + + @spec beginning_of_year(Date.t()) :: Date.t() + def beginning_of_year(d) do + Timex.beginning_of_year(d) + end + + @spec end_of_week(DateTime.t()) :: DateTime.t() + def end_of_week(dt) do + Timex.end_of_week(dt) + end + + @spec end_of_month(DateTime.t()) :: DateTime.t() + def end_of_month(dt) do + Timex.end_of_month(dt) + end + + @spec end_of_year(Date.t()) :: Date.t() + def end_of_year(t) do + Timex.end_of_year(t) + end end diff --git a/lib/workers/schedule_email_reports.ex b/lib/workers/schedule_email_reports.ex index 1551618741e2..91f2090f9a80 100644 --- a/lib/workers/schedule_email_reports.ex +++ b/lib/workers/schedule_email_reports.ex @@ -52,7 +52,7 @@ defmodule Plausible.Workers.ScheduleEmailReports do def monday_9am(timezone) do DateTime.now!(timezone) |> DateTime.shift(week: 1) - |> Timex.beginning_of_week() + |> Plausible.Times.beginning_of_week() |> DateTime.shift(hour: 9) end @@ -93,7 +93,7 @@ defmodule Plausible.Workers.ScheduleEmailReports do def first_of_month_9am(timezone) do DateTime.now!(timezone) |> DateTime.shift(month: 1) - |> Timex.beginning_of_month() + |> Plausible.Times.beginning_of_month() |> DateTime.shift(hour: 9) end end diff --git a/test/workers/send_email_report_test.exs b/test/workers/send_email_report_test.exs index ceb69f440448..f4af550f520f 100644 --- a/test/workers/send_email_report_test.exs +++ b/test/workers/send_email_report_test.exs @@ -46,10 +46,10 @@ defmodule Plausible.Workers.SendEmailReportTest do insert(:weekly_report, site: site, recipients: ["user@email.com"]) now = Timex.now(site.timezone) - last_monday = Timex.shift(now, weeks: -1) |> Timex.beginning_of_week() - last_sunday = Timex.shift(now, weeks: -1) |> Timex.end_of_week() + last_monday = Timex.shift(now, weeks: -1) |> Plausible.Times.beginning_of_week() + last_sunday = Timex.shift(now, weeks: -1) |> Plausible.Times.end_of_week() sunday_before_last = Timex.shift(last_monday, minutes: -1) - this_monday = Timex.beginning_of_week(now) + this_monday = Plausible.Times.beginning_of_week(now) populate_stats(site, [ # Sunday before last, not counted @@ -219,7 +219,7 @@ defmodule Plausible.Workers.SendEmailReportTest do last_month = Timex.now(site.timezone) |> Timex.shift(months: -1) - |> Timex.beginning_of_month() + |> Plausible.Times.beginning_of_month() |> Timex.format!("{Mfull}") perform_job(SendEmailReport, %{"site_id" => site.id, "interval" => "monthly"}) From e5a9827953fbd0543fd39d72d1f8e392109609af Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 13:44:16 +0200 Subject: [PATCH 13/20] Replace `Timex.format(!)` with native API calls --- .../api/stats_controller/main_graph_test.exs | 16 ++++++++-------- test/workers/send_email_report_test.exs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs b/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs index a3f007dd807f..1f1540f8748e 100644 --- a/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs @@ -412,8 +412,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=30d&metric=visitors") assert %{"labels" => labels} = json_response(conn, 200) - {:ok, first} = Date.utc_today() |> Timex.shift(days: -30) |> Timex.format("{ISOdate}") - {:ok, last} = Date.utc_today() |> Timex.shift(days: -1) |> Timex.format("{ISOdate}") + first = Date.utc_today() |> Timex.shift(days: -30) |> Date.to_iso8601() + last = Date.utc_today() |> Timex.shift(days: -1) |> Date.to_iso8601() assert List.first(labels) == first assert List.last(labels) == last end @@ -422,8 +422,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=7d&metric=visitors") assert %{"labels" => labels} = json_response(conn, 200) - {:ok, first} = Date.utc_today() |> Timex.shift(days: -7) |> Timex.format("{ISOdate}") - {:ok, last} = Date.utc_today() |> Timex.shift(days: -1) |> Timex.format("{ISOdate}") + first = Date.utc_today() |> Timex.shift(days: -7) |> Date.to_iso8601() + last = Date.utc_today() |> Timex.shift(days: -1) |> Date.to_iso8601() assert List.first(labels) == first assert List.last(labels) == last end @@ -1444,14 +1444,14 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do assert %{"labels" => labels, "comparison_labels" => comparison_labels} = json_response(conn, 200) - {:ok, first} = Date.utc_today() |> Timex.shift(days: -30) |> Timex.format("{ISOdate}") - {:ok, last} = Date.utc_today() |> Timex.shift(days: -1) |> Timex.format("{ISOdate}") + first = Date.utc_today() |> Timex.shift(days: -30) |> Date.to_iso8601() + last = Date.utc_today() |> Timex.shift(days: -1) |> Date.to_iso8601() assert List.first(labels) == first assert List.last(labels) == last - {:ok, first} = Date.utc_today() |> Timex.shift(days: -60) |> Timex.format("{ISOdate}") - {:ok, last} = Date.utc_today() |> Timex.shift(days: -31) |> Timex.format("{ISOdate}") + first = Date.utc_today() |> Timex.shift(days: -60) |> Date.to_iso8601() + last = Date.utc_today() |> Timex.shift(days: -31) |> Date.to_iso8601() assert List.first(comparison_labels) == first assert List.last(comparison_labels) == last diff --git a/test/workers/send_email_report_test.exs b/test/workers/send_email_report_test.exs index f4af550f520f..34aed2983306 100644 --- a/test/workers/send_email_report_test.exs +++ b/test/workers/send_email_report_test.exs @@ -220,7 +220,7 @@ defmodule Plausible.Workers.SendEmailReportTest do Timex.now(site.timezone) |> Timex.shift(months: -1) |> Plausible.Times.beginning_of_month() - |> Timex.format!("{Mfull}") + |> Calendar.strftime("%B") perform_job(SendEmailReport, %{"site_id" => site.id, "interval" => "monthly"}) From 98c7fd73b874066ffc03464873b155c93a1330bd Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 13:49:20 +0200 Subject: [PATCH 14/20] Replace `Timex.to_naive_datetime` with native API calls --- test/workers/notify_annual_renewal_test.exs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/workers/notify_annual_renewal_test.exs b/test/workers/notify_annual_renewal_test.exs index 1d627b516ef8..19cef4b5a8d5 100644 --- a/test/workers/notify_annual_renewal_test.exs +++ b/test/workers/notify_annual_renewal_test.exs @@ -134,10 +134,12 @@ defmodule Plausible.Workers.NotifyAnnualRenewalTest do test "does not send multiple notifications on second year", %{user: user} do subscribe_to_plan(user, @yearly_plan, next_bill_date: Date.shift(Date.utc_today(), day: 7)) + year_ago = Date.shift(Date.utc_today(), year: -1) + Repo.insert_all("sent_renewal_notifications", [ %{ user_id: user.id, - timestamp: Timex.shift(Date.utc_today(), years: -1) |> Timex.to_naive_datetime() + timestamp: NaiveDateTime.new!(year_ago, ~T[00:00:00]) } ]) From 34254dda305e55e9770e2c5548d1f3c6b511b304 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 14:18:04 +0200 Subject: [PATCH 15/20] Wrap time humanizing routines using Timex --- .../data_migration/populate_event_session_columns.ex | 4 +--- lib/plausible/times.ex | 12 ++++++++++++ lib/plausible_web/email.ex | 5 +---- lib/plausible_web/live/csv_export.ex | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/plausible/data_migration/populate_event_session_columns.ex b/lib/plausible/data_migration/populate_event_session_columns.ex index 3f911e96a01c..fdf04fa1f496 100644 --- a/lib/plausible/data_migration/populate_event_session_columns.ex +++ b/lib/plausible/data_migration/populate_event_session_columns.ex @@ -150,8 +150,6 @@ defmodule Plausible.DataMigration.PopulateEventSessionColumns do end defp format_duration(seconds) do - seconds - |> Timex.Duration.from_seconds() - |> Timex.format_duration(Timex.Format.Duration.Formatters.Humanized) + Plausible.Times.humanize_seconds(seconds) end end diff --git a/lib/plausible/times.ex b/lib/plausible/times.ex index 8aedaf195b54..345b380ad55b 100644 --- a/lib/plausible/times.ex +++ b/lib/plausible/times.ex @@ -61,4 +61,16 @@ defmodule Plausible.Times do def end_of_year(t) do Timex.end_of_year(t) end + + @spec humanize(DateTime.t()) :: String.t() + def humanize(%DateTime{} = dt) do + Timex.Format.DateTime.Formatters.Relative.format!(dt, "{relative}") + end + + @spec humanize_seconds(pos_integer()) :: String.t() + def humanize_seconds(seconds) do + seconds + |> Timex.Duration.from_seconds() + |> Timex.format_duration(Timex.Format.Duration.Formatters.Humanized) + end end diff --git a/lib/plausible_web/email.ex b/lib/plausible_web/email.ex index 8e49f56ffa6c..d30920bc838e 100644 --- a/lib/plausible_web/email.ex +++ b/lib/plausible_web/email.ex @@ -487,10 +487,7 @@ defmodule PlausibleWeb.Email do def export_success(user, site, expires_at) do expires_in = if expires_at do - Timex.Format.DateTime.Formatters.Relative.format!( - expires_at, - "{relative}" - ) + Plausible.Times.humanize(expires_at) end download_url = diff --git a/lib/plausible_web/live/csv_export.ex b/lib/plausible_web/live/csv_export.ex index bc8f8300598a..a83d19f8b32a 100644 --- a/lib/plausible_web/live/csv_export.ex +++ b/lib/plausible_web/live/csv_export.ex @@ -188,7 +188,7 @@ defmodule PlausibleWeb.Live.CSVExport do

Note: this file will expire <.hint message={@export.expires_at}> - {Timex.Format.DateTime.Formatters.Relative.format!(@export.expires_at, "{relative}")}. + {Plausible.Times.humanize(@export.expires_at)}.

From 36828e3a6b73fea96b76caa5db42babfe5f5b29d Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 14:39:39 +0200 Subject: [PATCH 16/20] Remove unnecessary `use Timex` instances --- lib/plausible/google/api.ex | 2 -- .../controllers/api/stats_controller/imported_test.exs | 1 - 2 files changed, 3 deletions(-) diff --git a/lib/plausible/google/api.ex b/lib/plausible/google/api.ex index b91505490a08..c1ab562d3df7 100644 --- a/lib/plausible/google/api.ex +++ b/lib/plausible/google/api.ex @@ -3,8 +3,6 @@ defmodule Plausible.Google.API do API to Google services. """ - use Timex - alias Plausible.Google.HTTP alias Plausible.Google.SearchConsole alias Plausible.Stats.Query diff --git a/test/plausible_web/controllers/api/stats_controller/imported_test.exs b/test/plausible_web/controllers/api/stats_controller/imported_test.exs index aeac5a1c70b2..81c3535fd56c 100644 --- a/test/plausible_web/controllers/api/stats_controller/imported_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/imported_test.exs @@ -1,6 +1,5 @@ defmodule PlausibleWeb.Api.StatsController.ImportedTest do use PlausibleWeb.ConnCase - use Timex @user_id Enum.random(1000..9999) From 044c16baadbcb187248c5ba80fd153304619dda8 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 16:33:40 +0200 Subject: [PATCH 17/20] Replace `Timex.shift` with native API calls --- test/plausible/billing/billing_test.exs | 4 +- test/plausible/billing/quota_test.exs | 42 +++++++------- test/plausible/billing/site_locker_test.exs | 12 ++-- test/plausible/plugins/api/token_test.exs | 18 +++--- test/plausible/plugins/api/tokens_test.exs | 4 +- test/plausible/session/cache_store_test.exs | 16 ++++-- test/plausible/stats/interval_test.exs | 6 +- .../teams/invitations/accept_test.exs | 12 ++-- .../aggregate_test.exs | 2 +- .../api/stats_controller/main_graph_test.exs | 34 ++++++------ .../api/stats_controller/regions_test.exs | 2 +- .../api/stats_controller/sources_test.exs | 6 +- .../api/stats_controller/top_stats_test.exs | 38 ++++++------- .../controllers/auth_controller_test.exs | 4 +- .../controllers/billing_controller_test.exs | 4 +- .../controllers/settings_controller_test.exs | 55 ++++++++++++------- .../controllers/stats_controller_test.exs | 29 ++++++---- test/plausible_web/live/choose_plan_test.exs | 8 ++- test/support/factory.ex | 4 +- test/support/test_utils.ex | 2 +- test/workers/check_usage_test.exs | 26 ++++----- test/workers/import_analytics_test.exs | 28 +++++----- test/workers/send_check_stats_emails_test.exs | 5 +- test/workers/send_email_report_test.exs | 42 +++++++------- test/workers/send_site_setup_emails_test.exs | 2 +- 25 files changed, 214 insertions(+), 191 deletions(-) diff --git a/test/plausible/billing/billing_test.exs b/test/plausible/billing/billing_test.exs index 91ab5addca6f..d946e299000a 100644 --- a/test/plausible/billing/billing_test.exs +++ b/test/plausible/billing/billing_test.exs @@ -438,7 +438,7 @@ defmodule Plausible.BillingTest do test "if teams's grace period has ended, upgrading will unlock sites and remove grace period" do grace_period = %Plausible.Teams.GracePeriod{ - end_date: Timex.shift(Date.utc_today(), days: -1) + end_date: Date.shift(Date.utc_today(), day: -1) } user = new_user(team: [grace_period: grace_period]) @@ -574,7 +574,7 @@ defmodule Plausible.BillingTest do Billing.subscription_payment_succeeded(%{ "alert_name" => "subscription_payment_succeeded", "subscription_id" => "nonexistent_subscription_id", - "next_bill_date" => Timex.shift(Date.utc_today(), days: 30), + "next_bill_date" => Date.shift(Date.utc_today(), day: 30), "unit_price" => "12.00" }) diff --git a/test/plausible/billing/quota_test.exs b/test/plausible/billing/quota_test.exs index f566181580de..f8b6d7b89f10 100644 --- a/test/plausible/billing/quota_test.exs +++ b/test/plausible/billing/quota_test.exs @@ -710,12 +710,12 @@ defmodule Plausible.Billing.QuotaTest do now = NaiveDateTime.utc_now() populate_stats(site, [ - build(:event, timestamp: Timex.shift(now, days: -40), name: "custom"), - build(:event, timestamp: Timex.shift(now, days: -10), name: "custom"), - build(:event, timestamp: Timex.shift(now, days: -9), name: "pageview"), - build(:event, timestamp: Timex.shift(now, days: -8), name: "pageview"), - build(:event, timestamp: Timex.shift(now, days: -7), name: "pageview"), - build(:event, timestamp: Timex.shift(now, days: -6), name: "custom") + build(:event, timestamp: NaiveDateTime.shift(now, day: -40), name: "custom"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -10), name: "custom"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -9), name: "pageview"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -8), name: "pageview"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -7), name: "pageview"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -6), name: "custom") ]) assert %{ @@ -735,9 +735,9 @@ defmodule Plausible.Billing.QuotaTest do now = NaiveDateTime.utc_now() populate_stats(site, [ - build(:event, timestamp: Timex.shift(now, days: -8), name: "custom"), - build(:pageview, user_id: 199, timestamp: Timex.shift(now, days: -5, minutes: -2)), - build(:engagement, user_id: 199, timestamp: Timex.shift(now, days: -5)) + build(:event, timestamp: NaiveDateTime.shift(now, day: -8), name: "custom"), + build(:pageview, user_id: 199, timestamp: NaiveDateTime.shift(now, day: -5, minute: -2)), + build(:engagement, user_id: 199, timestamp: NaiveDateTime.shift(now, day: -5)) ]) assert %{ @@ -761,12 +761,12 @@ defmodule Plausible.Billing.QuotaTest do now = NaiveDateTime.utc_now() populate_stats(site, [ - build(:event, timestamp: Timex.shift(now, days: -40), name: "custom"), - build(:event, timestamp: Timex.shift(now, days: -10), name: "custom"), - build(:event, timestamp: Timex.shift(now, days: -9), name: "pageview"), - build(:event, timestamp: Timex.shift(now, days: -8), name: "pageview"), - build(:event, timestamp: Timex.shift(now, days: -7), name: "pageview"), - build(:event, timestamp: Timex.shift(now, days: -6), name: "custom") + build(:event, timestamp: NaiveDateTime.shift(now, day: -40), name: "custom"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -10), name: "custom"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -9), name: "pageview"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -8), name: "pageview"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -7), name: "pageview"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -6), name: "custom") ]) assert %{ @@ -806,12 +806,12 @@ defmodule Plausible.Billing.QuotaTest do for site <- [site1, site2, site3] do populate_stats(site, [ - build(:event, timestamp: Timex.shift(now, days: -40), name: "custom"), - build(:event, timestamp: Timex.shift(now, days: -10), name: "custom"), - build(:event, timestamp: Timex.shift(now, days: -9), name: "pageview"), - build(:event, timestamp: Timex.shift(now, days: -8), name: "pageview"), - build(:event, timestamp: Timex.shift(now, days: -7), name: "pageview"), - build(:event, timestamp: Timex.shift(now, days: -6), name: "custom") + build(:event, timestamp: NaiveDateTime.shift(now, day: -40), name: "custom"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -10), name: "custom"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -9), name: "pageview"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -8), name: "pageview"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -7), name: "pageview"), + build(:event, timestamp: NaiveDateTime.shift(now, day: -6), name: "custom") ]) end diff --git a/test/plausible/billing/site_locker_test.exs b/test/plausible/billing/site_locker_test.exs index d105492ae432..0ccd14bf9be9 100644 --- a/test/plausible/billing/site_locker_test.exs +++ b/test/plausible/billing/site_locker_test.exs @@ -53,7 +53,7 @@ defmodule Plausible.Billing.SiteLockerTest do test "does not lock team which has an active subscription and is on grace period" do grace_period = %Plausible.Teams.GracePeriod{ - end_date: Timex.shift(Date.utc_today(), days: 1) + end_date: Date.shift(Date.utc_today(), day: 1) } user = @@ -104,7 +104,7 @@ defmodule Plausible.Billing.SiteLockerTest do test "locks all sites if team has an active subscription but grace period has ended (still over limits)" do grace_period = %Plausible.Teams.GracePeriod{ - end_date: Timex.shift(Date.utc_today(), days: -1) + end_date: Date.shift(Date.utc_today(), day: -1) } user = new_user(team: [grace_period: grace_period]) @@ -122,7 +122,7 @@ defmodule Plausible.Billing.SiteLockerTest do test "does not lock sites (and removes grace period), when on active subscription and grace period ended, but usage now within limits" do grace_period = %Plausible.Teams.GracePeriod{ - end_date: Timex.shift(Date.utc_today(), days: -1) + end_date: Date.shift(Date.utc_today(), day: -1) } user = new_user(team: [grace_period: grace_period]) @@ -139,7 +139,7 @@ defmodule Plausible.Billing.SiteLockerTest do test "sends email to all billing members if grace period has ended and still over limits" do grace_period = %Plausible.Teams.GracePeriod{ - end_date: Timex.shift(Date.utc_today(), days: -1) + end_date: Date.shift(Date.utc_today(), day: -1) } user = new_user(team: [grace_period: grace_period]) @@ -170,7 +170,7 @@ defmodule Plausible.Billing.SiteLockerTest do test "does not send grace period email if site is already locked" do grace_period = %Plausible.Teams.GracePeriod{ - end_date: Timex.shift(Date.utc_today(), days: -1), + end_date: Date.shift(Date.utc_today(), day: -1), is_over: false } @@ -204,7 +204,7 @@ defmodule Plausible.Billing.SiteLockerTest do test "unlocks already ended grace periods when they still have an active subscription and went within limits again" do grace_period = %Plausible.Teams.GracePeriod{ - end_date: Timex.shift(Date.utc_today(), days: -7), + end_date: Date.shift(Date.utc_today(), day: -7), is_over: true } diff --git a/test/plausible/plugins/api/token_test.exs b/test/plausible/plugins/api/token_test.exs index 8b91efbba9a1..c109d7ada377 100644 --- a/test/plausible/plugins/api/token_test.exs +++ b/test/plausible/plugins/api/token_test.exs @@ -79,18 +79,18 @@ defmodule Plausible.Plugins.API.TokenTest do now = NaiveDateTime.utc_now() last_seen = fn shift -> - Token.last_used_humanize(%Token{last_used_at: Timex.shift(now, shift)}) + Token.last_used_humanize(%Token{last_used_at: NaiveDateTime.shift(now, shift)}) end assert Token.last_used_humanize(%Token{}) == "Not yet" - assert last_seen.(minutes: -1) == "Just recently" - assert last_seen.(minutes: -4) == "Just recently" - assert last_seen.(minutes: -6) == "Several minutes ago" - assert last_seen.(hours: -1) == "An hour ago" - assert last_seen.(hours: -7) == "Hours ago" - assert last_seen.(days: -1) == "Yesterday" - assert last_seen.(days: -3) == "Sometime this week" - assert last_seen.(months: -1) == "Long time ago" + assert last_seen.(minute: -1) == "Just recently" + assert last_seen.(minute: -4) == "Just recently" + assert last_seen.(minute: -6) == "Several minutes ago" + assert last_seen.(hour: -1) == "An hour ago" + assert last_seen.(hour: -7) == "Hours ago" + assert last_seen.(day: -1) == "Yesterday" + assert last_seen.(day: -3) == "Sometime this week" + assert last_seen.(month: -1) == "Long time ago" end end diff --git a/test/plausible/plugins/api/tokens_test.exs b/test/plausible/plugins/api/tokens_test.exs index 223e92ddb3bf..74e68ce55f2e 100644 --- a/test/plausible/plugins/api/tokens_test.exs +++ b/test/plausible/plugins/api/tokens_test.exs @@ -81,11 +81,11 @@ defmodule Plausible.Plugins.API.TokensTest do now = NaiveDateTime.utc_now() {:ok, token1} = Tokens.update_last_seen(token0, now) - {:ok, token2} = Tokens.update_last_seen(token1, Timex.shift(now, minutes: 2)) + {:ok, token2} = Tokens.update_last_seen(token1, NaiveDateTime.shift(now, minute: 2)) assert token1.last_used_at == token2.last_used_at - {:ok, token3} = Tokens.update_last_seen(token2, Timex.shift(now, minutes: 6)) + {:ok, token3} = Tokens.update_last_seen(token2, NaiveDateTime.shift(now, minute: 6)) assert NaiveDateTime.compare(token3.last_used_at, token2.last_used_at) == :gt end diff --git a/test/plausible/session/cache_store_test.exs b/test/plausible/session/cache_store_test.exs index 6b76d53e647b..8efb8c90cb28 100644 --- a/test/plausible/session/cache_store_test.exs +++ b/test/plausible/session/cache_store_test.exs @@ -248,7 +248,9 @@ defmodule Plausible.Session.CacheStoreTest do test "updates session counters", %{buffer: buffer} do timestamp = DateTime.utc_now() - event1 = build(:event, name: "pageview", timestamp: timestamp |> Timex.shift(seconds: -10)) + + event1 = + build(:event, name: "pageview", timestamp: timestamp |> NaiveDateTime.shift(second: -10)) event2 = %{ event1 @@ -352,7 +354,7 @@ defmodule Plausible.Session.CacheStoreTest do site_id: site_id, pathname: "/path/1", hostname: "whatever.example.com", - timestamp: Timex.shift(DateTime.utc_now(), seconds: -5), + timestamp: DateTime.shift(DateTime.utc_now(), second: -5), user_id: 1 ), build(:event, @@ -379,7 +381,7 @@ defmodule Plausible.Session.CacheStoreTest do site_id: site_id, pathname: "/landing", hostname: "example.com", - timestamp: Timex.shift(DateTime.utc_now(), seconds: -5), + timestamp: DateTime.shift(DateTime.utc_now(), second: -5), user_id: 1 ), build(:event, @@ -406,7 +408,7 @@ defmodule Plausible.Session.CacheStoreTest do site_id: site_id, pathname: "/landing", hostname: "example.com", - timestamp: Timex.shift(DateTime.utc_now(), seconds: -5), + timestamp: DateTime.shift(DateTime.utc_now(), second: -5), user_id: 1 ), build(:event, @@ -414,7 +416,7 @@ defmodule Plausible.Session.CacheStoreTest do site_id: site_id, pathname: "/path/1", hostname: "analytics.example.com", - timestamp: Timex.shift(DateTime.utc_now(), seconds: -3), + timestamp: DateTime.shift(DateTime.utc_now(), second: -3), user_id: 1 ), build(:event, @@ -498,7 +500,9 @@ defmodule Plausible.Session.CacheStoreTest do test "calculates duration correctly for out-of-order events", %{buffer: buffer} do timestamp = DateTime.utc_now() - event1 = build(:event, name: "pageview", timestamp: timestamp |> Timex.shift(seconds: 10)) + + event1 = + build(:event, name: "pageview", timestamp: timestamp |> NaiveDateTime.shift(second: 10)) event2 = %{event1 | timestamp: timestamp} diff --git a/test/plausible/stats/interval_test.exs b/test/plausible/stats/interval_test.exs index 204914a531bd..6ce90ccdd64f 100644 --- a/test/plausible/stats/interval_test.exs +++ b/test/plausible/stats/interval_test.exs @@ -42,7 +42,7 @@ defmodule Plausible.Stats.IntervalTest do end test "for a site with stats starting over 12m ago" do - site = build(:site, stats_start_date: Timex.shift(Date.utc_today(), months: -13)) + site = build(:site, stats_start_date: Date.shift(Date.utc_today(), month: -13)) assert valid_by_period(site: site) == %{ "realtime" => ["minute"], @@ -61,7 +61,7 @@ defmodule Plausible.Stats.IntervalTest do end test "for a query range exceeding 12m" do - ago_13m = Timex.shift(Date.utc_today(), months: -13) + ago_13m = Date.shift(Date.utc_today(), month: -13) site = build(:site, stats_start_date: ago_13m) assert valid_by_period(site: site, from: ago_13m, to: Date.utc_today()) == %{ @@ -124,7 +124,7 @@ defmodule Plausible.Stats.IntervalTest do end test "for a site with stats starting over 12m ago" do - site = build(:site, stats_start_date: Timex.shift(Date.utc_today(), months: -13)) + site = build(:site, stats_start_date: Date.shift(Date.utc_today(), month: -13)) refute valid_for_period?("all", "day", site: site) assert valid_for_period?("custom", "day", diff --git a/test/plausible/teams/invitations/accept_test.exs b/test/plausible/teams/invitations/accept_test.exs index 7f467e720fd8..871c6760caa8 100644 --- a/test/plausible/teams/invitations/accept_test.exs +++ b/test/plausible/teams/invitations/accept_test.exs @@ -396,8 +396,8 @@ defmodule Plausible.Teams.Invitations.AcceptTest do new_owner_site = new_site(owner: new_owner) old_owner_site = new_site(owner: old_owner) - somewhere_last_month = NaiveDateTime.utc_now() |> Timex.shift(days: -5) - somewhere_penultimate_month = NaiveDateTime.utc_now() |> Timex.shift(days: -35) + somewhere_last_month = NaiveDateTime.utc_now() |> NaiveDateTime.shift(day: -5) + somewhere_penultimate_month = NaiveDateTime.utc_now() |> NaiveDateTime.shift(day: -35) generate_usage_for(new_owner_site, 5_000, somewhere_last_month) generate_usage_for(new_owner_site, 1_000, somewhere_penultimate_month) @@ -421,8 +421,8 @@ defmodule Plausible.Teams.Invitations.AcceptTest do new_owner_site = new_site(owner: new_owner) old_owner_site = new_site(owner: old_owner) - somewhere_last_month = NaiveDateTime.utc_now() |> Timex.shift(days: -5) - somewhere_penultimate_month = NaiveDateTime.utc_now() |> Timex.shift(days: -35) + somewhere_last_month = NaiveDateTime.utc_now() |> NaiveDateTime.shift(day: -5) + somewhere_penultimate_month = NaiveDateTime.utc_now() |> NaiveDateTime.shift(day: -35) generate_usage_for(new_owner_site, 5_000, somewhere_last_month) generate_usage_for(new_owner_site, 1_000, somewhere_penultimate_month) @@ -471,7 +471,7 @@ defmodule Plausible.Teams.Invitations.AcceptTest do new_user() |> subscribe_to_growth_plan( status: Plausible.Billing.Subscription.Status.deleted(), - next_bill_date: Timex.shift(Date.utc_today(), days: -1) + next_bill_date: Date.shift(Date.utc_today(), day: -1) ) user_on_paused_subscription = @@ -572,7 +572,7 @@ defmodule Plausible.Teams.Invitations.AcceptTest do new_user() |> subscribe_to_growth_plan( status: Plausible.Billing.Subscription.Status.deleted(), - next_bill_date: Timex.shift(Date.utc_today(), days: -1) + next_bill_date: Date.shift(Date.utc_today(), day: -1) ) user_on_paused_subscription = diff --git a/test/plausible_web/controllers/api/external_stats_controller/aggregate_test.exs b/test/plausible_web/controllers/api/external_stats_controller/aggregate_test.exs index b2ad7103c2d8..6a3c3b19a389 100644 --- a/test/plausible_web/controllers/api/external_stats_controller/aggregate_test.exs +++ b/test/plausible_web/controllers/api/external_stats_controller/aggregate_test.exs @@ -418,7 +418,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.AggregateTest do test "can compare conversion_rate with previous period", %{conn: conn, site: site} do today = ~N[2023-05-05 12:00:00] - yesterday = Timex.shift(today, days: -1) + yesterday = NaiveDateTime.shift(today, day: -1) populate_stats(site, [ build(:event, name: "Signup", timestamp: yesterday), diff --git a/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs b/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs index 1f1540f8748e..6ffc46e8f964 100644 --- a/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/main_graph_test.exs @@ -9,7 +9,7 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do test "displays pageviews for the last 30 minutes in realtime graph", %{conn: conn, site: site} do populate_stats(site, [ - build(:pageview, timestamp: relative_time(minutes: -5)) + build(:pageview, timestamp: relative_time(minute: -5)) ]) conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=realtime&metric=pageviews") @@ -29,7 +29,7 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do |> Plausible.Repo.update() populate_stats(site, [ - build(:pageview, timestamp: relative_time(minutes: -5)) + build(:pageview, timestamp: relative_time(minute: -5)) ]) conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=realtime&metric=pageviews") @@ -412,8 +412,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=30d&metric=visitors") assert %{"labels" => labels} = json_response(conn, 200) - first = Date.utc_today() |> Timex.shift(days: -30) |> Date.to_iso8601() - last = Date.utc_today() |> Timex.shift(days: -1) |> Date.to_iso8601() + first = Date.utc_today() |> Date.shift(day: -30) |> Date.to_iso8601() + last = Date.utc_today() |> Date.shift(day: -1) |> Date.to_iso8601() assert List.first(labels) == first assert List.last(labels) == last end @@ -422,8 +422,8 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do conn = get(conn, "/api/stats/#{site.domain}/main-graph?period=7d&metric=visitors") assert %{"labels" => labels} = json_response(conn, 200) - first = Date.utc_today() |> Timex.shift(days: -7) |> Date.to_iso8601() - last = Date.utc_today() |> Timex.shift(days: -1) |> Date.to_iso8601() + first = Date.utc_today() |> Date.shift(day: -7) |> Date.to_iso8601() + last = Date.utc_today() |> Date.shift(day: -1) |> Date.to_iso8601() assert List.first(labels) == first assert List.last(labels) == last end @@ -526,12 +526,12 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do site: site } do populate_stats(site, [ - build(:pageview, timestamp: relative_time(minutes: -35), user_id: 1), - build(:pageview, timestamp: relative_time(minutes: -20), user_id: 1), - build(:pageview, timestamp: relative_time(minutes: -25), user_id: 2), - build(:pageview, timestamp: relative_time(minutes: -15), user_id: 2), - build(:pageview, timestamp: relative_time(minutes: -5), user_id: 3), - build(:pageview, timestamp: relative_time(minutes: -3), user_id: 3) + build(:pageview, timestamp: relative_time(minute: -35), user_id: 1), + build(:pageview, timestamp: relative_time(minute: -20), user_id: 1), + build(:pageview, timestamp: relative_time(minute: -25), user_id: 2), + build(:pageview, timestamp: relative_time(minute: -15), user_id: 2), + build(:pageview, timestamp: relative_time(minute: -5), user_id: 3), + build(:pageview, timestamp: relative_time(minute: -3), user_id: 3) ]) conn = @@ -1444,14 +1444,14 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do assert %{"labels" => labels, "comparison_labels" => comparison_labels} = json_response(conn, 200) - first = Date.utc_today() |> Timex.shift(days: -30) |> Date.to_iso8601() - last = Date.utc_today() |> Timex.shift(days: -1) |> Date.to_iso8601() + first = Date.utc_today() |> Date.shift(day: -30) |> Date.to_iso8601() + last = Date.utc_today() |> Date.shift(day: -1) |> Date.to_iso8601() assert List.first(labels) == first assert List.last(labels) == last - first = Date.utc_today() |> Timex.shift(days: -60) |> Date.to_iso8601() - last = Date.utc_today() |> Timex.shift(days: -31) |> Date.to_iso8601() + first = Date.utc_today() |> Date.shift(day: -60) |> Date.to_iso8601() + last = Date.utc_today() |> Date.shift(day: -31) |> Date.to_iso8601() assert List.first(comparison_labels) == first assert List.last(comparison_labels) == last @@ -1540,7 +1540,7 @@ defmodule PlausibleWeb.Api.StatsController.MainGraphTest do site = new_site(owner: user, timezone: "America/Santiago") populate_stats(site, [ - build(:pageview, timestamp: relative_time(minutes: -5)) + build(:pageview, timestamp: relative_time(minute: -5)) ]) conn = diff --git a/test/plausible_web/controllers/api/stats_controller/regions_test.exs b/test/plausible_web/controllers/api/stats_controller/regions_test.exs index 68da14937689..a78fea91f1dc 100644 --- a/test/plausible_web/controllers/api/stats_controller/regions_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/regions_test.exs @@ -103,7 +103,7 @@ defmodule PlausibleWeb.Api.StatsController.RegionsTest do site = new_site(owner: user, timezone: "Atlantic/Azores") populate_stats(site, [ - build(:pageview, timestamp: relative_time(minutes: -5)) + build(:pageview, timestamp: relative_time(minute: -5)) ]) conn = diff --git a/test/plausible_web/controllers/api/stats_controller/sources_test.exs b/test/plausible_web/controllers/api/stats_controller/sources_test.exs index b2c1bb0a89ee..db3a6bd2f6ef 100644 --- a/test/plausible_web/controllers/api/stats_controller/sources_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/sources_test.exs @@ -422,17 +422,17 @@ defmodule PlausibleWeb.Api.StatsController.SourcesTest do build(:pageview, referrer_source: "Google", referrer: "google.com", - timestamp: relative_time(minutes: -3) + timestamp: relative_time(minute: -3) ), build(:pageview, referrer_source: "Google", referrer: "google.com", - timestamp: relative_time(minutes: -2) + timestamp: relative_time(minute: -2) ), build(:pageview, referrer_source: "DuckDuckGo", referrer: "duckduckgo.com", - timestamp: relative_time(minutes: -1) + timestamp: relative_time(minute: -1) ) ]) diff --git a/test/plausible_web/controllers/api/stats_controller/top_stats_test.exs b/test/plausible_web/controllers/api/stats_controller/top_stats_test.exs index 4a30cdf59e81..ed2dd5c51fbc 100644 --- a/test/plausible_web/controllers/api/stats_controller/top_stats_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/top_stats_test.exs @@ -843,9 +843,9 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do test "shows current visitors (last 5 minutes)", %{conn: conn, site: site} do populate_stats(site, [ - build(:pageview, timestamp: relative_time(minutes: -10)), - build(:pageview, timestamp: relative_time(minutes: -4)), - build(:pageview, timestamp: relative_time(minutes: -1)) + build(:pageview, timestamp: relative_time(minute: -10)), + build(:pageview, timestamp: relative_time(minute: -4)), + build(:pageview, timestamp: relative_time(minute: -1)) ]) conn = get(conn, "/api/stats/#{site.domain}/top-stats?period=realtime") @@ -859,9 +859,9 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do test "shows unique visitors (last 30 minutes)", %{conn: conn, site: site} do populate_stats(site, [ - build(:pageview, timestamp: relative_time(minutes: -45)), - build(:pageview, timestamp: relative_time(minutes: -25)), - build(:pageview, timestamp: relative_time(minutes: -1)) + build(:pageview, timestamp: relative_time(minute: -45)), + build(:pageview, timestamp: relative_time(minute: -25)), + build(:pageview, timestamp: relative_time(minute: -1)) ]) conn = get(conn, "/api/stats/#{site.domain}/top-stats?period=realtime") @@ -877,10 +877,10 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do test "shows pageviews (last 30 minutes)", %{conn: conn, site: site} do populate_stats(site, [ - build(:pageview, user_id: @user_id, timestamp: relative_time(minutes: -45)), - build(:pageview, user_id: @user_id, timestamp: relative_time(minutes: -25)), - build(:pageview, user_id: @user_id, timestamp: relative_time(minutes: -20)), - build(:pageview, timestamp: relative_time(minutes: -1)) + build(:pageview, user_id: @user_id, timestamp: relative_time(minute: -45)), + build(:pageview, user_id: @user_id, timestamp: relative_time(minute: -25)), + build(:pageview, user_id: @user_id, timestamp: relative_time(minute: -20)), + build(:pageview, timestamp: relative_time(minute: -1)) ]) conn = get(conn, "/api/stats/#{site.domain}/top-stats?period=realtime") @@ -894,10 +894,10 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do test "shows current visitors (last 5 min) with goal filter", %{conn: conn, site: site} do populate_stats(site, [ - build(:pageview, timestamp: relative_time(minutes: -10)), - build(:pageview, timestamp: relative_time(minutes: -3)), - build(:event, name: "Signup", timestamp: relative_time(minutes: -2)), - build(:event, name: "Signup", timestamp: relative_time(minutes: -1)) + build(:pageview, timestamp: relative_time(minute: -10)), + build(:pageview, timestamp: relative_time(minute: -3)), + build(:event, name: "Signup", timestamp: relative_time(minute: -2)), + build(:event, name: "Signup", timestamp: relative_time(minute: -1)) ]) filters = Jason.encode!([[:is, "event:goal", ["Signup"]]]) @@ -916,11 +916,11 @@ defmodule PlausibleWeb.Api.StatsController.TopStatsTest do site: site } do populate_stats(site, [ - build(:event, name: "Signup", timestamp: relative_time(minutes: -45)), - build(:event, name: "Signup", timestamp: relative_time(minutes: -25)), - build(:event, name: "Signup", user_id: @user_id, timestamp: relative_time(minutes: -22)), - build(:event, name: "Signup", user_id: @user_id, timestamp: relative_time(minutes: -21)), - build(:event, name: "Signup", user_id: @user_id, timestamp: relative_time(minutes: -20)) + build(:event, name: "Signup", timestamp: relative_time(minute: -45)), + build(:event, name: "Signup", timestamp: relative_time(minute: -25)), + build(:event, name: "Signup", user_id: @user_id, timestamp: relative_time(minute: -22)), + build(:event, name: "Signup", user_id: @user_id, timestamp: relative_time(minute: -21)), + build(:event, name: "Signup", user_id: @user_id, timestamp: relative_time(minute: -20)) ]) insert(:goal, site: site, event_name: "Signup") diff --git a/test/plausible_web/controllers/auth_controller_test.exs b/test/plausible_web/controllers/auth_controller_test.exs index 547630b11df2..ba655dd79699 100644 --- a/test/plausible_web/controllers/auth_controller_test.exs +++ b/test/plausible_web/controllers/auth_controller_test.exs @@ -329,7 +329,7 @@ defmodule PlausibleWeb.AuthControllerTest do test "regenerates an activation pin even if there's one already", %{conn: conn, user: user} do five_minutes_ago = NaiveDateTime.utc_now() - |> Timex.shift(minutes: -5) + |> NaiveDateTime.shift(minute: -5) |> NaiveDateTime.truncate(:second) {:ok, verification} = Auth.EmailVerification.issue_code(user, five_minutes_ago) @@ -380,7 +380,7 @@ defmodule PlausibleWeb.AuthControllerTest do test "with expired pin - reloads the form with error", %{conn: conn, user: user} do one_day_ago = NaiveDateTime.utc_now() - |> Timex.shift(days: -1) + |> NaiveDateTime.shift(day: -1) |> NaiveDateTime.truncate(:second) {:ok, verification} = Auth.EmailVerification.issue_code(user, one_day_ago) diff --git a/test/plausible_web/controllers/billing_controller_test.exs b/test/plausible_web/controllers/billing_controller_test.exs index 2c15718acd64..e25337a98d9a 100644 --- a/test/plausible_web/controllers/billing_controller_test.exs +++ b/test/plausible_web/controllers/billing_controller_test.exs @@ -69,8 +69,8 @@ defmodule PlausibleWeb.BillingControllerTest do site = new_site(owner: user) now = NaiveDateTime.utc_now() - generate_usage_for(site, 11_000, Timex.shift(now, days: -5)) - generate_usage_for(site, 11_000, Timex.shift(now, days: -35)) + generate_usage_for(site, 11_000, NaiveDateTime.shift(now, day: -5)) + generate_usage_for(site, 11_000, NaiveDateTime.shift(now, day: -35)) conn1 = post(conn, Routes.billing_path(conn, :change_plan, @v4_growth_plan)) diff --git a/test/plausible_web/controllers/settings_controller_test.exs b/test/plausible_web/controllers/settings_controller_test.exs index 83352d4d8722..e173b28460c0 100644 --- a/test/plausible_web/controllers/settings_controller_test.exs +++ b/test/plausible_web/controllers/settings_controller_test.exs @@ -270,13 +270,19 @@ defmodule PlausibleWeb.SettingsControllerTest do site = new_site(owner: user) populate_stats(site, [ - build(:event, name: "pageview", timestamp: Timex.shift(DateTime.utc_now(), days: -5)), - build(:event, name: "customevent", timestamp: Timex.shift(DateTime.utc_now(), days: -20)), - build(:event, name: "pageview", timestamp: Timex.shift(DateTime.utc_now(), days: -50)), - build(:event, name: "customevent", timestamp: Timex.shift(DateTime.utc_now(), days: -50)) + build(:event, name: "pageview", timestamp: DateTime.shift(DateTime.utc_now(), day: -5)), + build(:event, + name: "customevent", + timestamp: DateTime.shift(DateTime.utc_now(), day: -20) + ), + build(:event, name: "pageview", timestamp: DateTime.shift(DateTime.utc_now(), day: -50)), + build(:event, + name: "customevent", + timestamp: DateTime.shift(DateTime.utc_now(), day: -50) + ) ]) - last_bill_date = Timex.shift(Date.utc_today(), days: -10) + last_bill_date = Date.shift(Date.utc_today(), day: -10) subscribe_to_plan(user, @v4_plan_id, last_bill_date: last_bill_date, status: :deleted) @@ -288,21 +294,21 @@ defmodule PlausibleWeb.SettingsControllerTest do assert text_of_element(html, "#billing_cycle_tab_current_cycle") =~ Date.range( last_bill_date, - Timex.shift(last_bill_date, months: 1, days: -1) + Date.shift(last_bill_date, month: 1, day: -1) ) |> PlausibleWeb.TextHelpers.format_date_range() assert text_of_element(html, "#billing_cycle_tab_last_cycle") =~ Date.range( - Timex.shift(last_bill_date, months: -1), - Timex.shift(last_bill_date, days: -1) + Date.shift(last_bill_date, month: -1), + Date.shift(last_bill_date, day: -1) ) |> PlausibleWeb.TextHelpers.format_date_range() assert text_of_element(html, "#billing_cycle_tab_penultimate_cycle") =~ Date.range( - Timex.shift(last_bill_date, months: -2), - Timex.shift(last_bill_date, months: -1, days: -1) + Date.shift(last_bill_date, month: -2), + Date.shift(last_bill_date, month: -1, day: -1) ) |> PlausibleWeb.TextHelpers.format_date_range() @@ -340,7 +346,7 @@ defmodule PlausibleWeb.SettingsControllerTest do subscribe_to_plan(user, @v4_plan_id, status: :active, - last_bill_date: Timex.shift(DateTime.utc_now(), months: -6) + last_bill_date: Date.shift(Date.utc_today(), month: -6) ) subscription = @@ -368,7 +374,7 @@ defmodule PlausibleWeb.SettingsControllerTest do subscription |> Plausible.Billing.Subscription.changeset(%{ status: :deleted, - next_bill_date: Timex.shift(DateTime.utc_now(), months: 6) + next_bill_date: Date.shift(Date.utc_today(), month: 6) }) |> Repo.update!() @@ -383,11 +389,14 @@ defmodule PlausibleWeb.SettingsControllerTest do site = new_site(owner: user) populate_stats(site, [ - build(:event, name: "pageview", timestamp: Timex.shift(DateTime.utc_now(), days: -5)), - build(:event, name: "customevent", timestamp: Timex.shift(DateTime.utc_now(), days: -20)) + build(:event, name: "pageview", timestamp: DateTime.shift(DateTime.utc_now(), day: -5)), + build(:event, + name: "customevent", + timestamp: DateTime.shift(DateTime.utc_now(), day: -20) + ) ]) - last_bill_date = Timex.shift(Date.utc_today(), days: -10) + last_bill_date = Date.shift(Date.utc_today(), day: -10) subscribe_to_plan(user, @v4_plan_id, last_bill_date: last_bill_date) @@ -407,9 +416,7 @@ defmodule PlausibleWeb.SettingsControllerTest do conn: conn, user: user } do - subscribe_to_plan(user, @v4_plan_id, - last_bill_date: Timex.shift(Date.utc_today(), days: -1) - ) + subscribe_to_plan(user, @v4_plan_id, last_bill_date: Date.shift(Date.utc_today(), day: -1)) html = conn @@ -429,9 +436,15 @@ defmodule PlausibleWeb.SettingsControllerTest do site = new_site(owner: user) populate_stats(site, [ - build(:event, name: "pageview", timestamp: Timex.shift(DateTime.utc_now(), days: -1)), - build(:event, name: "customevent", timestamp: Timex.shift(DateTime.utc_now(), days: -10)), - build(:event, name: "customevent", timestamp: Timex.shift(DateTime.utc_now(), days: -20)) + build(:event, name: "pageview", timestamp: DateTime.shift(DateTime.utc_now(), day: -1)), + build(:event, + name: "customevent", + timestamp: DateTime.shift(DateTime.utc_now(), day: -10) + ), + build(:event, + name: "customevent", + timestamp: DateTime.shift(DateTime.utc_now(), day: -20) + ) ]) assert_usage = fn doc -> diff --git a/test/plausible_web/controllers/stats_controller_test.exs b/test/plausible_web/controllers/stats_controller_test.exs index 0f1c079c58d8..f3a21b9032f7 100644 --- a/test/plausible_web/controllers/stats_controller_test.exs +++ b/test/plausible_web/controllers/stats_controller_test.exs @@ -992,7 +992,8 @@ defmodule PlausibleWeb.StatsControllerTest do user_id: 123, pathname: "/", timestamp: - Timex.shift(~N[2021-10-20 12:00:00], minutes: -1) |> NaiveDateTime.truncate(:second), + NaiveDateTime.shift(~N[2021-10-20 12:00:00], minute: -1) + |> NaiveDateTime.truncate(:second), country_code: "EE", subdivision1_code: "EE-37", city_geoname_id: 588_409, @@ -1013,7 +1014,8 @@ defmodule PlausibleWeb.StatsControllerTest do user_id: 123, pathname: "/some-other-page", timestamp: - Timex.shift(~N[2021-10-20 12:00:00], minutes: -2) |> NaiveDateTime.truncate(:second), + NaiveDateTime.shift(~N[2021-10-20 12:00:00], minute: -2) + |> NaiveDateTime.truncate(:second), country_code: "EE", subdivision1_code: "EE-37", city_geoname_id: 588_409, @@ -1023,7 +1025,8 @@ defmodule PlausibleWeb.StatsControllerTest do user_id: 123, pathname: "/some-other-page", timestamp: - Timex.shift(~N[2021-10-20 12:00:00], minutes: -1) |> NaiveDateTime.truncate(:second), + NaiveDateTime.shift(~N[2021-10-20 12:00:00], minute: -1) + |> NaiveDateTime.truncate(:second), engagement_time: 60_000, scroll_depth: 30, country_code: "EE", @@ -1035,7 +1038,7 @@ defmodule PlausibleWeb.StatsControllerTest do user_id: 100, pathname: "/", timestamp: - Timex.shift(~N[2021-10-20 12:00:00], days: -1) |> NaiveDateTime.truncate(:second), + NaiveDateTime.shift(~N[2021-10-20 12:00:00], day: -1) |> NaiveDateTime.truncate(:second), utm_medium: "search", utm_campaign: "ads", utm_source: "google", @@ -1050,7 +1053,7 @@ defmodule PlausibleWeb.StatsControllerTest do user_id: 100, pathname: "/", timestamp: - Timex.shift(~N[2021-10-20 12:00:00], days: -1, minutes: 1) + NaiveDateTime.shift(~N[2021-10-20 12:00:00], day: -1, minute: 1) |> NaiveDateTime.truncate(:second), engagement_time: 30_000, scroll_depth: 30, @@ -1067,7 +1070,8 @@ defmodule PlausibleWeb.StatsControllerTest do build(:pageview, user_id: 200, timestamp: - Timex.shift(~N[2021-10-20 12:00:00], months: -1) |> NaiveDateTime.truncate(:second), + NaiveDateTime.shift(~N[2021-10-20 12:00:00], month: -1) + |> NaiveDateTime.truncate(:second), country_code: "EE", browser: "Firefox", browser_version: "120", @@ -1077,7 +1081,7 @@ defmodule PlausibleWeb.StatsControllerTest do build(:engagement, user_id: 200, timestamp: - Timex.shift(~N[2021-10-20 12:00:00], months: -1, minutes: 1) + NaiveDateTime.shift(~N[2021-10-20 12:00:00], month: -1, minute: 1) |> NaiveDateTime.truncate(:second), engagement_time: 30_000, scroll_depth: 20, @@ -1090,7 +1094,8 @@ defmodule PlausibleWeb.StatsControllerTest do build(:pageview, user_id: 300, timestamp: - Timex.shift(~N[2021-10-20 12:00:00], months: -5) |> NaiveDateTime.truncate(:second), + NaiveDateTime.shift(~N[2021-10-20 12:00:00], month: -5) + |> NaiveDateTime.truncate(:second), utm_campaign: "ads", country_code: "EE", referrer_source: "Google", @@ -1101,7 +1106,7 @@ defmodule PlausibleWeb.StatsControllerTest do build(:engagement, user_id: 300, timestamp: - Timex.shift(~N[2021-10-20 12:00:00], months: -5, minutes: 1) + NaiveDateTime.shift(~N[2021-10-20 12:00:00], month: -5, minute: 1) |> NaiveDateTime.truncate(:second), engagement_time: 30_000, scroll_depth: 20, @@ -1115,7 +1120,7 @@ defmodule PlausibleWeb.StatsControllerTest do build(:pageview, user_id: 456, timestamp: - Timex.shift(~N[2021-10-20 12:00:00], days: -1, minutes: -1) + NaiveDateTime.shift(~N[2021-10-20 12:00:00], day: -1, minute: -1) |> NaiveDateTime.truncate(:second), pathname: "/signup", "meta.key": ["variant"], @@ -1124,7 +1129,7 @@ defmodule PlausibleWeb.StatsControllerTest do build(:engagement, user_id: 456, timestamp: - Timex.shift(~N[2021-10-20 12:00:00], days: -1) |> NaiveDateTime.truncate(:second), + NaiveDateTime.shift(~N[2021-10-20 12:00:00], day: -1) |> NaiveDateTime.truncate(:second), pathname: "/signup", engagement_time: 60_000, scroll_depth: 20, @@ -1134,7 +1139,7 @@ defmodule PlausibleWeb.StatsControllerTest do build(:event, user_id: 456, timestamp: - Timex.shift(~N[2021-10-20 12:00:00], days: -1) |> NaiveDateTime.truncate(:second), + NaiveDateTime.shift(~N[2021-10-20 12:00:00], day: -1) |> NaiveDateTime.truncate(:second), name: "Signup", "meta.key": ["variant"], "meta.value": ["A"] diff --git a/test/plausible_web/live/choose_plan_test.exs b/test/plausible_web/live/choose_plan_test.exs index 4d76f3f7ccd0..f581dbb52a80 100644 --- a/test/plausible_web/live/choose_plan_test.exs +++ b/test/plausible_web/live/choose_plan_test.exs @@ -858,8 +858,8 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do } do now = NaiveDateTime.utc_now() - generate_usage_for(site, 11_000, Timex.shift(now, days: -5)) - generate_usage_for(site, 11_000, Timex.shift(now, days: -35)) + generate_usage_for(site, 11_000, NaiveDateTime.shift(now, day: -5)) + generate_usage_for(site, 11_000, NaiveDateTime.shift(now, day: -35)) user |> team_of() @@ -1052,7 +1052,9 @@ defmodule PlausibleWeb.Live.ChoosePlanTest do |> team_of() |> Repo.preload(:subscription) |> Map.fetch!(:subscription) - |> Subscription.changeset(%{next_bill_date: Timex.shift(DateTime.utc_now(), months: -2)}) + |> Subscription.changeset(%{ + next_bill_date: DateTime.shift(DateTime.utc_now(), month: -2) + }) |> Repo.update!() :ok diff --git a/test/support/factory.ex b/test/support/factory.ex index 63e7558c7ed7..52055b455a38 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -6,7 +6,7 @@ defmodule Plausible.Factory do def team_factory do %Plausible.Teams.Team{ name: Plausible.Teams.default_name(), - trial_expiry_date: Date.utc_today() |> Timex.shift(days: 30), + trial_expiry_date: Date.utc_today() |> Date.shift(day: 30), setup_complete: true, setup_at: NaiveDateTime.utc_now() } @@ -219,7 +219,7 @@ defmodule Plausible.Factory do email: sequence(:google_auth_email, &"email-#{&1}@example.com"), refresh_token: "123", access_token: "123", - expires: DateTime.utc_now() |> Timex.shift(days: 1) + expires: DateTime.utc_now() |> DateTime.shift(day: 1) } end diff --git a/test/support/test_utils.ex b/test/support/test_utils.ex index d67614b14062..2b136d43c141 100644 --- a/test/support/test_utils.ex +++ b/test/support/test_utils.ex @@ -222,7 +222,7 @@ defmodule Plausible.TestUtils do def relative_time(shifts) do NaiveDateTime.utc_now() - |> Timex.shift(shifts) + |> NaiveDateTime.shift(shifts) |> NaiveDateTime.truncate(:second) end diff --git a/test/workers/check_usage_test.exs b/test/workers/check_usage_test.exs index cf9f82a4bb82..7d1763a86201 100644 --- a/test/workers/check_usage_test.exs +++ b/test/workers/check_usage_test.exs @@ -144,7 +144,7 @@ defmodule Plausible.Workers.CheckUsageTest do subscribe_to_plan( user, @paddle_id_10k, - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: Plausible.Billing.Subscription.Status.paused() ) @@ -157,7 +157,7 @@ defmodule Plausible.Workers.CheckUsageTest do describe "#{status} subscription, regular customers" do test "ignores user with subscription but no usage", %{user: user} do subscribe_to_plan(user, @paddle_id_10k, - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) @@ -180,7 +180,7 @@ defmodule Plausible.Workers.CheckUsageTest do end) subscribe_to_plan(user, @paddle_id_10k, - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) @@ -203,7 +203,7 @@ defmodule Plausible.Workers.CheckUsageTest do end) subscribe_to_plan(user, @paddle_id_10k, - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) @@ -241,7 +241,7 @@ defmodule Plausible.Workers.CheckUsageTest do ) assert Repo.reload(team_of(user)).grace_period.end_date == - Timex.shift(Date.utc_today(), days: 7) + Date.shift(Date.utc_today(), day: 7) end test "sends an email suggesting enterprise plan when usage is greater than 10M ", %{ @@ -259,7 +259,7 @@ defmodule Plausible.Workers.CheckUsageTest do subscribe_to_plan( user, @paddle_id_10k, - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) @@ -284,7 +284,7 @@ defmodule Plausible.Workers.CheckUsageTest do end) subscribe_to_plan(user, @paddle_id_10k, - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) @@ -309,7 +309,7 @@ defmodule Plausible.Workers.CheckUsageTest do subscribe_to_plan( user, @paddle_id_10k, - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ) @@ -375,7 +375,7 @@ defmodule Plausible.Workers.CheckUsageTest do subscribe_to_enterprise_plan(user, monthly_pageview_limit: 1_000_000, subscription: [ - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ] ) @@ -403,7 +403,7 @@ defmodule Plausible.Workers.CheckUsageTest do user, monthly_pageview_limit: 1_000_000, subscription: [ - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ] ) @@ -433,7 +433,7 @@ defmodule Plausible.Workers.CheckUsageTest do user, monthly_pageview_limit: 1_000_000, subscription: [ - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status), # non-matching ID paddle_plan_id: @paddle_id_10k @@ -464,7 +464,7 @@ defmodule Plausible.Workers.CheckUsageTest do subscribe_to_enterprise_plan(user, site_limit: 2, subscription: [ - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ] ) @@ -495,7 +495,7 @@ defmodule Plausible.Workers.CheckUsageTest do user, monthly_pageview_limit: 1_000_000, subscription: [ - last_bill_date: Timex.shift(Date.utc_today(), days: -1), + last_bill_date: Date.shift(Date.utc_today(), day: -1), status: unquote(status) ] ) diff --git a/test/workers/import_analytics_test.exs b/test/workers/import_analytics_test.exs index 6d3c1a2c75de..f89337142e04 100644 --- a/test/workers/import_analytics_test.exs +++ b/test/workers/import_analytics_test.exs @@ -14,7 +14,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do setup do %{ import_opts: [ - start_date: Date.utc_today() |> Timex.shift(days: -7), + start_date: Date.utc_today() |> Date.shift(day: -7), end_date: Date.utc_today() ] } @@ -23,7 +23,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do test "updates site import after successful import", %{ import_opts: import_opts } do - user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: user) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) @@ -48,7 +48,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do test "clears stats_start_date field for the site after successful import", %{ import_opts: import_opts } do - user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: user, stats_start_date: ~D[2005-01-01]) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) @@ -65,7 +65,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do end test "sends email to owner after successful import", %{import_opts: import_opts} do - user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: user) {:ok, job} = Plausible.Imported.NoopImporter.new_import(site, user, import_opts) @@ -84,7 +84,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do test "send email after successful import only to the user who ran the import", %{ import_opts: import_opts } do - owner = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + owner = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: owner) importing_user = new_user() @@ -110,7 +110,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do end test "updates site import record after failed import", %{import_opts: import_opts} do - user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: user) import_opts = Keyword.put(import_opts, :error, true) @@ -125,7 +125,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do end test "clears any orphaned data during import", %{import_opts: import_opts} do - user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: user) import_opts = Keyword.put(import_opts, :error, true) @@ -150,7 +150,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do end test "sends email to owner after failed import", %{import_opts: import_opts} do - user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: user) import_opts = Keyword.put(import_opts, :error, true) @@ -170,7 +170,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do test "sends email after failed import only to the user who ran the import", %{ import_opts: import_opts } do - owner = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + owner = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: owner) import_opts = Keyword.put(import_opts, :error, true) @@ -211,7 +211,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do %{ import_opts: [ - start_date: Date.utc_today() |> Timex.shift(days: -7), + start_date: Date.utc_today() |> Date.shift(day: -7), end_date: Date.utc_today() ] } @@ -221,7 +221,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> - user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: user) site_id = site.id import_opts = Keyword.put(import_opts, :listen?, true) @@ -242,7 +242,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> - user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: user) site_id = site.id @@ -267,7 +267,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> - user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: user) site_id = site.id @@ -304,7 +304,7 @@ defmodule Plausible.Workers.ImportAnalyticsTest do import_opts: import_opts } do Ecto.Adapters.SQL.Sandbox.unboxed_run(Plausible.Repo, fn -> - user = new_user(trial_expiry_date: Date.utc_today() |> Timex.shift(days: 1)) + user = new_user(trial_expiry_date: Date.utc_today() |> Date.shift(day: 1)) site = new_site(owner: user) site_id = site.id diff --git a/test/workers/send_check_stats_emails_test.exs b/test/workers/send_check_stats_emails_test.exs index ac6f6b43affd..6d3e915eb61a 100644 --- a/test/workers/send_check_stats_emails_test.exs +++ b/test/workers/send_check_stats_emails_test.exs @@ -50,8 +50,7 @@ defmodule Plausible.Workers.SendCheckStatsEmailsTest do end defp days_ago(days) do - NaiveDateTime.utc_now() - |> NaiveDateTime.truncate(:second) - |> Timex.shift(days: -days) + NaiveDateTime.utc_now(:second) + |> NaiveDateTime.shift(day: -days) end end diff --git a/test/workers/send_email_report_test.exs b/test/workers/send_email_report_test.exs index 34aed2983306..7fad8d582fbd 100644 --- a/test/workers/send_email_report_test.exs +++ b/test/workers/send_email_report_test.exs @@ -46,9 +46,9 @@ defmodule Plausible.Workers.SendEmailReportTest do insert(:weekly_report, site: site, recipients: ["user@email.com"]) now = Timex.now(site.timezone) - last_monday = Timex.shift(now, weeks: -1) |> Plausible.Times.beginning_of_week() - last_sunday = Timex.shift(now, weeks: -1) |> Plausible.Times.end_of_week() - sunday_before_last = Timex.shift(last_monday, minutes: -1) + last_monday = DateTime.shift(now, week: -1) |> Plausible.Times.beginning_of_week() + last_sunday = DateTime.shift(now, week: -1) |> Plausible.Times.end_of_week() + sunday_before_last = DateTime.shift(last_monday, minute: -1) this_monday = Plausible.Times.beginning_of_week(now) populate_stats(site, [ @@ -78,18 +78,18 @@ defmodule Plausible.Workers.SendEmailReportTest do end test "includes the correct stats" do - now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) - site = new_site(domain: "test-site.com", inserted_at: Timex.shift(now, days: -8)) + now = NaiveDateTime.utc_now(:second) + site = new_site(domain: "test-site.com", inserted_at: NaiveDateTime.shift(now, day: -8)) insert(:weekly_report, site: site, recipients: ["user@email.com"]) populate_stats(site, [ build(:pageview, user_id: 123, - timestamp: Timex.shift(now, days: -7), + timestamp: NaiveDateTime.shift(now, day: -7), referrer_source: "Google" ), - build(:pageview, user_id: 123, timestamp: Timex.shift(now, days: -7)), - build(:pageview, timestamp: Timex.shift(now, days: -7)) + build(:pageview, user_id: 123, timestamp: NaiveDateTime.shift(now, day: -7)), + build(:pageview, timestamp: NaiveDateTime.shift(now, day: -7)) ]) perform_job(SendEmailReport, %{"site_id" => site.id, "interval" => "weekly"}) @@ -108,11 +108,11 @@ defmodule Plausible.Workers.SendEmailReportTest do end test "renders correct signs (+/-) and trend colors for positive percentage changes" do - now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) - week_ago = now |> Timex.shift(days: -7) - two_weeks_ago = now |> Timex.shift(days: -14) + now = NaiveDateTime.utc_now(:second) + week_ago = now |> NaiveDateTime.shift(day: -7) + two_weeks_ago = now |> NaiveDateTime.shift(day: -14) - site = new_site(inserted_at: Timex.shift(now, days: -15)) + site = new_site(inserted_at: NaiveDateTime.shift(now, day: -15)) insert(:weekly_report, site: site, recipients: ["user@email.com"]) populate_stats(site, [ @@ -143,11 +143,11 @@ defmodule Plausible.Workers.SendEmailReportTest do end test "renders correct signs (+/-) and trend colors for negative percentage changes" do - now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) - week_ago = now |> Timex.shift(days: -7) - two_weeks_ago = now |> Timex.shift(days: -14) + now = NaiveDateTime.utc_now(:second) + week_ago = now |> NaiveDateTime.shift(day: -7) + two_weeks_ago = now |> NaiveDateTime.shift(day: -14) - site = new_site(inserted_at: Timex.shift(now, days: -15)) + site = new_site(inserted_at: NaiveDateTime.shift(now, day: -15)) insert(:weekly_report, site: site, recipients: ["user@email.com"]) populate_stats(site, [ @@ -178,11 +178,11 @@ defmodule Plausible.Workers.SendEmailReportTest do end test "renders 0% changes with a green color and without a sign" do - now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) - week_ago = now |> Timex.shift(days: -7) - two_weeks_ago = now |> Timex.shift(days: -14) + now = NaiveDateTime.utc_now(:second) + week_ago = now |> NaiveDateTime.shift(day: -7) + two_weeks_ago = now |> NaiveDateTime.shift(day: -14) - site = new_site(inserted_at: Timex.shift(now, days: -15)) + site = new_site(inserted_at: NaiveDateTime.shift(now, day: -15)) insert(:weekly_report, site: site, recipients: ["user@email.com"]) populate_stats(site, [ @@ -218,7 +218,7 @@ defmodule Plausible.Workers.SendEmailReportTest do last_month = Timex.now(site.timezone) - |> Timex.shift(months: -1) + |> DateTime.shift(month: -1) |> Plausible.Times.beginning_of_month() |> Calendar.strftime("%B") diff --git a/test/workers/send_site_setup_emails_test.exs b/test/workers/send_site_setup_emails_test.exs index fbf614b3bb5e..8d2d0dc69016 100644 --- a/test/workers/send_site_setup_emails_test.exs +++ b/test/workers/send_site_setup_emails_test.exs @@ -101,6 +101,6 @@ defmodule Plausible.Workers.SendSiteSetupEmailsTest do defp hours_ago(hours) do NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second) - |> Timex.shift(hours: -hours) + |> NaiveDateTime.shift(hour: -hours) end end From d978010f926b9f245b1025a37ca9603433f8c67a Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 16:57:22 +0200 Subject: [PATCH 18/20] Make `QueryParser.parse_date` handle gaps and ambiguities gracefully --- lib/plausible/stats/filters/query_parser.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/plausible/stats/filters/query_parser.ex b/lib/plausible/stats/filters/query_parser.ex index b1f05365f45b..d0eae7b1ee3f 100644 --- a/lib/plausible/stats/filters/query_parser.ex +++ b/lib/plausible/stats/filters/query_parser.ex @@ -197,8 +197,12 @@ defmodule Plausible.Stats.Filters.QueryParser do end defp parse_date(site, date_string, _date, _now) when is_binary(date_string) do - case Date.from_iso8601(date_string) do - {:ok, date} -> {:ok, date, DateTime.new!(date, ~T[00:00:00], site.timezone)} + with {:ok, date} <- Date.from_iso8601(date_string), + {:ok, datetime} <- DateTime.new(date, ~T[00:00:00], site.timezone) do + {:ok, date, datetime} + else + {:gap, just_before, _just_after} -> just_before + {:ambiguous, first_datetime, _second_datetime} -> first_datetime _ -> {:error, "Invalid date '#{date_string}'."} end end From d8c3c995327d9a46cc8b21f15aff251b700386ff Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Tue, 9 Sep 2025 19:59:18 +0200 Subject: [PATCH 19/20] Replace `Timex.now(tz)` with `DateTime.now!(tz)` --- test/workers/send_email_report_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/workers/send_email_report_test.exs b/test/workers/send_email_report_test.exs index 7fad8d582fbd..2ec864bb6026 100644 --- a/test/workers/send_email_report_test.exs +++ b/test/workers/send_email_report_test.exs @@ -45,7 +45,7 @@ defmodule Plausible.Workers.SendEmailReportTest do insert(:weekly_report, site: site, recipients: ["user@email.com"]) - now = Timex.now(site.timezone) + now = DateTime.now!(site.timezone) last_monday = DateTime.shift(now, week: -1) |> Plausible.Times.beginning_of_week() last_sunday = DateTime.shift(now, week: -1) |> Plausible.Times.end_of_week() sunday_before_last = DateTime.shift(last_monday, minute: -1) @@ -217,7 +217,7 @@ defmodule Plausible.Workers.SendEmailReportTest do insert(:monthly_report, site: site, recipients: ["user@email.com", "user2@email.com"]) last_month = - Timex.now(site.timezone) + DateTime.now!(site.timezone) |> DateTime.shift(month: -1) |> Plausible.Times.beginning_of_month() |> Calendar.strftime("%B") From 4ac9ae2bb09e8c2b03b373ebe0d46aba6a023cab Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Wed, 10 Sep 2025 12:10:36 +0200 Subject: [PATCH 20/20] Use a more suitable Date function for comparison (h/t @aerosol) --- lib/plausible/billing/subscriptions.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plausible/billing/subscriptions.ex b/lib/plausible/billing/subscriptions.ex index 3c835c0446e6..c8543df2e2fe 100644 --- a/lib/plausible/billing/subscriptions.ex +++ b/lib/plausible/billing/subscriptions.ex @@ -27,7 +27,7 @@ defmodule Plausible.Billing.Subscriptions do def expired?(%Subscription{next_bill_date: next_bill_date} = subscription) do deleted? = Subscription.Status.deleted?(subscription) - expired? = Date.compare(next_bill_date, Date.utc_today()) == :lt + expired? = Date.before?(next_bill_date, Date.utc_today()) deleted? && expired? end