tzinfo 1.2.6 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -1
  4. data/.yardopts +3 -0
  5. data/CHANGES.md +482 -380
  6. data/LICENSE +12 -12
  7. data/README.md +368 -114
  8. data/lib/tzinfo.rb +64 -29
  9. data/lib/tzinfo/country.rb +141 -129
  10. data/lib/tzinfo/country_timezone.rb +70 -112
  11. data/lib/tzinfo/data_source.rb +389 -144
  12. data/lib/tzinfo/data_sources.rb +8 -0
  13. data/lib/tzinfo/data_sources/constant_offset_data_timezone_info.rb +56 -0
  14. data/lib/tzinfo/data_sources/country_info.rb +42 -0
  15. data/lib/tzinfo/data_sources/data_timezone_info.rb +91 -0
  16. data/lib/tzinfo/data_sources/linked_timezone_info.rb +33 -0
  17. data/lib/tzinfo/data_sources/ruby_data_source.rb +143 -0
  18. data/lib/tzinfo/data_sources/timezone_info.rb +47 -0
  19. data/lib/tzinfo/data_sources/transitions_data_timezone_info.rb +214 -0
  20. data/lib/tzinfo/data_sources/zoneinfo_data_source.rb +575 -0
  21. data/lib/tzinfo/data_sources/zoneinfo_reader.rb +286 -0
  22. data/lib/tzinfo/data_timezone.rb +33 -47
  23. data/lib/tzinfo/datetime_with_offset.rb +153 -0
  24. data/lib/tzinfo/format1.rb +10 -0
  25. data/lib/tzinfo/format1/country_definer.rb +17 -0
  26. data/lib/tzinfo/format1/country_index_definition.rb +64 -0
  27. data/lib/tzinfo/format1/timezone_definer.rb +64 -0
  28. data/lib/tzinfo/format1/timezone_definition.rb +39 -0
  29. data/lib/tzinfo/format1/timezone_index_definition.rb +77 -0
  30. data/lib/tzinfo/format2.rb +10 -0
  31. data/lib/tzinfo/format2/country_definer.rb +68 -0
  32. data/lib/tzinfo/format2/country_index_definer.rb +68 -0
  33. data/lib/tzinfo/format2/country_index_definition.rb +46 -0
  34. data/lib/tzinfo/format2/timezone_definer.rb +94 -0
  35. data/lib/tzinfo/format2/timezone_definition.rb +73 -0
  36. data/lib/tzinfo/format2/timezone_index_definer.rb +45 -0
  37. data/lib/tzinfo/format2/timezone_index_definition.rb +55 -0
  38. data/lib/tzinfo/info_timezone.rb +26 -21
  39. data/lib/tzinfo/linked_timezone.rb +33 -52
  40. data/lib/tzinfo/offset_timezone_period.rb +42 -0
  41. data/lib/tzinfo/string_deduper.rb +118 -0
  42. data/lib/tzinfo/time_with_offset.rb +128 -0
  43. data/lib/tzinfo/timestamp.rb +548 -0
  44. data/lib/tzinfo/timestamp_with_offset.rb +85 -0
  45. data/lib/tzinfo/timezone.rb +989 -502
  46. data/lib/tzinfo/timezone_offset.rb +84 -74
  47. data/lib/tzinfo/timezone_period.rb +151 -217
  48. data/lib/tzinfo/timezone_proxy.rb +70 -79
  49. data/lib/tzinfo/timezone_transition.rb +77 -109
  50. data/lib/tzinfo/transitions_timezone_period.rb +63 -0
  51. data/lib/tzinfo/untaint_ext.rb +18 -0
  52. data/lib/tzinfo/version.rb +7 -0
  53. data/lib/tzinfo/with_offset.rb +61 -0
  54. metadata +43 -99
  55. metadata.gz.sig +0 -0
  56. data/Rakefile +0 -107
  57. data/lib/tzinfo/country_index_definition.rb +0 -31
  58. data/lib/tzinfo/country_info.rb +0 -42
  59. data/lib/tzinfo/data_timezone_info.rb +0 -55
  60. data/lib/tzinfo/linked_timezone_info.rb +0 -26
  61. data/lib/tzinfo/offset_rationals.rb +0 -77
  62. data/lib/tzinfo/ruby_core_support.rb +0 -176
  63. data/lib/tzinfo/ruby_country_info.rb +0 -74
  64. data/lib/tzinfo/ruby_data_source.rb +0 -138
  65. data/lib/tzinfo/time_or_datetime.rb +0 -340
  66. data/lib/tzinfo/timezone_definition.rb +0 -36
  67. data/lib/tzinfo/timezone_index_definition.rb +0 -54
  68. data/lib/tzinfo/timezone_info.rb +0 -30
  69. data/lib/tzinfo/timezone_transition_definition.rb +0 -104
  70. data/lib/tzinfo/transition_data_timezone_info.rb +0 -274
  71. data/lib/tzinfo/zoneinfo_country_info.rb +0 -37
  72. data/lib/tzinfo/zoneinfo_data_source.rb +0 -496
  73. data/lib/tzinfo/zoneinfo_timezone_info.rb +0 -298
  74. data/test/tc_country.rb +0 -236
  75. data/test/tc_country_index_definition.rb +0 -69
  76. data/test/tc_country_info.rb +0 -16
  77. data/test/tc_country_timezone.rb +0 -173
  78. data/test/tc_data_source.rb +0 -218
  79. data/test/tc_data_timezone.rb +0 -99
  80. data/test/tc_data_timezone_info.rb +0 -18
  81. data/test/tc_info_timezone.rb +0 -34
  82. data/test/tc_linked_timezone.rb +0 -155
  83. data/test/tc_linked_timezone_info.rb +0 -23
  84. data/test/tc_offset_rationals.rb +0 -23
  85. data/test/tc_ruby_core_support.rb +0 -168
  86. data/test/tc_ruby_country_info.rb +0 -110
  87. data/test/tc_ruby_data_source.rb +0 -165
  88. data/test/tc_time_or_datetime.rb +0 -660
  89. data/test/tc_timezone.rb +0 -1359
  90. data/test/tc_timezone_definition.rb +0 -113
  91. data/test/tc_timezone_index_definition.rb +0 -73
  92. data/test/tc_timezone_info.rb +0 -11
  93. data/test/tc_timezone_london.rb +0 -143
  94. data/test/tc_timezone_melbourne.rb +0 -142
  95. data/test/tc_timezone_new_york.rb +0 -142
  96. data/test/tc_timezone_offset.rb +0 -126
  97. data/test/tc_timezone_period.rb +0 -555
  98. data/test/tc_timezone_proxy.rb +0 -136
  99. data/test/tc_timezone_transition.rb +0 -366
  100. data/test/tc_timezone_transition_definition.rb +0 -295
  101. data/test/tc_timezone_utc.rb +0 -27
  102. data/test/tc_transition_data_timezone_info.rb +0 -433
  103. data/test/tc_zoneinfo_country_info.rb +0 -78
  104. data/test/tc_zoneinfo_data_source.rb +0 -1204
  105. data/test/tc_zoneinfo_timezone_info.rb +0 -1234
  106. data/test/test_utils.rb +0 -188
  107. data/test/ts_all.rb +0 -7
  108. data/test/ts_all_ruby.rb +0 -5
  109. data/test/ts_all_zoneinfo.rb +0 -9
  110. data/test/tzinfo-data/tzinfo/data.rb +0 -8
  111. data/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb +0 -89
  112. data/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb +0 -315
  113. data/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb +0 -218
  114. data/test/tzinfo-data/tzinfo/data/definitions/EST.rb +0 -19
  115. data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb +0 -21
  116. data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb +0 -21
  117. data/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb +0 -21
  118. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb +0 -261
  119. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb +0 -186
  120. data/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb +0 -321
  121. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb +0 -265
  122. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb +0 -220
  123. data/test/tzinfo-data/tzinfo/data/definitions/UTC.rb +0 -16
  124. data/test/tzinfo-data/tzinfo/data/indexes/countries.rb +0 -927
  125. data/test/tzinfo-data/tzinfo/data/indexes/timezones.rb +0 -596
  126. data/test/tzinfo-data/tzinfo/data/version.rb +0 -14
  127. data/test/zoneinfo/America/Argentina/Buenos_Aires +0 -0
  128. data/test/zoneinfo/America/New_York +0 -0
  129. data/test/zoneinfo/Australia/Melbourne +0 -0
  130. data/test/zoneinfo/EST +0 -0
  131. data/test/zoneinfo/Etc/UTC +0 -0
  132. data/test/zoneinfo/Europe/Amsterdam +0 -0
  133. data/test/zoneinfo/Europe/Andorra +0 -0
  134. data/test/zoneinfo/Europe/London +0 -0
  135. data/test/zoneinfo/Europe/Paris +0 -0
  136. data/test/zoneinfo/Europe/Prague +0 -0
  137. data/test/zoneinfo/Factory +0 -0
  138. data/test/zoneinfo/iso3166.tab +0 -275
  139. data/test/zoneinfo/leapseconds +0 -61
  140. data/test/zoneinfo/posix/Europe/London +0 -0
  141. data/test/zoneinfo/posixrules +0 -0
  142. data/test/zoneinfo/right/Europe/London +0 -0
  143. data/test/zoneinfo/zone.tab +0 -439
  144. data/test/zoneinfo/zone1970.tab +0 -369
  145. data/tzinfo.gemspec +0 -21
@@ -1,104 +0,0 @@
1
- module TZInfo
2
- # A TimezoneTransition defined by as integer timestamp, as a rational to
3
- # create a DateTime or as both.
4
- #
5
- # @private
6
- class TimezoneTransitionDefinition < TimezoneTransition #:nodoc:
7
- # The numerator of the DateTime if the transition time is defined as a
8
- # DateTime, otherwise the transition time as a timestamp.
9
- attr_reader :numerator_or_time
10
- protected :numerator_or_time
11
-
12
- # Either the denominator of the DateTime if the transition time is defined
13
- # as a DateTime, otherwise nil.
14
- attr_reader :denominator
15
- protected :denominator
16
-
17
- # Creates a new TimezoneTransitionDefinition with the given offset,
18
- # previous_offset (both TimezoneOffset instances) and UTC time.
19
- #
20
- # The time can be specified as a timestamp, as a rational to create a
21
- # DateTime, or as both.
22
- #
23
- # If both a timestamp and rational are given, then the rational will only
24
- # be used if the timestamp falls outside of the range of Time on the
25
- # platform being used at runtime.
26
- #
27
- # DateTimes are created from the rational as follows:
28
- #
29
- # RubyCoreSupport.datetime_new!(RubyCoreSupport.rational_new!(numerator, denominator), 0, Date::ITALY)
30
- #
31
- # For performance reasons, the numerator and denominator must be specified
32
- # in their lowest form.
33
- def initialize(offset, previous_offset, numerator_or_timestamp, denominator_or_numerator = nil, denominator = nil)
34
- super(offset, previous_offset)
35
-
36
- if denominator
37
- numerator = denominator_or_numerator
38
- timestamp = numerator_or_timestamp
39
- elsif denominator_or_numerator
40
- numerator = numerator_or_timestamp
41
- denominator = denominator_or_numerator
42
- timestamp = nil
43
- else
44
- numerator = nil
45
- denominator = nil
46
- timestamp = numerator_or_timestamp
47
- end
48
-
49
- # Determine whether to use the timestamp or the numerator and denominator.
50
- if numerator && (
51
- !timestamp ||
52
- (timestamp < 0 && !RubyCoreSupport.time_supports_negative) ||
53
- ((timestamp < -2147483648 || timestamp > 2147483647) && !RubyCoreSupport.time_supports_64bit)
54
- )
55
-
56
- @numerator_or_time = numerator
57
- @denominator = denominator
58
- else
59
- @numerator_or_time = timestamp
60
- @denominator = nil
61
- end
62
-
63
- @at = nil
64
- end
65
-
66
- # A TimeOrDateTime instance representing the UTC time when this transition
67
- # occurs.
68
- def at
69
- # Thread-safety: It is possible that the value of @at may be calculated
70
- # multiple times in concurrently executing threads. It is not worth the
71
- # overhead of locking to ensure that @at is only calculated once.
72
-
73
- unless @at
74
- result = unless @denominator
75
- TimeOrDateTime.new(@numerator_or_time)
76
- else
77
- r = RubyCoreSupport.rational_new!(@numerator_or_time, @denominator)
78
- dt = RubyCoreSupport.datetime_new!(r, 0, Date::ITALY)
79
- TimeOrDateTime.new(dt)
80
- end
81
-
82
- return result if frozen?
83
- @at = result
84
- end
85
-
86
- @at
87
- end
88
-
89
- # Returns true if this TimezoneTransitionDefinition is equal to the given
90
- # TimezoneTransitionDefinition. Two TimezoneTransitionDefinition instances
91
- # are considered to be equal by eql? if offset, previous_offset,
92
- # numerator_or_time and denominator are all equal.
93
- def eql?(tti)
94
- tti.kind_of?(TimezoneTransitionDefinition) &&
95
- offset == tti.offset && previous_offset == tti.previous_offset &&
96
- numerator_or_time == tti.numerator_or_time && denominator == tti.denominator
97
- end
98
-
99
- # Returns a hash of this TimezoneTransitionDefinition instance.
100
- def hash
101
- @offset.hash ^ @previous_offset.hash ^ @numerator_or_time.hash ^ @denominator.hash
102
- end
103
- end
104
- end
@@ -1,274 +0,0 @@
1
- module TZInfo
2
- # Raised if no offsets have been defined when calling period_for_utc or
3
- # periods_for_local. Indicates an error in the timezone data.
4
- class NoOffsetsDefined < StandardError
5
- end
6
-
7
- # Represents a data timezone defined by a set of offsets and a set
8
- # of transitions.
9
- #
10
- # @private
11
- class TransitionDataTimezoneInfo < DataTimezoneInfo #:nodoc:
12
-
13
- # Constructs a new TransitionDataTimezoneInfo with its identifier.
14
- def initialize(identifier)
15
- super(identifier)
16
- @offsets = {}
17
- @transitions = []
18
- @previous_offset = nil
19
- @transitions_index = nil
20
- end
21
-
22
- # Defines a offset. The id uniquely identifies this offset within the
23
- # timezone. utc_offset and std_offset define the offset in seconds of
24
- # standard time from UTC and daylight savings from standard time
25
- # respectively. abbreviation describes the timezone offset (e.g. GMT, BST,
26
- # EST or EDT).
27
- #
28
- # The first offset to be defined is treated as the offset that applies
29
- # until the first transition. This will usually be in Local Mean Time (LMT).
30
- #
31
- # ArgumentError will be raised if the id is already defined.
32
- def offset(id, utc_offset, std_offset, abbreviation)
33
- raise ArgumentError, 'Offset already defined' if @offsets.has_key?(id)
34
-
35
- offset = TimezoneOffset.new(utc_offset, std_offset, abbreviation)
36
- @offsets[id] = offset
37
- @previous_offset = offset unless @previous_offset
38
- end
39
-
40
- # Defines a transition. Transitions must be defined in chronological order.
41
- # ArgumentError will be raised if a transition is added out of order.
42
- # offset_id refers to an id defined with offset. ArgumentError will be
43
- # raised if the offset_id cannot be found. numerator_or_time and
44
- # denomiator specify the time the transition occurs as. See
45
- # TimezoneTransition for more detail about specifying times.
46
- def transition(year, month, offset_id, numerator_or_timestamp, denominator_or_numerator = nil, denominator = nil)
47
- offset = @offsets[offset_id]
48
- raise ArgumentError, 'Offset not found' unless offset
49
-
50
- if @transitions_index
51
- if year < @last_year || (year == @last_year && month < @last_month)
52
- raise ArgumentError, 'Transitions must be increasing date order'
53
- end
54
-
55
- # Record the position of the first transition with this index.
56
- index = transition_index(year, month)
57
- @transitions_index[index] ||= @transitions.length
58
-
59
- # Fill in any gaps
60
- (index - 1).downto(0) do |i|
61
- break if @transitions_index[i]
62
- @transitions_index[i] = @transitions.length
63
- end
64
- else
65
- @transitions_index = [@transitions.length]
66
- @start_year = year
67
- @start_month = month
68
- end
69
-
70
- @transitions << TimezoneTransitionDefinition.new(offset, @previous_offset,
71
- numerator_or_timestamp, denominator_or_numerator, denominator)
72
- @last_year = year
73
- @last_month = month
74
- @previous_offset = offset
75
- end
76
-
77
- # Returns the TimezonePeriod for the given UTC time.
78
- # Raises NoOffsetsDefined if no offsets have been defined.
79
- def period_for_utc(utc)
80
- unless @transitions.empty?
81
- utc = TimeOrDateTime.wrap(utc)
82
- index = transition_index(utc.year, utc.mon)
83
-
84
- start_transition = nil
85
- start = transition_before_end(index)
86
- if start
87
- start.downto(0) do |i|
88
- if @transitions[i].at <= utc
89
- start_transition = @transitions[i]
90
- break
91
- end
92
- end
93
- end
94
-
95
- end_transition = nil
96
- start = transition_after_start(index)
97
- if start
98
- start.upto(@transitions.length - 1) do |i|
99
- if @transitions[i].at > utc
100
- end_transition = @transitions[i]
101
- break
102
- end
103
- end
104
- end
105
-
106
- if start_transition || end_transition
107
- TimezonePeriod.new(start_transition, end_transition)
108
- else
109
- # Won't happen since there are transitions. Must always find one
110
- # transition that is either >= or < the specified time.
111
- raise 'No transitions found in search'
112
- end
113
- else
114
- raise NoOffsetsDefined, 'No offsets have been defined' unless @previous_offset
115
- TimezonePeriod.new(nil, nil, @previous_offset)
116
- end
117
- end
118
-
119
- # Returns the set of TimezonePeriods for the given local time as an array.
120
- # Results returned are ordered by increasing UTC start date.
121
- # Returns an empty array if no periods are found for the given time.
122
- # Raises NoOffsetsDefined if no offsets have been defined.
123
- def periods_for_local(local)
124
- unless @transitions.empty?
125
- local = TimeOrDateTime.wrap(local)
126
- index = transition_index(local.year, local.mon)
127
-
128
- result = []
129
-
130
- start_index = transition_after_start(index - 1)
131
- if start_index && @transitions[start_index].local_end_at > local
132
- if start_index > 0
133
- if @transitions[start_index - 1].local_start_at <= local
134
- result << TimezonePeriod.new(@transitions[start_index - 1], @transitions[start_index])
135
- end
136
- else
137
- result << TimezonePeriod.new(nil, @transitions[start_index])
138
- end
139
- end
140
-
141
- end_index = transition_before_end(index + 1)
142
-
143
- if end_index
144
- start_index = end_index unless start_index
145
-
146
- start_index.upto(transition_before_end(index + 1)) do |i|
147
- if @transitions[i].local_start_at <= local
148
- if i + 1 < @transitions.length
149
- if @transitions[i + 1].local_end_at > local
150
- result << TimezonePeriod.new(@transitions[i], @transitions[i + 1])
151
- end
152
- else
153
- result << TimezonePeriod.new(@transitions[i], nil)
154
- end
155
- end
156
- end
157
- end
158
-
159
- result
160
- else
161
- raise NoOffsetsDefined, 'No offsets have been defined' unless @previous_offset
162
- [TimezonePeriod.new(nil, nil, @previous_offset)]
163
- end
164
- end
165
-
166
- # Returns an Array of TimezoneTransition instances representing the times
167
- # where the UTC offset of the timezone changes.
168
- #
169
- # Transitions are returned up to a given date and time up to a given date
170
- # and time, specified in UTC (utc_to).
171
- #
172
- # A from date and time may also be supplied using the utc_from parameter
173
- # (also specified in UTC). If utc_from is not nil, only transitions from
174
- # that date and time onwards will be returned.
175
- #
176
- # Comparisons with utc_to are exclusive. Comparisons with utc_from are
177
- # inclusive. If a transition falls precisely on utc_to, it will be excluded.
178
- # If a transition falls on utc_from, it will be included.
179
- #
180
- # Transitions returned are ordered by when they occur, from earliest to
181
- # latest.
182
- #
183
- # utc_to and utc_from can be specified using either DateTime, Time or
184
- # integer timestamps (Time.to_i).
185
- #
186
- # If utc_from is specified and utc_to is not greater than utc_from, then
187
- # transitions_up_to raises an ArgumentError exception.
188
- def transitions_up_to(utc_to, utc_from = nil)
189
- utc_to = TimeOrDateTime.wrap(utc_to)
190
- utc_from = utc_from ? TimeOrDateTime.wrap(utc_from) : nil
191
-
192
- if utc_from && utc_to <= utc_from
193
- raise ArgumentError, 'utc_to must be greater than utc_from'
194
- end
195
-
196
- unless @transitions.empty?
197
- if utc_from
198
- from = transition_after_start(transition_index(utc_from.year, utc_from.mon))
199
-
200
- if from
201
- while from < @transitions.length && @transitions[from].at < utc_from
202
- from += 1
203
- end
204
-
205
- if from >= @transitions.length
206
- return []
207
- end
208
- else
209
- # utc_from is later than last transition.
210
- return []
211
- end
212
- else
213
- from = 0
214
- end
215
-
216
- to = transition_before_end(transition_index(utc_to.year, utc_to.mon))
217
-
218
- if to
219
- while to >= 0 && @transitions[to].at >= utc_to
220
- to -= 1
221
- end
222
-
223
- if to < 0
224
- return []
225
- end
226
- else
227
- # utc_to is earlier than first transition.
228
- return []
229
- end
230
-
231
- @transitions[from..to]
232
- else
233
- []
234
- end
235
- end
236
-
237
- private
238
- # Returns the index into the @transitions_index array for a given year
239
- # and month.
240
- def transition_index(year, month)
241
- index = (year - @start_year) * 2
242
- index += 1 if month > 6
243
- index -= 1 if @start_month > 6
244
- index
245
- end
246
-
247
- # Returns the index into @transitions of the first transition that occurs
248
- # on or after the start of the given index into @transitions_index.
249
- # Returns nil if there are no such transitions.
250
- def transition_after_start(index)
251
- if index >= @transitions_index.length
252
- nil
253
- else
254
- index = 0 if index < 0
255
- @transitions_index[index]
256
- end
257
- end
258
-
259
- # Returns the index into @transitions of the first transition that occurs
260
- # before the end of the given index into @transitions_index.
261
- # Returns nil if there are no such transitions.
262
- def transition_before_end(index)
263
- index = index + 1
264
-
265
- if index <= 0
266
- nil
267
- elsif index >= @transitions_index.length
268
- @transitions.length - 1
269
- else
270
- @transitions_index[index] - 1
271
- end
272
- end
273
- end
274
- end
@@ -1,37 +0,0 @@
1
- module TZInfo
2
- # Represents information about a country returned by ZoneinfoDataSource.
3
- #
4
- # @private
5
- class ZoneinfoCountryInfo < CountryInfo #:nodoc:
6
- # Constructs a new CountryInfo with an ISO 3166 country code, name and
7
- # an array of CountryTimezones.
8
- def initialize(code, name, zones)
9
- super(code, name)
10
- @zones = zones.dup.freeze
11
- @zone_identifiers = nil
12
- end
13
-
14
- # Returns a frozen array of all the zone identifiers for the country ordered
15
- # geographically, most populous first.
16
- def zone_identifiers
17
- # Thread-safety: It is possible that the value of @zone_identifiers may be
18
- # calculated multiple times in concurrently executing threads. It is not
19
- # worth the overhead of locking to ensure that @zone_identifiers is only
20
- # calculated once.
21
-
22
- unless @zone_identifiers
23
- result = zones.collect {|zone| zone.identifier}.freeze
24
- return result if frozen?
25
- @zone_identifiers = result
26
- end
27
-
28
- @zone_identifiers
29
- end
30
-
31
- # Returns a frozen array of all the timezones for the for the country
32
- # ordered geographically, most populous first.
33
- def zones
34
- @zones
35
- end
36
- end
37
- end
@@ -1,496 +0,0 @@
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
-
10
- # An InvalidZoneinfoDirectory exception is raised if the DataSource is
11
- # set to a specific zoneinfo path, which is not a valid zoneinfo directory
12
- # (i.e. a directory containing index files named iso3166.tab and zone.tab
13
- # as well as other timezone files).
14
- class InvalidZoneinfoDirectory < StandardError
15
- end
16
-
17
- # A ZoneinfoDirectoryNotFound exception is raised if no valid zoneinfo
18
- # directory could be found when checking the paths listed in
19
- # ZoneinfoDataSource.search_path. A valid zoneinfo directory is one that
20
- # contains timezone files, a country code index file named iso3166.tab and a
21
- # timezone index file named zone1970.tab or zone.tab.
22
- class ZoneinfoDirectoryNotFound < StandardError
23
- end
24
-
25
- # A DataSource that loads data from a 'zoneinfo' directory containing
26
- # compiled "TZif" version 3 (or earlier) files in addition to iso3166.tab and
27
- # zone1970.tab or zone.tab index files.
28
- #
29
- # To have TZInfo load the system zoneinfo files, call TZInfo::DataSource.set
30
- # as follows:
31
- #
32
- # TZInfo::DataSource.set(:zoneinfo)
33
- #
34
- # To load zoneinfo files from a particular directory, pass the directory to
35
- # TZInfo::DataSource.set:
36
- #
37
- # TZInfo::DataSource.set(:zoneinfo, directory)
38
- #
39
- # Note that the platform used at runtime may limit the range of available
40
- # transition data that can be loaded from zoneinfo files. There are two
41
- # factors to consider:
42
- #
43
- # First of all, the zoneinfo support in TZInfo makes use of Ruby's Time class.
44
- # On 32-bit builds of Ruby 1.8, the Time class only supports 32-bit
45
- # timestamps. This means that only Times between 1901-12-13 20:45:52 and
46
- # 2038-01-19 03:14:07 can be represented. Furthermore, certain platforms only
47
- # allow for positive 32-bit timestamps (notably Windows), making the earliest
48
- # representable time 1970-01-01 00:00:00.
49
- #
50
- # 64-bit builds of Ruby 1.8 and all builds of Ruby 1.9 support 64-bit
51
- # timestamps. This means that there is no practical restriction on the range
52
- # of the Time class on these platforms.
53
- #
54
- # TZInfo will only load transitions that fall within the supported range of
55
- # the Time class. Any queries performed on times outside of this range may
56
- # give inaccurate results.
57
- #
58
- # The second factor concerns the zoneinfo files. Versions of the 'zic' tool
59
- # (used to build zoneinfo files) that were released prior to February 2006
60
- # created zoneinfo files that used 32-bit integers for transition timestamps.
61
- # Later versions of zic produce zoneinfo files that use 64-bit integers. If
62
- # you have 32-bit zoneinfo files on your system, then any queries falling
63
- # outside of the range 1901-12-13 20:45:52 to 2038-01-19 03:14:07 may be
64
- # inaccurate.
65
- #
66
- # Most modern platforms include 64-bit zoneinfo files. However, Mac OS X (up
67
- # to at least 10.8.4) still uses 32-bit zoneinfo files.
68
- #
69
- # To check whether your zoneinfo files contain 32-bit or 64-bit transition
70
- # data, you can run the following code (substituting the identifier of the
71
- # zone you want to test for zone_identifier):
72
- #
73
- # TZInfo::DataSource.set(:zoneinfo)
74
- # dir = TZInfo::DataSource.get.zoneinfo_dir
75
- # File.open(File.join(dir, zone_identifier), 'r') {|f| f.read(5) }
76
- #
77
- # If the last line returns "TZif\\x00", then you have a 32-bit zoneinfo file.
78
- # If it returns "TZif2" or "TZif3" then you have a 64-bit zoneinfo file.
79
- #
80
- # If you require support for 64-bit transitions, but are restricted to 32-bit
81
- # zoneinfo support, then you may want to consider using TZInfo::RubyDataSource
82
- # instead.
83
- class ZoneinfoDataSource < DataSource
84
- # The default value of ZoneinfoDataSource.search_path.
85
- DEFAULT_SEARCH_PATH = ['/usr/share/zoneinfo', '/usr/share/lib/zoneinfo', '/etc/zoneinfo'].freeze
86
-
87
- # The default value of ZoneinfoDataSource.alternate_iso3166_tab_search_path.
88
- DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH = ['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'].freeze
89
-
90
- # Paths to be checked to find the system zoneinfo directory.
91
- @@search_path = DEFAULT_SEARCH_PATH.dup
92
-
93
- # Paths to possible alternate iso3166.tab files (used to locate the
94
- # system-wide iso3166.tab files on FreeBSD and OpenBSD).
95
- @@alternate_iso3166_tab_search_path = DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH.dup
96
-
97
- # An Array of directories that will be checked to find the system zoneinfo
98
- # directory.
99
- #
100
- # Directories are checked in the order they appear in the Array.
101
- #
102
- # The default value is ['/usr/share/zoneinfo', '/usr/share/lib/zoneinfo', '/etc/zoneinfo'].
103
- def self.search_path
104
- @@search_path
105
- end
106
-
107
- # Sets the directories to be checked when locating the system zoneinfo
108
- # directory.
109
- #
110
- # Can be set to an Array of directories or a String containing directories
111
- # separated with File::PATH_SEPARATOR.
112
- #
113
- # Directories are checked in the order they appear in the Array or String.
114
- #
115
- # Set to nil to revert to the default paths.
116
- def self.search_path=(search_path)
117
- @@search_path = process_search_path(search_path, DEFAULT_SEARCH_PATH)
118
- end
119
-
120
- # An Array of paths that will be checked to find an alternate iso3166.tab
121
- # file if one was not included in the zoneinfo directory (for example, on
122
- # FreeBSD and OpenBSD systems).
123
- #
124
- # Paths are checked in the order they appear in the array.
125
- #
126
- # The default value is ['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'].
127
- def self.alternate_iso3166_tab_search_path
128
- @@alternate_iso3166_tab_search_path
129
- end
130
-
131
- # Sets the paths to check to locate an alternate iso3166.tab file if one was
132
- # not included in the zoneinfo directory.
133
- #
134
- # Can be set to an Array of directories or a String containing directories
135
- # separated with File::PATH_SEPARATOR.
136
- #
137
- # Paths are checked in the order they appear in the array.
138
- #
139
- # Set to nil to revert to the default paths.
140
- def self.alternate_iso3166_tab_search_path=(alternate_iso3166_tab_search_path)
141
- @@alternate_iso3166_tab_search_path = process_search_path(alternate_iso3166_tab_search_path, DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH)
142
- end
143
-
144
- # The zoneinfo directory being used.
145
- attr_reader :zoneinfo_dir
146
-
147
- # Creates a new ZoneinfoDataSource.
148
- #
149
- # If zoneinfo_dir is specified, it will be checked and used as the source
150
- # of zoneinfo files.
151
- #
152
- # The directory must contain a file named iso3166.tab and a file named
153
- # either zone1970.tab or zone.tab. These may either be included in the root
154
- # of the directory or in a 'tab' sub-directory and named 'country.tab' and
155
- # 'zone_sun.tab' respectively (as is the case on Solaris.
156
- #
157
- # Additionally, the path to iso3166.tab can be overridden using the
158
- # alternate_iso3166_tab_path parameter.
159
- #
160
- # InvalidZoneinfoDirectory will be raised if the iso3166.tab and
161
- # zone1970.tab or zone.tab files cannot be found using the zoneinfo_dir and
162
- # alternate_iso3166_tab_path parameters.
163
- #
164
- # If zoneinfo_dir is not specified or nil, the paths referenced in
165
- # search_path are searched in order to find a valid zoneinfo directory
166
- # (one that contains zone1970.tab or zone.tab and iso3166.tab files as
167
- # above).
168
- #
169
- # The paths referenced in alternate_iso3166_tab_search_path are also
170
- # searched to find an iso3166.tab file if one of the searched zoneinfo
171
- # directories doesn't contain an iso3166.tab file.
172
- #
173
- # If no valid directory can be found by searching, ZoneinfoDirectoryNotFound
174
- # will be raised.
175
- def initialize(zoneinfo_dir = nil, alternate_iso3166_tab_path = nil)
176
- if zoneinfo_dir
177
- iso3166_tab_path, zone_tab_path = validate_zoneinfo_dir(zoneinfo_dir, alternate_iso3166_tab_path)
178
-
179
- unless iso3166_tab_path && zone_tab_path
180
- raise InvalidZoneinfoDirectory, "#{zoneinfo_dir} is not a directory or doesn't contain a iso3166.tab file and a zone1970.tab or zone.tab file."
181
- end
182
-
183
- @zoneinfo_dir = zoneinfo_dir
184
- else
185
- @zoneinfo_dir, iso3166_tab_path, zone_tab_path = find_zoneinfo_dir
186
-
187
- unless @zoneinfo_dir && iso3166_tab_path && zone_tab_path
188
- raise ZoneinfoDirectoryNotFound, "None of the paths included in TZInfo::ZoneinfoDataSource.search_path are valid zoneinfo directories."
189
- end
190
- end
191
-
192
- @zoneinfo_dir = File.expand_path(@zoneinfo_dir).freeze
193
- @timezone_index = load_timezone_index.freeze
194
- @country_index = load_country_index(iso3166_tab_path, zone_tab_path).freeze
195
- end
196
-
197
- # Returns a TimezoneInfo instance for a given identifier.
198
- # Raises InvalidTimezoneIdentifier if the timezone is not found or the
199
- # identifier is invalid.
200
- def load_timezone_info(identifier)
201
- begin
202
- if @timezone_index.include?(identifier)
203
- path = File.join(@zoneinfo_dir, identifier)
204
-
205
- # Untaint path rather than identifier. We don't want to modify
206
- # identifier. identifier may also be frozen and therefore cannot be
207
- # untainted.
208
- path.untaint
209
-
210
- begin
211
- ZoneinfoTimezoneInfo.new(identifier, path)
212
- rescue InvalidZoneinfoFile => e
213
- raise InvalidTimezoneIdentifier, e.message
214
- end
215
- else
216
- raise InvalidTimezoneIdentifier, 'Invalid identifier'
217
- end
218
- rescue Errno::ENOENT, Errno::ENAMETOOLONG, Errno::ENOTDIR
219
- raise InvalidTimezoneIdentifier, 'Invalid identifier'
220
- rescue Errno::EACCES => e
221
- raise InvalidTimezoneIdentifier, e.message
222
- end
223
- end
224
-
225
- # Returns an array of all the available timezone identifiers.
226
- def timezone_identifiers
227
- @timezone_index
228
- end
229
-
230
- # Returns an array of all the available timezone identifiers for
231
- # data timezones (i.e. those that actually contain definitions).
232
- #
233
- # For ZoneinfoDataSource, this will always be identical to
234
- # timezone_identifers.
235
- def data_timezone_identifiers
236
- @timezone_index
237
- end
238
-
239
- # Returns an array of all the available timezone identifiers that
240
- # are links to other timezones.
241
- #
242
- # For ZoneinfoDataSource, this will always be an empty array.
243
- def linked_timezone_identifiers
244
- [].freeze
245
- end
246
-
247
- # Returns a CountryInfo instance for the given ISO 3166-1 alpha-2
248
- # country code. Raises InvalidCountryCode if the country could not be found
249
- # or the code is invalid.
250
- def load_country_info(code)
251
- info = @country_index[code]
252
- raise InvalidCountryCode, 'Invalid country code' unless info
253
- info
254
- end
255
-
256
- # Returns an array of all the available ISO 3166-1 alpha-2
257
- # country codes.
258
- def country_codes
259
- @country_index.keys.freeze
260
- end
261
-
262
- # Returns the name and information about this DataSource.
263
- def to_s
264
- "Zoneinfo DataSource: #{@zoneinfo_dir}"
265
- end
266
-
267
- # Returns internal object state as a programmer-readable string.
268
- def inspect
269
- "#<#{self.class}: #{@zoneinfo_dir}>"
270
- end
271
-
272
- private
273
-
274
- # Processes a path for use as the search_path or
275
- # alternate_iso3166_tab_search_path.
276
- def self.process_search_path(path, default)
277
- if path
278
- if path.kind_of?(String)
279
- path.split(File::PATH_SEPARATOR)
280
- else
281
- path.collect {|p| p.to_s}
282
- end
283
- else
284
- default.dup
285
- end
286
- end
287
-
288
- # Validates a zoneinfo directory and returns the paths to the iso3166.tab
289
- # and zone1970.tab or zone.tab files if valid. If the directory is not
290
- # valid, returns nil.
291
- #
292
- # The path to the iso3166.tab file may be overriden by passing in a path.
293
- # This is treated as either absolute or relative to the current working
294
- # directory.
295
- def validate_zoneinfo_dir(path, iso3166_tab_path = nil)
296
- if File.directory?(path)
297
- if iso3166_tab_path
298
- return nil unless File.file?(iso3166_tab_path)
299
- else
300
- iso3166_tab_path = resolve_tab_path(path, ['iso3166.tab'], 'country.tab')
301
- return nil unless iso3166_tab_path
302
- end
303
-
304
- zone_tab_path = resolve_tab_path(path, ['zone1970.tab', 'zone.tab'], 'zone_sun.tab')
305
- return nil unless zone_tab_path
306
-
307
- [iso3166_tab_path, zone_tab_path]
308
- else
309
- nil
310
- end
311
- end
312
-
313
- # Attempts to resolve the path to a tab file given its standard names and
314
- # tab sub-directory name (as used on Solaris).
315
- def resolve_tab_path(zoneinfo_path, standard_names, tab_name)
316
- standard_names.each do |standard_name|
317
- path = File.join(zoneinfo_path, standard_name)
318
- return path if File.file?(path)
319
- end
320
-
321
- path = File.join(zoneinfo_path, 'tab', tab_name)
322
- return path if File.file?(path)
323
-
324
- nil
325
- end
326
-
327
- # Finds a zoneinfo directory using search_path and
328
- # alternate_iso3166_tab_search_path. Returns the paths to the directory,
329
- # the iso3166.tab file and the zone.tab file or nil if not found.
330
- def find_zoneinfo_dir
331
- alternate_iso3166_tab_path = self.class.alternate_iso3166_tab_search_path.detect do |path|
332
- File.file?(path)
333
- end
334
-
335
- self.class.search_path.each do |path|
336
- # Try without the alternate_iso3166_tab_path first.
337
- iso3166_tab_path, zone_tab_path = validate_zoneinfo_dir(path)
338
- return path, iso3166_tab_path, zone_tab_path if iso3166_tab_path && zone_tab_path
339
-
340
- if alternate_iso3166_tab_path
341
- iso3166_tab_path, zone_tab_path = validate_zoneinfo_dir(path, alternate_iso3166_tab_path)
342
- return path, iso3166_tab_path, zone_tab_path if iso3166_tab_path && zone_tab_path
343
- end
344
- end
345
-
346
- # Not found.
347
- nil
348
- end
349
-
350
- # Scans @zoneinfo_dir and returns an Array of available timezone
351
- # identifiers.
352
- def load_timezone_index
353
- index = []
354
-
355
- # Ignoring particular files:
356
- # +VERSION is included on Mac OS X.
357
- # leapseconds is a list of leap seconds.
358
- # localtime is the current local timezone (may be a link).
359
- # posix, posixrules and right are directories containing other versions of the zoneinfo files.
360
- # src is a directory containing the tzdata source included on Solaris.
361
- # timeconfig is a symlink included on Slackware.
362
-
363
- enum_timezones(nil, ['+VERSION', 'leapseconds', 'localtime', 'posix', 'posixrules', 'right', 'src', 'timeconfig']) do |identifier|
364
- index << identifier
365
- end
366
-
367
- index.sort
368
- end
369
-
370
- # Recursively scans a directory of timezones, calling the passed in block
371
- # for each identifier found.
372
- def enum_timezones(dir, exclude = [], &block)
373
- Dir.foreach(dir ? File.join(@zoneinfo_dir, dir) : @zoneinfo_dir) do |entry|
374
- unless entry =~ /\./ || exclude.include?(entry)
375
- entry.untaint
376
- path = dir ? File.join(dir, entry) : entry
377
- full_path = File.join(@zoneinfo_dir, path)
378
-
379
- if File.directory?(full_path)
380
- enum_timezones(path, [], &block)
381
- elsif File.file?(full_path)
382
- yield path
383
- end
384
- end
385
- end
386
- end
387
-
388
- # Uses the iso3166.tab and zone1970.tab or zone.tab files to build an index
389
- # of the available countries and their timezones.
390
- def load_country_index(iso3166_tab_path, zone_tab_path)
391
-
392
- # Handle standard 3 to 4 column zone.tab files as well as the 4 to 5
393
- # column format used by Solaris.
394
- #
395
- # On Solaris, an extra column before the comment gives an optional
396
- # linked/alternate timezone identifier (or '-' if not set).
397
- #
398
- # Additionally, there is a section at the end of the file for timezones
399
- # covering regions. These are given lower-case "country" codes. The timezone
400
- # identifier column refers to a continent instead of an identifier. These
401
- # lines will be ignored by TZInfo.
402
- #
403
- # Since the last column is optional in both formats, testing for the
404
- # Solaris format is done in two passes. The first pass identifies if there
405
- # are any lines using 5 columns.
406
-
407
-
408
- # The first column is allowed to be a comma separated list of country
409
- # codes, as used in zone1970.tab (introduced in tzdata 2014f).
410
- #
411
- # The first country code in the comma-separated list is the country that
412
- # contains the city the zone identifer is based on. The first country
413
- # code on each line is considered to be primary with the others
414
- # secondary.
415
- #
416
- # The zones for each country are ordered primary first, then secondary.
417
- # Within the primary and secondary groups, the zones are ordered by their
418
- # order in the file.
419
-
420
- file_is_5_column = false
421
- zone_tab = []
422
-
423
- RubyCoreSupport.open_file(zone_tab_path, 'r', :external_encoding => 'UTF-8', :internal_encoding => 'UTF-8') do |file|
424
- file.each_line do |line|
425
- line.chomp!
426
-
427
- if line =~ /\A([A-Z]{2}(?:,[A-Z]{2})*)\t(?:([+\-])(\d{2})(\d{2})([+\-])(\d{3})(\d{2})|([+\-])(\d{2})(\d{2})(\d{2})([+\-])(\d{3})(\d{2})(\d{2}))\t([^\t]+)(?:\t([^\t]+))?(?:\t([^\t]+))?\z/
428
- codes = $1
429
-
430
- if $2
431
- latitude = dms_to_rational($2, $3, $4)
432
- longitude = dms_to_rational($5, $6, $7)
433
- else
434
- latitude = dms_to_rational($8, $9, $10, $11)
435
- longitude = dms_to_rational($12, $13, $14, $15)
436
- end
437
-
438
- zone_identifier = $16
439
- column4 = $17
440
- column5 = $18
441
-
442
- file_is_5_column = true if column5
443
-
444
- zone_tab << [codes.split(','.freeze), zone_identifier, latitude, longitude, column4, column5]
445
- end
446
- end
447
- end
448
-
449
- primary_zones = {}
450
- secondary_zones = {}
451
-
452
- zone_tab.each do |codes, zone_identifier, latitude, longitude, column4, column5|
453
- description = file_is_5_column ? column5 : column4
454
- country_timezone = CountryTimezone.new(zone_identifier, latitude, longitude, description)
455
-
456
- # codes will always have at least one element
457
-
458
- (primary_zones[codes.first] ||= []) << country_timezone
459
-
460
- codes[1..-1].each do |code|
461
- (secondary_zones[code] ||= []) << country_timezone
462
- end
463
- end
464
-
465
- countries = {}
466
-
467
- RubyCoreSupport.open_file(iso3166_tab_path, 'r', :external_encoding => 'UTF-8', :internal_encoding => 'UTF-8') do |file|
468
- file.each_line do |line|
469
- line.chomp!
470
-
471
- # Handle both the two column alpha-2 and name format used in the tz
472
- # database as well as the 4 column alpha-2, alpha-3, numeric-3 and
473
- # name format used by FreeBSD and OpenBSD.
474
-
475
- if line =~ /\A([A-Z]{2})(?:\t[A-Z]{3}\t[0-9]{3})?\t(.+)\z/
476
- code = $1
477
- name = $2
478
- zones = (primary_zones[code] || []) + (secondary_zones[code] || [])
479
-
480
- countries[code] = ZoneinfoCountryInfo.new(code, name, zones)
481
- end
482
- end
483
- end
484
-
485
- countries
486
- end
487
-
488
- # Converts degrees, minutes and seconds to a Rational.
489
- def dms_to_rational(sign, degrees, minutes, seconds = nil)
490
- result = degrees.to_i + Rational(minutes.to_i, 60)
491
- result += Rational(seconds.to_i, 3600) if seconds
492
- result = -result if sign == '-'.freeze
493
- result
494
- end
495
- end
496
- end