Skip to content

Commit e9425ab

Browse files
philrjaredbeckjonathanhefner
authored andcommitted
Update to TZInfo v2.0.0
Co-authored-by: Jared Beck <[email protected]> Co-authored-by: Jonathan Hefner <[email protected]>
1 parent fba1064 commit e9425ab

File tree

12 files changed

+113
-27
lines changed

12 files changed

+113
-27
lines changed

Gemfile.lock

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ PATH
7373
concurrent-ruby (~> 1.0, >= 1.0.2)
7474
i18n (>= 1.6, < 2)
7575
minitest (~> 5.1)
76-
tzinfo (~> 1.1)
76+
tzinfo (~> 2.0)
7777
zeitwerk (~> 2.2, >= 2.2.2)
7878
rails (6.1.0.alpha)
7979
actioncable (= 6.1.0.alpha)
@@ -490,14 +490,12 @@ GEM
490490
eventmachine (~> 1.0, >= 1.0.4)
491491
rack (>= 1, < 3)
492492
thor (1.0.1)
493-
thread_safe (0.3.6)
494-
thread_safe (0.3.6-java)
495493
tilt (2.0.10)
496494
turbolinks (5.2.1)
497495
turbolinks-source (~> 5.2)
498496
turbolinks-source (5.2.0)
499-
tzinfo (1.2.6)
500-
thread_safe (~> 0.1)
497+
tzinfo (2.0.0)
498+
concurrent-ruby (~> 1.0)
501499
tzinfo-data (1.2019.3)
502500
tzinfo (>= 1.0.0)
503501
uber (0.1.0)

activesupport/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
* Update to TZInfo v2.0.0.
2+
3+
This changes the output of `ActiveSupport::TimeZone.utc_to_local`, but
4+
can be controlled with the
5+
`Rails.application.config.active_support.utc_to_local_returns_utc_offset_times` config.
6+
7+
New Rails 6.1 apps have it enabled by default, existing apps can upgrade
8+
via the config in config/initializers/new_framework_defaults_6_1.rb
9+
10+
See the `utc_to_local_returns_utc_offset_times` documentation for details.
11+
12+
*Phil Ross and Jared Beck*
13+
114
* [Breaking change] `ActiveSupport::Callbacks#halted_callback_hook` now receive a 2nd argument:
215

316
`ActiveSupport::Callbacks#halted_callback_hook` now receive the name of the callback

activesupport/activesupport.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Gem::Specification.new do |s|
3434
# https://edgeguides.rubyonrails.org/security.html#dependency-management-and-cves
3535

3636
s.add_dependency "i18n", ">= 1.6", "< 2"
37-
s.add_dependency "tzinfo", "~> 1.1"
37+
s.add_dependency "tzinfo", "~> 2.0"
3838
s.add_dependency "minitest", "~> 5.1"
3939
s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
4040
s.add_dependency "zeitwerk", "~> 2.2", ">= 2.2.2"

activesupport/lib/active_support.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ def self.to_time_preserves_timezone
9595
def self.to_time_preserves_timezone=(value)
9696
DateAndTime::Compatibility.preserve_timezone = value
9797
end
98+
99+
def self.utc_to_local_returns_utc_offset_times
100+
DateAndTime::Compatibility.utc_to_local_returns_utc_offset_times
101+
end
102+
103+
def self.utc_to_local_returns_utc_offset_times=(value)
104+
DateAndTime::Compatibility.utc_to_local_returns_utc_offset_times = value
105+
end
98106
end
99107

100108
autoload :I18n, "active_support/i18n"

activesupport/lib/active_support/core_ext/date_and_time/compatibility.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,20 @@ module Compatibility
1212
# this behavior, but new apps will have an initializer that sets
1313
# this to true, because the new behavior is preferred.
1414
mattr_accessor :preserve_timezone, instance_writer: false, default: false
15+
16+
# Change the output of <tt>ActiveSupport::TimeZone.utc_to_local</tt>.
17+
#
18+
# When `true`, it returns local times with an UTC offset, with `false` local
19+
# times are returned as UTC.
20+
#
21+
# # Given this zone:
22+
# zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
23+
#
24+
# # With `utc_to_local_returns_utc_offset_times = false`, local time is converted to UTC:
25+
# zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 UTC
26+
#
27+
# # With `utc_to_local_returns_utc_offset_times = true`, local time is returned with UTC offset:
28+
# zone.utc_to_local(Time.utc(2000, 1)) # => 1999-12-31 19:00:00 -0500
29+
mattr_accessor :utc_to_local_returns_utc_offset_times, instance_writer: false, default: false
1530
end
1631
end

activesupport/lib/active_support/time_with_zone.rb

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ def initialize(utc_time, time_zone, local_time = nil, period = nil)
5757

5858
# Returns a <tt>Time</tt> instance that represents the time in +time_zone+.
5959
def time
60-
@time ||= period.to_local(@utc)
60+
@time ||= incorporate_utc_offset(@utc, utc_offset)
6161
end
6262

6363
# Returns a <tt>Time</tt> instance of the simultaneous time in the UTC timezone.
6464
def utc
65-
@utc ||= period.to_utc(@time)
65+
@utc ||= incorporate_utc_offset(@time, -utc_offset)
6666
end
6767
alias_method :comparable_time, :utc
6868
alias_method :getgm, :utc
@@ -104,13 +104,13 @@ def dst?
104104
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
105105
# Time.zone.now.utc? # => false
106106
def utc?
107-
period.offset.abbreviation == :UTC || period.offset.abbreviation == :UCT
107+
zone == "UTC" || zone == "UCT"
108108
end
109109
alias_method :gmt?, :utc?
110110

111111
# Returns the offset from current time to UTC time in seconds.
112112
def utc_offset
113-
period.utc_total_offset
113+
period.observed_utc_offset
114114
end
115115
alias_method :gmt_offset, :utc_offset
116116
alias_method :gmtoff, :utc_offset
@@ -132,7 +132,7 @@ def formatted_offset(colon = true, alternate_utc_string = nil)
132132
# Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
133133
# Time.zone.now.zone # => "EST"
134134
def zone
135-
period.zone_identifier.to_s
135+
period.abbreviation
136136
end
137137

138138
# Returns a string of the object's date, time, zone, and offset from UTC.
@@ -524,6 +524,16 @@ def method_missing(sym, *args, &block)
524524
end
525525

526526
private
527+
SECONDS_PER_DAY = 86400
528+
529+
def incorporate_utc_offset(time, offset)
530+
if time.kind_of?(Date)
531+
time + Rational(offset, SECONDS_PER_DAY)
532+
else
533+
time + offset
534+
end
535+
end
536+
527537
def get_period_and_ensure_valid_local_time(period)
528538
# we don't want a Time.local instance enforcing its own DST rules as well,
529539
# so transfer time values to a utc constructor if necessary

activesupport/lib/active_support/values/time_zone.rb

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def seconds_to_utc_offset(seconds, colon = true)
203203
end
204204

205205
def find_tzinfo(name)
206-
TZInfo::Timezone.new(MAPPING[name] || name)
206+
TZInfo::Timezone.get(MAPPING[name] || name)
207207
end
208208

209209
alias_method :create, :new
@@ -273,7 +273,7 @@ def load_country_zones(code)
273273
memo
274274
end
275275
else
276-
create(tz_id, nil, TZInfo::Timezone.new(tz_id))
276+
create(tz_id, nil, TZInfo::Timezone.get(tz_id))
277277
end
278278
end.sort!
279279
end
@@ -302,11 +302,7 @@ def initialize(name, utc_offset = nil, tzinfo = nil)
302302

303303
# Returns the offset of this time zone from UTC in seconds.
304304
def utc_offset
305-
if @utc_offset
306-
@utc_offset
307-
else
308-
tzinfo.current_period.utc_offset if tzinfo && tzinfo.current_period
309-
end
305+
@utc_offset || tzinfo&.current_period&.base_utc_offset
310306
end
311307

312308
# Returns a formatted string of the offset from UTC, or an alternative
@@ -507,10 +503,17 @@ def yesterday
507503
end
508504

509505
# Adjust the given time to the simultaneous time in the time zone
510-
# represented by +self+. Returns a Time.utc() instance -- if you want an
511-
# ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead.
506+
# represented by +self+. Returns a local time with the appropriate offset
507+
# -- if you want an ActiveSupport::TimeWithZone instance, use
508+
# Time#in_time_zone() instead.
509+
#
510+
# As of tzinfo 2, utc_to_local returns a Time with a non-zero utc_offset.
511+
# See the `utc_to_local_returns_utc_offset_times` config for more info.
512512
def utc_to_local(time)
513-
tzinfo.utc_to_local(time)
513+
tzinfo.utc_to_local(time).yield_self do |t|
514+
ActiveSupport.utc_to_local_returns_utc_offset_times ?
515+
t : Time.utc(t.year, t.month, t.day, t.hour, t.min, t.sec, t.sec_fraction)
516+
end
514517
end
515518

516519
# Adjust the given time to the simultaneous time in UTC. Returns a

activesupport/test/time_zone_test.rb

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,15 @@ class TimeZoneTest < ActiveSupport::TestCase
1010

1111
def test_utc_to_local
1212
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
13-
assert_equal Time.utc(1999, 12, 31, 19), zone.utc_to_local(Time.utc(2000, 1)) # standard offset -0500
14-
assert_equal Time.utc(2000, 6, 30, 20), zone.utc_to_local(Time.utc(2000, 7)) # dst offset -0400
13+
14+
with_utc_to_local_returns_utc_offset_times false do
15+
assert_equal Time.utc(1999, 12, 31, 19), zone.utc_to_local(Time.utc(2000, 1)) # standard offset -0500
16+
assert_equal Time.utc(2000, 6, 30, 20), zone.utc_to_local(Time.utc(2000, 7)) # dst offset -0400
17+
end
18+
with_utc_to_local_returns_utc_offset_times true do
19+
assert_equal Time.new(1999, 12, 31, 19, 0, 0, -18000), zone.utc_to_local(Time.utc(2000, 1)) # standard offset -0500
20+
assert_equal Time.new(2000, 6, 30, 20, 0, 0, -14400), zone.utc_to_local(Time.utc(2000, 7)) # dst offset -0400
21+
end
1522
end
1623

1724
def test_local_to_utc
@@ -22,7 +29,7 @@ def test_local_to_utc
2229

2330
def test_period_for_local
2431
zone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
25-
assert_instance_of TZInfo::TimezonePeriod, zone.period_for_local(Time.utc(2000))
32+
assert_kind_of TZInfo::TimezonePeriod, zone.period_for_local(Time.utc(2000))
2633
end
2734

2835
ActiveSupport::TimeZone::MAPPING.each_key do |name|
@@ -54,7 +61,7 @@ def test_from_duration_to_map
5461

5562
define_method("test_utc_offset_for_#{name}") do
5663
period = zone.tzinfo.current_period
57-
assert_equal period.utc_offset, zone.utc_offset
64+
assert_equal period.base_utc_offset, zone.utc_offset
5865
end
5966
end
6067

@@ -96,8 +103,15 @@ def test_unknown_timezones_delegation_to_tzinfo
96103
zone = ActiveSupport::TimeZone["America/Montevideo"]
97104
assert_equal ActiveSupport::TimeZone, zone.class
98105
assert_equal zone.object_id, ActiveSupport::TimeZone["America/Montevideo"].object_id
99-
assert_equal Time.utc(2010, 1, 31, 22), zone.utc_to_local(Time.utc(2010, 2)) # daylight saving offset -0200
100-
assert_equal Time.utc(2010, 3, 31, 21), zone.utc_to_local(Time.utc(2010, 4)) # standard offset -0300
106+
107+
with_utc_to_local_returns_utc_offset_times false do
108+
assert_equal Time.utc(2010, 1, 31, 22), zone.utc_to_local(Time.utc(2010, 2)) # daylight saving offset -0200
109+
assert_equal Time.utc(2010, 3, 31, 21), zone.utc_to_local(Time.utc(2010, 4)) # standard offset -0300
110+
end
111+
with_utc_to_local_returns_utc_offset_times true do
112+
assert_equal Time.new(2010, 1, 31, 22, 0, 0, -7200), zone.utc_to_local(Time.utc(2010, 2)) # daylight saving offset -0200
113+
assert_equal Time.new(2010, 3, 31, 21, 0, 0, -10800), zone.utc_to_local(Time.utc(2010, 4)) # standard offset -0300
114+
end
101115
end
102116

103117
def test_today

activesupport/test/time_zone_test_helpers.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,12 @@ def with_tz_mappings(mappings)
3636
ActiveSupport::TimeZone::MAPPING.clear
3737
ActiveSupport::TimeZone::MAPPING.merge!(old_mappings)
3838
end
39+
40+
def with_utc_to_local_returns_utc_offset_times(value)
41+
old_tzinfo2_format = ActiveSupport.utc_to_local_returns_utc_offset_times
42+
ActiveSupport.utc_to_local_returns_utc_offset_times = value
43+
yield
44+
ensure
45+
ActiveSupport.utc_to_local_returns_utc_offset_times = old_tzinfo2_format
46+
end
3947
end

railties/lib/rails/application/configuration.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ def load_defaults(target_version)
178178
if respond_to?(:action_dispatch)
179179
action_dispatch.cookies_same_site_protection = :lax
180180
end
181+
182+
if respond_to?(:active_support)
183+
active_support.utc_to_local_returns_utc_offset_times = true
184+
end
181185
else
182186
raise "Unknown version #{target_version.to_s.inspect}"
183187
end

0 commit comments

Comments
 (0)