/
ruby_data_source.rb
141 lines (120 loc) · 4.75 KB
/
ruby_data_source.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# encoding: UTF-8
# frozen_string_literal: true
module TZInfo
module DataSources
# A {TZInfoDataNotFound} exception is raised if the tzinfo-data gem could
# not be found (i.e. `require 'tzinfo/data'` failed) when selecting the Ruby
# data source.
class TZInfoDataNotFound < StandardError
end
# A DataSource implementation that loads data from the set of Ruby modules
# included in the tzinfo-data gem.
#
# TZInfo will use {RubyDataSource} by default if the tzinfo-data gem
# is available on the load path. It can also be selected by calling
# {DataSource.set} as follows:
#
# TZInfo::DataSource.set(:ruby)
class RubyDataSource < DataSource
# (see DataSource#data_timezone_identifiers)
attr_reader :data_timezone_identifiers
# (see DataSource#linked_timezone_identifiers)
attr_reader :linked_timezone_identifiers
# (see DataSource#country_codes)
attr_reader :country_codes
# Initializes a new {RubyDataSource} instance.
#
# @raise [TZInfoDataNotFound] if the tzinfo-data gem could not be found
# (i.e. `require 'tzinfo/data'` failed).
def initialize
super
begin
require('tzinfo/data')
rescue LoadError
raise TZInfoDataNotFound, "The tzinfo-data gem could not be found (require 'tzinfo/data' failed)."
end
if TZInfo::Data.const_defined?(:LOCATION)
# Format 2
@base_path = File.join(TZInfo::Data::LOCATION, 'tzinfo', 'data')
else
# Format 1
data_file = File.join('', 'tzinfo', 'data.rb')
path = $".reverse_each.detect {|p| p.end_with?(data_file) }
if path
@base_path = RubyCoreSupport.untaint(File.join(File.dirname(path), 'data'))
else
@base_path = 'tzinfo/data'
end
end
require_index('timezones')
require_index('countries')
@data_timezone_identifiers = Data::Indexes::Timezones.data_timezones
@linked_timezone_identifiers = Data::Indexes::Timezones.linked_timezones
@countries = Data::Indexes::Countries.countries
@country_codes = @countries.keys.sort!.freeze
end
# (see DataSource#to_s)
def to_s
"Ruby DataSource: #{version_info}"
end
# (see DataSource#inspect)
def inspect
"#<TZInfo::DataSources::RubyDataSource: #{version_info}>"
end
protected
# Returns a {TimezoneInfo} instance for the given time zone identifier.
# The result will either be a {ConstantOffsetDataTimezoneInfo}, a
# {TransitionsDataTimezoneInfo} or a {LinkedTimezoneInfo} depending on the
# type of time zone.
#
# @param identifier [String] A time zone identifier.
# @return [TimezoneInfo] a {TimezoneInfo} instance for the given time zone
# identifier.
# @raise [InvalidTimezoneIdentifier] if the time zone is not found or the
# identifier is invalid.
def load_timezone_info(identifier)
valid_identifier = validate_timezone_identifier(identifier)
split_identifier = valid_identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__').split('/')
begin
require_definition(split_identifier)
m = Data::Definitions
split_identifier.each {|part| m = m.const_get(part) }
m.get
rescue LoadError, NameError => e
raise InvalidTimezoneIdentifier, "#{e.message.encode(Encoding::UTF_8)} (loading #{valid_identifier})"
end
end
# (see DataSource#load_country_info)
def load_country_info(code)
lookup_country_info(@countries, code)
end
private
# Requires a zone definition by its identifier (split on /).
#
# @param identifier [Array<string>] the component parts of a time zone
# identifier (split on /). This must have already been validated.
def require_definition(identifier)
require_data('definitions', *identifier)
end
# Requires an index by its name.
#
# @param name [String] an index name.
def require_index(name)
require_data('indexes', name)
end
# Requires a file from tzinfo/data.
#
# @param file [Array<String>] a relative path to a file to be required.
def require_data(*file)
require(File.join(@base_path, *file))
end
# @return [String] a `String` containing TZInfo::Data version infomation
# for inclusion in the #to_s and #inspect output.
def version_info
# The TZInfo::Data::VERSION constant is only available from v1.2014.8
# onwards.
"tzdb v#{TZInfo::Data::Version::TZDATA}#{TZInfo::Data.const_defined?(:VERSION) ? ", tzinfo-data v#{TZInfo::Data::VERSION}" : ''}"
end
end
end
end