tzinfo 1.2.5

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