tzinfo 1.2.10 → 2.0.6

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