Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add predicate methods for previous and next day checks #37625

Merged
6 changes: 6 additions & 0 deletions activesupport/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
* Add Date and Time `#yesterday?` and `#tomorrow?` alongside `#today?`.

Aliased to `#prev_day?` and `#next_day?` to match the existing `#prev/next_day` methods.

*Jatin Dhankhar*

* Add `Enumerable#pick` to complement `ActiveRecord::Relation#pick`.

*Eugene Kenny*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ def today?
to_date == ::Date.current
end

# Returns true if the date/time is tomorrow.
def tomorrow?
to_date == ::Date.current.tomorrow
end
alias :next_day? :tomorrow?

# Returns true if the date/time is yesterday.
def yesterday?
to_date == ::Date.current.yesterday
end
alias :prev_day? :yesterday?

# Returns true if the date/time is in the past.
def past?
self < self.class.current
Expand Down
14 changes: 14 additions & 0 deletions activesupport/lib/active_support/time_with_zone.rb
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,20 @@ def today?
time.today?
end

# Returns true if the current object's time falls within
# the next day (tomorrow).
def tomorrow?
time.tomorrow?
end
alias :next_day? :tomorrow?

# Returns true if the current object's time falls within
# the previous day (yesterday).
def yesterday?
time.yesterday?
end
alias :prev_day? :yesterday?

# Returns true if the current object's time is in the future.
def future?
utc.future?
Expand Down
74 changes: 73 additions & 1 deletion activesupport/test/core_ext/date_time_ext_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,81 @@ def test_today_without_offset
end
end

def test_yesterday_with_offset
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, DateTime.civil(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)).yesterday?
assert_equal false, DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-18000, 86400)).yesterday?
assert_equal false, DateTime.civil(2000, 1, 1, 23, 59, 59, Rational(-18000, 86400)).yesterday?
assert_equal true, DateTime.civil(1999, 12, 31, 0, 0, 0, Rational(-18000, 86400)).yesterday?
end
end

def test_yesterday_without_offset
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, DateTime.civil(1999, 12, 31, 23, 59, 59).yesterday?
assert_equal false, DateTime.civil(2000, 1, 1, 0).yesterday?
assert_equal false, DateTime.civil(2000, 1, 1, 23, 59, 59).yesterday?
assert_equal false, DateTime.civil(2000, 1, 2, 0).yesterday?
end
end

def test_prev_day_with_offset
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, DateTime.civil(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)).prev_day?
assert_equal false, DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-18000, 86400)).prev_day?
assert_equal false, DateTime.civil(2000, 1, 1, 23, 59, 59, Rational(-18000, 86400)).prev_day?
assert_equal true, DateTime.civil(1999, 12, 31, 0, 0, 0, Rational(-18000, 86400)).prev_day?
end
end

def test_prev_day_without_offset
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, DateTime.civil(1999, 12, 31, 23, 59, 59).prev_day?
assert_equal false, DateTime.civil(2000, 1, 1, 0).prev_day?
assert_equal false, DateTime.civil(2000, 1, 1, 23, 59, 59).prev_day?
assert_equal false, DateTime.civil(2000, 1, 2, 0).prev_day?
end
end

def test_tomorrow_with_offset
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, DateTime.civil(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)).tomorrow?
assert_equal true, DateTime.civil(2000, 1, 2, 0, 0, 0, Rational(-18000, 86400)).tomorrow?
assert_equal false, DateTime.civil(2000, 1, 1, 23, 59, 59, Rational(-18000, 86400)).tomorrow?
assert_equal true, DateTime.civil(2000, 1, 2, 23, 59, 59, Rational(-18000, 86400)).tomorrow?
end
end

def test_tomorrow_without_offset
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, DateTime.civil(1999, 12, 31, 23, 59, 59).tomorrow?
assert_equal true, DateTime.civil(2000, 1, 2, 0).tomorrow?
assert_equal false, DateTime.civil(2000, 1, 1, 23, 59, 59).tomorrow?
assert_equal false, DateTime.civil(2000, 1, 3, 0).tomorrow?
end
end

def test_next_day_with_offset
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, DateTime.civil(1999, 12, 31, 23, 59, 59, Rational(-18000, 86400)).next_day?
assert_equal true, DateTime.civil(2000, 1, 2, 0, 0, 0, Rational(-18000, 86400)).next_day?
assert_equal false, DateTime.civil(2000, 1, 1, 23, 59, 59, Rational(-18000, 86400)).next_day?
assert_equal true, DateTime.civil(2000, 1, 2, 23, 59, 59, Rational(-18000, 86400)).next_day?
end
end

def test_next_day_without_offset
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, DateTime.civil(1999, 12, 31, 23, 59, 59).next_day?
assert_equal true, DateTime.civil(2000, 1, 2, 0).next_day?
assert_equal false, DateTime.civil(2000, 1, 1, 23, 59, 59).next_day?
assert_equal false, DateTime.civil(2000, 1, 3, 0).next_day?
end
end

def test_past_with_offset
DateTime.stub(:current, DateTime.civil(2005, 2, 10, 15, 30, 45, Rational(-18000, 86400))) do
assert_equal true, DateTime.civil(2005, 2, 10, 15, 30, 44, Rational(-18000, 86400)).past?
assert_equal true, DateTime.civil(2005, 2, 10, 15, 30, 44, Rational(-18000, 86400)).past?
assert_equal false, DateTime.civil(2005, 2, 10, 15, 30, 45, Rational(-18000, 86400)).past?
assert_equal false, DateTime.civil(2005, 2, 10, 15, 30, 46, Rational(-18000, 86400)).past?
end
Expand Down
72 changes: 72 additions & 0 deletions activesupport/test/core_ext/time_ext_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,78 @@ def test_today_with_time_utc
end
end

def test_yesterday_with_time_local
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, Time.local(1999, 12, 31, 23, 59, 59).yesterday?
assert_equal false, Time.local(2000, 1, 1, 0).yesterday?
assert_equal true, Time.local(1999, 12, 31).yesterday?
assert_equal false, Time.local(2000, 1, 2, 0).yesterday?
end
end

def test_yesterday_with_time_utc
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, Time.utc(1999, 12, 31, 23, 59, 59).yesterday?
assert_equal false, Time.utc(2000, 1, 1, 0).yesterday?
assert_equal true, Time.utc(1999, 12, 31).yesterday?
assert_equal false, Time.utc(2000, 1, 2, 0).yesterday?
end
end

def test_prev_day_with_time_local
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, Time.local(1999, 12, 31, 23, 59, 59).prev_day?
assert_equal false, Time.local(2000, 1, 1, 0).prev_day?
assert_equal true, Time.local(1999, 12, 31).prev_day?
assert_equal false, Time.local(2000, 1, 2, 0).prev_day?
end
end

def test_prev_day_with_time_utc
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, Time.utc(1999, 12, 31, 23, 59, 59).prev_day?
assert_equal false, Time.utc(2000, 1, 1, 0).prev_day?
assert_equal true, Time.utc(1999, 12, 31).prev_day?
assert_equal false, Time.utc(2000, 1, 2, 0).prev_day?
end
end

def test_tomorrow_with_time_local
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, Time.local(1999, 12, 31, 23, 59, 59).tomorrow?
assert_equal true, Time.local(2000, 1, 2, 0).tomorrow?
assert_equal true, Time.local(2000, 1, 2, 23, 59, 59).tomorrow?
assert_equal false, Time.local(2000, 1, 1, 0).tomorrow?
end
end

def test_tomorrow_with_time_utc
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, Time.utc(1999, 12, 31, 23, 59, 59).tomorrow?
assert_equal true, Time.utc(2000, 1, 2, 0).tomorrow?
assert_equal true, Time.utc(2000, 1, 2, 23, 59, 59).tomorrow?
assert_equal false, Time.utc(2000, 1, 1, 0).tomorrow?
end
end

def test_next_day_with_time_local
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, Time.local(1999, 12, 31, 23, 59, 59).next_day?
assert_equal true, Time.local(2000, 1, 2, 0).next_day?
assert_equal true, Time.local(2000, 1, 2, 23, 59, 59).next_day?
assert_equal false, Time.local(2000, 1, 1, 0).next_day?
end
end

def test_next_day_with_time_utc
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, Time.utc(1999, 12, 31, 23, 59, 59).next_day?
assert_equal true, Time.utc(2000, 1, 2, 0).next_day?
assert_equal true, Time.utc(2000, 1, 2, 23, 59, 59).next_day?
assert_equal false, Time.utc(2000, 1, 1, 0).next_day?
end
end

def test_past_with_time_current_as_time_local
with_env_tz "US/Eastern" do
Time.stub(:current, Time.local(2005, 2, 10, 15, 30, 45)) do
Expand Down
36 changes: 36 additions & 0 deletions activesupport/test/core_ext/time_with_zone_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,42 @@ def test_today
end
end

def test_yesterday?
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31, 23, 59, 59)).yesterday?
assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 1, 0)).yesterday?
assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31)).yesterday?
assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 2, 0)).yesterday?
end
end

def test_prev_day?
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31, 23, 59, 59)).prev_day?
assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 1, 0)).prev_day?
assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31)).prev_day?
assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 2, 0)).prev_day?
end
end

def test_tomorrow?
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31, 23, 59, 59)).tomorrow?
assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 2, 0)).tomorrow?
assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 1, 23, 59, 59)).tomorrow?
assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31, 0)).tomorrow?
end
end

def test_next_day?
Date.stub(:current, Date.new(2000, 1, 1)) do
assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31, 23, 59, 59)).next_day?
assert_equal true, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 2, 0)).next_day?
assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000, 1, 1, 23, 59, 59)).next_day?
assert_equal false, ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31, 0)).next_day?
end
end

def test_past_with_time_current_as_time_local
with_env_tz "US/Eastern" do
Time.stub(:current, Time.local(2005, 2, 10, 15, 30, 45)) do
Expand Down
4 changes: 2 additions & 2 deletions guides/source/active_support_core_extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2928,7 +2928,7 @@ INFO: The following calculation methods have edge cases in October 1582, since d

#### `Date.current`

Active Support defines `Date.current` to be today in the current time zone. That's like `Date.today`, except that it honors the user time zone, if defined. It also defines `Date.yesterday` and `Date.tomorrow`, and the instance predicates `past?`, `today?`, `future?`, `on_weekday?` and `on_weekend?`, all of them relative to `Date.current`.
Active Support defines `Date.current` to be today in the current time zone. That's like `Date.today`, except that it honors the user time zone, if defined. It also defines `Date.yesterday` and `Date.tomorrow`, and the instance predicates `past?`, `today?`, `tomorrow?`, `next_day?`, `yesterday?`, `prev_day?`, `future?`, `on_weekday?` and `on_weekend?`, all of them relative to `Date.current`.

When making Date comparisons using methods which honor the user time zone, make sure to use `Date.current` and not `Date.today`. There are cases where the user time zone might be in the future compared to the system time zone, which `Date.today` uses by default. This means `Date.today` may equal `Date.yesterday`.

Expand Down Expand Up @@ -3441,7 +3441,7 @@ t.advance(seconds: 1)

#### `Time.current`

Active Support defines `Time.current` to be today in the current time zone. That's like `Time.now`, except that it honors the user time zone, if defined. It also defines the instance predicates `past?`, `today?`, and `future?`, all of them relative to `Time.current`.
Active Support defines `Time.current` to be today in the current time zone. That's like `Time.now`, except that it honors the user time zone, if defined. It also defines the instance predicates `past?`, `today?`, `tomorrow?`, `next_day?`, `yesterday?`, `prev_day?` and `future?`, all of them relative to `Time.current`.

When making Time comparisons using methods which honor the user time zone, make sure to use `Time.current` instead of `Time.now`. There are cases where the user time zone might be in the future compared to the system time zone, which `Time.now` uses by default. This means `Time.now.to_date` may equal `Date.yesterday`.

Expand Down