Skip to content

Commit

Permalink
Wrap time ranges with timezones, closes #8807
Browse files Browse the repository at this point in the history
(cherry picked from commit e2e5136)
(cherry picked from commit dcdde7d)

Backport of #6183, original issue was #6179

Conflicts:
	activesupport/lib/active_support/core_ext/time/calculations.rb
	activesupport/test/core_ext/time_ext_test

Signed-off-by: Andrew White <andyw@pixeltrix.co.uk>
  • Loading branch information
nashby authored and pixeltrix committed Jan 8, 2013
1 parent d9a0480 commit d39d878
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 43 deletions.
12 changes: 4 additions & 8 deletions activesupport/lib/active_support/core_ext/time/calculations.rb
Expand Up @@ -217,7 +217,7 @@ def beginning_of_day

# Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
def end_of_day
change(:hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
change(:hour => 23, :min => 59, :sec => 59, :usec => Rational(999999999, 1000))

This comment has been minimized.

Copy link
@fxn

fxn Jan 8, 2013

Member

This rational object is constructed in every call, and in several methods. Would it make sense to give it a meaningful name and extract it to a constant?

This comment has been minimized.

Copy link
@pixeltrix

pixeltrix Jan 8, 2013

Contributor

Probably - but we'd need to fix master first 😄

What name do you think? end_of_second is the best I can think of

This comment has been minimized.

Copy link
@fxn

fxn Jan 8, 2013

Member

end_of_second sounds good, a little ambiguous but simple enough. Maybe with a brief comment that mentions that time is discrete in microseconds or something like that. I don't know how portable is that indeed, should investigate.

end

# Returns a new Time representing the start of the hour (x:00)
Expand All @@ -228,11 +228,7 @@ def beginning_of_hour

# Returns a new Time representing the end of the hour, x:59:59.999999 (.999999999 in ruby1.9)
def end_of_hour
change(
:min => 59,
:sec => 59,
:usec => 999999.999
)
change(:min => 59, :sec => 59, :usec => Rational(999999999, 1000))
end

# Returns a new Time representing the start of the month (1st of the month, 0:00)
Expand All @@ -246,7 +242,7 @@ def beginning_of_month
def end_of_month
#self - ((self.mday-1).days + self.seconds_since_midnight)
last_day = ::Time.days_in_month(month, year)
change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
change(:day => last_day, :hour => 23, :min => 59, :sec => 59, :usec => Rational(999999999, 1000))
end
alias :at_end_of_month :end_of_month

Expand All @@ -270,7 +266,7 @@ def beginning_of_year

# Returns a new Time representing the end of the year (end of the 31st of december)
def end_of_year
change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59, :usec => 999999.999)
change(:month => 12, :day => 31, :hour => 23, :min => 59, :sec => 59, :usec => Rational(999999999, 1000))
end
alias :at_end_of_year :end_of_year

Expand Down
16 changes: 13 additions & 3 deletions activesupport/lib/active_support/time_with_zone.rb
Expand Up @@ -329,8 +329,7 @@ def respond_to?(sym, include_priv = false)

# Send the missing method to +time+ instance, and wrap result in a new TimeWithZone with the existing +time_zone+.
def method_missing(sym, *args, &block)
result = time.__send__(sym, *args, &block)
result.acts_like?(:time) ? self.class.new(nil, time_zone, result) : result
wrap_with_time_zone time.__send__(sym, *args, &block)
end

private
Expand All @@ -348,11 +347,22 @@ def get_period_and_ensure_valid_local_time
end

def transfer_time_values_to_utc_constructor(time)
::Time.utc_time(time.year, time.month, time.day, time.hour, time.min, time.sec, time.respond_to?(:usec) ? time.usec : 0)
usec = time.respond_to?(:nsec) ? Rational(time.nsec, 1000) : (time.respond_to?(:usec) ? time.usec : 0)
::Time.utc_time(time.year, time.month, time.day, time.hour, time.min, time.sec, usec)
end

def duration_of_variable_length?(obj)
ActiveSupport::Duration === obj && obj.parts.any? {|p| p[0].in?([:years, :months, :days]) }
end

def wrap_with_time_zone(time)
if time.acts_like?(:time)
self.class.new(nil, time_zone, time)
elsif time.is_a?(Range)
wrap_with_time_zone(time.begin)..wrap_with_time_zone(time.end)
else
time
end
end
end
end
4 changes: 2 additions & 2 deletions activesupport/test/core_ext/date_ext_test.rb
Expand Up @@ -350,14 +350,14 @@ def test_beginning_of_day_when_zone_is_set
end

def test_end_of_day
assert_equal Time.local(2005,2,21,23,59,59,999999.999), Date.new(2005,2,21).end_of_day
assert_equal Time.local(2005,2,21,23,59,59,Rational(999999999, 1000)), Date.new(2005,2,21).end_of_day
end

def test_end_of_day_when_zone_is_set
zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
with_env_tz 'UTC' do
with_tz_default zone do
assert_equal zone.local(2005,2,21,23,59,59,999999.999), Date.new(2005,2,21).end_of_day
assert_equal zone.local(2005,2,21,23,59,59,Rational(999999999, 1000)), Date.new(2005,2,21).end_of_day
assert_equal zone, Date.new(2005,2,21).end_of_day.time_zone
end
end
Expand Down
68 changes: 38 additions & 30 deletions activesupport/test/core_ext/time_ext_test.rb
Expand Up @@ -109,49 +109,49 @@ def test_beginning_of_quarter
end

def test_end_of_day
assert_equal Time.local(2007,8,12,23,59,59,999999.999), Time.local(2007,8,12,10,10,10).end_of_day
assert_equal Time.local(2007,8,12,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,12,10,10,10).end_of_day
with_env_tz 'US/Eastern' do
assert_equal Time.local(2007,4,2,23,59,59,999999.999), Time.local(2007,4,2,10,10,10).end_of_day, 'start DST'
assert_equal Time.local(2007,10,29,23,59,59,999999.999), Time.local(2007,10,29,10,10,10).end_of_day, 'ends DST'
assert_equal Time.local(2007,4,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,4,2,10,10,10).end_of_day, 'start DST'
assert_equal Time.local(2007,10,29,23,59,59,Rational(999999999, 1000)), Time.local(2007,10,29,10,10,10).end_of_day, 'ends DST'
end
with_env_tz 'NZ' do
assert_equal Time.local(2006,3,19,23,59,59,999999.999), Time.local(2006,3,19,10,10,10).end_of_day, 'ends DST'
assert_equal Time.local(2006,10,1,23,59,59,999999.999), Time.local(2006,10,1,10,10,10).end_of_day, 'start DST'
assert_equal Time.local(2006,3,19,23,59,59,Rational(999999999, 1000)), Time.local(2006,3,19,10,10,10).end_of_day, 'ends DST'
assert_equal Time.local(2006,10,1,23,59,59,Rational(999999999, 1000)), Time.local(2006,10,1,10,10,10).end_of_day, 'start DST'
end
end

def test_end_of_week
assert_equal Time.local(2008,1,6,23,59,59,999999.999), Time.local(2007,12,31,10,10,10).end_of_week
assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,27,0,0,0).end_of_week #monday
assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,28,0,0,0).end_of_week #tuesday
assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,29,0,0,0).end_of_week #wednesday
assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,30,0,0,0).end_of_week #thursday
assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,8,31,0,0,0).end_of_week #friday
assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,9,01,0,0,0).end_of_week #saturday
assert_equal Time.local(2007,9,2,23,59,59,999999.999), Time.local(2007,9,02,0,0,0).end_of_week #sunday
assert_equal Time.local(2008,1,6,23,59,59,Rational(999999999, 1000)), Time.local(2007,12,31,10,10,10).end_of_week
assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,27,0,0,0).end_of_week #monday
assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,28,0,0,0).end_of_week #tuesday
assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,29,0,0,0).end_of_week #wednesday
assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,30,0,0,0).end_of_week #thursday
assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,8,31,0,0,0).end_of_week #friday
assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,9,01,0,0,0).end_of_week #saturday
assert_equal Time.local(2007,9,2,23,59,59,Rational(999999999, 1000)), Time.local(2007,9,02,0,0,0).end_of_week #sunday
end

def test_end_of_hour
assert_equal Time.local(2005,2,4,19,59,59,999999.999), Time.local(2005,2,4,19,30,10).end_of_hour
assert_equal Time.local(2005,2,4,19,59,59,Rational(999999999, 1000)), Time.local(2005,2,4,19,30,10).end_of_hour
end

def test_end_of_month
assert_equal Time.local(2005,3,31,23,59,59,999999.999), Time.local(2005,3,20,10,10,10).end_of_month
assert_equal Time.local(2005,2,28,23,59,59,999999.999), Time.local(2005,2,20,10,10,10).end_of_month
assert_equal Time.local(2005,4,30,23,59,59,999999.999), Time.local(2005,4,20,10,10,10).end_of_month
assert_equal Time.local(2005,3,31,23,59,59,Rational(999999999, 1000)), Time.local(2005,3,20,10,10,10).end_of_month
assert_equal Time.local(2005,2,28,23,59,59,Rational(999999999, 1000)), Time.local(2005,2,20,10,10,10).end_of_month
assert_equal Time.local(2005,4,30,23,59,59,Rational(999999999, 1000)), Time.local(2005,4,20,10,10,10).end_of_month
end

def test_end_of_quarter
assert_equal Time.local(2007,3,31,23,59,59,999999.999), Time.local(2007,2,15,10,10,10).end_of_quarter
assert_equal Time.local(2007,3,31,23,59,59,999999.999), Time.local(2007,3,31,0,0,0).end_of_quarter
assert_equal Time.local(2007,12,31,23,59,59,999999.999), Time.local(2007,12,21,10,10,10).end_of_quarter
assert_equal Time.local(2007,6,30,23,59,59,999999.999), Time.local(2007,4,1,0,0,0).end_of_quarter
assert_equal Time.local(2008,6,30,23,59,59,999999.999), Time.local(2008,5,31,0,0,0).end_of_quarter
assert_equal Time.local(2007,3,31,23,59,59,Rational(999999999, 1000)), Time.local(2007,2,15,10,10,10).end_of_quarter
assert_equal Time.local(2007,3,31,23,59,59,Rational(999999999, 1000)), Time.local(2007,3,31,0,0,0).end_of_quarter
assert_equal Time.local(2007,12,31,23,59,59,Rational(999999999, 1000)), Time.local(2007,12,21,10,10,10).end_of_quarter
assert_equal Time.local(2007,6,30,23,59,59,Rational(999999999, 1000)), Time.local(2007,4,1,0,0,0).end_of_quarter
assert_equal Time.local(2008,6,30,23,59,59,Rational(999999999, 1000)), Time.local(2008,5,31,0,0,0).end_of_quarter
end

def test_end_of_year
assert_equal Time.local(2007,12,31,23,59,59,999999.999), Time.local(2007,2,22,10,10,10).end_of_year
assert_equal Time.local(2007,12,31,23,59,59,999999.999), Time.local(2007,12,31,10,10,10).end_of_year
assert_equal Time.local(2007,12,31,23,59,59,Rational(999999999, 1000)), Time.local(2007,2,22,10,10,10).end_of_year
assert_equal Time.local(2007,12,31,23,59,59,Rational(999999999, 1000)), Time.local(2007,12,31,10,10,10).end_of_year
end

def test_beginning_of_year
Expand Down Expand Up @@ -807,24 +807,32 @@ def test_case_equality
end

def test_all_day
assert_equal Time.local(2011,6,7,0,0,0)..Time.local(2011,6,7,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_day
assert_equal Time.local(2011,6,7,0,0,0)..Time.local(2011,6,7,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_day
end

def test_all_day_with_timezone
beginning_of_day = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], Time.local(2011,6,7,0,0,0))
end_of_day = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], Time.local(2011,6,7,23,59,59,Rational(999999999, 1000)))

assert_equal beginning_of_day, ActiveSupport::TimeWithZone.new(Time.local(2011,6,7,10,10,10), ActiveSupport::TimeZone["Hawaii"]).all_day.begin
assert_equal end_of_day, ActiveSupport::TimeWithZone.new(Time.local(2011,6,7,10,10,10), ActiveSupport::TimeZone["Hawaii"]).all_day.end
end

def test_all_week
assert_equal Time.local(2011,6,6,0,0,0)..Time.local(2011,6,12,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_week
assert_equal Time.local(2011,6,5,0,0,0)..Time.local(2011,6,11,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_week(:sunday)
assert_equal Time.local(2011,6,6,0,0,0)..Time.local(2011,6,12,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_week
assert_equal Time.local(2011,6,5,0,0,0)..Time.local(2011,6,11,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_week(:sunday)
end

def test_all_month
assert_equal Time.local(2011,6,1,0,0,0)..Time.local(2011,6,30,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_month
assert_equal Time.local(2011,6,1,0,0,0)..Time.local(2011,6,30,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_month
end

def test_all_quarter
assert_equal Time.local(2011,4,1,0,0,0)..Time.local(2011,6,30,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_quarter
assert_equal Time.local(2011,4,1,0,0,0)..Time.local(2011,6,30,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_quarter
end

def test_all_year
assert_equal Time.local(2011,1,1,0,0,0)..Time.local(2011,12,31,23,59,59,999999.999), Time.local(2011,6,7,10,10,10).all_year
assert_equal Time.local(2011,1,1,0,0,0)..Time.local(2011,12,31,23,59,59,Rational(999999999, 1000)), Time.local(2011,6,7,10,10,10).all_year
end

protected
Expand Down
10 changes: 10 additions & 0 deletions activesupport/test/core_ext/time_with_zone_test.rb
Expand Up @@ -80,6 +80,16 @@ def test_to_json_with_use_standard_json_time_format_config_set_to_true
ActiveSupport.use_standard_json_time_format = old
end

if RUBY_VERSION >= '1.9'
def test_nsec
local = Time.local(2011,6,7,23,59,59,Rational(999999999, 1000))
with_zone = ActiveSupport::TimeWithZone.new(nil, ActiveSupport::TimeZone["Hawaii"], local)

assert_equal local.nsec, with_zone.nsec
assert_equal with_zone.nsec, 999999999
end
end

def test_strftime
assert_equal '1999-12-31 19:00:00 EST -0500', @twz.strftime('%Y-%m-%d %H:%M:%S %Z %z')
end
Expand Down

0 comments on commit d39d878

Please sign in to comment.