Skip to content

Commit 2dd9c68

Browse files
Include the final second in local heartbeat day ranges
Co-authored-by: Amp <amp@ampcode.com>
1 parent 306b123 commit 2dd9c68

3 files changed

Lines changed: 26 additions & 4 deletions

File tree

app/models/concerns/time_range_filterable.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module TimeRangeFilterable
44
RANGES = {
55
today: {
66
human_name: "Today",
7-
calculate: -> { Time.current.beginning_of_day..Time.current.end_of_day }
7+
calculate: -> { Time.current.beginning_of_day...Time.current.beginning_of_day.next_day }
88
},
99
yesterday: {
1010
human_name: "Yesterday",
@@ -73,8 +73,11 @@ def filter_by_time_range(interval, from = nil, to = nil)
7373
interval = interval&.to_sym
7474
if interval == :custom
7575
from_time = from.present? ? Time.zone.parse(from).beginning_of_day.to_i : 0
76-
to_time = to.present? ? Time.zone.parse(to).end_of_day.to_i : 253402300799
77-
where(time: from_time..to_time)
76+
if to.present?
77+
where(time: from_time...Time.zone.parse(to).beginning_of_day.next_day.to_i)
78+
else
79+
where(time: from_time..253402300799)
80+
end
7881
elsif RANGES.key?(interval)
7982
public_send(interval)
8083
else

app/models/heartbeat.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ class Heartbeat < ApplicationRecord
1111
# Default scope to exclude deleted records
1212
default_scope { where(deleted_at: nil) }
1313

14-
scope :today, -> { where(time: Time.current.beginning_of_day.to_i..Time.current.end_of_day.to_i) }
1514
scope :recent, -> { where("time > ?", 24.hours.ago.to_i) }
1615
scope :with_deleted, -> { unscope(where: :deleted_at) }
1716
scope :only_deleted, -> { with_deleted.where.not(deleted_at: nil) }

test/models/heartbeat_test.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,26 @@ class HeartbeatTest < ActiveSupport::TestCase
1616
ActiveJob::Base.queue_adapter = @original_queue_adapter
1717
end
1818

19+
test "today and custom days include the final fractional second across DST" do
20+
Time.use_zone("Europe/London") do
21+
[ "2026-03-29", "2026-10-25" ].each do |date|
22+
travel_to Time.zone.parse("#{date} 12:00:00") do
23+
user = create(:user)
24+
start = Time.current.beginning_of_day
25+
finish = start.next_day
26+
rows = [ start.to_f - 0.5, finish.to_f - 1.5, finish.to_f - 0.5, finish.to_f ].map do |time|
27+
create(:heartbeat, user:, time:, source_type: :test_entry)
28+
end
29+
[ user.heartbeats.today, user.heartbeats.filter_by_time_range("custom", date, date) ].each do |scope|
30+
assert_equal rows[1..2].map(&:id), scope.order(:time, :id).pluck(:id)
31+
assert_equal 1, scope.duration_seconds
32+
end
33+
assert_includes [ 23.hours, 25.hours ], finish - start
34+
end
35+
end
36+
end
37+
end
38+
1939
test "soft delete hides record from default scope and restore brings it back" do
2040
user = create(:user)
2141
heartbeat = create(:heartbeat, user: user,

0 commit comments

Comments
 (0)