Skip to content

Commit

Permalink
Don't store lazily-evaluated results if the object has been frozen.
Browse files Browse the repository at this point in the history
See #80.
  • Loading branch information
philr committed Jan 27, 2018
1 parent 806014b commit 3e5684b
Show file tree
Hide file tree
Showing 16 changed files with 175 additions and 17 deletions.
16 changes: 14 additions & 2 deletions lib/tzinfo/country_timezone.rb
Expand Up @@ -79,7 +79,13 @@ def latitude
# calculated multiple times in concurrently executing threads. It is not
# worth the overhead of locking to ensure that @latitude is only
# calculated once.
@latitude ||= RubyCoreSupport.rational_new!(@latitude_numerator, @latitude_denominator)
unless @latitude
result = RubyCoreSupport.rational_new!(@latitude_numerator, @latitude_denominator)
return result if frozen?
@latitude = result
end

@latitude
end

# The longitude of this timezone in degrees as a Rational.
Expand All @@ -88,7 +94,13 @@ def longitude
# calculated multiple times in concurrently executing threads. It is not
# worth the overhead of locking to ensure that @longitude is only
# calculated once.
@longitude ||= RubyCoreSupport.rational_new!(@longitude_numerator, @longitude_denominator)
unless @longitude
result = RubyCoreSupport.rational_new!(@longitude_numerator, @longitude_denominator)
return result if frozen?
@longitude = result
end

@longitude
end

# Returns true if and only if the given CountryTimezone is equal to the
Expand Down
8 changes: 6 additions & 2 deletions lib/tzinfo/ruby_country_info.rb
Expand Up @@ -22,7 +22,9 @@ def zone_identifiers
# calculated once.

unless @zone_identifiers
@zone_identifiers = zones.collect {|zone| zone.identifier}.freeze
result = zones.collect {|zone| zone.identifier}.freeze
return result if frozen?
@zone_identifiers = result
end

@zone_identifiers
Expand All @@ -40,8 +42,10 @@ def zones
unless @zones
zones = Zones.new
@block.call(zones) if @block
result = zones.list.freeze
return result if frozen?
@block = nil
@zones = zones.list.freeze
@zones = result
end

@zones
Expand Down
17 changes: 12 additions & 5 deletions lib/tzinfo/time_or_datetime.rb
Expand Up @@ -54,11 +54,14 @@ def to_time
# calculated once.

unless @time
if @timestamp
@time = Time.at(@timestamp).utc
result = if @timestamp
Time.at(@timestamp).utc
else
@time = Time.utc(year, mon, mday, hour, min, sec, usec)
Time.utc(year, mon, mday, hour, min, sec, usec)
end

return result if frozen?
@time = result
end

@time
Expand All @@ -78,7 +81,9 @@ def to_datetime
# Avoid using Rational unless necessary.
u = usec
s = u == 0 ? sec : Rational(sec * 1000000 + u, 1000000)
@datetime = RubyCoreSupport.datetime_new(year, mon, mday, hour, min, s)
result = RubyCoreSupport.datetime_new(year, mon, mday, hour, min, s)
return result if frozen?
@datetime = result
end

@datetime
Expand All @@ -92,7 +97,9 @@ def to_i
# calculated once.

unless @timestamp
@timestamp = to_time.to_i
result = to_time.to_i
return result if frozen?
@timestamp = result
end

@timestamp
Expand Down
4 changes: 3 additions & 1 deletion lib/tzinfo/timezone_period.rb
Expand Up @@ -71,7 +71,9 @@ def utc_total_offset_rational
# to ensure that @zone_identifiers is only calculated once.

unless @utc_total_offset_rational
@utc_total_offset_rational = OffsetRationals.rational_for_offset(utc_total_offset)
result = OffsetRationals.rational_for_offset(utc_total_offset)
return result if frozen?
@utc_total_offset_rational = result
end
@utc_total_offset_rational
end
Expand Down
8 changes: 7 additions & 1 deletion lib/tzinfo/timezone_proxy.rb
Expand Up @@ -93,7 +93,13 @@ def real_timezone
# calculated multiple times in concurrently executing threads. It is not
# worth the overhead of locking to ensure that @real_timezone is only
# calculated once.
@real_timezone ||= Timezone.get(@identifier)
unless @real_timezone
result = Timezone.get(@identifier)
return result if frozen?
@real_timezone = result
end

@real_timezone
end
end
end
14 changes: 12 additions & 2 deletions lib/tzinfo/timezone_transition.rb
Expand Up @@ -43,7 +43,12 @@ def local_end_at
# worth the overhead of locking to ensure that @local_end_at is only
# calculated once.

@local_end_at = at.add_with_convert(@previous_offset.utc_total_offset) unless @local_end_at
unless @local_end_at
result = at.add_with_convert(@previous_offset.utc_total_offset)
return result if frozen?
@local_end_at = result
end

@local_end_at
end

Expand All @@ -67,7 +72,12 @@ def local_start_at
# worth the overhead of locking to ensure that @local_start_at is only
# calculated once.

@local_start_at = at.add_with_convert(@offset.utc_total_offset) unless @local_start_at
unless @local_start_at
result = at.add_with_convert(@offset.utc_total_offset)
return result if frozen?
@local_start_at = result
end

@local_start_at
end

Expand Down
9 changes: 6 additions & 3 deletions lib/tzinfo/timezone_transition_definition.rb
Expand Up @@ -71,13 +71,16 @@ def at
# overhead of locking to ensure that @at is only calculated once.

unless @at
unless @denominator
@at = TimeOrDateTime.new(@numerator_or_time)
result = unless @denominator
TimeOrDateTime.new(@numerator_or_time)
else
r = RubyCoreSupport.rational_new!(@numerator_or_time, @denominator)
dt = RubyCoreSupport.datetime_new!(r, 0, Date::ITALY)
@at = TimeOrDateTime.new(dt)
TimeOrDateTime.new(dt)
end

return result if frozen?
@at = result
end

@at
Expand Down
4 changes: 3 additions & 1 deletion lib/tzinfo/zoneinfo_country_info.rb
Expand Up @@ -20,7 +20,9 @@ def zone_identifiers
# calculated once.

unless @zone_identifiers
@zone_identifiers = zones.collect {|zone| zone.identifier}.freeze
result = zones.collect {|zone| zone.identifier}.freeze
return result if frozen?
@zone_identifiers = result
end

@zone_identifiers
Expand Down
12 changes: 12 additions & 0 deletions test/tc_country_timezone.rb
Expand Up @@ -17,6 +17,12 @@ def test_latitude_new!
ct = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
assert_equal(Rational(2059, 40), ct.latitude)
end

def test_latitude_after_freeze_new!
ct = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
ct.freeze
assert_equal(Rational(2059, 40), ct.latitude)
end

def test_latitude_new
ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
Expand All @@ -27,6 +33,12 @@ def test_longitude_new!
ct = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
assert_equal(Rational(-5, 16), ct.longitude)
end

def test_longitude_after_freeze_new!
ct = CountryTimezone.new!('Europe/London', 2059, 40, -5, 16)
ct.freeze
assert_equal(Rational(-5, 16), ct.longitude)
end

def test_longitude_new
ct = CountryTimezone.new('Europe/London', Rational(2059, 40), Rational(-5, 16))
Expand Down
30 changes: 30 additions & 0 deletions test/tc_ruby_country_info.rb
Expand Up @@ -37,6 +37,19 @@ def test_zone_identifiers
assert_equal(['ZZ/TimezoneB', 'ZZ/TimezoneA', 'ZZ/TimezoneC', 'ZZ/TimezoneD'], ci.zone_identifiers)
assert(ci.zone_identifiers.frozen?)
end

def test_zone_identifiers_after_freeze
ci = RubyCountryInfo.new('ZZ', 'Zzz') do |c|
c.timezone('ZZ/TimezoneB', 1, 2, 1, 2, 'Timezone B')
c.timezone('ZZ/TimezoneA', 1, 4, 1, 4, 'Timezone A')
c.timezone('ZZ/TimezoneC', -10, 3, -20, 7, 'C')
c.timezone('ZZ/TimezoneD', -10, 3, -20, 7)
end

ci.freeze

assert_equal(['ZZ/TimezoneB', 'ZZ/TimezoneA', 'ZZ/TimezoneC', 'ZZ/TimezoneD'], ci.zone_identifiers)
end

def test_zones_empty
ci = RubyCountryInfo.new('ZZ', 'Zzz') {|c| }
Expand Down Expand Up @@ -65,6 +78,23 @@ def test_zones
ci.zones)
assert(ci.zones.frozen?)
end

def test_zones_after_freeze
ci = RubyCountryInfo.new('ZZ', 'Zzz') do |c|
c.timezone('ZZ/TimezoneB', 1, 2, 1, 2, 'Timezone B')
c.timezone('ZZ/TimezoneA', 1, 4, 1, 4, 'Timezone A')
c.timezone('ZZ/TimezoneC', -10, 3, -20, 7, 'C')
c.timezone('ZZ/TimezoneD', -10, 3, -20, 7)
end

ci.freeze

assert_equal([CountryTimezone.new!('ZZ/TimezoneB', 1, 2, 1, 2, 'Timezone B'),
CountryTimezone.new!('ZZ/TimezoneA', 1, 4, 1, 4, 'Timezone A'),
CountryTimezone.new!('ZZ/TimezoneC', -10, 3, -20, 7, 'C'),
CountryTimezone.new!('ZZ/TimezoneD', -10, 3, -20, 7)],
ci.zones)
end

def test_deferred_evaluate
block_called = false
Expand Down
15 changes: 15 additions & 0 deletions test/tc_time_or_datetime.rb
Expand Up @@ -128,6 +128,11 @@ def test_to_time_trunc_to_usec
assert_equal(Time.utc(2006, 3, 24, 15, 32, 3, 721123),
TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(7211239, 10000000))).to_time)
end

def test_to_time_after_freeze
assert_equal(Time.utc(2006, 3, 24, 15, 32, 3), TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3)).freeze.to_time)
assert_equal(Time.utc(2006, 3, 24, 15, 32, 3), TimeOrDateTime.new(1143214323).freeze.to_time)
end

def test_to_datetime
assert_equal(DateTime.new(2006, 3, 24, 15, 32, 3),
Expand Down Expand Up @@ -157,6 +162,11 @@ def test_to_datetime_trunc_to_usec
assert_equal(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721123, 1000000)),
TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 721123 + Rational(9, 10))).to_datetime)
end

def test_to_datetime_after_freeze
assert_equal(DateTime.new(2006, 3, 24, 15, 32, 3), TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)).freeze.to_datetime)
assert_equal(DateTime.new(2006, 3, 24, 15, 32, 3), TimeOrDateTime.new(1143214323).freeze.to_datetime)
end

def test_to_i
assert_equal(1143214323,
Expand All @@ -172,6 +182,11 @@ def test_to_i
assert_equal(1143214323,
TimeOrDateTime.new('1143214323').to_i)
end

def test_to_i_after_freeze
assert_equal(1143214323, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)).freeze.to_i)
assert_equal(1143214323, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3)).freeze.to_i)
end

def test_to_orig
assert_equal(Time.utc(2006, 3, 24, 15, 32, 3),
Expand Down
7 changes: 7 additions & 0 deletions test/tc_timezone_period.rb
Expand Up @@ -149,6 +149,13 @@ def test_initialize_offset
assert_nil(p.local_end)
assert_nil(p.local_end_time)
end

def test_utc_total_offset_rational_after_freeze
o = TimezoneOffset.new(3600, 0, :TEST)
p = TimezonePeriod.new(nil, nil, o)
p.freeze
assert_equal(Rational(1, 24), p.utc_total_offset_rational)
end

def test_dst
p1 = TimezonePeriod.new(nil, nil, TimezoneOffset.new(-14400, 3600, :TEST))
Expand Down
9 changes: 9 additions & 0 deletions test/tc_timezone_proxy.rb
Expand Up @@ -88,6 +88,15 @@ def test_canonical_linked
assert_same(Timezone.get('UTC'), proxy.canonical_zone)
end
end

def test_after_freeze
proxy = TimezoneProxy.new('Europe/London')
real = Timezone.get('Europe/London')
t = Time.utc(2017, 6, 1)
proxy.freeze
assert_equal('Europe/London', proxy.identifier)
assert_equal(real.utc_to_local(t), proxy.utc_to_local(t))
end

def test_equals
assert_equal(true, TimezoneProxy.new('Europe/London') == TimezoneProxy.new('Europe/London'))
Expand Down
14 changes: 14 additions & 0 deletions test/tc_timezone_transition.rb
Expand Up @@ -68,6 +68,13 @@ def test_local_end_at
assert(TimeOrDateTime.new(DateTime.new(2006, 5, 30, 1, 31, 20)).eql?(t2.local_end_at))
assert(TimeOrDateTime.new(Time.utc(2006, 5, 30, 1, 31, 20)).eql?(t3.local_end_at))
end

def test_local_end_at_after_freeze
t = TestTimezoneTransition.new(TimezoneOffset.new(3600, 3600, :TDT),
TimezoneOffset.new(3600, 0, :TST), 1148949080)
t.freeze
assert(TimeOrDateTime.new(1148952680).eql?(t.local_end_at))
end

def test_local_end
t1 = TestTimezoneTransition.new(TimezoneOffset.new(3600, 3600, :TDT),
Expand Down Expand Up @@ -107,6 +114,13 @@ def test_local_start_at
assert(TimeOrDateTime.new(DateTime.new(2006, 5, 30, 2, 31, 20)).eql?(t2.local_start_at))
assert(TimeOrDateTime.new(Time.utc(2006, 5, 30, 2, 31, 20)).eql?(t3.local_start_at))
end

def test_local_start_at_after_freeze
t = TestTimezoneTransition.new(TimezoneOffset.new(3600, 3600, :TDT),
TimezoneOffset.new(3600, 0, :TST), 1148949080)
t.freeze
assert(TimeOrDateTime.new(1148956280).eql?(t.local_start_at))
end

def test_local_start
t1 = TestTimezoneTransition.new(TimezoneOffset.new(3600, 3600, :TDT),
Expand Down
11 changes: 11 additions & 0 deletions test/tc_timezone_transition_definition.rb
Expand Up @@ -70,6 +70,17 @@ def test_at_after_32bit
assert(TimeOrDateTime.new(DateTime.new(2038, 1, 19, 3, 14, 8)).eql?(t.at))
end
end

def test_at_after_freeze
t1 = TimezoneTransitionDefinition.new(TimezoneOffset.new(3600, 3600, :TDT),
TimezoneOffset.new(3600, 0, :TST), 1148949080)
t2 = TimezoneTransitionDefinition.new(TimezoneOffset.new(3600, 3600, :TDT),
TimezoneOffset.new(3600, 0, :TST), 5300392727, 2160)
t1.freeze
t2.freeze
assert(TimeOrDateTime.new(1148949080).eql?(t1.at))
assert(TimeOrDateTime.new(DateTime.new(2006, 5, 30, 0, 31, 20)).eql?(t2.at))
end

def test_eql_timestamp
t1 = TimezoneTransitionDefinition.new(TimezoneOffset.new(3600, 3600, :TDT),
Expand Down
14 changes: 14 additions & 0 deletions test/tc_zoneinfo_country_info.rb
Expand Up @@ -35,6 +35,20 @@ def test_zone_identifiers
assert(!ci.zones.equal?(zones))
assert(!zones.frozen?)
end

def test_zone_identifiers_after_freeze
zones = [
CountryTimezone.new('ZZ/TimezoneB', Rational(1, 2), Rational(1, 2), 'Timezone B'),
CountryTimezone.new('ZZ/TimezoneA', Rational(1, 4), Rational(1, 4), 'Timezone A'),
CountryTimezone.new('ZZ/TimezoneC', Rational(-10, 3), Rational(-20, 7), 'C'),
CountryTimezone.new('ZZ/TimezoneD', Rational(-10, 3), Rational(-20, 7))
]

ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', zones)
ci.freeze

assert_equal(['ZZ/TimezoneB', 'ZZ/TimezoneA', 'ZZ/TimezoneC', 'ZZ/TimezoneD'], ci.zone_identifiers)
end

def test_zones_empty
ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', [])
Expand Down

0 comments on commit 3e5684b

Please sign in to comment.