tzinfo 1.2.4 → 1.2.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of tzinfo might be problematic. Click here for more details.

Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGES.md +75 -48
  5. data/LICENSE +1 -1
  6. data/README.md +9 -8
  7. data/lib/tzinfo/country_timezone.rb +14 -2
  8. data/lib/tzinfo/data_source.rb +1 -1
  9. data/lib/tzinfo/ruby_core_support.rb +24 -1
  10. data/lib/tzinfo/ruby_country_info.rb +6 -2
  11. data/lib/tzinfo/ruby_data_source.rb +24 -20
  12. data/lib/tzinfo/time_or_datetime.rb +12 -5
  13. data/lib/tzinfo/timezone.rb +10 -6
  14. data/lib/tzinfo/timezone_offset.rb +26 -3
  15. data/lib/tzinfo/timezone_period.rb +29 -5
  16. data/lib/tzinfo/timezone_proxy.rb +7 -1
  17. data/lib/tzinfo/timezone_transition.rb +12 -2
  18. data/lib/tzinfo/timezone_transition_definition.rb +6 -3
  19. data/lib/tzinfo/zoneinfo_country_info.rb +3 -1
  20. data/lib/tzinfo/zoneinfo_data_source.rb +8 -0
  21. data/lib/tzinfo/zoneinfo_timezone_info.rb +13 -7
  22. data/test/tc_country.rb +6 -4
  23. data/test/tc_country_timezone.rb +12 -0
  24. data/test/tc_ruby_country_info.rb +30 -0
  25. data/test/tc_ruby_data_source.rb +26 -2
  26. data/test/tc_time_or_datetime.rb +27 -6
  27. data/test/tc_timezone.rb +13 -2
  28. data/test/tc_timezone_period.rb +7 -0
  29. data/test/tc_timezone_proxy.rb +9 -0
  30. data/test/tc_timezone_transition.rb +14 -0
  31. data/test/tc_timezone_transition_definition.rb +11 -0
  32. data/test/tc_transition_data_timezone_info.rb +11 -1
  33. data/test/tc_zoneinfo_country_info.rb +14 -0
  34. data/test/tc_zoneinfo_data_source.rb +11 -2
  35. data/test/tc_zoneinfo_timezone_info.rb +108 -0
  36. data/test/test_utils.rb +63 -5
  37. data/test/ts_all_zoneinfo.rb +3 -1
  38. data/tzinfo.gemspec +2 -2
  39. metadata +22 -24
  40. metadata.gz.sig +0 -0
@@ -79,7 +79,13 @@ module TZInfo
79
79
  # calculated multiple times in concurrently executing threads. It is not
80
80
  # worth the overhead of locking to ensure that @latitude is only
81
81
  # calculated once.
82
- @latitude ||= RubyCoreSupport.rational_new!(@latitude_numerator, @latitude_denominator)
82
+ unless @latitude
83
+ result = RubyCoreSupport.rational_new!(@latitude_numerator, @latitude_denominator)
84
+ return result if frozen?
85
+ @latitude = result
86
+ end
87
+
88
+ @latitude
83
89
  end
84
90
 
85
91
  # The longitude of this timezone in degrees as a Rational.
@@ -88,7 +94,13 @@ module TZInfo
88
94
  # calculated multiple times in concurrently executing threads. It is not
89
95
  # worth the overhead of locking to ensure that @longitude is only
90
96
  # calculated once.
91
- @longitude ||= RubyCoreSupport.rational_new!(@longitude_numerator, @longitude_denominator)
97
+ unless @longitude
98
+ result = RubyCoreSupport.rational_new!(@longitude_numerator, @longitude_denominator)
99
+ return result if frozen?
100
+ @longitude = result
101
+ end
102
+
103
+ @longitude
92
104
  end
93
105
 
94
106
  # Returns true if and only if the given CountryTimezone is equal to the
@@ -179,7 +179,7 @@ module TZInfo
179
179
  begin
180
180
  return ZoneinfoDataSource.new
181
181
  rescue ZoneinfoDirectoryNotFound
182
- raise DataSourceNotFound, "No source of timezone data could be found.\nPlease refer to http://tzinfo.github.io/datasourcenotfound for help resolving this error."
182
+ raise DataSourceNotFound, "No source of timezone data could be found.\nPlease refer to https://tzinfo.github.io/datasourcenotfound for help resolving this error."
183
183
  end
184
184
  end
185
185
 
@@ -137,10 +137,33 @@ module TZInfo
137
137
  def self.open_file(file_name, mode, opts, &block)
138
138
  File.open(file_name, mode, &block)
139
139
  end
140
- else
140
+ elsif RUBY_VERSION =~ /\A1\.9\./
141
141
  def self.open_file(file_name, mode, opts, &block)
142
142
  File.open(file_name, mode, opts, &block)
143
143
  end
144
+ else
145
+ # Evaluate method as a string because **opts isn't valid syntax prior to
146
+ # Ruby 2.0.
147
+ eval(<<-EOF
148
+ def self.open_file(file_name, mode, opts, &block)
149
+ File.open(file_name, mode, **opts, &block)
150
+ end
151
+ EOF
152
+ )
153
+ end
154
+
155
+
156
+ # Object#untaint is a deprecated no-op in Ruby >= 2.7 and will be removed in
157
+ # 3.2. Add a refinement to either silence the warning, or supply the method
158
+ # if needed.
159
+ if !Object.new.respond_to?(:untaint) || RUBY_VERSION =~ /\A(\d+)\.(\d+)(?:\.|\z)/ && ($1 == '2' && $2.to_i >= 7 || $1.to_i >= 3)
160
+ module UntaintExt
161
+ refine Object do
162
+ def untaint
163
+ self
164
+ end
165
+ end
166
+ end
144
167
  end
145
168
  end
146
169
  end
@@ -22,7 +22,9 @@ module TZInfo
22
22
  # calculated once.
23
23
 
24
24
  unless @zone_identifiers
25
- @zone_identifiers = zones.collect {|zone| zone.identifier}.freeze
25
+ result = zones.collect {|zone| zone.identifier}.freeze
26
+ return result if frozen?
27
+ @zone_identifiers = result
26
28
  end
27
29
 
28
30
  @zone_identifiers
@@ -40,8 +42,10 @@ module TZInfo
40
42
  unless @zones
41
43
  zones = Zones.new
42
44
  @block.call(zones) if @block
45
+ result = zones.list.freeze
46
+ return result if frozen?
43
47
  @block = nil
44
- @zones = zones.list.freeze
48
+ @zones = result
45
49
  end
46
50
 
47
51
  @zones
@@ -1,4 +1,8 @@
1
1
  module TZInfo
2
+ # Use send as a workaround for erroneous 'wrong number of arguments' errors
3
+ # with JRuby 9.0.5.0 when calling methods with Java implementations. See #114.
4
+ send(:using, RubyCoreSupport::UntaintExt) if RubyCoreSupport.const_defined?(:UntaintExt)
5
+
2
6
  # A DataSource that loads data from the set of Ruby modules included in the
3
7
  # TZInfo::Data library (tzinfo-data gem).
4
8
  #
@@ -6,15 +10,30 @@ module TZInfo
6
10
  #
7
11
  # TZInfo::DataSource.set(:ruby)
8
12
  class RubyDataSource < DataSource
9
- # Base path for require.
10
- REQUIRE_PATH = File.join('tzinfo', 'data', 'definitions')
11
-
12
13
  # Whether the timezone index has been loaded yet.
13
14
  @@timezone_index_loaded = false
14
15
 
15
16
  # Whether the country index has been loaded yet.
16
17
  @@country_index_loaded = false
17
18
 
19
+ # Initializes a new RubyDataSource instance.
20
+ def initialize
21
+ tzinfo_data = File.join('tzinfo', 'data')
22
+ begin
23
+ require(tzinfo_data)
24
+
25
+ data_file = File.join('', 'tzinfo', 'data.rb')
26
+ path = $".reverse_each.detect {|p| p.end_with?(data_file) }
27
+ if path
28
+ @base_path = File.join(File.dirname(path), 'data').untaint
29
+ else
30
+ @base_path = tzinfo_data
31
+ end
32
+ rescue LoadError
33
+ @base_path = tzinfo_data
34
+ end
35
+ end
36
+
18
37
  # Returns a TimezoneInfo instance for a given identifier.
19
38
  # Raises InvalidTimezoneIdentifier if the timezone is not found or the
20
39
  # identifier is invalid.
@@ -93,27 +112,17 @@ module TZInfo
93
112
  end
94
113
 
95
114
  # Requires an index by its name.
96
- def self.require_index(name)
115
+ def require_index(name)
97
116
  require_data(*['indexes', name])
98
117
  end
99
118
 
100
119
  # Requires a file from tzinfo/data.
101
120
  def require_data(*file)
102
- self.class.require_data(*file)
103
- end
104
-
105
- # Requires a file from tzinfo/data.
106
- def self.require_data(*file)
107
- require File.join('tzinfo', 'data', *file)
121
+ require(File.join(@base_path, *file))
108
122
  end
109
123
 
110
124
  # Loads in the index of timezones if it hasn't already been loaded.
111
125
  def load_timezone_index
112
- self.class.load_timezone_index
113
- end
114
-
115
- # Loads in the index of timezones if it hasn't already been loaded.
116
- def self.load_timezone_index
117
126
  unless @@timezone_index_loaded
118
127
  require_index('timezones')
119
128
  @@timezone_index_loaded = true
@@ -122,11 +131,6 @@ module TZInfo
122
131
 
123
132
  # Loads in the index of countries if it hasn't already been loaded.
124
133
  def load_country_index
125
- self.class.load_country_index
126
- end
127
-
128
- # Loads in the index of countries if it hasn't already been loaded.
129
- def self.load_country_index
130
134
  unless @@country_index_loaded
131
135
  require_index('countries')
132
136
  @@country_index_loaded = true
@@ -54,11 +54,14 @@ module TZInfo
54
54
  # calculated once.
55
55
 
56
56
  unless @time
57
- if @timestamp
58
- @time = Time.at(@timestamp).utc
57
+ result = if @timestamp
58
+ Time.at(@timestamp).utc
59
59
  else
60
- @time = Time.utc(year, mon, mday, hour, min, sec, usec)
60
+ Time.utc(year, mon, mday, hour, min, sec, usec)
61
61
  end
62
+
63
+ return result if frozen?
64
+ @time = result
62
65
  end
63
66
 
64
67
  @time
@@ -78,7 +81,9 @@ module TZInfo
78
81
  # Avoid using Rational unless necessary.
79
82
  u = usec
80
83
  s = u == 0 ? sec : Rational(sec * 1000000 + u, 1000000)
81
- @datetime = RubyCoreSupport.datetime_new(year, mon, mday, hour, min, s)
84
+ result = RubyCoreSupport.datetime_new(year, mon, mday, hour, min, s)
85
+ return result if frozen?
86
+ @datetime = result
82
87
  end
83
88
 
84
89
  @datetime
@@ -92,7 +97,9 @@ module TZInfo
92
97
  # calculated once.
93
98
 
94
99
  unless @timestamp
95
- @timestamp = to_time.to_i
100
+ result = to_time.to_i
101
+ return result if frozen?
102
+ @timestamp = result
96
103
  end
97
104
 
98
105
  @timestamp
@@ -571,17 +571,21 @@ module TZInfo
571
571
  # version 2.0.0, %z will be passed to Time#strftime and DateTime#strftime
572
572
  # instead. Some of the formatting options may cease to be available
573
573
  # depending on the version of Ruby in use (for example, %:::z is only
574
- # supported by Time#strftime from MRI version 2.0.0 onwards.)
574
+ # supported by Time#strftime from MRI version 2.0.0 onwards).
575
575
  def strftime(format, utc = Time.now.utc)
576
+ utc = TimeOrDateTime.wrap(utc)
576
577
  period = period_for_utc(utc)
577
- local = period.to_local(utc)
578
- local = Time.at(local).utc unless local.kind_of?(Time) || local.kind_of?(DateTime)
578
+ local_wrapped = period.to_local(utc)
579
+ local = local_wrapped.to_orig
580
+ local = local_wrapped.to_time unless local.kind_of?(Time) || local.kind_of?(DateTime)
579
581
  abbreviation = period.abbreviation.to_s.gsub(/%/, '%%')
580
582
 
581
- format = format.gsub(/%(%*)(Z|:*z)/) do
583
+ format = format.gsub(/%(%*)([sZ]|:*z)/) do
582
584
  if $1.length.odd?
583
585
  # Escaped literal percent or series of percents. Pass on to strftime.
584
586
  "#$1%#$2"
587
+ elsif $2 == "s"
588
+ "#$1#{utc.to_i}"
585
589
  elsif $2 == "Z"
586
590
  "#$1#{abbreviation}"
587
591
  else
@@ -600,8 +604,8 @@ module TZInfo
600
604
  # Passing the invalid format string through to Time#strftime or
601
605
  # DateTime#strtime would normally result in it being returned in the
602
606
  # result. However, with Ruby 1.8.7 on Windows (as tested with Ruby
603
- # 1.8.7-p374 from http://rubyinstaller.org/downloads/archives), this
604
- # causes Time#strftime to always return an empty string (e.g.
607
+ # 1.8.7-p374 from https://rubyinstaller.org/downloads/archives),
608
+ # this causes Time#strftime to always return an empty string (e.g.
605
609
  # Time.now.strftime('a %::::z b') returns '').
606
610
  #
607
611
  # Escape the percent to force it to be evaluated as a literal.
@@ -1,11 +1,34 @@
1
1
  module TZInfo
2
2
  # Represents an offset defined in a Timezone data file.
3
3
  class TimezoneOffset
4
- # The base offset of the timezone from UTC in seconds.
4
+ # The base offset of the timezone from UTC in seconds. This does not include
5
+ # any adjustment made for daylight savings time and will typically remain
6
+ # constant throughout the year.
7
+ #
8
+ # To obtain the currently observed offset from UTC, including the effect of
9
+ # daylight savings time, use utc_total_offset instead.
10
+ #
11
+ # Note that zoneinfo files only include the value of utc_total_offset and a
12
+ # DST flag. When using ZoneinfoDataSource, the utc_offset will be derived
13
+ # from changes to the UTC total offset and the DST flag. As a consequence,
14
+ # utc_total_offset will always be correct, but utc_offset may be inaccurate.
15
+ #
16
+ # If you require utc_offset to be accurate, install the tzinfo-data gem and
17
+ # set RubyDataSource as the DataSource.
5
18
  attr_reader :utc_offset
6
19
 
7
- # The offset from standard time for the zone in seconds (i.e. non-zero if
8
- # daylight savings is being observed).
20
+ # The offset from the time zone's standard time in seconds. Zero
21
+ # when daylight savings time is not in effect. Non-zero (usually 3600 = 1
22
+ # hour) if daylight savings is being observed.
23
+ #
24
+ # Note that zoneinfo files only include the value of utc_total_offset and
25
+ # a DST flag. When using DataSources::ZoneinfoDataSource, the std_offset
26
+ # will be derived from changes to the UTC total offset and the DST flag. As
27
+ # a consequence, utc_total_offset will always be correct, but std_offset
28
+ # may be inaccurate.
29
+ #
30
+ # If you require std_offset to be accurate, install the tzinfo-data gem
31
+ # and set RubyDataSource as the DataSource.
9
32
  attr_reader :std_offset
10
33
 
11
34
  # The total offset of this observance from UTC in seconds
@@ -38,14 +38,36 @@ module TZInfo
38
38
  @utc_total_offset_rational = nil
39
39
  end
40
40
 
41
- # Base offset of the timezone from UTC (seconds).
41
+ # The base offset of the timezone from UTC in seconds. This does not include
42
+ # any adjustment made for daylight savings time and will typically remain
43
+ # constant throughout the year.
44
+ #
45
+ # To obtain the currently observed offset from UTC, including the effect of
46
+ # daylight savings time, use utc_total_offset instead.
47
+ #
48
+ # Note that zoneinfo files only include the value of utc_total_offset and a
49
+ # DST flag. When using ZoneinfoDataSource, the utc_offset will be derived
50
+ # from changes to the UTC total offset and the DST flag. As a consequence,
51
+ # utc_total_offset will always be correct, but utc_offset may be inaccurate.
52
+ #
53
+ # If you require utc_offset to be accurate, install the tzinfo-data gem and
54
+ # set RubyDataSource as the DataSource.
42
55
  def utc_offset
43
56
  @offset.utc_offset
44
57
  end
45
58
 
46
- # Offset from the local time where daylight savings is in effect (seconds).
47
- # E.g.: utc_offset could be -5 hours. Normally, std_offset would be 0.
48
- # During daylight savings, std_offset would typically become +1 hours.
59
+ # The offset from the time zone's standard time in seconds. Zero
60
+ # when daylight savings time is not in effect. Non-zero (usually 3600 = 1
61
+ # hour) if daylight savings is being observed.
62
+ #
63
+ # Note that zoneinfo files only include the value of utc_total_offset and
64
+ # a DST flag. When using DataSources::ZoneinfoDataSource, the std_offset
65
+ # will be derived from changes to the UTC total offset and the DST flag. As
66
+ # a consequence, utc_total_offset will always be correct, but std_offset
67
+ # may be inaccurate.
68
+ #
69
+ # If you require std_offset to be accurate, install the tzinfo-data gem
70
+ # and set RubyDataSource as the DataSource.
49
71
  def std_offset
50
72
  @offset.std_offset
51
73
  end
@@ -71,7 +93,9 @@ module TZInfo
71
93
  # to ensure that @zone_identifiers is only calculated once.
72
94
 
73
95
  unless @utc_total_offset_rational
74
- @utc_total_offset_rational = OffsetRationals.rational_for_offset(utc_total_offset)
96
+ result = OffsetRationals.rational_for_offset(utc_total_offset)
97
+ return result if frozen?
98
+ @utc_total_offset_rational = result
75
99
  end
76
100
  @utc_total_offset_rational
77
101
  end
@@ -93,7 +93,13 @@ module TZInfo
93
93
  # calculated multiple times in concurrently executing threads. It is not
94
94
  # worth the overhead of locking to ensure that @real_timezone is only
95
95
  # calculated once.
96
- @real_timezone ||= Timezone.get(@identifier)
96
+ unless @real_timezone
97
+ result = Timezone.get(@identifier)
98
+ return result if frozen?
99
+ @real_timezone = result
100
+ end
101
+
102
+ @real_timezone
97
103
  end
98
104
  end
99
105
  end
@@ -43,7 +43,12 @@ module TZInfo
43
43
  # worth the overhead of locking to ensure that @local_end_at is only
44
44
  # calculated once.
45
45
 
46
- @local_end_at = at.add_with_convert(@previous_offset.utc_total_offset) unless @local_end_at
46
+ unless @local_end_at
47
+ result = at.add_with_convert(@previous_offset.utc_total_offset)
48
+ return result if frozen?
49
+ @local_end_at = result
50
+ end
51
+
47
52
  @local_end_at
48
53
  end
49
54
 
@@ -67,7 +72,12 @@ module TZInfo
67
72
  # worth the overhead of locking to ensure that @local_start_at is only
68
73
  # calculated once.
69
74
 
70
- @local_start_at = at.add_with_convert(@offset.utc_total_offset) unless @local_start_at
75
+ unless @local_start_at
76
+ result = at.add_with_convert(@offset.utc_total_offset)
77
+ return result if frozen?
78
+ @local_start_at = result
79
+ end
80
+
71
81
  @local_start_at
72
82
  end
73
83
 
@@ -71,13 +71,16 @@ module TZInfo
71
71
  # overhead of locking to ensure that @at is only calculated once.
72
72
 
73
73
  unless @at
74
- unless @denominator
75
- @at = TimeOrDateTime.new(@numerator_or_time)
74
+ result = unless @denominator
75
+ TimeOrDateTime.new(@numerator_or_time)
76
76
  else
77
77
  r = RubyCoreSupport.rational_new!(@numerator_or_time, @denominator)
78
78
  dt = RubyCoreSupport.datetime_new!(r, 0, Date::ITALY)
79
- @at = TimeOrDateTime.new(dt)
79
+ TimeOrDateTime.new(dt)
80
80
  end
81
+
82
+ return result if frozen?
83
+ @at = result
81
84
  end
82
85
 
83
86
  @at
@@ -20,7 +20,9 @@ module TZInfo
20
20
  # calculated once.
21
21
 
22
22
  unless @zone_identifiers
23
- @zone_identifiers = zones.collect {|zone| zone.identifier}.freeze
23
+ result = zones.collect {|zone| zone.identifier}.freeze
24
+ return result if frozen?
25
+ @zone_identifiers = result
24
26
  end
25
27
 
26
28
  @zone_identifiers
@@ -1,4 +1,12 @@
1
1
  module TZInfo
2
+ # Use send as a workaround for an issue on JRuby 9.2.9.0 where using the
3
+ # refinement causes calls to RubyCoreSupport.file_open to fail to pass the
4
+ # block parameter.
5
+ #
6
+ # https://travis-ci.org/tzinfo/tzinfo/jobs/628812051#L1931
7
+ # https://github.com/jruby/jruby/issues/6009
8
+ send(:using, TZInfo::RubyCoreSupport::UntaintExt) if TZInfo::RubyCoreSupport.const_defined?(:UntaintExt)
9
+
2
10
  # An InvalidZoneinfoDirectory exception is raised if the DataSource is
3
11
  # set to a specific zoneinfo path, which is not a valid zoneinfo directory
4
12
  # (i.e. a directory containing index files named iso3166.tab and zone.tab
@@ -1,4 +1,8 @@
1
1
  module TZInfo
2
+ # Use send as a workaround for erroneous 'wrong number of arguments' errors
3
+ # with JRuby 9.0.5.0 when calling methods with Java implementations. See #114.
4
+ send(:using, RubyCoreSupport::UntaintExt) if RubyCoreSupport.const_defined?(:UntaintExt)
5
+
2
6
  # An InvalidZoneinfoFile exception is raised if an attempt is made to load an
3
7
  # invalid zoneinfo file.
4
8
  class InvalidZoneinfoFile < StandardError
@@ -93,19 +97,21 @@ module TZInfo
93
97
  if offset[:is_dst]
94
98
  utc_offset_from_next = transition[:utc_offset_from_next]
95
99
 
96
- difference_to_previous = utc_total_offset - (utc_offset_from_previous || utc_total_offset)
97
- difference_to_next = utc_total_offset - (utc_offset_from_next || utc_total_offset)
100
+ difference_to_previous = (utc_total_offset - (utc_offset_from_previous || utc_total_offset)).abs
101
+ difference_to_next = (utc_total_offset - (utc_offset_from_next || utc_total_offset)).abs
98
102
 
99
- utc_offset = if difference_to_previous > 0 && difference_to_next > 0
103
+ utc_offset = if difference_to_previous == 3600
104
+ utc_offset_from_previous
105
+ elsif difference_to_next == 3600
106
+ utc_offset_from_next
107
+ elsif difference_to_previous > 0 && difference_to_next > 0
100
108
  difference_to_previous < difference_to_next ? utc_offset_from_previous : utc_offset_from_next
101
109
  elsif difference_to_previous > 0
102
110
  utc_offset_from_previous
103
111
  elsif difference_to_next > 0
104
112
  utc_offset_from_next
105
- else # difference_to_previous <= 0 && difference_to_next <= 0
106
- # DST, but the either the offset has stayed the same or decreased
107
- # relative to both the previous and next used base utc offset, or
108
- # there are no non-DST offsets. Assume a 1 hour offset from base.
113
+ else
114
+ # No difference, assume a 1 hour offset from standard time.
109
115
  utc_total_offset - 3600
110
116
  end
111
117