Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions activesupport/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
* Add `#prev_day` and `#next_day` counterparts to `#yesterday` and
`#tomorrow` for `Date`, `Time`, and `DateTime`.

*George Claghorn*

* Add `same_time` option to `#next_week` and `#prev_week` for `Date`, `Time`,
and `DateTime`.

*George Claghorn*

* Add `#on_weekend?`, `#next_weekday`, `#prev_weekday` methods to `Date`,
`Time`, and `DateTime`.

`#on_weekend?` returns true if the receiving date/time falls on a Saturday
or Sunday.

`#next_weekday` returns a new date/time representing the next day that does
not fall on a Saturday or Sunday.

`#prev_weekday` returns a new date/time representing the previous day that
does not fall on a Saturday or Sunday.

*George Claghorn*

* Change the default test order from `:sorted` to `:random`.

*Rafael Mendonça França*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,26 @@ module Calculations
:saturday => 5,
:sunday => 6
}
WEEKEND_DAYS = [ 6, 0 ]

# Returns a new date/time representing yesterday.
def yesterday
advance(:days => -1)
advance(days: -1)
end

# Returns a new date/time representing the previous day.
def prev_day
advance(days: -1)
end

# Returns a new date/time representing tomorrow.
def tomorrow
advance(:days => 1)
advance(days: 1)
end

# Returns a new date/time representing the next day.
def next_day
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhh: I think this might be an alias of tomorrow. wdyt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could have been, and @georgeclaghorn initially implemented it as such, but I find that more confusing. Clarity > DRY.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great... 👍

advance(days: 1)
end

# Returns true if the date/time is today.
Expand All @@ -35,6 +46,11 @@ def future?
self > self.class.current
end

# Returns true if the date/time falls on a Saturday or Sunday.
def on_weekend?
wday.in?(WEEKEND_DAYS)
end

# Returns a new date/time the specified number of days ago.
def days_ago(days)
advance(:days => -days)
Expand Down Expand Up @@ -111,9 +127,19 @@ def beginning_of_year
# Returns a new date/time representing the given day in the next week.
# The +given_day_in_next_week+ defaults to the beginning of the week
# which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+
# when set. +DateTime+ objects have their time set to 0:00.
def next_week(given_day_in_next_week = Date.beginning_of_week)
first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))
# when set. +DateTime+ objects have their time set to 0:00 unless +same_time+ is true.
def next_week(given_day_in_next_week = Date.beginning_of_week, same_time: false)
result = first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))
same_time ? copy_time_to(result) : result
end

# Returns a new date/time representing the next weekday.
def next_weekday
if next_day.on_weekend?
next_week(:monday, same_time: true)
else
next_day
end
end

# Short-hand for months_since(1).
Expand All @@ -134,12 +160,23 @@ def next_year
# Returns a new date/time representing the given day in the previous week.
# Week is assumed to start on +start_day+, default is
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
# DateTime objects have their time set to 0:00.
def prev_week(start_day = Date.beginning_of_week)
first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
# DateTime objects have their time set to 0:00 unless +same_time+ is true.
def prev_week(start_day = Date.beginning_of_week, same_time: false)
result = first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
same_time ? copy_time_to(result) : result
end
alias_method :last_week, :prev_week

# Returns a new date/time representing the previous weekday.
def prev_weekday
if prev_day.on_weekend?
copy_time_to(beginning_of_week(:friday))
else
prev_day
end
end
alias_method :last_weekday, :prev_weekday

# Short-hand for months_ago(1).
def prev_month
months_ago(1)
Expand Down Expand Up @@ -235,17 +272,20 @@ def all_year
end

private
def first_hour(date_or_time)
date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
end

def first_hour(date_or_time)
date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
end
def last_hour(date_or_time)
date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
end

def last_hour(date_or_time)
date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
end
def days_span(day)
(DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
end

def days_span(day)
(DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
end
def copy_time_to(other)
other.change(hour: hour, min: min, sec: sec, usec: try(:usec))
end
end
end
70 changes: 70 additions & 0 deletions activesupport/test/core_ext/date_and_time_behavior.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@ def test_yesterday
assert_equal date_time_init(2005,2,28,10,10,10), date_time_init(2005,3,2,10,10,10).yesterday.yesterday
end

def test_prev_day
assert_equal date_time_init(2005,2,21,10,10,10), date_time_init(2005,2,22,10,10,10).prev_day
assert_equal date_time_init(2005,2,28,10,10,10), date_time_init(2005,3,2,10,10,10).prev_day.prev_day
end

def test_tomorrow
assert_equal date_time_init(2005,2,23,10,10,10), date_time_init(2005,2,22,10,10,10).tomorrow
assert_equal date_time_init(2005,3,2,10,10,10), date_time_init(2005,2,28,10,10,10).tomorrow.tomorrow
end

def test_next_day
assert_equal date_time_init(2005,2,23,10,10,10), date_time_init(2005,2,22,10,10,10).next_day
assert_equal date_time_init(2005,3,2,10,10,10), date_time_init(2005,2,28,10,10,10).next_day.next_day
end

def test_days_ago
assert_equal date_time_init(2005,6,4,10,10,10), date_time_init(2005,6,5,10,10,10).days_ago(1)
assert_equal date_time_init(2005,5,31,10,10,10), date_time_init(2005,6,5,10,10,10).days_ago(5)
Expand Down Expand Up @@ -115,6 +125,28 @@ def test_next_week_with_default_beginning_of_week_set
end
end

def test_next_week_at_same_time
assert_equal date_time_init(2005,2,28,15,15,10), date_time_init(2005,2,22,15,15,10).next_week(:monday, same_time: true)
assert_equal date_time_init(2005,3,4,15,15,10), date_time_init(2005,2,22,15,15,10).next_week(:friday, same_time: true)
assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,10,23,0,0,0).next_week(:monday, same_time: true)
assert_equal date_time_init(2006,11,1,0,0,0), date_time_init(2006,10,23,0,0,0).next_week(:wednesday, same_time: true)
end

def test_next_weekday_on_wednesday
assert_equal date_time_init(2015,1,8,0,0,0), date_time_init(2015,1,7,0,0,0).next_weekday
assert_equal date_time_init(2015,1,8,15,15,10), date_time_init(2015,1,7,15,15,10).next_weekday
end

def test_next_weekday_on_friday
assert_equal date_time_init(2015,1,5,0,0,0), date_time_init(2015,1,2,0,0,0).next_weekday
assert_equal date_time_init(2015,1,5,15,15,10), date_time_init(2015,1,2,15,15,10).next_weekday
end

def test_next_weekday_on_saturday
assert_equal date_time_init(2015,1,5,0,0,0), date_time_init(2015,1,3,0,0,0).next_weekday
assert_equal date_time_init(2015,1,5,15,15,10), date_time_init(2015,1,3,15,15,10).next_weekday
end

def test_next_month_on_31st
assert_equal date_time_init(2005,9,30,15,15,10), date_time_init(2005,8,31,15,15,10).next_month
end
Expand Down Expand Up @@ -144,6 +176,29 @@ def test_prev_week_with_default_beginning_of_week
end
end

def test_prev_week_at_same_time
assert_equal date_time_init(2005,2,21,15,15,10), date_time_init(2005,3,1,15,15,10).prev_week(:monday, same_time: true)
assert_equal date_time_init(2005,2,22,15,15,10), date_time_init(2005,3,1,15,15,10).prev_week(:tuesday, same_time: true)
assert_equal date_time_init(2005,2,25,15,15,10), date_time_init(2005,3,1,15,15,10).prev_week(:friday, same_time: true)
assert_equal date_time_init(2006,10,30,0,0,0), date_time_init(2006,11,6,0,0,0).prev_week(:monday, same_time: true)
assert_equal date_time_init(2006,11,15,0,0,0), date_time_init(2006,11,23,0,0,0).prev_week(:wednesday, same_time: true)
end

def test_prev_weekday_on_wednesday
assert_equal date_time_init(2015,1,6,0,0,0), date_time_init(2015,1,7,0,0,0).prev_weekday
assert_equal date_time_init(2015,1,6,15,15,10), date_time_init(2015,1,7,15,15,10).prev_weekday
end

def test_prev_weekday_on_monday
assert_equal date_time_init(2015,1,2,0,0,0), date_time_init(2015,1,5,0,0,0).prev_weekday
assert_equal date_time_init(2015,1,2,15,15,10), date_time_init(2015,1,5,15,15,10).prev_weekday
end

def test_prev_weekday_on_sunday
assert_equal date_time_init(2015,1,2,0,0,0), date_time_init(2015,1,4,0,0,0).prev_weekday
assert_equal date_time_init(2015,1,2,15,15,10), date_time_init(2015,1,4,15,15,10).prev_weekday
end

def test_prev_month_on_31st
assert_equal date_time_init(2004,2,29,10,10,10), date_time_init(2004,3,31,10,10,10).prev_month
end
Expand Down Expand Up @@ -231,6 +286,21 @@ def test_sunday_with_default_beginning_of_week_set
end
end

def test_on_weekend_on_saturday
assert date_time_init(2015,1,3,0,0,0).on_weekend?
assert date_time_init(2015,1,3,15,15,10).on_weekend?
end

def test_on_weekend_on_sunday
assert date_time_init(2015,1,4,0,0,0).on_weekend?
assert date_time_init(2015,1,4,15,15,10).on_weekend?
end

def test_on_weekend_on_monday
assert_not date_time_init(2015,1,5,0,0,0).on_weekend?
assert_not date_time_init(2015,1,5,15,15,10).on_weekend?
end

def with_bw_default(bw = :monday)
old_bw = Date.beginning_of_week
Date.beginning_of_week = bw
Expand Down