tzinfo 1.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +2 -0
  3. data.tar.gz.sig +1 -0
  4. data/.yardopts +6 -0
  5. data/CHANGES.md +797 -0
  6. data/LICENSE +19 -0
  7. data/README.md +152 -0
  8. data/Rakefile +107 -0
  9. data/lib/tzinfo.rb +40 -0
  10. data/lib/tzinfo/country.rb +196 -0
  11. data/lib/tzinfo/country_index_definition.rb +31 -0
  12. data/lib/tzinfo/country_info.rb +42 -0
  13. data/lib/tzinfo/country_timezone.rb +135 -0
  14. data/lib/tzinfo/data_source.rb +190 -0
  15. data/lib/tzinfo/data_timezone.rb +58 -0
  16. data/lib/tzinfo/data_timezone_info.rb +55 -0
  17. data/lib/tzinfo/info_timezone.rb +30 -0
  18. data/lib/tzinfo/linked_timezone.rb +63 -0
  19. data/lib/tzinfo/linked_timezone_info.rb +26 -0
  20. data/lib/tzinfo/offset_rationals.rb +77 -0
  21. data/lib/tzinfo/ruby_core_support.rb +176 -0
  22. data/lib/tzinfo/ruby_country_info.rb +74 -0
  23. data/lib/tzinfo/ruby_data_source.rb +138 -0
  24. data/lib/tzinfo/time_or_datetime.rb +340 -0
  25. data/lib/tzinfo/timezone.rb +673 -0
  26. data/lib/tzinfo/timezone_definition.rb +36 -0
  27. data/lib/tzinfo/timezone_index_definition.rb +54 -0
  28. data/lib/tzinfo/timezone_info.rb +30 -0
  29. data/lib/tzinfo/timezone_offset.rb +101 -0
  30. data/lib/tzinfo/timezone_period.rb +245 -0
  31. data/lib/tzinfo/timezone_proxy.rb +105 -0
  32. data/lib/tzinfo/timezone_transition.rb +130 -0
  33. data/lib/tzinfo/timezone_transition_definition.rb +104 -0
  34. data/lib/tzinfo/transition_data_timezone_info.rb +274 -0
  35. data/lib/tzinfo/zoneinfo_country_info.rb +37 -0
  36. data/lib/tzinfo/zoneinfo_data_source.rb +496 -0
  37. data/lib/tzinfo/zoneinfo_timezone_info.rb +298 -0
  38. data/test/tc_country.rb +236 -0
  39. data/test/tc_country_index_definition.rb +69 -0
  40. data/test/tc_country_info.rb +16 -0
  41. data/test/tc_country_timezone.rb +173 -0
  42. data/test/tc_data_source.rb +218 -0
  43. data/test/tc_data_timezone.rb +99 -0
  44. data/test/tc_data_timezone_info.rb +18 -0
  45. data/test/tc_info_timezone.rb +34 -0
  46. data/test/tc_linked_timezone.rb +155 -0
  47. data/test/tc_linked_timezone_info.rb +23 -0
  48. data/test/tc_offset_rationals.rb +23 -0
  49. data/test/tc_ruby_core_support.rb +168 -0
  50. data/test/tc_ruby_country_info.rb +110 -0
  51. data/test/tc_ruby_data_source.rb +165 -0
  52. data/test/tc_time_or_datetime.rb +660 -0
  53. data/test/tc_timezone.rb +1359 -0
  54. data/test/tc_timezone_definition.rb +113 -0
  55. data/test/tc_timezone_index_definition.rb +73 -0
  56. data/test/tc_timezone_info.rb +11 -0
  57. data/test/tc_timezone_london.rb +143 -0
  58. data/test/tc_timezone_melbourne.rb +142 -0
  59. data/test/tc_timezone_new_york.rb +142 -0
  60. data/test/tc_timezone_offset.rb +126 -0
  61. data/test/tc_timezone_period.rb +555 -0
  62. data/test/tc_timezone_proxy.rb +136 -0
  63. data/test/tc_timezone_transition.rb +366 -0
  64. data/test/tc_timezone_transition_definition.rb +295 -0
  65. data/test/tc_timezone_utc.rb +27 -0
  66. data/test/tc_transition_data_timezone_info.rb +433 -0
  67. data/test/tc_zoneinfo_country_info.rb +78 -0
  68. data/test/tc_zoneinfo_data_source.rb +1204 -0
  69. data/test/tc_zoneinfo_timezone_info.rb +1234 -0
  70. data/test/test_utils.rb +188 -0
  71. data/test/ts_all.rb +7 -0
  72. data/test/ts_all_ruby.rb +5 -0
  73. data/test/ts_all_zoneinfo.rb +9 -0
  74. data/test/tzinfo-data/tzinfo/data.rb +8 -0
  75. data/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb +89 -0
  76. data/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb +315 -0
  77. data/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb +218 -0
  78. data/test/tzinfo-data/tzinfo/data/definitions/EST.rb +19 -0
  79. data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb +21 -0
  80. data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb +21 -0
  81. data/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb +21 -0
  82. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb +261 -0
  83. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb +186 -0
  84. data/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb +321 -0
  85. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb +265 -0
  86. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb +220 -0
  87. data/test/tzinfo-data/tzinfo/data/definitions/UTC.rb +16 -0
  88. data/test/tzinfo-data/tzinfo/data/indexes/countries.rb +927 -0
  89. data/test/tzinfo-data/tzinfo/data/indexes/timezones.rb +596 -0
  90. data/test/tzinfo-data/tzinfo/data/version.rb +14 -0
  91. data/test/zoneinfo/America/Argentina/Buenos_Aires +0 -0
  92. data/test/zoneinfo/America/New_York +0 -0
  93. data/test/zoneinfo/Australia/Melbourne +0 -0
  94. data/test/zoneinfo/EST +0 -0
  95. data/test/zoneinfo/Etc/UTC +0 -0
  96. data/test/zoneinfo/Europe/Amsterdam +0 -0
  97. data/test/zoneinfo/Europe/Andorra +0 -0
  98. data/test/zoneinfo/Europe/London +0 -0
  99. data/test/zoneinfo/Europe/Paris +0 -0
  100. data/test/zoneinfo/Europe/Prague +0 -0
  101. data/test/zoneinfo/Factory +0 -0
  102. data/test/zoneinfo/iso3166.tab +275 -0
  103. data/test/zoneinfo/leapseconds +61 -0
  104. data/test/zoneinfo/posix/Europe/London +0 -0
  105. data/test/zoneinfo/posixrules +0 -0
  106. data/test/zoneinfo/right/Europe/London +0 -0
  107. data/test/zoneinfo/zone.tab +439 -0
  108. data/test/zoneinfo/zone1970.tab +369 -0
  109. data/tzinfo.gemspec +21 -0
  110. metadata +191 -0
  111. metadata.gz.sig +0 -0
@@ -0,0 +1,298 @@
1
+ module TZInfo
2
+ using RubyCoreSupport::UntaintExt if RubyCoreSupport.const_defined?(:UntaintExt)
3
+
4
+ # An InvalidZoneinfoFile exception is raised if an attempt is made to load an
5
+ # invalid zoneinfo file.
6
+ class InvalidZoneinfoFile < StandardError
7
+ end
8
+
9
+ # Represents a timezone defined by a compiled zoneinfo TZif (\0, 2 or 3) file.
10
+ #
11
+ # @private
12
+ class ZoneinfoTimezoneInfo < TransitionDataTimezoneInfo #:nodoc:
13
+
14
+ # Minimum supported timestamp (inclusive).
15
+ #
16
+ # Time.utc(1700, 1, 1).to_i
17
+ MIN_TIMESTAMP = -8520336000
18
+
19
+ # Maximum supported timestamp (exclusive).
20
+ #
21
+ # Time.utc(2500, 1, 1).to_i
22
+ MAX_TIMESTAMP = 16725225600
23
+
24
+ # Constructs the new ZoneinfoTimezoneInfo with an identifier and path
25
+ # to the file.
26
+ def initialize(identifier, file_path)
27
+ super(identifier)
28
+
29
+ File.open(file_path, 'rb') do |file|
30
+ parse(file)
31
+ end
32
+ end
33
+
34
+ private
35
+ # Unpack will return unsigned 32-bit integers. Translate to
36
+ # signed 32-bit.
37
+ def make_signed_int32(long)
38
+ long >= 0x80000000 ? long - 0x100000000 : long
39
+ end
40
+
41
+ # Unpack will return a 64-bit integer as two unsigned 32-bit integers
42
+ # (most significant first). Translate to signed 64-bit
43
+ def make_signed_int64(high, low)
44
+ unsigned = (high << 32) | low
45
+ unsigned >= 0x8000000000000000 ? unsigned - 0x10000000000000000 : unsigned
46
+ end
47
+
48
+ # Read bytes from file and check that the correct number of bytes could
49
+ # be read. Raises InvalidZoneinfoFile if the number of bytes didn't match
50
+ # the number requested.
51
+ def check_read(file, bytes)
52
+ result = file.read(bytes)
53
+
54
+ unless result && result.length == bytes
55
+ raise InvalidZoneinfoFile, "Expected #{bytes} bytes reading '#{file.path}', but got #{result ? result.length : 0} bytes"
56
+ end
57
+
58
+ result
59
+ end
60
+
61
+ # Zoneinfo files don't include the offset from standard time (std_offset)
62
+ # for DST periods. Derive the base offset (utc_offset) where DST is
63
+ # observed from either the previous or next non-DST period.
64
+ #
65
+ # Returns the index of the offset to be used prior to the first
66
+ # transition.
67
+ def derive_offsets(transitions, offsets)
68
+ # The first non-DST offset (if there is one) is the offset observed
69
+ # before the first transition. Fallback to the first DST offset if there
70
+ # are no non-DST offsets.
71
+ first_non_dst_offset_index = offsets.index {|o| !o[:is_dst] }
72
+ first_offset_index = first_non_dst_offset_index || 0
73
+ return first_offset_index if transitions.empty?
74
+
75
+ # Determine the utc_offset of the next non-dst offset at each transition.
76
+ utc_offset_from_next = nil
77
+
78
+ transitions.reverse_each do |transition|
79
+ offset = offsets[transition[:offset]]
80
+ if offset[:is_dst]
81
+ transition[:utc_offset_from_next] = utc_offset_from_next if utc_offset_from_next
82
+ else
83
+ utc_offset_from_next = offset[:utc_total_offset]
84
+ end
85
+ end
86
+
87
+ utc_offset_from_previous = first_non_dst_offset_index ? offsets[first_non_dst_offset_index][:utc_total_offset] : nil
88
+ defined_offsets = {}
89
+
90
+ transitions.each do |transition|
91
+ offset_index = transition[:offset]
92
+ offset = offsets[offset_index]
93
+ utc_total_offset = offset[:utc_total_offset]
94
+
95
+ if offset[:is_dst]
96
+ utc_offset_from_next = transition[:utc_offset_from_next]
97
+
98
+ difference_to_previous = (utc_total_offset - (utc_offset_from_previous || utc_total_offset)).abs
99
+ difference_to_next = (utc_total_offset - (utc_offset_from_next || utc_total_offset)).abs
100
+
101
+ utc_offset = if difference_to_previous == 3600
102
+ utc_offset_from_previous
103
+ elsif difference_to_next == 3600
104
+ utc_offset_from_next
105
+ elsif difference_to_previous > 0 && difference_to_next > 0
106
+ difference_to_previous < difference_to_next ? utc_offset_from_previous : utc_offset_from_next
107
+ elsif difference_to_previous > 0
108
+ utc_offset_from_previous
109
+ elsif difference_to_next > 0
110
+ utc_offset_from_next
111
+ else
112
+ # No difference, assume a 1 hour offset from standard time.
113
+ utc_total_offset - 3600
114
+ end
115
+
116
+ if !offset[:utc_offset]
117
+ offset[:utc_offset] = utc_offset
118
+ defined_offsets[offset] = offset_index
119
+ elsif offset[:utc_offset] != utc_offset
120
+ # An earlier transition has already derived a different
121
+ # utc_offset. Define a new offset or reuse an existing identically
122
+ # defined offset.
123
+ new_offset = offset.dup
124
+ new_offset[:utc_offset] = utc_offset
125
+
126
+ offset_index = defined_offsets[new_offset]
127
+
128
+ unless offset_index
129
+ offsets << new_offset
130
+ offset_index = offsets.length - 1
131
+ defined_offsets[new_offset] = offset_index
132
+ end
133
+
134
+ transition[:offset] = offset_index
135
+ end
136
+ else
137
+ utc_offset_from_previous = utc_total_offset
138
+ end
139
+ end
140
+
141
+ first_offset_index
142
+ end
143
+
144
+ # Defines an offset for the timezone based on the given index and offset
145
+ # Hash.
146
+ def define_offset(index, offset)
147
+ utc_total_offset = offset[:utc_total_offset]
148
+ utc_offset = offset[:utc_offset]
149
+
150
+ if utc_offset
151
+ # DST offset with base utc_offset derived by derive_offsets.
152
+ std_offset = utc_total_offset - utc_offset
153
+ elsif offset[:is_dst]
154
+ # DST offset unreferenced by a transition (offset in use before the
155
+ # first transition). No derived base UTC offset, so assume 1 hour
156
+ # DST.
157
+ utc_offset = utc_total_offset - 3600
158
+ std_offset = 3600
159
+ else
160
+ # Non-DST offset.
161
+ utc_offset = utc_total_offset
162
+ std_offset = 0
163
+ end
164
+
165
+ offset index, utc_offset, std_offset, offset[:abbr].untaint.to_sym
166
+ end
167
+
168
+ # Parses a zoneinfo file and intializes the DataTimezoneInfo structures.
169
+ def parse(file)
170
+ magic, version, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt =
171
+ check_read(file, 44).unpack('a4 a x15 NNNNNN')
172
+
173
+ if magic != 'TZif'
174
+ raise InvalidZoneinfoFile, "The file '#{file.path}' does not start with the expected header."
175
+ end
176
+
177
+ if (version == '2' || version == '3') && RubyCoreSupport.time_supports_64bit
178
+ # Skip the first 32-bit section and read the header of the second 64-bit section
179
+ file.seek(timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisgmtcnt + ttisstdcnt, IO::SEEK_CUR)
180
+
181
+ prev_version = version
182
+
183
+ magic, version, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt =
184
+ check_read(file, 44).unpack('a4 a x15 NNNNNN')
185
+
186
+ unless magic == 'TZif' && (version == prev_version)
187
+ raise InvalidZoneinfoFile, "The file '#{file.path}' contains an invalid 64-bit section header."
188
+ end
189
+
190
+ using_64bit = true
191
+ elsif version != '3' && version != '2' && version != "\0"
192
+ raise InvalidZoneinfoFile, "The file '#{file.path}' contains a version of the zoneinfo format that is not currently supported."
193
+ else
194
+ using_64bit = false
195
+ end
196
+
197
+ unless leapcnt == 0
198
+ raise InvalidZoneinfoFile, "The zoneinfo file '#{file.path}' contains leap second data. TZInfo requires zoneinfo files that omit leap seconds."
199
+ end
200
+
201
+ transitions = []
202
+
203
+ if using_64bit
204
+ timecnt.times do |i|
205
+ high, low = check_read(file, 8).unpack('NN'.freeze)
206
+ transition_time = make_signed_int64(high, low)
207
+ transitions << {:at => transition_time}
208
+ end
209
+ else
210
+ timecnt.times do |i|
211
+ transition_time = make_signed_int32(check_read(file, 4).unpack('N'.freeze)[0])
212
+ transitions << {:at => transition_time}
213
+ end
214
+ end
215
+
216
+ timecnt.times do |i|
217
+ localtime_type = check_read(file, 1).unpack('C'.freeze)[0]
218
+ transitions[i][:offset] = localtime_type
219
+ end
220
+
221
+ offsets = []
222
+
223
+ typecnt.times do |i|
224
+ gmtoff, isdst, abbrind = check_read(file, 6).unpack('NCC'.freeze)
225
+ gmtoff = make_signed_int32(gmtoff)
226
+ isdst = isdst == 1
227
+ offset = {:utc_total_offset => gmtoff, :is_dst => isdst, :abbr_index => abbrind}
228
+
229
+ unless isdst
230
+ offset[:utc_offset] = gmtoff
231
+ offset[:std_offset] = 0
232
+ end
233
+
234
+ offsets << offset
235
+ end
236
+
237
+ abbrev = check_read(file, charcnt)
238
+
239
+ offsets.each do |o|
240
+ abbrev_start = o[:abbr_index]
241
+ raise InvalidZoneinfoFile, "Abbreviation index is out of range in file '#{file.path}'" unless abbrev_start < abbrev.length
242
+
243
+ abbrev_end = abbrev.index("\0", abbrev_start)
244
+ raise InvalidZoneinfoFile, "Missing abbreviation null terminator in file '#{file.path}'" unless abbrev_end
245
+
246
+ o[:abbr] = RubyCoreSupport.force_encoding(abbrev[abbrev_start...abbrev_end], 'UTF-8')
247
+ end
248
+
249
+ transitions.each do |t|
250
+ if t[:offset] < 0 || t[:offset] >= offsets.length
251
+ raise InvalidZoneinfoFile, "Invalid offset referenced by transition in file '#{file.path}'."
252
+ end
253
+ end
254
+
255
+ # Derive the offsets from standard time (std_offset).
256
+ first_offset_index = derive_offsets(transitions, offsets)
257
+
258
+ define_offset(first_offset_index, offsets[first_offset_index])
259
+
260
+ offsets.each_with_index do |o, i|
261
+ define_offset(i, o) unless i == first_offset_index
262
+ end
263
+
264
+ if !using_64bit && !RubyCoreSupport.time_supports_negative
265
+ # Filter out transitions that are not supported by Time on this
266
+ # platform.
267
+
268
+ # Move the last transition before the epoch up to the epoch. This
269
+ # allows for accurate conversions for all supported timestamps on the
270
+ # platform.
271
+
272
+ before_epoch, after_epoch = transitions.partition {|t| t[:at] < 0}
273
+
274
+ if before_epoch.length > 0 && after_epoch.length > 0 && after_epoch.first[:at] != 0
275
+ last_before = before_epoch.last
276
+ last_before[:at] = 0
277
+ transitions = [last_before] + after_epoch
278
+ else
279
+ transitions = after_epoch
280
+ end
281
+ end
282
+
283
+ # Ignore transitions that occur outside of a defined window. The
284
+ # transition index cannot handle a large range of transition times.
285
+ #
286
+ # This is primarily intended to ignore the far in the past transition
287
+ # added in zic 2014c (at timestamp -2**63 in zic 2014c and at the
288
+ # approximate time of the big bang from zic 2014d).
289
+ transitions.each do |t|
290
+ at = t[:at]
291
+ if at >= MIN_TIMESTAMP && at < MAX_TIMESTAMP
292
+ time = Time.at(at).utc
293
+ transition time.year, time.mon, t[:offset], at
294
+ end
295
+ end
296
+ end
297
+ end
298
+ end
@@ -0,0 +1,236 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
2
+
3
+ include TZInfo
4
+
5
+ using TaintExt if Module.const_defined?(:TaintExt)
6
+
7
+ class TCCountry < Minitest::Test
8
+ def setup
9
+ @orig_data_source = DataSource.get
10
+ Country.send :init_countries
11
+ end
12
+
13
+ def teardown
14
+ DataSource.set(@orig_data_source)
15
+ end
16
+
17
+ def test_get_valid
18
+ c = Country.get('GB')
19
+
20
+ assert c
21
+ assert_equal('GB', c.code)
22
+ end
23
+
24
+ def test_get_not_exist
25
+ assert_raises(InvalidCountryCode) {
26
+ Country.get('ZZ')
27
+ }
28
+ end
29
+
30
+ def test_get_invalid
31
+ assert_raises(InvalidCountryCode) {
32
+ Country.get('../Countries/GB')
33
+ }
34
+ end
35
+
36
+ def test_get_nil
37
+ assert_raises(InvalidCountryCode) {
38
+ Country.get(nil)
39
+ }
40
+ end
41
+
42
+ def test_get_case
43
+ assert_raises(InvalidCountryCode) {
44
+ Country.get('gb')
45
+ }
46
+ end
47
+
48
+ def test_get_tainted_loaded
49
+ Country.get('GB')
50
+
51
+ safe_test(:unavailable => :skip) do
52
+ code = 'GB'.dup.taint
53
+ assert(code.tainted?)
54
+ country = Country.get(code)
55
+ assert_equal('GB', country.code)
56
+ assert(code.tainted?)
57
+ end
58
+ end
59
+
60
+ def test_get_tainted_and_frozen_loaded
61
+ Country.get('GB')
62
+
63
+ safe_test do
64
+ country = Country.get('GB'.dup.taint.freeze)
65
+ assert_equal('GB', country.code)
66
+ end
67
+ end
68
+
69
+ def test_get_tainted_not_previously_loaded
70
+ safe_test(:unavailable => :skip) do
71
+ code = 'GB'.dup.taint
72
+ assert(code.tainted?)
73
+ country = Country.get(code)
74
+ assert_equal('GB', country.code)
75
+ assert(code.tainted?)
76
+ end
77
+ end
78
+
79
+ def test_get_tainted_and_frozen_not_previously_loaded
80
+ safe_test do
81
+ country = Country.get('GB'.dup.taint.freeze)
82
+ assert_equal('GB', country.code)
83
+ end
84
+ end
85
+
86
+ def test_new_nil
87
+ assert_raises(InvalidCountryCode) {
88
+ Country.new(nil)
89
+ }
90
+ end
91
+
92
+ def test_new_arg
93
+ c = Country.new('GB')
94
+ assert_same(Country.get('GB'), c)
95
+ end
96
+
97
+ def test_new_arg_not_exist
98
+ assert_raises(InvalidCountryCode) {
99
+ Country.new('ZZ')
100
+ }
101
+ end
102
+
103
+ def test_all_codes
104
+ all_codes = Country.all_codes
105
+ assert_kind_of(Array, all_codes)
106
+ end
107
+
108
+ def test_all
109
+ all = Country.all
110
+ assert_equal(Country.all_codes, all.collect {|c| c.code})
111
+ end
112
+
113
+ def test_code
114
+ assert_equal('US', Country.get('US').code)
115
+ end
116
+
117
+ def test_name
118
+ assert_kind_of(String, Country.get('US').name)
119
+ end
120
+
121
+ def test_to_s
122
+ assert_equal(Country.get('US').name, Country.get('US').to_s)
123
+ assert_equal(Country.get('GB').name, Country.get('GB').to_s)
124
+ end
125
+
126
+ def test_zone_identifiers
127
+ zone_names = Country.get('US').zone_names
128
+ assert_kind_of(Array, zone_names)
129
+ assert_equal(true, zone_names.frozen?)
130
+ end
131
+
132
+ def test_zone_names
133
+ assert_equal(Country.get('US').zone_identifiers, Country.get('US').zone_names)
134
+ end
135
+
136
+ def test_zones
137
+ zones = Country.get('US').zones
138
+ assert_kind_of(Array, zones)
139
+ assert_equal(Country.get('US').zone_identifiers, zones.collect {|z| z.identifier})
140
+
141
+ zones.each {|z| assert_kind_of(TimezoneProxy, z)}
142
+ end
143
+
144
+ def test_zone_info
145
+ zones = Country.get('US').zone_info
146
+ assert_kind_of(Array, zones)
147
+ assert_equal(true, zones.frozen?)
148
+
149
+ assert_equal(Country.get('US').zone_identifiers, zones.collect {|z| z.identifier})
150
+ assert_equal(Country.get('US').zone_identifiers, zones.collect {|z| z.timezone.identifier})
151
+
152
+ zones.each {|z| assert_kind_of(CountryTimezone, z)}
153
+ end
154
+
155
+ def test_compare
156
+ assert_equal(0, Country.get('GB') <=> Country.get('GB'))
157
+ assert_equal(-1, Country.get('GB') <=> Country.get('US'))
158
+ assert_equal(1, Country.get('US') <=> Country.get('GB'))
159
+ assert_equal(-1, Country.get('FR') <=> Country.get('US'))
160
+ assert_equal(1, Country.get('US') <=> Country.get('FR'))
161
+ end
162
+
163
+ def test_compare_non_comparable
164
+ assert_nil(Country.get('GB') <=> Object.new)
165
+ end
166
+
167
+ def test_equality
168
+ assert_equal(true, Country.get('GB') == Country.get('GB'))
169
+ assert_equal(false, Country.get('GB') == Country.get('US'))
170
+ assert(!(Country.get('GB') == Object.new))
171
+ end
172
+
173
+ def test_eql
174
+ assert_equal(true, Country.get('GB').eql?(Country.get('GB')))
175
+ assert_equal(false, Country.get('GB').eql?(Country.get('US')))
176
+ assert(!Country.get('GB').eql?(Object.new))
177
+ end
178
+
179
+ def test_hash
180
+ assert_equal('GB'.hash, Country.get('GB').hash)
181
+ assert_equal('US'.hash, Country.get('US').hash)
182
+ end
183
+
184
+ def test_marshal
185
+ c = Country.get('US')
186
+
187
+ # Should get back the same instance because load calls Country.get.
188
+ assert_same(c, Marshal.load(Marshal.dump(c)))
189
+ end
190
+
191
+ def test_reload
192
+ # If country gets reloaded for some reason, it needs to force a reload of
193
+ # the country index.
194
+
195
+ assert_equal('US', Country.get('US').code)
196
+
197
+ # Suppress redefined method warnings.
198
+ without_warnings do
199
+ load 'tzinfo/country.rb'
200
+ end
201
+
202
+ assert_equal('US', Country.get('US').code)
203
+ end
204
+
205
+ def test_get_missing_data_source
206
+ DataSource.set(DataSource.new)
207
+
208
+ assert_raises(InvalidDataSource) do
209
+ Country.get('GB')
210
+ end
211
+ end
212
+
213
+ def test_new_missing_data_source
214
+ DataSource.set(DataSource.new)
215
+
216
+ assert_raises(InvalidDataSource) do
217
+ Country.new('GB')
218
+ end
219
+ end
220
+
221
+ def test_all_codes_missing_data_source
222
+ DataSource.set(DataSource.new)
223
+
224
+ assert_raises(InvalidDataSource) do
225
+ Country.all_codes
226
+ end
227
+ end
228
+
229
+ def test_all_missing_data_source
230
+ DataSource.set(DataSource.new)
231
+
232
+ assert_raises(InvalidDataSource) do
233
+ Country.all
234
+ end
235
+ end
236
+ end