tzinfo 1.2.5 → 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 +0 -0
  4. data/.yardopts +3 -0
  5. data/CHANGES.md +490 -377
  6. data/LICENSE +13 -13
  7. data/README.md +368 -113
  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 -498
  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 +63 -121
  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 -146
  63. data/lib/tzinfo/ruby_country_info.rb +0 -74
  64. data/lib/tzinfo/ruby_data_source.rb +0 -136
  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 -488
  73. data/lib/tzinfo/zoneinfo_timezone_info.rb +0 -296
  74. data/test/tc_country.rb +0 -234
  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 -143
  88. data/test/tc_time_or_datetime.rb +0 -654
  89. data/test/tc_timezone.rb +0 -1350
  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 -423
  103. data/test/tc_zoneinfo_country_info.rb +0 -78
  104. data/test/tc_zoneinfo_data_source.rb +0 -1195
  105. data/test/tc_zoneinfo_timezone_info.rb +0 -1232
  106. data/test/test_utils.rb +0 -163
  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 -7
  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,78 +0,0 @@
1
- require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
2
-
3
- include TZInfo
4
-
5
- class TCZoneinfoCountryInfo < Minitest::Test
6
-
7
- def test_code
8
- ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', []) {|c| }
9
- assert_equal('ZZ', ci.code)
10
- end
11
-
12
- def test_name
13
- ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', []) {|c| }
14
- assert_equal('Zzz', ci.name)
15
- end
16
-
17
- def test_zone_identifiers_empty
18
- ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', []) {|c| }
19
- assert(ci.zone_identifiers.empty?)
20
- assert(ci.zone_identifiers.frozen?)
21
- end
22
-
23
- def test_zone_identifiers
24
- zones = [
25
- CountryTimezone.new('ZZ/TimezoneB', Rational(1, 2), Rational(1, 2), 'Timezone B'),
26
- CountryTimezone.new('ZZ/TimezoneA', Rational(1, 4), Rational(1, 4), 'Timezone A'),
27
- CountryTimezone.new('ZZ/TimezoneC', Rational(-10, 3), Rational(-20, 7), 'C'),
28
- CountryTimezone.new('ZZ/TimezoneD', Rational(-10, 3), Rational(-20, 7))
29
- ]
30
-
31
- ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', zones)
32
-
33
- assert_equal(['ZZ/TimezoneB', 'ZZ/TimezoneA', 'ZZ/TimezoneC', 'ZZ/TimezoneD'], ci.zone_identifiers)
34
- assert(ci.zone_identifiers.frozen?)
35
- assert(!ci.zones.equal?(zones))
36
- assert(!zones.frozen?)
37
- end
38
-
39
- def test_zone_identifiers_after_freeze
40
- zones = [
41
- CountryTimezone.new('ZZ/TimezoneB', Rational(1, 2), Rational(1, 2), 'Timezone B'),
42
- CountryTimezone.new('ZZ/TimezoneA', Rational(1, 4), Rational(1, 4), 'Timezone A'),
43
- CountryTimezone.new('ZZ/TimezoneC', Rational(-10, 3), Rational(-20, 7), 'C'),
44
- CountryTimezone.new('ZZ/TimezoneD', Rational(-10, 3), Rational(-20, 7))
45
- ]
46
-
47
- ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', zones)
48
- ci.freeze
49
-
50
- assert_equal(['ZZ/TimezoneB', 'ZZ/TimezoneA', 'ZZ/TimezoneC', 'ZZ/TimezoneD'], ci.zone_identifiers)
51
- end
52
-
53
- def test_zones_empty
54
- ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', [])
55
- assert(ci.zones.empty?)
56
- assert(ci.zones.frozen?)
57
- end
58
-
59
- def test_zones
60
- zones = [
61
- CountryTimezone.new('ZZ/TimezoneB', Rational(1, 2), Rational(1, 2), 'Timezone B'),
62
- CountryTimezone.new('ZZ/TimezoneA', Rational(1, 4), Rational(1, 4), 'Timezone A'),
63
- CountryTimezone.new('ZZ/TimezoneC', Rational(-10, 3), Rational(-20, 7), 'C'),
64
- CountryTimezone.new('ZZ/TimezoneD', Rational(-10, 3), Rational(-20, 7))
65
- ]
66
-
67
- ci = ZoneinfoCountryInfo.new('ZZ', 'Zzz', zones)
68
-
69
- assert_equal([CountryTimezone.new('ZZ/TimezoneB', Rational(1, 2), Rational(1, 2), 'Timezone B'),
70
- CountryTimezone.new('ZZ/TimezoneA', Rational(1, 4), Rational(1, 4), 'Timezone A'),
71
- CountryTimezone.new('ZZ/TimezoneC', Rational(-10, 3), Rational(-20, 7), 'C'),
72
- CountryTimezone.new('ZZ/TimezoneD', Rational(-10, 3), Rational(-20, 7))],
73
- ci.zones)
74
- assert(ci.zones.frozen?)
75
- assert(!ci.zones.equal?(zones))
76
- assert(!zones.frozen?)
77
- end
78
- end
@@ -1,1195 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.join(File.expand_path(File.dirname(__FILE__)), 'test_utils')
4
- require 'fileutils'
5
- require 'pathname'
6
- require 'tmpdir'
7
-
8
- include TZInfo
9
-
10
- class TCZoneinfoDataSource < Minitest::Test
11
- ZONEINFO_DIR = File.join(File.expand_path(File.dirname(__FILE__)), 'zoneinfo').untaint
12
-
13
- def setup
14
- @orig_search_path = ZoneinfoDataSource.search_path.clone
15
- @orig_alternate_iso3166_tab_search_path = ZoneinfoDataSource.alternate_iso3166_tab_search_path.clone
16
- @orig_pwd = FileUtils.pwd
17
-
18
- # A zoneinfo directory containing files needed by the tests.
19
- # The symlinks in this directory are set up in test_utils.rb.
20
- @data_source = ZoneinfoDataSource.new(ZONEINFO_DIR)
21
- end
22
-
23
- def teardown
24
- ZoneinfoDataSource.search_path = @orig_search_path
25
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = @orig_alternate_iso3166_tab_search_path
26
- FileUtils.chdir(@orig_pwd)
27
- end
28
-
29
- def test_default_search_path
30
- assert_equal(['/usr/share/zoneinfo', '/usr/share/lib/zoneinfo', '/etc/zoneinfo'], ZoneinfoDataSource.search_path)
31
- assert_equal(false, ZoneinfoDataSource.search_path.frozen?)
32
- end
33
-
34
- def test_set_search_path_default
35
- ZoneinfoDataSource.search_path = ['/tmp/zoneinfo1', '/tmp/zoneinfo2']
36
- assert_equal(['/tmp/zoneinfo1', '/tmp/zoneinfo2'], ZoneinfoDataSource.search_path)
37
-
38
- ZoneinfoDataSource.search_path = nil
39
- assert_equal(['/usr/share/zoneinfo', '/usr/share/lib/zoneinfo', '/etc/zoneinfo'], ZoneinfoDataSource.search_path)
40
- assert_equal(false, ZoneinfoDataSource.search_path.frozen?)
41
- end
42
-
43
- def test_set_search_path_array
44
- path = ['/tmp/zoneinfo1', '/tmp/zoneinfo2']
45
- ZoneinfoDataSource.search_path = path
46
- assert_equal(['/tmp/zoneinfo1', '/tmp/zoneinfo2'], ZoneinfoDataSource.search_path)
47
- refute_same(path, ZoneinfoDataSource.search_path)
48
- end
49
-
50
- def test_set_search_path_array_to_s
51
- ZoneinfoDataSource.search_path = [Pathname.new('/tmp/zoneinfo3')]
52
- assert_equal(['/tmp/zoneinfo3'], ZoneinfoDataSource.search_path)
53
- end
54
-
55
- def test_set_search_path_string
56
- ZoneinfoDataSource.search_path = ['/tmp/zoneinfo4', '/tmp/zoneinfo5'].join(File::PATH_SEPARATOR)
57
- assert_equal(['/tmp/zoneinfo4', '/tmp/zoneinfo5'], ZoneinfoDataSource.search_path)
58
- end
59
-
60
- def test_default_alternate_iso3166_tab_search_path
61
- assert_equal(['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
62
- assert_equal(false, ZoneinfoDataSource.alternate_iso3166_tab_search_path.frozen?)
63
- end
64
-
65
- def test_set_alternate_iso3166_tab_search_path_default
66
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = ['/tmp/iso3166.tab', '/tmp/iso3166']
67
- assert_equal(['/tmp/iso3166.tab', '/tmp/iso3166'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
68
-
69
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = nil
70
- assert_equal(['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
71
- assert_equal(false, ZoneinfoDataSource.alternate_iso3166_tab_search_path.frozen?)
72
- end
73
-
74
- def test_set_alternate_iso3166_tab_search_path_array
75
- path = ['/tmp/iso3166.tab', '/tmp/iso3166']
76
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = path
77
- assert_equal(['/tmp/iso3166.tab', '/tmp/iso3166'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
78
- refute_same(path, ZoneinfoDataSource.alternate_iso3166_tab_search_path)
79
- end
80
-
81
- def test_set_alternate_iso3166_tab_search_path_array_to_s
82
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = [Pathname.new('/tmp/iso3166.tab')]
83
- assert_equal(['/tmp/iso3166.tab'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
84
- end
85
-
86
- def test_set_alternate_iso3166_tab_search_path_string
87
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = ['/tmp/iso3166.tab', '/tmp/iso3166'].join(File::PATH_SEPARATOR)
88
- assert_equal(['/tmp/iso3166.tab', '/tmp/iso3166'], ZoneinfoDataSource.alternate_iso3166_tab_search_path)
89
- end
90
-
91
- def test_new_search
92
- Dir.mktmpdir('tzinfo_test_dir1') do |dir1|
93
- Dir.mktmpdir('tzinfo_test_dir2') do |dir2|
94
- Dir.mktmpdir('tzinfo_test_dir3') do |dir3|
95
- Dir.mktmpdir('tzinfo_test_dir4') do |dir4|
96
- file = File.join(dir1, 'file')
97
- FileUtils.touch(File.join(dir2, 'zone.tab'))
98
- FileUtils.touch(File.join(dir3, 'iso3166.tab'))
99
- FileUtils.touch(File.join(dir4, 'zone.tab'))
100
- FileUtils.touch(File.join(dir4, 'iso3166.tab'))
101
-
102
- ZoneinfoDataSource.search_path = [file, dir2, dir3, dir4]
103
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = []
104
-
105
- data_source = ZoneinfoDataSource.new
106
- assert_equal(dir4, data_source.zoneinfo_dir)
107
- end
108
- end
109
- end
110
- end
111
- end
112
-
113
- def test_new_search_zone1970
114
- Dir.mktmpdir('tzinfo_test_dir1') do |dir1|
115
- Dir.mktmpdir('tzinfo_test_dir2') do |dir2|
116
- Dir.mktmpdir('tzinfo_test_dir3') do |dir3|
117
- Dir.mktmpdir('tzinfo_test_dir4') do |dir4|
118
- file = File.join(dir1, 'file')
119
- FileUtils.touch(File.join(dir2, 'zone1970.tab'))
120
- FileUtils.touch(File.join(dir3, 'iso3166.tab'))
121
- FileUtils.touch(File.join(dir4, 'zone1970.tab'))
122
- FileUtils.touch(File.join(dir4, 'iso3166.tab'))
123
-
124
- ZoneinfoDataSource.search_path = [file, dir2, dir3, dir4]
125
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = []
126
-
127
- data_source = ZoneinfoDataSource.new
128
- assert_equal(dir4, data_source.zoneinfo_dir)
129
- end
130
- end
131
- end
132
- end
133
- end
134
-
135
- def test_new_search_solaris_tab_files
136
- # Solaris names the tab files 'tab/country.tab' (iso3166.tab) and
137
- # 'tab/zone_sun.tab' (zone.tab).
138
-
139
- Dir.mktmpdir('tzinfo_test_dir') do |dir|
140
- tab = File.join(dir, 'tab')
141
- FileUtils.mkdir(tab)
142
- FileUtils.touch(File.join(tab, 'country.tab'))
143
- FileUtils.touch(File.join(tab, 'zone_sun.tab'))
144
-
145
- ZoneinfoDataSource.search_path = [dir]
146
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = []
147
-
148
- data_source = ZoneinfoDataSource.new
149
- assert_equal(dir, data_source.zoneinfo_dir)
150
- end
151
- end
152
-
153
- def test_new_search_alternate_iso3166_path
154
- Dir.mktmpdir('tzinfo_test_dir_zoneinfo') do |zoneinfo_dir|
155
- Dir.mktmpdir('tzinfo_test_dir_tab') do |tab_dir|
156
- FileUtils.touch(File.join(zoneinfo_dir, 'zone.tab'))
157
-
158
- tab_file = File.join(tab_dir, 'iso3166')
159
-
160
- ZoneinfoDataSource.search_path = [zoneinfo_dir]
161
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = [tab_file]
162
-
163
- assert_raises(ZoneinfoDirectoryNotFound) do
164
- ZoneinfoDataSource.new
165
- end
166
-
167
- FileUtils.touch(tab_file)
168
-
169
- data_source = ZoneinfoDataSource.new
170
- assert_equal(zoneinfo_dir, data_source.zoneinfo_dir)
171
- end
172
- end
173
- end
174
-
175
- def test_new_search_not_found
176
- Dir.mktmpdir('tzinfo_test_dir1') do |dir1|
177
- Dir.mktmpdir('tzinfo_test_dir2') do |dir2|
178
- Dir.mktmpdir('tzinfo_test_dir3') do |dir3|
179
- Dir.mktmpdir('tzinfo_test_dir4') do |dir4|
180
- Dir.mktmpdir('tzinfo_test_dir5') do |dir5|
181
- file = File.join(dir1, 'file')
182
- FileUtils.touch(file)
183
- FileUtils.touch(File.join(dir2, 'zone.tab'))
184
- FileUtils.touch(File.join(dir3, 'zone1970.tab'))
185
- FileUtils.touch(File.join(dir4, 'iso3166.tab'))
186
-
187
- ZoneinfoDataSource.search_path = [file, dir2, dir3, dir4, dir5]
188
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = []
189
-
190
- assert_raises(ZoneinfoDirectoryNotFound) do
191
- ZoneinfoDataSource.new
192
- end
193
- end
194
- end
195
- end
196
- end
197
- end
198
- end
199
-
200
- def test_new_search_relative
201
- Dir.mktmpdir('tzinfo_test') do |dir|
202
- FileUtils.touch(File.join(dir, 'zone.tab'))
203
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
204
-
205
- FileUtils.chdir(dir)
206
-
207
- ZoneinfoDataSource.search_path = ['.']
208
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = []
209
- data_source = ZoneinfoDataSource.new
210
- assert_equal(Pathname.new(dir).realpath.to_s, data_source.zoneinfo_dir)
211
-
212
- # Change out of the directory to allow it to be deleted on Windows.
213
- FileUtils.chdir(@orig_pwd)
214
- end
215
- end
216
-
217
- def test_new_dir
218
- Dir.mktmpdir('tzinfo_test') do |dir|
219
- FileUtils.touch(File.join(dir, 'zone.tab'))
220
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
221
-
222
- data_source = ZoneinfoDataSource.new(dir)
223
- assert_equal(dir, data_source.zoneinfo_dir)
224
- end
225
- end
226
-
227
- def test_new_dir_zone1970
228
- Dir.mktmpdir('tzinfo_test') do |dir|
229
- FileUtils.touch(File.join(dir, 'zone1970.tab'))
230
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
231
-
232
- data_source = ZoneinfoDataSource.new(dir)
233
- assert_equal(dir, data_source.zoneinfo_dir)
234
- end
235
- end
236
-
237
- def test_new_dir_solaris_tab_files
238
- # Solaris names the tab files 'tab/country.tab' (iso3166.tab) and
239
- # 'tab/zone_sun.tab' (zone.tab).
240
-
241
- Dir.mktmpdir('tzinfo_test') do |dir|
242
- tab = File.join(dir, 'tab')
243
- FileUtils.mkdir(tab)
244
- FileUtils.touch(File.join(tab, 'country.tab'))
245
- FileUtils.touch(File.join(tab, 'zone_sun.tab'))
246
-
247
- data_source = ZoneinfoDataSource.new(dir)
248
- assert_equal(dir, data_source.zoneinfo_dir)
249
- end
250
- end
251
-
252
- def test_new_dir_alternate_iso3166_path
253
- Dir.mktmpdir('tzinfo_test_dir_zoneinfo') do |zoneinfo_dir|
254
- Dir.mktmpdir('tzinfo_test_dir_tab') do |tab_dir|
255
- FileUtils.touch(File.join(zoneinfo_dir, 'zone.tab'))
256
-
257
- tab_file = File.join(tab_dir, 'iso3166')
258
- FileUtils.touch(tab_file)
259
-
260
- ZoneinfoDataSource.alternate_iso3166_tab_search_path = [tab_file]
261
-
262
- assert_raises(InvalidZoneinfoDirectory) do
263
- # The alternate_iso3166_tab_search_path should not be used. This should raise
264
- # an exception.
265
- ZoneinfoDataSource.new(zoneinfo_dir)
266
- end
267
-
268
- data_source = ZoneinfoDataSource.new(zoneinfo_dir, tab_file)
269
- assert_equal(zoneinfo_dir, data_source.zoneinfo_dir)
270
- end
271
- end
272
- end
273
-
274
- def test_new_dir_invalid
275
- Dir.mktmpdir('tzinfo_test') do |dir|
276
- assert_raises(InvalidZoneinfoDirectory) do
277
- ZoneinfoDataSource.new(dir)
278
- end
279
- end
280
- end
281
-
282
- def test_new_dir_invalid_alternate_iso3166_path
283
- Dir.mktmpdir('tzinfo_test_dir_zoneinfo') do |zoneinfo_dir|
284
- Dir.mktmpdir('tzinfo_test_dir_tab') do |tab_dir|
285
- FileUtils.touch(File.join(zoneinfo_dir, 'zone.tab'))
286
-
287
- assert_raises(InvalidZoneinfoDirectory) do
288
- ZoneinfoDataSource.new(zoneinfo_dir, File.join(tab_dir, 'iso3166'))
289
- end
290
- end
291
- end
292
- end
293
-
294
- def test_new_dir_invalid_alternate_iso3166_path_overrides_valid
295
- Dir.mktmpdir('tzinfo_test_dir_zoneinfo') do |zoneinfo_dir|
296
- Dir.mktmpdir('tzinfo_test_dir_tab') do |tab_dir|
297
- FileUtils.touch(File.join(zoneinfo_dir, 'iso3166.tab'))
298
- FileUtils.touch(File.join(zoneinfo_dir, 'zone.tab'))
299
-
300
- assert_raises(InvalidZoneinfoDirectory) do
301
- ZoneinfoDataSource.new(zoneinfo_dir, File.join(tab_dir, 'iso3166'))
302
- end
303
- end
304
- end
305
- end
306
-
307
- def test_new_file
308
- Dir.mktmpdir('tzinfo_test') do |dir|
309
- file = File.join(dir, 'file')
310
- FileUtils.touch(file)
311
-
312
- assert_raises(InvalidZoneinfoDirectory) do
313
- ZoneinfoDataSource.new(file)
314
- end
315
- end
316
- end
317
-
318
- def test_new_dir_relative
319
- Dir.mktmpdir('tzinfo_test') do |dir|
320
- FileUtils.touch(File.join(dir, 'zone.tab'))
321
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
322
-
323
- FileUtils.chdir(dir)
324
-
325
- data_source = ZoneinfoDataSource.new('.')
326
- assert_equal(Pathname.new(dir).realpath.to_s, data_source.zoneinfo_dir)
327
-
328
- # Change out of the directory to allow it to be deleted on Windows.
329
- FileUtils.chdir(@orig_pwd)
330
- end
331
- end
332
-
333
- def test_zoneinfo_dir
334
- Dir.mktmpdir('tzinfo_test') do |dir|
335
- FileUtils.touch(File.join(dir, 'zone.tab'))
336
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
337
-
338
- data_source = ZoneinfoDataSource.new(dir)
339
- assert_equal(dir, data_source.zoneinfo_dir)
340
- assert_equal(true, data_source.zoneinfo_dir.frozen?)
341
- end
342
- end
343
-
344
- def test_load_timezone_info_data
345
- info = @data_source.load_timezone_info('Europe/London')
346
- assert_kind_of(ZoneinfoTimezoneInfo, info)
347
- assert_equal('Europe/London', info.identifier)
348
- end
349
-
350
- def test_load_timezone_info_linked
351
- info = @data_source.load_timezone_info('UTC')
352
-
353
- # On platforms that don't support symlinks, 'UTC' will be created as a copy.
354
- # Either way, a ZoneinfoTimezoneInfo should be returned.
355
-
356
- assert_kind_of(ZoneinfoTimezoneInfo, info)
357
- assert_equal('UTC', info.identifier)
358
- end
359
-
360
- def test_load_timezone_info_does_not_exist
361
- assert_raises(InvalidTimezoneIdentifier) do
362
- @data_source.load_timezone_info('Nowhere/Special')
363
- end
364
- end
365
-
366
- def test_load_timezone_info_invalid
367
- assert_raises(InvalidTimezoneIdentifier) do
368
- @data_source.load_timezone_info('../Definitions/Europe/London')
369
- end
370
- end
371
-
372
- %w(leapseconds localtime).each do |file_name|
373
- define_method("test_load_timezone_info_ignored_#{file_name}_file") do
374
- assert_raises(InvalidTimezoneIdentifier) do
375
- @data_source.load_timezone_info(file_name)
376
- end
377
- end
378
- end
379
-
380
- def test_load_timezone_info_ignored_plus_version_file
381
- # Mac OS X includes a file named +VERSION containing the tzdata version.
382
-
383
- Dir.mktmpdir('tzinfo_test') do |dir|
384
- FileUtils.touch(File.join(dir, 'zone.tab'))
385
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
386
-
387
- File.open(File.join(dir, '+VERSION'), 'w') do |f|
388
- f.binmode
389
- f.write("2013a\n")
390
- end
391
-
392
- data_source = ZoneinfoDataSource.new(dir)
393
-
394
- assert_raises(InvalidTimezoneIdentifier) do
395
- data_source.load_timezone_info('+VERSION')
396
- end
397
- end
398
- end
399
-
400
- def test_load_timezone_info_ignored_timeconfig_symlink
401
- # Slackware includes a symlink named timeconfig that points at /usr/sbin/timeconfig.
402
-
403
- Dir.mktmpdir('tzinfo_test_target') do |target_dir|
404
- target_path = File.join(target_dir, 'timeconfig')
405
-
406
- File.open(target_path, 'w') do |f|
407
- f.write("#!/bin/sh\n")
408
- f.write("#\n")
409
- f.write('# timeconfig Slackware Linux timezone configuration utility.\n')
410
- end
411
-
412
- Dir.mktmpdir('tzinfo_test') do |dir|
413
- FileUtils.touch(File.join(dir, 'zone.tab'))
414
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
415
- FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(dir, 'EST'))
416
-
417
- symlink_path = File.join(dir, 'timeconfig')
418
- begin
419
- FileUtils.ln_s(target_path, symlink_path)
420
- rescue NotImplementedError, Errno::EACCES
421
- # Symlinks not supported on this platform, or permission denied
422
- # (administrative rights are required on Windows). Copy instead.
423
- FileUtils.cp(target_path, symlink_path)
424
- end
425
-
426
- data_source = ZoneinfoDataSource.new(dir)
427
-
428
- assert_raises(InvalidTimezoneIdentifier) do
429
- data_source.load_timezone_info('timeconfig')
430
- end
431
- end
432
- end
433
- end
434
-
435
- def test_load_timezone_info_nil
436
- assert_raises(InvalidTimezoneIdentifier) do
437
- @data_source.load_timezone_info(nil)
438
- end
439
- end
440
-
441
- def test_load_timezone_info_case
442
- assert_raises(InvalidTimezoneIdentifier) do
443
- @data_source.load_timezone_info('europe/london')
444
- end
445
- end
446
-
447
- def test_load_timezone_info_permission_denied
448
- Dir.mktmpdir('tzinfo_test') do |dir|
449
- FileUtils.touch(File.join(dir, 'zone.tab'))
450
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
451
-
452
- file = File.join(dir, 'UTC')
453
- FileUtils.touch(file)
454
- FileUtils.chmod(0200, file)
455
-
456
- data_source = ZoneinfoDataSource.new(dir)
457
-
458
- assert_raises(InvalidTimezoneIdentifier) do
459
- data_source.load_timezone_info('UTC')
460
- end
461
- end
462
- end
463
-
464
- def test_load_timezone_info_directory
465
- Dir.mktmpdir('tzinfo_test') do |dir|
466
- FileUtils.touch(File.join(dir, 'zone.tab'))
467
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
468
-
469
- subdir = File.join(dir, 'Subdir')
470
- FileUtils.mkdir(subdir)
471
-
472
- data_source = ZoneinfoDataSource.new(dir)
473
-
474
- assert_raises(InvalidTimezoneIdentifier) do
475
- data_source.load_timezone_info('Subdir')
476
- end
477
- end
478
- end
479
-
480
- def test_load_timezone_info_linked_absolute_outside
481
- Dir.mktmpdir('tzinfo_test') do |dir|
482
- Dir.mktmpdir('tzinfo_test') do |outside|
483
- outside_file = File.join(outside, 'EST')
484
- FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), outside_file)
485
-
486
- FileUtils.touch(File.join(dir, 'zone.tab'))
487
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
488
-
489
- file = File.join(dir, 'EST')
490
-
491
- begin
492
- FileUtils.ln_s(outside_file, file)
493
- rescue NotImplementedError, Errno::EACCES
494
- # Symlinks not supported on this platform, or permission denied
495
- # (administrative rights are required on Windows). Skip test.
496
- return
497
- end
498
-
499
- data_source = ZoneinfoDataSource.new(dir)
500
-
501
- info = data_source.load_timezone_info('EST')
502
- assert_kind_of(ZoneinfoTimezoneInfo, info)
503
- assert_equal('EST', info.identifier)
504
- end
505
- end
506
- end
507
-
508
- def test_load_timezone_info_linked_absolute_inside
509
- Dir.mktmpdir('tzinfo_test') do |dir|
510
- FileUtils.touch(File.join(dir, 'zone.tab'))
511
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
512
-
513
- FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(dir, 'EST'))
514
-
515
- link = File.join(dir, 'Link')
516
-
517
- begin
518
- FileUtils.ln_s(File.join(File.expand_path(dir), 'EST'), link)
519
- rescue NotImplementedError, Errno::EACCES
520
- # Symlinks not supported on this platform, or permission denied
521
- # (administrative rights are required on Windows). Skip test.
522
- return
523
- end
524
-
525
- data_source = ZoneinfoDataSource.new(dir)
526
-
527
- info = data_source.load_timezone_info('Link')
528
- assert_kind_of(ZoneinfoTimezoneInfo, info)
529
- assert_equal('Link', info.identifier)
530
- end
531
- end
532
-
533
- def test_load_timezone_info_linked_relative_outside
534
- Dir.mktmpdir('tzinfo_test') do |root|
535
- FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(root, 'outside'))
536
-
537
- dir = File.join(root, 'zoneinfo')
538
- FileUtils.mkdir(dir)
539
-
540
- FileUtils.touch(File.join(dir, 'zone.tab'))
541
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
542
-
543
- link = File.join(dir, 'Link')
544
-
545
- begin
546
- FileUtils.ln_s('../outside', link)
547
- rescue NotImplementedError, Errno::EACCES
548
- # Symlinks not supported on this platform, or permission denied
549
- # (administrative rights are required on Windows). Skip test.
550
- return
551
- end
552
-
553
- subdir = File.join(dir, 'Subdir')
554
- subdir_link = File.join(subdir, 'Link')
555
- FileUtils.mkdir(subdir)
556
- FileUtils.ln_s('../../outside', subdir_link)
557
-
558
- data_source = ZoneinfoDataSource.new(dir)
559
-
560
- info = data_source.load_timezone_info('Link')
561
- assert_kind_of(ZoneinfoTimezoneInfo, info)
562
- assert_equal('Link', info.identifier)
563
-
564
- info = data_source.load_timezone_info('Subdir/Link')
565
- assert_kind_of(ZoneinfoTimezoneInfo, info)
566
- assert_equal('Subdir/Link', info.identifier)
567
- end
568
- end
569
-
570
- def test_load_timezone_info_linked_relative_parent_inside
571
- Dir.mktmpdir('tzinfo_test') do |dir|
572
- FileUtils.touch(File.join(dir, 'zone.tab'))
573
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
574
-
575
- FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(dir, 'EST'))
576
-
577
- subdir = File.join(dir, 'Subdir')
578
- FileUtils.mkdir(subdir)
579
- FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(subdir, 'EST'))
580
-
581
- subdir_link = File.join(subdir, 'Link')
582
- begin
583
- FileUtils.ln_s('../Subdir/EST', subdir_link)
584
- rescue NotImplementedError, Errno::EACCES
585
- # Symlinks not supported on this platform, or permission denied
586
- # (administrative rights are required on Windows). Skip test.
587
- return
588
- end
589
-
590
- subdir_link2 = File.join(subdir, 'Link2')
591
- FileUtils.ln_s('../EST', subdir_link2)
592
-
593
- subdir2 = File.join(dir, 'Subdir2')
594
- FileUtils.mkdir(subdir2)
595
- subdir2_link = File.join(subdir2, 'Link')
596
- FileUtils.ln_s('../Subdir/EST', subdir2_link)
597
-
598
- data_source = ZoneinfoDataSource.new(dir)
599
-
600
- info = data_source.load_timezone_info('Subdir/Link')
601
- assert_kind_of(ZoneinfoTimezoneInfo, info)
602
- assert_equal('Subdir/Link', info.identifier)
603
-
604
- info = data_source.load_timezone_info('Subdir/Link2')
605
- assert_kind_of(ZoneinfoTimezoneInfo, info)
606
- assert_equal('Subdir/Link2', info.identifier)
607
-
608
- info = data_source.load_timezone_info('Subdir2/Link')
609
- assert_kind_of(ZoneinfoTimezoneInfo, info)
610
- assert_equal('Subdir2/Link', info.identifier)
611
- end
612
- end
613
-
614
- def test_load_timezone_info_invalid_file
615
- Dir.mktmpdir('tzinfo_test') do |dir|
616
- FileUtils.touch(File.join(dir, 'zone.tab'))
617
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
618
-
619
- File.open(File.join(dir, 'Zone'), 'wb') do |file|
620
- file.write('NotAValidTZifFile')
621
- end
622
-
623
- data_source = ZoneinfoDataSource.new(dir)
624
-
625
- assert_raises(InvalidTimezoneIdentifier) do
626
- data_source.load_timezone_info('Zone')
627
- end
628
- end
629
- end
630
-
631
- def test_load_timezone_info_invalid_file_2
632
- Dir.mktmpdir('tzinfo_test') do |dir|
633
- FileUtils.touch(File.join(dir, 'zone.tab'))
634
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
635
-
636
- zone = File.join(dir, 'Zone')
637
-
638
- File.open(File.join(@data_source.zoneinfo_dir, 'EST')) do |src|
639
- # Change header to TZif1 (which is not a valid header).
640
- File.open(zone, 'wb') do |dest|
641
- dest.write('TZif1')
642
- src.pos = 5
643
- FileUtils.copy_stream(src, dest)
644
- end
645
- end
646
-
647
- data_source = ZoneinfoDataSource.new(dir)
648
-
649
- assert_raises(InvalidTimezoneIdentifier) do
650
- data_source.load_timezone_info('Zone')
651
- end
652
- end
653
- end
654
-
655
- def test_load_timezone_info_tainted
656
- safe_test do
657
- identifier = 'Europe/Amsterdam'.dup.taint
658
- assert(identifier.tainted?)
659
- info = @data_source.load_timezone_info(identifier)
660
- assert_equal('Europe/Amsterdam', info.identifier)
661
- assert(identifier.tainted?)
662
- end
663
- end
664
-
665
- def test_load_timezone_info_tainted_and_frozen
666
- safe_test do
667
- info = @data_source.load_timezone_info('Europe/Amsterdam'.dup.taint.freeze)
668
- assert_equal('Europe/Amsterdam', info.identifier)
669
- end
670
- end
671
-
672
- def test_load_timezone_info_tainted_zoneinfo_dir_safe_mode
673
- safe_test(:unavailable => :skip) do
674
- assert_raises(SecurityError) do
675
- ZoneinfoDataSource.new(@data_source.zoneinfo_dir.dup.taint)
676
- end
677
- end
678
- end
679
-
680
- def test_load_timezone_info_tainted_zoneinfo_dir
681
- data_source = ZoneinfoDataSource.new(@data_source.zoneinfo_dir.dup.taint)
682
- info = data_source.load_timezone_info('Europe/London')
683
- assert_kind_of(ZoneinfoTimezoneInfo, info)
684
- assert_equal('Europe/London', info.identifier)
685
- end
686
-
687
- def get_timezone_filenames(directory)
688
- entries = Dir.glob(File.join(directory, '**', '*'))
689
-
690
- entries = entries.select do |file|
691
- file.untaint
692
- File.file?(file)
693
- end
694
-
695
- entries = entries.collect {|file| file[directory.length + File::SEPARATOR.length, file.length - directory.length - File::SEPARATOR.length]}
696
-
697
- # Exclude right (with leapseconds) and posix (copy) directories; .tab files; leapseconds, localtime and posixrules files.
698
- entries = entries.select do |file|
699
- file !~ /\A(posix|right)\// &&
700
- file !~ /\.tab\z/ &&
701
- !%w(leapseconds localtime posixrules).include?(file)
702
- end
703
-
704
- entries.sort
705
- end
706
-
707
- def test_timezone_identifiers
708
- expected = get_timezone_filenames(@data_source.zoneinfo_dir)
709
- all = @data_source.timezone_identifiers
710
- assert_kind_of(Array, all)
711
- assert_array_same_items(expected, all)
712
- assert_equal(true, all.frozen?)
713
- end
714
-
715
- def test_data_timezone_identifiers
716
- expected = get_timezone_filenames(@data_source.zoneinfo_dir)
717
- all_data = @data_source.data_timezone_identifiers
718
- assert_kind_of(Array, all_data)
719
- assert_array_same_items(expected, all_data)
720
- assert_equal(true, all_data.frozen?)
721
- end
722
-
723
- def test_linked_timezone_identifiers
724
- all_linked = @data_source.linked_timezone_identifiers
725
- assert_kind_of(Array, all_linked)
726
- assert_equal(true, all_linked.empty?)
727
- assert_equal(true, all_linked.frozen?)
728
- end
729
-
730
- def test_timezone_identifiers_safe_mode
731
- safe_test do
732
- expected = get_timezone_filenames(@data_source.zoneinfo_dir)
733
- all = @data_source.timezone_identifiers
734
- assert_kind_of(Array, all)
735
- assert_array_same_items(expected, all)
736
- assert_equal(true, all.frozen?)
737
- end
738
- end
739
-
740
- def test_timezone_identifiers_ignored_plus_version_file
741
- # Mac OS X includes a file named +VERSION containing the tzdata version.
742
-
743
- Dir.mktmpdir('tzinfo_test') do |dir|
744
- FileUtils.touch(File.join(dir, 'zone.tab'))
745
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
746
- FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(dir, 'EST'))
747
-
748
- File.open(File.join(dir, '+VERSION'), 'w') do |f|
749
- f.binmode
750
- f.write("2013a\n")
751
- end
752
-
753
- data_source = ZoneinfoDataSource.new(dir)
754
- assert_array_same_items(['EST'], data_source.timezone_identifiers)
755
- end
756
- end
757
-
758
- def test_timezone_identifiers_ignored_timeconfig_symlink
759
- # Slackware includes a symlink named timeconfig that points at /usr/sbin/timeconfig.
760
-
761
- Dir.mktmpdir('tzinfo_test_target') do |target_dir|
762
- target_path = File.join(target_dir, 'timeconfig')
763
-
764
- File.open(target_path, 'w') do |f|
765
- f.write("#!/bin/sh\n")
766
- f.write("#\n")
767
- f.write('# timeconfig Slackware Linux timezone configuration utility.\n')
768
- end
769
-
770
- Dir.mktmpdir('tzinfo_test') do |dir|
771
- FileUtils.touch(File.join(dir, 'zone.tab'))
772
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
773
- FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(dir, 'EST'))
774
-
775
- symlink_path = File.join(dir, 'timeconfig')
776
- begin
777
- FileUtils.ln_s(target_path, symlink_path)
778
- rescue NotImplementedError, Errno::EACCES
779
- # Symlinks not supported on this platform, or permission denied
780
- # (administrative rights are required on Windows). Copy instead.
781
- FileUtils.cp(target_path, symlink_path)
782
- end
783
-
784
- data_source = ZoneinfoDataSource.new(dir)
785
- assert_array_same_items(['EST'], data_source.timezone_identifiers)
786
- end
787
- end
788
- end
789
-
790
- def test_timezone_identifiers_ignored_src_directory
791
- # Solaris includes a src directory containing the source timezone data files
792
- # from the tzdata distribution. These should be ignored.
793
-
794
- Dir.mktmpdir('tzinfo_test') do |dir|
795
- FileUtils.touch(File.join(dir, 'zone.tab'))
796
- FileUtils.touch(File.join(dir, 'iso3166.tab'))
797
- FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(dir, 'EST'))
798
-
799
- src_dir = File.join(dir, 'src')
800
- FileUtils.mkdir(src_dir)
801
-
802
- File.open(File.join(src_dir, 'europe'), 'w') do |f|
803
- f.binmode
804
- f.write("Zone\tEurope/London\t0:00\tEU\tGMT/BST\n")
805
- end
806
-
807
- data_source = ZoneinfoDataSource.new(dir)
808
- assert_array_same_items(['EST'], data_source.timezone_identifiers)
809
- end
810
- end
811
-
812
- def test_load_country_info
813
- info = @data_source.load_country_info('GB')
814
- assert_equal('GB', info.code)
815
- assert_equal('Britain (UK)', info.name)
816
- end
817
-
818
- def test_load_country_info_not_exist
819
- assert_raises(InvalidCountryCode) do
820
- @data_source.load_country_info('ZZ')
821
- end
822
- end
823
-
824
- def test_load_country_info_invalid
825
- assert_raises(InvalidCountryCode) do
826
- @data_source.load_country_info('../Countries/GB')
827
- end
828
- end
829
-
830
- def test_load_country_info_nil
831
- assert_raises(InvalidCountryCode) do
832
- @data_source.load_country_info(nil)
833
- end
834
- end
835
-
836
- def test_load_country_info_case
837
- assert_raises(InvalidCountryCode) do
838
- @data_source.load_country_info('gb')
839
- end
840
- end
841
-
842
- def test_load_country_info_tainted
843
- safe_test do
844
- code = 'NL'.dup.taint
845
- assert(code.tainted?)
846
- info = @data_source.load_country_info(code)
847
- assert_equal('NL', info.code)
848
- assert(code.tainted?)
849
- end
850
- end
851
-
852
- def test_load_country_info_tainted_and_frozen
853
- safe_test do
854
- info = @data_source.load_country_info('NL'.dup.taint.freeze)
855
- assert_equal('NL', info.code)
856
- end
857
- end
858
-
859
- def test_load_country_info_check_zones
860
- Dir.mktmpdir('tzinfo_test') do |dir|
861
- RubyCoreSupport.open_file(File.join(dir, 'iso3166.tab'), 'w', :external_encoding => 'UTF-8') do |iso3166|
862
- iso3166.puts('# iso3166.tab')
863
- iso3166.puts('')
864
- iso3166.puts("FC\tFake Country")
865
- iso3166.puts("OC\tOther Country")
866
- end
867
-
868
- RubyCoreSupport.open_file(File.join(dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
869
- zone.puts('# zone.tab')
870
- zone.puts('')
871
- zone.puts("FC\t+513030-0000731\tFake/One\tDescription of one")
872
- zone.puts("FC\t+353916+1394441\tFake/Two\tAnother description")
873
- zone.puts("FC\t-2332-04637\tFake/Three\tThis is Three")
874
- zone.puts("OC\t+5005+01426\tOther/One")
875
- end
876
-
877
- data_source = ZoneinfoDataSource.new(dir)
878
-
879
- info = data_source.load_country_info('FC')
880
- assert_equal('FC', info.code)
881
- assert_equal('Fake Country', info.name)
882
- assert_equal(['Fake/One', 'Fake/Two', 'Fake/Three'], info.zone_identifiers)
883
- assert_equal(true, info.zone_identifiers.frozen?)
884
- assert_equal([
885
- CountryTimezone.new('Fake/One', Rational(6181, 120), Rational(-451, 3600), 'Description of one'),
886
- CountryTimezone.new('Fake/Two', Rational(32089, 900), Rational(503081, 3600), 'Another description'),
887
- CountryTimezone.new('Fake/Three', Rational(-353, 15), Rational(-2797, 60), 'This is Three')], info.zones)
888
- assert_equal(true, info.zones.frozen?)
889
-
890
- info = data_source.load_country_info('OC')
891
- assert_equal('OC', info.code)
892
- assert_equal('Other Country', info.name)
893
- assert_equal(['Other/One'], info.zone_identifiers)
894
- assert_equal(true, info.zone_identifiers.frozen?)
895
- assert_equal([CountryTimezone.new('Other/One', Rational(601, 12), Rational(433, 30))], info.zones)
896
- assert_equal(true, info.zones.frozen?)
897
- end
898
- end
899
-
900
- def test_load_country_info_check_zones_zone1970
901
- Dir.mktmpdir('tzinfo_test') do |dir|
902
- RubyCoreSupport.open_file(File.join(dir, 'iso3166.tab'), 'w', :external_encoding => 'UTF-8') do |iso3166|
903
- iso3166.puts('# iso3166.tab')
904
- iso3166.puts('')
905
- iso3166.puts("AC\tAnother Country")
906
- iso3166.puts("FC\tFake Country")
907
- iso3166.puts("OC\tOther Country")
908
- end
909
-
910
- # zone.tab will be ignored.
911
- RubyCoreSupport.open_file(File.join(dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
912
- zone.puts('# zone.tab')
913
- zone.puts('')
914
- zone.puts("FC\t+513030-0000731\tFake/One\tDescription of one")
915
- zone.puts("FC\t+353916+1394441\tFake/Two\tAnother description")
916
- zone.puts("FC\t-2332-04637\tFake/Three\tThis is Three")
917
- zone.puts("OC\t+5005+01426\tOther/One")
918
- end
919
-
920
- # zone1970.tab will be used.
921
- RubyCoreSupport.open_file(File.join(dir, 'zone1970.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
922
- zone.puts('# zone1970.tab')
923
- zone.puts('')
924
- zone.puts("AC,OC\t+0000+00000\tMiddle/Another/One\tAnother's One")
925
- zone.puts("FC\t+513030-0000731\tFake/One\tDescription of one")
926
- zone.puts("FC,OC\t+353916+1394441\tFake/Two\tAnother description")
927
- zone.puts("FC,OC\t-2332-04637\tFake/Three\tThis is Three")
928
- zone.puts("OC\t+5005+01426\tOther/One")
929
- zone.puts("OC\t+5015+11426\tOther/Two")
930
- end
931
-
932
- data_source = ZoneinfoDataSource.new(dir)
933
-
934
- info = data_source.load_country_info('AC')
935
- assert_equal('AC', info.code)
936
- assert_equal('Another Country', info.name)
937
- assert_equal(['Middle/Another/One'], info.zone_identifiers)
938
- assert_equal(true, info.zone_identifiers.frozen?)
939
- assert_equal([CountryTimezone.new('Middle/Another/One', Rational(0, 1), Rational(0, 1), "Another's One")], info.zones)
940
- assert_equal(true, info.zones.frozen?)
941
-
942
- info = data_source.load_country_info('FC')
943
- assert_equal('FC', info.code)
944
- assert_equal('Fake Country', info.name)
945
- assert_equal(['Fake/One', 'Fake/Two', 'Fake/Three'], info.zone_identifiers)
946
- assert_equal(true, info.zone_identifiers.frozen?)
947
- assert_equal([
948
- CountryTimezone.new('Fake/One', Rational(6181, 120), Rational(-451, 3600), 'Description of one'),
949
- CountryTimezone.new('Fake/Two', Rational(32089, 900), Rational(503081, 3600), 'Another description'),
950
- CountryTimezone.new('Fake/Three', Rational(-353, 15), Rational(-2797, 60), 'This is Three')], info.zones)
951
- assert_equal(true, info.zones.frozen?)
952
-
953
- # Testing the ordering of zones. A zone can either be primary (country
954
- # code is the first in the first column), or secondary (country code is
955
- # not the first). Should return all the primaries first in the order they
956
- # appeared in the file, followed by all the secondaries in the order they
957
- # appeared in file.
958
-
959
- info = data_source.load_country_info('OC')
960
- assert_equal('OC', info.code)
961
- assert_equal('Other Country', info.name)
962
- assert_equal(['Other/One', 'Other/Two', 'Middle/Another/One', 'Fake/Two', 'Fake/Three'], info.zone_identifiers)
963
- assert_equal(true, info.zone_identifiers.frozen?)
964
- assert_equal([
965
- CountryTimezone.new('Other/One', Rational(601, 12), Rational( 433, 30)),
966
- CountryTimezone.new('Other/Two', Rational(201, 4), Rational(3433, 30)),
967
- CountryTimezone.new('Middle/Another/One', Rational(0, 1), Rational(0, 1), "Another's One"),
968
- CountryTimezone.new('Fake/Two', Rational(32089, 900), Rational(503081, 3600), 'Another description'),
969
- CountryTimezone.new('Fake/Three', Rational(-353, 15), Rational(-2797, 60), 'This is Three')], info.zones)
970
- assert_equal(true, info.zones.frozen?)
971
- end
972
- end
973
-
974
- def test_load_country_info_check_zones_solaris_tab_files
975
- # Solaris uses 5 columns instead of the usual 4 in zone_sun.tab.
976
- # An extra column before the comment gives an optional linked/alternate
977
- # timezone identifier (or '-' if not set).
978
- #
979
- # Additionally, there is a section at the end of the file for timezones
980
- # covering regions. These are given lower-case "country" codes. The timezone
981
- # identifier column refers to a continent instead of an identifier. These
982
- # lines will be ignored by TZInfo.
983
-
984
- Dir.mktmpdir('tzinfo_test') do |dir|
985
- tab_dir = File.join(dir, 'tab')
986
- FileUtils.mkdir(tab_dir)
987
-
988
- RubyCoreSupport.open_file(File.join(tab_dir, 'country.tab'), 'w', :external_encoding => 'UTF-8') do |country|
989
- country.puts('# country.tab')
990
- country.puts('# Solaris')
991
- country.puts("FC\tFake Country")
992
- country.puts("OC\tOther Country")
993
- end
994
-
995
- RubyCoreSupport.open_file(File.join(tab_dir, 'zone_sun.tab'), 'w', :external_encoding => 'UTF-8') do |zone_sun|
996
- zone_sun.puts('# zone_sun.tab')
997
- zone_sun.puts('# Solaris')
998
- zone_sun.puts('# Countries')
999
- zone_sun.puts("FC\t+513030-0000731\tFake/One\t-\tDescription of one")
1000
- zone_sun.puts("FC\t+353916+1394441\tFake/Two\tFake/Alias/Two\tAnother description")
1001
- zone_sun.puts("FC\t-2332-04637\tFake/Three\tFake/Alias/Three\tThis is Three")
1002
- zone_sun.puts("OC\t+5005+01426\tOther/One\tOther/Linked/One")
1003
- zone_sun.puts("OC\t+5015+01436\tOther/Two\t-")
1004
- zone_sun.puts('# Regions')
1005
- zone_sun.puts("ee\t+0000+00000\tEurope/\tEET")
1006
- zone_sun.puts("me\t+0000+00000\tEurope/\tMET")
1007
- zone_sun.puts("we\t+0000+00000\tEurope/\tWET")
1008
- end
1009
-
1010
- data_source = ZoneinfoDataSource.new(dir)
1011
-
1012
- info = data_source.load_country_info('FC')
1013
- assert_equal('FC', info.code)
1014
- assert_equal('Fake Country', info.name)
1015
- assert_equal(['Fake/One', 'Fake/Two', 'Fake/Three'], info.zone_identifiers)
1016
- assert_equal(true, info.zone_identifiers.frozen?)
1017
- assert_equal([
1018
- CountryTimezone.new('Fake/One', Rational(6181, 120), Rational(-451, 3600), 'Description of one'),
1019
- CountryTimezone.new('Fake/Two', Rational(32089, 900), Rational(503081, 3600), 'Another description'),
1020
- CountryTimezone.new('Fake/Three', Rational(-353, 15), Rational(-2797, 60), 'This is Three')], info.zones)
1021
- assert_equal(true, info.zones.frozen?)
1022
-
1023
- info = data_source.load_country_info('OC')
1024
- assert_equal('OC', info.code)
1025
- assert_equal('Other Country', info.name)
1026
- assert_equal(['Other/One', 'Other/Two'], info.zone_identifiers)
1027
- assert_equal(true, info.zone_identifiers.frozen?)
1028
- assert_equal([
1029
- CountryTimezone.new('Other/One', Rational(601, 12), Rational(433, 30)),
1030
- CountryTimezone.new('Other/Two', Rational(201, 4), Rational(73, 5))], info.zones)
1031
- assert_equal(true, info.zones.frozen?)
1032
- end
1033
- end
1034
-
1035
- def test_load_country_info_check_zones_alternate_iso3166_file
1036
- Dir.mktmpdir('tzinfo_test') do |dir|
1037
- zoneinfo_dir = File.join(dir, 'zoneinfo')
1038
- tab_dir = File.join(dir, 'tab')
1039
- FileUtils.mkdir(zoneinfo_dir)
1040
- FileUtils.mkdir(tab_dir)
1041
-
1042
- tab_file = File.join(tab_dir, 'iso3166')
1043
- RubyCoreSupport.open_file(tab_file, 'w', :external_encoding => 'UTF-8') do |iso3166|
1044
- # Use the BSD 4 column format (alternate iso3166 is used on BSD).
1045
- iso3166.puts("FC\tFCC\t001\tFake Country")
1046
- iso3166.puts("OC\tOCC\t002\tOther Country")
1047
- end
1048
-
1049
- RubyCoreSupport.open_file(File.join(zoneinfo_dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
1050
- zone.puts("FC\t+513030-0000731\tFake/One\tDescription of one")
1051
- zone.puts("FC\t+353916+1394441\tFake/Two\tAnother description")
1052
- zone.puts("FC\t-2332-04637\tFake/Three\tThis is Three")
1053
- zone.puts("OC\t+5005+01426\tOther/One")
1054
- end
1055
-
1056
- data_source = ZoneinfoDataSource.new(zoneinfo_dir, tab_file)
1057
-
1058
- info = data_source.load_country_info('FC')
1059
- assert_equal('FC', info.code)
1060
- assert_equal('Fake Country', info.name)
1061
- assert_equal(['Fake/One', 'Fake/Two', 'Fake/Three'], info.zone_identifiers)
1062
- assert_equal(true, info.zone_identifiers.frozen?)
1063
- assert_equal([
1064
- CountryTimezone.new('Fake/One', Rational(6181, 120), Rational(-451, 3600), 'Description of one'),
1065
- CountryTimezone.new('Fake/Two', Rational(32089, 900), Rational(503081, 3600), 'Another description'),
1066
- CountryTimezone.new('Fake/Three', Rational(-353, 15), Rational(-2797, 60), 'This is Three')], info.zones)
1067
- assert_equal(true, info.zones.frozen?)
1068
-
1069
- info = data_source.load_country_info('OC')
1070
- assert_equal('OC', info.code)
1071
- assert_equal('Other Country', info.name)
1072
- assert_equal(['Other/One'], info.zone_identifiers)
1073
- assert_equal(true, info.zone_identifiers.frozen?)
1074
- assert_equal([CountryTimezone.new('Other/One', Rational(601, 12), Rational(433, 30))], info.zones)
1075
- assert_equal(true, info.zones.frozen?)
1076
- end
1077
- end
1078
-
1079
- def test_load_country_info_four_column_iso31611
1080
- # OpenBSD and FreeBSD use a 4 column iso3166.tab file that includes
1081
- # alpha-3 and numeric-3 codes in addition to the alpha-2 and name in the
1082
- # tz database version.
1083
-
1084
- Dir.mktmpdir('tzinfo_test') do |dir|
1085
- RubyCoreSupport.open_file(File.join(dir, 'iso3166.tab'), 'w', :external_encoding => 'UTF-8') do |iso3166|
1086
- iso3166.puts("FC\tFCC\t001\tFake Country")
1087
- iso3166.puts("OC\tOCC\t002\tOther Country")
1088
- end
1089
-
1090
- RubyCoreSupport.open_file(File.join(dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
1091
- zone.puts("FC\t+513030-0000731\tFake/One\tDescription of one")
1092
- zone.puts("OC\t+5005+01426\tOther/One")
1093
- end
1094
-
1095
- data_source = ZoneinfoDataSource.new(dir)
1096
-
1097
- info = data_source.load_country_info('FC')
1098
- assert_equal('FC', info.code)
1099
- assert_equal('Fake Country', info.name)
1100
-
1101
- info = data_source.load_country_info('OC')
1102
- assert_equal('OC', info.code)
1103
- assert_equal('Other Country', info.name)
1104
- end
1105
- end
1106
-
1107
- def test_load_country_info_utf8
1108
- # iso3166.tab is currently in ASCII (as of tzdata 2014f), but will be
1109
- # changed to UTF-8 in the future.
1110
-
1111
- # zone.tab is in ASCII, with no plans to change. Since ASCII is a subset of
1112
- # UTF-8, test that this is loaded in UTF-8 anyway.
1113
-
1114
- Dir.mktmpdir('tzinfo_test') do |dir|
1115
- RubyCoreSupport.open_file(File.join(dir, 'iso3166.tab'), 'w', :external_encoding => 'UTF-8') do |iso3166|
1116
- iso3166.puts("UT\tUnicode Test ✓")
1117
- end
1118
-
1119
- RubyCoreSupport.open_file(File.join(dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
1120
- zone.puts("UT\t+513030-0000731\tUnicode✓/One\tUnicode Description ✓")
1121
- end
1122
-
1123
- data_source = ZoneinfoDataSource.new(dir)
1124
-
1125
- info = data_source.load_country_info('UT')
1126
- assert_equal('UT', info.code)
1127
- assert_equal('Unicode Test ✓', info.name)
1128
- assert_equal(['Unicode✓/One'], info.zone_identifiers)
1129
- assert_equal([CountryTimezone.new('Unicode✓/One', Rational(6181, 120), Rational(-451, 3600), 'Unicode Description ✓')], info.zones)
1130
- end
1131
- end
1132
-
1133
- def test_load_country_info_utf8_zone1970
1134
- # iso3166.tab is currently in ASCII (as of tzdata 2014f), but will be
1135
- # changed to UTF-8 in the future.
1136
-
1137
- # zone1970.tab is in UTF-8.
1138
-
1139
- Dir.mktmpdir('tzinfo_test') do |dir|
1140
- RubyCoreSupport.open_file(File.join(dir, 'iso3166.tab'), 'w', :external_encoding => 'UTF-8') do |iso3166|
1141
- iso3166.puts("UT\tUnicode Test ✓")
1142
- end
1143
-
1144
- RubyCoreSupport.open_file(File.join(dir, 'zone1970.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
1145
- zone.puts("UT\t+513030-0000731\tUnicode✓/One\tUnicode Description ✓")
1146
- end
1147
-
1148
- data_source = ZoneinfoDataSource.new(dir)
1149
-
1150
- info = data_source.load_country_info('UT')
1151
- assert_equal('UT', info.code)
1152
- assert_equal('Unicode Test ✓', info.name)
1153
- assert_equal(['Unicode✓/One'], info.zone_identifiers)
1154
- assert_equal([CountryTimezone.new('Unicode✓/One', Rational(6181, 120), Rational(-451, 3600), 'Unicode Description ✓')], info.zones)
1155
- end
1156
- end
1157
-
1158
- def test_country_codes
1159
- file_codes = []
1160
-
1161
- RubyCoreSupport.open_file(File.join(@data_source.zoneinfo_dir, 'iso3166.tab'), 'r', :external_encoding => 'UTF-8', :internal_encoding => 'UTF-8') do |file|
1162
- file.each_line do |line|
1163
- line.chomp!
1164
- file_codes << $1 if line =~ /\A([A-Z]{2})\t/
1165
- end
1166
- end
1167
-
1168
- codes = @data_source.country_codes
1169
- assert_array_same_items(file_codes, codes)
1170
- assert_equal(true, codes.frozen?)
1171
- end
1172
-
1173
- def test_country_codes_four_column_iso3166
1174
- # OpenBSD and FreeBSD use a 4 column iso3166.tab file that includes
1175
- # alpha-3 and numeric-3 codes in addition to the alpha-2 and name in the
1176
- # tz database version.
1177
-
1178
- Dir.mktmpdir('tzinfo_test') do |dir|
1179
- RubyCoreSupport.open_file(File.join(dir, 'iso3166.tab'), 'w', :external_encoding => 'UTF-8') do |iso3166|
1180
- iso3166.puts("FC\tFCC\t001\tFake Country")
1181
- iso3166.puts("OC\tOCC\t002\tOther Country")
1182
- end
1183
-
1184
- RubyCoreSupport.open_file(File.join(dir, 'zone.tab'), 'w', :external_encoding => 'UTF-8') do |zone|
1185
- zone.puts("FC\t+513030-0000731\tFake/One\tDescription of one")
1186
- zone.puts("OC\t+5005+01426\tOther/One")
1187
- end
1188
-
1189
- data_source = ZoneinfoDataSource.new(dir)
1190
-
1191
- codes = data_source.country_codes
1192
- assert_array_same_items(%w(FC OC), codes)
1193
- end
1194
- end
1195
- end