diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index af70ed390c..86565cd226 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -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* diff --git a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb index 27cb47eb6e..21cfeed557 100644 --- a/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb @@ -31,6 +31,18 @@ module DateAndTime 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 diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 3be5f6f7b5..28001b518f 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -245,6 +245,20 @@ module ActiveSupport 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? diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 0894e1d7dd..2a1c33692b 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -253,9 +253,81 @@ class DateTimeExtCalculationsTest < ActiveSupport::TestCase 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 diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index f8bfbb5779..621a46022c 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -686,6 +686,78 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase 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 diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 3bcc74acb7..02205ac47d 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -251,6 +251,42 @@ class TimeWithZoneTest < ActiveSupport::TestCase 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 diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 686aeffd83..edc8e1cfc9 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -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`. @@ -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`.