diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 383cdbb52f52dfc22ca6327a7fa34c69ee22067b..4f970176e513c7df5aec796aface3855be3f5310 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,7 @@
*Rails 3.1.0 (unreleased)*
+* Added weeks_ago and prev_week to Date/DateTime/Time. [Rob Zolkos, fxn]
+
* Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! [Andrew White]
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index 1856b4dd8ba47d5e37dbdcb30bba99963ff7a496..f34185f22cbeb5df0119c2053566088cd424ef0b 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -5,6 +5,8 @@
require 'active_support/core_ext/time/zones'
class Date
+ DAYS_INTO_WEEK = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6 }
+
if RUBY_VERSION < '1.9'
undef :>>
@@ -127,6 +129,11 @@ def change(options)
)
end
+ # Returns a new Date/DateTime representing the time a number of specified weeks ago.
+ def weeks_ago(weeks)
+ advance(:weeks => -weeks)
+ end
+
# Returns a new Date/DateTime representing the time a number of specified months ago.
def months_ago(months)
advance(:months => -months)
@@ -185,10 +192,15 @@ def end_of_week
alias :sunday :end_of_week
alias :at_end_of_week :end_of_week
+ # Returns a new Date/DateTime representing the start of the given day in the previous week (default is Monday).
+ def prev_week(day = :monday)
+ result = (self - 7).beginning_of_week + DAYS_INTO_WEEK[day]
+ self.acts_like?(:time) ? result.change(:hour => 0) : result
+ end
+
# Returns a new Date/DateTime representing the start of the given day in next week (default is Monday).
def next_week(day = :monday)
- days_into_week = { :monday => 0, :tuesday => 1, :wednesday => 2, :thursday => 3, :friday => 4, :saturday => 5, :sunday => 6}
- result = (self + 7).beginning_of_week + days_into_week[day]
+ result = (self + 7).beginning_of_week + DAYS_INTO_WEEK[day]
self.acts_like?(:time) ? result.change(:hour => 0) : result
end
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index d4307516239359837784e0064119a38f79a4d6ff..fa052fa86b9ff106766abb85b81fc5f46dea7d99 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -22,7 +22,7 @@ def days_in_month(month, year = now.year)
# Returns a new Time if requested year can be accommodated by Ruby's Time class
# (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
- # otherwise returns a DateTime
+ # otherwise returns a DateTime.
def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
time = ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
# This check is needed because Time.utc(y) returns a time object in the 2000s for 0 <= y <= 138.
@@ -117,6 +117,11 @@ def since(seconds)
end
alias :in :since
+ # Returns a new Time representing the time a number of specified weeks ago.
+ def weeks_ago(weeks)
+ advance(:weeks => -weeks)
+ end
+
# Returns a new Time representing the time a number of specified months ago
def months_ago(months)
advance(:months => -months)
@@ -172,6 +177,11 @@ def end_of_week
end
alias :at_end_of_week :end_of_week
+ # Returns a new Time representing the start of the given day in the previous week (default is Monday).
+ def prev_week(day = :monday)
+ ago(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
+ end
+
# Returns a new Time representing the start of the given day in next week (default is Monday).
def next_week(day = :monday)
since(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index 3141e895e651a123cde87e28ecbe38021c7bc163..342a31cdefadd48fb604c7a7061709e8a289fff1 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -110,6 +110,14 @@ def test_beginning_of_year
assert_equal Date.new(2005,1,1).to_s, Date.new(2005,2,22).beginning_of_year.to_s
end
+ def test_weeks_ago
+ assert_equal Date.new(2005,5,10), Date.new(2005,5,17).weeks_ago(1)
+ assert_equal Date.new(2005,5,10), Date.new(2005,5,24).weeks_ago(2)
+ assert_equal Date.new(2005,5,10), Date.new(2005,5,31).weeks_ago(3)
+ assert_equal Date.new(2005,5,10), Date.new(2005,6,7).weeks_ago(4)
+ assert_equal Date.new(2006,12,31), Date.new(2007,2,4).weeks_ago(5)
+ end
+
def test_months_ago
assert_equal Date.new(2005,5,5), Date.new(2005,6,5).months_ago(1)
assert_equal Date.new(2004,11,5), Date.new(2005,6,5).months_ago(7)
@@ -219,6 +227,14 @@ def test_advance_in_calendar_reform
end
end
+ def test_prev_week
+ assert_equal Date.new(2005,5,9), Date.new(2005,5,17).prev_week
+ assert_equal Date.new(2006,12,25), Date.new(2007,1,7).prev_week
+ assert_equal Date.new(2010,2,12), Date.new(2010,2,19).prev_week(:friday)
+ assert_equal Date.new(2010,2,13), Date.new(2010,2,19).prev_week(:saturday)
+ assert_equal Date.new(2010,2,27), Date.new(2010,3,4).prev_week(:saturday)
+ end
+
def test_next_week
assert_equal Date.new(2005,2,28), Date.new(2005,2,22).next_week
assert_equal Date.new(2005,3,4), Date.new(2005,2,22).next_week(:friday)
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index cb290c541bf0c30c96108d24ead66cbea00fef4e..7d993d84e29a7e7e21b45c5fe94d00398a875c3d 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -92,6 +92,14 @@ def test_beginning_of_year
assert_equal DateTime.civil(2005,1,1,0,0,0), DateTime.civil(2005,2,22,10,10,10).beginning_of_year
end
+ def test_weeks_ago
+ assert_equal DateTime.civil(2005,5,29,10), DateTime.civil(2005,6,5,10,0,0).weeks_ago(1)
+ assert_equal DateTime.civil(2005,5,1,10), DateTime.civil(2005,6,5,10,0,0).weeks_ago(5)
+ assert_equal DateTime.civil(2005,4,24,10), DateTime.civil(2005,6,5,10,0,0).weeks_ago(6)
+ assert_equal DateTime.civil(2005,2,27,10), DateTime.civil(2005,6,5,10,0,0).weeks_ago(14)
+ assert_equal DateTime.civil(2004,12,25,10), DateTime.civil(2005,1,1,10,0,0).weeks_ago(1)
+ end
+
def test_months_ago
assert_equal DateTime.civil(2005,5,5,10), DateTime.civil(2005,6,5,10,0,0).months_ago(1)
assert_equal DateTime.civil(2004,11,5,10), DateTime.civil(2005,6,5,10,0,0).months_ago(7)
@@ -196,6 +204,14 @@ def test_advanced_processes_first_the_date_deltas_and_then_the_time_deltas
assert_equal DateTime.civil(2010, 3, 29), DateTime.civil(2010, 2, 28, 22, 58, 59).advance(:months => 1, :hours => 1, :minutes => 1, :seconds => 1)
end
+ def test_prev_week
+ assert_equal DateTime.civil(2005,2,21), DateTime.civil(2005,3,1,15,15,10).prev_week
+ assert_equal DateTime.civil(2005,2,22), DateTime.civil(2005,3,1,15,15,10).prev_week(:tuesday)
+ assert_equal DateTime.civil(2005,2,25), DateTime.civil(2005,3,1,15,15,10).prev_week(:friday)
+ assert_equal DateTime.civil(2006,10,30), DateTime.civil(2006,11,6,0,0,0).prev_week
+ assert_equal DateTime.civil(2006,11,15), DateTime.civil(2006,11,23,0,0,0).prev_week(:wednesday)
+ end
+
def test_next_week
assert_equal DateTime.civil(2005,2,28), DateTime.civil(2005,2,22,15,15,10).next_week
assert_equal DateTime.civil(2005,3,4), DateTime.civil(2005,2,22,15,15,10).next_week(:friday)
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index c43b3eb35831462cda17e69fbc231c74e80a66e6..53d497013ac74ac86ce85ade0e9ee07d350619c3 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -130,6 +130,14 @@ def test_beginning_of_year
assert_equal Time.local(2005,1,1,0,0,0), Time.local(2005,2,22,10,10,10).beginning_of_year
end
+ def test_weeks_ago
+ assert_equal Time.local(2005,5,29,10), Time.local(2005,6,5,10,0,0).weeks_ago(1)
+ assert_equal Time.local(2005,5,1,10), Time.local(2005,6,5,10,0,0).weeks_ago(5)
+ assert_equal Time.local(2005,4,24,10), Time.local(2005,6,5,10,0,0).weeks_ago(6)
+ assert_equal Time.local(2005,2,27,10), Time.local(2005,6,5,10,0,0).weeks_ago(14)
+ assert_equal Time.local(2004,12,25,10), Time.local(2005,1,1,10,0,0).weeks_ago(1)
+ end
+
def test_months_ago
assert_equal Time.local(2005,5,5,10), Time.local(2005,6,5,10,0,0).months_ago(1)
assert_equal Time.local(2004,11,5,10), Time.local(2005,6,5,10,0,0).months_ago(7)
@@ -463,6 +471,16 @@ def test_utc_advance
assert_equal Time.utc(2013,10,17,20,22,19), Time.utc(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5, :hours => 5, :minutes => 7, :seconds => 9)
end
+ def test_prev_week
+ with_env_tz 'US/Eastern' do
+ assert_equal Time.local(2005,2,21), Time.local(2005,3,1,15,15,10).prev_week
+ assert_equal Time.local(2005,2,22), Time.local(2005,3,1,15,15,10).prev_week(:tuesday)
+ assert_equal Time.local(2005,2,25), Time.local(2005,3,1,15,15,10).prev_week(:friday)
+ assert_equal Time.local(2006,10,30), Time.local(2006,11,6,0,0,0).prev_week
+ assert_equal Time.local(2006,11,15), Time.local(2006,11,23,0,0,0).prev_week(:wednesday)
+ end
+ end
+
def test_next_week
with_env_tz 'US/Eastern' do
assert_equal Time.local(2005,2,28), Time.local(2005,2,22,15,15,10).next_week
diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index dfc4d3811297d5f2f4315920944a3311e5c78c3b..9b1d264d2c341752819696e6423d408125863cd9 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -2859,9 +2859,9 @@ d.end_of_week # => Sun, 09 May 2010
+beginning_of_week+ is aliased to +monday+ and +at_beginning_of_week+. +end_of_week+ is aliased to +sunday+ and +at_end_of_week+.
-h6. +next_week+
+h6. +prev_week+, +next_week+
-+next_week+ receives a symbol with a day name in English (in lowercase, default is +:monday+) and it returns the date corresponding to that day in the next week:
+The method +next_week+ receives a symbol with a day name in English (in lowercase, default is +:monday+) and it returns the date corresponding to that day:
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
@@ -2869,6 +2869,14 @@ d.next_week # => Mon, 10 May 2010
d.next_week(:saturday) # => Sat, 15 May 2010
+The method +prev_week+ is analogous:
+
+
+d.prev_week # => Mon, 26 Apr 2010
+d.prev_week(:saturday) # => Sat, 01 May 2010
+d.prev_week(:friday) # => Fri, 30 Apr 2010
+
+
h6. +beginning_of_month+, +end_of_month+
The methods +beginning_of_month+ and +end_of_month+ return the dates for the beginning and end of the month:
@@ -2946,6 +2954,15 @@ Date.new(2010, 4, 30).months_ago(2) # => Sun, 28 Feb 2010
Date.new(2009, 12, 31).months_since(2) # => Sun, 28 Feb 2010
+h6. +weeks_ago+
+
+The method +weeks_ago+ works analogously for weeks:
+
+
+Date.new(2010, 5, 24).weeks_ago(1) # => Mon, 17 May 2010
+Date.new(2010, 5, 24).weeks_ago(2) # => Mon, 10 May 2010
+
+
h6. +advance+
The most generic way to jump to other days is +advance+. This method receives a hash with keys +:years+, +:months+, +:weeks+, +:days+, and returns a date advanced as much as the present keys indicate:
@@ -3067,6 +3084,8 @@ yesterday
tomorrow
beginning_of_week (monday, at_beginning_of_week)
end_on_week (at_end_of_week)
+weeks_ago
+prev_week
next_week
months_ago
months_since
@@ -3239,6 +3258,8 @@ beginning_of_day (midnight, at_midnight, at_beginning_of_day)
end_of_day
beginning_of_week (monday, at_beginning_of_week)
end_on_week (at_end_of_week)
+weeks_ago
+prev_week
next_week
months_ago
months_since