aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/core_ext/date_and_time/calculations.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support/core_ext/date_and_time/calculations.rb')
-rw-r--r--activesupport/lib/active_support/core_ext/date_and_time/calculations.rb127
1 files changed, 102 insertions, 25 deletions
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 b85e49aca5..e079af594d 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
@@ -9,15 +9,26 @@ module DateAndTime
: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
+ advance(days: 1)
end
# Returns true if the date/time is today.
@@ -35,6 +46,11 @@ module DateAndTime
self > self.class.current
end
+ # Returns true if the date/time falls on a Saturday or Sunday.
+ def on_weekend?
+ WEEKEND_DAYS.include?(wday)
+ end
+
# Returns a new date/time the specified number of days ago.
def days_ago(days)
advance(:days => -days)
@@ -76,15 +92,28 @@ module DateAndTime
end
# Returns a new date/time at the start of the month.
- # DateTime objects will have a time set to 0:00.
+ #
+ # today = Date.today # => Thu, 18 Jun 2015
+ # today.beginning_of_month # => Mon, 01 Jun 2015
+ #
+ # +DateTime+ objects will have a time set to 0:00.
+ #
+ # now = DateTime.current # => Thu, 18 Jun 2015 15:23:13 +0000
+ # now.beginning_of_month # => Mon, 01 Jun 2015 00:00:00 +0000
def beginning_of_month
first_hour(change(:day => 1))
end
alias :at_beginning_of_month :beginning_of_month
# Returns a new date/time at the start of the quarter.
- # Example: 1st January, 1st July, 1st October.
- # DateTime objects will have a time set to 0:00.
+ #
+ # today = Date.today # => Fri, 10 Jul 2015
+ # today.beginning_of_quarter # => Wed, 01 Jul 2015
+ #
+ # +DateTime+ objects will have a time set to 0:00.
+ #
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
+ # now.beginning_of_quarter # => Wed, 01 Jul 2015 00:00:00 +0000
def beginning_of_quarter
first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
beginning_of_month.change(:month => first_quarter_month)
@@ -92,28 +121,62 @@ module DateAndTime
alias :at_beginning_of_quarter :beginning_of_quarter
# Returns a new date/time at the end of the quarter.
- # Example: 31st March, 30th June, 30th September.
- # DateTime objects will have a time set to 23:59:59.
+ #
+ # today = Date.today # => Fri, 10 Jul 2015
+ # today.end_of_quarter # => Wed, 30 Sep 2015
+ #
+ # +DateTime+ objects will have a time set to 23:59:59.
+ #
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
+ # now.end_of_quarter # => Wed, 30 Sep 2015 23:59:59 +0000
def end_of_quarter
last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
beginning_of_month.change(:month => last_quarter_month).end_of_month
end
alias :at_end_of_quarter :end_of_quarter
- # Return a new date/time at the beginning of the year.
- # Example: 1st January.
- # DateTime objects will have a time set to 0:00.
+ # Returns a new date/time at the beginning of the year.
+ #
+ # today = Date.today # => Fri, 10 Jul 2015
+ # today.beginning_of_year # => Thu, 01 Jan 2015
+ #
+ # +DateTime+ objects will have a time set to 0:00.
+ #
+ # now = DateTime.current # => Fri, 10 Jul 2015 18:41:29 +0000
+ # now.beginning_of_year # => Thu, 01 Jan 2015 00:00:00 +0000
def beginning_of_year
change(:month => 1).beginning_of_month
end
alias :at_beginning_of_year :beginning_of_year
# Returns a new date/time representing the given day in the next week.
+ #
+ # today = Date.today # => Thu, 07 May 2015
+ # today.next_week # => Mon, 11 May 2015
+ #
# 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.
+ #
+ # today = Date.today # => Thu, 07 May 2015
+ # today.next_week(:friday) # => Fri, 15 May 2015
+ #
+ # +DateTime+ objects have their time set to 0:00 unless +same_time+ is true.
+ #
+ # now = DateTime.current # => Thu, 07 May 2015 13:31:16 +0000
+ # now.next_week # => Mon, 11 May 2015 00:00:00 +0000
+ 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).
@@ -134,12 +197,23 @@ module DateAndTime
# 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)
@@ -235,17 +309,20 @@ module DateAndTime
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