From cccd70b9448e7ba482b83f4dbddc6d2d60af4ee8 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Fri, 27 Jul 2018 20:13:02 +0100 Subject: [PATCH 01/81] Restrictions on timezones only apply to older (pre-1.9) Ruby releases. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 05bbbfe5..e190143f 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,9 @@ of `TZInfo::Timezone`) and convert a time in UTC to local New York time: local = tz.utc_to_local(Time.utc(2005,8,29,15,35,0)) Note that the local Time returned will have a UTC timezone (`local.zone` will -return `"UTC"`). This is because the Ruby Time class only supports two timezones: -UTC and the current system local timezone. +return `"UTC"`). This is because the Time class in older (but still supported by +TZInfo) versions of Ruby can only handle two timezones: UTC and the system local +timezone. To convert from a local time to UTC, the `local_to_utc` method can be used as follows: From fe9b2049eb44bb16841f32c998625e4b4dc0790e Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 30 Aug 2018 20:50:26 +0100 Subject: [PATCH 02/81] Return the correct seconds since the epoch value for strftime with %s. %s was being deferred to the result of the conversion to local. This is actually a UTC instance being used to represent local time, so the result would be the number of seconds since the epoch plus the total UTC offset. Override the handling of %s and use the passed in UTC instance to calculate the value. Resolves #91. --- lib/tzinfo/timezone.rb | 10 +++++++--- test/tc_timezone.rb | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/tzinfo/timezone.rb b/lib/tzinfo/timezone.rb index 518fe220..314ab6d3 100644 --- a/lib/tzinfo/timezone.rb +++ b/lib/tzinfo/timezone.rb @@ -573,15 +573,19 @@ def current_period_and_time # depending on the version of Ruby in use (for example, %:::z is only # supported by Time#strftime from MRI version 2.0.0 onwards.) def strftime(format, utc = Time.now.utc) + utc = TimeOrDateTime.wrap(utc) period = period_for_utc(utc) - local = period.to_local(utc) - local = Time.at(local).utc unless local.kind_of?(Time) || local.kind_of?(DateTime) + local_wrapped = period.to_local(utc) + local = local_wrapped.to_orig + local = local_wrapped.to_time unless local.kind_of?(Time) || local.kind_of?(DateTime) abbreviation = period.abbreviation.to_s.gsub(/%/, '%%') - format = format.gsub(/%(%*)(Z|:*z)/) do + format = format.gsub(/%(%*)([sZ]|:*z)/) do if $1.length.odd? # Escaped literal percent or series of percents. Pass on to strftime. "#$1%#$2" + elsif $2 == "s" + "#$1#{utc.to_i}" elsif $2 == "Z" "#$1#{abbreviation}" else diff --git a/test/tc_timezone.rb b/test/tc_timezone.rb index a4302bd2..e7f3cf4a 100644 --- a/test/tc_timezone.rb +++ b/test/tc_timezone.rb @@ -1260,6 +1260,7 @@ def test_strftime_datetime assert_equal('BST BST', tz.strftime('%Z %Z', dt)) assert_equal('BST %Z %BST %%Z %%BST', tz.strftime('%Z %%Z %%%Z %%%%Z %%%%%Z', dt)) assert_equal('+0100 +01:00 +01:00:00 +01 %::::z', tz.strftime('%z %:z %::z %:::z %::::z', dt)) + assert_equal('1153001522 %s %1153001522', tz.strftime('%s %%s %%%s', dt)) end def test_strftime_time @@ -1271,6 +1272,7 @@ def test_strftime_time assert_equal('BST BST', tz.strftime('%Z %Z', t)) assert_equal('BST %Z %BST %%Z %%BST', tz.strftime('%Z %%Z %%%Z %%%%Z %%%%%Z', t)) assert_equal('+0100 +01:00 +01:00:00 +01 %::::z', tz.strftime('%z %:z %::z %:::z %::::z', t)) + assert_equal('1153001522 %s %1153001522', tz.strftime('%s %%s %%%s', t)) end def test_strftime_int @@ -1282,6 +1284,7 @@ def test_strftime_int assert_equal('BST BST', tz.strftime('%Z %Z', i)) assert_equal('BST %Z %BST %%Z %%BST', tz.strftime('%Z %%Z %%%Z %%%%Z %%%%%Z', i)) assert_equal('+0100 +01:00 +01:00:00 +01 %::::z', tz.strftime('%z %:z %::z %:::z %::::z', i)) + assert_equal('1153001522 %s %1153001522', tz.strftime('%s %%s %%%s', i)) end def test_get_missing_data_source From 3b840ca962f5755bfef94c148776bd26e1b89662 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 30 Aug 2018 21:06:06 +0100 Subject: [PATCH 03/81] Fix a documentation typo. --- lib/tzinfo/timezone.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tzinfo/timezone.rb b/lib/tzinfo/timezone.rb index 314ab6d3..82edf1a1 100644 --- a/lib/tzinfo/timezone.rb +++ b/lib/tzinfo/timezone.rb @@ -571,7 +571,7 @@ def current_period_and_time # version 2.0.0, %z will be passed to Time#strftime and DateTime#strftime # instead. Some of the formatting options may cease to be available # depending on the version of Ruby in use (for example, %:::z is only - # supported by Time#strftime from MRI version 2.0.0 onwards.) + # supported by Time#strftime from MRI version 2.0.0 onwards). def strftime(format, utc = Time.now.utc) utc = TimeOrDateTime.wrap(utc) period = period_for_utc(utc) From 6f07fa5f8709103270f989d5fdd8f8328a600c7a Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 30 Aug 2018 21:06:24 +0100 Subject: [PATCH 04/81] Update to the latest Ruby, JRuby and Rubinius releases. --- .travis.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index e6594605..6fe82483 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,17 +12,18 @@ rvm: - 1.9.3-p551 - 2.0.0-p648 - 2.1.10 - - 2.2.9 - - 2.3.6 - - 2.4.3 - - 2.5.0 + - 2.2.10 + - 2.3.7 + - 2.4.4 + - 2.5.1 - ruby-head - jruby-18mode - jruby-1.7.27 - - jruby-9.1.15.0 + - jruby-9.1.17.0 + - jruby-9.2.0.0 - jruby-head - rbx-2.71828182 - - rbx-3.96 + - rbx-3.107 - ree matrix: allow_failures: From 7a660fcc1f198fd8b26aaa3a96d31accfeb57c65 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sat, 2 Jun 2018 17:25:04 +0100 Subject: [PATCH 05/81] Skip tests that fail due to Ruby bug 14060 on Ruby 2.4.4. On Ruby 2.4.4 in safe mode, require will fail with a SecurityError for any file that has not previously been loaded, regardless of whether the file name is tainted. See https://bugs.ruby-lang.org/issues/14060#note-5 Failing test job: https://travis-ci.org/tzinfo/tzinfo/jobs/422745006 (cherry picked from commit ed162f372db50ab398d915e9fcf7525c018e7011) --- test/tc_ruby_data_source.rb | 4 ++++ test/tc_timezone.rb | 4 ++++ test/test_utils.rb | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/test/tc_ruby_data_source.rb b/test/tc_ruby_data_source.rb index 0e9c3519..6e327fda 100644 --- a/test/tc_ruby_data_source.rb +++ b/test/tc_ruby_data_source.rb @@ -55,6 +55,8 @@ def test_load_timezone_info_minus end def test_load_timezone_info_tainted + skip_if_has_bug_14060 + safe_test do identifier = 'Europe/Amsterdam'.dup.taint assert(identifier.tainted?) @@ -65,6 +67,8 @@ def test_load_timezone_info_tainted end def test_load_timezone_info_tainted_and_frozen + skip_if_has_bug_14060 + safe_test do info = @data_source.load_timezone_info('Europe/Amsterdam'.dup.taint.freeze) assert_equal('Europe/Amsterdam', info.identifier) diff --git a/test/tc_timezone.rb b/test/tc_timezone.rb index e7f3cf4a..926c67fc 100644 --- a/test/tc_timezone.rb +++ b/test/tc_timezone.rb @@ -261,6 +261,8 @@ def test_get_tainted_and_frozen_loaded end def test_get_tainted_not_previously_loaded + skip_if_has_bug_14060 + safe_test do identifier = 'Europe/Andorra'.dup.taint assert(identifier.tainted?) @@ -271,6 +273,8 @@ def test_get_tainted_not_previously_loaded end def test_get_tainted_and_frozen_not_previously_loaded + skip_if_has_bug_14060 + safe_test do tz = Timezone.get('Europe/Amsterdam'.dup.taint.freeze) assert_equal('Europe/Amsterdam', tz.identifier) diff --git a/test/test_utils.rb b/test/test_utils.rb index 9b2409c6..0d1c7c02 100644 --- a/test/test_utils.rb +++ b/test/test_utils.rb @@ -96,6 +96,16 @@ def safe_test(options = {}) thread.join end end + + def skip_if_has_bug_14060 + # On Ruby 2.4.4 in safe mode, require will fail with a SecurityError for + # any file that has not previously been loaded, regardless of whether the + # file name is tainted. + # See https://bugs.ruby-lang.org/issues/14060#note-5. + if RUBY_ENGINE == 'ruby' && RUBY_VERSION == '2.4.4' + skip('Skipping test due to Ruby 2.4.4 being affected by Bug 14060 (see https://bugs.ruby-lang.org/issues/14060#note-5)') + end + end def assert_array_same_items(expected, actual, msg = nil) full_message = message(msg, '') { diff(expected, actual) } From f61cfe6d873eabcc06765ad7d0d8bdd3f33790a6 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 30 Aug 2018 21:36:54 +0100 Subject: [PATCH 06/81] Test that RUBY_ENGINE is defined. Fixes a test failure on Ruby 1.8.7: https://travis-ci.org/tzinfo/tzinfo/jobs/422752019 --- test/test_utils.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_utils.rb b/test/test_utils.rb index 0d1c7c02..dba96d2e 100644 --- a/test/test_utils.rb +++ b/test/test_utils.rb @@ -102,7 +102,7 @@ def skip_if_has_bug_14060 # any file that has not previously been loaded, regardless of whether the # file name is tainted. # See https://bugs.ruby-lang.org/issues/14060#note-5. - if RUBY_ENGINE == 'ruby' && RUBY_VERSION == '2.4.4' + if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby' && RUBY_VERSION == '2.4.4' skip('Skipping test due to Ruby 2.4.4 being affected by Bug 14060 (see https://bugs.ruby-lang.org/issues/14060#note-5)') end end From 769e26800160ee50459606fea9eba0b885c45e4f Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 22 Dec 2019 15:20:21 +0000 Subject: [PATCH 07/81] Fix SecurityErrors when loading data in safe mode. Recent Ruby releases will raise a "SecurityError: Insecure operation - require" exception when requiring a relative path name in safe mode. Calculate the full path to the data file for use in require. Similar to the changes made for 2.0.0, but doesn't raise an exception if tzinfo/data can't be found when RubyDataSource is initialized. --- lib/tzinfo/ruby_data_source.rb | 37 ++++++++++++++++++---------------- test/tc_ruby_data_source.rb | 16 +++++++++++++++ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/lib/tzinfo/ruby_data_source.rb b/lib/tzinfo/ruby_data_source.rb index 2635c264..6ab5d3a8 100644 --- a/lib/tzinfo/ruby_data_source.rb +++ b/lib/tzinfo/ruby_data_source.rb @@ -15,6 +15,24 @@ class RubyDataSource < DataSource # Whether the country index has been loaded yet. @@country_index_loaded = false + # Initializes a new RubyDataSource instance. + def initialize + tzinfo_data = File.join('tzinfo', 'data') + begin + require(tzinfo_data) + + data_file = File.join('', 'tzinfo', 'data.rb') + path = $".reverse_each.detect {|p| p.end_with?(data_file) } + if path + @base_path = File.join(File.dirname(path), 'data').untaint + else + @base_path = tzinfo_data + end + rescue LoadError + @base_path = tzinfo_data + end + end + # Returns a TimezoneInfo instance for a given identifier. # Raises InvalidTimezoneIdentifier if the timezone is not found or the # identifier is invalid. @@ -93,27 +111,17 @@ def require_definition(identifier) end # Requires an index by its name. - def self.require_index(name) + def require_index(name) require_data(*['indexes', name]) end # Requires a file from tzinfo/data. def require_data(*file) - self.class.require_data(*file) - end - - # Requires a file from tzinfo/data. - def self.require_data(*file) - require File.join('tzinfo', 'data', *file) + require(File.join(@base_path, *file)) end # Loads in the index of timezones if it hasn't already been loaded. def load_timezone_index - self.class.load_timezone_index - end - - # Loads in the index of timezones if it hasn't already been loaded. - def self.load_timezone_index unless @@timezone_index_loaded require_index('timezones') @@timezone_index_loaded = true @@ -122,11 +130,6 @@ def self.load_timezone_index # Loads in the index of countries if it hasn't already been loaded. def load_country_index - self.class.load_country_index - end - - # Loads in the index of countries if it hasn't already been loaded. - def self.load_country_index unless @@country_index_loaded require_index('countries') @@country_index_loaded = true diff --git a/test/tc_ruby_data_source.rb b/test/tc_ruby_data_source.rb index 6e327fda..aacded5c 100644 --- a/test/tc_ruby_data_source.rb +++ b/test/tc_ruby_data_source.rb @@ -7,6 +7,22 @@ def setup @data_source = RubyDataSource.new end + def test_initialize_not_found + # A failure to load tzinfo/data in initialize will not cause an exception. + # Attempts to load data files will subsequently fail. + code = <<-EOF + begin + ds = TZInfo::RubyDataSource.new + puts 'Initialized' + ds.load_timezone_info('Europe/London') + rescue Exception => e + puts e.class + end + EOF + + assert_sub_process_returns(['Initialized', 'TZInfo::InvalidTimezoneIdentifier'], code) + end + def test_load_timezone_info_data info = @data_source.load_timezone_info('Europe/London') assert_kind_of(DataTimezoneInfo, info) From 9fd78820d83b7ba42a23665e58de57c6cd8e5619 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 22 Dec 2019 15:23:57 +0000 Subject: [PATCH 08/81] Remove the unused REQUIRE_PATH constant from RubyDataSource. --- lib/tzinfo/ruby_data_source.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/tzinfo/ruby_data_source.rb b/lib/tzinfo/ruby_data_source.rb index 6ab5d3a8..76bcc616 100644 --- a/lib/tzinfo/ruby_data_source.rb +++ b/lib/tzinfo/ruby_data_source.rb @@ -6,9 +6,6 @@ module TZInfo # # TZInfo::DataSource.set(:ruby) class RubyDataSource < DataSource - # Base path for require. - REQUIRE_PATH = File.join('tzinfo', 'data', 'definitions') - # Whether the timezone index has been loaded yet. @@timezone_index_loaded = false From c1f3b2ab1ad917c5d629b1222db1435f14a51e7a Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 22 Dec 2019 22:22:33 +0000 Subject: [PATCH 09/81] Fix test failures on Ruby 1.8.7 caused by DateTime issues. Ruby 1.8.7 (built with GCC 8.3.0 on Ubuntu 19.04) fails to perform DateTime arithmetic and comparison operations correctly when sub-seconds are used. Disable the affected tests. --- test/tc_time_or_datetime.rb | 18 ++++++++++++------ test/tc_transition_data_timezone_info.rb | 12 +++++++++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/test/tc_time_or_datetime.rb b/test/tc_time_or_datetime.rb index 9f38bfbe..7dad11ce 100644 --- a/test/tc_time_or_datetime.rb +++ b/test/tc_time_or_datetime.rb @@ -4,6 +4,12 @@ include TZInfo class TCTimeOrDateTime < Minitest::Test + # Ruby 1.8.7 (built with GCC 8.3.0 on Ubuntu 19.04) fails to perform DateTime + # arithmetic operations correctly when sub-seconds are used. Detect this and + # disable the affected tests. + DATETIME_SUBSECOND_ARITHMETIC_IS_BROKEN = (DateTime.new(2019, 12, 22, 15, 0, Rational(1, 1000)) + Rational(1, 86400)).strftime('%Q') != '1577026801001' + puts "DateTime sub-second arithmetic is broken on this platform" if DATETIME_SUBSECOND_ARITHMETIC_IS_BROKEN + def test_initialize_time assert_nothing_raised do TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 721000)) @@ -500,12 +506,12 @@ def test_add assert_equal(Time.utc(2006, 3, 24, 15, 32, 4), (TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)) + 1).to_orig) assert_equal(Time.utc(2006, 3, 24, 15, 32, 4, 721000), (TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 721000)) + 1).to_orig) assert_equal(DateTime.new(2006, 3, 24, 15, 32, 4), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3)) + 1).to_orig) - assert_equal(DateTime.new(2006, 3, 24, 15, 32, 4 + Rational(721, 1000)), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))) + 1).to_orig) + assert_equal(DateTime.new(2006, 3, 24, 15, 32, 4 + Rational(721, 1000)), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))) + 1).to_orig) unless DATETIME_SUBSECOND_ARITHMETIC_IS_BROKEN assert_equal(1143214324, (TimeOrDateTime.new(1143214323) + 1).to_orig) assert_equal(Time.utc(2006, 3, 24, 15, 32, 2), (TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)) + (-1)).to_orig) assert_equal(Time.utc(2006, 3, 24, 15, 32, 2, 721000), (TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 721000)) + (-1)).to_orig) assert_equal(DateTime.new(2006, 3, 24, 15, 32, 2), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3)) + (-1)).to_orig) - assert_equal(DateTime.new(2006, 3, 24, 15, 32, 2 + Rational(721, 1000)), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))) + (-1)).to_orig) + assert_equal(DateTime.new(2006, 3, 24, 15, 32, 2 + Rational(721, 1000)), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))) + (-1)).to_orig) unless DATETIME_SUBSECOND_ARITHMETIC_IS_BROKEN assert_equal(1143214322, (TimeOrDateTime.new(1143214323) + (-1)).to_orig) end @@ -518,12 +524,12 @@ def test_subtract assert_equal(Time.utc(2006, 3, 24, 15, 32, 2), (TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)) - 1).to_orig) assert_equal(Time.utc(2006, 3, 24, 15, 32, 2, 721000), (TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 721000)) - 1).to_orig) assert_equal(DateTime.new(2006, 3, 24, 15, 32, 2), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3)) - 1).to_orig) - assert_equal(DateTime.new(2006, 3, 24, 15, 32, 2 + Rational(721, 1000)), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))) - 1).to_orig) + assert_equal(DateTime.new(2006, 3, 24, 15, 32, 2 + Rational(721, 1000)), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))) - 1).to_orig) unless DATETIME_SUBSECOND_ARITHMETIC_IS_BROKEN assert_equal(1143214322, (TimeOrDateTime.new(1143214323) - 1).to_orig) assert_equal(Time.utc(2006, 3, 24, 15, 32, 4), (TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)) - (-1)).to_orig) assert_equal(Time.utc(2006, 3, 24, 15, 32, 4, 721000), (TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 721000)) - (-1)).to_orig) assert_equal(DateTime.new(2006, 3, 24, 15, 32, 4), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3)) - (-1)).to_orig) - assert_equal(DateTime.new(2006, 3, 24, 15, 32, 4 + Rational(721, 1000)), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))) - (-1)).to_orig) + assert_equal(DateTime.new(2006, 3, 24, 15, 32, 4 + Rational(721, 1000)), (TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))) - (-1)).to_orig) unless DATETIME_SUBSECOND_ARITHMETIC_IS_BROKEN assert_equal(1143214324, (TimeOrDateTime.new(1143214323) - (-1)).to_orig) end @@ -536,12 +542,12 @@ def test_add_with_convert assert_equal(Time.utc(2006, 3, 24, 15, 32, 4), TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)).add_with_convert(1).to_orig) assert_equal(Time.utc(2006, 3, 24, 15, 32, 4, 721000), TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 721000)).add_with_convert(1).to_orig) assert_equal(DateTime.new(2006, 3, 24, 15, 32, 4), TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3)).add_with_convert(1).to_orig) - assert_equal(DateTime.new(2006, 3, 24, 15, 32, 4 + Rational(721, 1000)), TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))).add_with_convert(1).to_orig) + assert_equal(DateTime.new(2006, 3, 24, 15, 32, 4 + Rational(721, 1000)), TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))).add_with_convert(1).to_orig) unless DATETIME_SUBSECOND_ARITHMETIC_IS_BROKEN assert_equal(1143214324, TimeOrDateTime.new(1143214323).add_with_convert(1).to_orig) assert_equal(Time.utc(2006, 3, 24, 15, 32, 2), TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)).add_with_convert(-1).to_orig) assert_equal(Time.utc(2006, 3, 24, 15, 32, 2, 721000), TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3, 721000)).add_with_convert(-1).to_orig) assert_equal(DateTime.new(2006, 3, 24, 15, 32, 2), TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3)).add_with_convert(-1).to_orig) - assert_equal(DateTime.new(2006, 3, 24, 15, 32, 2 + Rational(721, 1000)), TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))).add_with_convert(-1).to_orig) + assert_equal(DateTime.new(2006, 3, 24, 15, 32, 2 + Rational(721, 1000)), TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3 + Rational(721, 1000))).add_with_convert(-1).to_orig) unless DATETIME_SUBSECOND_ARITHMETIC_IS_BROKEN assert_equal(1143214322, TimeOrDateTime.new(1143214323).add_with_convert(-1).to_orig) if RubyCoreSupport.time_supports_negative diff --git a/test/tc_transition_data_timezone_info.rb b/test/tc_transition_data_timezone_info.rb index a1614eee..cc4af72b 100644 --- a/test/tc_transition_data_timezone_info.rb +++ b/test/tc_transition_data_timezone_info.rb @@ -359,7 +359,17 @@ def test_transitions_up_to assert_equal([t1,t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,Rational(DATETIME_RESOLUTION,1000000)))) assert_equal([t1,t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,1), DateTime.new(2010,4,1,1,0,0))) assert_equal([t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,1), DateTime.new(2010,4,1,1,0,1))) - assert_equal([t2,t3,t4,t5], dti.transitions_up_to(DateTime.new(2011,10,1,1,0,1), DateTime.new(2010,4,1,1,0,Rational(DATETIME_RESOLUTION,1000000)))) + + # Ruby 1.8.7 (built with GCC 8.3.0 on Ubuntu 19.04) fails to perform + # DateTime comparisons correctly when sub-seconds are used. + to = DateTime.new(2011,10,1,1,0,1) + from = DateTime.new(2010,4,1,1,0,Rational(DATETIME_RESOLUTION,1000000)) + if to > from + assert_equal([t2,t3,t4,t5], dti.transitions_up_to(to, from)) + else + puts "This platform does not consider the DateTime #{to.strftime('%Y-%m-%d %H:%m:%S.%N')} to be later than the DateTime #{from.strftime('%Y-%m-%d %H:%m:%S.%N')}" + end + assert_equal([t5], dti.transitions_up_to(DateTime.new(2015,1,1,0,0,0), DateTime.new(2011,10,1,1,0,0))) assert_equal([], dti.transitions_up_to(DateTime.new(2015,1,1,0,0,0), DateTime.new(2011,10,1,1,0,1))) end From 3d4c491f472301e0ae5e7c446b96a3d10591df2c Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 22 Dec 2019 23:02:02 +0000 Subject: [PATCH 10/81] Suppress deprecation warnings due to Object#untaint on Ruby 2.7. Adds a no-op `Object#untaint` refinement that is used if Object#untaint does not exist or is non-functional. Add a similar `Object#taint` refinement for use in test cases. Skip safe_tests that require `Object#taint` to set `Object#tainted?`. This is a backport of commits 57f77deace76ac45dc5808f0dd70b22d67aafb33 and 26db7355c0b4feb42378be529c30ad606e4a5878 from master. --- lib/tzinfo/ruby_core_support.rb | 21 +++++++++++++++++++++ lib/tzinfo/ruby_data_source.rb | 2 ++ lib/tzinfo/zoneinfo_data_source.rb | 2 ++ lib/tzinfo/zoneinfo_timezone_info.rb | 2 ++ test/tc_country.rb | 6 ++++-- test/tc_ruby_data_source.rb | 6 ++++-- test/tc_timezone.rb | 6 ++++-- test/tc_zoneinfo_data_source.rb | 7 +++++-- test/tc_zoneinfo_timezone_info.rb | 2 ++ test/test_utils.rb | 21 ++++++++++++++++++--- test/ts_all_zoneinfo.rb | 4 +++- 11 files changed, 67 insertions(+), 12 deletions(-) diff --git a/lib/tzinfo/ruby_core_support.rb b/lib/tzinfo/ruby_core_support.rb index c97819e1..e2de2083 100644 --- a/lib/tzinfo/ruby_core_support.rb +++ b/lib/tzinfo/ruby_core_support.rb @@ -142,5 +142,26 @@ def self.open_file(file_name, mode, opts, &block) File.open(file_name, mode, opts, &block) end end + + + # Object#untaint is a deprecated no-op in Ruby >= 2.7 and will be removed in + # 3.0. Add a refinement to either silence the warning, or supply the method + # if needed. + old_verbose = $VERBOSE + $VERBOSE = false + begin + o = Object.new + if [:taint, :untaint, :tainted?].none? {|m| o.respond_to?(m) } || !o.taint.tainted? + module UntaintExt + refine Object do + def untaint + self + end + end + end + end + ensure + $VERBOSE = old_verbose + end end end diff --git a/lib/tzinfo/ruby_data_source.rb b/lib/tzinfo/ruby_data_source.rb index 76bcc616..0452c7d1 100644 --- a/lib/tzinfo/ruby_data_source.rb +++ b/lib/tzinfo/ruby_data_source.rb @@ -1,4 +1,6 @@ module TZInfo + using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt) + # A DataSource that loads data from the set of Ruby modules included in the # TZInfo::Data library (tzinfo-data gem). # diff --git a/lib/tzinfo/zoneinfo_data_source.rb b/lib/tzinfo/zoneinfo_data_source.rb index b38fbf68..2126e039 100644 --- a/lib/tzinfo/zoneinfo_data_source.rb +++ b/lib/tzinfo/zoneinfo_data_source.rb @@ -1,4 +1,6 @@ module TZInfo + using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt) + # An InvalidZoneinfoDirectory exception is raised if the DataSource is # set to a specific zoneinfo path, which is not a valid zoneinfo directory # (i.e. a directory containing index files named iso3166.tab and zone.tab diff --git a/lib/tzinfo/zoneinfo_timezone_info.rb b/lib/tzinfo/zoneinfo_timezone_info.rb index d145614d..668ded55 100644 --- a/lib/tzinfo/zoneinfo_timezone_info.rb +++ b/lib/tzinfo/zoneinfo_timezone_info.rb @@ -1,4 +1,6 @@ module TZInfo + using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt) + # An InvalidZoneinfoFile exception is raised if an attempt is made to load an # invalid zoneinfo file. class InvalidZoneinfoFile < StandardError diff --git a/test/tc_country.rb b/test/tc_country.rb index 4de8c5b7..211cb3fb 100644 --- a/test/tc_country.rb +++ b/test/tc_country.rb @@ -2,6 +2,8 @@ include TZInfo +using TaintExt if Module.const_defined?(:TaintExt) + class TCCountry < Minitest::Test def setup @orig_data_source = DataSource.get @@ -46,7 +48,7 @@ def test_get_case def test_get_tainted_loaded Country.get('GB') - safe_test do + safe_test(:unavailable => :skip) do code = 'GB'.dup.taint assert(code.tainted?) country = Country.get(code) @@ -65,7 +67,7 @@ def test_get_tainted_and_frozen_loaded end def test_get_tainted_not_previously_loaded - safe_test do + safe_test(:unavailable => :skip) do code = 'GB'.dup.taint assert(code.tainted?) country = Country.get(code) diff --git a/test/tc_ruby_data_source.rb b/test/tc_ruby_data_source.rb index aacded5c..67e90e81 100644 --- a/test/tc_ruby_data_source.rb +++ b/test/tc_ruby_data_source.rb @@ -2,6 +2,8 @@ include TZInfo +using TaintExt if Module.const_defined?(:TaintExt) + class TCRubyDataSource < Minitest::Test def setup @data_source = RubyDataSource.new @@ -73,7 +75,7 @@ def test_load_timezone_info_minus def test_load_timezone_info_tainted skip_if_has_bug_14060 - safe_test do + safe_test(:unavailable => :skip) do identifier = 'Europe/Amsterdam'.dup.taint assert(identifier.tainted?) info = @data_source.load_timezone_info(identifier) @@ -139,7 +141,7 @@ def test_load_country_info_case end def test_load_country_info_tainted - safe_test do + safe_test(:unavailable => :skip) do code = 'NL'.dup.taint assert(code.tainted?) info = @data_source.load_country_info(code) diff --git a/test/tc_timezone.rb b/test/tc_timezone.rb index 926c67fc..82cef9c7 100644 --- a/test/tc_timezone.rb +++ b/test/tc_timezone.rb @@ -2,6 +2,8 @@ include TZInfo +using TaintExt if Module.const_defined?(:TaintExt) + class TCTimezone < Minitest::Test class BlockCalled < StandardError @@ -242,7 +244,7 @@ def test_get_proxy_invalid def test_get_tainted_loaded Timezone.get('Europe/Andorra') - safe_test do + safe_test(:unavailable => :skip) do identifier = 'Europe/Andorra'.dup.taint assert(identifier.tainted?) tz = Timezone.get(identifier) @@ -263,7 +265,7 @@ def test_get_tainted_and_frozen_loaded def test_get_tainted_not_previously_loaded skip_if_has_bug_14060 - safe_test do + safe_test(:unavailable => :skip) do identifier = 'Europe/Andorra'.dup.taint assert(identifier.tainted?) tz = Timezone.get(identifier) diff --git a/test/tc_zoneinfo_data_source.rb b/test/tc_zoneinfo_data_source.rb index 2b49d8e9..42c6aa89 100644 --- a/test/tc_zoneinfo_data_source.rb +++ b/test/tc_zoneinfo_data_source.rb @@ -7,6 +7,9 @@ include TZInfo +using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt) +using TaintExt if Module.const_defined?(:TaintExt) + class TCZoneinfoDataSource < Minitest::Test ZONEINFO_DIR = File.join(File.expand_path(File.dirname(__FILE__)), 'zoneinfo').untaint @@ -653,7 +656,7 @@ def test_load_timezone_info_invalid_file_2 end def test_load_timezone_info_tainted - safe_test do + safe_test(:unavailable => :skip) do identifier = 'Europe/Amsterdam'.dup.taint assert(identifier.tainted?) info = @data_source.load_timezone_info(identifier) @@ -840,7 +843,7 @@ def test_load_country_info_case end def test_load_country_info_tainted - safe_test do + safe_test(:unavailable => :skip) do code = 'NL'.dup.taint assert(code.tainted?) info = @data_source.load_country_info(code) diff --git a/test/tc_zoneinfo_timezone_info.rb b/test/tc_zoneinfo_timezone_info.rb index 00c10811..32c7f504 100644 --- a/test/tc_zoneinfo_timezone_info.rb +++ b/test/tc_zoneinfo_timezone_info.rb @@ -5,6 +5,8 @@ include TZInfo +using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt) + class TCZoneinfoTimezoneInfo < Minitest::Test begin diff --git a/test/test_utils.rb b/test/test_utils.rb index dba96d2e..a8cec197 100644 --- a/test/test_utils.rb +++ b/test/test_utils.rb @@ -1,4 +1,6 @@ -TESTS_DIR = File.expand_path(File.dirname(__FILE__)).untaint +tests_dir = File.expand_path(File.dirname(__FILE__)) +tests_dir.untaint if RUBY_VERSION < '2.7' +TESTS_DIR = tests_dir TZINFO_LIB_DIR = File.expand_path(File.join(TESTS_DIR, '..', 'lib')) TZINFO_TEST_DATA_DIR = File.join(TESTS_DIR, 'tzinfo-data') TZINFO_TEST_ZONEINFO_DIR = File.join(TESTS_DIR, 'zoneinfo') @@ -55,8 +57,8 @@ def without_warnings end def safe_test(options = {}) - # JRuby and Rubinus don't support SAFE levels. - available = !(defined?(RUBY_ENGINE) && %w(jruby rbx).include?(RUBY_ENGINE)) + # Ruby >= 2.7, JRuby and Rubinus don't support SAFE levels. + available = RUBY_VERSION < '2.7' && !(defined?(RUBY_ENGINE) && %w(jruby rbx).include?(RUBY_ENGINE)) if available || options[:unavailable] != :skip thread = Thread.new do @@ -162,6 +164,19 @@ def assert_nothing_raised(msg = nil) end +# Object#taint is a deprecated no-op in Ruby 2.7 and outputs a warning. It will +# be removed in 3.0. Silence the warning or supply a replacement. +if TZInfo::RubyCoreSupport.const_defined?(:UntaintExt) + module TaintExt + refine Object do + def taint + self + end + end + end +end + + # JRuby 1.7.5 to 1.7.9 consider DateTime instances that differ by less than # 1 millisecond to be equivalent (https://github.com/jruby/jruby/issues/1311). # diff --git a/test/ts_all_zoneinfo.rb b/test/ts_all_zoneinfo.rb index 95c4685f..ae1ba15e 100644 --- a/test/ts_all_zoneinfo.rb +++ b/test/ts_all_zoneinfo.rb @@ -2,6 +2,8 @@ # Use a zoneinfo directory containing files needed by the tests. # The symlinks in this directory are set up in test_utils.rb. -TZInfo::DataSource.set(:zoneinfo, File.join(File.expand_path(File.dirname(__FILE__)), 'zoneinfo').untaint) +zoneinfo_path = File.join(File.expand_path(File.dirname(__FILE__)), 'zoneinfo') +zoneinfo_path.untaint if RUBY_VERSION < '2.7' +TZInfo::DataSource.set(:zoneinfo, zoneinfo_path) require File.join(File.expand_path(File.dirname(__FILE__)), 'ts_all.rb') From 54fce8c1f61ba3b891311f7c7befc32a52ea4a30 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 17:01:09 +0000 Subject: [PATCH 11/81] Eliminate a warning when calling File.open with keyword arguments. --- lib/tzinfo/ruby_core_support.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/tzinfo/ruby_core_support.rb b/lib/tzinfo/ruby_core_support.rb index e2de2083..5d8e219d 100644 --- a/lib/tzinfo/ruby_core_support.rb +++ b/lib/tzinfo/ruby_core_support.rb @@ -137,10 +137,19 @@ def self.force_encoding(str, encoding) def self.open_file(file_name, mode, opts, &block) File.open(file_name, mode, &block) end - else + elsif RUBY_VERSION =~ /\A1\.9\./ def self.open_file(file_name, mode, opts, &block) File.open(file_name, mode, opts, &block) end + else + # Evaluate method as a string because **opts isn't valid syntax prior to + # Ruby 2.0. + eval(<<-EOF + def self.open_file(file_name, mode, opts, &block) + File.open(file_name, mode, **opts, &block) + end + EOF + ) end From 9dd26283a8f47898b1386bf3ddac3f419484cabb Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 17:11:00 +0000 Subject: [PATCH 12/81] Allow newer versions of Rake that fix warnings with Ruby 2.7. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 8156681b..28518dce 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,6 @@ source "https://rubygems.org" gemspec group :test do - gem 'rake', '~> 10.5' + gem 'rake', ['>= 10.5', '< 14'] gem 'minitest', '~> 5.0' end From 05d476873124017c48be56c2958f5b12ccc3c0f4 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 17:15:20 +0000 Subject: [PATCH 13/81] Prevent bundler from attempting to use version minitest v5.12.0. It is only compatible with Ruby >= 2.0.0, but does not specify a minimum Ruby version. --- Gemfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 28518dce..ec0d5467 100644 --- a/Gemfile +++ b/Gemfile @@ -4,5 +4,8 @@ gemspec group :test do gem 'rake', ['>= 10.5', '< 14'] - gem 'minitest', '~> 5.0' + + # Exclude Minitest v5.12.0. It is only compatible with Ruby >= 2.0.0, but does + # not specify a minimum Ruby version. + gem 'minitest', ['>= 5.11.3', '< 6', '!= 5.12.0'] end From 1e2279c2eaa6a2b8c789073f3c7f11c57052636a Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 17:19:55 +0000 Subject: [PATCH 14/81] Update Travis CI Ruby versions. Add RubyGems and Bundler version restrictions from master. --- .travis.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6fe82483..e6a57a07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,10 @@ language: ruby dist: trusty sudo: false before_install: - - gem update bundler + - if [[ $TRAVIS_RUBY_VERSION =~ ^(1|2\.[012]|jruby-1)\. ]]; then gem install rubygems-update --version '~> 2.7' --no-document && update_rubygems; else gem update --system; fi + - gem --version + - if [[ $TRAVIS_RUBY_VERSION =~ ^(1|2\.[012]|jruby-1)\. ]]; then gem install bundler --version '~> 1.17'; else gem install bundler; fi + - bundle --version before_script: - bundle update cache: bundler @@ -13,14 +16,16 @@ rvm: - 2.0.0-p648 - 2.1.10 - 2.2.10 - - 2.3.7 - - 2.4.4 - - 2.5.1 + - 2.3.8 + - 2.4.9 + - 2.5.7 + - 2.6.5 + - 2.7.0-preview3 - ruby-head - jruby-18mode - jruby-1.7.27 - jruby-9.1.17.0 - - jruby-9.2.0.0 + - jruby-9.2.9.0 - jruby-head - rbx-2.71828182 - rbx-3.107 From d9df6bca683ad37a34ec3b53d5cdbe22083b2c06 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 17:20:48 +0000 Subject: [PATCH 15/81] Enable verbose test output. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index e6a57a07..a601130b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,9 @@ before_install: before_script: - bundle update cache: bundler +env: + global: + - TESTOPTS=--verbose rvm: - 1.8.7-head - 1.9.2-p330 From cde0846991d912393ad7bcc4d40ca09308907813 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 17:27:34 +0000 Subject: [PATCH 16/81] Run CI tests on Windows with AppVeyor. The config has been copied from commit a3b30319587ed780de2bb328e49e077c6132a21b on master. --- appveyor.yml | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..ecb545d3 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,94 @@ +version: "{build}-{branch}" + +cache: + - vendor/bundle + +environment: + TESTOPTS: --verbose + + matrix: + # Ruby 1.8.7 isn't available on AppVeyor. + + - RUBY_ENGINE: ruby + RUBY_VERSION: 193 + SSL_CERT_FILE: C:\Ruby193\lib\ruby\1.9.1\rubygems\ssl_certs\ca-bundle.pem + + - RUBY_ENGINE: ruby + RUBY_VERSION: 200 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 200-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 21 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 21-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 22 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 22-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 23 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 23-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 24 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 24-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 25 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 25-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 26 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 26-x64 + + - RUBY_ENGINE: jruby + JRUBY_VERSION: 1.7.27 + + - RUBY_ENGINE: jruby + JRUBY_VERSION: 9.1.17.0 + + - RUBY_ENGINE: jruby + JRUBY_VERSION: 9.2.9.0 + +install: + - if not exist vendor mkdir vendor + - if %RUBY_ENGINE%==jruby appveyor DownloadFile https://repo1.maven.org/maven2/org/jruby/jruby-dist/%JRUBY_VERSION%/jruby-dist-%JRUBY_VERSION%-bin.zip -FileName vendor\jruby-dist-%JRUBY_VERSION%-bin.zip + - if %RUBY_ENGINE%==jruby 7z x vendor\jruby-dist-%JRUBY_VERSION%-bin.zip -ovendor -y + - if %RUBY_ENGINE%==jruby set PATH=C:\projects\tzinfo\vendor\jruby-%JRUBY_VERSION%\bin;%PATH% + - if %RUBY_ENGINE%==jruby if "%JRUBY_VERSION:~0,2%"=="1." gem install bundler --version "~> 1.17" + - if %RUBY_ENGINE%==jruby if not "%JRUBY_VERSION:~0,2%"=="1." gem install bundler + - if v%RUBY_VERSION%==v193 appveyor DownloadFile https://github.com/philr/rubyinstaller/releases/download/1.9.3-p551-openssl-tls-1.1-1.2/ruby-1.9.3-p551-i386-mingw32.7z -FileName vendor\ruby-1.9.3-p551-i386-mingw32.7z + - if v%RUBY_VERSION%==v193 7z e vendor\ruby-1.9.3-p551-i386-mingw32.7z -ovendor ruby-1.9.3-p551-i386-mingw32\bin\libeay32.dll ruby-1.9.3-p551-i386-mingw32\bin\ssleay32.dll ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\i386-mingw32\openssl.so ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\rubygems\ssl_certs\ca-bundle.pem + - if v%RUBY_VERSION%==v193 copy /Y vendor\*eay32.dll C:\Ruby193\bin + - if v%RUBY_VERSION%==v193 copy /Y vendor\openssl.so C:\Ruby193\lib\ruby\1.9.1\i386-mingw32 + - if v%RUBY_VERSION%==v193 copy /Y vendor\ca-bundle.pem C:\Ruby193\lib\ruby\1.9.1\rubygems\ssl_certs + - if %RUBY_ENGINE%==ruby set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% + - bundle config --local path vendor/bundle + - bundle update + +build: off + +before_test: + - "%RUBY_ENGINE% -v" + - gem -v + - bundle -v + +test_script: + - bundle exec rake test + +after_test: + - bundle clean From 2bb387555a6382bfc691c0691997b2670957333e Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 17:38:10 +0000 Subject: [PATCH 17/81] Update to Ruby v2.7.0-rc2. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a601130b..ca211a8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ rvm: - 2.4.9 - 2.5.7 - 2.6.5 - - 2.7.0-preview3 + - 2.7.0-rc2 - ruby-head - jruby-18mode - jruby-1.7.27 From e23618693d93dee141f7aea405a79bf15d54de4c Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 17:41:58 +0000 Subject: [PATCH 18/81] Simplify minitest version constraint. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index ec0d5467..065bafe9 100644 --- a/Gemfile +++ b/Gemfile @@ -7,5 +7,5 @@ group :test do # Exclude Minitest v5.12.0. It is only compatible with Ruby >= 2.0.0, but does # not specify a minimum Ruby version. - gem 'minitest', ['>= 5.11.3', '< 6', '!= 5.12.0'] + gem 'minitest', ['~> 5.11', '!= 5.12.0'] end From c682d24e2ac81dfc5010eb01ff0f1a176d1fd3d5 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 17:54:23 +0000 Subject: [PATCH 19/81] Convert to UNIX line endings. --- appveyor.yml | 188 +++++++++++++++++++++++++-------------------------- 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ecb545d3..a5baef03 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,94 +1,94 @@ -version: "{build}-{branch}" - -cache: - - vendor/bundle - -environment: - TESTOPTS: --verbose - - matrix: - # Ruby 1.8.7 isn't available on AppVeyor. - - - RUBY_ENGINE: ruby - RUBY_VERSION: 193 - SSL_CERT_FILE: C:\Ruby193\lib\ruby\1.9.1\rubygems\ssl_certs\ca-bundle.pem - - - RUBY_ENGINE: ruby - RUBY_VERSION: 200 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 200-x64 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 21 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 21-x64 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 22 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 22-x64 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 23 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 23-x64 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 24 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 24-x64 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 25 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 25-x64 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 26 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 26-x64 - - - RUBY_ENGINE: jruby - JRUBY_VERSION: 1.7.27 - - - RUBY_ENGINE: jruby - JRUBY_VERSION: 9.1.17.0 - - - RUBY_ENGINE: jruby - JRUBY_VERSION: 9.2.9.0 - -install: - - if not exist vendor mkdir vendor - - if %RUBY_ENGINE%==jruby appveyor DownloadFile https://repo1.maven.org/maven2/org/jruby/jruby-dist/%JRUBY_VERSION%/jruby-dist-%JRUBY_VERSION%-bin.zip -FileName vendor\jruby-dist-%JRUBY_VERSION%-bin.zip - - if %RUBY_ENGINE%==jruby 7z x vendor\jruby-dist-%JRUBY_VERSION%-bin.zip -ovendor -y - - if %RUBY_ENGINE%==jruby set PATH=C:\projects\tzinfo\vendor\jruby-%JRUBY_VERSION%\bin;%PATH% - - if %RUBY_ENGINE%==jruby if "%JRUBY_VERSION:~0,2%"=="1." gem install bundler --version "~> 1.17" - - if %RUBY_ENGINE%==jruby if not "%JRUBY_VERSION:~0,2%"=="1." gem install bundler - - if v%RUBY_VERSION%==v193 appveyor DownloadFile https://github.com/philr/rubyinstaller/releases/download/1.9.3-p551-openssl-tls-1.1-1.2/ruby-1.9.3-p551-i386-mingw32.7z -FileName vendor\ruby-1.9.3-p551-i386-mingw32.7z - - if v%RUBY_VERSION%==v193 7z e vendor\ruby-1.9.3-p551-i386-mingw32.7z -ovendor ruby-1.9.3-p551-i386-mingw32\bin\libeay32.dll ruby-1.9.3-p551-i386-mingw32\bin\ssleay32.dll ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\i386-mingw32\openssl.so ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\rubygems\ssl_certs\ca-bundle.pem - - if v%RUBY_VERSION%==v193 copy /Y vendor\*eay32.dll C:\Ruby193\bin - - if v%RUBY_VERSION%==v193 copy /Y vendor\openssl.so C:\Ruby193\lib\ruby\1.9.1\i386-mingw32 - - if v%RUBY_VERSION%==v193 copy /Y vendor\ca-bundle.pem C:\Ruby193\lib\ruby\1.9.1\rubygems\ssl_certs - - if %RUBY_ENGINE%==ruby set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% - - bundle config --local path vendor/bundle - - bundle update - -build: off - -before_test: - - "%RUBY_ENGINE% -v" - - gem -v - - bundle -v - -test_script: - - bundle exec rake test - -after_test: - - bundle clean +version: "{build}-{branch}" + +cache: + - vendor/bundle + +environment: + TESTOPTS: --verbose + + matrix: + # Ruby 1.8.7 isn't available on AppVeyor. + + - RUBY_ENGINE: ruby + RUBY_VERSION: 193 + SSL_CERT_FILE: C:\Ruby193\lib\ruby\1.9.1\rubygems\ssl_certs\ca-bundle.pem + + - RUBY_ENGINE: ruby + RUBY_VERSION: 200 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 200-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 21 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 21-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 22 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 22-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 23 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 23-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 24 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 24-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 25 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 25-x64 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 26 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 26-x64 + + - RUBY_ENGINE: jruby + JRUBY_VERSION: 1.7.27 + + - RUBY_ENGINE: jruby + JRUBY_VERSION: 9.1.17.0 + + - RUBY_ENGINE: jruby + JRUBY_VERSION: 9.2.9.0 + +install: + - if not exist vendor mkdir vendor + - if %RUBY_ENGINE%==jruby appveyor DownloadFile https://repo1.maven.org/maven2/org/jruby/jruby-dist/%JRUBY_VERSION%/jruby-dist-%JRUBY_VERSION%-bin.zip -FileName vendor\jruby-dist-%JRUBY_VERSION%-bin.zip + - if %RUBY_ENGINE%==jruby 7z x vendor\jruby-dist-%JRUBY_VERSION%-bin.zip -ovendor -y + - if %RUBY_ENGINE%==jruby set PATH=C:\projects\tzinfo\vendor\jruby-%JRUBY_VERSION%\bin;%PATH% + - if %RUBY_ENGINE%==jruby if "%JRUBY_VERSION:~0,2%"=="1." gem install bundler --version "~> 1.17" + - if %RUBY_ENGINE%==jruby if not "%JRUBY_VERSION:~0,2%"=="1." gem install bundler + - if v%RUBY_VERSION%==v193 appveyor DownloadFile https://github.com/philr/rubyinstaller/releases/download/1.9.3-p551-openssl-tls-1.1-1.2/ruby-1.9.3-p551-i386-mingw32.7z -FileName vendor\ruby-1.9.3-p551-i386-mingw32.7z + - if v%RUBY_VERSION%==v193 7z e vendor\ruby-1.9.3-p551-i386-mingw32.7z -ovendor ruby-1.9.3-p551-i386-mingw32\bin\libeay32.dll ruby-1.9.3-p551-i386-mingw32\bin\ssleay32.dll ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\i386-mingw32\openssl.so ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\rubygems\ssl_certs\ca-bundle.pem + - if v%RUBY_VERSION%==v193 copy /Y vendor\*eay32.dll C:\Ruby193\bin + - if v%RUBY_VERSION%==v193 copy /Y vendor\openssl.so C:\Ruby193\lib\ruby\1.9.1\i386-mingw32 + - if v%RUBY_VERSION%==v193 copy /Y vendor\ca-bundle.pem C:\Ruby193\lib\ruby\1.9.1\rubygems\ssl_certs + - if %RUBY_ENGINE%==ruby set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% + - bundle config --local path vendor/bundle + - bundle update + +build: off + +before_test: + - "%RUBY_ENGINE% -v" + - gem -v + - bundle -v + +test_script: + - bundle exec rake test + +after_test: + - bundle clean From fc2c40004b11c54ea252e34bb9f656d000229ec4 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 17:54:50 +0000 Subject: [PATCH 20/81] Try and fix an incorrect rake version being picked with JRuby 1.7. https://ci.appveyor.com/project/philr/tzinfo/builds/29726224/job/b1omk2h5qua9e9kc Install RubyGems 2.7 on JRuby 1.7. --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index a5baef03..9b7f8dd3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -69,6 +69,8 @@ install: - if %RUBY_ENGINE%==jruby appveyor DownloadFile https://repo1.maven.org/maven2/org/jruby/jruby-dist/%JRUBY_VERSION%/jruby-dist-%JRUBY_VERSION%-bin.zip -FileName vendor\jruby-dist-%JRUBY_VERSION%-bin.zip - if %RUBY_ENGINE%==jruby 7z x vendor\jruby-dist-%JRUBY_VERSION%-bin.zip -ovendor -y - if %RUBY_ENGINE%==jruby set PATH=C:\projects\tzinfo\vendor\jruby-%JRUBY_VERSION%\bin;%PATH% + - if %RUBY_ENGINE%==jruby if "%JRUBY_VERSION:~0,2%"=="1." gem install rubygems-update --version '~> 2.7' --no-document + - if %RUBY_ENGINE%==jruby if "%JRUBY_VERSION:~0,2%"=="1." update_rubygems - if %RUBY_ENGINE%==jruby if "%JRUBY_VERSION:~0,2%"=="1." gem install bundler --version "~> 1.17" - if %RUBY_ENGINE%==jruby if not "%JRUBY_VERSION:~0,2%"=="1." gem install bundler - if v%RUBY_VERSION%==v193 appveyor DownloadFile https://github.com/philr/rubyinstaller/releases/download/1.9.3-p551-openssl-tls-1.1-1.2/ruby-1.9.3-p551-i386-mingw32.7z -FileName vendor\ruby-1.9.3-p551-i386-mingw32.7z From 37bf1076d0540a49d40d9b8034cb7700e5cff07b Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 18:00:19 +0000 Subject: [PATCH 21/81] Revert "Try and fix an incorrect rake version being picked with JRuby 1.7." This reverts commit fc2c40004b11c54ea252e34bb9f656d000229ec4. The issue appears to have been a temporary glitch. Working in https://ci.appveyor.com/project/philr/tzinfo/builds/29726455/job/9ndorru68ff0l38f before the offending commit was added. --- appveyor.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 9b7f8dd3..a5baef03 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -69,8 +69,6 @@ install: - if %RUBY_ENGINE%==jruby appveyor DownloadFile https://repo1.maven.org/maven2/org/jruby/jruby-dist/%JRUBY_VERSION%/jruby-dist-%JRUBY_VERSION%-bin.zip -FileName vendor\jruby-dist-%JRUBY_VERSION%-bin.zip - if %RUBY_ENGINE%==jruby 7z x vendor\jruby-dist-%JRUBY_VERSION%-bin.zip -ovendor -y - if %RUBY_ENGINE%==jruby set PATH=C:\projects\tzinfo\vendor\jruby-%JRUBY_VERSION%\bin;%PATH% - - if %RUBY_ENGINE%==jruby if "%JRUBY_VERSION:~0,2%"=="1." gem install rubygems-update --version '~> 2.7' --no-document - - if %RUBY_ENGINE%==jruby if "%JRUBY_VERSION:~0,2%"=="1." update_rubygems - if %RUBY_ENGINE%==jruby if "%JRUBY_VERSION:~0,2%"=="1." gem install bundler --version "~> 1.17" - if %RUBY_ENGINE%==jruby if not "%JRUBY_VERSION:~0,2%"=="1." gem install bundler - if v%RUBY_VERSION%==v193 appveyor DownloadFile https://github.com/philr/rubyinstaller/releases/download/1.9.3-p551-openssl-tls-1.1-1.2/ruby-1.9.3-p551-i386-mingw32.7z -FileName vendor\ruby-1.9.3-p551-i386-mingw32.7z From 4e01d0660caa4d8a8ed6a65969f538c8ebbb61e5 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 20:32:51 +0000 Subject: [PATCH 22/81] Fix block not being called by RubyCoreSupport.open_file on JRuby 9.2. Using a refinement would cause the block parameter to be ignored. https://travis-ci.org/tzinfo/tzinfo/jobs/628812051#L1931 https://github.com/jruby/jruby/issues/6009 Use `send(:using, Module)` instead of `using Module` as a workaround. --- lib/tzinfo/zoneinfo_data_source.rb | 8 +++++++- test/tc_zoneinfo_data_source.rb | 10 ++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/tzinfo/zoneinfo_data_source.rb b/lib/tzinfo/zoneinfo_data_source.rb index 2126e039..e781d78c 100644 --- a/lib/tzinfo/zoneinfo_data_source.rb +++ b/lib/tzinfo/zoneinfo_data_source.rb @@ -1,5 +1,11 @@ module TZInfo - using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt) + # Use send as a workaround for an issue on JRuby 9.2.9.0 where using the + # refinement causes calls to RubyCoreSupport.file_open to fail to pass the + # block parameter. + # + # https://travis-ci.org/tzinfo/tzinfo/jobs/628812051#L1931 + # https://github.com/jruby/jruby/issues/6009 + send(:using, TZInfo::RubyCoreSupport::UntaintExt) if TZInfo::RubyCoreSupport.const_defined?(:UntaintExt) # An InvalidZoneinfoDirectory exception is raised if the DataSource is # set to a specific zoneinfo path, which is not a valid zoneinfo directory diff --git a/test/tc_zoneinfo_data_source.rb b/test/tc_zoneinfo_data_source.rb index 42c6aa89..fe517cbb 100644 --- a/test/tc_zoneinfo_data_source.rb +++ b/test/tc_zoneinfo_data_source.rb @@ -7,8 +7,14 @@ include TZInfo -using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt) -using TaintExt if Module.const_defined?(:TaintExt) +# Use send as a workaround for an issue on JRuby 9.2.9.0 where using the +# refinement causes calls to RubyCoreSupport.file_open to fail to pass the block +# parameter. +# +# https://travis-ci.org/tzinfo/tzinfo/jobs/628812051#L1931 +# https://github.com/jruby/jruby/issues/6009 +send(:using, TZInfo::RubyCoreSupport::UntaintExt) if TZInfo::RubyCoreSupport.const_defined?(:UntaintExt) +send(:using, TaintExt) if Module.const_defined?(:TaintExt) class TCZoneinfoDataSource < Minitest::Test ZONEINFO_DIR = File.join(File.expand_path(File.dirname(__FILE__)), 'zoneinfo').untaint From 2a63fc5a5f2f59d957e8a8abbfd05094872a0b84 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 20:38:05 +0000 Subject: [PATCH 23/81] Ruby Enterprise Edition requires older versions of RubyGems and Bundler. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ca211a8a..3c8d1d4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,9 @@ language: ruby dist: trusty sudo: false before_install: - - if [[ $TRAVIS_RUBY_VERSION =~ ^(1|2\.[012]|jruby-1)\. ]]; then gem install rubygems-update --version '~> 2.7' --no-document && update_rubygems; else gem update --system; fi + - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-1)\.|ree$) ]]; then gem install rubygems-update --version '~> 2.7' --no-document && update_rubygems; else gem update --system; fi - gem --version - - if [[ $TRAVIS_RUBY_VERSION =~ ^(1|2\.[012]|jruby-1)\. ]]; then gem install bundler --version '~> 1.17'; else gem install bundler; fi + - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-1)\.|ree$) ]]; then gem install bundler --version '~> 1.17'; else gem install bundler; fi - bundle --version before_script: - bundle update From 9fdc19a43e97581c3cc90da83c4de9f06e79a951 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Dec 2019 22:34:46 +0000 Subject: [PATCH 24/81] Fix a comment. Minitest v5.12.0 works with Ruby 1.9.3, but still needs to be excluded because it doesn't work with Ruby 1.8.7. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 065bafe9..ce37d797 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ gemspec group :test do gem 'rake', ['>= 10.5', '< 14'] - # Exclude Minitest v5.12.0. It is only compatible with Ruby >= 2.0.0, but does + # Exclude Minitest v5.12.0. It is only compatible with Ruby >= 1.9.3, but does # not specify a minimum Ruby version. gem 'minitest', ['~> 5.11', '!= 5.12.0'] end From 1c1706308a2ae524b8cd55830afe824b36d235e6 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 24 Dec 2019 15:00:43 +0000 Subject: [PATCH 25/81] Replace expired gem signing certificate. Expires in 20 years time because RubyGems checks the certificate is valid at the time of installation rather than at the time the signature was produced. See #101. --- gem-public_cert.pem | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/gem-public_cert.pem b/gem-public_cert.pem index 0c952f79..0aa3d636 100644 --- a/gem-public_cert.pem +++ b/gem-public_cert.pem @@ -1,21 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDdDCCAlygAwIBAgIBATANBgkqhkiG9w0BAQUFADBAMRIwEAYDVQQDDAlwaGls -LnJvc3MxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv -bTAeFw0xNzEwMjMxOTQ2MDJaFw0xODEwMjMxOTQ2MDJaMEAxEjAQBgNVBAMMCXBo -aWwucm9zczEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYD -Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkZzB+qfhmyY+XRvU -u310LMTGsTkR4/8JFCMF0YeQX6ZKmLr1fKzF3At1+DlI+v0t/G2FS6Dic0V3l8MK -JczyFh72NANOaQhAo0GHh8WkaeCf2DLL5K6YJeLpvkvp39oxzn00A4zosnzxM50f -Xrjx2HmurcJQurzafeCDj67QccaNE+5H+mcIVAJlsA1h1f5QFZ3SqQ4mf8St40pE -6YR4ev/Eq6Hb8aUoUq30otxbeHAEHh8cdVhTNFq7sPWb0psQRF2D/+o0MLgHt8PY -EUm49szlLsnjVXAMCHU7wH9CmDR/5Lzcrgqh3DgyI8ay6DnlSQ213eYZH/Nkn1Yz -TcNLCQIDAQABo3kwdzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU -D5nzO9/MG4B6ygch/Pv6PF9Q5x8wHgYDVR0RBBcwFYETcGhpbC5yb3NzQGdtYWls -LmNvbTAeBgNVHRIEFzAVgRNwaGlsLnJvc3NAZ21haWwuY29tMA0GCSqGSIb3DQEB -BQUAA4IBAQAHbabsU8fIQudX8XYwqZJYO76Y4LbHnMqZZz9nmRBWJlFE3E5jaF8Y -p9v1LkOLlo04z9bdnIS0/RfSqvHkNYcdpYXHnmr5/GYItKt8LWpFDA5cLaeWv5cU -FQB6a0HlkirTSTbevJNssymV/E206AFAoPK9vzjROn+/2MG4VlvYf/zr2nSQG76M -BMVs6uF68qxYpWjHisX2oy6R1k4G32jopKfLpdh1WCnN2/U5jqND/b25SRZ2ZRxy -YbX/8MDD3wwHu+knVnVsGNVuu/leNr+hJGgTUGXgcsu6nqYc4QVD+Amj1rI8D6at -IYlrSPqJ7q3pK9kchFKrrktRA6yVf+fR +MIIDPDCCAiSgAwIBAgIBATANBgkqhkiG9w0BAQsFADAkMSIwIAYDVQQDDBlwaGls +LnJvc3MvREM9Z21haWwvREM9Y29tMB4XDTE5MTIyNDE0NTU0N1oXDTM5MTIyNDE0 +NTU0N1owJDEiMCAGA1UEAwwZcGhpbC5yb3NzL0RDPWdtYWlsL0RDPWNvbTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJGcwfqn4ZsmPl0b1Lt9dCzExrE5 +EeP/CRQjBdGHkF+mSpi69XysxdwLdfg5SPr9LfxthUug4nNFd5fDCiXM8hYe9jQD +TmkIQKNBh4fFpGngn9gyy+SumCXi6b5L6d/aMc59NAOM6LJ88TOdH1648dh5rq3C +ULq82n3gg4+u0HHGjRPuR/pnCFQCZbANYdX+UBWd0qkOJn/EreNKROmEeHr/xKuh +2/GlKFKt9KLcW3hwBB4fHHVYUzRau7D1m9KbEERdg//qNDC4B7fD2BFJuPbM5S7J +41VwDAh1O8B/Qpg0f+S83K4Kodw4MiPGsug55UkNtd3mGR/zZJ9WM03DSwkCAwEA +AaN5MHcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFA+Z8zvfzBuA +esoHIfz7+jxfUOcfMB4GA1UdEQQXMBWBE3BoaWwucm9zc0BnbWFpbC5jb20wHgYD +VR0SBBcwFYETcGhpbC5yb3NzQGdtYWlsLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEA +J80xgZ3gGdQVA8N+8NJANU5HLuZIU9jOaAlziU9ImoTgPiOHKGZC4as1TwT4kBt1 +Qcnu7YSANYRrxP5tpOHsWPF/MQYgerAFCZS5+PzOTudwZ+7OsMW4/EMHy6aCVHEd +c7HzQRC4mSrDRpWxzyBnZ5nX5OAmIkKA8NgeKybT/4Ku6iFPPUQwlyxQaO+Wlxdo +FqHwpjRyoiVSpe4RUTNK3d3qesWPYi7Lxn6k6ZZeEdvG6ya33AXktE3jmmF+jPR1 +J3Zn/kSTjTekiaspyGbczC3PUaeJNxr+yCvR4sk71Xmk/GaKKGOHedJ1uj/LAXrA +MR0mpl7b8zCg0PFC1J73uw== -----END CERTIFICATE----- From 0f2746fdc81d250192cb7b0824905508edc4d4a9 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 24 Dec 2019 15:09:52 +0000 Subject: [PATCH 26/81] Preparing v1.2.6. --- CHANGES.md | 11 +++++++++++ tzinfo.gemspec | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 2ee3826d..7d61386a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,14 @@ +Version 1.2.6 - 24-Dec-2019 +--------------------------- + +* Timezone#strftime('%s', time) will now return the correct number of seconds + since the epoch. #91. +* Removed the unused TZInfo::RubyDataSource::REQUIRE_PATH constant. +* Fixed "SecurityError: Insecure operation - require" exceptions when loading + data with recent Ruby releases in safe mode. +* Fixed warnings when running on Ruby 2.7. #106 and #111. + + Version 1.2.5 - 4-Feb-2018 -------------------------- diff --git a/tzinfo.gemspec b/tzinfo.gemspec index 5d9d34d4..51dfe94b 100644 --- a/tzinfo.gemspec +++ b/tzinfo.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'tzinfo' - s.version = '1.2.5' + s.version = '1.2.6' s.summary = 'Daylight savings aware timezone library' s.description = 'TZInfo provides daylight savings aware transformations between times in different time zones.' s.author = 'Philip Ross' From 048bad7dab90f19d408a0655033ee05eaba9645b Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 24 Dec 2019 15:31:34 +0000 Subject: [PATCH 27/81] Update copyright years. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index dc3697a0..f9e22742 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2005-2018 Philip Ross +Copyright (c) 2005-2019 Philip Ross Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 7a603da1baa30696e7603c5c95cc3b28dc4c1d07 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 26 Dec 2019 18:46:40 +0000 Subject: [PATCH 28/81] Update to Ruby 2.7.0. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3c8d1d4c..826ec7b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ rvm: - 2.4.9 - 2.5.7 - 2.6.5 - - 2.7.0-rc2 + - 2.7.0 - ruby-head - jruby-18mode - jruby-1.7.27 From 5f9b1cd56f80b8975dbe50678d11b14811a38167 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Wed, 29 Jan 2020 08:20:17 +0900 Subject: [PATCH 29/81] `$VERBOSE = false` won't be worked since `rb_warning` is changed to `rb_warn` Ref https://github.com/ruby/ruby/pull/2856. --- lib/tzinfo/ruby_core_support.rb | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/tzinfo/ruby_core_support.rb b/lib/tzinfo/ruby_core_support.rb index 5d8e219d..9b56893c 100644 --- a/lib/tzinfo/ruby_core_support.rb +++ b/lib/tzinfo/ruby_core_support.rb @@ -156,21 +156,15 @@ def self.open_file(file_name, mode, opts, &block) # Object#untaint is a deprecated no-op in Ruby >= 2.7 and will be removed in # 3.0. Add a refinement to either silence the warning, or supply the method # if needed. - old_verbose = $VERBOSE - $VERBOSE = false - begin - o = Object.new - if [:taint, :untaint, :tainted?].none? {|m| o.respond_to?(m) } || !o.taint.tainted? - module UntaintExt - refine Object do - def untaint - self - end + o = Object.new + if [:taint, :untaint, :tainted?].none? {|m| o.respond_to?(m) } || RUBY_VERSION >= '2.7' + module UntaintExt + refine Object do + def untaint + self end end end - ensure - $VERBOSE = old_verbose end end end From 83bdd0ed2a701ba85d1cb36cb11833385fb02b44 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Wed, 26 Feb 2020 20:17:52 +0000 Subject: [PATCH 30/81] Fix erroneous 'wrong number of arguments' errors on JRuby 9.0.5.0. Resolves #114. --- .travis.yml | 5 +++-- appveyor.yml | 9 +++++++-- lib/tzinfo/ruby_data_source.rb | 4 +++- lib/tzinfo/zoneinfo_timezone_info.rb | 4 +++- test/tc_country.rb | 4 +++- test/tc_ruby_data_source.rb | 4 +++- test/tc_timezone.rb | 4 +++- test/tc_zoneinfo_timezone_info.rb | 4 +++- test/test_utils.rb | 4 ++++ 9 files changed, 32 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 826ec7b6..f9647103 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,9 @@ language: ruby dist: trusty sudo: false before_install: - - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-1)\.|ree$) ]]; then gem install rubygems-update --version '~> 2.7' --no-document && update_rubygems; else gem update --system; fi + - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-(1|9\.0))\.|ree$) ]]; then gem install rubygems-update --version '~> 2.7' --no-document && update_rubygems; else gem update --system; fi - gem --version - - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-1)\.|ree$) ]]; then gem install bundler --version '~> 1.17'; else gem install bundler; fi + - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-(1|9\.0))\.|ree$) ]]; then gem install bundler --version '~> 1.17'; else gem install bundler; fi - bundle --version before_script: - bundle update @@ -27,6 +27,7 @@ rvm: - ruby-head - jruby-18mode - jruby-1.7.27 + - jruby-9.0.5.0 - jruby-9.1.17.0 - jruby-9.2.9.0 - jruby-head diff --git a/appveyor.yml b/appveyor.yml index a5baef03..13a40626 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -57,6 +57,11 @@ environment: - RUBY_ENGINE: jruby JRUBY_VERSION: 1.7.27 + JRUBY_BUNDLER_VERSION: "~> 1.17" + + - RUBY_ENGINE: jruby + JRUBY_VERSION: 9.0.5.0 + JRUBY_BUNDLER_VERSION: "~> 1.17" - RUBY_ENGINE: jruby JRUBY_VERSION: 9.1.17.0 @@ -69,8 +74,8 @@ install: - if %RUBY_ENGINE%==jruby appveyor DownloadFile https://repo1.maven.org/maven2/org/jruby/jruby-dist/%JRUBY_VERSION%/jruby-dist-%JRUBY_VERSION%-bin.zip -FileName vendor\jruby-dist-%JRUBY_VERSION%-bin.zip - if %RUBY_ENGINE%==jruby 7z x vendor\jruby-dist-%JRUBY_VERSION%-bin.zip -ovendor -y - if %RUBY_ENGINE%==jruby set PATH=C:\projects\tzinfo\vendor\jruby-%JRUBY_VERSION%\bin;%PATH% - - if %RUBY_ENGINE%==jruby if "%JRUBY_VERSION:~0,2%"=="1." gem install bundler --version "~> 1.17" - - if %RUBY_ENGINE%==jruby if not "%JRUBY_VERSION:~0,2%"=="1." gem install bundler + - if %RUBY_ENGINE%==jruby if defined JRUBY_BUNDLER_VERSION gem install bundler --version "%JRUBY_BUNDLER_VERSION%" + - if %RUBY_ENGINE%==jruby if not defined JRUBY_BUNDLER_VERSION gem install bundler - if v%RUBY_VERSION%==v193 appveyor DownloadFile https://github.com/philr/rubyinstaller/releases/download/1.9.3-p551-openssl-tls-1.1-1.2/ruby-1.9.3-p551-i386-mingw32.7z -FileName vendor\ruby-1.9.3-p551-i386-mingw32.7z - if v%RUBY_VERSION%==v193 7z e vendor\ruby-1.9.3-p551-i386-mingw32.7z -ovendor ruby-1.9.3-p551-i386-mingw32\bin\libeay32.dll ruby-1.9.3-p551-i386-mingw32\bin\ssleay32.dll ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\i386-mingw32\openssl.so ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\rubygems\ssl_certs\ca-bundle.pem - if v%RUBY_VERSION%==v193 copy /Y vendor\*eay32.dll C:\Ruby193\bin diff --git a/lib/tzinfo/ruby_data_source.rb b/lib/tzinfo/ruby_data_source.rb index 0452c7d1..b5a67524 100644 --- a/lib/tzinfo/ruby_data_source.rb +++ b/lib/tzinfo/ruby_data_source.rb @@ -1,5 +1,7 @@ module TZInfo - using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt) + # Use send as a workaround for erroneous 'wrong number of arguments' errors + # with JRuby 9.0.5.0 when calling methods with Java implementations. See #114. + send(:using, RubyCoreSupport::UntaintExt) if RubyCoreSupport.const_defined?(:UntaintExt) # A DataSource that loads data from the set of Ruby modules included in the # TZInfo::Data library (tzinfo-data gem). diff --git a/lib/tzinfo/zoneinfo_timezone_info.rb b/lib/tzinfo/zoneinfo_timezone_info.rb index 668ded55..4688ca61 100644 --- a/lib/tzinfo/zoneinfo_timezone_info.rb +++ b/lib/tzinfo/zoneinfo_timezone_info.rb @@ -1,5 +1,7 @@ module TZInfo - using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt) + # Use send as a workaround for erroneous 'wrong number of arguments' errors + # with JRuby 9.0.5.0 when calling methods with Java implementations. See #114. + send(:using, RubyCoreSupport::UntaintExt) if RubyCoreSupport.const_defined?(:UntaintExt) # An InvalidZoneinfoFile exception is raised if an attempt is made to load an # invalid zoneinfo file. diff --git a/test/tc_country.rb b/test/tc_country.rb index 211cb3fb..faa3bb52 100644 --- a/test/tc_country.rb +++ b/test/tc_country.rb @@ -2,7 +2,9 @@ include TZInfo -using TaintExt if Module.const_defined?(:TaintExt) +# Use send as a workaround for erroneous 'wrong number of arguments' errors with +# JRuby 9.0.5.0 when calling methods with Java implementations. See #114. +send(:using, TaintExt) if Module.const_defined?(:TaintExt) class TCCountry < Minitest::Test def setup diff --git a/test/tc_ruby_data_source.rb b/test/tc_ruby_data_source.rb index 67e90e81..790dd8eb 100644 --- a/test/tc_ruby_data_source.rb +++ b/test/tc_ruby_data_source.rb @@ -2,7 +2,9 @@ include TZInfo -using TaintExt if Module.const_defined?(:TaintExt) +# Use send as a workaround for erroneous 'wrong number of arguments' errors with +# JRuby 9.0.5.0 when calling methods with Java implementations. See #114. +send(:using, TaintExt) if Module.const_defined?(:TaintExt) class TCRubyDataSource < Minitest::Test def setup diff --git a/test/tc_timezone.rb b/test/tc_timezone.rb index 82cef9c7..0dc06111 100644 --- a/test/tc_timezone.rb +++ b/test/tc_timezone.rb @@ -2,7 +2,9 @@ include TZInfo -using TaintExt if Module.const_defined?(:TaintExt) +# Use send as a workaround for erroneous 'wrong number of arguments' errors with +# JRuby 9.0.5.0 when calling methods with Java implementations. See #114. +send(:using, TaintExt) if Module.const_defined?(:TaintExt) class TCTimezone < Minitest::Test diff --git a/test/tc_zoneinfo_timezone_info.rb b/test/tc_zoneinfo_timezone_info.rb index 32c7f504..8bf4e841 100644 --- a/test/tc_zoneinfo_timezone_info.rb +++ b/test/tc_zoneinfo_timezone_info.rb @@ -5,7 +5,9 @@ include TZInfo -using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt) +# Use send as a workaround for erroneous 'wrong number of arguments' errors with +# JRuby 9.0.5.0 when calling methods with Java implementations. See #114. +send(:using, RubyCoreSupport::UntaintExt) if RubyCoreSupport.const_defined?(:UntaintExt) class TCZoneinfoTimezoneInfo < Minitest::Test diff --git a/test/test_utils.rb b/test/test_utils.rb index a8cec197..bf669886 100644 --- a/test/test_utils.rb +++ b/test/test_utils.rb @@ -116,6 +116,10 @@ def assert_array_same_items(expected, actual, msg = nil) end def assert_sub_process_returns(expected_lines, code, extra_load_path = [], required = ['tzinfo']) + if RUBY_ENGINE == 'jruby' && JRUBY_VERSION.start_with?('9.0.') && RbConfig::CONFIG['host_os'] =~ /mswin/ + skip('JRuby 9.0 on Windows cannot handle writing to the IO instance returned by popen') + end + ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT']) From e0e66f8468f20c61882752b8f8c694825683bc48 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Wed, 26 Feb 2020 20:20:57 +0000 Subject: [PATCH 31/81] Fix test failures on Ruby 1.8.7. https://travis-ci.org/tzinfo/tzinfo/jobs/655518112 --- test/test_utils.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_utils.rb b/test/test_utils.rb index bf669886..7d899438 100644 --- a/test/test_utils.rb +++ b/test/test_utils.rb @@ -116,7 +116,7 @@ def assert_array_same_items(expected, actual, msg = nil) end def assert_sub_process_returns(expected_lines, code, extra_load_path = [], required = ['tzinfo']) - if RUBY_ENGINE == 'jruby' && JRUBY_VERSION.start_with?('9.0.') && RbConfig::CONFIG['host_os'] =~ /mswin/ + if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' && JRUBY_VERSION.start_with?('9.0.') && RbConfig::CONFIG['host_os'] =~ /mswin/ skip('JRuby 9.0 on Windows cannot handle writing to the IO instance returned by popen') end From 1d57e4787297c94849189dc76da3f85957bfcc44 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sat, 7 Mar 2020 20:15:29 +0000 Subject: [PATCH 32/81] Don't rely on lexicographic version comparisons. --- lib/tzinfo/ruby_core_support.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tzinfo/ruby_core_support.rb b/lib/tzinfo/ruby_core_support.rb index 9b56893c..06f7e53c 100644 --- a/lib/tzinfo/ruby_core_support.rb +++ b/lib/tzinfo/ruby_core_support.rb @@ -157,7 +157,7 @@ def self.open_file(file_name, mode, opts, &block) # 3.0. Add a refinement to either silence the warning, or supply the method # if needed. o = Object.new - if [:taint, :untaint, :tainted?].none? {|m| o.respond_to?(m) } || RUBY_VERSION >= '2.7' + if [:taint, :untaint, :tainted?].none? {|m| o.respond_to?(m) } || RUBY_VERSION =~ /\A(\d+)\.(\d+)(?:\.|\z)/ && ($1 == '2' && $2.to_i >= 7 || $1.to_i >= 3) module UntaintExt refine Object do def untaint From ae54281247c8e07f913090c5942c5be019d781c9 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sat, 7 Mar 2020 20:18:34 +0000 Subject: [PATCH 33/81] Fix comments relating to taint/untaint removal. --- lib/tzinfo/ruby_core_support.rb | 2 +- test/test_utils.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tzinfo/ruby_core_support.rb b/lib/tzinfo/ruby_core_support.rb index 06f7e53c..25184926 100644 --- a/lib/tzinfo/ruby_core_support.rb +++ b/lib/tzinfo/ruby_core_support.rb @@ -154,7 +154,7 @@ def self.open_file(file_name, mode, opts, &block) # Object#untaint is a deprecated no-op in Ruby >= 2.7 and will be removed in - # 3.0. Add a refinement to either silence the warning, or supply the method + # 3.2. Add a refinement to either silence the warning, or supply the method # if needed. o = Object.new if [:taint, :untaint, :tainted?].none? {|m| o.respond_to?(m) } || RUBY_VERSION =~ /\A(\d+)\.(\d+)(?:\.|\z)/ && ($1 == '2' && $2.to_i >= 7 || $1.to_i >= 3) diff --git a/test/test_utils.rb b/test/test_utils.rb index a8cec197..756b3398 100644 --- a/test/test_utils.rb +++ b/test/test_utils.rb @@ -165,7 +165,7 @@ def assert_nothing_raised(msg = nil) # Object#taint is a deprecated no-op in Ruby 2.7 and outputs a warning. It will -# be removed in 3.0. Silence the warning or supply a replacement. +# be removed in 3.2. Silence the warning or supply a replacement. if TZInfo::RubyCoreSupport.const_defined?(:UntaintExt) module TaintExt refine Object do From 62d902fc6668d9445ad4b4ec251d9281a4429adb Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sat, 7 Mar 2020 20:24:13 +0000 Subject: [PATCH 34/81] Test for just the non-existence of #untaint. Only Object#untaint is needed and defined by the refinement. --- lib/tzinfo/ruby_core_support.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/tzinfo/ruby_core_support.rb b/lib/tzinfo/ruby_core_support.rb index 25184926..7ba776b3 100644 --- a/lib/tzinfo/ruby_core_support.rb +++ b/lib/tzinfo/ruby_core_support.rb @@ -156,8 +156,7 @@ def self.open_file(file_name, mode, opts, &block) # Object#untaint is a deprecated no-op in Ruby >= 2.7 and will be removed in # 3.2. Add a refinement to either silence the warning, or supply the method # if needed. - o = Object.new - if [:taint, :untaint, :tainted?].none? {|m| o.respond_to?(m) } || RUBY_VERSION =~ /\A(\d+)\.(\d+)(?:\.|\z)/ && ($1 == '2' && $2.to_i >= 7 || $1.to_i >= 3) + if !Object.new.respond_to?(:untaint) || RUBY_VERSION =~ /\A(\d+)\.(\d+)(?:\.|\z)/ && ($1 == '2' && $2.to_i >= 7 || $1.to_i >= 3) module UntaintExt refine Object do def untaint From 075d5e31b6c5ab7e53679bfe1cb899790378d8dd Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 15 Mar 2020 20:45:02 +0000 Subject: [PATCH 35/81] Update to JRuby 9.2.11.0. Add --no-document to fix hangs when updating RubyGems (and improve test performance). Remove deprecated options set by Travis CI from JRUBY_OPTS. Travis CI sets the --client option (although the documentation says --server is set). Using the --client, --server or --noclient options will cause the jruby command (in 9.2+) to output a warning and break the sub-process tests. --- .travis.yml | 8 +++++--- appveyor.yml | 6 +++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index f9647103..84467812 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,11 @@ language: ruby dist: trusty sudo: false before_install: - - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-(1|9\.0))\.|ree$) ]]; then gem install rubygems-update --version '~> 2.7' --no-document && update_rubygems; else gem update --system; fi + - if [[ $TRAVIS_RUBY_VERSION =~ ^jruby- && ! $TRAVIS_RUBY_VERSION =~ ^jruby-(1|9\.[01]\.) ]]; then export JRUBY_OPTS=`echo "$JRUBY_OPTS" | sed -E -e 's/--((no)?client|server)//g'`; fi + - if [[ $TRAVIS_RUBY_VERSION =~ ^jruby- ]]; then echo "JRUBY_OPTS=$JRUBY_OPTS"; fi + - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-(1|9\.0))\.|ree$) ]]; then gem install rubygems-update --version '~> 2.7' --no-document && update_rubygems; else gem update --system --no-document; fi - gem --version - - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-(1|9\.0))\.|ree$) ]]; then gem install bundler --version '~> 1.17'; else gem install bundler; fi + - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-(1|9\.0))\.|ree$) ]]; then gem install bundler --version '~> 1.17' --no-document; else gem install bundler --no-document; fi - bundle --version before_script: - bundle update @@ -29,7 +31,7 @@ rvm: - jruby-1.7.27 - jruby-9.0.5.0 - jruby-9.1.17.0 - - jruby-9.2.9.0 + - jruby-9.2.11.0 - jruby-head - rbx-2.71828182 - rbx-3.107 diff --git a/appveyor.yml b/appveyor.yml index 13a40626..83f5c28b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -67,15 +67,15 @@ environment: JRUBY_VERSION: 9.1.17.0 - RUBY_ENGINE: jruby - JRUBY_VERSION: 9.2.9.0 + JRUBY_VERSION: 9.2.11.0 install: - if not exist vendor mkdir vendor - if %RUBY_ENGINE%==jruby appveyor DownloadFile https://repo1.maven.org/maven2/org/jruby/jruby-dist/%JRUBY_VERSION%/jruby-dist-%JRUBY_VERSION%-bin.zip -FileName vendor\jruby-dist-%JRUBY_VERSION%-bin.zip - if %RUBY_ENGINE%==jruby 7z x vendor\jruby-dist-%JRUBY_VERSION%-bin.zip -ovendor -y - if %RUBY_ENGINE%==jruby set PATH=C:\projects\tzinfo\vendor\jruby-%JRUBY_VERSION%\bin;%PATH% - - if %RUBY_ENGINE%==jruby if defined JRUBY_BUNDLER_VERSION gem install bundler --version "%JRUBY_BUNDLER_VERSION%" - - if %RUBY_ENGINE%==jruby if not defined JRUBY_BUNDLER_VERSION gem install bundler + - if %RUBY_ENGINE%==jruby if defined JRUBY_BUNDLER_VERSION gem install bundler --version "%JRUBY_BUNDLER_VERSION%" --no-document + - if %RUBY_ENGINE%==jruby if not defined JRUBY_BUNDLER_VERSION gem install bundler --no-document - if v%RUBY_VERSION%==v193 appveyor DownloadFile https://github.com/philr/rubyinstaller/releases/download/1.9.3-p551-openssl-tls-1.1-1.2/ruby-1.9.3-p551-i386-mingw32.7z -FileName vendor\ruby-1.9.3-p551-i386-mingw32.7z - if v%RUBY_VERSION%==v193 7z e vendor\ruby-1.9.3-p551-i386-mingw32.7z -ovendor ruby-1.9.3-p551-i386-mingw32\bin\libeay32.dll ruby-1.9.3-p551-i386-mingw32\bin\ssleay32.dll ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\i386-mingw32\openssl.so ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\rubygems\ssl_certs\ca-bundle.pem - if v%RUBY_VERSION%==v193 copy /Y vendor\*eay32.dll C:\Ruby193\bin From 1f81e20ad8b2ea76a1012e666d98d5b8e9648239 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Mar 2020 22:28:02 +0000 Subject: [PATCH 36/81] Use https for links where available. --- CHANGES.md | 24 ++++++++++++------------ README.md | 12 ++++++------ lib/tzinfo/data_source.rb | 2 +- lib/tzinfo/timezone.rb | 4 ++-- tzinfo.gemspec | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7d61386a..295fb19b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -202,70 +202,70 @@ Version 0.3.45 (tzdata v2015g) - 3-Oct-2015 ------------------------------------------- * Updated to tzdata version 2015g - (http://mm.icann.org/pipermail/tz-announce/2015-October/000034.html). + (https://mm.icann.org/pipermail/tz-announce/2015-October/000034.html). Version 0.3.44 (tzdata v2015d) - 24-Apr-2015 -------------------------------------------- * Updated to tzdata version 2015d - (http://mm.icann.org/pipermail/tz-announce/2015-April/000031.html). + (https://mm.icann.org/pipermail/tz-announce/2015-April/000031.html). Version 0.3.43 (tzdata v2015a) - 31-Jan-2015 -------------------------------------------- * Updated to tzdata version 2015a - (http://mm.icann.org/pipermail/tz-announce/2015-January/000028.html). + (https://mm.icann.org/pipermail/tz-announce/2015-January/000028.html). Version 0.3.42 (tzdata v2014i) - 23-Oct-2014 -------------------------------------------- * Updated to tzdata version 2014i - (http://mm.icann.org/pipermail/tz-announce/2014-October/000026.html). + (https://mm.icann.org/pipermail/tz-announce/2014-October/000026.html). Version 0.3.41 (tzdata v2014f) - 8-Aug-2014 ------------------------------------------- * Updated to tzdata version 2014f - (http://mm.icann.org/pipermail/tz-announce/2014-August/000023.html). + (https://mm.icann.org/pipermail/tz-announce/2014-August/000023.html). Version 0.3.40 (tzdata v2014e) - 10-Jul-2014 -------------------------------------------- * Updated to tzdata version 2014e - (http://mm.icann.org/pipermail/tz-announce/2014-June/000022.html). + (https://mm.icann.org/pipermail/tz-announce/2014-June/000022.html). Version 0.3.39 (tzdata v2014a) - 9-Mar-2014 ------------------------------------------- * Updated to tzdata version 2014a - (http://mm.icann.org/pipermail/tz-announce/2014-March/000018.html). + (https://mm.icann.org/pipermail/tz-announce/2014-March/000018.html). Version 0.3.38 (tzdata v2013g) - 8-Oct-2013 ------------------------------------------- * Updated to tzdata version 2013g - (http://mm.icann.org/pipermail/tz-announce/2013-October/000015.html). + (https://mm.icann.org/pipermail/tz-announce/2013-October/000015.html). Version 0.3.37 (tzdata v2013b) - 11-Mar-2013 -------------------------------------------- * Updated to tzdata version 2013b - (http://mm.icann.org/pipermail/tz-announce/2013-March/000010.html). + (https://mm.icann.org/pipermail/tz-announce/2013-March/000010.html). Version 0.3.36 (tzdata v2013a) - 3-Mar-2013 ------------------------------------------- * Updated to tzdata version 2013a - (http://mm.icann.org/pipermail/tz-announce/2013-March/000009.html). + (https://mm.icann.org/pipermail/tz-announce/2013-March/000009.html). * Fix TimezoneTransitionInfo#eql? incorrectly returning false when running on Ruby 2.0. * Change eql? and == implementations to test the class of the passed in object @@ -276,14 +276,14 @@ Version 0.3.35 (tzdata v2012i) - 4-Nov-2012 ------------------------------------------- * Updated to tzdata version 2012i - (http://mm.icann.org/pipermail/tz-announce/2012-November/000007.html). + (https://mm.icann.org/pipermail/tz-announce/2012-November/000007.html). Version 0.3.34 (tzdata v2012h) - 27-Oct-2012 -------------------------------------------- * Updated to tzdata version 2012h - (http://mm.icann.org/pipermail/tz-announce/2012-October/000006.html). + (https://mm.icann.org/pipermail/tz-announce/2012-October/000006.html). Version 0.3.33 (tzdata v2012c) - 8-Apr-2012 diff --git a/README.md b/README.md index e190143f..5f386cd8 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ TZInfo - Ruby Timezone Library ============================== -[![Gem Version](https://badge.fury.io/rb/tzinfo.svg)](http://badge.fury.io/rb/tzinfo) [![Build Status](https://travis-ci.org/tzinfo/tzinfo.svg?branch=master)](https://travis-ci.org/tzinfo/tzinfo) +[![Gem Version](https://badge.fury.io/rb/tzinfo.svg)](https://badge.fury.io/rb/tzinfo) [![Build Status](https://travis-ci.org/tzinfo/tzinfo.svg?branch=master)](https://travis-ci.org/tzinfo/tzinfo) -[TZInfo](http://tzinfo.github.io) provides daylight savings aware +[TZInfo](https://tzinfo.github.io) provides daylight savings aware transformations between times in different timezones. @@ -13,10 +13,10 @@ Data Sources TZInfo requires a source of timezone data. There are two built-in options: 1. The TZInfo::Data library (the tzinfo-data gem). TZInfo::Data contains a set - of Ruby modules that are generated from the [IANA Time Zone Database](http://www.iana.org/time-zones). + of Ruby modules that are generated from the [IANA Time Zone Database](https://www.iana.org/time-zones). 2. A zoneinfo directory. Most Unix-like systems include a zoneinfo directory containing timezone definitions. These are also generated from the - [IANA Time Zone Database](http://www.iana.org/time-zones). + [IANA Time Zone Database](https://www.iana.org/time-zones). By default, TZInfo::Data will be used. If TZInfo::Data is not available (i.e. if `require 'tzinfo/data'` fails), then TZInfo will search for a zoneinfo @@ -25,7 +25,7 @@ directory instead (using the search path specified by If no data source can be found, a `TZInfo::DataSourceNotFound` exception will be raised when TZInfo is used. Further information is available -[in the wiki](http://tzinfo.github.io/datasourcenotfound) to help with +[in the wiki](https://tzinfo.github.io/datasourcenotfound) to help with resolving `TZInfo::DataSourceNotFound` errors. The default data source selection can be overridden using @@ -130,7 +130,7 @@ thread boundaries. Documentation ------------- -API documentation for TZInfo is available on [RubyDoc.info](http://rubydoc.info/gems/tzinfo/frames). +API documentation for TZInfo is available on [RubyDoc.info](https://rubydoc.info/gems/tzinfo/frames). License diff --git a/lib/tzinfo/data_source.rb b/lib/tzinfo/data_source.rb index c7d4a117..c5deeac9 100644 --- a/lib/tzinfo/data_source.rb +++ b/lib/tzinfo/data_source.rb @@ -179,7 +179,7 @@ def self.create_default_data_source begin return ZoneinfoDataSource.new rescue ZoneinfoDirectoryNotFound - raise DataSourceNotFound, "No source of timezone data could be found.\nPlease refer to http://tzinfo.github.io/datasourcenotfound for help resolving this error." + raise DataSourceNotFound, "No source of timezone data could be found.\nPlease refer to https://tzinfo.github.io/datasourcenotfound for help resolving this error." end end diff --git a/lib/tzinfo/timezone.rb b/lib/tzinfo/timezone.rb index 82edf1a1..ee838b97 100644 --- a/lib/tzinfo/timezone.rb +++ b/lib/tzinfo/timezone.rb @@ -604,8 +604,8 @@ def strftime(format, utc = Time.now.utc) # Passing the invalid format string through to Time#strftime or # DateTime#strtime would normally result in it being returned in the # result. However, with Ruby 1.8.7 on Windows (as tested with Ruby - # 1.8.7-p374 from http://rubyinstaller.org/downloads/archives), this - # causes Time#strftime to always return an empty string (e.g. + # 1.8.7-p374 from https://rubyinstaller.org/downloads/archives), + # this causes Time#strftime to always return an empty string (e.g. # Time.now.strftime('a %::::z b') returns ''). # # Escape the percent to force it to be evaluated as a literal. diff --git a/tzinfo.gemspec b/tzinfo.gemspec index 51dfe94b..ca7351fc 100644 --- a/tzinfo.gemspec +++ b/tzinfo.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.description = 'TZInfo provides daylight savings aware transformations between times in different time zones.' s.author = 'Philip Ross' s.email = 'phil.ross@gmail.com' - s.homepage = 'http://tzinfo.github.io' + s.homepage = 'https://tzinfo.github.io' s.license = 'MIT' s.files = %w(CHANGES.md LICENSE Rakefile README.md tzinfo.gemspec .yardopts) + Dir['lib/**/*.rb'].delete_if {|f| f.include?('.svn')} + From 0876b266667a4734f125f784fff79bc204ccafab Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 23 Mar 2020 22:37:46 +0000 Subject: [PATCH 37/81] Replace broken links. --- CHANGES.md | 72 +++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 295fb19b..4aa331a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -290,35 +290,35 @@ Version 0.3.33 (tzdata v2012c) - 8-Apr-2012 ------------------------------------------- * Updated to tzdata version 2012c - (http://article.gmane.org/gmane.comp.time.tz/4859). + (https://mm.icann.org/pipermail/tz/2012-April/017627.html). Version 0.3.32 (tzdata v2012b) - 4-Mar-2012 ------------------------------------------- * Updated to tzdata version 2012b - (http://article.gmane.org/gmane.comp.time.tz/4756). + (https://mm.icann.org/pipermail/tz/2012-March/017524.html). Version 0.3.31 (tzdata v2011n) - 6-Nov-2011 ------------------------------------------- * Updated to tzdata version 2011n - (http://article.gmane.org/gmane.comp.time.tz/4434). + (https://mm.icann.org/pipermail/tz/2011-October/017201.html). Version 0.3.30 (tzdata v2011k) - 29-Sep-2011 -------------------------------------------- * Updated to tzdata version 2011k - (http://article.gmane.org/gmane.comp.time.tz/4084). + (https://mm.icann.org/pipermail/tz/2011-September/008889.html). Version 0.3.29 (tzdata v2011h) - 27-Jun-2011 -------------------------------------------- * Updated to tzdata version 2011h - (http://article.gmane.org/gmane.comp.time.tz/3814). + (https://mm.icann.org/pipermail/tz/2011-June/008576.html). * Allow the default value of the local_to_utc and period_for_local dst parameter to be specified globally with a Timezone.default_dst attribute. Thanks to Kurt Werle for the suggestion and patch. @@ -336,35 +336,35 @@ Version 0.3.27 (tzdata v2011g) - 26-Apr-2011 -------------------------------------------- * Updated to tzdata version 2011g - (http://article.gmane.org/gmane.comp.time.tz/3758). + (https://mm.icann.org/pipermail/tz/2011-April/016875.html). Version 0.3.26 (tzdata v2011e) - 2-Apr-2011 ------------------------------------------- * Updated to tzdata version 2011e - (http://article.gmane.org/gmane.comp.time.tz/3707). + (https://mm.icann.org/pipermail/tz/2011-April/016809.html). Version 0.3.25 (tzdata v2011d) - 14-Mar-2011 -------------------------------------------- * Updated to tzdata version 2011d - (http://article.gmane.org/gmane.comp.time.tz/3662). + (https://mm.icann.org/pipermail/tz/2011-March/016746.html). Version 0.3.24 (tzdata v2010o) - 15-Jan-2011 -------------------------------------------- * Updated to tzdata version 2010o - (http://article.gmane.org/gmane.comp.time.tz/3473). + (https://mm.icann.org/pipermail/tz/2010-November/016517.html). Version 0.3.23 (tzdata v2010l) - 19-Aug-2010 -------------------------------------------- * Updated to tzdata version 2010l - (http://article.gmane.org/gmane.comp.time.tz/3354). + (https://mm.icann.org/pipermail/tz/2010-August/016360.html). Version 0.3.22 (tzdata v2010j) - 29-May-2010 @@ -377,7 +377,7 @@ Version 0.3.21 (tzdata v2010j) - 28-May-2010 -------------------------------------------- * Updated to tzdata version 2010j - (http://article.gmane.org/gmane.comp.time.tz/3225). + (https://mm.icann.org/pipermail/tz/2010-May/016211.html). * Change invalid timezone check to exclude characters not used in timezone identifiers and avoid 'character class has duplicated range' warnings with Ruby 1.9.2. @@ -395,28 +395,28 @@ Version 0.3.20 (tzdata v2010i) - 19-Apr-2010 -------------------------------------------- * Updated to tzdata version 2010i - (http://article.gmane.org/gmane.comp.time.tz/3202). + (https://mm.icann.org/pipermail/tz/2010-April/016184.html). Version 0.3.19 (tzdata v2010h) - 5-Apr-2010 ------------------------------------------- * Updated to tzdata version 2010h - (http://article.gmane.org/gmane.comp.time.tz/3188). + (https://mm.icann.org/pipermail/tz/2010-April/016161.html). Version 0.3.18 (tzdata v2010g) - 29-Mar-2010 -------------------------------------------- * Updated to tzdata version 2010g - (http://article.gmane.org/gmane.comp.time.tz/3172). + (https://mm.icann.org/pipermail/tz/2010-March/016140.html). Version 0.3.17 (tzdata v2010e) - 8-Mar-2010 ------------------------------------------- * Updated to tzdata version 2010e - (http://article.gmane.org/gmane.comp.time.tz/3128). + (https://mm.icann.org/pipermail/tz/2010-March/016088.html). Version 0.3.16 (tzdata v2009u) - 5-Jan-2010 @@ -425,14 +425,14 @@ Version 0.3.16 (tzdata v2009u) - 5-Jan-2010 * Support the use of '-' to denote '0' as an offset in the tz data files. Used for the first time in the SAVE field in tzdata v2009u. * Updated to tzdata version 2009u - (http://article.gmane.org/gmane.comp.time.tz/3053). + (https://mm.icann.org/pipermail/tz/2009-December/016001.html). Version 0.3.15 (tzdata v2009p) - 26-Oct-2009 -------------------------------------------- * Updated to tzdata version 2009p - (http://article.gmane.org/gmane.comp.time.tz/2953). + (https://mm.icann.org/pipermail/tz/2009-October/015889.html). * Added a description to the gem spec. * List test files in test_files instead of files in the gem spec. @@ -441,7 +441,7 @@ Version 0.3.14 (tzdata v2009l) - 19-Aug-2009 -------------------------------------------- * Updated to tzdata version 2009l - (http://article.gmane.org/gmane.comp.time.tz/2818). + (https://mm.icann.org/pipermail/tz/2009-August/015729.html). * Include current directory in load path to allow running tests on Ruby 1.9.2, which doesn't include it by default any more. @@ -450,7 +450,7 @@ Version 0.3.13 (tzdata v2009f) - 15-Apr-2009 -------------------------------------------- * Updated to tzdata version 2009f - (http://article.gmane.org/gmane.comp.time.tz/2668). + (https://mm.icann.org/pipermail/tz/2009-April/015544.html). * Untaint the timezone module filename after validation to allow use with $SAFE == 1 (e.g. under mod_ruby). Thanks to Dmitry Borodaenko for the suggestion. Closes #25349. @@ -460,14 +460,14 @@ Version 0.3.12 (tzdata v2008i) - 12-Nov-2008 -------------------------------------------- * Updated to tzdata version 2008i - (http://article.gmane.org/gmane.comp.time.tz/2440). + (https://mm.icann.org/pipermail/tz/2008-October/015260.html). Version 0.3.11 (tzdata v2008g) - 7-Oct-2008 ------------------------------------------- * Updated to tzdata version 2008g - (http://article.gmane.org/gmane.comp.time.tz/2335). + (https://mm.icann.org/pipermail/tz/2008-October/015139.html). * Support Ruby 1.9.0-5. Rational.new! has now been removed in Ruby 1.9. Only use Rational.new! if it is available (it is preferable in Ruby 1.8 for performance reasons). Thanks to Jeremy Kemper and Pratik Naik for @@ -480,14 +480,14 @@ Version 0.3.10 (tzdata v2008f) - 16-Sep-2008 -------------------------------------------- * Updated to tzdata version 2008f - (http://article.gmane.org/gmane.comp.time.tz/2293). + (https://mm.icann.org/pipermail/tz/2008-September/015090.html). Version 0.3.9 (tzdata v2008c) - 27-May-2008 ------------------------------------------- * Updated to tzdata version 2008c - (http://article.gmane.org/gmane.comp.time.tz/2183). + (https://mm.icann.org/pipermail/tz/2008-May/014956.html). * Support loading timezone data in the latest trunk versions of Ruby 1.9. Rational.new! is now private, so call it using Rational.send :new! instead. Thanks to Jeremy Kemper and Pratik Naik for spotting this. Closes #19184. @@ -499,7 +499,7 @@ Version 0.3.8 (tzdata v2008b) - 24-Mar-2008 ------------------------------------------- * Updated to tzdata version 2008b - (http://article.gmane.org/gmane.comp.time.tz/2149). + (https://mm.icann.org/pipermail/tz/2008-March/014910.html). * Support loading timezone data in Ruby 1.9.0. Use DateTime.new! if it is available instead of DateTime.new0 when constructing transition times. DateTime.new! was added in Ruby 1.8.6. DateTime.new0 was removed in @@ -512,14 +512,14 @@ Version 0.3.7 (tzdata v2008a) - 10-Mar-2008 ------------------------------------------- * Updated to tzdata version 2008a - (http://article.gmane.org/gmane.comp.time.tz/2071). + (https://mm.icann.org/pipermail/tz/2008-March/014851.html). Version 0.3.6 (tzdata v2007k) - 1-Jan-2008 ------------------------------------------ * Updated to tzdata version 2007k - (http://article.gmane.org/gmane.comp.time.tz/2029). + (https://mm.icann.org/pipermail/tz/2007-December/014765.html). * Removed deprecated RubyGems autorequire option. @@ -527,28 +527,28 @@ Version 0.3.5 (tzdata v2007h) - 1-Oct-2007 ------------------------------------------ * Updated to tzdata version 2007h - (http://article.gmane.org/gmane.comp.time.tz/1878). + (https://mm.icann.org/pipermail/tz/2007-October/014585.html). Version 0.3.4 (tzdata v2007g) - 21-Aug-2007 ------------------------------------------- * Updated to tzdata version 2007g - (http://article.gmane.org/gmane.comp.time.tz/1810). + (https://mm.icann.org/pipermail/tz/2007-August/014499.html). Version 0.3.3 (tzdata v2006p) - 27-Nov-2006 ------------------------------------------- * Updated to tzdata version 2006p - (http://article.gmane.org/gmane.comp.time.tz/1358). + (https://mm.icann.org/pipermail/tz/2006-November/013999.html). Version 0.3.2 (tzdata v2006n) - 11-Oct-2006 ------------------------------------------- * Updated to tzdata version 2006n - (http://article.gmane.org/gmane.comp.time.tz/1288). Note that this release of + (https://mm.icann.org/pipermail/tz/2006-October/013911.html). Note that this release of tzdata removes the country Serbia and Montenegro (CS) and replaces it with separate Serbia (RS) and Montenegro (ME) entries. @@ -559,7 +559,7 @@ Version 0.3.1 (tzdata v2006j) - 21-Aug-2006 * Remove colon from case statements to avoid warning in Ruby 1.8.5. #5198. * Use temporary variable to avoid dynamic string warning from rdoc. * Updated to tzdata version 2006j - (http://article.gmane.org/gmane.comp.time.tz/1175). + (https://mm.icann.org/pipermail/tz/2006-August/013767.html). Version 0.3.0 (tzdata v2006g) - 17-Jul-2006 @@ -625,7 +625,7 @@ Version 0.2.2 (tzdata v2006g) - 17-May-2006 zone identifier. * The zdumptestall utility now exits if not supplied with enough parameters. * Updated to tzdata version 2006g - (http://article.gmane.org/gmane.comp.time.tz/1008). + (https://mm.icann.org/pipermail/tz/2006-May/013590.html). Version 0.2.1 (tzdata v2006d) - 17-Apr-2006 @@ -641,7 +641,7 @@ Version 0.2.1 (tzdata v2006d) - 17-Apr-2006 Jamis Buck for reporting this. * Added abbreviation as an alias for TimezonePeriod.zone_identifier. * Updated to tzdata version 2006d - (http://article.gmane.org/gmane.comp.time.tz/936). + (https://mm.icann.org/pipermail/tz/2006-April/013517.html). * Ignore any offset in DateTimes passed in (as is already done for Times). All of the following now refer to the same UTC time (15:40 on 17 April 2006). Previously, the DateTime in the second line would have been interpreted @@ -674,7 +674,7 @@ Version 0.2.0 (tzdata v2006c) - 3-Apr-2006 * Omit the final transition to DST if there is a prior transition in the last year processed to standard time. * Updated to tzdata version 2006c - (http://article.gmane.org/gmane.comp.time.tz/920). + (https://mm.icann.org/pipermail/tz/2006-April/013500.html). Version 0.1.2 (tzdata v2006a) - 5-Feb-2006 @@ -683,7 +683,7 @@ Version 0.1.2 (tzdata v2006a) - 5-Feb-2006 * Add lib directory to the load path when tzinfo is required. Makes it easier to use tzinfo gem when unpacked to vendor directory in rails. * Updated to tzdata version 2006a - (http://article.gmane.org/gmane.comp.time.tz/738). + (https://mm.icann.org/pipermail/tz/2006-January/013311.html). * build_tz_classes rake task now handles running svn add and svn delete as new timezones and countries are added and old ones are removed. * Return a better error when attempting to use a Timezone instance that was @@ -788,7 +788,7 @@ Version 0.0.2 (tzdata v2005m) - 13-Sep-2005 Thanks to Scott Barron of Lunchbox Software for the suggestions in his article about using TZInfo with Rails -(http://lunchroom.lunchboxsoftware.com/pages/tzinfo_rails) +(https://web.archive.org/web/20060425190845/http://lunchroom.lunchboxsoftware.com/pages/tzinfo_rails) Version 0.0.1 (tzdata v2005m) - 29-Aug-2005 From fcaf8681c246a31b29b22eba09197fed85fd1682 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 24 Mar 2020 22:25:56 +0000 Subject: [PATCH 38/81] Add a build status badge for AppVeyor. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f386cd8..533c6cae 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ TZInfo - Ruby Timezone Library ============================== -[![Gem Version](https://badge.fury.io/rb/tzinfo.svg)](https://badge.fury.io/rb/tzinfo) [![Build Status](https://travis-ci.org/tzinfo/tzinfo.svg?branch=master)](https://travis-ci.org/tzinfo/tzinfo) +[![Gem Version](https://badge.fury.io/rb/tzinfo.svg)](https://badge.fury.io/rb/tzinfo) [![Build Status](https://travis-ci.org/tzinfo/tzinfo.svg?branch=master)](https://travis-ci.org/tzinfo/tzinfo) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/btinuu4g8sdachj3/branch/1.2?svg=true)](https://ci.appveyor.com/project/philr/tzinfo/branch/1.2) [TZInfo](https://tzinfo.github.io) provides daylight savings aware transformations between times in different timezones. From 0fcafc22041900cd000573d519f7902af8d2394f Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 24 Mar 2020 22:28:13 +0000 Subject: [PATCH 39/81] Update copyright years. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index f9e22742..6e74e290 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2005-2019 Philip Ross +Copyright (c) 2005-2020 Philip Ross Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 171f0590c03968ed47caadb09e1f9851d53d3f2e Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 26 Mar 2020 21:00:59 +0000 Subject: [PATCH 40/81] Use shields.io for badges. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 533c6cae..ca25b752 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ TZInfo - Ruby Timezone Library ============================== -[![Gem Version](https://badge.fury.io/rb/tzinfo.svg)](https://badge.fury.io/rb/tzinfo) [![Build Status](https://travis-ci.org/tzinfo/tzinfo.svg?branch=master)](https://travis-ci.org/tzinfo/tzinfo) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/btinuu4g8sdachj3/branch/1.2?svg=true)](https://ci.appveyor.com/project/philr/tzinfo/branch/1.2) +[![RubyGems](https://img.shields.io/gem/v/tzinfo)](https://rubygems.org/gems/tzinfo) [![Travis CI Build](https://img.shields.io/travis/tzinfo/tzinfo/1.2?logo=travis)](https://travis-ci.org/tzinfo/tzinfo) [![AppVeyor Build](https://img.shields.io/appveyor/build/philr/tzinfo/1.2?logo=appveyor)](https://ci.appveyor.com/project/philr/tzinfo/branch/1.2) [TZInfo](https://tzinfo.github.io) provides daylight savings aware transformations between times in different timezones. From 93cb2b4c5a824b7a3de0335436c71d78a82b0af5 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 31 Mar 2020 19:35:47 +0100 Subject: [PATCH 41/81] Update to Ruby 2.4.10, 2.5.8, 2.6.6, 2.7.1 and JRuby 9.2.11.1. --- .travis.yml | 10 +++++----- appveyor.yml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 84467812..91d06587 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,16 +22,16 @@ rvm: - 2.1.10 - 2.2.10 - 2.3.8 - - 2.4.9 - - 2.5.7 - - 2.6.5 - - 2.7.0 + - 2.4.10 + - 2.5.8 + - 2.6.6 + - 2.7.1 - ruby-head - jruby-18mode - jruby-1.7.27 - jruby-9.0.5.0 - jruby-9.1.17.0 - - jruby-9.2.11.0 + - jruby-9.2.11.1 - jruby-head - rbx-2.71828182 - rbx-3.107 diff --git a/appveyor.yml b/appveyor.yml index 83f5c28b..e0c7a4e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -67,7 +67,7 @@ environment: JRUBY_VERSION: 9.1.17.0 - RUBY_ENGINE: jruby - JRUBY_VERSION: 9.2.11.0 + JRUBY_VERSION: 9.2.11.1 install: - if not exist vendor mkdir vendor From 4610d364365edbb66753acde2bb02d1ec2b2eef5 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Wed, 1 Apr 2020 18:23:59 +0100 Subject: [PATCH 42/81] Revert to Ruby 2.4.9 and 2.7.0. 2.4.10 and 2.7.1 aren't yet available on Travis CI. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 91d06587..1dd9abd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,10 +22,10 @@ rvm: - 2.1.10 - 2.2.10 - 2.3.8 - - 2.4.10 + - 2.4.9 - 2.5.8 - 2.6.6 - - 2.7.1 + - 2.7.0 - ruby-head - jruby-18mode - jruby-1.7.27 From f7487ef3edff40bd4ce2f750a49448f0fa496ce0 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 2 Apr 2020 19:54:56 +0100 Subject: [PATCH 43/81] Update to Ruby 2.7.1. Ruby 2.4.10 is still not available. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1dd9abd3..37543272 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ rvm: - 2.4.9 - 2.5.8 - 2.6.6 - - 2.7.0 + - 2.7.1 - ruby-head - jruby-18mode - jruby-1.7.27 From e944161908133cd1740fab62d5c806ad887598b9 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 2 Apr 2020 20:07:57 +0100 Subject: [PATCH 44/81] Preparing v1.2.7. --- CHANGES.md | 7 +++++++ tzinfo.gemspec | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 4aa331a3..606eb31a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,10 @@ +Version 1.2.7 - 2-Apr-2020 +-------------------------- + +* Fix 'wrong number of arguments' errors running on JRuby 9.0. #114. +* Fix warnings when running on Ruby 2.8. #112. + + Version 1.2.6 - 24-Dec-2019 --------------------------- diff --git a/tzinfo.gemspec b/tzinfo.gemspec index ca7351fc..a73a5945 100644 --- a/tzinfo.gemspec +++ b/tzinfo.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'tzinfo' - s.version = '1.2.6' + s.version = '1.2.7' s.summary = 'Daylight savings aware timezone library' s.description = 'TZInfo provides daylight savings aware transformations between times in different time zones.' s.author = 'Philip Ross' From 97f73b201d9a061374b9a063a494f6fe63a0fe5f Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 2 Apr 2020 20:47:27 +0100 Subject: [PATCH 45/81] Improve grammar. --- CHANGES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 606eb31a..76d26d61 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,8 @@ Version 1.2.7 - 2-Apr-2020 -------------------------- -* Fix 'wrong number of arguments' errors running on JRuby 9.0. #114. -* Fix warnings when running on Ruby 2.8. #112. +* Fixed 'wrong number of arguments' errors when running on JRuby 9.0. #114. +* Fixed warnings when running on Ruby 2.8. #112. Version 1.2.6 - 24-Dec-2019 From 27d96bb218971a1aafe2d7dd70fad9dfb3f511d9 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 17 May 2020 10:46:50 +0100 Subject: [PATCH 46/81] Update to Ruby 2.4.10. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 37543272..91d06587 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ rvm: - 2.1.10 - 2.2.10 - 2.3.8 - - 2.4.9 + - 2.4.10 - 2.5.8 - 2.6.6 - 2.7.1 From 7299f88059dbdb5592ea3a27e07f79dee83de9bd Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 17 May 2020 12:33:29 +0100 Subject: [PATCH 47/81] Stop supporting Rubinius. Rubinius binaries no longer appear to be available on Travis CI. The latest version (5.0) fails to compile on Ubuntu 19.10. --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 91d06587..b54df513 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,11 +33,8 @@ rvm: - jruby-9.1.17.0 - jruby-9.2.11.1 - jruby-head - - rbx-2.71828182 - - rbx-3.107 - ree matrix: allow_failures: - rvm: ruby-head - rvm: jruby-head - - rvm: rbx-2.71828182 From 62ae85493981fc17e4d497fbd32d8818cd50e908 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 17 May 2020 13:35:16 +0100 Subject: [PATCH 48/81] Add Ruby 2.7 on AppVeyor. --- appveyor.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index e0c7a4e8..ba2e2266 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -55,6 +55,12 @@ environment: - RUBY_ENGINE: ruby RUBY_VERSION: 26-x64 + - RUBY_ENGINE: ruby + RUBY_VERSION: 27 + + - RUBY_ENGINE: ruby + RUBY_VERSION: 27-x64 + - RUBY_ENGINE: jruby JRUBY_VERSION: 1.7.27 JRUBY_BUNDLER_VERSION: "~> 1.17" From 4edb23c6ef338446c12c9d4734426c6152fdf460 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 17 May 2020 14:36:23 +0100 Subject: [PATCH 49/81] Revert "Add Ruby 2.7 on AppVeyor." This reverts commit 62ae85493981fc17e4d497fbd32d8818cd50e908. SSL certificate verification is broken. https://ci.appveyor.com/project/philr/tzinfo/builds/32930413/job/ftdhm6p4b5m6ovk9 https://ci.appveyor.com/project/philr/tzinfo/builds/32930413/job/7qy49m6gg6d0uub8 --- appveyor.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ba2e2266..e0c7a4e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -55,12 +55,6 @@ environment: - RUBY_ENGINE: ruby RUBY_VERSION: 26-x64 - - RUBY_ENGINE: ruby - RUBY_VERSION: 27 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 27-x64 - - RUBY_ENGINE: jruby JRUBY_VERSION: 1.7.27 JRUBY_BUNDLER_VERSION: "~> 1.17" From 4b6f0725ecdc1ffc09b33d016e633caedaf63f4e Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 5 Nov 2020 19:57:03 +0000 Subject: [PATCH 50/81] Update to Ruby 2.7.2 and JRuby 9.2.13.0. --- .travis.yml | 4 ++-- appveyor.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index b54df513..0b1db3bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,13 +25,13 @@ rvm: - 2.4.10 - 2.5.8 - 2.6.6 - - 2.7.1 + - 2.7.2 - ruby-head - jruby-18mode - jruby-1.7.27 - jruby-9.0.5.0 - jruby-9.1.17.0 - - jruby-9.2.11.1 + - jruby-9.2.13.0 - jruby-head - ree matrix: diff --git a/appveyor.yml b/appveyor.yml index e0c7a4e8..60526e2d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -67,7 +67,7 @@ environment: JRUBY_VERSION: 9.1.17.0 - RUBY_ENGINE: jruby - JRUBY_VERSION: 9.2.11.1 + JRUBY_VERSION: 9.2.13.0 install: - if not exist vendor mkdir vendor From 4433b3eca1a40ff047f508268aecdfd590d9b833 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sat, 7 Nov 2020 15:16:35 +0000 Subject: [PATCH 51/81] Switch to travis-ci.com. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca25b752..50766f84 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ TZInfo - Ruby Timezone Library ============================== -[![RubyGems](https://img.shields.io/gem/v/tzinfo)](https://rubygems.org/gems/tzinfo) [![Travis CI Build](https://img.shields.io/travis/tzinfo/tzinfo/1.2?logo=travis)](https://travis-ci.org/tzinfo/tzinfo) [![AppVeyor Build](https://img.shields.io/appveyor/build/philr/tzinfo/1.2?logo=appveyor)](https://ci.appveyor.com/project/philr/tzinfo/branch/1.2) +[![RubyGems](https://img.shields.io/gem/v/tzinfo)](https://rubygems.org/gems/tzinfo) [![Travis CI Build](https://img.shields.io/travis/com/tzinfo/tzinfo/1.2?logo=travis)](https://travis-ci.com/tzinfo/tzinfo) [![AppVeyor Build](https://img.shields.io/appveyor/build/philr/tzinfo/1.2?logo=appveyor)](https://ci.appveyor.com/project/philr/tzinfo/branch/1.2) [TZInfo](https://tzinfo.github.io) provides daylight savings aware transformations between times in different timezones. From 5fda4c3e0f30a5a7405795d327e4373c78b1fd5d Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sat, 7 Nov 2020 17:56:13 +0000 Subject: [PATCH 52/81] Remove the deprecated sudo option. All builds now run on the virtual machine-based infrastructure. https://blog.travis-ci.com/2018-11-19-required-linux-infrastructure-migration --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0b1db3bb..5cce94a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: ruby dist: trusty -sudo: false before_install: - if [[ $TRAVIS_RUBY_VERSION =~ ^jruby- && ! $TRAVIS_RUBY_VERSION =~ ^jruby-(1|9\.[01]\.) ]]; then export JRUBY_OPTS=`echo "$JRUBY_OPTS" | sed -E -e 's/--((no)?client|server)//g'`; fi - if [[ $TRAVIS_RUBY_VERSION =~ ^jruby- ]]; then echo "JRUBY_OPTS=$JRUBY_OPTS"; fi From 6dfdfc20ebc98125c58442687cfdfa0823312ffd Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sat, 7 Nov 2020 18:01:00 +0000 Subject: [PATCH 53/81] Use up to date Linux distros to test (where possible). Older versions of Ruby are only available (or only work) on older distributions. --- .travis.yml | 62 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5cce94a4..ce031323 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,4 @@ language: ruby -dist: trusty before_install: - if [[ $TRAVIS_RUBY_VERSION =~ ^jruby- && ! $TRAVIS_RUBY_VERSION =~ ^jruby-(1|9\.[01]\.) ]]; then export JRUBY_OPTS=`echo "$JRUBY_OPTS" | sed -E -e 's/--((no)?client|server)//g'`; fi - if [[ $TRAVIS_RUBY_VERSION =~ ^jruby- ]]; then echo "JRUBY_OPTS=$JRUBY_OPTS"; fi @@ -13,27 +12,46 @@ cache: bundler env: global: - TESTOPTS=--verbose -rvm: - - 1.8.7-head - - 1.9.2-p330 - - 1.9.3-p551 - - 2.0.0-p648 - - 2.1.10 - - 2.2.10 - - 2.3.8 - - 2.4.10 - - 2.5.8 - - 2.6.6 - - 2.7.2 - - ruby-head - - jruby-18mode - - jruby-1.7.27 - - jruby-9.0.5.0 - - jruby-9.1.17.0 - - jruby-9.2.13.0 - - jruby-head - - ree -matrix: +jobs: + include: + - rvm: 1.8.7-head + dist: trusty + - rvm: 1.9.2-p330 + dist: trusty + - rvm: 1.9.3-p551 + dist: trusty + - rvm: 2.0.0-p648 + dist: trusty + - rvm: 2.1.10 + dist: trusty + - rvm: 2.2.10 + dist: trusty + - rvm: 2.3.8 + dist: bionic + - rvm: 2.4.10 + dist: focal + - rvm: 2.5.8 + dist: focal + - rvm: 2.6.6 + dist: focal + - rvm: 2.7.2 + dist: focal + - rvm: ruby-head + dist: focal + - rvm: jruby-18mode + dist: trusty + - rvm: jruby-1.7.27 + dist: trusty + - rvm: jruby-9.0.5.0 + dist: trusty + - rvm: jruby-9.1.17.0 + dist: focal + - rvm: jruby-9.2.13.0 + dist: focal + - rvm: jruby-head + dist: focal + - rvm: ree + dist: trusty allow_failures: - rvm: ruby-head - rvm: jruby-head From 4b1852416a1f57a2e3766a89835c2d35bc7fde59 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Wed, 4 Nov 2020 21:11:02 +0000 Subject: [PATCH 54/81] Support "slim" zoneinfo files produced by default by zic >= 2020b. Use the POSIX-style TZ string to calculate transitions after the last defined transition contained within the file. At the moment these transitions are generated when the file is loaded. In a later release this will likely be changed to calculate transitions on demand. Use the 64-bit section of zoneinfo files regardless of whether the runtime supports 64-bit Times. The 32-bit section is empty in "slim" zoneinfo files. Resolves #120. --- lib/tzinfo.rb | 3 + lib/tzinfo/annual_rules.rb | 51 ++ lib/tzinfo/posix_time_zone_parser.rb | 136 ++++ lib/tzinfo/time_or_datetime.rb | 11 + lib/tzinfo/transition_rule.rb | 325 ++++++++ lib/tzinfo/zoneinfo_data_source.rb | 3 +- lib/tzinfo/zoneinfo_timezone_info.rb | 295 ++++++- test/tc_annual_rules.rb | 95 +++ test/tc_posix_time_zone_parser.rb | 261 +++++++ test/tc_time_or_datetime.rb | 14 + test/tc_transition_rule.rb | 663 ++++++++++++++++ test/tc_zoneinfo_timezone_info.rb | 1065 +++++++++++++++++++++++--- 12 files changed, 2768 insertions(+), 154 deletions(-) create mode 100644 lib/tzinfo/annual_rules.rb create mode 100644 lib/tzinfo/posix_time_zone_parser.rb create mode 100644 lib/tzinfo/transition_rule.rb create mode 100644 test/tc_annual_rules.rb create mode 100644 test/tc_posix_time_zone_parser.rb create mode 100644 test/tc_transition_rule.rb diff --git a/lib/tzinfo.rb b/lib/tzinfo.rb index 0b64589b..7a11ef77 100644 --- a/lib/tzinfo.rb +++ b/lib/tzinfo.rb @@ -10,6 +10,8 @@ module TZInfo require 'tzinfo/timezone_offset' require 'tzinfo/timezone_transition' +require 'tzinfo/transition_rule' +require 'tzinfo/annual_rules' require 'tzinfo/timezone_transition_definition' require 'tzinfo/timezone_index_definition' @@ -22,6 +24,7 @@ module TZInfo require 'tzinfo/data_source' require 'tzinfo/ruby_data_source' +require 'tzinfo/posix_time_zone_parser' require 'tzinfo/zoneinfo_data_source' require 'tzinfo/timezone_period' diff --git a/lib/tzinfo/annual_rules.rb b/lib/tzinfo/annual_rules.rb new file mode 100644 index 00000000..fec436b7 --- /dev/null +++ b/lib/tzinfo/annual_rules.rb @@ -0,0 +1,51 @@ +module TZInfo + # A set of rules that define when transitions occur in time zones with + # annually occurring daylight savings time. + # + # @private + class AnnualRules #:nodoc: + # Returned by #transitions. #offset is the TimezoneOffset that applies + # from the UTC TimeOrDateTime #at. #previous_offset is the prior + # TimezoneOffset. + Transition = Struct.new(:offset, :previous_offset, :at) + + # The standard offset that applies when daylight savings time is not in + # force. + attr_reader :std_offset + + # The offset that applies when daylight savings time is in force. + attr_reader :dst_offset + + # The rule that determines when daylight savings time starts. + attr_reader :dst_start_rule + + # The rule that determines when daylight savings time ends. + attr_reader :dst_end_rule + + # Initializes a new {AnnualRules} instance. + def initialize(std_offset, dst_offset, dst_start_rule, dst_end_rule) + @std_offset = std_offset + @dst_offset = dst_offset + @dst_start_rule = dst_start_rule + @dst_end_rule = dst_end_rule + end + + # Returns the transitions between standard and daylight savings time for a + # given year. The results are ordered by time of occurrence (earliest to + # latest). + def transitions(year) + start_dst = apply_rule(@dst_start_rule, @std_offset, @dst_offset, year) + end_dst = apply_rule(@dst_end_rule, @dst_offset, @std_offset, year) + + end_dst.at < start_dst.at ? [end_dst, start_dst] : [start_dst, end_dst] + end + + private + + # Applies a given rule between offsets on a year. + def apply_rule(rule, from_offset, to_offset, year) + at = rule.at(from_offset, year) + Transition.new(to_offset, from_offset, at) + end + end +end diff --git a/lib/tzinfo/posix_time_zone_parser.rb b/lib/tzinfo/posix_time_zone_parser.rb new file mode 100644 index 00000000..af2d7e01 --- /dev/null +++ b/lib/tzinfo/posix_time_zone_parser.rb @@ -0,0 +1,136 @@ +# encoding: UTF-8 +# frozen_string_literal: true + +require 'strscan' + +module TZInfo + # An {InvalidPosixTimeZone} exception is raised if an invalid POSIX-style + # time zone string is encountered. + # + # @private + class InvalidPosixTimeZone < StandardError #:nodoc: + end + + # A parser for POSIX-style TZ strings used in zoneinfo files and specified + # by tzfile.5 and tzset.3. + # + # @private + class PosixTimeZoneParser #:nodoc: + # Parses a POSIX-style TZ string, returning either a TimezoneOffset or + # an AnnualRules instance. + def parse(tz_string) + raise InvalidPosixTimeZone unless tz_string.kind_of?(String) + return nil if tz_string.empty? + + s = StringScanner.new(tz_string) + check_scan(s, /([^-+,\d<][^-+,\d]*) | <([^>]+)>/x) + std_abbrev = s[1] || s[2] + check_scan(s, /([-+]?\d+)(?::(\d+)(?::(\d+))?)?/) + std_offset = get_offset_from_hms(s[1], s[2], s[3]) + + if s.scan(/([^-+,\d<][^-+,\d]*) | <([^>]+)>/x) + dst_abbrev = s[1] || s[2] + + if s.scan(/([-+]?\d+)(?::(\d+)(?::(\d+))?)?/) + dst_offset = get_offset_from_hms(s[1], s[2], s[3]) + else + # POSIX is negative for ahead of UTC. + dst_offset = std_offset - 3600 + end + + dst_difference = std_offset - dst_offset + + start_rule = parse_rule(s, 'start') + end_rule = parse_rule(s, 'end') + + raise InvalidPosixTimeZone, "Expected the end of a POSIX-style time zone string but found '#{s.rest}'." if s.rest? + + if start_rule.is_always_first_day_of_year? && start_rule.transition_at == 0 && + end_rule.is_always_last_day_of_year? && end_rule.transition_at == 86400 + dst_difference + # Constant daylight savings time. + # POSIX is negative for ahead of UTC. + TimezoneOffset.new(-std_offset, dst_difference, dst_abbrev.to_sym) + else + AnnualRules.new( + TimezoneOffset.new(-std_offset, 0, std_abbrev.to_sym), + TimezoneOffset.new(-std_offset, dst_difference, dst_abbrev.to_sym), + start_rule, + end_rule) + end + elsif !s.rest? + # Constant standard time. + # POSIX is negative for ahead of UTC. + TimezoneOffset.new(-std_offset, 0, std_abbrev.to_sym) + else + raise InvalidPosixTimeZone, "Expected the end of a POSIX-style time zone string but found '#{s.rest}'." + end + end + + private + + # Parses the rule from the TZ string, returning a TransitionRule. + def parse_rule(s, type) + check_scan(s, /,(?: (?: J(\d+) ) | (\d+) | (?: M(\d+)\.(\d)\.(\d) ) )/x) + julian_day_of_year = s[1] + absolute_day_of_year = s[2] + month = s[3] + week = s[4] + day_of_week = s[5] + + if s.scan(/\//) + check_scan(s, /([-+]?\d+)(?::(\d+)(?::(\d+))?)?/) + transition_at = get_seconds_after_midnight_from_hms(s[1], s[2], s[3]) + else + transition_at = 7200 + end + + begin + if julian_day_of_year + JulianDayOfYearTransitionRule.new(julian_day_of_year.to_i, transition_at) + elsif absolute_day_of_year + AbsoluteDayOfYearTransitionRule.new(absolute_day_of_year.to_i, transition_at) + elsif week == '5' + LastDayOfMonthTransitionRule.new(month.to_i, day_of_week.to_i, transition_at) + else + DayOfMonthTransitionRule.new(month.to_i, week.to_i, day_of_week.to_i, transition_at) + end + rescue ArgumentError => e + raise InvalidPosixTimeZone, "Invalid #{type} rule in POSIX-style time zone string: #{e}" + end + end + + # Returns an offset in seconds from hh:mm:ss values. The value can be + # negative. -02:33:12 would represent 2 hours, 33 minutes and 12 seconds + # ahead of UTC. + def get_offset_from_hms(h, m, s) + h = h.to_i + m = m.to_i + s = s.to_i + raise InvalidPosixTimeZone, "Invalid minute #{m} in offset for POSIX-style time zone string." if m > 59 + raise InvalidPosixTimeZone, "Invalid second #{s} in offset for POSIX-style time zone string." if s > 59 + magnitude = (h.abs * 60 + m) * 60 + s + h < 0 ? -magnitude : magnitude + end + + # Returns the seconds from midnight from hh:mm:ss values. Hours can exceed + # 24 for a time on the following day. Hours can be negative to subtract + # hours from midnight on the given day. -02:33:12 represents 22:33:12 on + # the prior day. + def get_seconds_after_midnight_from_hms(h, m, s) + h = h.to_i + m = m.to_i + s = s.to_i + raise InvalidPosixTimeZone, "Invalid minute #{m} in time for POSIX-style time zone string." if m > 59 + raise InvalidPosixTimeZone, "Invalid second #{s} in time for POSIX-style time zone string." if s > 59 + (h * 3600) + m * 60 + s + end + + # Scans for a pattern and raises an exception if the pattern does not + # match the input. + def check_scan(s, pattern) + result = s.scan(pattern) + raise InvalidPosixTimeZone, "Expected '#{s.rest}' to match #{pattern} in POSIX-style time zone string." unless result + result + end + end +end diff --git a/lib/tzinfo/time_or_datetime.rb b/lib/tzinfo/time_or_datetime.rb index f358005f..56c33147 100644 --- a/lib/tzinfo/time_or_datetime.rb +++ b/lib/tzinfo/time_or_datetime.rb @@ -160,6 +160,17 @@ def mday end end alias :day :mday + + # Returns the day of the week (0..6 for Sunday to Saturday). + def wday + if @time + @time.wday + elsif @datetime + @datetime.wday + else + to_time.wday + end + end # Returns the hour of the day (0..23). def hour diff --git a/lib/tzinfo/transition_rule.rb b/lib/tzinfo/transition_rule.rb new file mode 100644 index 00000000..b8d16052 --- /dev/null +++ b/lib/tzinfo/transition_rule.rb @@ -0,0 +1,325 @@ +require 'date' + +module TZInfo + # Base class for rules definining the transition between standard and daylight + # savings time. + class TransitionRule #:nodoc: + # Returns the number of seconds after midnight local time on the day + # identified by the rule at which the transition occurs. Can be negative to + # denote a time on the prior day. Can be greater than or equal to 86,400 to + # denote a time of the following day. + attr_reader :transition_at + + # Initializes a new TransitionRule. + def initialize(transition_at) + raise ArgumentError, 'Invalid transition_at' unless transition_at.kind_of?(Integer) + @transition_at = transition_at + end + + # Calculates the UTC time of the transition from a given offset on a given + # year. + def at(offset, year) + day = get_day(year) + day.add_with_convert(@transition_at - offset.utc_total_offset) + end + + # Determines if this TransitionRule is equal to another instance. + def ==(r) + r.kind_of?(TransitionRule) && @transition_at == r.transition_at + end + alias eql? == + + # Returns a hash based on hash_args (defaulting to transition_at). + def hash + hash_args.hash + end + + protected + + # Returns an Array of parameters that will influence the output of hash. + def hash_args + [@transition_at] + end + + def new_time_or_datetime(year, month = 1, day = 1) + result = if ((year >= 2039 || (year == 2038 && (month >= 2 || (month == 1 && day >= 20)))) && !RubyCoreSupport.time_supports_64bit) || + (year < 1970 && !RubyCoreSupport.time_supports_negative) + + # Time handles 29 February on a non-leap year as 1 March. + # DateTime rejects. Advance manually. + if month == 2 && day == 29 && !Date.gregorian_leap?(year) + month = 3 + day = 1 + end + + RubyCoreSupport.datetime_new(year, month, day) + else + Time.utc(year, month, day) + end + + TimeOrDateTime.wrap(result) + end + end + + # A base class for transition rules that activate based on an integer day of + # the year. + # + # @private + class DayOfYearTransitionRule < TransitionRule #:nodoc: + # Initializes a new DayOfYearTransitionRule. + def initialize(day, transition_at) + super(transition_at) + raise ArgumentError, 'Invalid day' unless day.kind_of?(Integer) + @seconds = day * 86400 + end + + # Determines if this DayOfYearTransitionRule is equal to another instance. + def ==(r) + super(r) && r.kind_of?(DayOfYearTransitionRule) && @seconds == r.seconds + end + alias eql? == + + protected + + # @return [Integer] the day multipled by the number of seconds in a day. + attr_reader :seconds + + # Returns an Array of parameters that will influence the output of hash. + def hash_args + [@seconds] + super + end + end + + # Defines transitions that occur on the zero-based nth day of the year. + # + # Day 0 is 1 January. + # + # Leap days are counted. Day 59 will be 29 February on a leap year and 1 March + # on a non-leap year. Day 365 will be 31 December on a leap year and 1 January + # the following year on a non-leap year. + # + # @private + class AbsoluteDayOfYearTransitionRule < DayOfYearTransitionRule #:nodoc: + # Initializes a new AbsoluteDayOfYearTransitionRule. + def initialize(day, transition_at = 0) + super(day, transition_at) + raise ArgumentError, 'Invalid day' unless day >= 0 && day <= 365 + end + + # Returns true if the day specified by this transition is the first in the + # year (a day number of 0), otherwise false. + def is_always_first_day_of_year? + seconds == 0 + end + + # @returns false. + def is_always_last_day_of_year? + false + end + + # Determines if this AbsoluteDayOfYearTransitionRule is equal to another + # instance. + def ==(r) + super(r) && r.kind_of?(AbsoluteDayOfYearTransitionRule) + end + alias eql? == + + protected + + # Returns a TimeOrDateTime representing midnight local time on the day + # specified by the rule for the given offset and year. + def get_day(year) + new_time_or_datetime(year).add_with_convert(seconds) + end + + # Returns an Array of parameters that will influence the output of hash. + def hash_args + [AbsoluteDayOfYearTransitionRule] + super + end + end + + # Defines transitions that occur on the one-based nth Julian day of the year. + # + # Leap days are not counted. Day 1 is 1 January. Day 60 is always 1 March. + # Day 365 is always 31 December. + # + # @private + class JulianDayOfYearTransitionRule < DayOfYearTransitionRule #:nodoc: + # The 60 days in seconds. + LEAP = 60 * 86400 + + # The length of a non-leap year in seconds. + YEAR = 365 * 86400 + + # Initializes a new JulianDayOfYearTransitionRule. + def initialize(day, transition_at = 0) + super(day, transition_at) + raise ArgumentError, 'Invalid day' unless day >= 1 && day <= 365 + end + + # Returns true if the day specified by this transition is the first in the + # year (a day number of 1), otherwise false. + def is_always_first_day_of_year? + seconds == 86400 + end + + # Returns true if the day specified by this transition is the last in the + # year (a day number of 365), otherwise false. + def is_always_last_day_of_year? + seconds == YEAR + end + + # Determines if this JulianDayOfYearTransitionRule is equal to another + # instance. + def ==(r) + super(r) && r.kind_of?(JulianDayOfYearTransitionRule) + end + alias eql? == + + protected + + # Returns a TimeOrDateTime representing midnight local time on the day + # specified by the rule for the given offset and year. + def get_day(year) + # Returns 1 March on non-leap years. + leap = new_time_or_datetime(year, 2, 29) + diff = seconds - LEAP + diff += 86400 if diff >= 0 && leap.mday == 29 + leap.add_with_convert(diff) + end + + # Returns an Array of parameters that will influence the output of hash. + def hash_args + [JulianDayOfYearTransitionRule] + super + end + end + + # A base class for rules that transition on a particular day of week of a + # given week (subclasses specify which week of the month). + # + # @private + class DayOfWeekTransitionRule < TransitionRule #:nodoc: + # Initializes a new DayOfWeekTransitionRule. + def initialize(month, day_of_week, transition_at) + super(transition_at) + raise ArgumentError, 'Invalid month' unless month.kind_of?(Integer) && month >= 1 && month <= 12 + raise ArgumentError, 'Invalid day_of_week' unless day_of_week.kind_of?(Integer) && day_of_week >= 0 && day_of_week <= 6 + @month = month + @day_of_week = day_of_week + end + + # Returns false. + def is_always_first_day_of_year? + false + end + + # Returns false. + def is_always_last_day_of_year? + false + end + + # Determines if this DayOfWeekTransitionRule is equal to another instance. + def ==(r) + super(r) && r.kind_of?(DayOfWeekTransitionRule) && @month == r.month && @day_of_week == r.day_of_week + end + alias eql? == + + protected + + # Returns the month of the year (1 to 12). + attr_reader :month + + # Returns the day of the week (0 to 6 for Sunday to Monday). + attr_reader :day_of_week + + # Returns an Array of parameters that will influence the output of hash. + def hash_args + [@month, @day_of_week] + super + end + end + + # A rule that transitions on the nth occurrence of a particular day of week + # of a calendar month. + # + # @private + class DayOfMonthTransitionRule < DayOfWeekTransitionRule #:nodoc: + # Initializes a new DayOfMonthTransitionRule. + def initialize(month, week, day_of_week, transition_at = 0) + super(month, day_of_week, transition_at) + raise ArgumentError, 'Invalid week' unless week.kind_of?(Integer) && week >= 1 && week <= 4 + @offset_start = (week - 1) * 7 + 1 + end + + # Determines if this DayOfMonthTransitionRule is equal to another instance. + def ==(r) + super(r) && r.kind_of?(DayOfMonthTransitionRule) && @offset_start == r.offset_start + end + alias eql? == + + protected + + # Returns the day the week starts on for a month starting on a Sunday. + attr_reader :offset_start + + # Returns a TimeOrDateTime representing midnight local time on the day + # specified by the rule for the given offset and year. + def get_day(year) + candidate = new_time_or_datetime(year, month, @offset_start) + diff = day_of_week - candidate.wday + + if diff < 0 + candidate.add_with_convert((7 + diff) * 86400) + elsif diff > 0 + candidate.add_with_convert(diff * 86400) + else + candidate + end + end + + # Returns an Array of parameters that will influence the output of hash. + def hash_args + [@offset_start] + super + end + end + + # A rule that transitions on the last occurrence of a particular day of week + # of a calendar month. + # + # @private + class LastDayOfMonthTransitionRule < DayOfWeekTransitionRule #:nodoc: + # Initializes a new LastDayOfMonthTransitionRule. + def initialize(month, day_of_week, transition_at = 0) + super(month, day_of_week, transition_at) + end + + # Determines if this LastDayOfMonthTransitionRule is equal to another + # instance. + def ==(r) + super(r) && r.kind_of?(LastDayOfMonthTransitionRule) + end + alias eql? == + + protected + + # Returns a TimeOrDateTime representing midnight local time on the day + # specified by the rule for the given offset and year. + def get_day(year) + next_month = month + 1 + if next_month == 13 + year += 1 + next_month = 1 + end + + candidate = new_time_or_datetime(year, next_month).add_with_convert(-86400) + diff = candidate.wday - day_of_week + + if diff < 0 + candidate - (diff + 7) * 86400 + elsif diff > 0 + candidate - diff * 86400 + else + candidate + end + end + end +end diff --git a/lib/tzinfo/zoneinfo_data_source.rb b/lib/tzinfo/zoneinfo_data_source.rb index e781d78c..3959090f 100644 --- a/lib/tzinfo/zoneinfo_data_source.rb +++ b/lib/tzinfo/zoneinfo_data_source.rb @@ -192,6 +192,7 @@ def initialize(zoneinfo_dir = nil, alternate_iso3166_tab_path = nil) @zoneinfo_dir = File.expand_path(@zoneinfo_dir).freeze @timezone_index = load_timezone_index.freeze @country_index = load_country_index(iso3166_tab_path, zone_tab_path).freeze + @posix_tz_parser = PosixTimeZoneParser.new end # Returns a TimezoneInfo instance for a given identifier. @@ -208,7 +209,7 @@ def load_timezone_info(identifier) path.untaint begin - ZoneinfoTimezoneInfo.new(identifier, path) + ZoneinfoTimezoneInfo.new(identifier, path, @posix_tz_parser) rescue InvalidZoneinfoFile => e raise InvalidTimezoneIdentifier, e.message end diff --git a/lib/tzinfo/zoneinfo_timezone_info.rb b/lib/tzinfo/zoneinfo_timezone_info.rb index 4688ca61..4f28a19c 100644 --- a/lib/tzinfo/zoneinfo_timezone_info.rb +++ b/lib/tzinfo/zoneinfo_timezone_info.rb @@ -12,7 +12,11 @@ class InvalidZoneinfoFile < StandardError # # @private class ZoneinfoTimezoneInfo < TransitionDataTimezoneInfo #:nodoc: - + # The year to generate transitions up to. + # + # @private + GENERATE_UP_TO = RubyCoreSupport.time_supports_64bit ? Time.now.utc.year + 100 : 2037 + # Minimum supported timestamp (inclusive). # # Time.utc(1700, 1, 1).to_i @@ -23,13 +27,13 @@ class ZoneinfoTimezoneInfo < TransitionDataTimezoneInfo #:nodoc: # Time.utc(2500, 1, 1).to_i MAX_TIMESTAMP = 16725225600 - # Constructs the new ZoneinfoTimezoneInfo with an identifier and path - # to the file. - def initialize(identifier, file_path) + # Constructs the new ZoneinfoTimezoneInfo with an identifier, path + # to the file and parser to use to parse the POSIX-like TZ string. + def initialize(identifier, file_path, posix_tz_parser) super(identifier) File.open(file_path, 'rb') do |file| - parse(file) + parse(file, posix_tz_parser) end end @@ -59,7 +63,7 @@ def check_read(file, bytes) result end - + # Zoneinfo files don't include the offset from standard time (std_offset) # for DST periods. Derive the base offset (utc_offset) where DST is # observed from either the previous or next non-DST period. @@ -143,6 +147,184 @@ def derive_offsets(transitions, offsets) first_offset_index end + # Remove transitions before a minimum supported value. If there is not a + # transition exactly on the minimum supported value move the latest from + # before up to the minimum supported value. + def remove_unsupported_negative_transitions(transitions, min_supported) + result = transitions.drop_while {|t| t[:at] < min_supported } + if result.empty? || (result[0][:at] > min_supported && result.length < transitions.length) + last_before = transitions[-1 - result.length] + last_before[:at] = min_supported + [last_before] + result + else + result + end + end + + # Determines if the offset from a transition matches the offset from a + # rule. This is a looser match than TimezoneOffset#==, not requiring that + # the utc_offset and std_offset both match (which have to be derived for + # transitions, but are known for rules. + def offset_matches_rule?(offset, rule_offset) + offset[:utc_total_offset] == rule_offset.utc_total_offset && + offset[:is_dst] == rule_offset.dst? && + offset[:abbr] == rule_offset.abbreviation.to_s + end + + # Determins if the offset from a transition exactly matches the offset + # from a rule. + def offset_equals_rule?(offset, rule_offset) + offset_matches_rule?(offset, rule_offset) && + (offset[:utc_offset] || (offset[:is_dst] ? offset[:utc_total_offset] - 3600 : offset[:utc_total_offset])) == rule_offset.utc_offset + end + + # Finds an offset hash that is an exact match to the rule offset specified. + def find_existing_offset_index(offsets, rule_offset) + offsets.find_index {|o| offset_equals_rule?(o, rule_offset) } + end + + # Gets an existing matching offset index or adds a new offset hash for a + # rule offset. + def get_rule_offset_index(offsets, offset) + index = find_existing_offset_index(offsets, offset) + unless index + index = offsets.length + offsets << {:utc_total_offset => offset.utc_total_offset, :utc_offset => offset.utc_offset, :is_dst => offset.dst?, :abbr => offset.abbreviation} + end + index + end + + # Gets a hash mapping rule offsets to indexes in offsets, creating new + # offset hashes if required. + def get_rule_offset_indexes(offsets, annual_rules) + { + annual_rules.std_offset => get_rule_offset_index(offsets, annual_rules.std_offset), + annual_rules.dst_offset => get_rule_offset_index(offsets, annual_rules.dst_offset) + } + end + + # Converts an array of rule transitions to hashes. + def convert_transitions_to_hashes(offset_indexes, transitions) + transitions.map {|t| {:at => t.at.to_i, :offset => offset_indexes[t.offset]} } + end + + # Apply the rules from the TZ string when there were no defined + # transitions. Checks for a matching offset. Returns the rules-based + # constant offset or generates transitions from 1970 until 100 years into + # the future (at the time of loading zoneinfo_timezone_info.rb) or 2037 if + # limited to 32-bit Times. + def apply_rules_without_transitions(file, offsets, first_offset_index, rules) + first_offset = offsets[first_offset_index] + + if rules.kind_of?(TimezoneOffset) + unless offset_matches_rule?(first_offset, rules) + raise InvalidZoneinfoFile, "Constant offset POSIX-style TZ string does not match constant offset in file '#{file.path}'." + end + + first_offset[:utc_offset] = rules.utc_offset + [] + else + transitions = 1970.upto(GENERATE_UP_TO).map {|y| rules.transitions(y) }.flatten + first_transition = transitions[0] + + if offset_matches_rule?(first_offset, first_transition.previous_offset) + # Correct the first offset if it isn't an exact match. + first_offset[:utc_offset] = first_transition.previous_offset.utc_offset + else + # Not transitioning from the designated first offset. + if offset_matches_rule?(first_offset, first_transition.offset) + # Correct the first offset if it isn't an exact match. + first_offset[:utc_offset] = first_transition.offset.utc_offset + + # Skip an unnecessary transition to the first offset. + transitions.shift + end + + # If the first offset doesn't match either the offset or previous + # offset, then it will be retained. + end + + offset_indexes = get_rule_offset_indexes(offsets, rules) + convert_transitions_to_hashes(offset_indexes, transitions) + end + end + + # Validates the rules offset against the offset of the last defined + # transition. Replaces the transition with an equivalent using the rules + # offset if the rules give a different definition for the base offset. + def replace_last_transition_offset_if_valid_and_needed(file, transitions, offsets) + last_transition = transitions.last + last_offset = offsets[last_transition[:offset]] + rule_offset = yield last_offset + + unless offset_matches_rule?(last_offset, rule_offset) + raise InvalidZoneinfoFile, "Offset from POSIX-style TZ string does not match final transition in file '#{file.path}'." + end + + # The total_utc_offset and abbreviation must always be the same. The + # base utc_offset and std_offset might differ. In which case the rule + # should be used as it will be more precise. + last_offset[:utc_offset] = rule_offset.utc_offset + last_transition + end + + # todo: port over validate_and_fix_last_defined_transition_offset + # when fixing the previous offset will need to define a new one + + # Validates the offset indicated to be observed by the rules before the + # first generated transition against the offset of the last defined + # transition. + # + # Fix the last defined transition if it differ on just base/std offsets + # (which are derived). Raise an error if the observed UTC offset or + # abbreviations differ. + def validate_and_fix_last_defined_transition_offset(file, offsets, last_defined, first_rule_offset) + offset_of_last_defined = offsets[last_defined[:offset]] + + if offset_equals_rule?(offset_of_last_defined, first_rule_offset) + last_defined + else + if offset_matches_rule?(offset_of_last_defined, first_rule_offset) + # The same overall offset, but differing in the base or std + # offset (which are derived). Correct by using the rule. + + offset_index = get_rule_offset_index(offsets, first_rule_offset) + {:at => last_defined[:at], :offset => offset_index} + else + raise InvalidZoneinfoFile, "The first offset indicated by the POSIX-style TZ string did not match the final defined offset in file '#{file.path}'." + end + end + end + + # Apply the rules from the TZ string when there were defined transitions. + # Checks for a matching offset with the last transition. Redefines the + # last transition if required and if the rules don't specific a constant + # offset, generates transitions until 100 years into the future (at the + # time of loading zoneinfo_timezone_info.rb) or 2037 if limited to 32-bit + # Times. + def apply_rules_with_transitions(file, transitions, offsets, first_offset_index, rules) + last_defined = transitions[-1] + + if rules.kind_of?(TimezoneOffset) + transitions[-1] = validate_and_fix_last_defined_transition_offset(file, offsets, last_defined, rules) + else + previous_offset_index = transitions.length > 1 ? transitions[-2][:offset] : first_offset_index + previous_offset = offsets[previous_offset_index] + last_year = (Time.at(last_defined[:at]).utc + previous_offset[:utc_total_offset]).year + + if last_year <= GENERATE_UP_TO + generated = rules.transitions(last_year).find_all {|t| t.at > last_defined[:at] } + + (last_year + 1).upto(GENERATE_UP_TO).map {|y| rules.transitions(y) }.flatten + + unless generated.empty? + transitions[-1] = validate_and_fix_last_defined_transition_offset(file, offsets, last_defined, generated[0].previous_offset) + rule_offset_indexes = get_rule_offset_indexes(offsets, rules) + transitions.concat(convert_transitions_to_hashes(rule_offset_indexes, generated)) + end + end + end + end + # Defines an offset for the timezone based on the given index and offset # Hash. def define_offset(index, offset) @@ -168,21 +350,24 @@ def define_offset(index, offset) end # Parses a zoneinfo file and intializes the DataTimezoneInfo structures. - def parse(file) - magic, version, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt = + def parse(file, posix_tz_parser) + magic, version, ttisutccnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt = check_read(file, 44).unpack('a4 a x15 NNNNNN') if magic != 'TZif' raise InvalidZoneinfoFile, "The file '#{file.path}' does not start with the expected header." end - if (version == '2' || version == '3') && RubyCoreSupport.time_supports_64bit - # Skip the first 32-bit section and read the header of the second 64-bit section - file.seek(timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisgmtcnt + ttisstdcnt, IO::SEEK_CUR) + if version == '2' || version == '3' + # Skip the first 32-bit section and read the header of the second + # 64-bit section. The 64-bit section is always used even if the + # runtime platform doesn't support 64-bit timestamps. In "slim" format + # zoneinfo files the 32-bit section will be empty. + file.seek(timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisstdcnt + ttisutccnt, IO::SEEK_CUR) prev_version = version - magic, version, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt = + magic, version, ttisutccnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt = check_read(file, 44).unpack('a4 a x15 NNNNNN') unless magic == 'TZif' && (version == prev_version) @@ -230,7 +415,6 @@ def parse(file) unless isdst offset[:utc_offset] = gmtoff - offset[:std_offset] = 0 end offsets << offset @@ -238,6 +422,23 @@ def parse(file) abbrev = check_read(file, charcnt) + if using_64bit + # Skip to the POSIX-style TZ string. + file.seek(ttisstdcnt + ttisutccnt, IO::SEEK_CUR) # + leapcnt * 8, but leapcnt is checked above and guaranteed to be 0. + tz_string_start = check_read(file, 1) + raise InvalidZoneinfoFile, "Expected newline starting POSIX-style TZ string in file '#{file.path}'." unless tz_string_start == "\n" + tz_string = RubyCoreSupport.force_encoding(file.readline("\n"), 'UTF-8') + raise InvalidZoneinfoFile, "Expected newline ending POSIX-style TZ string in file '#{file.path}'." unless tz_string.chomp!("\n") + + begin + rules = posix_tz_parser.parse(tz_string) + rescue InvalidPosixTimeZone => e + raise InvalidZoneinfoFile, "Failed to parse POSIX-style TZ string in file '#{file.path}': #{e}" + end + else + rules = nil + end + offsets.each do |o| abbrev_start = o[:abbr_index] raise InvalidZoneinfoFile, "Abbreviation index is out of range in file '#{file.path}'" unless abbrev_start < abbrev.length @@ -256,44 +457,58 @@ def parse(file) # Derive the offsets from standard time (std_offset). first_offset_index = derive_offsets(transitions, offsets) - - define_offset(first_offset_index, offsets[first_offset_index]) - - offsets.each_with_index do |o, i| - define_offset(i, o) unless i == first_offset_index - end - - if !using_64bit && !RubyCoreSupport.time_supports_negative - # Filter out transitions that are not supported by Time on this - # platform. - # Move the last transition before the epoch up to the epoch. This - # allows for accurate conversions for all supported timestamps on the - # platform. + # Filter out transitions that are not supported by Time on this + # platform. + unless transitions.empty? + if !RubyCoreSupport.time_supports_negative + transitions = remove_unsupported_negative_transitions(transitions, 0) + elsif !RubyCoreSupport.time_supports_64bit + transitions = remove_unsupported_negative_transitions(transitions, -2**31) + else + # Ignore transitions that occur outside of a defined window. The + # transition index cannot handle a large range of transition times. + # + # This is primarily intended to ignore the far in the past + # transition added in zic 2014c (at timestamp -2**63 in zic 2014c + # and at the approximate time of the big bang from zic 2014d). + # + # Assumes MIN_TIMESTAMP is less than -2**31. + transitions = remove_unsupported_negative_transitions(transitions, MIN_TIMESTAMP) + end - before_epoch, after_epoch = transitions.partition {|t| t[:at] < 0} + if !RubyCoreSupport.time_supports_64bit + i = transitions.find_index {|t| t[:at] >= 2**31 } + had_later_transition = !!i + transitions = transitions.first(i) if i + else + had_later_transition = false + end + end - if before_epoch.length > 0 && after_epoch.length > 0 && after_epoch.first[:at] != 0 - last_before = before_epoch.last - last_before[:at] = 0 - transitions = [last_before] + after_epoch + if rules && !had_later_transition + if transitions.empty? + transitions = apply_rules_without_transitions(file, offsets, first_offset_index, rules) else - transitions = after_epoch + apply_rules_with_transitions(file, transitions, offsets, first_offset_index, rules) end end + + define_offset(first_offset_index, offsets[first_offset_index]) + + used_offset_indexes = transitions.map {|t| t[:offset] }.to_set + + offsets.each_with_index do |o, i| + define_offset(i, o) if i != first_offset_index && used_offset_indexes.include?(i) + end # Ignore transitions that occur outside of a defined window. The # transition index cannot handle a large range of transition times. - # - # This is primarily intended to ignore the far in the past transition - # added in zic 2014c (at timestamp -2**63 in zic 2014c and at the - # approximate time of the big bang from zic 2014d). transitions.each do |t| at = t[:at] - if at >= MIN_TIMESTAMP && at < MAX_TIMESTAMP - time = Time.at(at).utc - transition time.year, time.mon, t[:offset], at - end + break if at >= MAX_TIMESTAMP + time = Time.at(at).utc + transition time.year, time.mon, t[:offset], at end end end diff --git a/test/tc_annual_rules.rb b/test/tc_annual_rules.rb new file mode 100644 index 00000000..610d878f --- /dev/null +++ b/test/tc_annual_rules.rb @@ -0,0 +1,95 @@ +require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils') + +include TZInfo + +class TCAnnualRules < Minitest::Test + + def test_initialize + std_offset = TimezoneOffset.new(0, 0, 'GMT') + dst_offset = TimezoneOffset.new(0, 3600, 'BST') + dst_start_rule = FakeAlwaysDateAdjustmentRule.new(3, 1) + dst_end_rule = FakeAlwaysDateAdjustmentRule.new(10, 1) + + rules = AnnualRules.new(std_offset, dst_offset, dst_start_rule, dst_end_rule) + assert_same(std_offset, rules.std_offset) + assert_same(dst_offset, rules.dst_offset) + assert_same(dst_start_rule, rules.dst_start_rule) + assert_same(dst_end_rule, rules.dst_end_rule) + end + + [2020, 2021].each do |year| + define_method "test_transitions_for_dst_mid_year_#{year}" do + std_offset = TimezoneOffset.new(3600, 0, 'TEST') + dst_offset = TimezoneOffset.new(3600, 3600, 'TESTS') + + rules = AnnualRules.new( + std_offset, + dst_offset, + FakeAlwaysDateAdjustmentRule.new(3, 21), + FakeAlwaysDateAdjustmentRule.new(10, 22) + ) + + result = rules.transitions(year) + + expected = [ + AnnualRules::Transition.new(dst_offset, std_offset, TimeOrDateTime.wrap(Time.utc(year, 3, 21, 0, 0, 0) - 3600)), + AnnualRules::Transition.new(std_offset, dst_offset, TimeOrDateTime.wrap(Time.utc(year, 10, 22, 0, 0, 0) - 7200)) + ] + + assert_equal(expected, result) + end + + define_method "test_transitions_for_dst_start_and_end_of_year_#{year}" do + std_offset = TimezoneOffset.new(3600, 0, 'TEST') + dst_offset = TimezoneOffset.new(3600, 3600, 'TESTS') + + rules = AnnualRules.new( + std_offset, + dst_offset, + FakeAlwaysDateAdjustmentRule.new(10, 22), + FakeAlwaysDateAdjustmentRule.new(3, 21) + ) + + result = rules.transitions(year) + + expected = [ + AnnualRules::Transition.new(std_offset, dst_offset, TimeOrDateTime.wrap(Time.utc(year, 3, 21, 0, 0, 0) - 7200)), + AnnualRules::Transition.new(dst_offset, std_offset, TimeOrDateTime.wrap(Time.utc(year, 10, 22, 0, 0, 0) - 3600)) + ] + + assert_equal(expected, result) + end + + define_method "test_transitions_for_negative_dst_start_and_end_of_year_#{year}" do + std_offset = TimezoneOffset.new(7200, 0, 'TEST') + dst_offset = TimezoneOffset.new(7200, -3600, 'TESTW') + + rules = AnnualRules.new( + std_offset, + dst_offset, + FakeAlwaysDateAdjustmentRule.new(10, 22), + FakeAlwaysDateAdjustmentRule.new(3, 21) + ) + + result = rules.transitions(year) + + expected = [ + AnnualRules::Transition.new(std_offset, dst_offset, TimeOrDateTime.wrap(Time.utc(year, 3, 21, 0, 0, 0) - 3600)), + AnnualRules::Transition.new(dst_offset, std_offset, TimeOrDateTime.wrap(Time.utc(year, 10, 22, 0, 0, 0) - 7200)) + ] + + assert_equal(expected, result) + end + end + + class FakeAlwaysDateAdjustmentRule + def initialize(month, day) + @month = month + @day = day + end + + def at(offset, year) + TimeOrDateTime.wrap(Time.utc(year, @month, @day, 0, 0, 0) - offset.utc_total_offset) + end + end +end diff --git a/test/tc_posix_time_zone_parser.rb b/test/tc_posix_time_zone_parser.rb new file mode 100644 index 00000000..1e8f1537 --- /dev/null +++ b/test/tc_posix_time_zone_parser.rb @@ -0,0 +1,261 @@ +require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils') + +include TZInfo + +class TCPosixTimeZoneParser < Minitest::Test + HOUR = 3600 + MINUTE = 60 + + class << self + private + + def append_time_to_rule(day_rule, time) + time ? "#{day_rule}/#{time}" : day_rule + end + + def define_invalid_dst_rule_tests(type, rule) + define_method "test_#{type}_dst_start_rule_for_invalid_#{rule}" do + tz_string = "STD-1DST,#{rule},300" + assert_raises(InvalidPosixTimeZone) { @parser.parse(tz_string) } + end + + define_method "test_#{type}_dst_end_rule_for_invalid_#{rule}" do + tz_string = "STD-1DST,60,#{rule}" + assert_raises(InvalidPosixTimeZone) { @parser.parse(tz_string) } + end + end + end + + def setup + @parser = PosixTimeZoneParser.new + end + + def test_empty_rule_returns_nil + result = @parser.parse('') + assert_nil(result) + end + + ABBREVIATIONS_WITH_OFFSETS = [ + ['UTC0', :UTC, 0], + ['U0', :U, 0], + ['West1', :West, -HOUR], + ['East-1', :East, HOUR], + ['<-05>5', :'-05', -5 * HOUR], + ['<+12>-12', :'+12', 12 * HOUR], + ['HMM2:30', :HMM, -(2 * HOUR + 30 * MINUTE)], + ['HHMM02:30', :HHMM, -(2 * HOUR + 30 * MINUTE)], + ['HHMM+02:30', :HHMM, -(2 * HOUR + 30 * MINUTE)], + ['HHMSS-12:5:50', :HHMSS, 12 * HOUR + 5 * MINUTE + 50], + ['HHMMSS-12:05:50', :HHMMSS, 12 * HOUR + 5 * MINUTE + 50], + ['HHMMS-12:05:7', :HHMMS, 12 * HOUR + 5 * MINUTE + 7] + ] + + ABBREVIATIONS_WITH_OFFSETS.each do |(tz_string, expected_abbrev, expected_base_offset)| + define_method "test_std_only_returns_std_offset_#{tz_string}" do + result = @parser.parse(tz_string) + expected = TimezoneOffset.new(expected_base_offset, 0, expected_abbrev) + assert_equal(expected, result) + end + end + + ABBREVIATIONS_WITH_OFFSETS.each do |(abbrev_and_offset, expected_abbrev, expected_base_offset)| + define_method "test_std_offset_#{abbrev_and_offset}" do + result = @parser.parse(abbrev_and_offset + 'DST,60,300') + expected_std_offset = TimezoneOffset.new(expected_base_offset, 0, expected_abbrev) + assert_equal(expected_std_offset, result.std_offset) + end + end + + [ + ['Zero0One-1', :One, 0, HOUR], + ['Zero0One', :One, 0, HOUR], + ['Z0O', :O, 0, HOUR], + ['West1WestS0', :WestS, -HOUR, HOUR], + ['West1WestS', :WestS, -HOUR, HOUR], + ['East-1EastS-2', :EastS, HOUR, HOUR], + ['East-1EastS', :EastS, HOUR, HOUR], + ['Neg2NegS3', :NegS, -2 * HOUR, -HOUR], + ['<-05>5<-04>4', :'-04', -5 * HOUR, HOUR], + ['STD5<-04>4', :'-04', -5 * HOUR, HOUR], + ['<+12>-12<+13>-13', :'+13', 12 * HOUR, HOUR], + ['STD-12<+13>-13', :'+13', 12 * HOUR, HOUR], + ['HMM2:30SHMM1:15', :SHMM, -(2 * HOUR + 30 * MINUTE), HOUR + 15 * MINUTE], + ['HHMM02:30SHHMM01:15', :SHHMM, -(2 * HOUR + 30 * MINUTE), HOUR + 15 * MINUTE], + ['HHMM+02:30SHHMM+01:15', :SHHMM, -(2 * HOUR + 30 * MINUTE), HOUR + 15 * MINUTE], + ['HHMSS-12:5:50SHHMSS-13:4:30', :SHHMSS, 12 * HOUR + 5 * MINUTE + 50, 58 * MINUTE + 40], + ['HHMMSS-12:05:50SHHMMSS-13:04:30', :SHHMMSS, 12 * HOUR + 5 * MINUTE + 50, 58 * MINUTE + 40], + ['HHMMS-12:05:7SHHMMSS-13:06:8', :SHHMMSS, 12 * HOUR + 5 * MINUTE + 7, HOUR + MINUTE + 1], + ].each do |(abbrevs_and_offsets, expected_abbrev, expected_base_offset, expected_std_offset)| + define_method "test_dst_offset_#{abbrevs_and_offsets}" do + result = @parser.parse(abbrevs_and_offsets + ',60,300') + expected_dst_offset = TimezoneOffset.new(expected_base_offset, expected_std_offset, expected_abbrev) + assert_equal(expected_dst_offset, result.dst_offset) + end + end + + ['01:-1', '01:60', '01:00:-1', '01:00:60'].each do |abbrev_and_offset| + ['', 'DST,60,300'].each do |dst_suffix| + tz_string = abbrev_and_offset + dst_suffix + define_method "test_std_offset_invalid_#{tz_string}" do + assert_raises(InvalidPosixTimeZone) { @parser.parse(tz_string) } + end + end + + tz_string = "STD1#{abbrev_and_offset},60,300" + define_method "test_dst_offset_invalid_#{tz_string}" do + assert_raises(InvalidPosixTimeZone) { @parser.parse(tz_string) } + end + end + + [ + [nil, 2 * HOUR], + ['2', 2 * HOUR], + ['+2', 2 * HOUR], + ['-2', -2 * HOUR], + ['2:3:4', 2 * HOUR + 3 * MINUTE + 4], + ['02:03:04', 2 * HOUR + 3 * MINUTE + 4], + ['-2:3:4', -2 * HOUR + 3 * MINUTE + 4], # 22:03:04 on the day prior to the one specified + ['-02:03:04', -2 * HOUR + 3 * MINUTE + 4], + ['167', 167 * HOUR], + ['-167', -167 * HOUR] + ].each do |(time, expected_offset_from_midnight)| + [ + ['J1', 1], + ['J365', 365] + ].each do |(julian_day_rule, expected_julian_day)| + rule = append_time_to_rule(julian_day_rule, time) + + define_method "test_julian_day_dst_start_rule_for_#{rule}" do + result = @parser.parse("STD-1DST,#{rule},300") + expected_dst_start_rule = JulianDayOfYearTransitionRule.new(expected_julian_day, expected_offset_from_midnight) + assert_equal(expected_dst_start_rule, result.dst_start_rule) + end + + define_method "test_julian_day_dst_end_rule_for_#{rule}" do + result = @parser.parse("STD-1DST,60,#{rule}") + expected_dst_end_rule = JulianDayOfYearTransitionRule.new(expected_julian_day, expected_offset_from_midnight) + assert_equal(expected_dst_end_rule, result.dst_end_rule) + end + end + + [ + ['0', 0], + ['365', 365] + ].each do |(absolute_day_rule, expected_day)| + rule = append_time_to_rule(absolute_day_rule, time) + + define_method "test_absolute_day_dst_start_rule_for_#{rule}" do + result = @parser.parse("STD-1DST,#{rule},J300") + expected_dst_start_rule = AbsoluteDayOfYearTransitionRule.new(expected_day, expected_offset_from_midnight) + assert_equal(expected_dst_start_rule, result.dst_start_rule) + end + + define_method "test_absolute_day_dst_end_rule_for_#{rule}" do + result = @parser.parse("STD-1DST,J60,#{rule}") + expected_dst_end_rule = AbsoluteDayOfYearTransitionRule.new(expected_day, expected_offset_from_midnight) + assert_equal(expected_dst_end_rule, result.dst_end_rule) + end + end + + [ + ['M1.1.0', 1, 1, 0], + ['M12.4.6', 12, 4, 6] + ].each do |(day_of_month_rule, expected_month, expected_week, expected_day_of_week)| + rule = append_time_to_rule(day_of_month_rule, time) + + define_method "test_day_of_month_dst_start_rule_for_#{rule}" do + result = @parser.parse("STD-1DST,#{rule},300") + expected_dst_start_rule = DayOfMonthTransitionRule.new(expected_month, expected_week, expected_day_of_week, expected_offset_from_midnight) + assert_equal(expected_dst_start_rule, result.dst_start_rule) + end + + define_method "test_day_of_month_dst_end_rule_for_#{rule}" do + result = @parser.parse("STD-1DST,60,#{rule}") + expected_dst_end_rule = DayOfMonthTransitionRule.new(expected_month, expected_week, expected_day_of_week, expected_offset_from_midnight) + assert_equal(expected_dst_end_rule, result.dst_end_rule) + end + end + + [ + ['M1.5.0', 1, 0], + ['M12.5.6', 12, 6] + ].each do |(last_day_of_month_rule, expected_month, expected_day_of_week)| + rule = append_time_to_rule(last_day_of_month_rule, time) + + define_method "test_last_day_of_month_dst_start_rule_for_#{rule}" do + result = @parser.parse("STD-1DST,#{rule},300") + expected_dst_start_rule = LastDayOfMonthTransitionRule.new(expected_month, expected_day_of_week, expected_offset_from_midnight) + assert_equal(expected_dst_start_rule, result.dst_start_rule) + end + + define_method "test_last_day_of_month_dst_end_rule_for_#{rule}" do + result = @parser.parse("STD-1DST,60,#{rule}") + expected_dst_end_rule = LastDayOfMonthTransitionRule.new(expected_month, expected_day_of_week, expected_offset_from_midnight) + assert_equal(expected_dst_end_rule, result.dst_end_rule) + end + end + end + + ['J0', 'J366'].each do |julian_day_rule| + define_invalid_dst_rule_tests('julian_day', julian_day_rule) + end + + ['-1', '366'].each do |absolute_day_rule| + define_invalid_dst_rule_tests('absolute_day', absolute_day_rule) + end + + ['M0,1,0', 'M13,1,0', 'M6,0,0', 'M6,6,0', 'M6,1,-1', 'M6,1,7'].each do |day_of_month_rule| + define_invalid_dst_rule_tests('day_of_month', day_of_month_rule) + end + + ['M0,5,0', 'M13,5,0', 'M6,5,-1', 'M6,5,7'].each do |last_day_of_month_rule| + define_invalid_dst_rule_tests('last_day_of_month', last_day_of_month_rule) + end + + def test_invalid_dst_start_rule + assert_raises(InvalidPosixTimeZone) { @parser.parse('STD1DST,X60,300') } + end + + def test_invalid_dst_end_rule + assert_raises(InvalidPosixTimeZone) { @parser.parse('STD1DST,60,X300') } + end + + [ + ['STD5DST,0/0,J365/25', :DST, -5 * HOUR, HOUR], + ['STD-5DST,0/0,J365/25', :DST, 5 * HOUR, HOUR], + ['STD5DST3,0/0,J365/26', :DST, -5 * HOUR, 2 * HOUR], + ['STD5DST6,0/0,J365/23', :DST, -5 * HOUR, -HOUR], + ['STD5DST,J1/0,J365/25', :DST, -5 * HOUR, HOUR], + ['Winter5Summer,0/0,J365/25', :Summer, -5 * HOUR, HOUR], + ['<-05>5<-06>,0/0,J365/25', :'-06', -5 * HOUR, HOUR] + ].each do |(tz_string, expected_abbrev, expected_base_offset, expected_std_offset)| + define_method "test_dst_only_returns_continuous_offset_for_#{tz_string}" do + result = @parser.parse(tz_string) + expected = TimezoneOffset.new(expected_base_offset, expected_std_offset, expected_abbrev) + assert_equal(expected, result) + end + end + + def test_parses_tainted_string_in_safe_mode_and_returns_untainted_abbreviations + safe_test(:unavailable => :skip) do + result = @parser.parse('STD1DST,60,300'.dup.taint) + + assert_equal(:STD, result.std_offset.abbreviation) + assert_equal(:DST, result.dst_offset.abbreviation) + end + end + + ['STD1', 'STD1DST,60,300'].each do |tz_string| + tz_string += "-" + define_method "test_content_after_end_for_#{tz_string}" do + error = assert_raises(InvalidPosixTimeZone) { @parser.parse(tz_string) } + assert_equal("Expected the end of a POSIX-style time zone string but found '-'.", error.message) + end + end + + ['X', 0].each do |invalid_tz_string| + define_method "test_invalid_tz_string_#{invalid_tz_string}" do + assert_raises(InvalidPosixTimeZone) { @parser.parse(invalid_tz_string) } + end + end +end diff --git a/test/tc_time_or_datetime.rb b/test/tc_time_or_datetime.rb index 7dad11ce..d5ca73ef 100644 --- a/test/tc_time_or_datetime.rb +++ b/test/tc_time_or_datetime.rb @@ -249,6 +249,20 @@ def test_day assert_equal(24, TimeOrDateTime.new(DateTime.new(2006, 3, 24, 15, 32, 3)).day) assert_equal(24, TimeOrDateTime.new(1143214323).day) end + + 19.upto(25).each_with_index do |mday,i| + define_method "test_wday_time_#{i}" do + assert_equal(i, TimeOrDateTime.new(Time.utc(2006, 3, mday)).wday) + end + + define_method "test_wday_datetime_#{i}" do + assert_equal(i, TimeOrDateTime.new(DateTime.new(2006, 3, mday)).wday) + end + + define_method "test_wday_timestamp_#{i}" do + assert_equal(i, TimeOrDateTime.new(Time.utc(2006, 3, mday).to_i).wday) + end + end def test_hour assert_equal(15, TimeOrDateTime.new(Time.utc(2006, 3, 24, 15, 32, 3)).hour) diff --git a/test/tc_transition_rule.rb b/test/tc_transition_rule.rb new file mode 100644 index 00000000..c180f0c5 --- /dev/null +++ b/test/tc_transition_rule.rb @@ -0,0 +1,663 @@ +require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils') + +include TZInfo + +class TCTransitionRule < Minitest::Test + [-1, 0, 1].each do |transition_at| + define_method "test_transition_at_#{transition_at}" do + rule = TestTransitionRule.new(transition_at) + assert_equal(transition_at, rule.transition_at) + end + end + + [ + [-1, 19, 23, 59, 59], + [0, 20, 0, 0, 0], + [1, 20, 0, 0, 1], + [60, 20, 0, 1, 0], + [86399, 20, 23, 59, 59], + [86400, 21, 0, 0, 0] + ].each do |(transition_at, expected_day, expected_hour, expected_minute, expected_second)| + define_method "test_at_with_transition_at_#{transition_at}" do + offset = TimezoneOffset.new(0, 0, 'TEST') + rule = TestTransitionRule.new(transition_at) do |y| + assert_equal(2020, y) + TimeOrDateTime.wrap(Time.utc(2020, 3, 20)) + end + + result = rule.at(offset, 2020) + + assert_kind_of(TimeOrDateTime, result) + assert_equal(Time.utc(2020, 3, expected_day, expected_hour, expected_minute, expected_second), result.to_time) + end + end + + [-7200, 0, 7200].each do |offset_seconds| + define_method "test_at_with_offset_#{offset_seconds}" do + offset = TimezoneOffset.new(offset_seconds, 0, 'TEST') + rule = TestTransitionRule.new(3600) do |y| + assert_equal(2020, y) + TimeOrDateTime.wrap(Time.utc(2020, 3, 20)) + end + + result = rule.at(offset, 2020) + + assert_kind_of(TimeOrDateTime, result) + assert_equal(Time.utc(2020, 3, 20, 1, 0, 0) - offset_seconds, result.to_time) + end + end + + [2020, 2021].each do |year| + define_method "test_at_with_year_#{year}" do + offset = TimezoneOffset.new(0, 0, 'TEST') + rule = TestTransitionRule.new(3600) do |y| + assert_equal(year, y) + TimeOrDateTime.wrap(Time.utc(year, 3, 20)) + end + + result = rule.at(offset, year) + + assert_kind_of(TimeOrDateTime, result) + assert_equal(Time.utc(year, 3, 20, 1, 0, 0), result.to_time) + end + end + + def test_at_crosses_64_bit_boundary + offset = TimezoneOffset.new(0, 0, 'TEST') + rule = TestTransitionRule.new(11648) do |y| + assert_equal(2038, y) + TimeOrDateTime.wrap(Time.utc(2038, 1, 19)) + end + + result = rule.at(offset, 2038) + + assert_kind_of(TimeOrDateTime, result) + if RubyCoreSupport.time_supports_64bit + assert_kind_of(Time, result.to_orig) + else + assert_kind_of(DateTime, result.to_orig) + end + assert_equal(DateTime.new(2038, 1, 19, 3, 14, 8), result.to_datetime) + end + + def test_at_crosses_negative_boundary + offset = TimezoneOffset.new(0, 0, 'TEST') + rule = TestTransitionRule.new(-1) do |y| + assert_equal(1970, y) + TimeOrDateTime.wrap(Time.utc(1970, 1, 1)) + end + + result = rule.at(offset, 1970) + + assert_kind_of(TimeOrDateTime, result) + if RubyCoreSupport.time_supports_negative + assert_kind_of(Time, result.to_orig) + else + assert_kind_of(DateTime, result.to_orig) + end + assert_equal(DateTime.new(1969, 12, 31, 23, 59, 59), result.to_datetime) + end + + NEGATIVE_DATES = [[1969, 12, 31]] + B32_DATES = [[1970, 1, 1], [2038, 1, 19]] + B64_DATES = [[2038, 1, 20], [2038, 2, 1], [2039, 1, 1]] + + B32_DATES.concat(RubyCoreSupport.time_supports_negative ? NEGATIVE_DATES : []).concat(RubyCoreSupport.time_supports_64bit ? B64_DATES : []).each do |(year, month, day)| + define_method "test_new_time_or_datetime_returns_time_based_instance_for_#{year}_#{month}_#{day}" do + result = TestTransitionRule.new(0).new_time_or_datetime!(year, month, day) + assert_kind_of(TimeOrDateTime, result) + assert_kind_of(Time, result.to_orig) + assert_equal(Time.utc(year, month, day), result.to_orig) + end + end + + (RubyCoreSupport.time_supports_negative ? [] : NEGATIVE_DATES).concat(RubyCoreSupport.time_supports_64bit ? [] : B64_DATES).each do |(year, month, day)| + define_method "test_new_time_or_datetime_returns_datetime_based_instance_for_#{year}_#{month}_#{day}" do + result = TestTransitionRule.new(0).new_time_or_datetime!(year, month, day) + assert_kind_of(TimeOrDateTime, result) + assert_kind_of(DateTime, result.to_orig) + assert_equal(DateTime.new(year, month, day), result.to_orig) + end + end + + [1900, 2021, 2039].each do |year| + define_method "test_new_time_or_datetime_returns_march_1_for_feb_29_on_non_leap_year_#{year}" do + result = TestTransitionRule.new(0).new_time_or_datetime!(year, 2, 29) + assert_kind_of(TimeOrDateTime, result) + assert_equal(DateTime.new(year, 3, 1), result.to_datetime) + end + end + + [1968, 2000, 2040].each do |year| + define_method "test_new_time_or_datetime_returns_feb_29_on_leap_year_#{year}" do + result = TestTransitionRule.new(0).new_time_or_datetime!(year, 2, 29) + assert_kind_of(TimeOrDateTime, result) + assert_equal(DateTime.new(year, 2, 29), result.to_datetime) + end + end + + class TestTransitionRule < TransitionRule + def initialize(transition_at, &block) + super(transition_at) + @get_day = block + end + + alias new_time_or_datetime! new_time_or_datetime + public :new_time_or_datetime! + + protected + + def get_day(year) + @get_day.call(year) + end + end +end + +module BaseTransitionRuleTestHelper + def test_invalid_transition_at + error = assert_raises(ArgumentError) { create_with_transition_at('0') } + assert_match(/\btransition_at(\b.*)?/, error.message) + end + + [:==, :eql?].each do |method| + define_method "test_not_equal_by_transition_at_with_#{method}" do + rule1 = create_with_transition_at(0) + rule2 = create_with_transition_at(1) + assert_equal(false, rule1.send(method, rule2)) + assert_equal(false, rule2.send(method, rule1)) + end + + define_method "test_not_equal_to_other_with_#{method}" do + rule = create_with_transition_at(0) + assert_equal(false, rule.send(method, Object.new)) + end + end +end + +class TCAbsoluteDayOfYearTransitionRule < Minitest::Test + include BaseTransitionRuleTestHelper + + [-1, 366, '0'].each do |value| + define_method "test_invalid_day_#{value}" do + error = assert_raises(ArgumentError) { AbsoluteDayOfYearTransitionRule.new(value) } + assert_match(/\bday\b/, error.message) + end + end + + [ + [2020, 0, 1, 1], + [2020, 58, 2, 28], + [2020, 59, 2, 29], + [2020, 365, 12, 31], + [2021, 59, 3, 1], + [2021, 365, 13, 1], + [2100, 59, 3, 1], + [2100, 365, 13, 1], + [2000, 59, 2, 29], + [2000, 365, 12, 31] + ].each do |(year, day, expected_month, expected_day)| + define_method "test_day_#{day}_of_year_#{year}" do + rule = AbsoluteDayOfYearTransitionRule.new(day, 3600) + offset = TimezoneOffset.new(0, 0, 'TEST') + + result = rule.at(offset, year) + + expected_year = year + if expected_month == 13 + expected_year += 1 + expected_month = 1 + end + + assert_kind_of(TimeOrDateTime, result) + assert_equal(DateTime.new(expected_year, expected_month, expected_day, 1, 0, 0), result.to_datetime) + end + end + + def test_crosses_64_bit_boundary + # Internally the calculation starts with Jan 1 and adds days. Jan 20 2038 + # will require a DateTime on 32-bit only systems. + + rule = AbsoluteDayOfYearTransitionRule.new(19, 3600) + offset = TimezoneOffset.new(0, 0, 'TEST') + + result = rule.at(offset, 2038) + + assert_kind_of(TimeOrDateTime, result) + if RubyCoreSupport.time_supports_64bit + assert_kind_of(Time, result.to_orig) + else + assert_kind_of(DateTime, result.to_orig) + end + assert_equal(DateTime.new(2038, 1, 20, 1, 0, 0), result.to_datetime) + end + + def test_day_0_is_always_first_day_of_year + rule = AbsoluteDayOfYearTransitionRule.new(0) + assert_equal(true, rule.is_always_first_day_of_year?) + end + + [1, 365].each do |day| + define_method "test_day_#{day}_is_not_always_first_day_of_year" do + rule = AbsoluteDayOfYearTransitionRule.new(day) + assert_equal(false, rule.is_always_first_day_of_year?) + end + + define_method "test_day_#{day}_is_not_always_last_day_of_year" do + rule = AbsoluteDayOfYearTransitionRule.new(day) + assert_equal(false, rule.is_always_last_day_of_year?) + end + end + + [:==, :eql?].each do |method| + [ + [0, 0], + [0, 3600], + [365, 0] + ].each do |(day, transition_at)| + define_method "test_equal_for_day_#{day}_and_transition_at_#{transition_at}_with_#{method}" do + rule1 = AbsoluteDayOfYearTransitionRule.new(day, transition_at) + rule2 = AbsoluteDayOfYearTransitionRule.new(day, transition_at) + assert_equal(true, rule1.send(method, rule2)) + assert_equal(true, rule2.send(method, rule1)) + end + end + + define_method "test_not_equal_by_day_with_#{method}" do + rule1 = AbsoluteDayOfYearTransitionRule.new(0, 3600) + rule2 = AbsoluteDayOfYearTransitionRule.new(1, 3600) + assert_equal(false, rule1.send(method, rule2)) + assert_equal(false, rule2.send(method, rule1)) + end + + define_method "test_not_equal_to_julian_with_#{method}" do + rule1 = AbsoluteDayOfYearTransitionRule.new(1, 0) + rule2 = JulianDayOfYearTransitionRule.new(1, 0) + assert_equal(false, rule1.send(method, rule2)) + assert_equal(false, rule2.send(method, rule1)) + end + end + + [ + [0, 0], + [0, 3600], + [365, 0] + ].each do |(day, transition_at)| + define_method "test_hash_for_day_#{day}_and_transition_at_#{transition_at}" do + rule = AbsoluteDayOfYearTransitionRule.new(day, transition_at) + expected = [AbsoluteDayOfYearTransitionRule, day * 86400, transition_at].hash + assert_equal(expected, rule.hash) + end + end + + protected + + def create_with_transition_at(transition_at) + AbsoluteDayOfYearTransitionRule.new(1, transition_at) + end +end + +class TCJulianDayOfYearTransitionRule < Minitest::Test + include BaseTransitionRuleTestHelper + + [0, 366, '1'].each do |value| + define_method "test_invalid_day_#{value}" do + error = assert_raises(ArgumentError) { JulianDayOfYearTransitionRule.new(value) } + assert_match(/\bday\b/, error.message) + end + end + + [2020, 2021, 2100, 2000].each do |year| + [ + [1, 1, 1], + [59, 2, 28], + [60, 3, 1], + [365, 12, 31] + ].each do |(day, expected_month, expected_day)| + define_method "test_day_#{day}_of_year_#{year}" do + rule = JulianDayOfYearTransitionRule.new(day, 3600) + offset = TimezoneOffset.new(0, 0, 'TEST') + + result = rule.at(offset, year) + + assert_kind_of(TimeOrDateTime, result) + assert_equal(DateTime.new(year, expected_month, expected_day, 1, 0, 0), result.to_datetime) + end + end + end + + def test_day_1_is_always_first_day_of_year + rule = JulianDayOfYearTransitionRule.new(1) + assert_equal(true, rule.is_always_first_day_of_year?) + end + + [2, 365].each do |day| + define_method "test_day_#{day}_is_not_always_first_day_of_year" do + rule = JulianDayOfYearTransitionRule.new(day) + assert_equal(false, rule.is_always_first_day_of_year?) + end + end + + def test_day_365_is_always_last_day_of_year + rule = JulianDayOfYearTransitionRule.new(365) + assert_equal(true, rule.is_always_last_day_of_year?) + end + + [1, 364].each do |day| + define_method "test_day_#{day}_is_not_always_last_day_of_year" do + rule = JulianDayOfYearTransitionRule.new(day) + assert_equal(false, rule.is_always_last_day_of_year?) + end + end + + [:==, :eql?].each do |method| + [ + [1, 0], + [1, 3600], + [365, 0] + ].each do |(day, transition_at)| + define_method "test_equal_for_day_#{day}_and_transition_at_#{transition_at}_with_#{method}" do + rule1 = JulianDayOfYearTransitionRule.new(day, transition_at) + rule2 = JulianDayOfYearTransitionRule.new(day, transition_at) + assert_equal(true, rule1.send(method, rule2)) + assert_equal(true, rule2.send(method, rule1)) + end + end + + define_method "test_not_equal_by_day_with_#{method}" do + rule1 = JulianDayOfYearTransitionRule.new(1, 0) + rule2 = JulianDayOfYearTransitionRule.new(2, 0) + assert_equal(false, rule1.send(method, rule2)) + assert_equal(false, rule2.send(method, rule1)) + end + + define_method "test_not_equal_to_absolute_with_#{method}" do + rule1 = JulianDayOfYearTransitionRule.new(1, 0) + rule2 = AbsoluteDayOfYearTransitionRule.new(1, 0) + assert_equal(false, rule1.send(method, rule2)) + assert_equal(false, rule2.send(method, rule1)) + end + end + + [ + [1, 0], + [1, 3600], + [365, 0] + ].each do |(day, transition_at)| + define_method "test_hash_for_day_#{day}_and_transition_at_#{transition_at}" do + rule = JulianDayOfYearTransitionRule.new(day, transition_at) + expected = [JulianDayOfYearTransitionRule, day * 86400, transition_at].hash + assert_equal(expected, rule.hash) + end + end + + protected + + def create_with_transition_at(transition_at) + JulianDayOfYearTransitionRule.new(1, transition_at) + end +end + +module DayOfWeekTransitionRuleTestHelper + [-1, 0, 13, '1'].each do |month| + define_method "test_invalid_month_#{month}" do + error = assert_raises(ArgumentError) { create_with_month_and_day_of_week(month, 0) } + assert_match(/\bmonth\b/, error.message) + end + end + + [-1, 7, '0'].each do |day_of_week| + define_method "test_invalid_day_of_week_#{day_of_week}" do + error = assert_raises(ArgumentError) { create_with_month_and_day_of_week(1, day_of_week) } + assert_match(/\bday_of_week\b/, error.message) + end + end + + def test_is_not_always_first_day_of_year + rule = create_with_month_and_day_of_week(1, 0) + assert_equal(false, rule.is_always_first_day_of_year?) + end + + def test_is_not_always_last_day_of_year + rule = create_with_month_and_day_of_week(12, 6) + assert_equal(false, rule.is_always_last_day_of_year?) + end + + [:==, :eql?].each do |method| + define_method "test_not_equal_by_month_with_#{method}" do + rule1 = create_with_month_and_day_of_week(1, 0) + rule2 = create_with_month_and_day_of_week(2, 0) + assert_equal(false, rule1.send(method, rule2)) + assert_equal(false, rule2.send(method, rule1)) + end + + define_method "test_not_equal_by_day_of_week_with_#{method}" do + rule1 = create_with_month_and_day_of_week(1, 0) + rule2 = create_with_month_and_day_of_week(1, 1) + assert_equal(false, rule1.send(method, rule2)) + assert_equal(false, rule2.send(method, rule1)) + end + end +end + +class TCDayOfMonthTransitionRule < Minitest::Test + include BaseTransitionRuleTestHelper + include DayOfWeekTransitionRuleTestHelper + + [-1, 0, 5, '1'].each do |week| + define_method "test_invalid_week_#{week}" do + error = assert_raises(ArgumentError) { DayOfMonthTransitionRule.new(1, week, 0) } + assert_match(/\bweek\b/, error.message) + end + end + + [ + # All possible first week start days. + [2020, 3, [1, 2, 3, 4, 5, 6, 7]], + [2021, 3, [7, 1, 2, 3, 4, 5, 6]], + [2022, 3, [6, 7, 1, 2, 3, 4, 5]], + [2023, 3, [5, 6, 7, 1, 2, 3, 4]], + [2018, 3, [4, 5, 6, 7, 1, 2, 3]], + [2024, 3, [3, 4, 5, 6, 7, 1, 2]], + [2025, 3, [2, 3, 4, 5, 6, 7, 1]], + + # All possible months. + [2019, 1, [6]], + [2019, 2, [3]], + [2019, 3, [3]], + [2019, 4, [7]], + [2019, 5, [5]], + [2019, 6, [2]], + [2019, 7, [7]], + [2019, 8, [4]], + [2019, 9, [1]], + [2019, 10, [6]], + [2019, 11, [3]], + [2019, 12, [1]] + ].each do |(year, month, days)| + days.each_with_index do |expected_day, day_of_week| + (1..4).each do |week| + define_method "test_month_#{month}_week_#{week}_and_day_of_week_#{day_of_week}_year_#{year}" do + rule = DayOfMonthTransitionRule.new(month, week, day_of_week, 3600) + offset = TimezoneOffset.new(0, 0, 'TEST') + + result = rule.at(offset, year) + + assert_kind_of(TimeOrDateTime, result) + assert_equal(Time.utc(year, month, expected_day + (week - 1) * 7, 1, 0, 0), result.to_time) + end + end + end + end + + [[3, 3, 20], [4, 6, 23]].each do |(week, day_of_week, expected_day)| + define_method "test_crosses_64_bit_boundary_for_day_of_week_#{day_of_week}" do + rule = DayOfMonthTransitionRule.new(1, week, day_of_week, 3600) + offset = TimezoneOffset.new(0, 0, 'TEST') + + result = rule.at(offset, 2038) + + assert_kind_of(TimeOrDateTime, result) + if RubyCoreSupport.time_supports_64bit + assert_kind_of(Time, result.to_orig) + else + assert_kind_of(DateTime, result.to_orig) + end + assert_equal(DateTime.new(2038, 1, expected_day, 1, 0, 0), result.to_datetime) + end + end + + [:==, :eql?].each do |method| + [ + [1, 1, 0, 0], + [1, 1, 0, 1], + [1, 1, 1, 0], + [1, 2, 0, 0], + [2, 1, 0, 0] + ].each do |(month, week, day_of_week, transition_at)| + define_method "test_equal_for_month_#{month}_week_#{week}_day_of_week_#{day_of_week}_and_transition_at_#{transition_at}_with_#{method}" do + rule1 = DayOfMonthTransitionRule.new(month, week, day_of_week, transition_at) + rule2 = DayOfMonthTransitionRule.new(month, week, day_of_week, transition_at) + assert_equal(true, rule1.send(method, rule2)) + assert_equal(true, rule2.send(method, rule1)) + end + end + + define_method "test_not_equal_by_week_with_#{method}" do + rule1 = DayOfMonthTransitionRule.new(1, 1, 0, 0) + rule2 = DayOfMonthTransitionRule.new(1, 2, 0, 0) + assert_equal(false, rule1.send(method, rule2)) + assert_equal(false, rule2.send(method, rule1)) + end + + define_method "test_not_equal_to_last_day_of_month_with_#{method}" do + rule1 = DayOfMonthTransitionRule.new(1, 1, 0, 0) + rule2 = LastDayOfMonthTransitionRule.new(1, 0, 0) + assert_equal(false, rule1.send(method, rule2)) + assert_equal(false, rule2.send(method, rule1)) + end + end + + [ + [1, 1, 0, 0], + [1, 1, 0, 1], + [1, 1, 1, 0], + [1, 2, 0, 0], + [2, 1, 0, 0] + ].each do |(month, week, day_of_week, transition_at)| + define_method "test_hash_for_month_#{month}_week_#{week}_day_of_week_#{day_of_week}_and_transition_at_#{transition_at}" do + rule = DayOfMonthTransitionRule.new(month, week, day_of_week, transition_at) + expected = [(week - 1) * 7 + 1, month, day_of_week, transition_at].hash + assert_equal(expected, rule.hash) + end + end + + protected + + def create_with_transition_at(transition_at) + DayOfMonthTransitionRule.new(1, 1, 0, transition_at) + end + + def create_with_month_and_day_of_week(month, day_of_week) + DayOfMonthTransitionRule.new(month, 1, day_of_week) + end +end + +class TCLastDayOfMonthTransitionRule < Minitest::Test + include BaseTransitionRuleTestHelper + include DayOfWeekTransitionRuleTestHelper + + [ + # All possible last days. + [2021, 10, [31, 25, 26, 27, 28, 29, 30]], + [2022, 10, [30, 31, 25, 26, 27, 28, 29]], + [2023, 10, [29, 30, 31, 25, 26, 27, 28]], + [2018, 10, [28, 29, 30, 31, 25, 26, 27]], + [2024, 10, [27, 28, 29, 30, 31, 25, 26]], + [2025, 10, [26, 27, 28, 29, 30, 31, 25]], + [2026, 10, [25, 26, 27, 28, 29, 30, 31]], + + # All possible months. + [2020, 1, [26]], + [2020, 2, [23]], + [2020, 3, [29]], + [2020, 4, [26]], + [2020, 5, [31]], + [2020, 6, [28]], + [2020, 7, [26]], + [2020, 8, [30]], + [2020, 9, [27]], + [2020, 10, [25]], + [2020, 11, [29]], + [2020, 12, [27]] + ].each do |(year, month, days)| + days.each_with_index do |expected_day, day_of_week| + define_method "test_month_#{month}_day_of_week_#{day_of_week}_year_#{year}" do + rule = LastDayOfMonthTransitionRule.new(month, day_of_week, 7200) + offset = TimezoneOffset.new(0, 0, 'TEST') + + result = rule.at(offset, year) + + assert_kind_of(TimeOrDateTime, result) + assert_equal(Time.utc(year, month, expected_day, 2, 0, 0), result.to_time) + end + end + end + + [[2020, 6, 29], [2021, 0, 28], [2000, 2, 29], [2100, 0, 28]].each do |(year, day_of_week, expected_day)| + define_method "test_#{expected_day == 29 ? '' : 'non_'}leap_year_#{year}" do + rule = LastDayOfMonthTransitionRule.new(2, day_of_week, 7200) + offset = TimezoneOffset.new(0, 0, 'TEST') + + result = rule.at(offset, year) + + assert_kind_of(TimeOrDateTime, result) + assert_equal(DateTime.new(year, 2, expected_day, 2, 0, 0), result.to_datetime) + end + end + + [:==, :eql?].each do |method| + [ + [1, 0, 0], + [1, 0, 1], + [1, 1, 0], + [2, 0, 0] + ].each do |(month, day_of_week, transition_at)| + define_method "test_equal_for_month_#{month}_day_of_week_#{day_of_week}_and_transition_at_#{transition_at}_with_#{method}" do + rule1 = LastDayOfMonthTransitionRule.new(month, day_of_week, transition_at) + rule2 = LastDayOfMonthTransitionRule.new(month, day_of_week, transition_at) + assert_equal(true, rule1.send(method, rule2)) + assert_equal(true, rule2.send(method, rule1)) + end + end + + define_method "test_not_equal_to_day_of_month_with_#{method}" do + rule1 = LastDayOfMonthTransitionRule.new(1, 0, 0) + rule2 = DayOfMonthTransitionRule.new(1, 1, 0, 0) + assert_equal(false, rule1.send(method, rule2)) + assert_equal(false, rule2.send(method, rule1)) + end + end + + [ + [1, 0, 0], + [1, 0, 1], + [1, 1, 0], + [2, 0, 0] + ].each do |(month, day_of_week, transition_at)| + define_method "test_hash_for_month_#{month}_day_of_week_#{day_of_week}_and_transition_at_#{transition_at}" do + rule = LastDayOfMonthTransitionRule.new(month, day_of_week, transition_at) + expected = [month, day_of_week, transition_at].hash + assert_equal(expected, rule.hash) + end + end + + protected + + def create_with_transition_at(transition_at) + LastDayOfMonthTransitionRule.new(1, 0, transition_at) + end + + def create_with_month_and_day_of_week(month, day_of_week) + LastDayOfMonthTransitionRule.new(month, day_of_week) + end +end diff --git a/test/tc_zoneinfo_timezone_info.rb b/test/tc_zoneinfo_timezone_info.rb index 8bf4e841..c8c47a07 100644 --- a/test/tc_zoneinfo_timezone_info.rb +++ b/test/tc_zoneinfo_timezone_info.rb @@ -10,6 +10,15 @@ send(:using, RubyCoreSupport::UntaintExt) if RubyCoreSupport.const_defined?(:UntaintExt) class TCZoneinfoTimezoneInfo < Minitest::Test + class FakePosixTimeZoneParser + def initialize(&block) + @on_parse = block + end + + def parse(tz_string) + @on_parse.call(tz_string) + end + end begin Time.at(-2147483649) @@ -27,6 +36,16 @@ class TCZoneinfoTimezoneInfo < Minitest::Test SUPPORTS_NEGATIVE = false end + def setup + @expect_tz_string = nil + @tz_parse_result = nil + @posix_tz_parser = FakePosixTimeZoneParser.new do |tz_string| + raise "Unexpected tz_string passed to PosixTimeZoneParser: #{tz_string}" unless tz_string == @expect_tz_string + raise InvalidPosixTimeZone, 'FakePosixTimeZoneParser Failure.' if @tz_parse_result == :fail + @tz_parse_result + end + end + def assert_period(abbreviation, utc_offset, std_offset, dst, start_at, end_at, info) if start_at period = info.period_for_utc(start_at) @@ -36,24 +55,24 @@ def assert_period(abbreviation, utc_offset, std_offset, dst, start_at, end_at, i # no transitions, pick the epoch period = info.period_for_utc(Time.utc(1970, 1, 1)) end - - assert_equal(abbreviation, period.abbreviation) - assert_equal(utc_offset, period.utc_offset) - assert_equal(std_offset, period.std_offset) - assert_equal(dst, period.dst?) - + + assert_equal(abbreviation, period.abbreviation, 'abbreviation') + assert_equal(utc_offset, period.utc_offset, 'utc_offset') + assert_equal(std_offset, period.std_offset, 'std_offset') + assert_equal(dst, period.dst?, 'dst') + if start_at - refute_nil(period.utc_start_time) - assert_equal(start_at, period.utc_start_time) + refute_nil(period.utc_start_time, 'utc_start_time') + assert_equal(start_at, period.utc_start_time, 'utc_start_time') else - assert_nil(period.utc_start_time) + assert_nil(period.utc_start_time, 'utc_start_time') end if end_at - refute_nil(period.utc_end_time) - assert_equal(end_at, period.utc_end_time) + refute_nil(period.utc_end_time, 'utc_end_time') + assert_equal(end_at, period.utc_end_time, 'utc_end_time') else - assert_nil(period.utc_end_time) + assert_nil(period.utc_end_time, 'utc_end_time') end end @@ -81,13 +100,15 @@ def pack_int64_signed_network_order(values) pack_int64_network_order(values.collect {|value| value < 0 ? value + 0x10000000000000000 : value}) end - def write_tzif(format, offsets, transitions, leaps = [], options = {}) + def write_tzif(format, offsets, transitions, tz_string, leaps, options = {}) # Options for testing malformed zoneinfo files. magic = options[:magic] section2_magic = options[:section2_magic] abbrev_separator = options[:abbrev_separator] || "\0" abbrev_offset_base = options[:abbrev_offset_base] || 0 + omit_tz_string_start_new_line = options[:omit_tz_string_start_new_line] + omit_tz_string_end_new_line = options[:omit_tz_string_end_new_line] unless magic if format == 1 @@ -191,20 +212,37 @@ def write_tzif(format, offsets, transitions, leaps = [], options = {}) end # Empty POSIX timezone string - file.write("\n\n") + file.write("\n") unless omit_tz_string_start_new_line + tz_string = tz_string.encode(Encoding::UTF_8) if tz_string.respond_to?(:encode) + file.write(tz_string) + file.write("\n") unless omit_tz_string_end_new_line end file.flush - yield file.path, format + yield file.path end end - def tzif_test(offsets, transitions, leaps = [], options = {}, &block) - min_format = options[:min_format] || 1 + def tzif_test(offsets, transitions, options = {}, &block) + rules = options[:rules] + tz_string = options[:tz_string] || (rules ? "TEST_TZ_STRING_#{rand(1000000)}" : '') + leaps = options[:leaps] || [] + min_format = options[:min_format] || (tz_string.empty? ? 1 : 2) min_format.upto(3) do |format| - write_tzif(format, offsets, transitions, leaps, options, &block) + write_tzif(format, offsets, transitions, tz_string, leaps, options) do |path| + if format >= 2 + @tz_parse_result = rules + @expect_tz_string = tz_string + end + begin + yield path, format + ensure + @tz_parse_result = nil + @expect_tz_string = nil + end + end end end @@ -222,7 +260,7 @@ def test_load {:at => Time.utc(2000, 12, 31), :offset_index => 3}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/One', path) + info = ZoneinfoTimezoneInfo.new('Zone/One', path, @posix_tz_parser) assert_equal('Zone/One', info.identifier) assert_period(:LMT, 3542, 0, false, nil, Time.utc(1971, 1, 2), info) @@ -247,7 +285,7 @@ def test_load_negative_utc_offset {:at => Time.utc(1992, 4, 1, 4, 30, 0), :offset_index => 3}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/One', path) + info = ZoneinfoTimezoneInfo.new('Zone/One', path, @posix_tz_parser) assert_equal('Zone/One', info.identifier) assert_period(:LMT, -12492, 0, false, nil, Time.utc(1971, 7, 9, 3, 0, 0), info) @@ -272,7 +310,7 @@ def test_load_dst_first {:at => Time.utc(2000, 12, 31), :offset_index => 3}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/Two', path) + info = ZoneinfoTimezoneInfo.new('Zone/Two', path, @posix_tz_parser) assert_equal('Zone/Two', info.identifier) assert_period(:LMT, 3542, 0, false, nil, Time.utc(1979, 1, 2), info) @@ -283,7 +321,7 @@ def test_load_no_transitions offsets = [{:gmtoff => -12094, :isdst => false, :abbrev => 'LT'}] tzif_test(offsets, []) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/three', path) + info = ZoneinfoTimezoneInfo.new('Zone/three', path, @posix_tz_parser) assert_equal('Zone/three', info.identifier) assert_period(:LT, -12094, 0, false, nil, nil, info) @@ -296,7 +334,7 @@ def test_load_no_offsets tzif_test(offsets, transitions) do |path, format| assert_raises(InvalidZoneinfoFile) do - ZoneinfoTimezoneInfo.new('Zone', path) + ZoneinfoTimezoneInfo.new('Zone', path, @posix_tz_parser) end end end @@ -307,7 +345,7 @@ def test_load_invalid_offset_index tzif_test(offsets, transitions) do |path, format| assert_raises(InvalidZoneinfoFile) do - ZoneinfoTimezoneInfo.new('Zone', path) + ZoneinfoTimezoneInfo.new('Zone', path, @posix_tz_parser) end end end @@ -316,9 +354,9 @@ def test_load_with_leap_seconds offsets = [{:gmtoff => -0, :isdst => false, :abbrev => 'LMT'}] leaps = [{:at => Time.utc(1972,6,30,23,59,60), :seconds => 1}] - tzif_test(offsets, [], leaps) do |path, format| + tzif_test(offsets, [], :leaps => leaps) do |path, format| assert_raises(InvalidZoneinfoFile) do - ZoneinfoTimezoneInfo.new('Zone', path) + ZoneinfoTimezoneInfo.new('Zone', path, @posix_tz_parser) end end end @@ -327,41 +365,36 @@ def test_load_invalid_magic ['TZif4', 'tzif2', '12345'].each do |magic| offsets = [{:gmtoff => -12094, :isdst => false, :abbrev => 'LT'}] - tzif_test(offsets, [], [], :magic => magic) do |path, format| + tzif_test(offsets, [], :magic => magic) do |path, format| assert_raises(InvalidZoneinfoFile) do - ZoneinfoTimezoneInfo.new('Zone2', path) + ZoneinfoTimezoneInfo.new('Zone2', path, @posix_tz_parser) end end end end - # These tests can only be run if the platform supports 64-bit Times. When - # 64-bit support is unavailable, the second section will not be read, so no - # error will be raised. - if SUPPORTS_64BIT - def test_load_invalid_section2_magic - ['TZif4', 'tzif2', '12345'].each do |section2_magic| - offsets = [{:gmtoff => -12094, :isdst => false, :abbrev => 'LT'}] - - tzif_test(offsets, [], [], :min_format => 2, :section2_magic => section2_magic) do |path, format| - assert_raises(InvalidZoneinfoFile) do - ZoneinfoTimezoneInfo.new('Zone4', path) - end + def test_load_invalid_section2_magic + ['TZif4', 'tzif2', '12345'].each do |section2_magic| + offsets = [{:gmtoff => -12094, :isdst => false, :abbrev => 'LT'}] + + tzif_test(offsets, [], :min_format => 2, :section2_magic => section2_magic) do |path, format| + assert_raises(InvalidZoneinfoFile) do + ZoneinfoTimezoneInfo.new('Zone4', path, @posix_tz_parser) end end end + end + + def test_load_mismatched_section2_magic + minus_one = Proc.new {|f| f == 2 ? "TZif\0" : "TZif#{f - 1}" } + plus_one = Proc.new {|f| "TZif#{f + 1}" } - def test_load_mismatched_section2_magic - minus_one = Proc.new {|f| f == 2 ? "TZif\0" : "TZif#{f - 1}" } - plus_one = Proc.new {|f| "TZif#{f + 1}" } - - [minus_one, plus_one].each do |section2_magic| - offsets = [{:gmtoff => -12094, :isdst => false, :abbrev => 'LT'}] - - tzif_test(offsets, [], [], :min_format => 2, :section2_magic => section2_magic) do |path, format| - assert_raises(InvalidZoneinfoFile) do - ZoneinfoTimezoneInfo.new('Zone5', path) - end + [minus_one, plus_one].each do |section2_magic| + offsets = [{:gmtoff => -12094, :isdst => false, :abbrev => 'LT'}] + + tzif_test(offsets, [], :min_format => 2, :section2_magic => section2_magic) do |path, format| + assert_raises(InvalidZoneinfoFile) do + ZoneinfoTimezoneInfo.new('Zone5', path, @posix_tz_parser) end end end @@ -373,7 +406,7 @@ def test_load_invalid_format file.flush assert_raises(InvalidZoneinfoFile) do - ZoneinfoTimezoneInfo.new('Zone3', file.path) + ZoneinfoTimezoneInfo.new('Zone3', file.path, @posix_tz_parser) end end end @@ -386,9 +419,9 @@ def test_load_missing_abbrev_null_termination transitions = [ {:at => Time.utc(2000, 1, 1), :offset_index => 1}] - tzif_test(offsets, transitions, [], :abbrev_separator => '^') do |path, format| + tzif_test(offsets, transitions, :abbrev_separator => '^') do |path, format| assert_raises(InvalidZoneinfoFile) do - ZoneinfoTimezoneInfo.new('Zone', path) + ZoneinfoTimezoneInfo.new('Zone', path, @posix_tz_parser) end end end @@ -401,9 +434,9 @@ def test_load_out_of_range_abbrev_offsets transitions = [ {:at => Time.utc(2000, 1, 1), :offset_index => 1}] - tzif_test(offsets, transitions, [], :abbrev_offset_base => 8) do |path, format| + tzif_test(offsets, transitions, :abbrev_offset_base => 8) do |path, format| assert_raises(InvalidZoneinfoFile) do - ZoneinfoTimezoneInfo.new('Zone', path) + ZoneinfoTimezoneInfo.new('Zone', path, @posix_tz_parser) end end end @@ -428,7 +461,7 @@ def test_load_before_epoch {:at => Time.utc(2000, 12, 31), :offset_index => 3}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/Negative', path) + info = ZoneinfoTimezoneInfo.new('Zone/Negative', path, @posix_tz_parser) assert_equal('Zone/Negative', info.identifier) if SUPPORTS_NEGATIVE @@ -459,7 +492,7 @@ def test_load_on_epoch {:at => Time.utc(2000, 12, 31), :offset_index => 3}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/Negative', path) + info = ZoneinfoTimezoneInfo.new('Zone/Negative', path, @posix_tz_parser) assert_equal('Zone/Negative', info.identifier) if SUPPORTS_NEGATIVE @@ -476,13 +509,15 @@ def test_load_on_epoch end def test_load_64bit - # Some platforms support 64-bit Times, others only 32-bit. The TZif version - # 2 and later format contains both 32-bit and 64-bit times. + # The TZif version2 and later format contains both 32-bit and 64-bit + # sections. The 64-bit section is always used. + # + # Negative transitions before the supported range are moved to the start of + # the supported range. + # + # Transitions after 2**31 - 1 are discarded if 64-bit times aren't + # supported. - # Where 64-bit is supported and a TZif 2 or later file is provided, the - # 64-bit times should be used, otherwise the 32-bit information should be - # used. - offsets = [ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, {:gmtoff => 3600, :isdst => false, :abbrev => 'XST'}, @@ -496,27 +531,35 @@ def test_load_64bit {:at => 2240524800, :offset_index => 3}] # Time.utc(2040, 12, 31) tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/SixtyFour', path) + info = ZoneinfoTimezoneInfo.new('Zone/SixtyFour', path, @posix_tz_parser) assert_equal('Zone/SixtyFour', info.identifier) - if SUPPORTS_64BIT && format >= 2 - assert_period(:LMT, 3542, 0, false, nil, Time.utc(1850, 1, 2), info) + if SUPPORTS_64BIT && SUPPORTS_NEGATIVE && format >= 2 + assert_period(:LMT, 3542, 0, false, nil, Time.utc(1850, 1, 2), info) assert_period(:XST, 3600, 0, false, Time.utc(1850, 1, 2), Time.utc(2003, 4, 22), info) assert_period(:XDT, 3600, 3600, true, Time.utc(2003, 4, 22), Time.utc(2003, 10, 21), info) assert_period(:XST, 3600, 0, false, Time.utc(2003, 10, 21), Time.utc(2040, 12, 31), info) - assert_period(:XNST, 0, 0, false, Time.utc(2040, 12, 31), nil, info) - else - assert_period(:LMT, 3542, 0, false, nil, Time.utc(2003, 4, 22), info) + assert_period(:XNST, 0, 0, false, Time.utc(2040, 12, 31), nil, info) + elsif format < 2 + assert_period(:LMT, 3542, 0, false, nil, Time.utc(2003, 4, 22), info) assert_period(:XDT, 3600, 3600, true, Time.utc(2003, 4, 22), Time.utc(2003, 10, 21), info) - assert_period(:XST, 3600, 0, false, Time.utc(2003, 10, 21), nil, info) + assert_period(:XST, 3600, 0, false, Time.utc(2003, 10, 21), nil, info) + else + min_supported = SUPPORTS_NEGATIVE ? -2**31 : 0 + assert_period(:LMT, 3542, 0, false, nil, Time.at(min_supported).utc, info) + assert_period(:XST, 3600, 0, false, Time.at(min_supported).utc, Time.utc(2003, 4, 22), info) + assert_period(:XDT, 3600, 3600, true, Time.utc(2003, 4, 22), Time.utc(2003, 10, 21), info) + assert_period(:XST, 3600, 0, false, Time.utc(2003, 10, 21), nil, info) end end end def test_load_64bit_range # The full range of 64 bit timestamps is not currently supported because of - # the way transitions are indexed. Transitions outside the supported range - # will be ignored. + # the way transitions are indexed. The last transition before the earliest + # supported time will be moved to that time if there isn't already a + # transition at that time. Transitions after the latest supported time are + # ignored. offsets = [ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, @@ -529,7 +572,7 @@ def test_load_64bit_range {:at => 2**63 - 1, :offset_index => 0}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/SixtyFourRange', path) + info = ZoneinfoTimezoneInfo.new('Zone/SixtyFourRange', path, @posix_tz_parser) assert_equal('Zone/SixtyFourRange', info.identifier) if SUPPORTS_64BIT && format >= 2 @@ -540,19 +583,73 @@ def test_load_64bit_range #assert_period(:LMT, 3542, 0, false, Time.at(2**63 - 1).utc, nil, info) # Without full range support, the following periods will be defined: + assert_period(:LMT, 3542, 0, false, nil, Time.utc(1700, 1, 1), info) + assert_period(:XST, 3600, 0, false, Time.utc(1700, 1, 1), Time.utc(2014, 5, 27), info) + assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), nil, info) + elsif format < 2 assert_period(:LMT, 3542, 0, false, nil, Time.utc(2014, 5, 27), info) assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), nil, info) else + min_supported = SUPPORTS_NEGATIVE ? -2**31 : 0 + assert_period(:LMT, 3542, 0, false, nil, Time.at(min_supported).utc, info) + assert_period(:XST, 3600, 0, false, Time.at(min_supported).utc, Time.utc(2014, 5, 27), info) + assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), nil, info) + end + end + end + + def test_load_64bit_range_transition_at_earliest_supported + # The full range of 64 bit timestamps is not currently supported because of + # the way transitions are indexed. The last transition before the earliest + # supported time will be moved to that time if there isn't already a + # transition at that time. Transitions after the latest supported time are + # ignored. + + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST2'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XNST'}] + + transitions = [ + {:at => -2**63, :offset_index => 1}, + {:at => -8520336000, :offset_index => 2}, # Time.utc(1700, 1, 1).to_i + {:at => Time.utc(2014, 5, 27), :offset_index => 3}, + {:at => 2**63 - 1, :offset_index => 0}] + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/SixtyFourRange', path, @posix_tz_parser) + assert_equal('Zone/SixtyFourRange', info.identifier) + + if SUPPORTS_64BIT && format >= 2 + # When the full range is supported, the following periods will be defined: + #assert_period(:LMT, 3542, 0, false, nil, Time.at(-2**63).utc, info) + #assert_period(:XST, 3600, 0, false, Time.at(-2**63).utc, Time.utc(2014, 5, 27), info) + #assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), Time.at(2**63 - 1).utc, info) + #assert_period(:LMT, 3542, 0, false, Time.at(2**63 - 1).utc, nil, info) + + # Without full range support, the following periods will be defined: + assert_period(:LMT, 3542, 0, false, nil, Time.utc(1700, 1, 1), info) + assert_period(:XST2, 3600, 0, false, Time.utc(1700, 1, 1), Time.utc(2014, 5, 27), info) + assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), nil, info) + elsif format < 2 assert_period(:LMT, 3542, 0, false, nil, Time.utc(2014, 5, 27), info) assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), nil, info) + elsif SUPPORTS_NEGATIVE + assert_period(:LMT, 3542, 0, false, nil, Time.at(-2**31).utc, info) + assert_period(:XST, 3600, 0, false, Time.at(-2**31).utc, Time.utc(2014, 5, 27), info) + assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), nil, info) + else + assert_period(:LMT, 3542, 0, false, nil, Time.utc(1970, 1, 1), info) + assert_period(:XST2, 3600, 0, false, Time.utc(1970, 1, 1), Time.utc(2014, 5, 27), info) + assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), nil, info) end end end def test_load_supported_64bit_range # The full range of 64 bit timestamps is not currently supported because of - # the way transitions are indexed. Transitions outside the supported range - # will be ignored. + # the way transitions are indexed. min_timestamp = -8520336000 # Time.utc(1700, 1, 1).to_i max_timestamp = 16725225600 # Time.utc(2500, 1, 1).to_i @@ -568,7 +665,7 @@ def test_load_supported_64bit_range {:at => max_timestamp - 1, :offset_index => 0}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/SupportedSixtyFourRange', path) + info = ZoneinfoTimezoneInfo.new('Zone/SupportedSixtyFourRange', path, @posix_tz_parser) assert_equal('Zone/SupportedSixtyFourRange', info.identifier) if SUPPORTS_64BIT && format >= 2 @@ -576,9 +673,14 @@ def test_load_supported_64bit_range assert_period(:XST, 3600, 0, false, Time.at(min_timestamp).utc, Time.utc(2014, 5, 27), info) assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), Time.at(max_timestamp - 1).utc, info) assert_period(:LMT, 3542, 0, false, Time.at(max_timestamp - 1).utc, nil, info) - else + elsif format < 2 assert_period(:LMT, 3542, 0, false, nil, Time.utc(2014, 5, 27), info) assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), nil, info) + else + min_supported = SUPPORTS_NEGATIVE ? -2**31 : 0 + assert_period(:LMT, 3542, 0, false, nil, Time.at(min_supported).utc, info) + assert_period(:XST, 3600, 0, false, Time.at(min_supported).utc, Time.utc(2014, 5, 27), info) + assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), nil, info) end end end @@ -595,19 +697,14 @@ def test_load_32bit_range {:at => 2**31 - 1, :offset_index => 0}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/ThirtyTwoRange', path) + info = ZoneinfoTimezoneInfo.new('Zone/ThirtyTwoRange', path, @posix_tz_parser) assert_equal('Zone/ThirtyTwoRange', info.identifier) - if SUPPORTS_NEGATIVE - assert_period(:LMT, 3542, 0, false, nil, Time.at(-2**31).utc, info) - assert_period(:XST, 3600, 0, false, Time.at(-2**31).utc, Time.utc(2014, 5, 27), info) - assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), Time.at(2**31 - 1).utc, info) - assert_period(:LMT, 3542, 0, false, Time.at(2**31 - 1).utc, nil, info) - else - assert_period(:XST, 3600, 0, false, Time.utc(1970, 1, 1), Time.utc(2014, 5, 27), info) - assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), Time.at(2**31 - 1).utc, info) - assert_period(:LMT, 3542, 0, false, Time.at(2**31 - 1).utc, nil, info) - end + min_supported = SUPPORTS_NEGATIVE ? -2**31 : 0 + assert_period(:LMT, 3542, 0, false, nil, Time.at(min_supported).utc, info) + assert_period(:XST, 3600, 0, false, Time.at(min_supported).utc, Time.utc(2014, 5, 27), info) + assert_period(:XNST, 7200, 0, false, Time.utc(2014, 5, 27), Time.at(2**31 - 1), info) + assert_period(:LMT, 3542, 0, false, Time.at(2**31 - 1).utc, nil, info) end end @@ -628,7 +725,7 @@ def test_load_std_offset_changes {:at => Time.utc(2000, 4, 1), :offset_index => 1}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path) + info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path, @posix_tz_parser) assert_equal('Zone/DoubleDaylight', info.identifier) assert_period(:LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -645,7 +742,7 @@ def test_load_std_offset_changes_jump_to_double_dst offsets = [ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, - {:gmtoff => 3600, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST'}, {:gmtoff => 10800, :isdst => true, :abbrev => 'XDDT'}] transitions = [ @@ -654,7 +751,7 @@ def test_load_std_offset_changes_jump_to_double_dst {:at => Time.utc(2000, 6, 1), :offset_index => 1}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path) + info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path, @posix_tz_parser) assert_equal('Zone/DoubleDaylight', info.identifier) assert_period(:LMT, 3542, 0, false, nil, Time.utc(2000, 4, 1), info) @@ -683,7 +780,7 @@ def test_load_std_offset_changes_negative {:at => Time.utc(2000, 6, 1), :offset_index => 1}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path) + info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path, @posix_tz_parser) assert_equal('Zone/DoubleDaylight', info.identifier) assert_period(:LMT, -10821, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -712,7 +809,7 @@ def test_load_starts_two_hour_std_offset {:at => Time.utc(2000, 3, 1), :offset_index => 1}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path) + info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path, @posix_tz_parser) assert_equal('Zone/DoubleDaylight', info.identifier) assert_period(:LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -733,7 +830,7 @@ def test_load_starts_only_dst_transition_with_lmt transitions = [{:at => Time.utc(2000, 1, 1), :offset_index => 1}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/OnlyDST', path) + info = ZoneinfoTimezoneInfo.new('Zone/OnlyDST', path, @posix_tz_parser) assert_equal('Zone/OnlyDST', info.identifier) assert_period(:LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -750,7 +847,7 @@ def test_load_starts_only_dst_transition_without_lmt transitions = [{:at => Time.utc(2000, 1, 1), :offset_index => 0}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/OnlyDST', path) + info = ZoneinfoTimezoneInfo.new('Zone/OnlyDST', path, @posix_tz_parser) assert_equal('Zone/OnlyDST', info.identifier) assert_period(:XDT, 3600, 3600, true, nil, Time.utc(2000, 1, 1), info) @@ -775,7 +872,7 @@ def test_load_switch_to_dst_and_change_utc_offset {:at => Time.utc(2000, 2, 1), :offset_index => 2}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path) + info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path, @posix_tz_parser) assert_equal('Zone/DoubleDaylight', info.identifier) assert_period(:LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -805,7 +902,7 @@ def test_load_apia_international_dateline_change {:at => Time.utc(2012, 3, 31, 14, 0, 0), :offset_index => 4}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Test/Pacific/Apia', path) + info = ZoneinfoTimezoneInfo.new('Test/Pacific/Apia', path, @posix_tz_parser) assert_equal('Test/Pacific/Apia', info.identifier) assert_period( :LMT, 45184, 0, false, nil, Time.utc(2011, 4, 2, 14, 0, 0), info) @@ -844,7 +941,7 @@ def test_load_offset_split_for_different_utc_offset # offsets. tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/SplitUtcOffset', path) + info = ZoneinfoTimezoneInfo.new('Zone/SplitUtcOffset', path, @posix_tz_parser) assert_equal('Zone/SplitUtcOffset', info.identifier) assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -886,7 +983,7 @@ def test_load_offset_utc_offset_taken_from_minimum_difference_minimum_after # utc_offset of 3600 and std_offset of 7200). tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/MinimumUtcOffset', path) + info = ZoneinfoTimezoneInfo.new('Zone/MinimumUtcOffset', path, @posix_tz_parser) assert_equal('Zone/MinimumUtcOffset', info.identifier) assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -915,7 +1012,7 @@ def test_load_offset_utc_offset_taken_from_minimum_difference_minimum_before # utc_offset of 3600 and std_offset of 7200). tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/MinimumUtcOffset', path) + info = ZoneinfoTimezoneInfo.new('Zone/MinimumUtcOffset', path, @posix_tz_parser) assert_equal('Zone/MinimumUtcOffset', info.identifier) assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -944,7 +1041,7 @@ def test_load_offset_does_not_use_equal_utc_total_offset_equal_after # equivalent (or greater) utc_total_offset. tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetEqual', path) + info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetEqual', path, @posix_tz_parser) assert_equal('Zone/UtcOffsetEqual', info.identifier) assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -973,7 +1070,7 @@ def test_load_offset_does_not_use_equal_utc_total_offset_equal_before # equivalent (or greater) utc_total_offset. tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetEqual', path) + info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetEqual', path, @posix_tz_parser) assert_equal('Zone/UtcOffsetEqual', info.identifier) assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -1001,7 +1098,7 @@ def test_load_offset_both_adjacent_non_dst_equal_utc_total_offset # from utc_total_offset - std_offset. tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/AdjacentEqual', path) + info = ZoneinfoTimezoneInfo.new('Zone/AdjacentEqual', path, @posix_tz_parser) assert_equal('Zone/AdjacentEqual', info.identifier) assert_period(:LMT, 7142, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -1032,7 +1129,7 @@ def test_load_offset_utc_offset_preserved_from_next # an equivalent utc_offset of 3600 and std_offset of 7200). tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetPreserved', path) + info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetPreserved', path, @posix_tz_parser) assert_equal('Zone/UtcOffsetPreserved', info.identifier) assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -1064,7 +1161,7 @@ def test_load_offset_utc_offset_preserved_from_previous # an equivalent utc_offset of 3600 and std_offset of 7200). tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetPreserved', path) + info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetPreserved', path, @posix_tz_parser) assert_equal('Zone/UtcOffsetPreserved', info.identifier) assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -1092,7 +1189,7 @@ def test_read_offset_negative_std_offset_dst {:at => Time.utc(2000, 5, 1), :offset_index => 1}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/NegativeStdOffsetDst', path) + info = ZoneinfoTimezoneInfo.new('Zone/NegativeStdOffsetDst', path, @posix_tz_parser) assert_equal('Zone/NegativeStdOffsetDst', info.identifier) assert_period(:LMT, -100, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -1121,7 +1218,7 @@ def test_read_offset_negative_std_offset_dst_initial_dst {:at => Time.utc(2000, 5, 1), :offset_index => 1}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/NegativeStdOffsetDstInitialDst', path) + info = ZoneinfoTimezoneInfo.new('Zone/NegativeStdOffsetDstInitialDst', path, @posix_tz_parser) assert_equal('Zone/NegativeStdOffsetDstInitialDst', info.identifier) assert_period(:LMT, -100, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -1146,7 +1243,7 @@ def test_read_offset_prefer_base_offset_moves_to_dst_not_hour {:at => Time.utc(2000, 3, 1), :offset_index => 3}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/BaseOffsetMovesToDstNotHour', path) + info = ZoneinfoTimezoneInfo.new('Zone/BaseOffsetMovesToDstNotHour', path, @posix_tz_parser) assert_equal('Zone/BaseOffsetMovesToDstNotHour', info.identifier) assert_period(:LMT, -100, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -1169,7 +1266,7 @@ def test_read_offset_prefer_base_offset_moves_from_dst_not_hour {:at => Time.utc(2000, 3, 1), :offset_index => 3}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/BaseOffsetMovesFromDstNotHour', path) + info = ZoneinfoTimezoneInfo.new('Zone/BaseOffsetMovesFromDstNotHour', path, @posix_tz_parser) assert_equal('Zone/BaseOffsetMovesFromDstNotHour', info.identifier) assert_period(:LMT, -100, 0, false, nil, Time.utc(2000, 1, 1), info) @@ -1187,7 +1284,7 @@ def test_load_in_safe_mode path.untaint safe_test do - info = ZoneinfoTimezoneInfo.new('Zone/three', path) + info = ZoneinfoTimezoneInfo.new('Zone/three', path, @posix_tz_parser) assert_equal('Zone/three', info.identifier) assert_period(:LT, -12094, 0, false, nil, nil, info) @@ -1207,7 +1304,7 @@ def test_load_encoding {:at => Time.utc(1971, 1, 2), :offset_index => 1}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/One', path) + info = ZoneinfoTimezoneInfo.new('Zone/One', path, @posix_tz_parser) assert_equal('Zone/One', info.identifier) assert_period(:LMT, 3542, 0, false, nil, Time.utc(1971, 1, 2), info) @@ -1226,11 +1323,753 @@ def test_load_binmode {:at => Time.utc(2011, 12, 31, 13, 24, 26), :offset_index => 1}] tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/One', path) + info = ZoneinfoTimezoneInfo.new('Zone/One', path, @posix_tz_parser) assert_equal('Zone/One', info.identifier) assert_period(:LMT, 3542, 0, false, nil, Time.utc(2011, 12, 31, 13, 24, 26), info) assert_period(:XST, 3600, 0, false, Time.utc(2011, 12, 31, 13, 24, 26), nil, info) end end + + def test_load_invalid_tz_string + offsets = [{:gmtoff => 0, :isdst => false, :abbrev => 'UTC'}] + + tzif_test(offsets, [], :rules => :fail) do |path, format| + error = assert_raises(InvalidZoneinfoFile) { ZoneinfoTimezoneInfo.new('Invalid/String', path, @posix_tz_parser) } + assert_equal("Failed to parse POSIX-style TZ string in file '#{path}': FakePosixTimeZoneParser Failure.", error.message) + end + end + + def test_load_tz_string_missing_start_newline + offsets = [{:gmtoff => 0, :isdst => false, :abbrev => 'UTC'}] + rules = TimezoneOffset.new(0, 0, 'UTC') + + tzif_test(offsets, [], :rules => rules, :omit_tz_string_start_new_line => true) do |path, format| + error = assert_raises(InvalidZoneinfoFile) { ZoneinfoTimezoneInfo.new('Missing/Start', path, @posix_tz_parser) } + assert_equal("Expected newline starting POSIX-style TZ string in file '#{path}'.", error.message) + end + end + + def test_load_tz_string_missing_end_newline + offsets = [{:gmtoff => 0, :isdst => false, :abbrev => 'UTC'}] + rules = TimezoneOffset.new(0, 0, 'UTC') + + tzif_test(offsets, [], :rules => rules, :omit_tz_string_end_new_line => true) do |path, format| + error = assert_raises(InvalidZoneinfoFile) { ZoneinfoTimezoneInfo.new('Missing/End', path, @posix_tz_parser) } + assert_equal("Expected newline ending POSIX-style TZ string in file '#{path}'.", error.message) + end + end + + [ + [false, 1, 0, 'TEST'], + [false, 0, 1, 'TEST'], + [false, 0, 0, 'TEST2'], + [false, -1, 1, 'TEST'], + [true, 0, 0, 'TEST'] + ].each do |(isdst, base_utc_offset, std_offset, abbreviation)| + define_method "test_load_tz_string_does_not_match_#{isdst ? 'dst' : 'std'}_constant_offset_#{base_utc_offset}_#{std_offset}_#{abbreviation}" do + offsets = [{:gmtoff => 0, :isdst => isdst, :abbrev => 'TEST'}] + rules = TimezoneOffset.new(base_utc_offset, std_offset, abbreviation) + + tzif_test(offsets, [], :rules => rules) do |path, format| + error = assert_raises(InvalidZoneinfoFile) { ZoneinfoTimezoneInfo.new('Offset/Mismatch', path, @posix_tz_parser) } + assert_equal("Constant offset POSIX-style TZ string does not match constant offset in file '#{path}'.", error.message) + end + end + end + + [ + [3601, 'XST'], + [3600, 'YST'] + ].each do |(base_utc_offset, abbreviation)| + define_method "test_load_tz_string_does_not_match_final_std_transition_offset_#{base_utc_offset}_#{abbreviation}" do + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 2, 0, 0) - 3542, :offset_index => 1}, + {:at => Time.utc(1981, 4, 10, 2, 0, 0) - 3600, :offset_index => 2}, + {:at => Time.utc(1981, 10, 27, 2, 0, 0) - 7200, :offset_index => 1} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(base_utc_offset, 0, abbreviation), + TimezoneOffset.new(3600, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 7200), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + error = assert_raises(InvalidZoneinfoFile) { ZoneinfoTimezoneInfo.new('Offset/Mismatch', path, @posix_tz_parser) } + assert_equal("The first offset indicated by the POSIX-style TZ string did not match the final defined offset in file '#{path}'.", error.message) + end + end + end + + [ + [3601, 0, 'XST'], + [3600, 1, 'XST'], + [3600, 0, 'YST'] + ].each do |(base_utc_offset, std_offset, abbreviation)| + define_method "test_load_tz_string_does_not_match_final_dst_transition_offset_#{base_utc_offset}_#{std_offset}_#{abbreviation}" do + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 2, 0, 0) - 3542, :offset_index => 1}, + {:at => Time.utc(1981, 4, 10, 2, 0, 0) - 3600, :offset_index => 2} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(3600, 0, 'XST'), + TimezoneOffset.new(base_utc_offset, std_offset, abbreviation), + JulianDayOfYearTransitionRule.new(100, 7200), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + error = assert_raises(InvalidZoneinfoFile) { ZoneinfoTimezoneInfo.new('Offset/Mismatch', path, @posix_tz_parser) } + assert_equal("The first offset indicated by the POSIX-style TZ string did not match the final defined offset in file '#{path}'.", error.message) + end + end + end + + [ + [3600, 0], + [3600, 3600], + [10800, -3600] + ].each do |(base_utc_offset, std_offset)| + rules = TimezoneOffset.new(base_utc_offset, std_offset, 'TEST') + + define_method "test_load_tz_string_uses_constant_offset_with_no_transitions_#{base_utc_offset}_#{std_offset}" do + offsets = [{:gmtoff => base_utc_offset + std_offset, :isdst => std_offset != 0, :abbrev => 'TEST'}] + + tzif_test(offsets, [], :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('Constant/Offset', path, @posix_tz_parser) + assert_equal('Constant/Offset', info.identifier) + assert_period(:TEST, base_utc_offset, std_offset, std_offset != 0, nil, nil, info) + end + end + + define_method "test_load_tz_string_uses_constant_offset_after_last_transition_#{base_utc_offset}_#{std_offset}" do + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => base_utc_offset + std_offset, :isdst => std_offset != 0, :abbrev => 'TEST'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 2, 0, 0) - 3542, :offset_index => 1} + ] + + t0 = Time.utc(1971, 1, 2, 2, 0, 0) - 3542 + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('Constant/After', path, @posix_tz_parser) + assert_equal('Constant/After', info.identifier) + assert_period(:LMT, 3542, 0, false, nil, t0, info) + assert_period(:TEST, base_utc_offset, std_offset, std_offset != 0, t0, nil, info) + end + end + end + + def test_load_tz_string_uses_rules_to_generate_all_transitions_when_none_defined + offsets = [{:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, [], :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('All/Rules', path, @posix_tz_parser) + assert_equal('All/Rules', info.identifier) + + assert_period(:XST, 7200, 0, false, nil, Time.utc(1970, 4, 10, 1, 0, 0) - 7200, info) + + 1970.upto(generate_up_to - 1).each do |year| + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_uses_rules_to_generate_all_transitions_when_none_defined_omitting_first_if_matches_first_offset + offsets = [{:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'}] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, [], :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('All/Rules', path, @posix_tz_parser) + assert_equal('All/Rules', info.identifier) + + assert_period(:XDT, 7200, 3600, true, nil, Time.utc(1970, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(1970, 10, 27, 2, 0, 0) - 10800, Time.utc(1971, 4, 10, 1, 0, 0) - 7200, info) + + 1971.upto(generate_up_to - 1).each do |year| + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_uses_rules_to_generate_all_transitions_when_none_defined_with_previous_offset_of_first_matching_first_offset + offsets = [{:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, [], :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('All/Rules', path, @posix_tz_parser) + assert_equal('All/Rules', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1970, 4, 10, 1, 0, 0) - 7200, info) + + 1970.upto(generate_up_to - 1).each do |year| + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_uses_rules_to_generate_all_transitions_when_none_defined_correcting_initial_offset + offsets = [{:gmtoff => 10800, :isdst => true, :abbrev => 'XDDT'}] + + rules = AnnualRules.new( + TimezoneOffset.new(3600, 0, 'XST'), + TimezoneOffset.new(3600, 7200, 'XDDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, [], :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('All/Rules', path, @posix_tz_parser) + assert_equal('All/Rules', info.identifier) + + assert_period(:XDDT, 3600, 7200, true, nil, Time.utc(1970, 10, 27, 2, 0, 0) - 10800, info) # would be :XDT, 7200, 3600 otherwise + assert_period(:XST, 3600, 0, false, Time.utc(1970, 10, 27, 2, 0, 0) - 10800, Time.utc(1971, 4, 10, 1, 0, 0) - 3600, info) + + 1971.upto(generate_up_to - 1).each do |year| + assert_period(:XDDT, 3600, 7200, true, Time.utc(year, 4, 10, 1, 0, 0) - 3600, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 3600, 0, false, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 3600, info) + end + + assert_period(:XDDT, 3600, 7200, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 3600, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 3600, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_extends_transitions_starting_from_std_to_dst_following_year + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 2, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(1981, 4, 10, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(1981, 10, 27, 2, 0, 0) - 10800, :offset_index => 1} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('From/Rules', path, @posix_tz_parser) + assert_equal('From/Rules', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, info) + assert_period(:XST, 7200, 0, false, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, Time.utc(1981, 4, 10, 1, 0, 0) - 7200, info) + + 1981.upto(generate_up_to - 1).each do |year| + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_extends_transitions_starting_from_dst_to_std_same_year + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 2, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(1981, 4, 10, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(1981, 10, 27, 2, 0, 0) - 10800, :offset_index => 1}, + {:at => Time.utc(1982, 4, 10, 1, 0, 0) - 7200, :offset_index => 2} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('From/Rules', path, @posix_tz_parser) + assert_equal('From/Rules', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, info) + assert_period(:XST, 7200, 0, false, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, Time.utc(1981, 4, 10, 1, 0, 0) - 7200, info) + + 1981.upto(generate_up_to - 1).each do |year| + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_extends_transitions_starting_from_dst_to_std_following_year + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 1, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(1981, 10, 27, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(1982, 4, 10, 2, 0, 0) - 10800, :offset_index => 1}, + {:at => Time.utc(1982, 10, 27, 1, 0, 0) - 7200, :offset_index => 2} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(300, 3600), + JulianDayOfYearTransitionRule.new(100, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('From/Rules', path, @posix_tz_parser) + assert_equal('From/Rules', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1971, 1, 2, 1, 0, 0) - 7142, info) + assert_period(:XST, 7200, 0, false, Time.utc(1971, 1, 2, 1, 0, 0) - 7142, Time.utc(1981, 10, 27, 1, 0, 0) - 7200, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(1981, 10, 27, 1, 0, 0) - 7200, Time.utc(1982, 4, 10, 2, 0, 0) - 10800, info) + + 1982.upto(generate_up_to - 1).each do |year| + assert_period(:XST, 7200, 0, false, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_extends_transitions_starting_from_std_to_dst_same_year + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 1, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(1981, 10, 27, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(1982, 4, 10, 2, 0, 0) - 10800, :offset_index => 1}, + {:at => Time.utc(1982, 10, 27, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(1983, 4, 10, 2, 0, 0) - 10800, :offset_index => 1} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(300, 3600), + JulianDayOfYearTransitionRule.new(100, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('From/Rules', path, @posix_tz_parser) + assert_equal('From/Rules', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1971, 1, 2, 1, 0, 0) - 7142, info) + assert_period(:XST, 7200, 0, false, Time.utc(1971, 1, 2, 1, 0, 0) - 7142, Time.utc(1981, 10, 27, 1, 0, 0) - 7200, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(1981, 10, 27, 1, 0, 0) - 7200, Time.utc(1982, 4, 10, 2, 0, 0) - 10800, info) + + 1982.upto(generate_up_to - 1).each do |year| + assert_period(:XST, 7200, 0, false, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_extends_transitions_negative_dst + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}, + {:gmtoff => 10800, :isdst => false, :abbrev => 'XST'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 2, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(1981, 4, 10, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(1981, 10, 27, 2, 0, 0) - 10800, :offset_index => 1} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(10800, 0, 'XST'), + TimezoneOffset.new(10800, -3600, 'XDT'), + JulianDayOfYearTransitionRule.new(300, 7200), + JulianDayOfYearTransitionRule.new(100, 3600) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('From/Rules', path, @posix_tz_parser) + assert_equal('From/Rules', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, info) + assert_period(:XDT, 10800, -3600, true, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, Time.utc(1981, 4, 10, 1, 0, 0) - 7200, info) + + 1981.upto(generate_up_to - 1).each do |year| + assert_period(:XST, 10800, 0, false, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XDT, 10800, -3600, true, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XST, 10800, 0, false, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XDT, 10800, -3600, true, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_extends_single_transition_in_final_year + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + transitions = [ + {:at => Time.utc( 1971, 1, 2, 2, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(generate_up_to - 1, 4, 10, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(generate_up_to - 1, 10, 27, 2, 0, 0) - 10800, :offset_index => 1}, + {:at => Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, :offset_index => 2} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('Final/Year', path, @posix_tz_parser) + assert_equal('Final/Year', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc( 1971, 1, 2, 2, 0, 0) - 7142, info) + assert_period(:XST, 7200, 0, false, Time.utc( 1971, 1, 2, 2, 0, 0) - 7142, Time.utc(generate_up_to - 1, 4, 10, 1, 0, 0) - 7200, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to - 1, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to - 1, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to - 1, 10, 27, 2, 0, 0) - 10800, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_adds_nothing_if_transitions_up_to_final_year + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + transitions = [ + {:at => Time.utc( 1971, 1, 2, 2, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, :offset_index => 1}, + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('Final/Year', path, @posix_tz_parser) + assert_equal('Final/Year', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc( 1971, 1, 2, 2, 0, 0) - 7142, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_corrects_offset_of_final_transition + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}] + + transitions = [{:at => Time.utc(2000, 4, 10, 1, 0, 0) - 3542, :offset_index => 1}] + + rules = AnnualRules.new( + TimezoneOffset.new(3600, 0, 'XST'), + TimezoneOffset.new(3600, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('Correct/Final', path, @posix_tz_parser) + assert_equal('Correct/Final', info.identifier) + + assert_period(:LMT, 3542, 0, false, nil, Time.utc(2000, 4, 10, 1, 0, 0) - 3542, info) + assert_period(:XDT, 3600, 3600, true, Time.utc(2000, 4, 10, 1, 0, 0) - 3542, Time.utc(2000, 10, 27, 2, 0, 0) - 7200, info) # would be :XDT, 3542, 3658 without tz_string + assert_period(:XST, 3600, 0, false, Time.utc(2000, 10, 27, 2, 0, 0) - 7200, Time.utc(2001, 4, 10, 1, 0, 0) - 3600, info) + + 2001.upto(generate_up_to - 1).each do |year| + assert_period(:XDT, 3600, 3600, true, Time.utc(year, 4, 10, 1, 0, 0) - 3600, Time.utc(year, 10, 27, 2, 0, 0) - 7200, info) + assert_period(:XST, 3600, 0, false, Time.utc(year, 10, 27, 2, 0, 0) - 7200, Time.utc(year + 1, 4, 10, 1, 0, 0) - 3600, info) + end + + assert_period(:XDT, 3600, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 3600, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 7200, info) + assert_period(:XST, 3600, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 7200, nil, info) + end + end + + def test_load_tz_string_specifies_transition_to_offset_of_final_transition_same_year + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 2, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(1981, 4, 10, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(1981, 10, 27, 2, 0, 0) - 10800, :offset_index => 1}, + {:at => Time.utc(1982, 4, 10, 1, 0, 0) - 7200, :offset_index => 2} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(101, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + error = assert_raises(InvalidZoneinfoFile) { ZoneinfoTimezoneInfo.new('Invalid/Offset', path, @posix_tz_parser) } + assert_equal("The first offset indicated by the POSIX-style TZ string did not match the final defined offset in file '#{path}'.", error.message) + end + end + + def test_load_tz_string_specifies_transition_to_offset_of_final_transition_following_year + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 2, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(1981, 10, 27, 2, 0, 0) - 7200, :offset_index => 2} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(299, 7200) + ) + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + error = assert_raises(InvalidZoneinfoFile) { ZoneinfoTimezoneInfo.new('Invalid/Offset', path, @posix_tz_parser) } + assert_equal("The first offset indicated by the POSIX-style TZ string did not match the final defined offset in file '#{path}'.", error.message) + end + end + + unless SUPPORTS_64BIT + def test_generate_up_to_limited_to_2037_with_no_64_bit_support + assert_equal(2037, ZoneinfoTimezoneInfo::GENERATE_UP_TO) + end + + def test_load_tz_string_does_not_generate_if_transition_after_32_bit_range_with_no_64_bit_support + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 2, 0, 0) - 7142, :offset_index => 1}, + {:at => 2154474000 - 7200, :offset_index => 2}, # Time.utc(2038, 4, 10, 1, 0, 0).to_i + {:at => 2171757600 - 10800, :offset_index => 1} # Time.utc(2038, 10, 27, 2, 0, 0).to_i + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('From/Rules', path, @posix_tz_parser) + assert_equal('From/Rules', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, info) + assert_period(:XST, 7200, 0, false, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, nil, info) + end + end + end + + if SUPPORTS_NEGATIVE + def test_load_tz_string_generates_from_last_transition_if_before_1970_and_supports_negative + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1961, 1, 2, 2, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(1962, 4, 10, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(1962, 10, 27, 2, 0, 0) - 10800, :offset_index => 1} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('Pre/1970', path, @posix_tz_parser) + assert_equal('Pre/1970', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1961, 1, 2, 2, 0, 0) - 7142, info) + assert_period(:XST, 7200, 0, false, Time.utc(1961, 1, 2, 2, 0, 0) - 7142, Time.utc(1962, 4, 10, 1, 0, 0) - 7200, info) + + 1962.upto(generate_up_to - 1).each do |year| + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + else + def test_load_tz_string_generates_from_1970_if_last_transition_before_1970_and_does_not_support_negative + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => -283903200 - 7142, :offset_index => 1}, # Time.utc(1961, 1, 2, 2, 0, 0) + {:at => -243903600 - 7200, :offset_index => 2}, # Time.utc(1962, 4, 10, 1, 0, 0) + {:at => -226620000 - 10800, :offset_index => 1} # Time.utc(1962, 10, 27, 2, 0, 0) + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(300, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('Pre/1970', path, @posix_tz_parser) + assert_equal('Pre/1970', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1970, 1, 1), info) + assert_period(:XST, 7200, 0, false, Time.utc(1970, 1, 1), Time.utc(1970, 4, 10, 1, 0, 0) - 7200, info) + + 1970.upto(generate_up_to - 1).each do |year| + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(year, 10, 27, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + end + + def test_load_tz_string_as_utf8 + offsets = [{:gmtoff => 3600, :isdst => false, :abbrev => 'áccént'}] + rules = TimezoneOffset.new(3600, 0, 'áccént') + + tzif_test(offsets, [], :tz_string => '<áccént>1', :rules => rules) do |path, format| + # FakePosixTimeZoneParser will test that the tz_string matches. + info = ZoneinfoTimezoneInfo.new('Test/Utf8', path, @posix_tz_parser) + assert_period(:'áccént', 3600, 0, false, nil, nil, info) + end + end end From 196967a9e159930f70563ec824e705da65b9c889 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 5 Nov 2020 19:32:40 +0000 Subject: [PATCH 55/81] Update time zone test fixtures based on tzdata 2020d. zoneinfo files are using the "slim" format. --- .../America/Argentina/Buenos_Aires.rb | 10 +- .../data/definitions/America/New_York.rb | 14 +- .../data/definitions/Australia/Melbourne.rb | 14 +- .../tzinfo/data/definitions/EST.rb | 2 +- .../tzinfo/data/definitions/Etc/GMT__m__1.rb | 4 +- .../tzinfo/data/definitions/Etc/GMT__p__1.rb | 4 +- .../tzinfo/data/definitions/Etc/UTC.rb | 2 +- .../data/definitions/Europe/Amsterdam.rb | 18 +- .../tzinfo/data/definitions/Europe/Andorra.rb | 14 +- .../tzinfo/data/definitions/Europe/London.rb | 14 +- .../tzinfo/data/definitions/Europe/Paris.rb | 18 +- .../tzinfo/data/definitions/Europe/Prague.rb | 23 +- .../tzinfo/data/definitions/UTC.rb | 2 +- .../tzinfo/data/indexes/countries.rb | 381 +++++++++--------- .../tzinfo/data/indexes/timezones.rb | 107 ++--- test/tzinfo-data/tzinfo/data/version.rb | 12 +- test/zoneinfo/America/Argentina/Buenos_Aires | Bin 1087 -> 708 bytes test/zoneinfo/America/New_York | Bin 3545 -> 1744 bytes test/zoneinfo/Australia/Melbourne | Bin 2223 -> 904 bytes test/zoneinfo/EST | Bin 127 -> 111 bytes test/zoneinfo/Etc/UTC | Bin 127 -> 111 bytes test/zoneinfo/Europe/Amsterdam | Bin 2943 -> 1071 bytes test/zoneinfo/Europe/Andorra | Bin 1751 -> 389 bytes test/zoneinfo/Europe/London | Bin 3687 -> 1599 bytes test/zoneinfo/Europe/Paris | Bin 2971 -> 1105 bytes test/zoneinfo/Europe/Prague | Bin 2272 -> 723 bytes test/zoneinfo/Factory | Bin 264 -> 113 bytes test/zoneinfo/iso3166.tab | 27 +- test/zoneinfo/leapseconds | 59 ++- test/zoneinfo/posix/Europe/London | Bin 3687 -> 1599 bytes test/zoneinfo/posixrules | Bin 3545 -> 1744 bytes test/zoneinfo/right/Europe/London | Bin 4187 -> 2358 bytes test/zoneinfo/zone.tab | 331 +++++++-------- test/zoneinfo/zone1970.tab | 355 ++++++++-------- 34 files changed, 787 insertions(+), 624 deletions(-) diff --git a/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb b/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb index d5b64833..19853cec 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -14,10 +14,10 @@ module Buenos_Aires timezone 'America/Argentina/Buenos_Aires' do |tz| tz.offset :o0, -14028, 0, :LMT tz.offset :o1, -15408, 0, :CMT - tz.offset :o2, -14400, 0, :ART - tz.offset :o3, -14400, 3600, :ARST - tz.offset :o4, -10800, 0, :ART - tz.offset :o5, -10800, 3600, :ARST + tz.offset :o2, -14400, 0, :'-04' + tz.offset :o3, -14400, 3600, :'-03' + tz.offset :o4, -10800, 0, :'-03' + tz.offset :o5, -10800, 3600, :'-02' tz.transition 1894, 10, :o1, -2372097972, 17374555169, 7200 tz.transition 1920, 5, :o2, -1567453392, 1453467407, 600 diff --git a/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb b/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb index 31ef4cb6..6802f535 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -307,6 +307,18 @@ module New_York tz.transition 2063, 11, :o1, 2961381600, 9899451, 4 tz.transition 2064, 3, :o2, 2972271600, 59399731, 24 tz.transition 2064, 11, :o1, 2992831200, 9900907, 4 + tz.transition 2065, 3, :o2, 3003721200, 59408467, 24 + tz.transition 2065, 11, :o1, 3024280800, 9902363, 4 + tz.transition 2066, 3, :o2, 3035775600, 59417371, 24 + tz.transition 2066, 11, :o1, 3056335200, 9903847, 4 + tz.transition 2067, 3, :o2, 3067225200, 59426107, 24 + tz.transition 2067, 11, :o1, 3087784800, 9905303, 4 + tz.transition 2068, 3, :o2, 3098674800, 59434843, 24 + tz.transition 2068, 11, :o1, 3119234400, 9906759, 4 + tz.transition 2069, 3, :o2, 3130124400, 59443579, 24 + tz.transition 2069, 11, :o1, 3150684000, 9908215, 4 + tz.transition 2070, 3, :o2, 3161574000, 59452315, 24 + tz.transition 2070, 11, :o1, 3182133600, 9909671, 4 end end end diff --git a/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb b/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb index 1398ccb8..6acd078e 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -210,6 +210,18 @@ module Melbourne tz.transition 2063, 3, :o1, 2942582400, 14847871, 6 tz.transition 2063, 10, :o2, 2958912000, 14849005, 6 tz.transition 2064, 4, :o1, 2974636800, 14850097, 6 + tz.transition 2064, 10, :o2, 2990361600, 14851189, 6 + tz.transition 2065, 4, :o1, 3006086400, 14852281, 6 + tz.transition 2065, 10, :o2, 3021811200, 14853373, 6 + tz.transition 2066, 4, :o1, 3037536000, 14854465, 6 + tz.transition 2066, 10, :o2, 3053260800, 14855557, 6 + tz.transition 2067, 4, :o1, 3068985600, 14856649, 6 + tz.transition 2067, 10, :o2, 3084710400, 14857741, 6 + tz.transition 2068, 3, :o1, 3100435200, 14858833, 6 + tz.transition 2068, 10, :o2, 3116764800, 14859967, 6 + tz.transition 2069, 4, :o1, 3132489600, 14861059, 6 + tz.transition 2069, 10, :o2, 3148214400, 14862151, 6 + tz.transition 2070, 4, :o1, 3163939200, 14863243, 6 end end end diff --git a/test/tzinfo-data/tzinfo/data/definitions/EST.rb b/test/tzinfo-data/tzinfo/data/definitions/EST.rb index 66940efc..82808df3 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/EST.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/EST.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data diff --git a/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb b/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb index 12972083..57ab6ed0 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -11,7 +11,7 @@ module GMT__m__1 include TimezoneDefinition timezone 'Etc/GMT-1' do |tz| - tz.offset :o0, 3600, 0, :'GMT-1' + tz.offset :o0, 3600, 0, :'+01' end end diff --git a/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb b/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb index 41977619..dec19e56 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -11,7 +11,7 @@ module GMT__p__1 include TimezoneDefinition timezone 'Etc/GMT+1' do |tz| - tz.offset :o0, -3600, 0, :'GMT+1' + tz.offset :o0, -3600, 0, :'-01' end end diff --git a/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb b/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb index ae5afe98..48fbcf29 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data diff --git a/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb b/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb index cab58db8..bedf7e4c 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -14,8 +14,8 @@ module Amsterdam tz.offset :o0, 1172, 0, :LMT tz.offset :o1, 1172, 0, :AMT tz.offset :o2, 1172, 3600, :NST - tz.offset :o3, 1200, 3600, :NEST - tz.offset :o4, 1200, 0, :NET + tz.offset :o3, 1200, 3600, :'+0120' + tz.offset :o4, 1200, 0, :'+0020' tz.offset :o5, 3600, 3600, :CEST tz.offset :o6, 3600, 0, :CET @@ -253,6 +253,18 @@ module Amsterdam tz.transition 2063, 10, :o6, 2960758800, 59396533, 24 tz.transition 2064, 3, :o5, 2974064400, 59400229, 24 tz.transition 2064, 10, :o6, 2992208400, 59405269, 24 + tz.transition 2065, 3, :o5, 3005514000, 59408965, 24 + tz.transition 2065, 10, :o6, 3023658000, 59414005, 24 + tz.transition 2066, 3, :o5, 3036963600, 59417701, 24 + tz.transition 2066, 10, :o6, 3055712400, 59422909, 24 + tz.transition 2067, 3, :o5, 3068413200, 59426437, 24 + tz.transition 2067, 10, :o6, 3087162000, 59431645, 24 + tz.transition 2068, 3, :o5, 3099862800, 59435173, 24 + tz.transition 2068, 10, :o6, 3118611600, 59440381, 24 + tz.transition 2069, 3, :o5, 3131917200, 59444077, 24 + tz.transition 2069, 10, :o6, 3150061200, 59449117, 24 + tz.transition 2070, 3, :o5, 3163366800, 59452813, 24 + tz.transition 2070, 10, :o6, 3181510800, 59457853, 24 end end end diff --git a/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb b/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb index d89ef877..d51c5dc3 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -178,6 +178,18 @@ module Andorra tz.transition 2063, 10, :o2, 2960758800, 59396533, 24 tz.transition 2064, 3, :o3, 2974064400, 59400229, 24 tz.transition 2064, 10, :o2, 2992208400, 59405269, 24 + tz.transition 2065, 3, :o3, 3005514000, 59408965, 24 + tz.transition 2065, 10, :o2, 3023658000, 59414005, 24 + tz.transition 2066, 3, :o3, 3036963600, 59417701, 24 + tz.transition 2066, 10, :o2, 3055712400, 59422909, 24 + tz.transition 2067, 3, :o3, 3068413200, 59426437, 24 + tz.transition 2067, 10, :o2, 3087162000, 59431645, 24 + tz.transition 2068, 3, :o3, 3099862800, 59435173, 24 + tz.transition 2068, 10, :o2, 3118611600, 59440381, 24 + tz.transition 2069, 3, :o3, 3131917200, 59444077, 24 + tz.transition 2069, 10, :o2, 3150061200, 59449117, 24 + tz.transition 2070, 3, :o3, 3163366800, 59452813, 24 + tz.transition 2070, 10, :o2, 3181510800, 59457853, 24 end end end diff --git a/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb b/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb index 1a76daee..942e0084 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -313,6 +313,18 @@ module London tz.transition 2063, 10, :o1, 2960758800, 59396533, 24 tz.transition 2064, 3, :o2, 2974064400, 59400229, 24 tz.transition 2064, 10, :o1, 2992208400, 59405269, 24 + tz.transition 2065, 3, :o2, 3005514000, 59408965, 24 + tz.transition 2065, 10, :o1, 3023658000, 59414005, 24 + tz.transition 2066, 3, :o2, 3036963600, 59417701, 24 + tz.transition 2066, 10, :o1, 3055712400, 59422909, 24 + tz.transition 2067, 3, :o2, 3068413200, 59426437, 24 + tz.transition 2067, 10, :o1, 3087162000, 59431645, 24 + tz.transition 2068, 3, :o2, 3099862800, 59435173, 24 + tz.transition 2068, 10, :o1, 3118611600, 59440381, 24 + tz.transition 2069, 3, :o2, 3131917200, 59444077, 24 + tz.transition 2069, 10, :o1, 3150061200, 59449117, 24 + tz.transition 2070, 3, :o2, 3163366800, 59452813, 24 + tz.transition 2070, 10, :o1, 3181510800, 59457853, 24 end end end diff --git a/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb b/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb index 341fc2c7..e16ba37e 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -19,8 +19,8 @@ module Paris tz.offset :o5, 3600, 0, :CET tz.offset :o6, 0, 7200, :WEMT - tz.transition 1891, 3, :o1, -2486678901, 69460027033, 28800 - tz.transition 1911, 3, :o2, -1855958901, 69670267033, 28800 + tz.transition 1891, 3, :o1, -2486592561, 69460055813, 28800 + tz.transition 1911, 3, :o2, -1855958961, 69670267013, 28800 tz.transition 1916, 6, :o3, -1689814800, 58104707, 24 tz.transition 1916, 10, :o2, -1680397200, 58107323, 24 tz.transition 1917, 3, :o3, -1665363600, 58111499, 24 @@ -257,6 +257,18 @@ module Paris tz.transition 2063, 10, :o5, 2960758800, 59396533, 24 tz.transition 2064, 3, :o4, 2974064400, 59400229, 24 tz.transition 2064, 10, :o5, 2992208400, 59405269, 24 + tz.transition 2065, 3, :o4, 3005514000, 59408965, 24 + tz.transition 2065, 10, :o5, 3023658000, 59414005, 24 + tz.transition 2066, 3, :o4, 3036963600, 59417701, 24 + tz.transition 2066, 10, :o5, 3055712400, 59422909, 24 + tz.transition 2067, 3, :o4, 3068413200, 59426437, 24 + tz.transition 2067, 10, :o5, 3087162000, 59431645, 24 + tz.transition 2068, 3, :o4, 3099862800, 59435173, 24 + tz.transition 2068, 10, :o5, 3118611600, 59440381, 24 + tz.transition 2069, 3, :o4, 3131917200, 59444077, 24 + tz.transition 2069, 10, :o5, 3150061200, 59449117, 24 + tz.transition 2070, 3, :o4, 3163366800, 59452813, 24 + tz.transition 2070, 10, :o5, 3181510800, 59457853, 24 end end end diff --git a/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb b/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb index 16d559f5..9db42087 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -15,6 +15,7 @@ module Prague tz.offset :o1, 3464, 0, :PMT tz.offset :o2, 3600, 0, :CET tz.offset :o3, 3600, 3600, :CEST + tz.offset :o4, 3600, -3600, :GMT tz.transition 1849, 12, :o1, -3786829064, 25884991367, 10800 tz.transition 1891, 9, :o2, -2469401864, 26049669767, 10800 @@ -29,11 +30,13 @@ module Prague tz.transition 1943, 3, :o3, -844556400, 58339501, 24 tz.transition 1943, 10, :o2, -828226800, 58344037, 24 tz.transition 1944, 4, :o3, -812502000, 58348405, 24 - tz.transition 1944, 9, :o2, -798073200, 58352413, 24 - tz.transition 1945, 4, :o3, -780534000, 58357285, 24 - tz.transition 1945, 11, :o2, -761180400, 58362661, 24 + tz.transition 1944, 10, :o2, -796777200, 58352773, 24 + tz.transition 1945, 4, :o3, -781052400, 58357141, 24 + tz.transition 1945, 10, :o2, -765327600, 58361509, 24 tz.transition 1946, 5, :o3, -746578800, 58366717, 24 tz.transition 1946, 10, :o2, -733359600, 58370389, 24 + tz.transition 1946, 12, :o4, -728517600, 29185867, 12 + tz.transition 1947, 2, :o2, -721260000, 29186875, 12 tz.transition 1947, 4, :o3, -716425200, 58375093, 24 tz.transition 1947, 10, :o2, -701910000, 58379125, 24 tz.transition 1948, 4, :o3, -684975600, 58383829, 24 @@ -212,6 +215,18 @@ module Prague tz.transition 2063, 10, :o2, 2960758800, 59396533, 24 tz.transition 2064, 3, :o3, 2974064400, 59400229, 24 tz.transition 2064, 10, :o2, 2992208400, 59405269, 24 + tz.transition 2065, 3, :o3, 3005514000, 59408965, 24 + tz.transition 2065, 10, :o2, 3023658000, 59414005, 24 + tz.transition 2066, 3, :o3, 3036963600, 59417701, 24 + tz.transition 2066, 10, :o2, 3055712400, 59422909, 24 + tz.transition 2067, 3, :o3, 3068413200, 59426437, 24 + tz.transition 2067, 10, :o2, 3087162000, 59431645, 24 + tz.transition 2068, 3, :o3, 3099862800, 59435173, 24 + tz.transition 2068, 10, :o2, 3118611600, 59440381, 24 + tz.transition 2069, 3, :o3, 3131917200, 59444077, 24 + tz.transition 2069, 10, :o2, 3150061200, 59449117, 24 + tz.transition 2070, 3, :o3, 3163366800, 59452813, 24 + tz.transition 2070, 10, :o2, 3181510800, 59457853, 24 end end end diff --git a/test/tzinfo-data/tzinfo/data/definitions/UTC.rb b/test/tzinfo-data/tzinfo/data/definitions/UTC.rb index efdb864e..2fec70c5 100644 --- a/test/tzinfo-data/tzinfo/data/definitions/UTC.rb +++ b/test/tzinfo-data/tzinfo/data/definitions/UTC.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data diff --git a/test/tzinfo-data/tzinfo/data/indexes/countries.rb b/test/tzinfo-data/tzinfo/data/indexes/countries.rb index ee102fae..689e68b2 100644 --- a/test/tzinfo-data/tzinfo/data/indexes/countries.rb +++ b/test/tzinfo-data/tzinfo/data/indexes/countries.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -31,27 +31,27 @@ module Countries c.timezone 'Asia/Yerevan', 2411, 60, 89, 2 end country 'AO', 'Angola' do |c| - c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time (UTC+1)' + c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time' end country 'AQ', 'Antarctica' do |c| - c.timezone 'Antarctica/Rothera', -2027, 30, -1022, 15, 'Rothera Station, Adelaide Island' - c.timezone 'Antarctica/Palmer', -324, 5, -641, 10, 'Palmer Station, Anvers Island' - c.timezone 'Antarctica/Mawson', -338, 5, 3773, 60, 'Mawson Station, Holme Bay' - c.timezone 'Antarctica/Davis', -823, 12, 2339, 30, 'Davis Station, Vestfold Hills' - c.timezone 'Antarctica/Casey', -3977, 60, 6631, 60, 'Casey Station, Bailey Peninsula' - c.timezone 'Antarctica/Vostok', -392, 5, 1069, 10, 'Vostok Station, Lake Vostok' - c.timezone 'Antarctica/DumontDUrville', -200, 3, 8401, 60, 'Dumont-d\'Urville Station, Terre Adelie' - c.timezone 'Antarctica/Syowa', -124211, 1800, 3959, 100, 'Syowa Station, E Ongul I' - c.timezone 'Antarctica/Troll', -259241, 3600, 507, 200, 'Troll Station, Queen Maud Land' + c.timezone 'Antarctica/Casey', -3977, 60, 6631, 60, 'Casey' + c.timezone 'Antarctica/Davis', -823, 12, 2339, 30, 'Davis' + c.timezone 'Antarctica/DumontDUrville', -200, 3, 8401, 60, 'Dumont-d\'Urville' + c.timezone 'Antarctica/Mawson', -338, 5, 3773, 60, 'Mawson' + c.timezone 'Antarctica/Palmer', -324, 5, -641, 10, 'Palmer' + c.timezone 'Antarctica/Rothera', -2027, 30, -1022, 15, 'Rothera' + c.timezone 'Antarctica/Syowa', -124211, 1800, 3959, 100, 'Syowa' + c.timezone 'Antarctica/Troll', -259241, 3600, 507, 200, 'Troll' + c.timezone 'Antarctica/Vostok', -392, 5, 1069, 10, 'Vostok' c.timezone 'Pacific/Auckland', -553, 15, 5243, 30, 'New Zealand time' end country 'AR', 'Argentina' do |c| c.timezone 'America/Argentina/Buenos_Aires', -173, 5, -1169, 20, 'Buenos Aires (BA, CF)' - c.timezone 'America/Argentina/Cordoba', -157, 5, -3851, 60, 'most locations (CB, CC, CN, ER, FM, MN, SE, SF)' - c.timezone 'America/Argentina/Salta', -1487, 60, -785, 12, '(SA, LP, NQ, RN)' + c.timezone 'America/Argentina/Cordoba', -157, 5, -3851, 60, 'Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF)' + c.timezone 'America/Argentina/Salta', -1487, 60, -785, 12, 'Salta (SA, LP, NQ, RN)' c.timezone 'America/Argentina/Jujuy', -1451, 60, -653, 10, 'Jujuy (JY)' c.timezone 'America/Argentina/Tucuman', -1609, 60, -3913, 60, 'Tucumán (TM)' - c.timezone 'America/Argentina/Catamarca', -427, 15, -3947, 60, 'Catamarca (CT), Chubut (CH)' + c.timezone 'America/Argentina/Catamarca', -427, 15, -3947, 60, 'Catamarca (CT); Chubut (CH)' c.timezone 'America/Argentina/La_Rioja', -883, 30, -1337, 20, 'La Rioja (LR)' c.timezone 'America/Argentina/San_Juan', -473, 15, -4111, 60, 'San Juan (SJ)' c.timezone 'America/Argentina/Mendoza', -1973, 60, -4129, 60, 'Mendoza (MZ)' @@ -68,22 +68,22 @@ module Countries country 'AU', 'Australia' do |c| c.timezone 'Australia/Lord_Howe', -631, 20, 1909, 12, 'Lord Howe Island' c.timezone 'Antarctica/Macquarie', -109, 2, 3179, 20, 'Macquarie Island' - c.timezone 'Australia/Hobart', -2573, 60, 8839, 60, 'Tasmania - most locations' - c.timezone 'Australia/Currie', -599, 15, 2158, 15, 'Tasmania - King Island' + c.timezone 'Australia/Hobart', -2573, 60, 8839, 60, 'Tasmania (most areas)' + c.timezone 'Australia/Currie', -599, 15, 2158, 15, 'Tasmania (King Island)' c.timezone 'Australia/Melbourne', -2269, 60, 4349, 30, 'Victoria' - c.timezone 'Australia/Sydney', -508, 15, 9073, 60, 'New South Wales - most locations' - c.timezone 'Australia/Broken_Hill', -639, 20, 2829, 20, 'New South Wales - Yancowinna' - c.timezone 'Australia/Brisbane', -412, 15, 4591, 30, 'Queensland - most locations' - c.timezone 'Australia/Lindeman', -304, 15, 149, 1, 'Queensland - Holiday Islands' + c.timezone 'Australia/Sydney', -508, 15, 9073, 60, 'New South Wales (most areas)' + c.timezone 'Australia/Broken_Hill', -639, 20, 2829, 20, 'New South Wales (Yancowinna)' + c.timezone 'Australia/Brisbane', -412, 15, 4591, 30, 'Queensland (most areas)' + c.timezone 'Australia/Lindeman', -304, 15, 149, 1, 'Queensland (Whitsunday Islands)' c.timezone 'Australia/Adelaide', -419, 12, 1663, 12, 'South Australia' c.timezone 'Australia/Darwin', -187, 15, 785, 6, 'Northern Territory' - c.timezone 'Australia/Perth', -639, 20, 2317, 20, 'Western Australia - most locations' - c.timezone 'Australia/Eucla', -1903, 60, 1933, 15, 'Western Australia - Eucla area' + c.timezone 'Australia/Perth', -639, 20, 2317, 20, 'Western Australia (most areas)' + c.timezone 'Australia/Eucla', -1903, 60, 1933, 15, 'Western Australia (Eucla)' end country 'AW', 'Aruba' do |c| c.timezone 'America/Curacao', 731, 60, -69, 1 end - country 'AX', 'Aaland Islands' do |c| + country 'AX', 'Åland Islands' do |c| c.timezone 'Europe/Helsinki', 361, 6, 749, 30 end country 'AZ', 'Azerbaijan' do |c| @@ -111,10 +111,10 @@ module Countries c.timezone 'Asia/Qatar', 1517, 60, 773, 15 end country 'BI', 'Burundi' do |c| - c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time (UTC+2)' + c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time' end country 'BJ', 'Benin' do |c| - c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time (UTC+1)' + c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time' end country 'BL', 'St Barthelemy' do |c| c.timezone 'America/Port_of_Spain', 213, 20, -3691, 60 @@ -128,25 +128,25 @@ module Countries country 'BO', 'Bolivia' do |c| c.timezone 'America/La_Paz', -33, 2, -1363, 20 end - country 'BQ', 'Caribbean Netherlands' do |c| + country 'BQ', 'Caribbean NL' do |c| c.timezone 'America/Curacao', 731, 60, -69, 1 end country 'BR', 'Brazil' do |c| c.timezone 'America/Noronha', -77, 20, -389, 12, 'Atlantic islands' - c.timezone 'America/Belem', -29, 20, -2909, 60, 'Amapá, E Pará' - c.timezone 'America/Fortaleza', -223, 60, -77, 2, 'NE Brazil (MA, PI, CE, RN, PB)' + c.timezone 'America/Belem', -29, 20, -2909, 60, 'Pará (east); Amapá' + c.timezone 'America/Fortaleza', -223, 60, -77, 2, 'Brazil (northeast: MA, PI, CE, RN, PB)' c.timezone 'America/Recife', -161, 20, -349, 10, 'Pernambuco' c.timezone 'America/Araguaina', -36, 5, -241, 5, 'Tocantins' c.timezone 'America/Maceio', -29, 3, -2143, 60, 'Alagoas, Sergipe' c.timezone 'America/Bahia', -779, 60, -2311, 60, 'Bahia' - c.timezone 'America/Sao_Paulo', -353, 15, -2797, 60, 'S & SE Brazil (GO, DF, MG, ES, RJ, SP, PR, SC, RS)' + c.timezone 'America/Sao_Paulo', -353, 15, -2797, 60, 'Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS)' c.timezone 'America/Campo_Grande', -409, 20, -3277, 60, 'Mato Grosso do Sul' c.timezone 'America/Cuiaba', -187, 12, -673, 12, 'Mato Grosso' - c.timezone 'America/Santarem', -73, 30, -823, 15, 'W Pará' + c.timezone 'America/Santarem', -73, 30, -823, 15, 'Pará (west)' c.timezone 'America/Porto_Velho', -263, 30, -639, 10, 'Rondônia' c.timezone 'America/Boa_Vista', 169, 60, -182, 3, 'Roraima' - c.timezone 'America/Manaus', -47, 15, -3601, 60, 'E Amazonas' - c.timezone 'America/Eirunepe', -20, 3, -1048, 15, 'W Amazonas' + c.timezone 'America/Manaus', -47, 15, -3601, 60, 'Amazonas (east)' + c.timezone 'America/Eirunepe', -20, 3, -1048, 15, 'Amazonas (west)' c.timezone 'America/Rio_Branco', -299, 30, -339, 5, 'Acre' end country 'BS', 'Bahamas' do |c| @@ -157,7 +157,7 @@ module Countries end country 'BV', 'Bouvet Island' country 'BW', 'Botswana' do |c| - c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time (UTC+2)' + c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time' end country 'BY', 'Belarus' do |c| c.timezone 'Europe/Minsk', 539, 10, 827, 30 @@ -166,62 +166,64 @@ module Countries c.timezone 'America/Belize', 35, 2, -441, 5 end country 'CA', 'Canada' do |c| - c.timezone 'America/St_Johns', 1427, 30, -3163, 60, 'Newfoundland Time, including SE Labrador' - c.timezone 'America/Halifax', 893, 20, -318, 5, 'Atlantic Time - Nova Scotia (most places), PEI' - c.timezone 'America/Glace_Bay', 231, 5, -1199, 20, 'Atlantic Time - Nova Scotia - places that did not observe DST 1966-1971' - c.timezone 'America/Moncton', 461, 10, -3887, 60, 'Atlantic Time - New Brunswick' - c.timezone 'America/Goose_Bay', 160, 3, -725, 12, 'Atlantic Time - Labrador - most locations' - c.timezone 'America/Blanc-Sablon', 617, 12, -3427, 60, 'Atlantic Standard Time - Quebec - Lower North Shore' - c.timezone 'America/Toronto', 873, 20, -4763, 60, 'Eastern Time - Ontario & Quebec - most locations' - c.timezone 'America/Nipigon', 2941, 60, -1324, 15, 'Eastern Time - Ontario & Quebec - places that did not observe DST 1967-1973' - c.timezone 'America/Thunder_Bay', 2903, 60, -357, 4, 'Eastern Time - Thunder Bay, Ontario' - c.timezone 'America/Iqaluit', 956, 15, -1027, 15, 'Eastern Time - east Nunavut - most locations' - c.timezone 'America/Pangnirtung', 992, 15, -986, 15, 'Eastern Time - Pangnirtung, Nunavut' - c.timezone 'America/Resolute', 33613, 450, -22759, 240, 'Central Time - Resolute, Nunavut' - c.timezone 'America/Atikokan', 175531, 3600, -54973, 600, 'Eastern Standard Time - Atikokan, Ontario and Southampton I, Nunavut' - c.timezone 'America/Rankin_Inlet', 3769, 60, -331499, 3600, 'Central Time - central Nunavut' - c.timezone 'America/Winnipeg', 2993, 60, -1943, 20, 'Central Time - Manitoba & west Ontario' - c.timezone 'America/Rainy_River', 2923, 60, -2837, 30, 'Central Time - Rainy River & Fort Frances, Ontario' - c.timezone 'America/Regina', 252, 5, -2093, 20, 'Central Standard Time - Saskatchewan - most locations' - c.timezone 'America/Swift_Current', 3017, 60, -647, 6, 'Central Standard Time - Saskatchewan - midwest' - c.timezone 'America/Edmonton', 1071, 20, -1702, 15, 'Mountain Time - Alberta, east British Columbia & west Saskatchewan' - c.timezone 'America/Cambridge_Bay', 24881, 360, -37819, 360, 'Mountain Time - west Nunavut' - c.timezone 'America/Yellowknife', 1249, 20, -2287, 20, 'Mountain Time - central Northwest Territories' - c.timezone 'America/Inuvik', 246059, 3600, -8023, 60, 'Mountain Time - west Northwest Territories' - c.timezone 'America/Creston', 491, 10, -6991, 60, 'Mountain Standard Time - Creston, British Columbia' - c.timezone 'America/Dawson_Creek', 1793, 30, -3607, 30, 'Mountain Standard Time - Dawson Creek & Fort Saint John, British Columbia' - c.timezone 'America/Vancouver', 739, 15, -7387, 60, 'Pacific Time - west British Columbia' - c.timezone 'America/Whitehorse', 3643, 60, -2701, 20, 'Pacific Time - south Yukon' - c.timezone 'America/Dawson', 961, 15, -1673, 12, 'Pacific Time - north Yukon' + c.timezone 'America/St_Johns', 1427, 30, -3163, 60, 'Newfoundland; Labrador (southeast)' + c.timezone 'America/Halifax', 893, 20, -318, 5, 'Atlantic - NS (most areas); PE' + c.timezone 'America/Glace_Bay', 231, 5, -1199, 20, 'Atlantic - NS (Cape Breton)' + c.timezone 'America/Moncton', 461, 10, -3887, 60, 'Atlantic - New Brunswick' + c.timezone 'America/Goose_Bay', 160, 3, -725, 12, 'Atlantic - Labrador (most areas)' + c.timezone 'America/Blanc-Sablon', 617, 12, -3427, 60, 'AST - QC (Lower North Shore)' + c.timezone 'America/Toronto', 873, 20, -4763, 60, 'Eastern - ON, QC (most areas)' + c.timezone 'America/Nipigon', 2941, 60, -1324, 15, 'Eastern - ON, QC (no DST 1967-73)' + c.timezone 'America/Thunder_Bay', 2903, 60, -357, 4, 'Eastern - ON (Thunder Bay)' + c.timezone 'America/Iqaluit', 956, 15, -1027, 15, 'Eastern - NU (most east areas)' + c.timezone 'America/Pangnirtung', 992, 15, -986, 15, 'Eastern - NU (Pangnirtung)' + c.timezone 'America/Atikokan', 175531, 3600, -54973, 600, 'EST - ON (Atikokan); NU (Coral H)' + c.timezone 'America/Winnipeg', 2993, 60, -1943, 20, 'Central - ON (west); Manitoba' + c.timezone 'America/Rainy_River', 2923, 60, -2837, 30, 'Central - ON (Rainy R, Ft Frances)' + c.timezone 'America/Resolute', 33613, 450, -22759, 240, 'Central - NU (Resolute)' + c.timezone 'America/Rankin_Inlet', 3769, 60, -331499, 3600, 'Central - NU (central)' + c.timezone 'America/Regina', 252, 5, -2093, 20, 'CST - SK (most areas)' + c.timezone 'America/Swift_Current', 3017, 60, -647, 6, 'CST - SK (midwest)' + c.timezone 'America/Edmonton', 1071, 20, -1702, 15, 'Mountain - AB; BC (E); SK (W)' + c.timezone 'America/Cambridge_Bay', 24881, 360, -37819, 360, 'Mountain - NU (west)' + c.timezone 'America/Yellowknife', 1249, 20, -2287, 20, 'Mountain - NT (central)' + c.timezone 'America/Inuvik', 246059, 3600, -8023, 60, 'Mountain - NT (west)' + c.timezone 'America/Creston', 491, 10, -6991, 60, 'MST - BC (Creston)' + c.timezone 'America/Dawson_Creek', 1793, 30, -3607, 30, 'MST - BC (Dawson Cr, Ft St John)' + c.timezone 'America/Fort_Nelson', 294, 5, -1227, 10, 'MST - BC (Ft Nelson)' + c.timezone 'America/Vancouver', 739, 15, -7387, 60, 'Pacific - BC (most areas)' + c.timezone 'America/Whitehorse', 3643, 60, -2701, 20, 'Pacific - Yukon (east)' + c.timezone 'America/Dawson', 961, 15, -1673, 12, 'Pacific - Yukon (west)' end country 'CC', 'Cocos (Keeling) Islands' do |c| c.timezone 'Indian/Cocos', -73, 6, 1163, 12 end country 'CD', 'Congo (Dem. Rep.)' do |c| - c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time (UTC+2)' - c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time (UTC+1)' + c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time' + c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time' end country 'CF', 'Central African Rep.' do |c| - c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time (UTC+1)' + c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time' end country 'CG', 'Congo (Rep.)' do |c| - c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time (UTC+1)' + c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time' end country 'CH', 'Switzerland' do |c| c.timezone 'Europe/Zurich', 2843, 60, 128, 15, 'Swiss time' end - country 'CI', 'Cote d\'Ivoire' do |c| + country 'CI', 'Côte d\'Ivoire' do |c| c.timezone 'Africa/Abidjan', 319, 60, -121, 30 end country 'CK', 'Cook Islands' do |c| c.timezone 'Pacific/Rarotonga', -637, 30, -4793, 30 end country 'CL', 'Chile' do |c| - c.timezone 'America/Santiago', -669, 20, -212, 3, 'most locations' + c.timezone 'America/Santiago', -669, 20, -212, 3, 'Chile (most areas)' + c.timezone 'America/Punta_Arenas', -1063, 20, -851, 12, 'Region of Magallanes' c.timezone 'Pacific/Easter', -543, 20, -3283, 30, 'Easter Island' end country 'CM', 'Cameroon' do |c| - c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time (UTC+1)' + c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time' end country 'CN', 'China' do |c| c.timezone 'Asia/Shanghai', 937, 30, 1822, 15, 'Beijing Time' @@ -239,20 +241,21 @@ module Countries country 'CV', 'Cape Verde' do |c| c.timezone 'Atlantic/Cape_Verde', 179, 12, -1411, 60 end - country 'CW', 'Curacao' do |c| + country 'CW', 'Curaçao' do |c| c.timezone 'America/Curacao', 731, 60, -69, 1 end country 'CX', 'Christmas Island' do |c| c.timezone 'Indian/Christmas', -125, 12, 6343, 60 end country 'CY', 'Cyprus' do |c| - c.timezone 'Asia/Nicosia', 211, 6, 1001, 30 + c.timezone 'Asia/Nicosia', 211, 6, 1001, 30, 'Cyprus (most areas)' + c.timezone 'Asia/Famagusta', 2107, 60, 679, 20, 'Northern Cyprus' end country 'CZ', 'Czech Republic' do |c| c.timezone 'Europe/Prague', 601, 12, 433, 30 end country 'DE', 'Germany' do |c| - c.timezone 'Europe/Berlin', 105, 2, 401, 30, 'Berlin time' + c.timezone 'Europe/Berlin', 105, 2, 401, 30, 'Germany (most areas)' c.timezone 'Europe/Zurich', 2843, 60, 128, 15, 'Swiss time' end country 'DJ', 'Djibouti' do |c| @@ -271,7 +274,7 @@ module Countries c.timezone 'Africa/Algiers', 2207, 60, 61, 20 end country 'EC', 'Ecuador' do |c| - c.timezone 'America/Guayaquil', -13, 6, -479, 6, 'mainland' + c.timezone 'America/Guayaquil', -13, 6, -479, 6, 'Ecuador (mainland)' c.timezone 'Pacific/Galapagos', -9, 10, -448, 5, 'Galápagos Islands' end country 'EE', 'Estonia' do |c| @@ -287,8 +290,8 @@ module Countries c.timezone 'Africa/Nairobi', -77, 60, 2209, 60 end country 'ES', 'Spain' do |c| - c.timezone 'Europe/Madrid', 202, 5, -221, 60, 'mainland' - c.timezone 'Africa/Ceuta', 2153, 60, -319, 60, 'Ceuta & Melilla' + c.timezone 'Europe/Madrid', 202, 5, -221, 60, 'Spain (mainland)' + c.timezone 'Africa/Ceuta', 2153, 60, -319, 60, 'Ceuta, Melilla' c.timezone 'Atlantic/Canary', 281, 10, -77, 5, 'Canary Islands' end country 'ET', 'Ethiopia' do |c| @@ -304,8 +307,8 @@ module Countries c.timezone 'Atlantic/Stanley', -517, 10, -1157, 20 end country 'FM', 'Micronesia' do |c| - c.timezone 'Pacific/Chuuk', 89, 12, 9107, 60, 'Chuuk (Truk) and Yap' - c.timezone 'Pacific/Pohnpei', 209, 30, 9493, 60, 'Pohnpei (Ponape)' + c.timezone 'Pacific/Chuuk', 89, 12, 9107, 60, 'Chuuk/Truk, Yap' + c.timezone 'Pacific/Pohnpei', 209, 30, 9493, 60, 'Pohnpei/Ponape' c.timezone 'Pacific/Kosrae', 319, 60, 9779, 60, 'Kosrae' end country 'FO', 'Faroe Islands' do |c| @@ -315,7 +318,7 @@ module Countries c.timezone 'Europe/Paris', 733, 15, 7, 3 end country 'GA', 'Gabon' do |c| - c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time (UTC+1)' + c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time' end country 'GB', 'Britain (UK)' do |c| c.timezone 'Europe/London', 6181, 120, -451, 3600 @@ -339,10 +342,10 @@ module Countries c.timezone 'Europe/Gibraltar', 542, 15, -107, 20 end country 'GL', 'Greenland' do |c| - c.timezone 'America/Godthab', 3851, 60, -776, 15, 'most locations' - c.timezone 'America/Danmarkshavn', 2303, 30, -56, 3, 'east coast, north of Scoresbysund' - c.timezone 'America/Scoresbysund', 4229, 60, -659, 30, 'Scoresbysund / Ittoqqortoormiit' - c.timezone 'America/Thule', 2297, 30, -4127, 60, 'Thule / Pituffik' + c.timezone 'America/Nuuk', 3851, 60, -776, 15, 'Greenland (most areas)' + c.timezone 'America/Danmarkshavn', 2303, 30, -56, 3, 'National Park (east coast)' + c.timezone 'America/Scoresbysund', 4229, 60, -659, 30, 'Scoresbysund/Ittoqqortoormiit' + c.timezone 'America/Thule', 2297, 30, -4127, 60, 'Thule/Pituffik' end country 'GM', 'Gambia' do |c| c.timezone 'Africa/Abidjan', 319, 60, -121, 30 @@ -354,7 +357,7 @@ module Countries c.timezone 'America/Port_of_Spain', 213, 20, -3691, 60 end country 'GQ', 'Equatorial Guinea' do |c| - c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time (UTC+1)' + c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time' end country 'GR', 'Greece' do |c| c.timezone 'Europe/Athens', 1139, 30, 1423, 60 @@ -391,10 +394,10 @@ module Countries c.timezone 'Europe/Budapest', 95, 2, 229, 12 end country 'ID', 'Indonesia' do |c| - c.timezone 'Asia/Jakarta', -37, 6, 534, 5, 'Java & Sumatra' - c.timezone 'Asia/Pontianak', -1, 30, 328, 3, 'west & central Borneo' - c.timezone 'Asia/Makassar', -307, 60, 597, 5, 'east & south Borneo, Sulawesi (Celebes), Bali, Nusa Tengarra, west Timor' - c.timezone 'Asia/Jayapura', -38, 15, 1407, 10, 'west New Guinea (Irian Jaya) & Malukus (Moluccas)' + c.timezone 'Asia/Jakarta', -37, 6, 534, 5, 'Java, Sumatra' + c.timezone 'Asia/Pontianak', -1, 30, 328, 3, 'Borneo (west, central)' + c.timezone 'Asia/Makassar', -307, 60, 597, 5, 'Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west)' + c.timezone 'Asia/Jayapura', -38, 15, 1407, 10, 'New Guinea (West Papua / Irian Jaya); Malukus/Moluccas' end country 'IE', 'Ireland' do |c| c.timezone 'Europe/Dublin', 160, 3, -25, 4 @@ -442,7 +445,7 @@ module Countries c.timezone 'Asia/Bishkek', 429, 10, 373, 5 end country 'KH', 'Cambodia' do |c| - c.timezone 'Asia/Bangkok', 55, 4, 6031, 60 + c.timezone 'Asia/Bangkok', 55, 4, 6031, 60, 'Indochina (most areas)' end country 'KI', 'Kiribati' do |c| c.timezone 'Pacific/Tarawa', 17, 12, 173, 1, 'Gilbert Islands' @@ -468,14 +471,16 @@ module Countries c.timezone 'America/Panama', 269, 30, -1193, 15 end country 'KZ', 'Kazakhstan' do |c| - c.timezone 'Asia/Almaty', 173, 4, 1539, 20, 'most locations' - c.timezone 'Asia/Qyzylorda', 224, 5, 982, 15, 'Qyzylorda (Kyzylorda, Kzyl-Orda)' - c.timezone 'Asia/Aqtobe', 3017, 60, 343, 6, 'Aqtobe (Aktobe)' - c.timezone 'Asia/Aqtau', 2671, 60, 754, 15, 'Atyrau (Atirau, Gur\'yev), Mangghystau (Mankistau)' + c.timezone 'Asia/Almaty', 173, 4, 1539, 20, 'Kazakhstan (most areas)' + c.timezone 'Asia/Qyzylorda', 224, 5, 982, 15, 'Qyzylorda/Kyzylorda/Kzyl-Orda' + c.timezone 'Asia/Qostanay', 266, 5, 3817, 60, 'Qostanay/Kostanay/Kustanay' + c.timezone 'Asia/Aqtobe', 3017, 60, 343, 6, 'Aqtöbe/Aktobe' + c.timezone 'Asia/Aqtau', 2671, 60, 754, 15, 'Mangghystaū/Mankistau' + c.timezone 'Asia/Atyrau', 2827, 60, 779, 15, 'Atyraū/Atirau/Gur\'yev' c.timezone 'Asia/Oral', 3073, 60, 1027, 20, 'West Kazakhstan' end country 'LA', 'Laos' do |c| - c.timezone 'Asia/Bangkok', 55, 4, 6031, 60 + c.timezone 'Asia/Bangkok', 55, 4, 6031, 60, 'Indochina (most areas)' end country 'LB', 'Lebanon' do |c| c.timezone 'Asia/Beirut', 2033, 60, 71, 2 @@ -519,32 +524,32 @@ module Countries country 'ME', 'Montenegro' do |c| c.timezone 'Europe/Belgrade', 269, 6, 41, 2 end - country 'MF', 'St Martin (French part)' do |c| + country 'MF', 'St Martin (French)' do |c| c.timezone 'America/Port_of_Spain', 213, 20, -3691, 60 end country 'MG', 'Madagascar' do |c| c.timezone 'Africa/Nairobi', -77, 60, 2209, 60 end country 'MH', 'Marshall Islands' do |c| - c.timezone 'Pacific/Majuro', 143, 20, 856, 5, 'most locations' + c.timezone 'Pacific/Majuro', 143, 20, 856, 5, 'Marshall Islands (most areas)' c.timezone 'Pacific/Kwajalein', 109, 12, 502, 3, 'Kwajalein' end - country 'MK', 'Macedonia' do |c| + country 'MK', 'North Macedonia' do |c| c.timezone 'Europe/Belgrade', 269, 6, 41, 2 end country 'ML', 'Mali' do |c| c.timezone 'Africa/Abidjan', 319, 60, -121, 30 end country 'MM', 'Myanmar (Burma)' do |c| - c.timezone 'Asia/Rangoon', 1007, 60, 577, 6 + c.timezone 'Asia/Yangon', 1007, 60, 577, 6 end country 'MN', 'Mongolia' do |c| - c.timezone 'Asia/Ulaanbaatar', 575, 12, 6413, 60, 'most locations' + c.timezone 'Asia/Ulaanbaatar', 575, 12, 6413, 60, 'Mongolia (most areas)' c.timezone 'Asia/Hovd', 2881, 60, 1833, 20, 'Bayan-Ölgii, Govi-Altai, Hovd, Uvs, Zavkhan' c.timezone 'Asia/Choibalsan', 721, 15, 229, 2, 'Dornod, Sükhbaatar' end country 'MO', 'Macau' do |c| - c.timezone 'Asia/Macau', 667, 30, 1363, 12 + c.timezone 'Asia/Macau', 7991, 360, 2725, 24 end country 'MP', 'Northern Mariana Islands' do |c| c.timezone 'Pacific/Guam', 202, 15, 579, 4 @@ -568,28 +573,27 @@ module Countries c.timezone 'Indian/Maldives', 25, 6, 147, 2 end country 'MW', 'Malawi' do |c| - c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time (UTC+2)' + c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time' end country 'MX', 'Mexico' do |c| - c.timezone 'America/Mexico_City', 97, 5, -1983, 20, 'Central Time - most locations' - c.timezone 'America/Cancun', 253, 12, -2603, 30, 'Central Time - Quintana Roo' + c.timezone 'America/Mexico_City', 97, 5, -1983, 20, 'Central Time' + c.timezone 'America/Cancun', 253, 12, -2603, 30, 'Eastern Standard Time - Quintana Roo' c.timezone 'America/Merida', 629, 30, -5377, 60, 'Central Time - Campeche, Yucatán' - c.timezone 'America/Monterrey', 77, 3, -6019, 60, 'Mexican Central Time - Coahuila, Durango, Nuevo León, Tamaulipas away from US border' - c.timezone 'America/Matamoros', 155, 6, -195, 2, 'US Central Time - Coahuila, Durango, Nuevo León, Tamaulipas near US border' - c.timezone 'America/Mazatlan', 1393, 60, -1277, 12, 'Mountain Time - S Baja, Nayarit, Sinaloa' - c.timezone 'America/Chihuahua', 859, 30, -1273, 12, 'Mexican Mountain Time - Chihuahua away from US border' - c.timezone 'America/Ojinaga', 887, 30, -1253, 12, 'US Mountain Time - Chihuahua near US border' + c.timezone 'America/Monterrey', 77, 3, -6019, 60, 'Central Time - Durango; Coahuila, Nuevo León, Tamaulipas (most areas)' + c.timezone 'America/Matamoros', 155, 6, -195, 2, 'Central Time US - Coahuila, Nuevo León, Tamaulipas (US border)' + c.timezone 'America/Mazatlan', 1393, 60, -1277, 12, 'Mountain Time - Baja California Sur, Nayarit, Sinaloa' + c.timezone 'America/Chihuahua', 859, 30, -1273, 12, 'Mountain Time - Chihuahua (most areas)' + c.timezone 'America/Ojinaga', 887, 30, -1253, 12, 'Mountain Time US - Chihuahua (US border)' c.timezone 'America/Hermosillo', 436, 15, -3329, 30, 'Mountain Standard Time - Sonora' - c.timezone 'America/Tijuana', 488, 15, -7021, 60, 'US Pacific Time - Baja California near US border' - c.timezone 'America/Santa_Isabel', 303, 10, -1723, 15, 'Mexican Pacific Time - Baja California away from US border' - c.timezone 'America/Bahia_Banderas', 104, 5, -421, 4, 'Mexican Central Time - Bahía de Banderas' + c.timezone 'America/Tijuana', 488, 15, -7021, 60, 'Pacific Time US - Baja California' + c.timezone 'America/Bahia_Banderas', 104, 5, -421, 4, 'Central Time - Bahía de Banderas' end country 'MY', 'Malaysia' do |c| - c.timezone 'Asia/Kuala_Lumpur', 19, 6, 1017, 10, 'peninsular Malaysia' - c.timezone 'Asia/Kuching', 31, 20, 331, 3, 'Sabah & Sarawak' + c.timezone 'Asia/Kuala_Lumpur', 19, 6, 1017, 10, 'Malaysia (peninsula)' + c.timezone 'Asia/Kuching', 31, 20, 331, 3, 'Sabah, Sarawak' end country 'MZ', 'Mozambique' do |c| - c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time (UTC+2)' + c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time' end country 'NA', 'Namibia' do |c| c.timezone 'Africa/Windhoek', -677, 30, 171, 10 @@ -598,13 +602,13 @@ module Countries c.timezone 'Pacific/Noumea', -334, 15, 3329, 20 end country 'NE', 'Niger' do |c| - c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time (UTC+1)' + c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time' end country 'NF', 'Norfolk Island' do |c| c.timezone 'Pacific/Norfolk', -581, 20, 5039, 30 end country 'NG', 'Nigeria' do |c| - c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time (UTC+1)' + c.timezone 'Africa/Lagos', 129, 20, 17, 5, 'West Africa Time' end country 'NI', 'Nicaragua' do |c| c.timezone 'America/Managua', 243, 20, -5177, 60 @@ -643,7 +647,8 @@ module Countries c.timezone 'Pacific/Gambier', -347, 15, -2699, 20, 'Gambier Islands' end country 'PG', 'Papua New Guinea' do |c| - c.timezone 'Pacific/Port_Moresby', -19, 2, 883, 6 + c.timezone 'Pacific/Port_Moresby', -19, 2, 883, 6, 'Papua New Guinea (most areas)' + c.timezone 'Pacific/Bougainville', -373, 60, 4667, 30, 'Bougainville' end country 'PH', 'Philippines' do |c| c.timezone 'Asia/Manila', 175, 12, 121, 1 @@ -668,7 +673,7 @@ module Countries c.timezone 'Asia/Hebron', 473, 15, 7019, 200, 'West Bank' end country 'PT', 'Portugal' do |c| - c.timezone 'Europe/Lisbon', 2323, 60, -137, 15, 'mainland' + c.timezone 'Europe/Lisbon', 2323, 60, -137, 15, 'Portugal (mainland)' c.timezone 'Atlantic/Madeira', 979, 30, -169, 10, 'Madeira Islands' c.timezone 'Atlantic/Azores', 566, 15, -77, 3, 'Azores' end @@ -681,8 +686,8 @@ module Countries country 'QA', 'Qatar' do |c| c.timezone 'Asia/Qatar', 1517, 60, 773, 15 end - country 'RE', 'Reunion' do |c| - c.timezone 'Indian/Reunion', -313, 15, 832, 15, 'Réunion, Crozet Is, Scattered Is' + country 'RE', 'Réunion' do |c| + c.timezone 'Indian/Reunion', -313, 15, 832, 15, 'Réunion, Crozet, Scattered Islands' end country 'RO', 'Romania' do |c| c.timezone 'Europe/Bucharest', 1333, 30, 261, 10 @@ -691,30 +696,36 @@ module Countries c.timezone 'Europe/Belgrade', 269, 6, 41, 2 end country 'RU', 'Russia' do |c| - c.timezone 'Europe/Kaliningrad', 3283, 60, 41, 2, 'Moscow-01 - Kaliningrad' - c.timezone 'Europe/Moscow', 66907, 1200, 8464, 225, 'Moscow+00 - west Russia' - c.timezone 'Europe/Simferopol', 899, 20, 341, 10, 'Moscow+00 - Crimea' - c.timezone 'Europe/Volgograd', 731, 15, 533, 12, 'Moscow+00 - Caspian Sea' - c.timezone 'Europe/Samara', 266, 5, 1003, 20, 'Moscow+00 (Moscow+01 after 2014-10-26) - Samara, Udmurtia' - c.timezone 'Asia/Yekaterinburg', 1137, 20, 303, 5, 'Moscow+02 - Urals' - c.timezone 'Asia/Omsk', 55, 1, 367, 5, 'Moscow+03 - west Siberia' - c.timezone 'Asia/Novosibirsk', 1651, 30, 995, 12, 'Moscow+03 - Novosibirsk' - c.timezone 'Asia/Novokuznetsk', 215, 4, 5227, 60, 'Moscow+03 (Moscow+04 after 2014-10-26) - Kemerovo' - c.timezone 'Asia/Krasnoyarsk', 3361, 60, 557, 6, 'Moscow+04 - Yenisei River' - c.timezone 'Asia/Irkutsk', 784, 15, 313, 3, 'Moscow+05 - Lake Baikal' - c.timezone 'Asia/Chita', 1041, 20, 1702, 15, 'Moscow+06 (Moscow+05 after 2014-10-26) - Zabaykalsky' - c.timezone 'Asia/Yakutsk', 62, 1, 389, 3, 'Moscow+06 - Lena River' - c.timezone 'Asia/Khandyga', 225563, 3600, 243997, 1800, 'Moscow+06 - Tomponsky, Ust-Maysky' - c.timezone 'Asia/Vladivostok', 259, 6, 1979, 15, 'Moscow+07 - Amur River' - c.timezone 'Asia/Sakhalin', 1409, 30, 1427, 10, 'Moscow+07 - Sakhalin Island' - c.timezone 'Asia/Ust-Nera', 232417, 3600, 10742, 75, 'Moscow+07 - Oymyakonsky' - c.timezone 'Asia/Magadan', 1787, 30, 754, 5, 'Moscow+08 (Moscow+07 after 2014-10-26) - Magadan' - c.timezone 'Asia/Srednekolymsk', 1012, 15, 9223, 60, 'Moscow+08 - E Sakha, N Kuril Is' - c.timezone 'Asia/Kamchatka', 3181, 60, 3173, 20, 'Moscow+08 (Moscow+09 after 2014-10-26) - Kamchatka' - c.timezone 'Asia/Anadyr', 259, 4, 10649, 60, 'Moscow+08 (Moscow+09 after 2014-10-26) - Bering Sea' + c.timezone 'Europe/Kaliningrad', 3283, 60, 41, 2, 'MSK-01 - Kaliningrad' + c.timezone 'Europe/Moscow', 66907, 1200, 8464, 225, 'MSK+00 - Moscow area' + c.timezone 'Europe/Simferopol', 899, 20, 341, 10, 'Crimea' + c.timezone 'Europe/Kirov', 293, 5, 993, 20, 'MSK+00 - Kirov' + c.timezone 'Europe/Astrakhan', 927, 20, 961, 20, 'MSK+01 - Astrakhan' + c.timezone 'Europe/Volgograd', 731, 15, 533, 12, 'MSK+01 - Volgograd' + c.timezone 'Europe/Saratov', 1547, 30, 1381, 30, 'MSK+01 - Saratov' + c.timezone 'Europe/Ulyanovsk', 163, 3, 242, 5, 'MSK+01 - Ulyanovsk' + c.timezone 'Europe/Samara', 266, 5, 1003, 20, 'MSK+01 - Samara, Udmurtia' + c.timezone 'Asia/Yekaterinburg', 1137, 20, 303, 5, 'MSK+02 - Urals' + c.timezone 'Asia/Omsk', 55, 1, 367, 5, 'MSK+03 - Omsk' + c.timezone 'Asia/Novosibirsk', 1651, 30, 995, 12, 'MSK+04 - Novosibirsk' + c.timezone 'Asia/Barnaul', 1601, 30, 335, 4, 'MSK+04 - Altai' + c.timezone 'Asia/Tomsk', 113, 2, 2549, 30, 'MSK+04 - Tomsk' + c.timezone 'Asia/Novokuznetsk', 215, 4, 5227, 60, 'MSK+04 - Kemerovo' + c.timezone 'Asia/Krasnoyarsk', 3361, 60, 557, 6, 'MSK+04 - Krasnoyarsk area' + c.timezone 'Asia/Irkutsk', 784, 15, 313, 3, 'MSK+05 - Irkutsk, Buryatia' + c.timezone 'Asia/Chita', 1041, 20, 1702, 15, 'MSK+06 - Zabaykalsky' + c.timezone 'Asia/Yakutsk', 62, 1, 389, 3, 'MSK+06 - Lena River' + c.timezone 'Asia/Khandyga', 225563, 3600, 243997, 1800, 'MSK+06 - Tomponsky, Ust-Maysky' + c.timezone 'Asia/Vladivostok', 259, 6, 1979, 15, 'MSK+07 - Amur River' + c.timezone 'Asia/Ust-Nera', 232417, 3600, 10742, 75, 'MSK+07 - Oymyakonsky' + c.timezone 'Asia/Magadan', 1787, 30, 754, 5, 'MSK+08 - Magadan' + c.timezone 'Asia/Sakhalin', 1409, 30, 1427, 10, 'MSK+08 - Sakhalin Island' + c.timezone 'Asia/Srednekolymsk', 1012, 15, 9223, 60, 'MSK+08 - Sakha (E); North Kuril Is' + c.timezone 'Asia/Kamchatka', 3181, 60, 3173, 20, 'MSK+09 - Kamchatka' + c.timezone 'Asia/Anadyr', 259, 4, 10649, 60, 'MSK+09 - Bering Sea' end country 'RW', 'Rwanda' do |c| - c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time (UTC+2)' + c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time' end country 'SA', 'Saudi Arabia' do |c| c.timezone 'Asia/Riyadh', 739, 30, 2803, 60 @@ -762,21 +773,21 @@ module Countries c.timezone 'America/Paramaribo', 35, 6, -331, 6 end country 'SS', 'South Sudan' do |c| - c.timezone 'Africa/Khartoum', 78, 5, 488, 15 + c.timezone 'Africa/Juba', 97, 20, 1897, 60 end country 'ST', 'Sao Tome & Principe' do |c| - c.timezone 'Africa/Abidjan', 319, 60, -121, 30 + c.timezone 'Africa/Sao_Tome', 1, 3, 101, 15 end country 'SV', 'El Salvador' do |c| c.timezone 'America/El_Salvador', 137, 10, -446, 5 end - country 'SX', 'St Maarten (Dutch part)' do |c| + country 'SX', 'St Maarten (Dutch)' do |c| c.timezone 'America/Curacao', 731, 60, -69, 1 end country 'SY', 'Syria' do |c| c.timezone 'Asia/Damascus', 67, 2, 363, 10 end - country 'SZ', 'Swaziland' do |c| + country 'SZ', 'Eswatini (Swaziland)' do |c| c.timezone 'Africa/Johannesburg', -105, 4, 28, 1 end country 'TC', 'Turks & Caicos Is' do |c| @@ -786,14 +797,14 @@ module Countries c.timezone 'Africa/Ndjamena', 727, 60, 301, 20 end country 'TF', 'French Southern & Antarctic Lands' do |c| - c.timezone 'Indian/Kerguelen', -17767, 360, 28087, 400, 'Kerguelen, St Paul I, Amsterdam I' - c.timezone 'Indian/Reunion', -313, 15, 832, 15, 'Réunion, Crozet Is, Scattered Is' + c.timezone 'Indian/Kerguelen', -17767, 360, 28087, 400, 'Kerguelen, St Paul Island, Amsterdam Island' + c.timezone 'Indian/Reunion', -313, 15, 832, 15, 'Réunion, Crozet, Scattered Islands' end country 'TG', 'Togo' do |c| c.timezone 'Africa/Abidjan', 319, 60, -121, 30 end country 'TH', 'Thailand' do |c| - c.timezone 'Asia/Bangkok', 55, 4, 6031, 60 + c.timezone 'Asia/Bangkok', 55, 4, 6031, 60, 'Indochina (most areas)' end country 'TJ', 'Tajikistan' do |c| c.timezone 'Asia/Dushanbe', 463, 12, 344, 5 @@ -829,9 +840,10 @@ module Countries c.timezone 'Africa/Nairobi', -77, 60, 2209, 60 end country 'UA', 'Ukraine' do |c| - c.timezone 'Europe/Kiev', 1513, 30, 1831, 60, 'most locations' - c.timezone 'Europe/Uzhgorod', 2917, 60, 223, 10, 'Ruthenia' - c.timezone 'Europe/Zaporozhye', 287, 6, 211, 6, 'Zaporozh\'ye, E Lugansk / Zaporizhia, E Luhansk' + c.timezone 'Europe/Kiev', 1513, 30, 1831, 60, 'Ukraine (most areas)' + c.timezone 'Europe/Uzhgorod', 2917, 60, 223, 10, 'Transcarpathia' + c.timezone 'Europe/Zaporozhye', 287, 6, 211, 6, 'Zaporozhye and east Lugansk' + c.timezone 'Europe/Simferopol', 899, 20, 341, 10, 'Crimea' end country 'UG', 'Uganda' do |c| c.timezone 'Africa/Nairobi', -77, 60, 2209, 60 @@ -839,45 +851,45 @@ module Countries country 'UM', 'US minor outlying islands' do |c| c.timezone 'Pacific/Wake', 1157, 60, 9997, 60, 'Wake Island' c.timezone 'Pacific/Pago_Pago', -214, 15, -1707, 10, 'Samoa, Midway' - c.timezone 'Pacific/Honolulu', 15341, 720, -18943, 120, 'Hawaii time' + c.timezone 'Pacific/Honolulu', 15341, 720, -18943, 120, 'Hawaii' end country 'US', 'United States' do |c| - c.timezone 'America/New_York', 48857, 1200, -266423, 3600, 'Eastern Time' - c.timezone 'America/Detroit', 152393, 3600, -19931, 240, 'Eastern Time - Michigan - most locations' - c.timezone 'America/Kentucky/Louisville', 9181, 240, -154367, 1800, 'Eastern Time - Kentucky - Louisville area' - c.timezone 'America/Kentucky/Monticello', 132587, 3600, -101819, 1200, 'Eastern Time - Kentucky - Wayne County' - c.timezone 'America/Indiana/Indianapolis', 23861, 600, -310169, 3600, 'Eastern Time - Indiana - most locations' - c.timezone 'America/Indiana/Vincennes', 69619, 1800, -315103, 3600, 'Eastern Time - Indiana - Daviess, Dubois, Knox & Martin Counties' - c.timezone 'America/Indiana/Winamac', 29557, 720, -311771, 3600, 'Eastern Time - Indiana - Pulaski County' - c.timezone 'America/Indiana/Marengo', 17269, 450, -310841, 3600, 'Eastern Time - Indiana - Crawford County' - c.timezone 'America/Indiana/Petersburg', 138571, 3600, -314203, 3600, 'Eastern Time - Indiana - Pike County' - c.timezone 'America/Indiana/Vevay', 34873, 900, -153121, 1800, 'Eastern Time - Indiana - Switzerland County' - c.timezone 'America/Chicago', 837, 20, -1753, 20, 'Central Time' - c.timezone 'America/Indiana/Tell_City', 136631, 3600, -312341, 3600, 'Central Time - Indiana - Perry County' - c.timezone 'America/Indiana/Knox', 9911, 240, -693, 8, 'Central Time - Indiana - Starke County' - c.timezone 'America/Menominee', 40597, 900, -105137, 1200, 'Central Time - Michigan - Dickinson, Gogebic, Iron & Menominee Counties' - c.timezone 'America/North_Dakota/Center', 169619, 3600, -121559, 1200, 'Central Time - North Dakota - Oliver County' - c.timezone 'America/North_Dakota/New_Salem', 9369, 200, -121693, 1200, 'Central Time - North Dakota - Morton County (except Mandan area)' - c.timezone 'America/North_Dakota/Beulah', 56717, 1200, -916, 9, 'Central Time - North Dakota - Mercer County' - c.timezone 'America/Denver', 47687, 1200, -125981, 1200, 'Mountain Time' - c.timezone 'America/Boise', 157009, 3600, -46481, 400, 'Mountain Time - south Idaho & east Oregon' - c.timezone 'America/Phoenix', 20069, 600, -16811, 150, 'Mountain Standard Time - Arizona (except Navajo)' - c.timezone 'America/Los_Angeles', 30647, 900, -212837, 1800, 'Pacific Time' - c.timezone 'America/Metlakatla', 198457, 3600, -18947, 144, 'Pacific Standard Time - Annette Island, Alaska' - c.timezone 'America/Anchorage', 44077, 720, -539641, 3600, 'Alaska Time' - c.timezone 'America/Juneau', 209887, 3600, -483911, 3600, 'Alaska Time - Alaska panhandle' - c.timezone 'America/Sitka', 41167, 720, -487087, 3600, 'Alaska Time - southeast Alaska panhandle' - c.timezone 'America/Yakutat', 214369, 3600, -251509, 1800, 'Alaska Time - Alaska panhandle neck' - c.timezone 'America/Nome', 58051, 900, -595463, 3600, 'Alaska Time - west Alaska' + c.timezone 'America/New_York', 48857, 1200, -266423, 3600, 'Eastern (most areas)' + c.timezone 'America/Detroit', 152393, 3600, -19931, 240, 'Eastern - MI (most areas)' + c.timezone 'America/Kentucky/Louisville', 9181, 240, -154367, 1800, 'Eastern - KY (Louisville area)' + c.timezone 'America/Kentucky/Monticello', 132587, 3600, -101819, 1200, 'Eastern - KY (Wayne)' + c.timezone 'America/Indiana/Indianapolis', 23861, 600, -310169, 3600, 'Eastern - IN (most areas)' + c.timezone 'America/Indiana/Vincennes', 69619, 1800, -315103, 3600, 'Eastern - IN (Da, Du, K, Mn)' + c.timezone 'America/Indiana/Winamac', 29557, 720, -311771, 3600, 'Eastern - IN (Pulaski)' + c.timezone 'America/Indiana/Marengo', 17269, 450, -310841, 3600, 'Eastern - IN (Crawford)' + c.timezone 'America/Indiana/Petersburg', 138571, 3600, -314203, 3600, 'Eastern - IN (Pike)' + c.timezone 'America/Indiana/Vevay', 34873, 900, -153121, 1800, 'Eastern - IN (Switzerland)' + c.timezone 'America/Chicago', 837, 20, -1753, 20, 'Central (most areas)' + c.timezone 'America/Indiana/Tell_City', 136631, 3600, -312341, 3600, 'Central - IN (Perry)' + c.timezone 'America/Indiana/Knox', 9911, 240, -693, 8, 'Central - IN (Starke)' + c.timezone 'America/Menominee', 40597, 900, -105137, 1200, 'Central - MI (Wisconsin border)' + c.timezone 'America/North_Dakota/Center', 169619, 3600, -121559, 1200, 'Central - ND (Oliver)' + c.timezone 'America/North_Dakota/New_Salem', 9369, 200, -121693, 1200, 'Central - ND (Morton rural)' + c.timezone 'America/North_Dakota/Beulah', 56717, 1200, -916, 9, 'Central - ND (Mercer)' + c.timezone 'America/Denver', 47687, 1200, -125981, 1200, 'Mountain (most areas)' + c.timezone 'America/Boise', 157009, 3600, -46481, 400, 'Mountain - ID (south); OR (east)' + c.timezone 'America/Phoenix', 20069, 600, -16811, 150, 'MST - Arizona (except Navajo)' + c.timezone 'America/Los_Angeles', 30647, 900, -212837, 1800, 'Pacific' + c.timezone 'America/Anchorage', 44077, 720, -539641, 3600, 'Alaska (most areas)' + c.timezone 'America/Juneau', 209887, 3600, -483911, 3600, 'Alaska - Juneau area' + c.timezone 'America/Sitka', 41167, 720, -487087, 3600, 'Alaska - Sitka area' + c.timezone 'America/Metlakatla', 198457, 3600, -18947, 144, 'Alaska - Annette Island' + c.timezone 'America/Yakutat', 214369, 3600, -251509, 1800, 'Alaska - Yakutat' + c.timezone 'America/Nome', 58051, 900, -595463, 3600, 'Alaska (west)' c.timezone 'America/Adak', 1297, 25, -635969, 3600, 'Aleutian Islands' - c.timezone 'Pacific/Honolulu', 15341, 720, -18943, 120, 'Hawaii time' + c.timezone 'Pacific/Honolulu', 15341, 720, -18943, 120, 'Hawaii' end country 'UY', 'Uruguay' do |c| - c.timezone 'America/Montevideo', -2093, 60, -3371, 60 + c.timezone 'America/Montevideo', -41891, 1200, -4497, 80 end country 'UZ', 'Uzbekistan' do |c| - c.timezone 'Asia/Samarkand', 119, 3, 334, 5, 'west Uzbekistan' - c.timezone 'Asia/Tashkent', 124, 3, 693, 10, 'east Uzbekistan' + c.timezone 'Asia/Samarkand', 119, 3, 334, 5, 'Uzbekistan (west)' + c.timezone 'Asia/Tashkent', 124, 3, 693, 10, 'Uzbekistan (east)' end country 'VA', 'Vatican City' do |c| c.timezone 'Europe/Rome', 419, 10, 749, 60 @@ -895,7 +907,8 @@ module Countries c.timezone 'America/Port_of_Spain', 213, 20, -3691, 60 end country 'VN', 'Vietnam' do |c| - c.timezone 'Asia/Bangkok', 55, 4, 6031, 60 + c.timezone 'Asia/Ho_Chi_Minh', 43, 4, 320, 3, 'Vietnam (south)' + c.timezone 'Asia/Bangkok', 55, 4, 6031, 60, 'Indochina (most areas)' end country 'VU', 'Vanuatu' do |c| c.timezone 'Pacific/Efate', -53, 3, 2021, 12 @@ -916,10 +929,10 @@ module Countries c.timezone 'Africa/Johannesburg', -105, 4, 28, 1 end country 'ZM', 'Zambia' do |c| - c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time (UTC+2)' + c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time' end country 'ZW', 'Zimbabwe' do |c| - c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time (UTC+2)' + c.timezone 'Africa/Maputo', -779, 30, 391, 12, 'Central Africa Time' end end end diff --git a/test/tzinfo-data/tzinfo/data/indexes/timezones.rb b/test/tzinfo-data/tzinfo/data/indexes/timezones.rb index 1928d796..a42a2573 100644 --- a/test/tzinfo-data/tzinfo/data/indexes/timezones.rb +++ b/test/tzinfo-data/tzinfo/data/indexes/timezones.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 # This file contains data derived from the IANA Time Zone Database -# (http://www.iana.org/time-zones). +# (https://www.iana.org/time-zones). module TZInfo module Data @@ -11,54 +11,54 @@ module Timezones timezone 'Africa/Abidjan' timezone 'Africa/Accra' - timezone 'Africa/Addis_Ababa' + linked_timezone 'Africa/Addis_Ababa' timezone 'Africa/Algiers' - timezone 'Africa/Asmara' + linked_timezone 'Africa/Asmara' linked_timezone 'Africa/Asmera' linked_timezone 'Africa/Bamako' - timezone 'Africa/Bangui' + linked_timezone 'Africa/Bangui' linked_timezone 'Africa/Banjul' timezone 'Africa/Bissau' - timezone 'Africa/Blantyre' - timezone 'Africa/Brazzaville' - timezone 'Africa/Bujumbura' + linked_timezone 'Africa/Blantyre' + linked_timezone 'Africa/Brazzaville' + linked_timezone 'Africa/Bujumbura' timezone 'Africa/Cairo' timezone 'Africa/Casablanca' timezone 'Africa/Ceuta' linked_timezone 'Africa/Conakry' linked_timezone 'Africa/Dakar' - timezone 'Africa/Dar_es_Salaam' - timezone 'Africa/Djibouti' - timezone 'Africa/Douala' + linked_timezone 'Africa/Dar_es_Salaam' + linked_timezone 'Africa/Djibouti' + linked_timezone 'Africa/Douala' timezone 'Africa/El_Aaiun' linked_timezone 'Africa/Freetown' - timezone 'Africa/Gaborone' - timezone 'Africa/Harare' + linked_timezone 'Africa/Gaborone' + linked_timezone 'Africa/Harare' timezone 'Africa/Johannesburg' - linked_timezone 'Africa/Juba' - timezone 'Africa/Kampala' + timezone 'Africa/Juba' + linked_timezone 'Africa/Kampala' timezone 'Africa/Khartoum' - timezone 'Africa/Kigali' - timezone 'Africa/Kinshasa' + linked_timezone 'Africa/Kigali' + linked_timezone 'Africa/Kinshasa' timezone 'Africa/Lagos' - timezone 'Africa/Libreville' + linked_timezone 'Africa/Libreville' linked_timezone 'Africa/Lome' - timezone 'Africa/Luanda' - timezone 'Africa/Lubumbashi' - timezone 'Africa/Lusaka' - timezone 'Africa/Malabo' + linked_timezone 'Africa/Luanda' + linked_timezone 'Africa/Lubumbashi' + linked_timezone 'Africa/Lusaka' + linked_timezone 'Africa/Malabo' timezone 'Africa/Maputo' - timezone 'Africa/Maseru' - timezone 'Africa/Mbabane' - timezone 'Africa/Mogadishu' + linked_timezone 'Africa/Maseru' + linked_timezone 'Africa/Mbabane' + linked_timezone 'Africa/Mogadishu' timezone 'Africa/Monrovia' timezone 'Africa/Nairobi' timezone 'Africa/Ndjamena' - timezone 'Africa/Niamey' + linked_timezone 'Africa/Niamey' linked_timezone 'Africa/Nouakchott' linked_timezone 'Africa/Ouagadougou' - timezone 'Africa/Porto-Novo' - linked_timezone 'Africa/Sao_Tome' + linked_timezone 'Africa/Porto-Novo' + timezone 'Africa/Sao_Tome' linked_timezone 'Africa/Timbuktu' timezone 'Africa/Tripoli' timezone 'Africa/Tunis' @@ -66,7 +66,7 @@ module Timezones timezone 'America/Adak' timezone 'America/Anchorage' linked_timezone 'America/Anguilla' - timezone 'America/Antigua' + linked_timezone 'America/Antigua' timezone 'America/Araguaina' timezone 'America/Argentina/Buenos_Aires' timezone 'America/Argentina/Catamarca' @@ -101,7 +101,7 @@ module Timezones timezone 'America/Caracas' linked_timezone 'America/Catamarca' timezone 'America/Cayenne' - timezone 'America/Cayman' + linked_timezone 'America/Cayman' timezone 'America/Chicago' timezone 'America/Chihuahua' linked_timezone 'America/Coral_Harbour' @@ -120,10 +120,11 @@ module Timezones timezone 'America/Eirunepe' timezone 'America/El_Salvador' linked_timezone 'America/Ensenada' + timezone 'America/Fort_Nelson' linked_timezone 'America/Fort_Wayne' timezone 'America/Fortaleza' timezone 'America/Glace_Bay' - timezone 'America/Godthab' + linked_timezone 'America/Godthab' timezone 'America/Goose_Bay' timezone 'America/Grand_Turk' linked_timezone 'America/Grenada' @@ -173,7 +174,7 @@ module Timezones timezone 'America/Moncton' timezone 'America/Monterrey' timezone 'America/Montevideo' - timezone 'America/Montreal' + linked_timezone 'America/Montreal' linked_timezone 'America/Montserrat' timezone 'America/Nassau' timezone 'America/New_York' @@ -183,6 +184,7 @@ module Timezones timezone 'America/North_Dakota/Beulah' timezone 'America/North_Dakota/Center' timezone 'America/North_Dakota/New_Salem' + timezone 'America/Nuuk' timezone 'America/Ojinaga' timezone 'America/Panama' timezone 'America/Pangnirtung' @@ -193,6 +195,7 @@ module Timezones linked_timezone 'America/Porto_Acre' timezone 'America/Porto_Velho' timezone 'America/Puerto_Rico' + timezone 'America/Punta_Arenas' timezone 'America/Rainy_River' timezone 'America/Rankin_Inlet' timezone 'America/Recife' @@ -200,7 +203,7 @@ module Timezones timezone 'America/Resolute' timezone 'America/Rio_Branco' linked_timezone 'America/Rosario' - timezone 'America/Santa_Isabel' + linked_timezone 'America/Santa_Isabel' timezone 'America/Santarem' timezone 'America/Santiago' timezone 'America/Santo_Domingo' @@ -240,7 +243,7 @@ module Timezones timezone 'Antarctica/Troll' timezone 'Antarctica/Vostok' linked_timezone 'Arctic/Longyearbyen' - timezone 'Asia/Aden' + linked_timezone 'Asia/Aden' timezone 'Asia/Almaty' timezone 'Asia/Amman' timezone 'Asia/Anadyr' @@ -248,10 +251,12 @@ module Timezones timezone 'Asia/Aqtobe' timezone 'Asia/Ashgabat' linked_timezone 'Asia/Ashkhabad' + timezone 'Asia/Atyrau' timezone 'Asia/Baghdad' - timezone 'Asia/Bahrain' + linked_timezone 'Asia/Bahrain' timezone 'Asia/Baku' timezone 'Asia/Bangkok' + timezone 'Asia/Barnaul' timezone 'Asia/Beirut' timezone 'Asia/Bishkek' timezone 'Asia/Brunei' @@ -267,6 +272,7 @@ module Timezones timezone 'Asia/Dili' timezone 'Asia/Dubai' timezone 'Asia/Dushanbe' + timezone 'Asia/Famagusta' timezone 'Asia/Gaza' linked_timezone 'Asia/Harbin' timezone 'Asia/Hebron' @@ -289,24 +295,25 @@ module Timezones timezone 'Asia/Krasnoyarsk' timezone 'Asia/Kuala_Lumpur' timezone 'Asia/Kuching' - timezone 'Asia/Kuwait' + linked_timezone 'Asia/Kuwait' linked_timezone 'Asia/Macao' timezone 'Asia/Macau' timezone 'Asia/Magadan' timezone 'Asia/Makassar' timezone 'Asia/Manila' - timezone 'Asia/Muscat' + linked_timezone 'Asia/Muscat' timezone 'Asia/Nicosia' timezone 'Asia/Novokuznetsk' timezone 'Asia/Novosibirsk' timezone 'Asia/Omsk' timezone 'Asia/Oral' - timezone 'Asia/Phnom_Penh' + linked_timezone 'Asia/Phnom_Penh' timezone 'Asia/Pontianak' timezone 'Asia/Pyongyang' timezone 'Asia/Qatar' + timezone 'Asia/Qostanay' timezone 'Asia/Qyzylorda' - timezone 'Asia/Rangoon' + linked_timezone 'Asia/Rangoon' timezone 'Asia/Riyadh' linked_timezone 'Asia/Saigon' timezone 'Asia/Sakhalin' @@ -323,14 +330,16 @@ module Timezones linked_timezone 'Asia/Thimbu' timezone 'Asia/Thimphu' timezone 'Asia/Tokyo' + timezone 'Asia/Tomsk' linked_timezone 'Asia/Ujung_Pandang' timezone 'Asia/Ulaanbaatar' linked_timezone 'Asia/Ulan_Bator' timezone 'Asia/Urumqi' timezone 'Asia/Ust-Nera' - timezone 'Asia/Vientiane' + linked_timezone 'Asia/Vientiane' timezone 'Asia/Vladivostok' timezone 'Asia/Yakutsk' + timezone 'Asia/Yangon' timezone 'Asia/Yekaterinburg' timezone 'Asia/Yerevan' timezone 'Atlantic/Azores' @@ -376,7 +385,6 @@ module Timezones timezone 'CST6CDT' linked_timezone 'Canada/Atlantic' linked_timezone 'Canada/Central' - linked_timezone 'Canada/East-Saskatchewan' linked_timezone 'Canada/Eastern' linked_timezone 'Canada/Mountain' linked_timezone 'Canada/Newfoundland' @@ -422,12 +430,13 @@ module Timezones timezone 'Etc/GMT-9' linked_timezone 'Etc/GMT0' linked_timezone 'Etc/Greenwich' - timezone 'Etc/UCT' + linked_timezone 'Etc/UCT' timezone 'Etc/UTC' linked_timezone 'Etc/Universal' linked_timezone 'Etc/Zulu' timezone 'Europe/Amsterdam' timezone 'Europe/Andorra' + timezone 'Europe/Astrakhan' timezone 'Europe/Athens' linked_timezone 'Europe/Belfast' timezone 'Europe/Belgrade' @@ -448,6 +457,7 @@ module Timezones linked_timezone 'Europe/Jersey' timezone 'Europe/Kaliningrad' timezone 'Europe/Kiev' + timezone 'Europe/Kirov' timezone 'Europe/Lisbon' linked_timezone 'Europe/Ljubljana' timezone 'Europe/London' @@ -468,6 +478,7 @@ module Timezones timezone 'Europe/Samara' linked_timezone 'Europe/San_Marino' linked_timezone 'Europe/Sarajevo' + timezone 'Europe/Saratov' timezone 'Europe/Simferopol' linked_timezone 'Europe/Skopje' timezone 'Europe/Sofia' @@ -475,6 +486,7 @@ module Timezones timezone 'Europe/Tallinn' timezone 'Europe/Tirane' linked_timezone 'Europe/Tiraspol' + timezone 'Europe/Ulyanovsk' timezone 'Europe/Uzhgorod' linked_timezone 'Europe/Vaduz' linked_timezone 'Europe/Vatican' @@ -485,6 +497,7 @@ module Timezones linked_timezone 'Europe/Zagreb' timezone 'Europe/Zaporozhye' timezone 'Europe/Zurich' + timezone 'Factory' linked_timezone 'GB' linked_timezone 'GB-Eire' linked_timezone 'GMT' @@ -495,16 +508,16 @@ module Timezones timezone 'HST' linked_timezone 'Hongkong' linked_timezone 'Iceland' - timezone 'Indian/Antananarivo' + linked_timezone 'Indian/Antananarivo' timezone 'Indian/Chagos' timezone 'Indian/Christmas' timezone 'Indian/Cocos' - timezone 'Indian/Comoro' + linked_timezone 'Indian/Comoro' timezone 'Indian/Kerguelen' timezone 'Indian/Mahe' timezone 'Indian/Maldives' timezone 'Indian/Mauritius' - timezone 'Indian/Mayotte' + linked_timezone 'Indian/Mayotte' timezone 'Indian/Reunion' linked_timezone 'Iran' linked_timezone 'Israel' @@ -525,6 +538,7 @@ module Timezones timezone 'PST8PDT' timezone 'Pacific/Apia' timezone 'Pacific/Auckland' + timezone 'Pacific/Bougainville' timezone 'Pacific/Chatham' timezone 'Pacific/Chuuk' timezone 'Pacific/Easter' @@ -544,7 +558,7 @@ module Timezones timezone 'Pacific/Kwajalein' timezone 'Pacific/Majuro' timezone 'Pacific/Marquesas' - timezone 'Pacific/Midway' + linked_timezone 'Pacific/Midway' timezone 'Pacific/Nauru' timezone 'Pacific/Niue' timezone 'Pacific/Norfolk' @@ -556,7 +570,7 @@ module Timezones linked_timezone 'Pacific/Ponape' timezone 'Pacific/Port_Moresby' timezone 'Pacific/Rarotonga' - timezone 'Pacific/Saipan' + linked_timezone 'Pacific/Saipan' linked_timezone 'Pacific/Samoa' timezone 'Pacific/Tahiti' timezone 'Pacific/Tarawa' @@ -583,7 +597,6 @@ module Timezones linked_timezone 'US/Michigan' linked_timezone 'US/Mountain' linked_timezone 'US/Pacific' - linked_timezone 'US/Pacific-New' linked_timezone 'US/Samoa' linked_timezone 'UTC' linked_timezone 'Universal' diff --git a/test/tzinfo-data/tzinfo/data/version.rb b/test/tzinfo-data/tzinfo/data/version.rb index 0296a3b9..51e2d31a 100644 --- a/test/tzinfo-data/tzinfo/data/version.rb +++ b/test/tzinfo-data/tzinfo/data/version.rb @@ -1,14 +1,20 @@ module TZInfo module Data + # TZInfo::Data version number. + VERSION = '1.2020.4' + # TZInfo::Data version information. module Version # The format of the Ruby modules. The only format currently supported by # TZInfo is version 1. FORMAT = 1 - - # The version of the {IANA Time Zone Database}[http://www.iana.org/time-zones] + + # TZInfo::Data version number. + STRING = VERSION + + # The version of the {IANA Time Zone Database}[https://www.iana.org/time-zones] # used to generate this version of TZInfo::Data. - TZDATA = '2014f' + TZDATA = '2020d' end end end diff --git a/test/zoneinfo/America/Argentina/Buenos_Aires b/test/zoneinfo/America/Argentina/Buenos_Aires index a1fae8c8d7a8b459eeae71e6b4afd81b376b957d..d6f999b8605c9f73653a16e2ddbd5a49b96c0f56 100644 GIT binary patch delta 104 zcmdnbafEe(xC{dtU(iTXu0kO#DON=iVCo3>UDGU7nfAs`7YdW>RsD`bH4u#&TXiFd*HIB>4q!LDF?pTau|G>|Lwq6 z)*ORxMgk7sp3UFzL)iJikK>mOets!;_;uNA!|%hU2mb5{Hu$?H$l>1{Q-l9KCJyY2 zy*6;DseG(4HBtVE*mc z28;b#8{Eq|H@NfMsaNp$wP=Iqo0@`ED1pwFFo`L`X diff --git a/test/zoneinfo/America/New_York b/test/zoneinfo/America/New_York index 7553fee37a5d03e9163ee19b1ced730a02345cfb..2b6c2eea14df07392729ae9f5712a44ec4f02bae 100644 GIT binary patch delta 80 zcmca9eSvp^xC{dtUay^h@1!9rS7OWMllLc7}C-bt@O+LY5H2EN} F1OP@_3XK2& delta 1594 zcmZwGdr(w$6u|M-WYyIbN$~}eVTCBLJX{}$i79QUD?UIG4UPuwAQUGbxD>LMDB(y^5&|Y^>x+o7F_rZJKJZ{lzR&~sAj!3RwtxDgJ9F>n&hP$rzUQ9RHh63t zXmL@gRC3F8U%BPI{~G0C-Q|T_f4T?Xtykf;Z%@Paq#L+n?Hwvwx*vDyi(%K?Q(Wwx z3nd|Yxx}%P_VoLb_cZ3v_wC4gZOOFHK8N>Pr&DS9X#OF~pUSq(#sj8lP@Wlr6$U@3 zT;z@ieJp+8#|cmIkeZ>&qXDa$>)>#=$9%Y^0*>6g#6OpwC0lhBAI&>J$BH)d@w82J zB6~HTj7q2K#036D7fUt9O#Ia~lxqFsv9@DAoYIElsrvEo+vkjR!yjQCbdr7LO+Fuf zjV{F0@Wo+A>5{&L>s8yR!Q#Gw8)z+Eb`;~4V@Y(iF&D4x{tOyzDR@0M5y)!9rsTKb zMwSl$h#Ubo z;q}mq)?x34?W8?s!ah~me#r*zzhI<=+#`K?!1&%Y(6<*4 z^7e(nJz983*IqEBO@kgyE#P_XJ`O#82E59C$5*zM!LW1{zxruDc*oq};Y&;DHGMU| zKJ6=d!+jU~j9gAWj+5xCi6P&{ojCIDSQ=%^K|gym_*;`PpnMGIvZiBT{vZf4nK*cv z@h`ylOh!o5nlZ_sgQS)?H2JuJ>2w$- ztJ@)YpBFA|z5po&p`3cAl|D%Kj^;sTFxz%uMn@ccY+Z++)Q3T4mI+rM@`9}RNVI&dg8U{IpL}-FEr hzjmwG<3Fs2G%z2S7+Ml$T?v1c+h>UqA&b>6eGmMM_$m z%$Oq0=%T`CO>-<_wXs5Ebj^bSvMs!v;XH=7j0d1+xk3v{-0;>p7*_azaM{j zZOsDxgJAawHym;|&y&p^?Q36eZ|{x!dC>o^1_w$4=g$<|P*+CaLPvv!TVete0~r=H zX}W?7Vl4RAbPGwoVIf~cTWI)Y3w`f*3%lBHx1Na7ZC`z>NzKbt zW}oh8+OIofPiksuji&ywPZ8<4inwyZB4<`xdm<~O}*Nj-IzydzU7M+z-fQc53yR8m;LnQU1|ft!>HB!;SM)5Gb;B6*;!P;65u{7H|HP zC@YE!v*PgUR($g(E4g~fN-tlqvf(}}?>lXeoV}<=-x$({why%Ng>G$X>QKej!+NZA zuPSpJRFz(>>i8mUj?7WblsenutF$e@6x!C&EUW!2&9?Q=vB%$;W>1{F#h!d=+@7)@ zY*>25yE9|-YbvD8G*EKQdKX6Gc-@8Auj-B=eNiTLN^zYAW^8H$#oyAe;=QW>? zQ~rP7NvnI!+sQTW8`4cjL&Skd%x*lp?O^uf*=-23Bg~dCd%|prpJP{;Z2|l8>^6qk znP;~(%-%e^&0%(j*&eVzV1vL8fh_`i1U3om64)lNPtR_nz)n58tz!1-*=-iHThDI0 znEhfljM*_}%a}a_o5t)K*fy|lVB^5ffvp33_v|(g?B286KCpkF0hkV8T7c;RrU^h7 zFm1r}0n-RfCorwR^un`i2BsUHT{|%Sz%&HY5uhbNPk^QXT>;tx^aaxxpfi}(V0wdT z4$vLXu0241fCd2_0$K$02xt<}C7?|}pD>NWbPCfdOs_D_!gR~CYZsUB*HECNKudw10!;G7(6z1F_=fa+a%%n?u-)8S9xCiH9uAAjrl_ z5@cXtnF6w2XbB?=n9TtsH!$!3MGi0uc``8Y2{1@7Fvut{%J}$(F!%+BFgOBfZ36=% m10ZP#B%NJDfEXyj1!U5kyH%QhG!qfl!}_6ucy&7TF^b!_u%qYjx(n zIhHv!ZwTrT(eO6F`-P^G)hIPxo389)_l-OWZMoqS>q7T`dr(CwTbl7NbX`N(ky|Tovj=p47pO|HD`##^Q zsY=3H#ACk zb&d2`y;dS@m7WVSgeRcVtM9HIg`}L=xLd zWoY+u9eQ!P4EwoQhc~+Aq0h5*MCDXXsvoWoFHP2wtHO0uexxRs4ULpouF90s z>oT>xS*A^Gk?D)R)u%`9kr}zCG_U7MnVD3h`5kxZtRAYfzFwiT+X{6~(*k|wqDPUJTO>`uU(RofSX!! z=BO-aJ*O|$R?17w^}2B7VO> znGbIB;O4;`KISkt{{D2BPoBR|$ZqouCn2|f|LQO1XcsiT07;6Y$qJGdBrix}kjx;d zL2`p62gweS9wa|Vf{+X$DME7OXp)3v2}zTq$rF+&BvVMLkX#|jLb8RV3&|IfFeGD0 z%8;BPNpmz=L(=AG@`fZ1$sCe8BzH*iknADpb2Rxw5{P6FNgpaN0VwK*GRIFY$NGL@{J@M$vBd7B9%m6Y4$Q&S(fXo6i4ahto6M@VGG8M>NIGV{oX2a1; z2QnYXgdj75ObIe4$fO{%f=mlCFUZ6oGlNVGGB=K9a*)|^G}D934>Cc>3?Wm5%n>q4 z$Sfh#gv=8%QOHaoQ-#bGGFixMIhyH0<_noHWX6ywL*@*bG-TG0X+!1>nK)$Tkf}rF z4w*b;_8iUhA@k>GCJ>oHWD1cvL?#iLMPwS0d3621k7hRm{?$JEn-v`p&y2>TC&Uhk Ojf+W-kHdip{=WiRC6!qK diff --git a/test/zoneinfo/Europe/Andorra b/test/zoneinfo/Europe/Andorra index b06de7a5904dd87bc1c43c023418bf2829c01df0..38685d4219d244f56f665c8afb92eaa6737badb8 100644 GIT binary patch delta 136 zcmcc4+sZsaT!sM+Far4?3<8rInZ!jQOb96nq*#DhaPn5B6Hbgw%-A8&gdC_gplUt= e1`Y-W83jfjAKws$aMut9XD|*9VVJy|O$GqLg$!!| literal 1751 zcmdVaT};h!9LMqhQB7-R-)v;l1Cqkgb5TlioLUsBKjk5b9>ftPtvtn;nYH%4Xv{E7 zYi4OS;UZ=XW30K*3^N<^I5Qg-v&QfJcj>~N&A*-VI&0_LecoStd2Mx~^~V!n{=6C#nI`!6Yo!0wJ-JS1rddCxu-FHvp8n0-4QJl99M_>UpH)cOk0wpO>XBJbOEsgbTr>O9b@u*j%{m&YbE+dXdtaK& z%?VIX#&{py#aQ-@m2>?QOEK`;;!K ztCPiTd$q9SpceV6w0MS3?fhI_60%U2x~J&U!FVklog<}>M(MJzF|z#97hN$BBr8t_ zOIhy+S=IVk$~*dHb@_l)?0m0lvM$Tovin*YaYU*zd$c<6qSl0|*1SEe>xLS%_D+kg ze`f23Gb_}0yIwXP$ke*7V%fAVLO1VEk@~_6X{Zj7EeYY$=>0BRgDq)_8?8;BUP$xE zL2Z6;Q(8Vg)@|pzWP5*~?l^KoclI3DT`is3+TNd&%V?1abPvPOy(Xj!4- zT_b*f&M&a760dD}oL8~U*IX{=&HnoUH<~Xx1N_GC%=6PcyHYQ7AcN##l*llVaUugn zMv4p-87neaWVFa|k?|q}b~GbKhK!6E88k9#WZ1~Kk%1#4M~04!9T_|_dSv*>_>lk{ zO$0~?jwS{q2qX$53?vRD5F`>L6eJcT7$h1b93&njAS5CrBu5hy5|pEf3JD8|3keK~ z3<(X14G9j34hav54+#*75DC%I#E1mxXre^IMB+pOMIuE)MPfyQMWRK*MdC#QMj}Q+ zMq);Sb~I5VVIy%Pfg_P4p(C*)!6VTl;Un=Q2LL$&$RR+E0df!=%~3!OgQGbP$bmqP s1ac^lV}TqD2SN07ND(-S=2V%uGi6q^zo?=DD)$GD`RU#;eS#{d8T diff --git a/test/zoneinfo/Europe/London b/test/zoneinfo/Europe/London index 4527515ca3f249a44599be855b3e12800ebe480d..323cd3818ac6f77b4394982ea5a342b9ed262777 100644 GIT binary patch delta 114 zcmaDZv!7>zxC{dtUaz2n^1!BR?1*~6KSPWxt3QQK{)tW58!ZF#J e*BZ|L!z(A?;~T=@6dc0f4x(LxLniz1Ndo{j2NH(> literal 3687 zcmeI!dvwor9LMqBn$hO=nHU-N(OjC={5BD438AqqmtXotj4)(rmX?v0Q>h%sdF!Yp zo})#oKr`C_xqiB{Lb&^cR0@Re!e`th9*Y2 z?s+_D-{FVH75l^M!1wGg(;6)@)Asu5>D%3A#+-HLtLZb$%#7N`e7IWAO1))f51OvO z9<$NRiC<~HX;Y%-HteV8HO|wSO~Q5NnPM~FwcE@usG%1WUDOLVB!qLWFw2r-&GM9GCcA5d$%*NuzjG_IA}ZCata{n3s^>F6=R@i(EQ)-zEM1 z{+IL*D|hQ3mzJ5IzR1yQrUmP@qcZf*qf7NVZ<=1;ZI1b+WpAC=dad5z-D@@!`kT!` zjbwAi%d%x>J=yy9Q?hNTOY-}9)pj{5JBIg_ohd%wmNFScE z)f`EA-W(k^QXlKy#QfnAb3Ce&IT5``p9~Jur>c9*sgjDisC0xrU3gBPInYC&UAQ1Fzp+dQT#VNh zm(@0vcDQxr$+t|ECDnA*5eJQ$8esyvtufWolzAv}wyEyDY-)s_k)W1&Qqy;v)T)&! zwT>;8hl?jj?RAqS_{cm}XJMAAyY>@RZ=$G>S;Lf0N>TO4#i>VPJu0++Q`I26g=*Nj zi!`cKOEqrtxHP`@goFiDm9T;!X;O4a9?LG4@J+kr@hOE8@okPY9r?YuKgkgH$p)^- zKEJ7`lx)>3I#)f>{d3j4?hMr;YLseGnyy*~Pmz`f`m0tYsnYs~_UftqUU_J<~H`edI~ebcX~gem!|UqXTEKlZe|+Gd3s&}XX* z44JA1MQ2IkgE=y|uE^k188W0aMTTxnlh+P-WLRczNy;0cUjH~+hR^Mx-WU=h$>W== z5#63vDTy`Jo00X@$PPEuTY)Z>+O&qGUOcKsRk<#scC1%v=YNyYOXjLE`ML7W)SqQ+ z)(m+!ZH}Z*N|y(G?)T~IbN9(NeedJ@<;vgtoBhiF)3d@qefVELyM6n1j=f;6D$1uE za@6W*pGv&rvhV!;eLjti^SIje@VGkRbM}SH$H&M1INwFjzu(TQcm5bxVDX)Ax$Ix! zcI`g?Taa7oXzwXZ-+9n`fK-z%x0cix%38WQBFC1+%kZvIDK>C3+ z#L;%d(Y6HXiKA@_(iKPB7NjqZwlPR&kk%l*L7Ibf2Wbz|AEZG@hmaN_JwlpHG@ z8|_2-hcpoBAksod+e4&@j<$C?SSkDWJ4f30@)JCo;f$R=sdm#G**&xUcLAD68N03c|>=I;~Ao~Q_D9BE6v|9z)E68R+ zb_=pyko|&e7-YvFTL#%P$fiMd4YF;JeS>TqWal{At%K|xN4t5D-Ggi&Wd9%=2-!i% u7DD!r>wf1D6X@>q|3PH`740Ajuv5uxCsTM_vxw#q?xvC9aglCb1^fkK<6HXx diff --git a/test/zoneinfo/Europe/Paris b/test/zoneinfo/Europe/Paris index cf6e2e2ee95355039a90146a7f77d14224551b65..00a27264c2cb3e28f2f46e5c267e12d575236a9d 100644 GIT binary patch delta 159 zcmbO&evxB>xC{dtUn1rYy&ybp=hDa(P>P<;SFcCG$j${r^Lzgq3=Ds#( z8JehwPl#5i82J2zOUY_xS}x0_Jd&eK_zCWJP zQwrlqy(PdGVh?UVbkdG=*Re)iI`^b%8cKFV5CzrtF3=uz|9;-9T`<=str{wA}2 zp1;`;-P&xNonb1H=9^99!mY}PB(piP%xuZ&Vr})AYqnlbw6 zP4(t*v$K4twd+N1^ZMec<_)W}^=9!Rt0vpa+C3=7?1{N*?Tyc{YFjl}wRiVf`@Y>~ zz4bx9sr&GRsc+b8_SbGP2iE0T2VYrY4!vs3;gTYAWOA-Knv-RY_4T%n_lz|sdap7k zbE>S97n_$i6h&S>eQk)G*&3{k8&c(+mlEad&`&R)UHqMSx45mmm(@>O)b(=tZx1mo zD<){b()QA7L7K{pWDP8c)YhJM+9q>=+>`L528DmEZQFdT!A;L-@Qnl7?#fQR_x*Bh ze{_Y0)bE$js%q)5W}S3grV=)1zH}-omixv|kj@z%3Gb68_eUj5L};XR@oy)Q-l-aS zGf}&K9--aNwbky&ee{8v+Zt8zrS@2PQKJ{UtuZs|HMU@f#^qj+_~IH#NU4*=;j1L6 z!&*s>FOlRsrP4EGrS`luUV8mBQ+w}Em4`mc(LR-Vno`$WA70pB`<8`jzrt|szo3^q zGSFL73qoW-#5GMzy(0s?PicDiCz5{sstjs+SOzsV$lxm*Wk~fl$vAybGnX%utg7`o zblNn@UiN|x8?{7-7fsX=4;HB>BV8X0&eD-l-E`!wSj`C*F`O%IH1c>zH$X zGPXQWa%;YkaiupTZ)Kz8=be-BbHCCj`tFqpxu>-tY_&{Gsn$Z@-8!j*>ZC7T*2#B@ zbxOl5ee#k=r*0XeMJJ}qQ;Sk`TIC3NdUBXr3zKB}us%{;7%b1kb(9%tKghFzE}0n< zq%+NDQW9`WOU@pXS=TS<>|K@eTw|TiS$#z3R(|2nzk0b`P2%jICRZ<)D?r@7yyG|f=X+P6%N5$m9rTB5dp`5~bM7-TJ+5r~ z9F;bLi^rAfoX#8jvCHLl|9Uz%&R^o^j=% zMe>Uz7|Ae_VkE~%l94PUX-4vlBpS&yl4>N^NV1V^Bk6Xu^Nl1N$vBd7B36jAk4%80Jp;%TAaj6B0x}E8G$8YUOaw9$$W$P6flLN6 z8_0AZ^MOo=qdg(iTXu00`sp--l72FUu7y%nEadBWV1Po2xC1XGZPwQLF1##Bk>s+csoEY0+K8Y z3^EFg91IM60t~z$F-Acj-w=iXAa-^Q1`#0)?m!L~kfCb`64LQC)-&bRGtluhG|)5E JGtf8Y0szFeIE?@R literal 2272 zcmc)Ke@xVM9LMoH&J$tE-e`Cv2UtW%o&1K-AhQz~dgm`LBNfp+QR^ZgV^A8lm~)M> zcaG^C;*Vmino)mXt)aDkjuxV6m!0L>a%EaMH*->r+2{G)T3h7be!u(P<8!e47MLb+Z{f8WnZLUdwP%P{`XW5wC~ZOm_{AGut<)- z?$VK0ymDe!ft;*gqhGe(kW+`2$m#8F{i^bmgnh;Gb>@Ja@yw8KVs=Z!-XM{SEh4`K z8{&f*IyI1})BFqMw){UeA?+8PKJ7boIgYFA>ie2_{w=-z^g*5R@ynWY^q9C0_e=7= z1Cr9AlDhQ;nHg%5J65lkJADC3D=Cw^JVlc3&XBt&CrXCXs~Oh{bk>jQni-z1_k0?y z_YRDzr}v`H?mDYk?O$m2mZO>zd{c94B9hlMAo-<(Qm}lt6ejmbQC^D_jkQT}QkNE= zTPJh=YSy{OymJ4y)mqY5tEGc;^?@DpbzZkyAFNB$`R#M$p>n5sgGsU={eqTx$E4i( zxmKioFBO-6l1j&iQaLgt3(vnQi~8RX-|z?O@9dDphY#tJ`g&Q~@uDt!V$HsJ~LGxy*5i$zVo}T3dhRogYi-`@T;t8yDGI^BeJ$O zEbF#i(#Ph#E9+~9HJG|rHk9^j-J~PBF9>)LYi6KTC1(P!W8 zljlYTb?e@bbX$LqKHu7_ZJlizWsh=97=K4OC*F$N{E&B=AF|Co(>}pwixXSS&CW~x z0h_n1ikASNu$v3bA@KjRnPmRS!=>io96!oCbKNjkO69Im44t@S$((r4q>x!5(?aHj zObnSBGBspw$mEdOA=5+VhfENeAu>f|j>sgDSt8R!=4ojr%6DL<$W)QJB9ldCi%b`p zFEU|EGh<}R$efW$BeO=Pjm#UFI5Kl&>d4%Y$s@BzrjN`YNdS@oOOpa52S^f-EFft> z@_-})$pn%LBo|0BkZd67K=Oek1j&e{NePk@OOq5ND@a<9yda4|GJ~WB$qkYmBs)lY zko+JCLNbJ;2+5J9NfMGJOOqxfPe`JWOd+X4a)l%d$rh3>Bwt9vkc=TILvm(ml7?i> z(xeT^8n}zs~WuV`B8J zxc>>T$*>8q$*`$poYtj>n&ygW379e* +# or . # For more about leap-seconds.list, please see # The NTP Timescale and Leap Seconds -# https://www.eecis.udel.edu/~mills/leap.html +# . -# The International Earth Rotation and Reference Systems Service +# The rules for leap seconds are specified in Annex 1 (Time scales) of: +# Standard-frequency and time-signal emissions. +# International Telecommunication Union - Radiocommunication Sector +# (ITU-R) Recommendation TF.460-6 (02/2002) +# . +# The International Earth Rotation and Reference Systems Service (IERS) # periodically uses leap seconds to keep UTC to within 0.9 s of UT1 -# (which measures the true angular orientation of the earth in space); see -# Levine J. Coordinated Universal Time and the leap second. +# (a proxy for Earth's angle in space as measured by astronomers) +# and publishes leap second data in a copyrighted file +# . +# See: Levine J. Coordinated Universal Time and the leap second. # URSI Radio Sci Bull. 2016;89(4):30-6. doi:10.23919/URSIRSB.2016.7909995 -# http://ieeexplore.ieee.org/document/7909995/ -# There were no leap seconds before 1972, because the official mechanism -# accounting for the discrepancy between atomic time and the earth's rotation -# did not exist until the early 1970s. +# . -# The correction (+ or -) is made at the given time, so lines -# will typically look like: -# Leap YEAR MON DAY 23:59:60 + R/S -# or -# Leap YEAR MON DAY 23:59:59 - R/S +# There were no leap seconds before 1972, as no official mechanism +# accounted for the discrepancy between atomic time (TAI) and the earth's +# rotation. The first ("1 Jan 1972") data line in leap-seconds.list +# does not denote a leap second; it denotes the start of the current definition +# of UTC. -# If the leapsecond is Rolling (R) the given time is local time. -# If the leapsecond is Stationary (S) the given time is UTC. - -# Leap YEAR MONTH DAY HH:MM:SS CORR R/S +# All leap-seconds are Stationary (S) at the given UTC time. +# The correction (+ or -) is made at the given time, so in the unlikely +# event of a negative leap second, a line would look like this: +# Leap YEAR MON DAY 23:59:59 - S +# Typical lines look like this: +# Leap YEAR MON DAY 23:59:60 + S Leap 1972 Jun 30 23:59:60 + S Leap 1972 Dec 31 23:59:60 + S Leap 1973 Dec 31 23:59:60 + S @@ -57,5 +64,15 @@ Leap 2012 Jun 30 23:59:60 + S Leap 2015 Jun 30 23:59:60 + S Leap 2016 Dec 31 23:59:60 + S -# Updated through IERS Bulletin C54 -# File expires on: 28 June 2018 +# UTC timestamp when this leap second list expires. +# Any additional leap seconds will come after this. +# This Expires line is commented out for now, +# so that pre-2020a zic implementations do not reject this file. +#Expires 2021 Jun 28 00:00:00 + +# POSIX timestamps for the data in this file: +#updated 1467936000 (2016-07-08 00:00:00 UTC) +#expires 1624838400 (2021-06-28 00:00:00 UTC) + +# Updated through IERS Bulletin C60 +# File expires on: 28 June 2021 diff --git a/test/zoneinfo/posix/Europe/London b/test/zoneinfo/posix/Europe/London index 4527515ca3f249a44599be855b3e12800ebe480d..323cd3818ac6f77b4394982ea5a342b9ed262777 100644 GIT binary patch delta 114 zcmaDZv!7>zxC{dtUaz2n^1!BR?1*~6KSPWxt3QQK{)tW58!ZF#J e*BZ|L!z(A?;~T=@6dc0f4x(LxLniz1Ndo{j2NH(> literal 3687 zcmeI!dvwor9LMqBn$hO=nHU-N(OjC={5BD438AqqmtXotj4)(rmX?v0Q>h%sdF!Yp zo})#oKr`C_xqiB{Lb&^cR0@Re!e`th9*Y2 z?s+_D-{FVH75l^M!1wGg(;6)@)Asu5>D%3A#+-HLtLZb$%#7N`e7IWAO1))f51OvO z9<$NRiC<~HX;Y%-HteV8HO|wSO~Q5NnPM~FwcE@usG%1WUDOLVB!qLWFw2r-&GM9GCcA5d$%*NuzjG_IA}ZCata{n3s^>F6=R@i(EQ)-zEM1 z{+IL*D|hQ3mzJ5IzR1yQrUmP@qcZf*qf7NVZ<=1;ZI1b+WpAC=dad5z-D@@!`kT!` zjbwAi%d%x>J=yy9Q?hNTOY-}9)pj{5JBIg_ohd%wmNFScE z)f`EA-W(k^QXlKy#QfnAb3Ce&IT5``p9~Jur>c9*sgjDisC0xrU3gBPInYC&UAQ1Fzp+dQT#VNh zm(@0vcDQxr$+t|ECDnA*5eJQ$8esyvtufWolzAv}wyEyDY-)s_k)W1&Qqy;v)T)&! zwT>;8hl?jj?RAqS_{cm}XJMAAyY>@RZ=$G>S;Lf0N>TO4#i>VPJu0++Q`I26g=*Nj zi!`cKOEqrtxHP`@goFiDm9T;!X;O4a9?LG4@J+kr@hOE8@okPY9r?YuKgkgH$p)^- zKEJ7`lx)>3I#)f>{d3j4?hMr;YLseGnyy*~Pmz`f`m0tYsnYs~_UftqUU_J<~H`edI~ebcX~gem!|UqXTEKlZe|+Gd3s&}XX* z44JA1MQ2IkgE=y|uE^k188W0aMTTxnlh+P-WLRczNy;0cUjH~+hR^Mx-WU=h$>W== z5#63vDTy`Jo00X@$PPEuTY)Z>+O&qGUOcKsRk<#scC1%v=YNyYOXjLE`ML7W)SqQ+ z)(m+!ZH}Z*N|y(G?)T~IbN9(NeedJ@<;vgtoBhiF)3d@qefVELyM6n1j=f;6D$1uE za@6W*pGv&rvhV!;eLjti^SIje@VGkRbM}SH$H&M1INwFjzu(TQcm5bxVDX)Ax$Ix! zcI`g?Taa7oXzwXZ-+9n`fK-z%x0cix%38WQBFC1+%kZvIDK>C3+ z#L;%d(Y6HXiKA@_(iKPB7NjqZwlPR&kk%l*L7Ibf2Wbz|AEZG@hmaN_JwlpHG@ z8|_2-hcpoBAksod+e4&@j<$C?SSkDWJ4f30@)JCo;f$R=sdm#G**&xUcLAD68N03c|>=I;~Ao~Q_D9BE6v|9z)E68R+ zb_=pyko|&e7-YvFTL#%P$fiMd4YF;JeS>TqWal{At%K|xN4t5D-Ggi&Wd9%=2-!i% u7DD!r>wf1D6X@>q|3PH`740Ajuv5uxCsTM_vxw#q?xvC9aglCb1^fkK<6HXx diff --git a/test/zoneinfo/posixrules b/test/zoneinfo/posixrules index 7553fee37a5d03e9163ee19b1ced730a02345cfb..2b6c2eea14df07392729ae9f5712a44ec4f02bae 100644 GIT binary patch delta 80 zcmca9eSvp^xC{dtUay^h@1!9rS7OWMllLc7}C-bt@O+LY5H2EN} F1OP@_3XK2& delta 1594 zcmZwGdr(w$6u|M-WYyIbN$~}eVTCBLJX{}$i79QUD?UIG4UPuwAQUGbxD>LMDB(y^5&|Y^>x+o7F_rZJKJZ{lzR&~sAj!3RwtxDgJ9F>n&hP$rzUQ9RHh63t zXmL@gRC3F8U%BPI{~G0C-Q|T_f4T?Xtykf;Z%@Paq#L+n?Hwvwx*vDyi(%K?Q(Wwx z3nd|Yxx}%P_VoLb_cZ3v_wC4gZOOFHK8N>Pr&DS9X#OF~pUSq(#sj8lP@Wlr6$U@3 zT;z@ieJp+8#|cmIkeZ>&qXDa$>)>#=$9%Y^0*>6g#6OpwC0lhBAI&>J$BH)d@w82J zB6~HTj7q2K#036D7fUt9O#Ia~lxqFsv9@DAoYIElsrvEo+vkjR!yjQCbdr7LO+Fuf zjV{F0@Wo+A>5{&L>s8yR!Q#Gw8)z+Eb`;~4V@Y(iF&D4x{tOyzDR@0M5y)!9rsTKb zMwSl$h#Ubo z;q}mq)?x34?W8?s!ah~me#r*zzhI<=+#`K?!1&%Y(6<*4 z^7e(nJz983*IqEBO@kgyE#P_XJ`O#82E59C$5*zM!LW1{zxruDc*oq};Y&;DHGMU| zKJ6=d!+jU~j9gAWj+5xCi6P&{ojCIDSQ=%^K|gym_*;`PpnMGIvZiBT{vZf4nK*cv z@h`ylOh!o5nlZ_sgQS)?H2JuJ>2w$- ztJ@)YpBFA|z5po&p`3cAl|D%Kj^;sTFxz%uMn@ccY+Z++)Q3T4mI+rM@`9}RNVI&dg8U{IpL}-FEr hWgT zrXxh%v_Slb>q4tRbmUhJZ4e!`?F*P6eU%H$k69oHren9f0n>5ypTTtelyhJ@A^s|a zz9}d@`66#F5F1SvV37i12}Z`ruUX_af8n!Zlmr?bI$3unkdgvf6OrLI3rIED9Ki9Jou<+IG`*{tZfd5Uq^8g&(h?GtQpF#T|EQa;R<#A=WBFGsZ^;?XN$%Xj1TLP{$c!^Jl6!^r8oA_iW2UXqfL8n?4qSKW( z&@ZLK_{`5o(b+6ZRGpZC&TSY%=M_olLU1Ae)nf^&@vKI*id(q0Sqon>l_@V}EK*)> zlql=Jo~*o5%276~PzbLoA1JRyFIP6Qai+q?K{w&`b;Ib!DhA*5YeP*E8{9N~NZHid zCcG6`jBX9{@NMT*bbH_@)T|$an!BE&mPcmz_sb3F?u7w-ui^=AEipxH>Gk-2+-&?{ zeLQ+N-x2>2s>F}nbn#=aL#W-7K^?}SxMNTUbq>X%C(X}L*WD1rK0SFAJu82MpYJV2 zFSe|~-MO1kPq-g`$%dg9?r7p z-K$T9ic)Nyw_c2*;>0#v0>oqdLnXH1E)qK@cZqb)+t2BY#|abG|bI@^=H%psVNSD#d+#^(a{b;Dc_Ueg*(aos0b4BaTE#l_a&d21(F3$ z3|a76P824y7{zZQvZ&92S$wRQENRkamSRI@S;bRkIs1wUOK)ISgf%hYo1T!B-p83$ z%j(H$>r5uXtAs?3I!e}vm1OOjjtsJPD2}YFOCq1$4JGTG>V9;fRXa*ic2j z$vj6kmE@3blL`r&%96AmO?jHNz>C%7e)Rsu*Zj~W_|WEm-V?lgS6a0|!g4tJLaqiq zZ2mrj=(m4BC}(;|D90C^K^Gcj3fSiI3!ieS9|@AJzoYWB)`ak=8alI+w^I3fV|BJs zH4SvVHK_vOv$*ZlQKAMF9B*1CxtDfOwaqpUYg2VBR=WO3-_Ow%OP}ac^=z6S=~4Bi z`*KpL2KH>D0afT2)Tg8xI=L~1RFSjM(%n=enyC@hczosTbgGGaiqM2=>T}$357o?X zsUU-DKJP`EIn{#0<#Opwm-IsoUA%4=&shA=^S77oz;DBT_z(S*hV^P_z&w=C<9l;i z0?(j45b7I|76?LxAe0C~jUW^WLX{wt2|}G96beG6Ae0J1tsoQ&MtHF$BM%^5cmVm1 zNDBs`Vh~COp=J<@2BB&Y$_Als5DEvOau7-fp>`082jf@M>|p`vgOLA-bOb;cgAwT{ zfG`dqj06Z{0m5j2FdiU`2nb`s`9IEy=-+cv-YS^NW!_6ln@)|KPLET-c;|`Eau-*p K09QGzwEqH~FL9Uv diff --git a/test/zoneinfo/zone.tab b/test/zoneinfo/zone.tab index 92b9c981..8d056e37 100644 --- a/test/zoneinfo/zone.tab +++ b/test/zoneinfo/zone.tab @@ -1,9 +1,9 @@ -# tz zone descriptions (deprecated version) +# tzdb timezone descriptions (deprecated version) # # This file is in the public domain, so clarified as of # 2009-05-17 by Arthur David Olson. # -# From Paul Eggert (2014-07-31): +# From Paul Eggert (2018-06-27): # This file is intended as a backward-compatibility aid for older programs. # New programs should use zone1970.tab. This file is like zone1970.tab (see # zone1970.tab's comments), but with the following additional restrictions: @@ -12,13 +12,13 @@ # 2. The first data column contains exactly one country code. # # Because of (2), each row stands for an area that is the intersection -# of a region identified by a country code and of a zone where civil +# of a region identified by a country code and of a timezone where civil # clocks have agreed since 1970; this is a narrower definition than # that of zone1970.tab. # -# This table is intended as an aid for users, to help them select time -# zone data appropriate for their practical needs. It is not intended -# to take or endorse any position on legal or territorial claims. +# This table is intended as an aid for users, to help them select timezones +# appropriate for their practical needs. It is not intended to take or +# endorse any position on legal or territorial claims. # #country- #code coordinates TZ comments @@ -30,22 +30,22 @@ AI +1812-06304 America/Anguilla AL +4120+01950 Europe/Tirane AM +4011+04430 Asia/Yerevan AO -0848+01314 Africa/Luanda -AQ -7750+16636 Antarctica/McMurdo McMurdo, South Pole, Scott (New Zealand time) -AQ -6734-06808 Antarctica/Rothera Rothera Station, Adelaide Island -AQ -6448-06406 Antarctica/Palmer Palmer Station, Anvers Island -AQ -6736+06253 Antarctica/Mawson Mawson Station, Holme Bay -AQ -6835+07758 Antarctica/Davis Davis Station, Vestfold Hills -AQ -6617+11031 Antarctica/Casey Casey Station, Bailey Peninsula -AQ -7824+10654 Antarctica/Vostok Vostok Station, Lake Vostok -AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville Station, Terre Adelie -AQ -690022+0393524 Antarctica/Syowa Syowa Station, E Ongul I -AQ -720041+0023206 Antarctica/Troll Troll Station, Queen Maud Land +AQ -7750+16636 Antarctica/McMurdo New Zealand time - McMurdo, South Pole +AQ -6617+11031 Antarctica/Casey Casey +AQ -6835+07758 Antarctica/Davis Davis +AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville +AQ -6736+06253 Antarctica/Mawson Mawson +AQ -6448-06406 Antarctica/Palmer Palmer +AQ -6734-06808 Antarctica/Rothera Rothera +AQ -690022+0393524 Antarctica/Syowa Syowa +AQ -720041+0023206 Antarctica/Troll Troll +AQ -7824+10654 Antarctica/Vostok Vostok AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) -AR -3124-06411 America/Argentina/Cordoba most locations (CB, CC, CN, ER, FM, MN, SE, SF) -AR -2447-06525 America/Argentina/Salta (SA, LP, NQ, RN) +AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF) +AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) AR -2649-06513 America/Argentina/Tucuman Tucuman (TM) -AR -2828-06547 America/Argentina/Catamarca Catamarca (CT), Chubut (CH) +AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH) AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) @@ -56,17 +56,17 @@ AS -1416-17042 Pacific/Pago_Pago AT +4813+01620 Europe/Vienna AU -3133+15905 Australia/Lord_Howe Lord Howe Island AU -5430+15857 Antarctica/Macquarie Macquarie Island -AU -4253+14719 Australia/Hobart Tasmania - most locations -AU -3956+14352 Australia/Currie Tasmania - King Island +AU -4253+14719 Australia/Hobart Tasmania (most areas) +AU -3956+14352 Australia/Currie Tasmania (King Island) AU -3749+14458 Australia/Melbourne Victoria -AU -3352+15113 Australia/Sydney New South Wales - most locations -AU -3157+14127 Australia/Broken_Hill New South Wales - Yancowinna -AU -2728+15302 Australia/Brisbane Queensland - most locations -AU -2016+14900 Australia/Lindeman Queensland - Holiday Islands +AU -3352+15113 Australia/Sydney New South Wales (most areas) +AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna) +AU -2728+15302 Australia/Brisbane Queensland (most areas) +AU -2016+14900 Australia/Lindeman Queensland (Whitsunday Islands) AU -3455+13835 Australia/Adelaide South Australia AU -1228+13050 Australia/Darwin Northern Territory -AU -3157+11551 Australia/Perth Western Australia - most locations -AU -3143+12852 Australia/Eucla Western Australia - Eucla area +AU -3157+11551 Australia/Perth Western Australia (most areas) +AU -3143+12852 Australia/Eucla Western Australia (Eucla) AW +1230-06958 America/Aruba AX +6006+01957 Europe/Mariehamn AZ +4023+04951 Asia/Baku @@ -85,62 +85,64 @@ BN +0456+11455 Asia/Brunei BO -1630-06809 America/La_Paz BQ +120903-0681636 America/Kralendijk BR -0351-03225 America/Noronha Atlantic islands -BR -0127-04829 America/Belem Amapa, E Para -BR -0343-03830 America/Fortaleza NE Brazil (MA, PI, CE, RN, PB) +BR -0127-04829 America/Belem Para (east); Amapa +BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) BR -0803-03454 America/Recife Pernambuco BR -0712-04812 America/Araguaina Tocantins BR -0940-03543 America/Maceio Alagoas, Sergipe BR -1259-03831 America/Bahia Bahia -BR -2332-04637 America/Sao_Paulo S & SE Brazil (GO, DF, MG, ES, RJ, SP, PR, SC, RS) +BR -2332-04637 America/Sao_Paulo Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS) BR -2027-05437 America/Campo_Grande Mato Grosso do Sul BR -1535-05605 America/Cuiaba Mato Grosso -BR -0226-05452 America/Santarem W Para +BR -0226-05452 America/Santarem Para (west) BR -0846-06354 America/Porto_Velho Rondonia BR +0249-06040 America/Boa_Vista Roraima -BR -0308-06001 America/Manaus E Amazonas -BR -0640-06952 America/Eirunepe W Amazonas +BR -0308-06001 America/Manaus Amazonas (east) +BR -0640-06952 America/Eirunepe Amazonas (west) BR -0958-06748 America/Rio_Branco Acre BS +2505-07721 America/Nassau BT +2728+08939 Asia/Thimphu BW -2439+02555 Africa/Gaborone BY +5354+02734 Europe/Minsk BZ +1730-08812 America/Belize -CA +4734-05243 America/St_Johns Newfoundland Time, including SE Labrador -CA +4439-06336 America/Halifax Atlantic Time - Nova Scotia (most places), PEI -CA +4612-05957 America/Glace_Bay Atlantic Time - Nova Scotia - places that did not observe DST 1966-1971 -CA +4606-06447 America/Moncton Atlantic Time - New Brunswick -CA +5320-06025 America/Goose_Bay Atlantic Time - Labrador - most locations -CA +5125-05707 America/Blanc-Sablon Atlantic Standard Time - Quebec - Lower North Shore -CA +4339-07923 America/Toronto Eastern Time - Ontario & Quebec - most locations -CA +4901-08816 America/Nipigon Eastern Time - Ontario & Quebec - places that did not observe DST 1967-1973 -CA +4823-08915 America/Thunder_Bay Eastern Time - Thunder Bay, Ontario -CA +6344-06828 America/Iqaluit Eastern Time - east Nunavut - most locations -CA +6608-06544 America/Pangnirtung Eastern Time - Pangnirtung, Nunavut -CA +744144-0944945 America/Resolute Central Time - Resolute, Nunavut -CA +484531-0913718 America/Atikokan Eastern Standard Time - Atikokan, Ontario and Southampton I, Nunavut -CA +624900-0920459 America/Rankin_Inlet Central Time - central Nunavut -CA +4953-09709 America/Winnipeg Central Time - Manitoba & west Ontario -CA +4843-09434 America/Rainy_River Central Time - Rainy River & Fort Frances, Ontario -CA +5024-10439 America/Regina Central Standard Time - Saskatchewan - most locations -CA +5017-10750 America/Swift_Current Central Standard Time - Saskatchewan - midwest -CA +5333-11328 America/Edmonton Mountain Time - Alberta, east British Columbia & west Saskatchewan -CA +690650-1050310 America/Cambridge_Bay Mountain Time - west Nunavut -CA +6227-11421 America/Yellowknife Mountain Time - central Northwest Territories -CA +682059-1334300 America/Inuvik Mountain Time - west Northwest Territories -CA +4906-11631 America/Creston Mountain Standard Time - Creston, British Columbia -CA +5946-12014 America/Dawson_Creek Mountain Standard Time - Dawson Creek & Fort Saint John, British Columbia -CA +4916-12307 America/Vancouver Pacific Time - west British Columbia -CA +6043-13503 America/Whitehorse Pacific Time - south Yukon -CA +6404-13925 America/Dawson Pacific Time - north Yukon +CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast) +CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE +CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) +CA +4606-06447 America/Moncton Atlantic - New Brunswick +CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) +CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) +CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) +CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73) +CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay) +CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) +CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) +CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) +CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba +CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances) +CA +744144-0944945 America/Resolute Central - NU (Resolute) +CA +624900-0920459 America/Rankin_Inlet Central - NU (central) +CA +5024-10439 America/Regina CST - SK (most areas) +CA +5017-10750 America/Swift_Current CST - SK (midwest) +CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) +CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) +CA +6227-11421 America/Yellowknife Mountain - NT (central) +CA +682059-1334300 America/Inuvik Mountain - NT (west) +CA +4906-11631 America/Creston MST - BC (Creston) +CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) +CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) +CA +4916-12307 America/Vancouver Pacific - BC (most areas) +CA +6043-13503 America/Whitehorse Pacific - Yukon (east) +CA +6404-13925 America/Dawson Pacific - Yukon (west) CC -1210+09655 Indian/Cocos -CD -0418+01518 Africa/Kinshasa west Dem. Rep. of Congo -CD -1140+02728 Africa/Lubumbashi east Dem. Rep. of Congo +CD -0418+01518 Africa/Kinshasa Dem. Rep. of Congo (west) +CD -1140+02728 Africa/Lubumbashi Dem. Rep. of Congo (east) CF +0422+01835 Africa/Bangui CG -0416+01517 Africa/Brazzaville CH +4723+00832 Europe/Zurich CI +0519-00402 Africa/Abidjan CK -2114-15946 Pacific/Rarotonga -CL -3327-07040 America/Santiago most locations +CL -3327-07040 America/Santiago Chile (most areas) +CL -5309-07055 America/Punta_Arenas Region of Magallanes CL -2709-10926 Pacific/Easter Easter Island CM +0403+00942 Africa/Douala CN +3114+12128 Asia/Shanghai Beijing Time @@ -151,30 +153,31 @@ CU +2308-08222 America/Havana CV +1455-02331 Atlantic/Cape_Verde CW +1211-06900 America/Curacao CX -1025+10543 Indian/Christmas -CY +3510+03322 Asia/Nicosia +CY +3510+03322 Asia/Nicosia Cyprus (most areas) +CY +3507+03357 Asia/Famagusta Northern Cyprus CZ +5005+01426 Europe/Prague -DE +5230+01322 Europe/Berlin most locations +DE +5230+01322 Europe/Berlin Germany (most areas) DE +4742+00841 Europe/Busingen Busingen DJ +1136+04309 Africa/Djibouti DK +5540+01235 Europe/Copenhagen DM +1518-06124 America/Dominica DO +1828-06954 America/Santo_Domingo DZ +3647+00303 Africa/Algiers -EC -0210-07950 America/Guayaquil mainland +EC -0210-07950 America/Guayaquil Ecuador (mainland) EC -0054-08936 Pacific/Galapagos Galapagos Islands EE +5925+02445 Europe/Tallinn EG +3003+03115 Africa/Cairo EH +2709-01312 Africa/El_Aaiun ER +1520+03853 Africa/Asmara -ES +4024-00341 Europe/Madrid mainland -ES +3553-00519 Africa/Ceuta Ceuta & Melilla +ES +4024-00341 Europe/Madrid Spain (mainland) +ES +3553-00519 Africa/Ceuta Ceuta, Melilla ES +2806-01524 Atlantic/Canary Canary Islands ET +0902+03842 Africa/Addis_Ababa FI +6010+02458 Europe/Helsinki FJ -1808+17825 Pacific/Fiji FK -5142-05751 Atlantic/Stanley -FM +0725+15147 Pacific/Chuuk Chuuk (Truk) and Yap -FM +0658+15813 Pacific/Pohnpei Pohnpei (Ponape) +FM +0725+15147 Pacific/Chuuk Chuuk/Truk, Yap +FM +0658+15813 Pacific/Pohnpei Pohnpei/Ponape FM +0519+16259 Pacific/Kosrae Kosrae FO +6201-00646 Atlantic/Faroe FR +4852+00220 Europe/Paris @@ -183,13 +186,13 @@ GB +513030-0000731 Europe/London GD +1203-06145 America/Grenada GE +4143+04449 Asia/Tbilisi GF +0456-05220 America/Cayenne -GG +4927-00232 Europe/Guernsey +GG +492717-0023210 Europe/Guernsey GH +0533-00013 Africa/Accra GI +3608-00521 Europe/Gibraltar -GL +6411-05144 America/Godthab most locations -GL +7646-01840 America/Danmarkshavn east coast, north of Scoresbysund -GL +7029-02158 America/Scoresbysund Scoresbysund / Ittoqqortoormiit -GL +7634-06847 America/Thule Thule / Pituffik +GL +6411-05144 America/Nuuk Greenland (most areas) +GL +7646-01840 America/Danmarkshavn National Park (east coast) +GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit +GL +7634-06847 America/Thule Thule/Pituffik GM +1328-01639 Africa/Banjul GN +0931-01343 Africa/Conakry GP +1614-06132 America/Guadeloupe @@ -205,10 +208,10 @@ HN +1406-08713 America/Tegucigalpa HR +4548+01558 Europe/Zagreb HT +1832-07220 America/Port-au-Prince HU +4730+01905 Europe/Budapest -ID -0610+10648 Asia/Jakarta Java & Sumatra -ID -0002+10920 Asia/Pontianak west & central Borneo -ID -0507+11924 Asia/Makassar east & south Borneo, Sulawesi (Celebes), Bali, Nusa Tengarra, west Timor -ID -0232+14042 Asia/Jayapura west New Guinea (Irian Jaya) & Malukus (Moluccas) +ID -0610+10648 Asia/Jakarta Java, Sumatra +ID -0002+10920 Asia/Pontianak Borneo (west, central) +ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west) +ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas IE +5320-00615 Europe/Dublin IL +314650+0351326 Asia/Jerusalem IM +5409-00428 Europe/Isle_of_Man @@ -218,7 +221,7 @@ IQ +3321+04425 Asia/Baghdad IR +3540+05126 Asia/Tehran IS +6409-02151 Atlantic/Reykjavik IT +4154+01229 Europe/Rome -JE +4912-00207 Europe/Jersey +JE +491101-0020624 Europe/Jersey JM +175805-0764736 America/Jamaica JO +3157+03556 Asia/Amman JP +353916+1394441 Asia/Tokyo @@ -234,10 +237,12 @@ KP +3901+12545 Asia/Pyongyang KR +3733+12658 Asia/Seoul KW +2920+04759 Asia/Kuwait KY +1918-08123 America/Cayman -KZ +4315+07657 Asia/Almaty most locations -KZ +4448+06528 Asia/Qyzylorda Qyzylorda (Kyzylorda, Kzyl-Orda) -KZ +5017+05710 Asia/Aqtobe Aqtobe (Aktobe) -KZ +4431+05016 Asia/Aqtau Atyrau (Atirau, Gur'yev), Mangghystau (Mankistau) +KZ +4315+07657 Asia/Almaty Kazakhstan (most areas) +KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda +KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay +KZ +5017+05710 Asia/Aqtobe Aqtobe/Aktobe +KZ +4431+05016 Asia/Aqtau Mangghystau/Mankistau +KZ +4707+05156 Asia/Atyrau Atyrau/Atirau/Gur'yev KZ +5113+05121 Asia/Oral West Kazakhstan LA +1758+10236 Asia/Vientiane LB +3353+03530 Asia/Beirut @@ -256,15 +261,15 @@ MD +4700+02850 Europe/Chisinau ME +4226+01916 Europe/Podgorica MF +1804-06305 America/Marigot MG -1855+04731 Indian/Antananarivo -MH +0709+17112 Pacific/Majuro most locations +MH +0709+17112 Pacific/Majuro Marshall Islands (most areas) MH +0905+16720 Pacific/Kwajalein Kwajalein MK +4159+02126 Europe/Skopje ML +1239-00800 Africa/Bamako -MM +1647+09610 Asia/Rangoon -MN +4755+10653 Asia/Ulaanbaatar most locations +MM +1647+09610 Asia/Yangon +MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas) MN +4801+09139 Asia/Hovd Bayan-Olgiy, Govi-Altai, Hovd, Uvs, Zavkhan MN +4804+11430 Asia/Choibalsan Dornod, Sukhbaatar -MO +2214+11335 Asia/Macau +MO +221150+1133230 Asia/Macau MP +1512+14545 Pacific/Saipan MQ +1436-06105 America/Martinique MR +1806-01557 Africa/Nouakchott @@ -273,20 +278,19 @@ MT +3554+01431 Europe/Malta MU -2010+05730 Indian/Mauritius MV +0410+07330 Indian/Maldives MW -1547+03500 Africa/Blantyre -MX +1924-09909 America/Mexico_City Central Time - most locations -MX +2105-08646 America/Cancun Central Time - Quintana Roo +MX +1924-09909 America/Mexico_City Central Time +MX +2105-08646 America/Cancun Eastern Standard Time - Quintana Roo MX +2058-08937 America/Merida Central Time - Campeche, Yucatan -MX +2540-10019 America/Monterrey Mexican Central Time - Coahuila, Durango, Nuevo Leon, Tamaulipas away from US border -MX +2550-09730 America/Matamoros US Central Time - Coahuila, Durango, Nuevo Leon, Tamaulipas near US border -MX +2313-10625 America/Mazatlan Mountain Time - S Baja, Nayarit, Sinaloa -MX +2838-10605 America/Chihuahua Mexican Mountain Time - Chihuahua away from US border -MX +2934-10425 America/Ojinaga US Mountain Time - Chihuahua near US border +MX +2540-10019 America/Monterrey Central Time - Durango; Coahuila, Nuevo Leon, Tamaulipas (most areas) +MX +2550-09730 America/Matamoros Central Time US - Coahuila, Nuevo Leon, Tamaulipas (US border) +MX +2313-10625 America/Mazatlan Mountain Time - Baja California Sur, Nayarit, Sinaloa +MX +2838-10605 America/Chihuahua Mountain Time - Chihuahua (most areas) +MX +2934-10425 America/Ojinaga Mountain Time US - Chihuahua (US border) MX +2904-11058 America/Hermosillo Mountain Standard Time - Sonora -MX +3232-11701 America/Tijuana US Pacific Time - Baja California near US border -MX +3018-11452 America/Santa_Isabel Mexican Pacific Time - Baja California away from US border -MX +2048-10515 America/Bahia_Banderas Mexican Central Time - Bahia de Banderas -MY +0310+10142 Asia/Kuala_Lumpur peninsular Malaysia -MY +0133+11020 Asia/Kuching Sabah & Sarawak +MX +3232-11701 America/Tijuana Pacific Time US - Baja California +MX +2048-10515 America/Bahia_Banderas Central Time - Bahia de Banderas +MY +0310+10142 Asia/Kuala_Lumpur Malaysia (peninsula) +MY +0133+11020 Asia/Kuching Sabah, Sarawak MZ -2558+03235 Africa/Maputo NA -2234+01706 Africa/Windhoek NC -2216+16627 Pacific/Noumea @@ -299,7 +303,7 @@ NO +5955+01045 Europe/Oslo NP +2743+08519 Asia/Kathmandu NR -0031+16655 Pacific/Nauru NU -1901-16955 Pacific/Niue -NZ -3652+17446 Pacific/Auckland most locations +NZ -3652+17446 Pacific/Auckland New Zealand (most areas) NZ -4357-17633 Pacific/Chatham Chatham Islands OM +2336+05835 Asia/Muscat PA +0858-07932 America/Panama @@ -307,7 +311,8 @@ PE -1203-07703 America/Lima PF -1732-14934 Pacific/Tahiti Society Islands PF -0900-13930 Pacific/Marquesas Marquesas Islands PF -2308-13457 Pacific/Gambier Gambier Islands -PG -0930+14710 Pacific/Port_Moresby +PG -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas) +PG -0613+15534 Pacific/Bougainville Bougainville PH +1435+12100 Asia/Manila PK +2452+06703 Asia/Karachi PL +5215+02100 Europe/Warsaw @@ -316,7 +321,7 @@ PN -2504-13005 Pacific/Pitcairn PR +182806-0660622 America/Puerto_Rico PS +3130+03428 Asia/Gaza Gaza Strip PS +313200+0350542 Asia/Hebron West Bank -PT +3843-00908 Europe/Lisbon mainland +PT +3843-00908 Europe/Lisbon Portugal (mainland) PT +3238-01654 Atlantic/Madeira Madeira Islands PT +3744-02540 Atlantic/Azores Azores PW +0720+13429 Pacific/Palau @@ -325,27 +330,36 @@ QA +2517+05132 Asia/Qatar RE -2052+05528 Indian/Reunion RO +4426+02606 Europe/Bucharest RS +4450+02030 Europe/Belgrade -RU +5443+02030 Europe/Kaliningrad Moscow-01 - Kaliningrad -RU +554521+0373704 Europe/Moscow Moscow+00 - west Russia -RU +4457+03406 Europe/Simferopol Moscow+00 - Crimea -RU +4844+04425 Europe/Volgograd Moscow+00 - Caspian Sea -RU +5312+05009 Europe/Samara Moscow+00 (Moscow+01 after 2014-10-26) - Samara, Udmurtia -RU +5651+06036 Asia/Yekaterinburg Moscow+02 - Urals -RU +5500+07324 Asia/Omsk Moscow+03 - west Siberia -RU +5502+08255 Asia/Novosibirsk Moscow+03 - Novosibirsk -RU +5345+08707 Asia/Novokuznetsk Moscow+03 (Moscow+04 after 2014-10-26) - Kemerovo -RU +5601+09250 Asia/Krasnoyarsk Moscow+04 - Yenisei River -RU +5216+10420 Asia/Irkutsk Moscow+05 - Lake Baikal -RU +5203+11328 Asia/Chita Moscow+06 (Moscow+05 after 2014-10-26) - Zabaykalsky -RU +6200+12940 Asia/Yakutsk Moscow+06 - Lena River -RU +623923+1353314 Asia/Khandyga Moscow+06 - Tomponsky, Ust-Maysky -RU +4310+13156 Asia/Vladivostok Moscow+07 - Amur River -RU +4658+14242 Asia/Sakhalin Moscow+07 - Sakhalin Island -RU +643337+1431336 Asia/Ust-Nera Moscow+07 - Oymyakonsky -RU +5934+15048 Asia/Magadan Moscow+08 (Moscow+07 after 2014-10-26) - Magadan -RU +6728+15343 Asia/Srednekolymsk Moscow+08 - E Sakha, N Kuril Is -RU +5301+15839 Asia/Kamchatka Moscow+08 (Moscow+09 after 2014-10-26) - Kamchatka -RU +6445+17729 Asia/Anadyr Moscow+08 (Moscow+09 after 2014-10-26) - Bering Sea +RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad +RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area +# The obsolescent zone.tab format cannot represent Europe/Simferopol well. +# Put it in RU section and list as UA. See "territorial claims" above. +# Programs should use zone1970.tab instead; see above. +UA +4457+03406 Europe/Simferopol Crimea +RU +5836+04939 Europe/Kirov MSK+00 - Kirov +RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan +RU +4844+04425 Europe/Volgograd MSK+01 - Volgograd +RU +5134+04602 Europe/Saratov MSK+01 - Saratov +RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk +RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia +RU +5651+06036 Asia/Yekaterinburg MSK+02 - Urals +RU +5500+07324 Asia/Omsk MSK+03 - Omsk +RU +5502+08255 Asia/Novosibirsk MSK+04 - Novosibirsk +RU +5322+08345 Asia/Barnaul MSK+04 - Altai +RU +5630+08458 Asia/Tomsk MSK+04 - Tomsk +RU +5345+08707 Asia/Novokuznetsk MSK+04 - Kemerovo +RU +5601+09250 Asia/Krasnoyarsk MSK+04 - Krasnoyarsk area +RU +5216+10420 Asia/Irkutsk MSK+05 - Irkutsk, Buryatia +RU +5203+11328 Asia/Chita MSK+06 - Zabaykalsky +RU +6200+12940 Asia/Yakutsk MSK+06 - Lena River +RU +623923+1353314 Asia/Khandyga MSK+06 - Tomponsky, Ust-Maysky +RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River +RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky +RU +5934+15048 Asia/Magadan MSK+08 - Magadan +RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is +RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka +RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea RW -0157+03004 Africa/Kigali SA +2438+04643 Asia/Riyadh SB -0932+16012 Pacific/Guadalcanal @@ -362,7 +376,7 @@ SM +4355+01228 Europe/San_Marino SN +1440-01726 Africa/Dakar SO +0204+04522 Africa/Mogadishu SR +0550-05510 America/Paramaribo -SS +0451+03136 Africa/Juba +SS +0451+03137 Africa/Juba ST +0020+00644 Africa/Sao_Tome SV +1342-08912 America/El_Salvador SX +180305-0630250 America/Lower_Princes @@ -384,45 +398,44 @@ TT +1039-06131 America/Port_of_Spain TV -0831+17913 Pacific/Funafuti TW +2503+12130 Asia/Taipei TZ -0648+03917 Africa/Dar_es_Salaam -UA +5026+03031 Europe/Kiev most locations -UA +4837+02218 Europe/Uzhgorod Ruthenia -UA +4750+03510 Europe/Zaporozhye Zaporozh'ye, E Lugansk / Zaporizhia, E Luhansk +UA +5026+03031 Europe/Kiev Ukraine (most areas) +UA +4837+02218 Europe/Uzhgorod Transcarpathia +UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk UG +0019+03225 Africa/Kampala -UM +1645-16931 Pacific/Johnston Johnston Atoll UM +2813-17722 Pacific/Midway Midway Islands UM +1917+16637 Pacific/Wake Wake Island -US +404251-0740023 America/New_York Eastern Time -US +421953-0830245 America/Detroit Eastern Time - Michigan - most locations -US +381515-0854534 America/Kentucky/Louisville Eastern Time - Kentucky - Louisville area -US +364947-0845057 America/Kentucky/Monticello Eastern Time - Kentucky - Wayne County -US +394606-0860929 America/Indiana/Indianapolis Eastern Time - Indiana - most locations -US +384038-0873143 America/Indiana/Vincennes Eastern Time - Indiana - Daviess, Dubois, Knox & Martin Counties -US +410305-0863611 America/Indiana/Winamac Eastern Time - Indiana - Pulaski County -US +382232-0862041 America/Indiana/Marengo Eastern Time - Indiana - Crawford County -US +382931-0871643 America/Indiana/Petersburg Eastern Time - Indiana - Pike County -US +384452-0850402 America/Indiana/Vevay Eastern Time - Indiana - Switzerland County -US +415100-0873900 America/Chicago Central Time -US +375711-0864541 America/Indiana/Tell_City Central Time - Indiana - Perry County -US +411745-0863730 America/Indiana/Knox Central Time - Indiana - Starke County -US +450628-0873651 America/Menominee Central Time - Michigan - Dickinson, Gogebic, Iron & Menominee Counties -US +470659-1011757 America/North_Dakota/Center Central Time - North Dakota - Oliver County -US +465042-1012439 America/North_Dakota/New_Salem Central Time - North Dakota - Morton County (except Mandan area) -US +471551-1014640 America/North_Dakota/Beulah Central Time - North Dakota - Mercer County -US +394421-1045903 America/Denver Mountain Time -US +433649-1161209 America/Boise Mountain Time - south Idaho & east Oregon -US +332654-1120424 America/Phoenix Mountain Standard Time - Arizona (except Navajo) -US +340308-1181434 America/Los_Angeles Pacific Time -US +550737-1313435 America/Metlakatla Pacific Standard Time - Annette Island, Alaska -US +611305-1495401 America/Anchorage Alaska Time -US +581807-1342511 America/Juneau Alaska Time - Alaska panhandle -US +571035-1351807 America/Sitka Alaska Time - southeast Alaska panhandle -US +593249-1394338 America/Yakutat Alaska Time - Alaska panhandle neck -US +643004-1652423 America/Nome Alaska Time - west Alaska +US +404251-0740023 America/New_York Eastern (most areas) +US +421953-0830245 America/Detroit Eastern - MI (most areas) +US +381515-0854534 America/Kentucky/Louisville Eastern - KY (Louisville area) +US +364947-0845057 America/Kentucky/Monticello Eastern - KY (Wayne) +US +394606-0860929 America/Indiana/Indianapolis Eastern - IN (most areas) +US +384038-0873143 America/Indiana/Vincennes Eastern - IN (Da, Du, K, Mn) +US +410305-0863611 America/Indiana/Winamac Eastern - IN (Pulaski) +US +382232-0862041 America/Indiana/Marengo Eastern - IN (Crawford) +US +382931-0871643 America/Indiana/Petersburg Eastern - IN (Pike) +US +384452-0850402 America/Indiana/Vevay Eastern - IN (Switzerland) +US +415100-0873900 America/Chicago Central (most areas) +US +375711-0864541 America/Indiana/Tell_City Central - IN (Perry) +US +411745-0863730 America/Indiana/Knox Central - IN (Starke) +US +450628-0873651 America/Menominee Central - MI (Wisconsin border) +US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) +US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) +US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) +US +394421-1045903 America/Denver Mountain (most areas) +US +433649-1161209 America/Boise Mountain - ID (south); OR (east) +US +332654-1120424 America/Phoenix MST - Arizona (except Navajo) +US +340308-1181434 America/Los_Angeles Pacific +US +611305-1495401 America/Anchorage Alaska (most areas) +US +581807-1342511 America/Juneau Alaska - Juneau area +US +571035-1351807 America/Sitka Alaska - Sitka area +US +550737-1313435 America/Metlakatla Alaska - Annette Island +US +593249-1394338 America/Yakutat Alaska - Yakutat +US +643004-1652423 America/Nome Alaska (west) US +515248-1763929 America/Adak Aleutian Islands US +211825-1575130 Pacific/Honolulu Hawaii -UY -3453-05611 America/Montevideo -UZ +3940+06648 Asia/Samarkand west Uzbekistan -UZ +4120+06918 Asia/Tashkent east Uzbekistan +UY -345433-0561245 America/Montevideo +UZ +3940+06648 Asia/Samarkand Uzbekistan (west) +UZ +4120+06918 Asia/Tashkent Uzbekistan (east) VA +415408+0122711 Europe/Vatican VC +1309-06114 America/St_Vincent VE +1030-06656 America/Caracas diff --git a/test/zoneinfo/zone1970.tab b/test/zoneinfo/zone1970.tab index c39380c9..53ee77e8 100644 --- a/test/zoneinfo/zone1970.tab +++ b/test/zoneinfo/zone1970.tab @@ -1,35 +1,35 @@ -# tz zone descriptions +# tzdb timezone descriptions # # This file is in the public domain. # -# From Paul Eggert (2014-07-31): -# This file contains a table where each row stands for a zone where -# civil time stamps have agreed since 1970. Columns are separated by +# From Paul Eggert (2018-06-27): +# This file contains a table where each row stands for a timezone where +# civil timestamps have agreed since 1970. Columns are separated by # a single tab. Lines beginning with '#' are comments. All text uses # UTF-8 encoding. The columns of the table are as follows: # -# 1. The countries that overlap the zone, as a comma-separated list +# 1. The countries that overlap the timezone, as a comma-separated list # of ISO 3166 2-character country codes. See the file 'iso3166.tab'. -# 2. Latitude and longitude of the zone's principal location +# 2. Latitude and longitude of the timezone's principal location # in ISO 6709 sign-degrees-minutes-seconds format, -# either +-DDMM+-DDDMM or +-DDMMSS+-DDDMMSS, +# either ±DDMM±DDDMM or ±DDMMSS±DDDMMSS, # first latitude (+ is north), then longitude (+ is east). -# 3. Zone name used in value of TZ environment variable. -# Please see the 'Theory' file for how zone names are chosen. -# If multiple zones overlap a country, each has a row in the +# 3. Timezone name used in value of TZ environment variable. +# Please see the theory.html file for how these names are chosen. +# If multiple timezones overlap a country, each has a row in the # table, with each column 1 containing the country code. -# 4. Comments; present if and only if a country has multiple zones. +# 4. Comments; present if and only if a country has multiple timezones. # -# If a zone covers multiple countries, the most-populous city is used, +# If a timezone covers multiple countries, the most-populous city is used, # and that country is listed first in column 1; any other countries # are listed alphabetically by country code. The table is sorted # first by country code, then (if possible) by an order within the # country that (1) makes some geographical sense, and (2) puts the -# most populous zones first, where that does not contradict (1). +# most populous timezones first, where that does not contradict (1). # -# This table is intended as an aid for users, to help them select time -# zone data appropriate for their practical needs. It is not intended -# to take or endorse any position on legal or territorial claims. +# This table is intended as an aid for users, to help them select timezones +# appropriate for their practical needs. It is not intended to take or +# endorse any position on legal or territorial claims. # #country- #codes coordinates TZ comments @@ -38,21 +38,21 @@ AE,OM +2518+05518 Asia/Dubai AF +3431+06912 Asia/Kabul AL +4120+01950 Europe/Tirane AM +4011+04430 Asia/Yerevan -AQ -6734-06808 Antarctica/Rothera Rothera Station, Adelaide Island -AQ -6448-06406 Antarctica/Palmer Palmer Station, Anvers Island -AQ -6736+06253 Antarctica/Mawson Mawson Station, Holme Bay -AQ -6835+07758 Antarctica/Davis Davis Station, Vestfold Hills -AQ -6617+11031 Antarctica/Casey Casey Station, Bailey Peninsula -AQ -7824+10654 Antarctica/Vostok Vostok Station, Lake Vostok -AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville Station, Terre Adelie -AQ -690022+0393524 Antarctica/Syowa Syowa Station, E Ongul I -AQ -720041+0023206 Antarctica/Troll Troll Station, Queen Maud Land +AQ -6617+11031 Antarctica/Casey Casey +AQ -6835+07758 Antarctica/Davis Davis +AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville +AQ -6736+06253 Antarctica/Mawson Mawson +AQ -6448-06406 Antarctica/Palmer Palmer +AQ -6734-06808 Antarctica/Rothera Rothera +AQ -690022+0393524 Antarctica/Syowa Syowa +AQ -720041+0023206 Antarctica/Troll Troll +AQ -7824+10654 Antarctica/Vostok Vostok AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) -AR -3124-06411 America/Argentina/Cordoba most locations (CB, CC, CN, ER, FM, MN, SE, SF) -AR -2447-06525 America/Argentina/Salta (SA, LP, NQ, RN) +AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF) +AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) AR -2649-06513 America/Argentina/Tucuman Tucumán (TM) -AR -2828-06547 America/Argentina/Catamarca Catamarca (CT), Chubut (CH) +AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH) AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) @@ -63,17 +63,17 @@ AS,UM -1416-17042 Pacific/Pago_Pago Samoa, Midway AT +4813+01620 Europe/Vienna AU -3133+15905 Australia/Lord_Howe Lord Howe Island AU -5430+15857 Antarctica/Macquarie Macquarie Island -AU -4253+14719 Australia/Hobart Tasmania - most locations -AU -3956+14352 Australia/Currie Tasmania - King Island +AU -4253+14719 Australia/Hobart Tasmania (most areas) +AU -3956+14352 Australia/Currie Tasmania (King Island) AU -3749+14458 Australia/Melbourne Victoria -AU -3352+15113 Australia/Sydney New South Wales - most locations -AU -3157+14127 Australia/Broken_Hill New South Wales - Yancowinna -AU -2728+15302 Australia/Brisbane Queensland - most locations -AU -2016+14900 Australia/Lindeman Queensland - Holiday Islands +AU -3352+15113 Australia/Sydney New South Wales (most areas) +AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna) +AU -2728+15302 Australia/Brisbane Queensland (most areas) +AU -2016+14900 Australia/Lindeman Queensland (Whitsunday Islands) AU -3455+13835 Australia/Adelaide South Australia AU -1228+13050 Australia/Darwin Northern Territory -AU -3157+11551 Australia/Perth Western Australia - most locations -AU -3143+12852 Australia/Eucla Western Australia - Eucla area +AU -3157+11551 Australia/Perth Western Australia (most areas) +AU -3143+12852 Australia/Eucla Western Australia (Eucla) AZ +4023+04951 Asia/Baku BB +1306-05937 America/Barbados BD +2343+09025 Asia/Dhaka @@ -83,57 +83,59 @@ BM +3217-06446 Atlantic/Bermuda BN +0456+11455 Asia/Brunei BO -1630-06809 America/La_Paz BR -0351-03225 America/Noronha Atlantic islands -BR -0127-04829 America/Belem Amapá, E Pará -BR -0343-03830 America/Fortaleza NE Brazil (MA, PI, CE, RN, PB) +BR -0127-04829 America/Belem Pará (east); Amapá +BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) BR -0803-03454 America/Recife Pernambuco BR -0712-04812 America/Araguaina Tocantins BR -0940-03543 America/Maceio Alagoas, Sergipe BR -1259-03831 America/Bahia Bahia -BR -2332-04637 America/Sao_Paulo S & SE Brazil (GO, DF, MG, ES, RJ, SP, PR, SC, RS) +BR -2332-04637 America/Sao_Paulo Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS) BR -2027-05437 America/Campo_Grande Mato Grosso do Sul BR -1535-05605 America/Cuiaba Mato Grosso -BR -0226-05452 America/Santarem W Pará +BR -0226-05452 America/Santarem Pará (west) BR -0846-06354 America/Porto_Velho Rondônia BR +0249-06040 America/Boa_Vista Roraima -BR -0308-06001 America/Manaus E Amazonas -BR -0640-06952 America/Eirunepe W Amazonas +BR -0308-06001 America/Manaus Amazonas (east) +BR -0640-06952 America/Eirunepe Amazonas (west) BR -0958-06748 America/Rio_Branco Acre BS +2505-07721 America/Nassau BT +2728+08939 Asia/Thimphu BY +5354+02734 Europe/Minsk BZ +1730-08812 America/Belize -CA +4734-05243 America/St_Johns Newfoundland Time, including SE Labrador -CA +4439-06336 America/Halifax Atlantic Time - Nova Scotia (most places), PEI -CA +4612-05957 America/Glace_Bay Atlantic Time - Nova Scotia - places that did not observe DST 1966-1971 -CA +4606-06447 America/Moncton Atlantic Time - New Brunswick -CA +5320-06025 America/Goose_Bay Atlantic Time - Labrador - most locations -CA +5125-05707 America/Blanc-Sablon Atlantic Standard Time - Quebec - Lower North Shore -CA +4339-07923 America/Toronto Eastern Time - Ontario & Quebec - most locations -CA +4901-08816 America/Nipigon Eastern Time - Ontario & Quebec - places that did not observe DST 1967-1973 -CA +4823-08915 America/Thunder_Bay Eastern Time - Thunder Bay, Ontario -CA +6344-06828 America/Iqaluit Eastern Time - east Nunavut - most locations -CA +6608-06544 America/Pangnirtung Eastern Time - Pangnirtung, Nunavut -CA +744144-0944945 America/Resolute Central Time - Resolute, Nunavut -CA +484531-0913718 America/Atikokan Eastern Standard Time - Atikokan, Ontario and Southampton I, Nunavut -CA +624900-0920459 America/Rankin_Inlet Central Time - central Nunavut -CA +4953-09709 America/Winnipeg Central Time - Manitoba & west Ontario -CA +4843-09434 America/Rainy_River Central Time - Rainy River & Fort Frances, Ontario -CA +5024-10439 America/Regina Central Standard Time - Saskatchewan - most locations -CA +5017-10750 America/Swift_Current Central Standard Time - Saskatchewan - midwest -CA +5333-11328 America/Edmonton Mountain Time - Alberta, east British Columbia & west Saskatchewan -CA +690650-1050310 America/Cambridge_Bay Mountain Time - west Nunavut -CA +6227-11421 America/Yellowknife Mountain Time - central Northwest Territories -CA +682059-1334300 America/Inuvik Mountain Time - west Northwest Territories -CA +4906-11631 America/Creston Mountain Standard Time - Creston, British Columbia -CA +5946-12014 America/Dawson_Creek Mountain Standard Time - Dawson Creek & Fort Saint John, British Columbia -CA +4916-12307 America/Vancouver Pacific Time - west British Columbia -CA +6043-13503 America/Whitehorse Pacific Time - south Yukon -CA +6404-13925 America/Dawson Pacific Time - north Yukon +CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast) +CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE +CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) +CA +4606-06447 America/Moncton Atlantic - New Brunswick +CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) +CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) +CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) +CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73) +CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay) +CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) +CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) +CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) +CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba +CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances) +CA +744144-0944945 America/Resolute Central - NU (Resolute) +CA +624900-0920459 America/Rankin_Inlet Central - NU (central) +CA +5024-10439 America/Regina CST - SK (most areas) +CA +5017-10750 America/Swift_Current CST - SK (midwest) +CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) +CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) +CA +6227-11421 America/Yellowknife Mountain - NT (central) +CA +682059-1334300 America/Inuvik Mountain - NT (west) +CA +4906-11631 America/Creston MST - BC (Creston) +CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) +CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) +CA +4916-12307 America/Vancouver Pacific - BC (most areas) +CA +6043-13503 America/Whitehorse Pacific - Yukon (east) +CA +6404-13925 America/Dawson Pacific - Yukon (west) CC -1210+09655 Indian/Cocos CH,DE,LI +4723+00832 Europe/Zurich Swiss time -CI,BF,GM,GN,ML,MR,SH,SL,SN,ST,TG +0519-00402 Africa/Abidjan +CI,BF,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan CK -2114-15946 Pacific/Rarotonga -CL -3327-07040 America/Santiago most locations +CL -3327-07040 America/Santiago Chile (most areas) +CL -5309-07055 America/Punta_Arenas Region of Magallanes CL -2709-10926 Pacific/Easter Easter Island CN +3114+12128 Asia/Shanghai Beijing Time CN +4348+08735 Asia/Urumqi Xinjiang Time @@ -143,25 +145,26 @@ CU +2308-08222 America/Havana CV +1455-02331 Atlantic/Cape_Verde CW,AW,BQ,SX +1211-06900 America/Curacao CX -1025+10543 Indian/Christmas -CY +3510+03322 Asia/Nicosia +CY +3510+03322 Asia/Nicosia Cyprus (most areas) +CY +3507+03357 Asia/Famagusta Northern Cyprus CZ,SK +5005+01426 Europe/Prague -DE +5230+01322 Europe/Berlin Berlin time +DE +5230+01322 Europe/Berlin Germany (most areas) DK +5540+01235 Europe/Copenhagen DO +1828-06954 America/Santo_Domingo DZ +3647+00303 Africa/Algiers -EC -0210-07950 America/Guayaquil mainland +EC -0210-07950 America/Guayaquil Ecuador (mainland) EC -0054-08936 Pacific/Galapagos Galápagos Islands EE +5925+02445 Europe/Tallinn EG +3003+03115 Africa/Cairo EH +2709-01312 Africa/El_Aaiun -ES +4024-00341 Europe/Madrid mainland -ES +3553-00519 Africa/Ceuta Ceuta & Melilla +ES +4024-00341 Europe/Madrid Spain (mainland) +ES +3553-00519 Africa/Ceuta Ceuta, Melilla ES +2806-01524 Atlantic/Canary Canary Islands FI,AX +6010+02458 Europe/Helsinki FJ -1808+17825 Pacific/Fiji FK -5142-05751 Atlantic/Stanley -FM +0725+15147 Pacific/Chuuk Chuuk (Truk) and Yap -FM +0658+15813 Pacific/Pohnpei Pohnpei (Ponape) +FM +0725+15147 Pacific/Chuuk Chuuk/Truk, Yap +FM +0658+15813 Pacific/Pohnpei Pohnpei/Ponape FM +0519+16259 Pacific/Kosrae Kosrae FO +6201-00646 Atlantic/Faroe FR +4852+00220 Europe/Paris @@ -170,10 +173,10 @@ GE +4143+04449 Asia/Tbilisi GF +0456-05220 America/Cayenne GH +0533-00013 Africa/Accra GI +3608-00521 Europe/Gibraltar -GL +6411-05144 America/Godthab most locations -GL +7646-01840 America/Danmarkshavn east coast, north of Scoresbysund -GL +7029-02158 America/Scoresbysund Scoresbysund / Ittoqqortoormiit -GL +7634-06847 America/Thule Thule / Pituffik +GL +6411-05144 America/Nuuk Greenland (most areas) +GL +7646-01840 America/Danmarkshavn National Park (east coast) +GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit +GL +7634-06847 America/Thule Thule/Pituffik GR +3758+02343 Europe/Athens GS -5416-03632 Atlantic/South_Georgia GT +1438-09031 America/Guatemala @@ -184,10 +187,10 @@ HK +2217+11409 Asia/Hong_Kong HN +1406-08713 America/Tegucigalpa HT +1832-07220 America/Port-au-Prince HU +4730+01905 Europe/Budapest -ID -0610+10648 Asia/Jakarta Java & Sumatra -ID -0002+10920 Asia/Pontianak west & central Borneo -ID -0507+11924 Asia/Makassar east & south Borneo, Sulawesi (Celebes), Bali, Nusa Tengarra, west Timor -ID -0232+14042 Asia/Jayapura west New Guinea (Irian Jaya) & Malukus (Moluccas) +ID -0610+10648 Asia/Jakarta Java, Sumatra +ID -0002+10920 Asia/Pontianak Borneo (west, central) +ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west) +ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas IE +5320-00615 Europe/Dublin IL +314650+0351326 Asia/Jerusalem IN +2232+08822 Asia/Kolkata @@ -206,10 +209,12 @@ KI -0308-17105 Pacific/Enderbury Phoenix Islands KI +0152-15720 Pacific/Kiritimati Line Islands KP +3901+12545 Asia/Pyongyang KR +3733+12658 Asia/Seoul -KZ +4315+07657 Asia/Almaty most locations -KZ +4448+06528 Asia/Qyzylorda Qyzylorda (Kyzylorda, Kzyl-Orda) -KZ +5017+05710 Asia/Aqtobe Aqtobe (Aktobe) -KZ +4431+05016 Asia/Aqtau Atyrau (Atirau, Gur'yev), Mangghystau (Mankistau) +KZ +4315+07657 Asia/Almaty Kazakhstan (most areas) +KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda +KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay +KZ +5017+05710 Asia/Aqtobe Aqtöbe/Aktobe +KZ +4431+05016 Asia/Aqtau Mangghystaū/Mankistau +KZ +4707+05156 Asia/Atyrau Atyraū/Atirau/Gur'yev KZ +5113+05121 Asia/Oral West Kazakhstan LB +3353+03530 Asia/Beirut LK +0656+07951 Asia/Colombo @@ -221,36 +226,35 @@ LY +3254+01311 Africa/Tripoli MA +3339-00735 Africa/Casablanca MC +4342+00723 Europe/Monaco MD +4700+02850 Europe/Chisinau -MH +0709+17112 Pacific/Majuro most locations +MH +0709+17112 Pacific/Majuro Marshall Islands (most areas) MH +0905+16720 Pacific/Kwajalein Kwajalein -MM +1647+09610 Asia/Rangoon -MN +4755+10653 Asia/Ulaanbaatar most locations +MM +1647+09610 Asia/Yangon +MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas) MN +4801+09139 Asia/Hovd Bayan-Ölgii, Govi-Altai, Hovd, Uvs, Zavkhan MN +4804+11430 Asia/Choibalsan Dornod, Sükhbaatar -MO +2214+11335 Asia/Macau +MO +221150+1133230 Asia/Macau MQ +1436-06105 America/Martinique MT +3554+01431 Europe/Malta MU -2010+05730 Indian/Mauritius MV +0410+07330 Indian/Maldives -MX +1924-09909 America/Mexico_City Central Time - most locations -MX +2105-08646 America/Cancun Central Time - Quintana Roo +MX +1924-09909 America/Mexico_City Central Time +MX +2105-08646 America/Cancun Eastern Standard Time - Quintana Roo MX +2058-08937 America/Merida Central Time - Campeche, Yucatán -MX +2540-10019 America/Monterrey Mexican Central Time - Coahuila, Durango, Nuevo León, Tamaulipas away from US border -MX +2550-09730 America/Matamoros US Central Time - Coahuila, Durango, Nuevo León, Tamaulipas near US border -MX +2313-10625 America/Mazatlan Mountain Time - S Baja, Nayarit, Sinaloa -MX +2838-10605 America/Chihuahua Mexican Mountain Time - Chihuahua away from US border -MX +2934-10425 America/Ojinaga US Mountain Time - Chihuahua near US border +MX +2540-10019 America/Monterrey Central Time - Durango; Coahuila, Nuevo León, Tamaulipas (most areas) +MX +2550-09730 America/Matamoros Central Time US - Coahuila, Nuevo León, Tamaulipas (US border) +MX +2313-10625 America/Mazatlan Mountain Time - Baja California Sur, Nayarit, Sinaloa +MX +2838-10605 America/Chihuahua Mountain Time - Chihuahua (most areas) +MX +2934-10425 America/Ojinaga Mountain Time US - Chihuahua (US border) MX +2904-11058 America/Hermosillo Mountain Standard Time - Sonora -MX +3232-11701 America/Tijuana US Pacific Time - Baja California near US border -MX +3018-11452 America/Santa_Isabel Mexican Pacific Time - Baja California away from US border -MX +2048-10515 America/Bahia_Banderas Mexican Central Time - Bahía de Banderas -MY +0310+10142 Asia/Kuala_Lumpur peninsular Malaysia -MY +0133+11020 Asia/Kuching Sabah & Sarawak -MZ,BI,BW,CD,MW,RW,ZM,ZW -2558+03235 Africa/Maputo Central Africa Time (UTC+2) +MX +3232-11701 America/Tijuana Pacific Time US - Baja California +MX +2048-10515 America/Bahia_Banderas Central Time - Bahía de Banderas +MY +0310+10142 Asia/Kuala_Lumpur Malaysia (peninsula) +MY +0133+11020 Asia/Kuching Sabah, Sarawak +MZ,BI,BW,CD,MW,RW,ZM,ZW -2558+03235 Africa/Maputo Central Africa Time NA -2234+01706 Africa/Windhoek NC -2216+16627 Pacific/Noumea NF -2903+16758 Pacific/Norfolk -NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE +0627+00324 Africa/Lagos West Africa Time (UTC+1) +NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE +0627+00324 Africa/Lagos West Africa Time NI +1209-08617 America/Managua NL +5222+00454 Europe/Amsterdam NO,SJ +5955+01045 Europe/Oslo @@ -264,7 +268,8 @@ PE -1203-07703 America/Lima PF -1732-14934 Pacific/Tahiti Society Islands PF -0900-13930 Pacific/Marquesas Marquesas Islands PF -2308-13457 Pacific/Gambier Gambier Islands -PG -0930+14710 Pacific/Port_Moresby +PG -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas) +PG -0613+15534 Pacific/Bougainville Bougainville PH +1435+12100 Asia/Manila PK +2452+06703 Asia/Karachi PL +5215+02100 Europe/Warsaw @@ -273,49 +278,58 @@ PN -2504-13005 Pacific/Pitcairn PR +182806-0660622 America/Puerto_Rico PS +3130+03428 Asia/Gaza Gaza Strip PS +313200+0350542 Asia/Hebron West Bank -PT +3843-00908 Europe/Lisbon mainland +PT +3843-00908 Europe/Lisbon Portugal (mainland) PT +3238-01654 Atlantic/Madeira Madeira Islands PT +3744-02540 Atlantic/Azores Azores PW +0720+13429 Pacific/Palau PY -2516-05740 America/Asuncion QA,BH +2517+05132 Asia/Qatar -RE,TF -2052+05528 Indian/Reunion Réunion, Crozet Is, Scattered Is +RE,TF -2052+05528 Indian/Reunion Réunion, Crozet, Scattered Islands RO +4426+02606 Europe/Bucharest RS,BA,HR,ME,MK,SI +4450+02030 Europe/Belgrade -RU +5443+02030 Europe/Kaliningrad Moscow-01 - Kaliningrad -RU +554521+0373704 Europe/Moscow Moscow+00 - west Russia -RU +4457+03406 Europe/Simferopol Moscow+00 - Crimea -RU +4844+04425 Europe/Volgograd Moscow+00 - Caspian Sea -RU +5312+05009 Europe/Samara Moscow+00 (Moscow+01 after 2014-10-26) - Samara, Udmurtia -RU +5651+06036 Asia/Yekaterinburg Moscow+02 - Urals -RU +5500+07324 Asia/Omsk Moscow+03 - west Siberia -RU +5502+08255 Asia/Novosibirsk Moscow+03 - Novosibirsk -RU +5345+08707 Asia/Novokuznetsk Moscow+03 (Moscow+04 after 2014-10-26) - Kemerovo -RU +5601+09250 Asia/Krasnoyarsk Moscow+04 - Yenisei River -RU +5216+10420 Asia/Irkutsk Moscow+05 - Lake Baikal -RU +5203+11328 Asia/Chita Moscow+06 (Moscow+05 after 2014-10-26) - Zabaykalsky -RU +6200+12940 Asia/Yakutsk Moscow+06 - Lena River -RU +623923+1353314 Asia/Khandyga Moscow+06 - Tomponsky, Ust-Maysky -RU +4310+13156 Asia/Vladivostok Moscow+07 - Amur River -RU +4658+14242 Asia/Sakhalin Moscow+07 - Sakhalin Island -RU +643337+1431336 Asia/Ust-Nera Moscow+07 - Oymyakonsky -RU +5934+15048 Asia/Magadan Moscow+08 (Moscow+07 after 2014-10-26) - Magadan -RU +6728+15343 Asia/Srednekolymsk Moscow+08 - E Sakha, N Kuril Is -RU +5301+15839 Asia/Kamchatka Moscow+08 (Moscow+09 after 2014-10-26) - Kamchatka -RU +6445+17729 Asia/Anadyr Moscow+08 (Moscow+09 after 2014-10-26) - Bering Sea +RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad +RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area +# Mention RU and UA alphabetically. See "territorial claims" above. +RU,UA +4457+03406 Europe/Simferopol Crimea +RU +5836+04939 Europe/Kirov MSK+00 - Kirov +RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan +RU +4844+04425 Europe/Volgograd MSK+01 - Volgograd +RU +5134+04602 Europe/Saratov MSK+01 - Saratov +RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk +RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia +RU +5651+06036 Asia/Yekaterinburg MSK+02 - Urals +RU +5500+07324 Asia/Omsk MSK+03 - Omsk +RU +5502+08255 Asia/Novosibirsk MSK+04 - Novosibirsk +RU +5322+08345 Asia/Barnaul MSK+04 - Altai +RU +5630+08458 Asia/Tomsk MSK+04 - Tomsk +RU +5345+08707 Asia/Novokuznetsk MSK+04 - Kemerovo +RU +5601+09250 Asia/Krasnoyarsk MSK+04 - Krasnoyarsk area +RU +5216+10420 Asia/Irkutsk MSK+05 - Irkutsk, Buryatia +RU +5203+11328 Asia/Chita MSK+06 - Zabaykalsky +RU +6200+12940 Asia/Yakutsk MSK+06 - Lena River +RU +623923+1353314 Asia/Khandyga MSK+06 - Tomponsky, Ust-Maysky +RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River +RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky +RU +5934+15048 Asia/Magadan MSK+08 - Magadan +RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is +RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka +RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea SA,KW,YE +2438+04643 Asia/Riyadh SB -0932+16012 Pacific/Guadalcanal SC -0440+05528 Indian/Mahe -SD,SS +1536+03232 Africa/Khartoum +SD +1536+03232 Africa/Khartoum SE +5920+01803 Europe/Stockholm SG +0117+10351 Asia/Singapore SR +0550-05510 America/Paramaribo +SS +0451+03137 Africa/Juba +ST +0020+00644 Africa/Sao_Tome SV +1342-08912 America/El_Salvador SY +3330+03618 Asia/Damascus TC +2128-07108 America/Grand_Turk TD +1207+01503 Africa/Ndjamena -TF -492110+0701303 Indian/Kerguelen Kerguelen, St Paul I, Amsterdam I -TH,KH,LA,VN +1345+10031 Asia/Bangkok +TF -492110+0701303 Indian/Kerguelen Kerguelen, St Paul Island, Amsterdam Island +TH,KH,LA,VN +1345+10031 Asia/Bangkok Indochina (most areas) TJ +3835+06848 Asia/Dushanbe TK -0922-17114 Pacific/Fakaofo TL -0833+12535 Asia/Dili @@ -323,46 +337,47 @@ TM +3757+05823 Asia/Ashgabat TN +3648+01011 Africa/Tunis TO -2110-17510 Pacific/Tongatapu TR +4101+02858 Europe/Istanbul -TT,AG,AI,BL,DM,GD,GP,MF,LC,KN,MS,VC,VG,VI +1039-06131 America/Port_of_Spain +TT,AG,AI,BL,DM,GD,GP,KN,LC,MF,MS,VC,VG,VI +1039-06131 America/Port_of_Spain TV -0831+17913 Pacific/Funafuti TW +2503+12130 Asia/Taipei -UA +5026+03031 Europe/Kiev most locations -UA +4837+02218 Europe/Uzhgorod Ruthenia -UA +4750+03510 Europe/Zaporozhye Zaporozh'ye, E Lugansk / Zaporizhia, E Luhansk +UA +5026+03031 Europe/Kiev Ukraine (most areas) +UA +4837+02218 Europe/Uzhgorod Transcarpathia +UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk UM +1917+16637 Pacific/Wake Wake Island -US +404251-0740023 America/New_York Eastern Time -US +421953-0830245 America/Detroit Eastern Time - Michigan - most locations -US +381515-0854534 America/Kentucky/Louisville Eastern Time - Kentucky - Louisville area -US +364947-0845057 America/Kentucky/Monticello Eastern Time - Kentucky - Wayne County -US +394606-0860929 America/Indiana/Indianapolis Eastern Time - Indiana - most locations -US +384038-0873143 America/Indiana/Vincennes Eastern Time - Indiana - Daviess, Dubois, Knox & Martin Counties -US +410305-0863611 America/Indiana/Winamac Eastern Time - Indiana - Pulaski County -US +382232-0862041 America/Indiana/Marengo Eastern Time - Indiana - Crawford County -US +382931-0871643 America/Indiana/Petersburg Eastern Time - Indiana - Pike County -US +384452-0850402 America/Indiana/Vevay Eastern Time - Indiana - Switzerland County -US +415100-0873900 America/Chicago Central Time -US +375711-0864541 America/Indiana/Tell_City Central Time - Indiana - Perry County -US +411745-0863730 America/Indiana/Knox Central Time - Indiana - Starke County -US +450628-0873651 America/Menominee Central Time - Michigan - Dickinson, Gogebic, Iron & Menominee Counties -US +470659-1011757 America/North_Dakota/Center Central Time - North Dakota - Oliver County -US +465042-1012439 America/North_Dakota/New_Salem Central Time - North Dakota - Morton County (except Mandan area) -US +471551-1014640 America/North_Dakota/Beulah Central Time - North Dakota - Mercer County -US +394421-1045903 America/Denver Mountain Time -US +433649-1161209 America/Boise Mountain Time - south Idaho & east Oregon -US +332654-1120424 America/Phoenix Mountain Standard Time - Arizona (except Navajo) -US +340308-1181434 America/Los_Angeles Pacific Time -US +550737-1313435 America/Metlakatla Pacific Standard Time - Annette Island, Alaska -US +611305-1495401 America/Anchorage Alaska Time -US +581807-1342511 America/Juneau Alaska Time - Alaska panhandle -US +571035-1351807 America/Sitka Alaska Time - southeast Alaska panhandle -US +593249-1394338 America/Yakutat Alaska Time - Alaska panhandle neck -US +643004-1652423 America/Nome Alaska Time - west Alaska +US +404251-0740023 America/New_York Eastern (most areas) +US +421953-0830245 America/Detroit Eastern - MI (most areas) +US +381515-0854534 America/Kentucky/Louisville Eastern - KY (Louisville area) +US +364947-0845057 America/Kentucky/Monticello Eastern - KY (Wayne) +US +394606-0860929 America/Indiana/Indianapolis Eastern - IN (most areas) +US +384038-0873143 America/Indiana/Vincennes Eastern - IN (Da, Du, K, Mn) +US +410305-0863611 America/Indiana/Winamac Eastern - IN (Pulaski) +US +382232-0862041 America/Indiana/Marengo Eastern - IN (Crawford) +US +382931-0871643 America/Indiana/Petersburg Eastern - IN (Pike) +US +384452-0850402 America/Indiana/Vevay Eastern - IN (Switzerland) +US +415100-0873900 America/Chicago Central (most areas) +US +375711-0864541 America/Indiana/Tell_City Central - IN (Perry) +US +411745-0863730 America/Indiana/Knox Central - IN (Starke) +US +450628-0873651 America/Menominee Central - MI (Wisconsin border) +US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) +US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) +US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) +US +394421-1045903 America/Denver Mountain (most areas) +US +433649-1161209 America/Boise Mountain - ID (south); OR (east) +US +332654-1120424 America/Phoenix MST - Arizona (except Navajo) +US +340308-1181434 America/Los_Angeles Pacific +US +611305-1495401 America/Anchorage Alaska (most areas) +US +581807-1342511 America/Juneau Alaska - Juneau area +US +571035-1351807 America/Sitka Alaska - Sitka area +US +550737-1313435 America/Metlakatla Alaska - Annette Island +US +593249-1394338 America/Yakutat Alaska - Yakutat +US +643004-1652423 America/Nome Alaska (west) US +515248-1763929 America/Adak Aleutian Islands -US,UM +211825-1575130 Pacific/Honolulu Hawaii time -UY -3453-05611 America/Montevideo -UZ +3940+06648 Asia/Samarkand west Uzbekistan -UZ +4120+06918 Asia/Tashkent east Uzbekistan +US,UM +211825-1575130 Pacific/Honolulu Hawaii +UY -345433-0561245 America/Montevideo +UZ +3940+06648 Asia/Samarkand Uzbekistan (west) +UZ +4120+06918 Asia/Tashkent Uzbekistan (east) VE +1030-06656 America/Caracas +VN +1045+10640 Asia/Ho_Chi_Minh Vietnam (south) VU -1740+16825 Pacific/Efate WF -1318-17610 Pacific/Wallis WS -1350-17144 Pacific/Apia From 4ace6e8f8e7fc07b080c5b3d39e1b70a5160d860 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 8 Nov 2020 11:42:34 +0000 Subject: [PATCH 56/81] Preparing v1.2.8. --- CHANGES.md | 11 +++++++++++ tzinfo.gemspec | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 76d26d61..dfe39a57 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,14 @@ +Version 1.2.8 - 8-Nov-2020 +-------------------------- + +* Added support for handling "slim" format zoneinfo files that are produced by + default by zic version 2020b and later. The POSIX-style TZ string is now used + calculate DST transition times after the final defined transition in the file. + The 64-bit section is now always used regardless of whether Time has support + for 64-bit times. +* Rubinius is no longer supported. + + Version 1.2.7 - 2-Apr-2020 -------------------------- diff --git a/tzinfo.gemspec b/tzinfo.gemspec index a73a5945..c868d47a 100644 --- a/tzinfo.gemspec +++ b/tzinfo.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'tzinfo' - s.version = '1.2.7' + s.version = '1.2.8' s.summary = 'Daylight savings aware timezone library' s.description = 'TZInfo provides daylight savings aware transformations between times in different time zones.' s.author = 'Philip Ross' From 85d6cf62db6d836fb51ddcda7d458ee3956537ee Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 8 Nov 2020 12:21:45 +0000 Subject: [PATCH 57/81] [ci skip] Add issue number reference. --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index dfe39a57..3573d2d8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,7 @@ Version 1.2.8 - 8-Nov-2020 default by zic version 2020b and later. The POSIX-style TZ string is now used calculate DST transition times after the final defined transition in the file. The 64-bit section is now always used regardless of whether Time has support - for 64-bit times. + for 64-bit times. #120. * Rubinius is no longer supported. From 246d5ba82ebb860d109db251f0df3cac7c8d5a0c Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 14 Dec 2020 22:32:55 +0000 Subject: [PATCH 58/81] Ignore generated transations that do not change the offset. Required to handle Africa/Casablanca in 2018e. The last defined transitions are: At 2037-03-29 02:00Z change to WEST UTC+1 At 2037-10-04 02:00Z change to WET UTC+0 The rules define the end of DST to be at 03:00 local time on the last Sunday of October (2037-10-31). This later transition needs to be ignored. Resolves #123 --- lib/tzinfo/zoneinfo_timezone_info.rb | 9 ++- test/tc_zoneinfo_timezone_info.rb | 84 +++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/lib/tzinfo/zoneinfo_timezone_info.rb b/lib/tzinfo/zoneinfo_timezone_info.rb index 4f28a19c..a2fa9954 100644 --- a/lib/tzinfo/zoneinfo_timezone_info.rb +++ b/lib/tzinfo/zoneinfo_timezone_info.rb @@ -313,8 +313,13 @@ def apply_rules_with_transitions(file, transitions, offsets, first_offset_index, last_year = (Time.at(last_defined[:at]).utc + previous_offset[:utc_total_offset]).year if last_year <= GENERATE_UP_TO - generated = rules.transitions(last_year).find_all {|t| t.at > last_defined[:at] } + - (last_year + 1).upto(GENERATE_UP_TO).map {|y| rules.transitions(y) }.flatten + last_defined_offset = offsets[last_defined[:offset]] + + generated = rules.transitions(last_year).find_all do |t| + t.at > last_defined[:at] && !offset_matches_rule?(last_defined_offset, t.offset) + end + + generated += (last_year + 1).upto(GENERATE_UP_TO).map {|y| rules.transitions(y) }.flatten unless generated.empty? transitions[-1] = validate_and_fix_last_defined_transition_offset(file, offsets, last_defined, generated[0].previous_offset) diff --git a/test/tc_zoneinfo_timezone_info.rb b/test/tc_zoneinfo_timezone_info.rb index c8c47a07..7d66282e 100644 --- a/test/tc_zoneinfo_timezone_info.rb +++ b/test/tc_zoneinfo_timezone_info.rb @@ -1895,7 +1895,18 @@ def test_load_tz_string_corrects_offset_of_final_transition end end - def test_load_tz_string_specifies_transition_to_offset_of_final_transition_same_year + def test_load_tz_string_specifies_transition_to_offset_of_final_transition_same_year_skip_dst_start + # TZInfo v1.2.8 considered this to be an error. However, this is a valid + # situation with Africa/Casablanca in 2018e. + # + # The last defined transitions are: + # At 2037-03-29 02:00Z change to WEST UTC+1 + # At 2037-10-04 02:00Z change to WET UTC+0 + # + # The rules define the end of DST to be at 03:00 local time on the last + # Sunday of October (2037-10-31). This later transition needs to be + # ignored. + offsets = [ {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, @@ -1916,9 +1927,76 @@ def test_load_tz_string_specifies_transition_to_offset_of_final_transition_same_ JulianDayOfYearTransitionRule.new(300, 7200) ) + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + tzif_test(offsets, transitions, :rules => rules) do |path, format| - error = assert_raises(InvalidZoneinfoFile) { ZoneinfoTimezoneInfo.new('Invalid/Offset', path, @posix_tz_parser) } - assert_equal("The first offset indicated by the POSIX-style TZ string did not match the final defined offset in file '#{path}'.", error.message) + info = ZoneinfoTimezoneInfo.new('Ignore/Std', path, @posix_tz_parser) + assert_equal('Ignore/Std', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, info) + assert_period(:XST, 7200, 0, false, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, Time.utc(1981, 4, 10, 1, 0, 0) - 7200, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(1981, 4, 10, 1, 0, 0) - 7200, Time.utc(1981, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(1981, 10, 27, 2, 0, 0) - 10800, Time.utc(1982, 4, 10, 1, 0, 0) - 7200, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(1982, 4, 10, 1, 0, 0) - 7200, Time.utc(1982, 10, 27, 2, 0, 0) - 10800, info) + + 1983.upto(generate_up_to).each do |year| + assert_period(:XST, 7200, 0, false, Time.utc(year - 1, 10, 27, 2, 0, 0) - 10800, Time.utc(year, 4, 11, 1, 0, 0) - 7200, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 4, 11, 1, 0, 0) - 7200, Time.utc(year, 10, 27, 2, 0, 0) - 10800, info) + end + + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 27, 2, 0, 0) - 10800, nil, info) + end + end + + def test_load_tz_string_specifies_transition_to_offset_of_final_transition_same_year_skip_dst_end + # TZInfo v1.2.8 considered this to be an error. However, this is a valid + # situation with Africa/Casablanca in 2018e. + # + # The last defined transitions are: + # At 2037-03-29 02:00Z change to WEST UTC+1 + # At 2037-10-04 02:00Z change to WET UTC+0 + # + # The rules define the end of DST to be at 03:00 local time on the last + # Sunday of October (2037-10-31). This later transition needs to be + # ignored. + + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'} + ] + + transitions = [ + {:at => Time.utc(1971, 1, 2, 2, 0, 0) - 7142, :offset_index => 1}, + {:at => Time.utc(1981, 4, 10, 1, 0, 0) - 7200, :offset_index => 2}, + {:at => Time.utc(1981, 10, 27, 2, 0, 0) - 10800, :offset_index => 1} + ] + + rules = AnnualRules.new( + TimezoneOffset.new(7200, 0, 'XST'), + TimezoneOffset.new(7200, 3600, 'XDT'), + JulianDayOfYearTransitionRule.new(100, 3600), + JulianDayOfYearTransitionRule.new(301, 7200) + ) + + generate_up_to = ZoneinfoTimezoneInfo::GENERATE_UP_TO + + tzif_test(offsets, transitions, :rules => rules) do |path, format| + info = ZoneinfoTimezoneInfo.new('Ignore/Std', path, @posix_tz_parser) + assert_equal('Ignore/Std', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, info) + assert_period(:XST, 7200, 0, false, Time.utc(1971, 1, 2, 2, 0, 0) - 7142, Time.utc(1981, 4, 10, 1, 0, 0) - 7200, info) + assert_period(:XDT, 7200, 3600, true, Time.utc(1981, 4, 10, 1, 0, 0) - 7200, Time.utc(1981, 10, 27, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(1981, 10, 27, 2, 0, 0) - 10800, Time.utc(1982, 4, 10, 1, 0, 0) - 7200, info) + + 1982.upto(generate_up_to - 1).each do |year| + assert_period(:XDT, 7200, 3600, true, Time.utc(year, 4, 10, 1, 0, 0) - 7200, Time.utc(year, 10, 28, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(year, 10, 28, 2, 0, 0) - 10800, Time.utc(year + 1, 4, 10, 1, 0, 0) - 7200, info) + end + + assert_period(:XDT, 7200, 3600, true, Time.utc(generate_up_to, 4, 10, 1, 0, 0) - 7200, Time.utc(generate_up_to, 10, 28, 2, 0, 0) - 10800, info) + assert_period(:XST, 7200, 0, false, Time.utc(generate_up_to, 10, 28, 2, 0, 0) - 10800, nil, info) end end From 1f5f78ac5392d986549f8a1d02074760d9746f88 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 14 Dec 2020 22:42:38 +0000 Subject: [PATCH 59/81] Update to JRuby 9.2.14.0. --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ce031323..3b398218 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,7 +46,7 @@ jobs: dist: trusty - rvm: jruby-9.1.17.0 dist: focal - - rvm: jruby-9.2.13.0 + - rvm: jruby-9.2.14.0 dist: focal - rvm: jruby-head dist: focal diff --git a/appveyor.yml b/appveyor.yml index 60526e2d..e58c0bae 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -67,7 +67,7 @@ environment: JRUBY_VERSION: 9.1.17.0 - RUBY_ENGINE: jruby - JRUBY_VERSION: 9.2.13.0 + JRUBY_VERSION: 9.2.14.0 install: - if not exist vendor mkdir vendor From fa848cc859b4e919f9604b37fd39a84294b4eb54 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 15 Dec 2020 19:41:56 +0000 Subject: [PATCH 60/81] Add 1.8.7-head, 1.9.2-p330, jruby-9.0.5.0 and ree to allow failures. These Ruby versions can no longer connect to https://rubygems.org. These versions either fail to verify the hostname (and disabling verification doesn't work) or fail to establish a TLS connection. https://travis-ci.com/github/tzinfo/tzinfo/jobs/459919362 https://travis-ci.com/github/tzinfo/tzinfo/jobs/459919363 https://travis-ci.com/github/tzinfo/tzinfo/jobs/459919375 https://travis-ci.com/github/tzinfo/tzinfo/jobs/459919378 --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 3b398218..38446fdf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,5 +53,9 @@ jobs: - rvm: ree dist: trusty allow_failures: + - rvm: 1.8.7-head + - rvm: 1.9.2-p330 - rvm: ruby-head + - rvm: jruby-9.0.5.0 - rvm: jruby-head + - rvm: ree From 047a8995c5689cddb2b3fc1658dad66bc89efa55 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 15 Dec 2020 20:07:51 +0000 Subject: [PATCH 61/81] Download GlobalSign Root CA - R3 for use with older Ruby versions. The rubygems.org certificate is now issued by GlobalSign Root CA - R3. This isn't included in the RubyGems version included in older Ruby versions. --- appveyor.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index e58c0bae..26d88c91 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,27 +15,35 @@ environment: - RUBY_ENGINE: ruby RUBY_VERSION: 200 + UPDATE_SSL_CA_CERT: 1 - RUBY_ENGINE: ruby RUBY_VERSION: 200-x64 + UPDATE_SSL_CA_CERT: 1 - RUBY_ENGINE: ruby RUBY_VERSION: 21 + UPDATE_SSL_CA_CERT: 1 - RUBY_ENGINE: ruby RUBY_VERSION: 21-x64 + UPDATE_SSL_CA_CERT: 1 - RUBY_ENGINE: ruby RUBY_VERSION: 22 + UPDATE_SSL_CA_CERT: 1 - RUBY_ENGINE: ruby RUBY_VERSION: 22-x64 + UPDATE_SSL_CA_CERT: 1 - RUBY_ENGINE: ruby RUBY_VERSION: 23 + UPDATE_SSL_CA_CERT: 1 - RUBY_ENGINE: ruby RUBY_VERSION: 23-x64 + UPDATE_SSL_CA_CERT: 1 - RUBY_ENGINE: ruby RUBY_VERSION: 24 @@ -81,6 +89,8 @@ install: - if v%RUBY_VERSION%==v193 copy /Y vendor\*eay32.dll C:\Ruby193\bin - if v%RUBY_VERSION%==v193 copy /Y vendor\openssl.so C:\Ruby193\lib\ruby\1.9.1\i386-mingw32 - if v%RUBY_VERSION%==v193 copy /Y vendor\ca-bundle.pem C:\Ruby193\lib\ruby\1.9.1\rubygems\ssl_certs + - if b%UPDATE_SSL_CA_CERT%==b1 appveyor DownloadFile https://github.com/rubygems/rubygems/raw/v3.2.1/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem -FileName vendor\GlobalSignRootCA_R3.pem + - if b%UPDATE_SSL_CA_CERT%==b1 set SSL_CERT_FILE=C:\projects\tzinfo\vendor\GlobalSignRootCA_R3.pem - if %RUBY_ENGINE%==ruby set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% - bundle config --local path vendor/bundle - bundle update From 814d1a1d8acedc334a33fee98a9babe096a66958 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 15 Dec 2020 20:10:08 +0000 Subject: [PATCH 62/81] Remove JRuby 9.0.5.0. The included SSL library is no longer capable of connecting to https://rubygems.org. --- appveyor.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 26d88c91..41be5075 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -67,10 +67,6 @@ environment: JRUBY_VERSION: 1.7.27 JRUBY_BUNDLER_VERSION: "~> 1.17" - - RUBY_ENGINE: jruby - JRUBY_VERSION: 9.0.5.0 - JRUBY_BUNDLER_VERSION: "~> 1.17" - - RUBY_ENGINE: jruby JRUBY_VERSION: 9.1.17.0 From f1150e9b9eb2a890002d0ade991824c08d7b20c1 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Wed, 16 Dec 2020 20:31:28 +0000 Subject: [PATCH 63/81] Preparing v1.2.9. --- CHANGES.md | 9 +++++++++ tzinfo.gemspec | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 3573d2d8..4705ec2c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,12 @@ +Version 1.2.9 - 16-Dec-2020 +--------------------------- + +* Fixed an incorrect InvalidTimezoneIdentifier exception raised when loading a + zoneinfo file that includes rules specifying an additional transition to the + final defined offset (for example, Africa/Casablanca in version 2018e of the + Time Zone Database). #123. + + Version 1.2.8 - 8-Nov-2020 -------------------------- diff --git a/tzinfo.gemspec b/tzinfo.gemspec index c868d47a..ed748285 100644 --- a/tzinfo.gemspec +++ b/tzinfo.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'tzinfo' - s.version = '1.2.8' + s.version = '1.2.9' s.summary = 'Daylight savings aware timezone library' s.description = 'TZInfo provides daylight savings aware transformations between times in different time zones.' s.author = 'Philip Ross' From a3e65b0256b8e73800bed0654529c7243ea9fd19 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 7 Jan 2021 20:53:40 +0000 Subject: [PATCH 64/81] Switch to GitHub Actions for CI. --- .github/workflows/tests.yml | 67 +++++++++++++++++++++++ .travis.yml | 61 --------------------- README.md | 2 +- appveyor.yml | 105 ------------------------------------ 4 files changed, 68 insertions(+), 167 deletions(-) create mode 100644 .github/workflows/tests.yml delete mode 100644 .travis.yml delete mode 100644 appveyor.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..fcec27fc --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,67 @@ +name: Tests + +on: [push, pull_request] + +jobs: + test: + name: ${{ matrix.ruby }} on ${{ matrix.os }}${{ matrix.name_suffix || '' }} + strategy: + matrix: + os: [ubuntu-20.04, windows-2019] + ruby: ['1.8.7', '1.9.3', '2.0', '2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', jruby-1.7, jruby-9.0, jruby-9.1, jruby-9.2] + architecture: [default] + name_suffix: [''] + experimental: [false] + exclude: + - os: windows-2019 + ruby: '1.8.7' + - os: windows-2019 + ruby: jruby-1.7 + include: + - os: windows-2019 + ruby: '2.0' + achitecture: x86 + name_suffix: ' (x86)' + experimental: false + - os: ubuntu-20.04 + ruby: head + achitecture: default + name_suffix: ' (experimental)' + experimental: true + - os: ubuntu-20.04 + ruby: jruby-head + achitecture: default + name_suffix: ' (experimental)' + experimental: true + - os: windows-2019 + ruby: head + achitecture: default + name_suffix: ' (experimental)' + experimental: true + - os: windows-2019 + ruby: jruby-head + achitecture: default + name_suffix: ' (experimental)' + experimental: true + fail-fast: false + runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.experimental }} + steps: + - uses: actions/checkout@v2 + - if: startsWith(matrix.ruby, '1.') || startsWith(matrix.ruby, '2.0') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0') + uses: philr/setup-ruby@legacy-v1 + with: + ruby-version: ${{ matrix.ruby }} + architecture: ${{ matrix.architecture }} + bundler-cache: true + - if: ${{ !(startsWith(matrix.ruby, '1.') || startsWith(matrix.ruby, '2.0') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0')) }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - run: ruby --version + - run: gem --version + - run: bundle --version + - run: bundle exec rake test + env: + TESTOPTS: --verbose diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 38446fdf..00000000 --- a/.travis.yml +++ /dev/null @@ -1,61 +0,0 @@ -language: ruby -before_install: - - if [[ $TRAVIS_RUBY_VERSION =~ ^jruby- && ! $TRAVIS_RUBY_VERSION =~ ^jruby-(1|9\.[01]\.) ]]; then export JRUBY_OPTS=`echo "$JRUBY_OPTS" | sed -E -e 's/--((no)?client|server)//g'`; fi - - if [[ $TRAVIS_RUBY_VERSION =~ ^jruby- ]]; then echo "JRUBY_OPTS=$JRUBY_OPTS"; fi - - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-(1|9\.0))\.|ree$) ]]; then gem install rubygems-update --version '~> 2.7' --no-document && update_rubygems; else gem update --system --no-document; fi - - gem --version - - if [[ $TRAVIS_RUBY_VERSION =~ ^((1|2\.[012]|jruby-(1|9\.0))\.|ree$) ]]; then gem install bundler --version '~> 1.17' --no-document; else gem install bundler --no-document; fi - - bundle --version -before_script: - - bundle update -cache: bundler -env: - global: - - TESTOPTS=--verbose -jobs: - include: - - rvm: 1.8.7-head - dist: trusty - - rvm: 1.9.2-p330 - dist: trusty - - rvm: 1.9.3-p551 - dist: trusty - - rvm: 2.0.0-p648 - dist: trusty - - rvm: 2.1.10 - dist: trusty - - rvm: 2.2.10 - dist: trusty - - rvm: 2.3.8 - dist: bionic - - rvm: 2.4.10 - dist: focal - - rvm: 2.5.8 - dist: focal - - rvm: 2.6.6 - dist: focal - - rvm: 2.7.2 - dist: focal - - rvm: ruby-head - dist: focal - - rvm: jruby-18mode - dist: trusty - - rvm: jruby-1.7.27 - dist: trusty - - rvm: jruby-9.0.5.0 - dist: trusty - - rvm: jruby-9.1.17.0 - dist: focal - - rvm: jruby-9.2.14.0 - dist: focal - - rvm: jruby-head - dist: focal - - rvm: ree - dist: trusty - allow_failures: - - rvm: 1.8.7-head - - rvm: 1.9.2-p330 - - rvm: ruby-head - - rvm: jruby-9.0.5.0 - - rvm: jruby-head - - rvm: ree diff --git a/README.md b/README.md index 50766f84..f5abb249 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ TZInfo - Ruby Timezone Library ============================== -[![RubyGems](https://img.shields.io/gem/v/tzinfo)](https://rubygems.org/gems/tzinfo) [![Travis CI Build](https://img.shields.io/travis/com/tzinfo/tzinfo/1.2?logo=travis)](https://travis-ci.com/tzinfo/tzinfo) [![AppVeyor Build](https://img.shields.io/appveyor/build/philr/tzinfo/1.2?logo=appveyor)](https://ci.appveyor.com/project/philr/tzinfo/branch/1.2) +[![RubyGems](https://img.shields.io/gem/v/tzinfo?label=Gem)](https://rubygems.org/gems/tzinfo) [![Tests](https://github.com/tzinfo/tzinfo/workflows/Tests/badge.svg?branch=1.2&event=push)](https://github.com/tzinfo/tzinfo/actions?query=workflow%3ATests+branch%3A1.2+event%3Apush) [TZInfo](https://tzinfo.github.io) provides daylight savings aware transformations between times in different timezones. diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 41be5075..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,105 +0,0 @@ -version: "{build}-{branch}" - -cache: - - vendor/bundle - -environment: - TESTOPTS: --verbose - - matrix: - # Ruby 1.8.7 isn't available on AppVeyor. - - - RUBY_ENGINE: ruby - RUBY_VERSION: 193 - SSL_CERT_FILE: C:\Ruby193\lib\ruby\1.9.1\rubygems\ssl_certs\ca-bundle.pem - - - RUBY_ENGINE: ruby - RUBY_VERSION: 200 - UPDATE_SSL_CA_CERT: 1 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 200-x64 - UPDATE_SSL_CA_CERT: 1 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 21 - UPDATE_SSL_CA_CERT: 1 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 21-x64 - UPDATE_SSL_CA_CERT: 1 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 22 - UPDATE_SSL_CA_CERT: 1 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 22-x64 - UPDATE_SSL_CA_CERT: 1 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 23 - UPDATE_SSL_CA_CERT: 1 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 23-x64 - UPDATE_SSL_CA_CERT: 1 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 24 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 24-x64 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 25 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 25-x64 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 26 - - - RUBY_ENGINE: ruby - RUBY_VERSION: 26-x64 - - - RUBY_ENGINE: jruby - JRUBY_VERSION: 1.7.27 - JRUBY_BUNDLER_VERSION: "~> 1.17" - - - RUBY_ENGINE: jruby - JRUBY_VERSION: 9.1.17.0 - - - RUBY_ENGINE: jruby - JRUBY_VERSION: 9.2.14.0 - -install: - - if not exist vendor mkdir vendor - - if %RUBY_ENGINE%==jruby appveyor DownloadFile https://repo1.maven.org/maven2/org/jruby/jruby-dist/%JRUBY_VERSION%/jruby-dist-%JRUBY_VERSION%-bin.zip -FileName vendor\jruby-dist-%JRUBY_VERSION%-bin.zip - - if %RUBY_ENGINE%==jruby 7z x vendor\jruby-dist-%JRUBY_VERSION%-bin.zip -ovendor -y - - if %RUBY_ENGINE%==jruby set PATH=C:\projects\tzinfo\vendor\jruby-%JRUBY_VERSION%\bin;%PATH% - - if %RUBY_ENGINE%==jruby if defined JRUBY_BUNDLER_VERSION gem install bundler --version "%JRUBY_BUNDLER_VERSION%" --no-document - - if %RUBY_ENGINE%==jruby if not defined JRUBY_BUNDLER_VERSION gem install bundler --no-document - - if v%RUBY_VERSION%==v193 appveyor DownloadFile https://github.com/philr/rubyinstaller/releases/download/1.9.3-p551-openssl-tls-1.1-1.2/ruby-1.9.3-p551-i386-mingw32.7z -FileName vendor\ruby-1.9.3-p551-i386-mingw32.7z - - if v%RUBY_VERSION%==v193 7z e vendor\ruby-1.9.3-p551-i386-mingw32.7z -ovendor ruby-1.9.3-p551-i386-mingw32\bin\libeay32.dll ruby-1.9.3-p551-i386-mingw32\bin\ssleay32.dll ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\i386-mingw32\openssl.so ruby-1.9.3-p551-i386-mingw32\lib\ruby\1.9.1\rubygems\ssl_certs\ca-bundle.pem - - if v%RUBY_VERSION%==v193 copy /Y vendor\*eay32.dll C:\Ruby193\bin - - if v%RUBY_VERSION%==v193 copy /Y vendor\openssl.so C:\Ruby193\lib\ruby\1.9.1\i386-mingw32 - - if v%RUBY_VERSION%==v193 copy /Y vendor\ca-bundle.pem C:\Ruby193\lib\ruby\1.9.1\rubygems\ssl_certs - - if b%UPDATE_SSL_CA_CERT%==b1 appveyor DownloadFile https://github.com/rubygems/rubygems/raw/v3.2.1/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA_R3.pem -FileName vendor\GlobalSignRootCA_R3.pem - - if b%UPDATE_SSL_CA_CERT%==b1 set SSL_CERT_FILE=C:\projects\tzinfo\vendor\GlobalSignRootCA_R3.pem - - if %RUBY_ENGINE%==ruby set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% - - bundle config --local path vendor/bundle - - bundle update - -build: off - -before_test: - - "%RUBY_ENGINE% -v" - - gem -v - - bundle -v - -test_script: - - bundle exec rake test - -after_test: - - bundle clean From a8fdfd87878ca99d4b1ae884f5796c310963e4c0 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 7 Jan 2021 20:56:02 +0000 Subject: [PATCH 65/81] Ignore warnings from sub-process tests. From JRuby 1.7 and 9.0 when running with modern versions of Java and from Bundler 1 when running on Ruby 3.0. --- test/test_utils.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/test_utils.rb b/test/test_utils.rb index c97facf4..389d935a 100644 --- a/test/test_utils.rb +++ b/test/test_utils.rb @@ -153,6 +153,22 @@ def assert_sub_process_returns(expected_lines, code, extra_load_path = [], requi actual_lines = process.readlines actual_lines = actual_lines.collect {|l| l.chomp} + + # Ignore warnings from JRuby 1.7 and 9.0 on modern versions of Java: + # https://github.com/tzinfo/tzinfo/runs/1664655982#step:8:1893 + # + # Ignore untaint deprecation warnings from Bundler 1 on Ruby 3.0. + actual_lines = actual_lines.reject do |l| + l.start_with?('unsupported Java version') || + l.start_with?('WARNING: An illegal reflective access operation has occurred') || + l.start_with?('WARNING: Illegal reflective access by') || + l.start_with?('WARNING: Please consider reporting this to the maintainers of') || + l.start_with?('WARNING: All illegal access operations will be denied in a future release') || + l.start_with?('WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations') || + l.start_with?('io/console on JRuby shells out to stty for most operations') || + l =~ /\/bundler-1\..*\/lib\/bundler\/.*\.rb:\d+: warning: (Object|Pathname)#untaint is deprecated and will be removed in Ruby 3\.2\.\z/ + end + assert_equal(expected_lines, actual_lines) end end From 104fe91b6a8552471b882d0d1b1ee0d27531accd Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Thu, 7 Jan 2021 22:46:36 +0000 Subject: [PATCH 66/81] Add RubyGems logo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f5abb249..1b84f4ee 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ TZInfo - Ruby Timezone Library ============================== -[![RubyGems](https://img.shields.io/gem/v/tzinfo?label=Gem)](https://rubygems.org/gems/tzinfo) [![Tests](https://github.com/tzinfo/tzinfo/workflows/Tests/badge.svg?branch=1.2&event=push)](https://github.com/tzinfo/tzinfo/actions?query=workflow%3ATests+branch%3A1.2+event%3Apush) +[![RubyGems](https://img.shields.io/gem/v/tzinfo?logo=rubygems&label=Gem)](https://rubygems.org/gems/tzinfo) [![Tests](https://github.com/tzinfo/tzinfo/workflows/Tests/badge.svg?branch=1.2&event=push)](https://github.com/tzinfo/tzinfo/actions?query=workflow%3ATests+branch%3A1.2+event%3Apush) [TZInfo](https://tzinfo.github.io) provides daylight savings aware transformations between times in different timezones. From a79d7e9fbfb3ef7e4b2c9bb7f5ba19263506e82f Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 11 Jan 2021 20:47:28 +0000 Subject: [PATCH 67/81] Remove an unnecessary or. The name_suffix property is always set. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fcec27fc..d5d1139d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: test: - name: ${{ matrix.ruby }} on ${{ matrix.os }}${{ matrix.name_suffix || '' }} + name: ${{ matrix.ruby }} on ${{ matrix.os }}${{ matrix.name_suffix }} strategy: matrix: os: [ubuntu-20.04, windows-2019] From fa8bd6d2aec6f920c66d8aa808c689168a49a2f7 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Mon, 11 Jan 2021 20:48:17 +0000 Subject: [PATCH 68/81] Convert from CRLF to LF line endings. --- .github/workflows/tests.yml | 134 ++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d5d1139d..83bd3160 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,67 +1,67 @@ -name: Tests - -on: [push, pull_request] - -jobs: - test: - name: ${{ matrix.ruby }} on ${{ matrix.os }}${{ matrix.name_suffix }} - strategy: - matrix: - os: [ubuntu-20.04, windows-2019] - ruby: ['1.8.7', '1.9.3', '2.0', '2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', jruby-1.7, jruby-9.0, jruby-9.1, jruby-9.2] - architecture: [default] - name_suffix: [''] - experimental: [false] - exclude: - - os: windows-2019 - ruby: '1.8.7' - - os: windows-2019 - ruby: jruby-1.7 - include: - - os: windows-2019 - ruby: '2.0' - achitecture: x86 - name_suffix: ' (x86)' - experimental: false - - os: ubuntu-20.04 - ruby: head - achitecture: default - name_suffix: ' (experimental)' - experimental: true - - os: ubuntu-20.04 - ruby: jruby-head - achitecture: default - name_suffix: ' (experimental)' - experimental: true - - os: windows-2019 - ruby: head - achitecture: default - name_suffix: ' (experimental)' - experimental: true - - os: windows-2019 - ruby: jruby-head - achitecture: default - name_suffix: ' (experimental)' - experimental: true - fail-fast: false - runs-on: ${{ matrix.os }} - continue-on-error: ${{ matrix.experimental }} - steps: - - uses: actions/checkout@v2 - - if: startsWith(matrix.ruby, '1.') || startsWith(matrix.ruby, '2.0') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0') - uses: philr/setup-ruby@legacy-v1 - with: - ruby-version: ${{ matrix.ruby }} - architecture: ${{ matrix.architecture }} - bundler-cache: true - - if: ${{ !(startsWith(matrix.ruby, '1.') || startsWith(matrix.ruby, '2.0') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0')) }} - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - run: ruby --version - - run: gem --version - - run: bundle --version - - run: bundle exec rake test - env: - TESTOPTS: --verbose +name: Tests + +on: [push, pull_request] + +jobs: + test: + name: ${{ matrix.ruby }} on ${{ matrix.os }}${{ matrix.name_suffix }} + strategy: + matrix: + os: [ubuntu-20.04, windows-2019] + ruby: ['1.8.7', '1.9.3', '2.0', '2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', jruby-1.7, jruby-9.0, jruby-9.1, jruby-9.2] + architecture: [default] + name_suffix: [''] + experimental: [false] + exclude: + - os: windows-2019 + ruby: '1.8.7' + - os: windows-2019 + ruby: jruby-1.7 + include: + - os: windows-2019 + ruby: '2.0' + achitecture: x86 + name_suffix: ' (x86)' + experimental: false + - os: ubuntu-20.04 + ruby: head + achitecture: default + name_suffix: ' (experimental)' + experimental: true + - os: ubuntu-20.04 + ruby: jruby-head + achitecture: default + name_suffix: ' (experimental)' + experimental: true + - os: windows-2019 + ruby: head + achitecture: default + name_suffix: ' (experimental)' + experimental: true + - os: windows-2019 + ruby: jruby-head + achitecture: default + name_suffix: ' (experimental)' + experimental: true + fail-fast: false + runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.experimental }} + steps: + - uses: actions/checkout@v2 + - if: startsWith(matrix.ruby, '1.') || startsWith(matrix.ruby, '2.0') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0') + uses: philr/setup-ruby@legacy-v1 + with: + ruby-version: ${{ matrix.ruby }} + architecture: ${{ matrix.architecture }} + bundler-cache: true + - if: ${{ !(startsWith(matrix.ruby, '1.') || startsWith(matrix.ruby, '2.0') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0')) }} + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - run: ruby --version + - run: gem --version + - run: bundle --version + - run: bundle exec rake test + env: + TESTOPTS: --verbose From dc5b27e45799419e32bdcecddf79e9c0c764f930 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Fri, 29 Oct 2021 20:25:01 +0100 Subject: [PATCH 69/81] Update copyright years. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 6e74e290..cd3e076a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2005-2020 Philip Ross +Copyright (c) 2005-2021 Philip Ross Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 34bcff8155fae7efb46bfce390b083d3384548fd Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Fri, 29 Oct 2021 20:19:02 +0100 Subject: [PATCH 70/81] Fix a typo. This would have been causing the Ruby 2.0.0 x86 Windows test to run using the 64-bit build of Ruby. --- .github/workflows/tests.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 83bd3160..b7019782 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,27 +20,27 @@ jobs: include: - os: windows-2019 ruby: '2.0' - achitecture: x86 + architecture: x86 name_suffix: ' (x86)' experimental: false - os: ubuntu-20.04 ruby: head - achitecture: default + architecture: default name_suffix: ' (experimental)' experimental: true - os: ubuntu-20.04 ruby: jruby-head - achitecture: default + architecture: default name_suffix: ' (experimental)' experimental: true - os: windows-2019 ruby: head - achitecture: default + architecture: default name_suffix: ' (experimental)' experimental: true - os: windows-2019 ruby: jruby-head - achitecture: default + architecture: default name_suffix: ' (experimental)' experimental: true fail-fast: false From 2a60712e8aa4a3dfb2cf6afda45bd29628d49a39 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Fri, 29 Oct 2021 20:24:07 +0100 Subject: [PATCH 71/81] Switch to ruby/setup-ruby for 1.9.3 (non-Windows) and 2.0.0 (x64). Support has now been added for these legacy versions. --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b7019782..a13a8292 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -48,13 +48,13 @@ jobs: continue-on-error: ${{ matrix.experimental }} steps: - uses: actions/checkout@v2 - - if: startsWith(matrix.ruby, '1.') || startsWith(matrix.ruby, '2.0') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0') + - if: startsWith(matrix.ruby, '1.8') || (startsWith(matrix.ruby, '1.9') && startsWith(matrix.os, 'windows')) || (startsWith(matrix.ruby, '2.0') && startsWith(matrix.os, 'windows') && matrix.architecture == 'x86') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0') uses: philr/setup-ruby@legacy-v1 with: ruby-version: ${{ matrix.ruby }} architecture: ${{ matrix.architecture }} bundler-cache: true - - if: ${{ !(startsWith(matrix.ruby, '1.') || startsWith(matrix.ruby, '2.0') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0')) }} + - if: ${{ !(startsWith(matrix.ruby, '1.8') || (startsWith(matrix.ruby, '1.9') && startsWith(matrix.os, 'windows')) || (startsWith(matrix.ruby, '2.0') && startsWith(matrix.os, 'windows') && matrix.architecture == 'x86') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0')) }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} From 07b5941668423f4eb1aaeaf04d67837519d08fac Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Fri, 29 Oct 2021 20:24:44 +0100 Subject: [PATCH 72/81] Add JRuby 9.3. --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a13a8292..ebd90cb8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ubuntu-20.04, windows-2019] - ruby: ['1.8.7', '1.9.3', '2.0', '2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', jruby-1.7, jruby-9.0, jruby-9.1, jruby-9.2] + ruby: ['1.8.7', '1.9.3', '2.0', '2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', jruby-1.7, jruby-9.0, jruby-9.1, jruby-9.2, jruby-9.3] architecture: [default] name_suffix: [''] experimental: [false] From 9905ca93abf7bf3e387bd592406e403cd18334c7 Mon Sep 17 00:00:00 2001 From: Tobias Kraze Date: Mon, 11 Jul 2022 09:54:12 +0200 Subject: [PATCH 73/81] Fix directory traversal in Timezone.get when using Ruby data source --- lib/tzinfo/ruby_data_source.rb | 2 +- test/assets/payload.rb | 1 + test/tc_ruby_data_source.rb | 6 ++++++ test/tc_timezone.rb | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 test/assets/payload.rb diff --git a/lib/tzinfo/ruby_data_source.rb b/lib/tzinfo/ruby_data_source.rb index b5a67524..b8a34e78 100644 --- a/lib/tzinfo/ruby_data_source.rb +++ b/lib/tzinfo/ruby_data_source.rb @@ -38,7 +38,7 @@ def initialize # Raises InvalidTimezoneIdentifier if the timezone is not found or the # identifier is invalid. def load_timezone_info(identifier) - raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/ + raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~ /\A[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*\z/ identifier = identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__') diff --git a/test/assets/payload.rb b/test/assets/payload.rb new file mode 100644 index 00000000..7ad83fc9 --- /dev/null +++ b/test/assets/payload.rb @@ -0,0 +1 @@ +raise 'This should never be executed' diff --git a/test/tc_ruby_data_source.rb b/test/tc_ruby_data_source.rb index 790dd8eb..9bd069a4 100644 --- a/test/tc_ruby_data_source.rb +++ b/test/tc_ruby_data_source.rb @@ -51,6 +51,12 @@ def test_load_timezone_info_invalid @data_source.load_timezone_info('../Definitions/UTC') end end + + def test_load_timezone_info_directory_traversal + test_data_depth = TZINFO_TEST_DATA_DIR.scan('/').size + payload_path = File.join(TESTS_DIR, 'assets', 'payload') + assert_raises(InvalidTimezoneIdentifier) { Timezone.get("foo\n#{'/..' * (test_data_depth + 4)}#{payload_path}") } + end def test_load_timezone_info_nil assert_raises(InvalidTimezoneIdentifier) do diff --git a/test/tc_timezone.rb b/test/tc_timezone.rb index 0dc06111..5f4614d3 100644 --- a/test/tc_timezone.rb +++ b/test/tc_timezone.rb @@ -213,7 +213,7 @@ def test_get_not_exist end def test_get_invalid - assert_raises(InvalidTimezoneIdentifier) { Timezone.get('../Definitions/UTC') } + assert_raises(InvalidTimezoneIdentifier) { Timezone.get('../definitions/UTC') } end def test_get_nil From 6bd7a5191d9c1ca48a97420652460b8c4dec865d Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 17 Jul 2022 12:18:13 +0100 Subject: [PATCH 74/81] Update copyright years. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index cd3e076a..232a0e90 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2005-2021 Philip Ross +Copyright (c) 2005-2022 Philip Ross Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 17fc9e1fa918c24ca8c1915419d4cc15f56b6729 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 17 Jul 2022 12:00:44 +0100 Subject: [PATCH 75/81] Workaround for 'Permission denied - NUL' errors with JRuby on Windows. https://github.com/ruby/setup-ruby/issues/339 https://bugs.openjdk.org/browse/JDK-8285445 Can be removed once the July 2022 JDK release is available. (cherry picked from commit 5d53b5923e9a62db08bc391cbe518412ccb90b4c) --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ebd90cb8..d66cd555 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,6 +46,8 @@ jobs: fail-fast: false runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} + env: + JAVA_OPTS: -Djdk.io.File.enableADS=true steps: - uses: actions/checkout@v2 - if: startsWith(matrix.ruby, '1.8') || (startsWith(matrix.ruby, '1.9') && startsWith(matrix.os, 'windows')) || (startsWith(matrix.ruby, '2.0') && startsWith(matrix.os, 'windows') && matrix.architecture == 'x86') || startsWith(matrix.ruby, 'jruby-1.7') || startsWith(matrix.ruby, 'jruby-9.0') From 5e9f99086f820573eb43ffe242e074b9a8295027 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 17 Jul 2022 12:38:21 +0100 Subject: [PATCH 76/81] Exclude Arch Linux's SECURITY file from the time zone index. Resolves #134 for version 1.2. --- lib/tzinfo/zoneinfo_data_source.rb | 36 +++++++++++++++++++++--------- test/tc_zoneinfo_data_source.rb | 19 ++++++++++++++++ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/lib/tzinfo/zoneinfo_data_source.rb b/lib/tzinfo/zoneinfo_data_source.rb index 3959090f..6b250a13 100644 --- a/lib/tzinfo/zoneinfo_data_source.rb +++ b/lib/tzinfo/zoneinfo_data_source.rb @@ -87,6 +87,30 @@ class ZoneinfoDataSource < DataSource # The default value of ZoneinfoDataSource.alternate_iso3166_tab_search_path. DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH = ['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'].freeze + # File and directories in the top level zoneinfo directory that will be + # excluded from the list of available time zones: + # + # - +VERSION is included on Mac OS X. + # - leapseconds is a list of leap seconds. + # - localtime is the current local timezone (may be a link). + # - posix, posixrules and right are directories containing other versions + # of the zoneinfo files. + # - SECURITY is included in the Arch Linux tzdata package. + # - src is a directory containing the tzdata source included on Solaris. + # - timeconfig is a symlink included on Slackware. + EXCLUDED_FILENAMES = [ + '+VERSION', + 'leapseconds', + 'localtime', + 'posix', + 'posixrules', + 'right', + 'SECURITY', + 'src', + 'timeconfig' + ].freeze + private_constant :EXCLUDED_FILENAMES + # Paths to be checked to find the system zoneinfo directory. @@search_path = DEFAULT_SEARCH_PATH.dup @@ -352,16 +376,8 @@ def find_zoneinfo_dir # identifiers. def load_timezone_index index = [] - - # Ignoring particular files: - # +VERSION is included on Mac OS X. - # leapseconds is a list of leap seconds. - # localtime is the current local timezone (may be a link). - # posix, posixrules and right are directories containing other versions of the zoneinfo files. - # src is a directory containing the tzdata source included on Solaris. - # timeconfig is a symlink included on Slackware. - - enum_timezones(nil, ['+VERSION', 'leapseconds', 'localtime', 'posix', 'posixrules', 'right', 'src', 'timeconfig']) do |identifier| + + enum_timezones(nil, EXCLUDED_FILENAMES) do |identifier| index << identifier end diff --git a/test/tc_zoneinfo_data_source.rb b/test/tc_zoneinfo_data_source.rb index fe517cbb..4fd0d13b 100644 --- a/test/tc_zoneinfo_data_source.rb +++ b/test/tc_zoneinfo_data_source.rb @@ -818,6 +818,25 @@ def test_timezone_identifiers_ignored_src_directory end end + def test_timezone_identifiers_ignored_security_file + # The Arch linux tzdata package includes a file named SECURITY giving + # instructions for reporting security-related bugs. + + Dir.mktmpdir('tzinfo_test') do |dir| + FileUtils.touch(File.join(dir, 'zone.tab')) + FileUtils.touch(File.join(dir, 'iso3166.tab')) + FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(dir, 'EST')) + + File.open(File.join(dir, 'SECURITY'), 'w') do |f| + f.binmode + f.write("Please report any sensitive security-related bugs...\n") + end + + data_source = ZoneinfoDataSource.new(dir) + assert_equal(['EST'], data_source.timezone_identifiers) + end + end + def test_load_country_info info = @data_source.load_country_info('GB') assert_equal('GB', info.code) From 394c381eb6a16eaeafb81196270c363234cf1956 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Sun, 17 Jul 2022 12:42:10 +0100 Subject: [PATCH 77/81] Remove `private_constant` for consistency and compatibility. --- lib/tzinfo/zoneinfo_data_source.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/tzinfo/zoneinfo_data_source.rb b/lib/tzinfo/zoneinfo_data_source.rb index 6b250a13..5cbc6947 100644 --- a/lib/tzinfo/zoneinfo_data_source.rb +++ b/lib/tzinfo/zoneinfo_data_source.rb @@ -109,7 +109,6 @@ class ZoneinfoDataSource < DataSource 'src', 'timeconfig' ].freeze - private_constant :EXCLUDED_FILENAMES # Paths to be checked to find the system zoneinfo directory. @@search_path = DEFAULT_SEARCH_PATH.dup From 9d49bf9728a6d42e55f822c497ebf362e86a65a6 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 19 Jul 2022 18:29:56 +0100 Subject: [PATCH 78/81] Fix relative path loading tests. Use correct parent directory name and case. --- test/tc_ruby_data_source.rb | 2 +- test/tc_zoneinfo_data_source.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tc_ruby_data_source.rb b/test/tc_ruby_data_source.rb index 9bd069a4..23ecdfab 100644 --- a/test/tc_ruby_data_source.rb +++ b/test/tc_ruby_data_source.rb @@ -48,7 +48,7 @@ def test_load_timezone_info_does_not_exist def test_load_timezone_info_invalid assert_raises(InvalidTimezoneIdentifier) do - @data_source.load_timezone_info('../Definitions/UTC') + @data_source.load_timezone_info('../definitions/UTC') end end diff --git a/test/tc_zoneinfo_data_source.rb b/test/tc_zoneinfo_data_source.rb index fe517cbb..e8554292 100644 --- a/test/tc_zoneinfo_data_source.rb +++ b/test/tc_zoneinfo_data_source.rb @@ -374,7 +374,7 @@ def test_load_timezone_info_does_not_exist def test_load_timezone_info_invalid assert_raises(InvalidTimezoneIdentifier) do - @data_source.load_timezone_info('../Definitions/Europe/London') + @data_source.load_timezone_info('../zoneinfo/Europe/London') end end From ac3ee6828afd67e6a8ee981cba791ee34d20e9fb Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 19 Jul 2022 18:37:45 +0100 Subject: [PATCH 79/81] Remove unnecessary escaping of + within regex character classes. --- lib/tzinfo/ruby_data_source.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tzinfo/ruby_data_source.rb b/lib/tzinfo/ruby_data_source.rb index b8a34e78..792ca820 100644 --- a/lib/tzinfo/ruby_data_source.rb +++ b/lib/tzinfo/ruby_data_source.rb @@ -38,7 +38,7 @@ def initialize # Raises InvalidTimezoneIdentifier if the timezone is not found or the # identifier is invalid. def load_timezone_info(identifier) - raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~ /\A[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*\z/ + raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~ /\A[A-Za-z0-9+\-_]+(\/[A-Za-z0-9+\-_]+)*\z/ identifier = identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__') From fd05e2a61cc569cef81ebd1a90d0b57f69e401bd Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 19 Jul 2022 19:14:37 +0100 Subject: [PATCH 80/81] Preparing v1.2.10. --- CHANGES.md | 10 ++++++++++ tzinfo.gemspec | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 4705ec2c..9e623850 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,13 @@ +Version 1.2.10 - 18-Jul-2022 +---------------------------- + +* Fixed a relative path traversal bug that could cause arbitrary files to be + loaded with require when used with RubyDataSource. Please refer to + https://github.com/tzinfo/tzinfo/security/advisories/GHSA-5cm2-9h8c-rvfx for + details. CVE-2022-31163. +* Ignore the SECURITY file from Arch Linux's tzdata package. #134. + + Version 1.2.9 - 16-Dec-2020 --------------------------- diff --git a/tzinfo.gemspec b/tzinfo.gemspec index ed748285..6513be49 100644 --- a/tzinfo.gemspec +++ b/tzinfo.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'tzinfo' - s.version = '1.2.9' + s.version = '1.2.10' s.summary = 'Daylight savings aware timezone library' s.description = 'TZInfo provides daylight savings aware transformations between times in different time zones.' s.author = 'Philip Ross' From 0814dcd6195f247cc90e62a46b86ff0b76e08ed6 Mon Sep 17 00:00:00 2001 From: Phil Ross Date: Tue, 19 Jul 2022 19:18:49 +0100 Subject: [PATCH 81/81] Fix the release date. --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 9e623850..154cf234 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,4 @@ -Version 1.2.10 - 18-Jul-2022 +Version 1.2.10 - 19-Jul-2022 ---------------------------- * Fixed a relative path traversal bug that could cause arbitrary files to be