tzinfo 1.2.11 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.yardopts +3 -0
- data/CHANGES.md +469 -431
- data/LICENSE +13 -13
- data/README.md +368 -114
- data/lib/tzinfo/country.rb +131 -129
- data/lib/tzinfo/country_timezone.rb +70 -112
- data/lib/tzinfo/data_source.rb +389 -144
- data/lib/tzinfo/data_sources/constant_offset_data_timezone_info.rb +56 -0
- data/lib/tzinfo/data_sources/country_info.rb +42 -0
- data/lib/tzinfo/data_sources/data_timezone_info.rb +91 -0
- data/lib/tzinfo/data_sources/linked_timezone_info.rb +33 -0
- data/lib/tzinfo/data_sources/ruby_data_source.rb +141 -0
- data/lib/tzinfo/data_sources/timezone_info.rb +47 -0
- data/lib/tzinfo/data_sources/transitions_data_timezone_info.rb +214 -0
- data/lib/tzinfo/data_sources/zoneinfo_data_source.rb +573 -0
- data/lib/tzinfo/data_sources/zoneinfo_reader.rb +284 -0
- data/lib/tzinfo/data_sources.rb +8 -0
- data/lib/tzinfo/data_timezone.rb +33 -47
- data/lib/tzinfo/datetime_with_offset.rb +153 -0
- data/lib/tzinfo/format1/country_definer.rb +17 -0
- data/lib/tzinfo/format1/country_index_definition.rb +64 -0
- data/lib/tzinfo/format1/timezone_definer.rb +64 -0
- data/lib/tzinfo/format1/timezone_definition.rb +39 -0
- data/lib/tzinfo/format1/timezone_index_definition.rb +77 -0
- data/lib/tzinfo/format1.rb +10 -0
- data/lib/tzinfo/format2/country_definer.rb +68 -0
- data/lib/tzinfo/format2/country_index_definer.rb +68 -0
- data/lib/tzinfo/format2/country_index_definition.rb +46 -0
- data/lib/tzinfo/format2/timezone_definer.rb +94 -0
- data/lib/tzinfo/format2/timezone_definition.rb +73 -0
- data/lib/tzinfo/format2/timezone_index_definer.rb +45 -0
- data/lib/tzinfo/format2/timezone_index_definition.rb +55 -0
- data/lib/tzinfo/format2.rb +10 -0
- data/lib/tzinfo/info_timezone.rb +26 -21
- data/lib/tzinfo/linked_timezone.rb +33 -52
- data/lib/tzinfo/offset_timezone_period.rb +42 -0
- data/lib/tzinfo/string_deduper.rb +118 -0
- data/lib/tzinfo/time_with_offset.rb +128 -0
- data/lib/tzinfo/timestamp.rb +548 -0
- data/lib/tzinfo/timestamp_with_offset.rb +85 -0
- data/lib/tzinfo/timezone.rb +979 -502
- data/lib/tzinfo/timezone_offset.rb +84 -74
- data/lib/tzinfo/timezone_period.rb +151 -217
- data/lib/tzinfo/timezone_proxy.rb +70 -79
- data/lib/tzinfo/timezone_transition.rb +77 -109
- data/lib/tzinfo/transitions_timezone_period.rb +63 -0
- data/lib/tzinfo/version.rb +7 -0
- data/lib/tzinfo/with_offset.rb +61 -0
- data/lib/tzinfo.rb +60 -40
- data.tar.gz.sig +0 -0
- metadata +51 -115
- metadata.gz.sig +2 -3
- data/Rakefile +0 -107
- data/lib/tzinfo/annual_rules.rb +0 -51
- data/lib/tzinfo/country_index_definition.rb +0 -31
- data/lib/tzinfo/country_info.rb +0 -42
- data/lib/tzinfo/data_timezone_info.rb +0 -55
- data/lib/tzinfo/linked_timezone_info.rb +0 -26
- data/lib/tzinfo/offset_rationals.rb +0 -77
- data/lib/tzinfo/posix_time_zone_parser.rb +0 -136
- data/lib/tzinfo/ruby_core_support.rb +0 -176
- data/lib/tzinfo/ruby_country_info.rb +0 -74
- data/lib/tzinfo/ruby_data_source.rb +0 -136
- data/lib/tzinfo/time_or_datetime.rb +0 -351
- data/lib/tzinfo/timezone_definition.rb +0 -36
- data/lib/tzinfo/timezone_index_definition.rb +0 -54
- data/lib/tzinfo/timezone_info.rb +0 -30
- data/lib/tzinfo/timezone_transition_definition.rb +0 -104
- data/lib/tzinfo/transition_data_timezone_info.rb +0 -274
- data/lib/tzinfo/transition_rule.rb +0 -325
- data/lib/tzinfo/zoneinfo_country_info.rb +0 -37
- data/lib/tzinfo/zoneinfo_data_source.rb +0 -504
- data/lib/tzinfo/zoneinfo_timezone_info.rb +0 -516
- data/test/assets/payload.rb +0 -1
- data/test/tc_annual_rules.rb +0 -95
- data/test/tc_country.rb +0 -240
- data/test/tc_country_index_definition.rb +0 -69
- data/test/tc_country_info.rb +0 -16
- data/test/tc_country_timezone.rb +0 -173
- data/test/tc_data_source.rb +0 -218
- data/test/tc_data_timezone.rb +0 -99
- data/test/tc_data_timezone_info.rb +0 -18
- data/test/tc_info_timezone.rb +0 -34
- data/test/tc_linked_timezone.rb +0 -155
- data/test/tc_linked_timezone_info.rb +0 -23
- data/test/tc_offset_rationals.rb +0 -23
- data/test/tc_posix_time_zone_parser.rb +0 -261
- data/test/tc_ruby_core_support.rb +0 -168
- data/test/tc_ruby_country_info.rb +0 -110
- data/test/tc_ruby_data_source.rb +0 -175
- data/test/tc_time_or_datetime.rb +0 -674
- data/test/tc_timezone.rb +0 -1361
- data/test/tc_timezone_definition.rb +0 -113
- data/test/tc_timezone_index_definition.rb +0 -73
- data/test/tc_timezone_info.rb +0 -11
- data/test/tc_timezone_london.rb +0 -143
- data/test/tc_timezone_melbourne.rb +0 -142
- data/test/tc_timezone_new_york.rb +0 -142
- data/test/tc_timezone_offset.rb +0 -126
- data/test/tc_timezone_period.rb +0 -555
- data/test/tc_timezone_proxy.rb +0 -136
- data/test/tc_timezone_transition.rb +0 -366
- data/test/tc_timezone_transition_definition.rb +0 -295
- data/test/tc_timezone_utc.rb +0 -27
- data/test/tc_transition_data_timezone_info.rb +0 -433
- data/test/tc_transition_rule.rb +0 -663
- data/test/tc_zoneinfo_country_info.rb +0 -78
- data/test/tc_zoneinfo_data_source.rb +0 -1226
- data/test/tc_zoneinfo_timezone_info.rb +0 -2149
- data/test/test_utils.rb +0 -214
- data/test/ts_all.rb +0 -7
- data/test/ts_all_ruby.rb +0 -5
- data/test/ts_all_zoneinfo.rb +0 -9
- data/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb +0 -89
- data/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb +0 -327
- data/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb +0 -230
- data/test/tzinfo-data/tzinfo/data/definitions/EST.rb +0 -19
- data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb +0 -21
- data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb +0 -21
- data/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb +0 -21
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb +0 -273
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb +0 -198
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb +0 -333
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb +0 -277
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb +0 -235
- data/test/tzinfo-data/tzinfo/data/definitions/UTC.rb +0 -16
- data/test/tzinfo-data/tzinfo/data/indexes/countries.rb +0 -940
- data/test/tzinfo-data/tzinfo/data/indexes/timezones.rb +0 -609
- data/test/tzinfo-data/tzinfo/data/version.rb +0 -20
- data/test/tzinfo-data/tzinfo/data.rb +0 -8
- data/test/zoneinfo/America/Argentina/Buenos_Aires +0 -0
- data/test/zoneinfo/America/New_York +0 -0
- data/test/zoneinfo/Australia/Melbourne +0 -0
- data/test/zoneinfo/EST +0 -0
- data/test/zoneinfo/Etc/UTC +0 -0
- data/test/zoneinfo/Europe/Amsterdam +0 -0
- data/test/zoneinfo/Europe/Andorra +0 -0
- data/test/zoneinfo/Europe/London +0 -0
- data/test/zoneinfo/Europe/Paris +0 -0
- data/test/zoneinfo/Europe/Prague +0 -0
- data/test/zoneinfo/Factory +0 -0
- data/test/zoneinfo/iso3166.tab +0 -274
- data/test/zoneinfo/leapseconds +0 -78
- data/test/zoneinfo/posix/Europe/London +0 -0
- data/test/zoneinfo/posixrules +0 -0
- data/test/zoneinfo/right/Europe/London +0 -0
- data/test/zoneinfo/zone.tab +0 -452
- data/test/zoneinfo/zone1970.tab +0 -384
- data/tzinfo.gemspec +0 -21
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module TZInfo
|
|
5
|
+
module DataSources
|
|
6
|
+
# Represents a country and references to its time zones as returned by a
|
|
7
|
+
# {DataSource}.
|
|
8
|
+
class CountryInfo
|
|
9
|
+
# @return [String] the ISO 3166-1 alpha-2 country code.
|
|
10
|
+
attr_reader :code
|
|
11
|
+
|
|
12
|
+
# @return [String] the name of the country.
|
|
13
|
+
attr_reader :name
|
|
14
|
+
|
|
15
|
+
# @return [Array<CountryTimezone>] the time zones observed in the country.
|
|
16
|
+
attr_reader :zones
|
|
17
|
+
|
|
18
|
+
# Initializes a new {CountryInfo}. The passed in `code`, `name` and
|
|
19
|
+
# `zones` instances will be frozen.
|
|
20
|
+
#
|
|
21
|
+
# @param code [String] an ISO 3166-1 alpha-2 country code.
|
|
22
|
+
# @param name [String] the name of the country.
|
|
23
|
+
# @param zones [Array<CountryTimezone>] the time zones observed in the
|
|
24
|
+
# country.
|
|
25
|
+
# @raise [ArgumentError] if `code`, `name` or `zones` is `nil`.
|
|
26
|
+
def initialize(code, name, zones)
|
|
27
|
+
raise ArgumentError, 'code must be specified' unless code
|
|
28
|
+
raise ArgumentError, 'name must be specified' unless name
|
|
29
|
+
raise ArgumentError, 'zones must be specified' unless zones
|
|
30
|
+
@code = code.freeze
|
|
31
|
+
@name = name.freeze
|
|
32
|
+
@zones = zones.freeze
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [String] the internal object state as a programmer-readable
|
|
36
|
+
# `String`.
|
|
37
|
+
def inspect
|
|
38
|
+
"#<#{self.class}: #@code>"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module TZInfo
|
|
5
|
+
module DataSources
|
|
6
|
+
# The base class for time zones defined as either a series of transitions
|
|
7
|
+
# ({TransitionsDataTimezoneInfo}) or a constantly observed offset
|
|
8
|
+
# ({ConstantOffsetDataTimezoneInfo}).
|
|
9
|
+
#
|
|
10
|
+
# @abstract Data sources return instances of {DataTimezoneInfo} subclasses.
|
|
11
|
+
class DataTimezoneInfo < TimezoneInfo
|
|
12
|
+
# @param timestamp [Timestamp] a {Timestamp} with a specified
|
|
13
|
+
# {Timestamp#utc_offset utc_offset}.
|
|
14
|
+
# @return [TimezonePeriod] the {TimezonePeriod} observed at the time
|
|
15
|
+
# specified by `timestamp`.
|
|
16
|
+
# @raise [ArgumentError] may be raised if `timestamp` is `nil` or does not
|
|
17
|
+
# have a specified {Timestamp#utc_offset utc_offset}.
|
|
18
|
+
def period_for(timestamp)
|
|
19
|
+
raise_not_implemented('period_for')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns an `Array` containing the {TimezonePeriod TimezonePeriods} that
|
|
23
|
+
# could be observed at the local time specified by `local_timestamp`. The
|
|
24
|
+
# results are are ordered by increasing UTC start date. An empty `Array`
|
|
25
|
+
# is returned if no periods are found for the given local time.
|
|
26
|
+
#
|
|
27
|
+
# @param local_timestamp [Timestamp] a {Timestamp} representing a local
|
|
28
|
+
# time - must have an unspecified {Timestamp#utc_offset utc_offset}.
|
|
29
|
+
# @return [Array<TimezonePeriod>] an `Array` containing the
|
|
30
|
+
# {TimezonePeriod TimezonePeriods} that could be observed at the local
|
|
31
|
+
# time specified by `local_timestamp`.
|
|
32
|
+
# @raise [ArgumentError] may be raised if `local_timestamp` is `nil`, or
|
|
33
|
+
# has a specified {Timestamp#utc_offset utc_offset}.
|
|
34
|
+
def periods_for_local(local_timestamp)
|
|
35
|
+
raise_not_implemented('periods_for_local')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns an `Array` of {TimezoneTransition} instances representing the
|
|
39
|
+
# times where the UTC offset of the time zone changes.
|
|
40
|
+
#
|
|
41
|
+
# Transitions are returned up to a given {Timestamp} (`to_timestamp`).
|
|
42
|
+
#
|
|
43
|
+
# A from {Timestamp} may also be supplied using the `from_timestamp`
|
|
44
|
+
# parameter. If `from_timestamp` is specified, only transitions from that
|
|
45
|
+
# time onwards will be returned.
|
|
46
|
+
#
|
|
47
|
+
# Comparisons with `to_timestamp` are exclusive. Comparisons with
|
|
48
|
+
# `from_timestamp` are inclusive. If a transition falls precisely on
|
|
49
|
+
# `to_timestamp`, it will be excluded. If a transition falls on
|
|
50
|
+
# `from_timestamp`, it will be included.
|
|
51
|
+
#
|
|
52
|
+
# Transitions returned are ordered by when they occur, from earliest to
|
|
53
|
+
# latest.
|
|
54
|
+
#
|
|
55
|
+
# @param to_timestamp [Timestamp] a {Timestamp} with a specified
|
|
56
|
+
# {Timestamp#utc_offset utc_offset}. Transitions are returned if they
|
|
57
|
+
# occur before this time.
|
|
58
|
+
# @param from_timestamp [Timestamp] an optional {Timestamp} with a
|
|
59
|
+
# specified {Timestamp#utc_offset utc_offset}. If specified, transitions
|
|
60
|
+
# are returned if they occur at or after this time.
|
|
61
|
+
# @return [Array<TimezoneTransition>] an `Array` of {TimezoneTransition}
|
|
62
|
+
# instances representing the times where the UTC offset of the time zone
|
|
63
|
+
# changes.
|
|
64
|
+
# @raise [ArgumentError] may be raised if `to_timestamp` is `nil` or does
|
|
65
|
+
# not have a specified {Timestamp#utc_offset utc_offset}.
|
|
66
|
+
# @raise [ArgumentError] may be raised if `from_timestamp` is specified
|
|
67
|
+
# but does not have a specified {Timestamp#utc_offset utc_offset}.
|
|
68
|
+
# @raise [ArgumentError] may be raised if `from_timestamp` is specified
|
|
69
|
+
# but is not earlier than or at the same time as `to_timestamp`.
|
|
70
|
+
def transitions_up_to(to_timestamp, from_timestamp = nil)
|
|
71
|
+
raise_not_implemented('transitions_up_to')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @return [DataTimezone] a new {DataTimezone} instance for the time zone
|
|
75
|
+
# represented by this {DataTimezoneInfo}.
|
|
76
|
+
def create_timezone
|
|
77
|
+
DataTimezone.new(self)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
# Raises a {NotImplementedError} to indicate that the base class is
|
|
83
|
+
# incorrectly being used directly.
|
|
84
|
+
#
|
|
85
|
+
# raise [NotImplementedError] always.
|
|
86
|
+
def raise_not_implemented(method_name)
|
|
87
|
+
raise NotImplementedError, "Subclasses must override #{method_name}"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module TZInfo
|
|
4
|
+
module DataSources
|
|
5
|
+
# Represents a time zone that is defined as a link to or alias of another
|
|
6
|
+
# zone.
|
|
7
|
+
class LinkedTimezoneInfo < TimezoneInfo
|
|
8
|
+
# @return [String] the identifier of the time zone that provides the data
|
|
9
|
+
# (that this zone links to or is an alias for).
|
|
10
|
+
attr_reader :link_to_identifier
|
|
11
|
+
|
|
12
|
+
# Initializes a new {LinkedTimezoneInfo}. The passed in `identifier` and
|
|
13
|
+
# `link_to_identifier` instances will be frozen.
|
|
14
|
+
#
|
|
15
|
+
# @param identifier [String] the identifier of the time zone.
|
|
16
|
+
# @param link_to_identifier [String] the identifier of the time zone that
|
|
17
|
+
# this zone link to.
|
|
18
|
+
# @raise [ArgumentError] if `identifier` or `link_to_identifier` are
|
|
19
|
+
# `nil`.
|
|
20
|
+
def initialize(identifier, link_to_identifier)
|
|
21
|
+
super(identifier)
|
|
22
|
+
raise ArgumentError, 'link_to_identifier must be specified' unless link_to_identifier
|
|
23
|
+
@link_to_identifier = link_to_identifier.freeze
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [LinkedTimezone] a new {LinkedTimezone} instance for the time
|
|
27
|
+
# zone represented by this {LinkedTimezoneInfo}.
|
|
28
|
+
def create_timezone
|
|
29
|
+
LinkedTimezone.new(self)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module TZInfo
|
|
5
|
+
module DataSources
|
|
6
|
+
# A {TZInfoDataNotFound} exception is raised if the tzinfo-data gem could
|
|
7
|
+
# not be found (i.e. `require 'tzinfo/data'` failed) when selecting the Ruby
|
|
8
|
+
# data source.
|
|
9
|
+
class TZInfoDataNotFound < StandardError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# A DataSource implementation that loads data from the set of Ruby modules
|
|
13
|
+
# included in the tzinfo-data gem.
|
|
14
|
+
#
|
|
15
|
+
# TZInfo will use {RubyDataSource} by default if the tzinfo-data gem
|
|
16
|
+
# is available on the load path. It can also be selected by calling
|
|
17
|
+
# {DataSource.set} as follows:
|
|
18
|
+
#
|
|
19
|
+
# TZInfo::DataSource.set(:ruby)
|
|
20
|
+
class RubyDataSource < DataSource
|
|
21
|
+
# (see DataSource#data_timezone_identifiers)
|
|
22
|
+
attr_reader :data_timezone_identifiers
|
|
23
|
+
|
|
24
|
+
# (see DataSource#linked_timezone_identifiers)
|
|
25
|
+
attr_reader :linked_timezone_identifiers
|
|
26
|
+
|
|
27
|
+
# (see DataSource#country_codes)
|
|
28
|
+
attr_reader :country_codes
|
|
29
|
+
|
|
30
|
+
# Initializes a new {RubyDataSource} instance.
|
|
31
|
+
#
|
|
32
|
+
# @raise [TZInfoDataNotFound] if the tzinfo-data gem could not be found
|
|
33
|
+
# (i.e. `require 'tzinfo/data'` failed).
|
|
34
|
+
def initialize
|
|
35
|
+
super
|
|
36
|
+
|
|
37
|
+
begin
|
|
38
|
+
require('tzinfo/data')
|
|
39
|
+
rescue LoadError
|
|
40
|
+
raise TZInfoDataNotFound, "The tzinfo-data gem could not be found (require 'tzinfo/data' failed)."
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if TZInfo::Data.const_defined?(:LOCATION)
|
|
44
|
+
# Format 2
|
|
45
|
+
@base_path = File.join(TZInfo::Data::LOCATION, 'tzinfo', 'data')
|
|
46
|
+
else
|
|
47
|
+
# Format 1
|
|
48
|
+
data_file = File.join('', 'tzinfo', 'data.rb')
|
|
49
|
+
path = $".reverse_each.detect {|p| p.end_with?(data_file) }
|
|
50
|
+
if path
|
|
51
|
+
@base_path = File.join(File.dirname(path), 'data')
|
|
52
|
+
else
|
|
53
|
+
@base_path = 'tzinfo/data'
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
require_index('timezones')
|
|
58
|
+
require_index('countries')
|
|
59
|
+
|
|
60
|
+
@data_timezone_identifiers = Data::Indexes::Timezones.data_timezones
|
|
61
|
+
@linked_timezone_identifiers = Data::Indexes::Timezones.linked_timezones
|
|
62
|
+
@countries = Data::Indexes::Countries.countries
|
|
63
|
+
@country_codes = @countries.keys.sort!.freeze
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# (see DataSource#to_s)
|
|
67
|
+
def to_s
|
|
68
|
+
"Ruby DataSource: #{version_info}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# (see DataSource#inspect)
|
|
72
|
+
def inspect
|
|
73
|
+
"#<TZInfo::DataSources::RubyDataSource: #{version_info}>"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
protected
|
|
77
|
+
|
|
78
|
+
# Returns a {TimezoneInfo} instance for the given time zone identifier.
|
|
79
|
+
# The result will either be a {ConstantOffsetDataTimezoneInfo}, a
|
|
80
|
+
# {TransitionsDataTimezoneInfo} or a {LinkedTimezoneInfo} depending on the
|
|
81
|
+
# type of time zone.
|
|
82
|
+
#
|
|
83
|
+
# @param identifier [String] A time zone identifier.
|
|
84
|
+
# @return [TimezoneInfo] a {TimezoneInfo} instance for the given time zone
|
|
85
|
+
# identifier.
|
|
86
|
+
# @raise [InvalidTimezoneIdentifier] if the time zone is not found or the
|
|
87
|
+
# identifier is invalid.
|
|
88
|
+
def load_timezone_info(identifier)
|
|
89
|
+
valid_identifier = validate_timezone_identifier(identifier)
|
|
90
|
+
split_identifier = valid_identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__').split('/')
|
|
91
|
+
|
|
92
|
+
begin
|
|
93
|
+
require_definition(split_identifier)
|
|
94
|
+
|
|
95
|
+
m = Data::Definitions
|
|
96
|
+
split_identifier.each {|part| m = m.const_get(part) }
|
|
97
|
+
m.get
|
|
98
|
+
rescue LoadError, NameError => e
|
|
99
|
+
raise InvalidTimezoneIdentifier, "#{e.message.encode(Encoding::UTF_8)} (loading #{valid_identifier})"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# (see DataSource#load_country_info)
|
|
104
|
+
def load_country_info(code)
|
|
105
|
+
lookup_country_info(@countries, code)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
# Requires a zone definition by its identifier (split on /).
|
|
111
|
+
#
|
|
112
|
+
# @param identifier [Array<string>] the component parts of a time zone
|
|
113
|
+
# identifier (split on /). This must have already been validated.
|
|
114
|
+
def require_definition(identifier)
|
|
115
|
+
require_data(*(['definitions'] + identifier))
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Requires an index by its name.
|
|
119
|
+
#
|
|
120
|
+
# @param name [String] an index name.
|
|
121
|
+
def require_index(name)
|
|
122
|
+
require_data(*['indexes', name])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Requires a file from tzinfo/data.
|
|
126
|
+
#
|
|
127
|
+
# @param file [Array<String>] a relative path to a file to be required.
|
|
128
|
+
def require_data(*file)
|
|
129
|
+
require(File.join(@base_path, *file))
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @return [String] a `String` containing TZInfo::Data version infomation
|
|
133
|
+
# for inclusion in the #to_s and #inspect output.
|
|
134
|
+
def version_info
|
|
135
|
+
# The TZInfo::Data::VERSION constant is only available from v1.2014.8
|
|
136
|
+
# onwards.
|
|
137
|
+
"tzdb v#{TZInfo::Data::Version::TZDATA}#{TZInfo::Data.const_defined?(:VERSION) ? ", tzinfo-data v#{TZInfo::Data::VERSION}" : ''}"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module TZInfo
|
|
5
|
+
module DataSources
|
|
6
|
+
# Represents a time zone defined by a data source.
|
|
7
|
+
#
|
|
8
|
+
# @abstract Data sources return instances of {TimezoneInfo} subclasses.
|
|
9
|
+
class TimezoneInfo
|
|
10
|
+
# @return [String] the identifier of the time zone.
|
|
11
|
+
attr_reader :identifier
|
|
12
|
+
|
|
13
|
+
# Initializes a new TimezoneInfo. The passed in `identifier` instance will
|
|
14
|
+
# be frozen.
|
|
15
|
+
#
|
|
16
|
+
# @param identifier [String] the identifier of the time zone.
|
|
17
|
+
# @raise [ArgumentError] if `identifier` is `nil`.
|
|
18
|
+
def initialize(identifier)
|
|
19
|
+
raise ArgumentError, 'identifier must be specified' unless identifier
|
|
20
|
+
@identifier = identifier.freeze
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @return [String] the internal object state as a programmer-readable
|
|
24
|
+
# `String`.
|
|
25
|
+
def inspect
|
|
26
|
+
"#<#{self.class}: #@identifier>"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [Timezone] a new {Timezone} instance for the time zone
|
|
30
|
+
# represented by this {TimezoneInfo}.
|
|
31
|
+
def create_timezone
|
|
32
|
+
raise_not_implemented('create_timezone')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Raises a {NotImplementedError}.
|
|
38
|
+
#
|
|
39
|
+
# @param method_name [String] the name of the method that must be
|
|
40
|
+
# overridden.
|
|
41
|
+
# @raise NotImplementedError always.
|
|
42
|
+
def raise_not_implemented(method_name)
|
|
43
|
+
raise NotImplementedError, "Subclasses must override #{method_name}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module TZInfo
|
|
5
|
+
module DataSources
|
|
6
|
+
# Represents a data time zone defined by a list of transitions that change
|
|
7
|
+
# the locally observed time.
|
|
8
|
+
class TransitionsDataTimezoneInfo < DataTimezoneInfo
|
|
9
|
+
# @return [Array<TimezoneTransition>] the transitions that define this
|
|
10
|
+
# time zone in order of ascending timestamp.
|
|
11
|
+
attr_reader :transitions
|
|
12
|
+
|
|
13
|
+
# Initializes a new {TransitionsDataTimezoneInfo}.
|
|
14
|
+
#
|
|
15
|
+
# The passed in `identifier` instance will be frozen. A reference to the
|
|
16
|
+
# passed in `Array` will be retained.
|
|
17
|
+
#
|
|
18
|
+
# The `transitions` `Array` must be sorted in order of ascending
|
|
19
|
+
# timestamp. Each transition must have a
|
|
20
|
+
# {TimezoneTransition#timestamp_value timestamp_value} that is greater
|
|
21
|
+
# than the {TimezoneTransition#timestamp_value timestamp_value} of the
|
|
22
|
+
# prior transition.
|
|
23
|
+
#
|
|
24
|
+
# @param identifier [String] the identifier of the time zone.
|
|
25
|
+
# @param transitions [Array<TimezoneTransitions>] an `Array` of
|
|
26
|
+
# transitions that each indicate when a change occurs in the locally
|
|
27
|
+
# observed time.
|
|
28
|
+
# @raise [ArgumentError] if `identifier` is `nil`.
|
|
29
|
+
# @raise [ArgumentError] if `transitions` is `nil`.
|
|
30
|
+
# @raise [ArgumentError] if `transitions` is an empty `Array`.
|
|
31
|
+
def initialize(identifier, transitions)
|
|
32
|
+
super(identifier)
|
|
33
|
+
raise ArgumentError, 'transitions must be specified' unless transitions
|
|
34
|
+
raise ArgumentError, 'transitions must not be an empty Array' if transitions.empty?
|
|
35
|
+
@transitions = transitions.freeze
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# (see DataTimezoneInfo#period_for)
|
|
39
|
+
def period_for(timestamp)
|
|
40
|
+
raise ArgumentError, 'timestamp must be specified' unless timestamp
|
|
41
|
+
raise ArgumentError, 'timestamp must have a specified utc_offset' unless timestamp.utc_offset
|
|
42
|
+
|
|
43
|
+
timestamp_value = timestamp.value
|
|
44
|
+
|
|
45
|
+
index = find_minimum_transition {|t| t.timestamp_value >= timestamp_value }
|
|
46
|
+
|
|
47
|
+
if index
|
|
48
|
+
transition = @transitions[index]
|
|
49
|
+
|
|
50
|
+
if transition.timestamp_value == timestamp_value
|
|
51
|
+
# timestamp occurs within the second of the found transition, so is
|
|
52
|
+
# the transition that starts the period.
|
|
53
|
+
start_transition = transition
|
|
54
|
+
end_transition = @transitions[index + 1]
|
|
55
|
+
else
|
|
56
|
+
# timestamp occurs before the second of the found transition, so is
|
|
57
|
+
# the transition that ends the period.
|
|
58
|
+
start_transition = index == 0 ? nil : @transitions[index - 1]
|
|
59
|
+
end_transition = transition
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
start_transition = @transitions.last
|
|
63
|
+
end_transition = nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
TransitionsTimezonePeriod.new(start_transition, end_transition)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# (see DataTimezoneInfo#periods_for_local)
|
|
70
|
+
def periods_for_local(local_timestamp)
|
|
71
|
+
raise ArgumentError, 'local_timestamp must be specified' unless local_timestamp
|
|
72
|
+
raise ArgumentError, 'local_timestamp must have an unspecified utc_offset' if local_timestamp.utc_offset
|
|
73
|
+
|
|
74
|
+
local_timestamp_value = local_timestamp.value
|
|
75
|
+
latest_possible_utc_value = local_timestamp_value + 86400
|
|
76
|
+
earliest_possible_utc_value = local_timestamp_value - 86400
|
|
77
|
+
|
|
78
|
+
# Find the index of the first transition that occurs after a latest
|
|
79
|
+
# possible UTC representation of the local timestamp and then search
|
|
80
|
+
# backwards until an earliest possible UTC representation.
|
|
81
|
+
|
|
82
|
+
index = find_minimum_transition {|t| t.timestamp_value >= latest_possible_utc_value }
|
|
83
|
+
|
|
84
|
+
# No transitions after latest_possible_utc_value, set to max index + 1
|
|
85
|
+
# to search backwards including the period after the last transition
|
|
86
|
+
index = @transitions.length unless index
|
|
87
|
+
|
|
88
|
+
result = []
|
|
89
|
+
|
|
90
|
+
index.downto(0) do |i|
|
|
91
|
+
start_transition = i > 0 ? @transitions[i - 1] : nil
|
|
92
|
+
end_transition = @transitions[i]
|
|
93
|
+
offset = start_transition ? start_transition.offset : end_transition.previous_offset
|
|
94
|
+
utc_timestamp_value = local_timestamp_value - offset.observed_utc_offset
|
|
95
|
+
|
|
96
|
+
# It is not necessary to compare the sub-seconds because a timestamp
|
|
97
|
+
# is in the period if is >= the start transition (sub-seconds would
|
|
98
|
+
# make == become >) and if it is < the end transition (which
|
|
99
|
+
# sub-seconds cannot affect).
|
|
100
|
+
if (!start_transition || utc_timestamp_value >= start_transition.timestamp_value) && (!end_transition || utc_timestamp_value < end_transition.timestamp_value)
|
|
101
|
+
result << TransitionsTimezonePeriod.new(start_transition, end_transition)
|
|
102
|
+
elsif end_transition && end_transition.timestamp_value < earliest_possible_utc_value
|
|
103
|
+
break
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
result.reverse!
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# (see DataTimezoneInfo#transitions_up_to)
|
|
111
|
+
def transitions_up_to(to_timestamp, from_timestamp = nil)
|
|
112
|
+
raise ArgumentError, 'to_timestamp must be specified' unless to_timestamp
|
|
113
|
+
raise ArgumentError, 'to_timestamp must have a specified utc_offset' unless to_timestamp.utc_offset
|
|
114
|
+
|
|
115
|
+
if from_timestamp
|
|
116
|
+
raise ArgumentError, 'from_timestamp must have a specified utc_offset' unless from_timestamp.utc_offset
|
|
117
|
+
raise ArgumentError, 'to_timestamp must be greater than from_timestamp' if to_timestamp <= from_timestamp
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
if from_timestamp
|
|
121
|
+
from_index = find_minimum_transition {|t| transition_on_or_after_timestamp?(t, from_timestamp) }
|
|
122
|
+
return [] unless from_index
|
|
123
|
+
else
|
|
124
|
+
from_index = 0
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
to_index = find_minimum_transition {|t| transition_on_or_after_timestamp?(t, to_timestamp) }
|
|
128
|
+
|
|
129
|
+
if to_index
|
|
130
|
+
return [] if to_index < 1
|
|
131
|
+
to_index -= 1
|
|
132
|
+
else
|
|
133
|
+
to_index = -1
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
@transitions[from_index..to_index]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
# Array#bsearch_index was added in Ruby 2.3.0. Use bsearch_index to find
|
|
142
|
+
# transitions if it is available, otherwise use a Ruby implementation.
|
|
143
|
+
if [].respond_to?(:bsearch_index)
|
|
144
|
+
# Performs a binary search on {transitions} to find the index of the
|
|
145
|
+
# earliest transition satisfying a condition.
|
|
146
|
+
#
|
|
147
|
+
# @yield [transition] the caller will be yielded to to test the search
|
|
148
|
+
# condition.
|
|
149
|
+
# @yieldparam transition [TimezoneTransition] a {TimezoneTransition}
|
|
150
|
+
# instance from {transitions}.
|
|
151
|
+
# @yieldreturn [Boolean] `true` for the earliest transition that
|
|
152
|
+
# satisfies the condition and return `true` for all subsequent
|
|
153
|
+
# transitions. In all other cases, the result of the block must be
|
|
154
|
+
# `false`.
|
|
155
|
+
# @return [Integer] the index of the earliest transition safisfying
|
|
156
|
+
# the condition or `nil` if there are no such transitions.
|
|
157
|
+
#
|
|
158
|
+
# :nocov_no_array_bsearch_index:
|
|
159
|
+
def find_minimum_transition(&block)
|
|
160
|
+
@transitions.bsearch_index(&block)
|
|
161
|
+
end
|
|
162
|
+
# :nocov_no_array_bsearch_index:
|
|
163
|
+
else
|
|
164
|
+
# Performs a binary search on {transitions} to find the index of the
|
|
165
|
+
# earliest transition satisfying a condition.
|
|
166
|
+
#
|
|
167
|
+
# @yield [transition] the caller will be yielded to to test the search
|
|
168
|
+
# condition.
|
|
169
|
+
# @yieldparam transition [TimezoneTransition] a {TimezoneTransition}
|
|
170
|
+
# instance from {transitions}.
|
|
171
|
+
# @yieldreturn [Boolean] `true` for the earliest transition that
|
|
172
|
+
# satisfies the condition and return `true` for all subsequent
|
|
173
|
+
# transitions. In all other cases, the result of the block must be
|
|
174
|
+
# `false`.
|
|
175
|
+
# @return [Integer] the index of the earliest transition safisfying
|
|
176
|
+
# the condition or `nil` if there are no such transitions.
|
|
177
|
+
#
|
|
178
|
+
# :nocov_array_bsearch_index:
|
|
179
|
+
def find_minimum_transition
|
|
180
|
+
# A Ruby implementation of the find-minimum mode of Array#bsearch_index.
|
|
181
|
+
low = 0
|
|
182
|
+
high = @transitions.length
|
|
183
|
+
satisfied = false
|
|
184
|
+
|
|
185
|
+
while low < high do
|
|
186
|
+
mid = (low + high).div(2)
|
|
187
|
+
if yield @transitions[mid]
|
|
188
|
+
satisfied = true
|
|
189
|
+
high = mid
|
|
190
|
+
else
|
|
191
|
+
low = mid + 1
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
satisfied ? low : nil
|
|
196
|
+
end
|
|
197
|
+
# :nocov_array_bsearch_index:
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Determines if a transition occurs at or after a given {Timestamp},
|
|
201
|
+
# taking the {Timestamp#sub_second sub_second} into consideration.
|
|
202
|
+
#
|
|
203
|
+
# @param transition [TimezoneTransition] the transition to compare.
|
|
204
|
+
# @param timestamp [Timestamp] the timestamp to compare.
|
|
205
|
+
# @return [Boolean] `true` if `transition` occurs at or after `timestamp`,
|
|
206
|
+
# otherwise `false`.
|
|
207
|
+
def transition_on_or_after_timestamp?(transition, timestamp)
|
|
208
|
+
transition_timestamp_value = transition.timestamp_value
|
|
209
|
+
timestamp_value = timestamp.value
|
|
210
|
+
transition_timestamp_value > timestamp_value || transition_timestamp_value == timestamp_value && timestamp.sub_second == 0
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|