tzinfo 1.2.7 → 2.0.2

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.
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 +489 -382
  6. data/LICENSE +12 -12
  7. data/README.md +368 -114
  8. data/lib/tzinfo.rb +59 -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 +145 -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 +577 -0
  21. data/lib/tzinfo/data_sources/zoneinfo_reader.rb +288 -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 -502
  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 +42 -98
  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 -169
  63. data/lib/tzinfo/ruby_country_info.rb +0 -74
  64. data/lib/tzinfo/ruby_data_source.rb +0 -140
  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 -496
  73. data/lib/tzinfo/zoneinfo_timezone_info.rb +0 -300
  74. data/test/tc_country.rb +0 -238
  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 -167
  88. data/test/tc_time_or_datetime.rb +0 -660
  89. data/test/tc_timezone.rb +0 -1361
  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 -433
  103. data/test/tc_zoneinfo_country_info.rb +0 -78
  104. data/test/tc_zoneinfo_data_source.rb +0 -1204
  105. data/test/tc_zoneinfo_timezone_info.rb +0 -1236
  106. data/test/test_utils.rb +0 -192
  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 -9
  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
@@ -0,0 +1,8 @@
1
+ # encoding: UTF-8
2
+
3
+ module TZInfo
4
+ # {DataSource} implementations and classes used by {DataSource}
5
+ # implementations.
6
+ module DataSources
7
+ end
8
+ end
@@ -0,0 +1,56 @@
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 constantly observed offset.
7
+ class ConstantOffsetDataTimezoneInfo < DataTimezoneInfo
8
+ # @return [TimezoneOffset] the offset that is constantly observed.
9
+ attr_reader :constant_offset
10
+
11
+ # Initializes a new {ConstantOffsetDataTimezoneInfo}.
12
+ #
13
+ # The passed in `identifier` instance will be frozen. A reference to the
14
+ # passed in {TimezoneOffset} will be retained.
15
+ #
16
+ # @param identifier [String] the identifier of the time zone.
17
+ # @param constant_offset [TimezoneOffset] the constantly observed offset.
18
+ # @raise [ArgumentError] if `identifier` or `constant_offset` is `nil`.
19
+ def initialize(identifier, constant_offset)
20
+ super(identifier)
21
+ raise ArgumentError, 'constant_offset must be specified' unless constant_offset
22
+ @constant_offset = constant_offset
23
+ end
24
+
25
+ # @param timestamp [Timestamp] ignored.
26
+ # @return [TimezonePeriod] an unbounded {TimezonePeriod} for the time
27
+ # zone's constantly observed offset.
28
+ def period_for(timestamp)
29
+ constant_period
30
+ end
31
+
32
+ # @param local_timestamp [Timestamp] ignored.
33
+ # @return [Array<TimezonePeriod>] an `Array` containing a single unbounded
34
+ # {TimezonePeriod} for the time zone's constantly observed offset.
35
+ def periods_for_local(local_timestamp)
36
+ [constant_period]
37
+ end
38
+
39
+ # @param to_timestamp [Timestamp] ignored.
40
+ # @param from_timestamp [Timestamp] ignored.
41
+ # @return [Array] an empty `Array`, since there are no transitions in time
42
+ # zones that observe a constant offset.
43
+ def transitions_up_to(to_timestamp, from_timestamp = nil)
44
+ []
45
+ end
46
+
47
+ private
48
+
49
+ # @return [TimezonePeriod] an unbounded {TimezonePeriod} with the constant
50
+ # offset of this timezone.
51
+ def constant_period
52
+ OffsetTimezonePeriod.new(@constant_offset)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -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,145 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ module TZInfo
5
+ # Use send as a workaround for erroneous 'wrong number of arguments' errors
6
+ # with JRuby 9.0.5.0 when calling methods with Java implementations. See #114.
7
+ send(:using, UntaintExt) if TZInfo.const_defined?(:UntaintExt)
8
+
9
+ module DataSources
10
+ # A {TZInfoDataNotFound} exception is raised if the tzinfo-data gem could
11
+ # not be found (i.e. `require 'tzinfo/data'` failed) when selecting the Ruby
12
+ # data source.
13
+ class TZInfoDataNotFound < StandardError
14
+ end
15
+
16
+ # A DataSource implementation that loads data from the set of Ruby modules
17
+ # included in the tzinfo-data gem.
18
+ #
19
+ # TZInfo will use {RubyDataSource} by default if the tzinfo-data gem
20
+ # is available on the load path. It can also be selected by calling
21
+ # {DataSource.set} as follows:
22
+ #
23
+ # TZInfo::DataSource.set(:ruby)
24
+ class RubyDataSource < DataSource
25
+ # (see DataSource#data_timezone_identifiers)
26
+ attr_reader :data_timezone_identifiers
27
+
28
+ # (see DataSource#linked_timezone_identifiers)
29
+ attr_reader :linked_timezone_identifiers
30
+
31
+ # (see DataSource#country_codes)
32
+ attr_reader :country_codes
33
+
34
+ # Initializes a new {RubyDataSource} instance.
35
+ #
36
+ # @raise [TZInfoDataNotFound] if the tzinfo-data gem could not be found
37
+ # (i.e. `require 'tzinfo/data'` failed).
38
+ def initialize
39
+ super
40
+
41
+ begin
42
+ require('tzinfo/data')
43
+ rescue LoadError
44
+ raise TZInfoDataNotFound, "The tzinfo-data gem could not be found (require 'tzinfo/data' failed)."
45
+ end
46
+
47
+ if TZInfo::Data.const_defined?(:LOCATION)
48
+ # Format 2
49
+ @base_path = File.join(TZInfo::Data::LOCATION, 'tzinfo', 'data')
50
+ else
51
+ # Format 1
52
+ data_file = File.join('', 'tzinfo', 'data.rb')
53
+ path = $".reverse_each.detect {|p| p.end_with?(data_file) }
54
+ if path
55
+ @base_path = File.join(File.dirname(path), 'data').untaint
56
+ else
57
+ @base_path = 'tzinfo/data'
58
+ end
59
+ end
60
+
61
+ require_index('timezones')
62
+ require_index('countries')
63
+
64
+ @data_timezone_identifiers = Data::Indexes::Timezones.data_timezones
65
+ @linked_timezone_identifiers = Data::Indexes::Timezones.linked_timezones
66
+ @countries = Data::Indexes::Countries.countries
67
+ @country_codes = @countries.keys.sort!.freeze
68
+ end
69
+
70
+ # (see DataSource#to_s)
71
+ def to_s
72
+ "Ruby DataSource: #{version_info}"
73
+ end
74
+
75
+ # (see DataSource#inspect)
76
+ def inspect
77
+ "#<TZInfo::DataSources::RubyDataSource: #{version_info}>"
78
+ end
79
+
80
+ protected
81
+
82
+ # Returns a {TimezoneInfo} instance for the given time zone identifier.
83
+ # The result will either be a {ConstantOffsetDataTimezoneInfo}, a
84
+ # {TransitionsDataTimezoneInfo} or a {LinkedTimezoneInfo} depending on the
85
+ # type of time zone.
86
+ #
87
+ # @param identifier [String] A time zone identifier.
88
+ # @return [TimezoneInfo] a {TimezoneInfo} instance for the given time zone
89
+ # identifier.
90
+ # @raise [InvalidTimezoneIdentifier] if the time zone is not found or the
91
+ # identifier is invalid.
92
+ def load_timezone_info(identifier)
93
+ valid_identifier = validate_timezone_identifier(identifier)
94
+ split_identifier = valid_identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__').split('/')
95
+
96
+ begin
97
+ require_definition(split_identifier)
98
+
99
+ m = Data::Definitions
100
+ split_identifier.each {|part| m = m.const_get(part) }
101
+ m.get
102
+ rescue LoadError, NameError => e
103
+ raise InvalidTimezoneIdentifier, "#{e.message.encode(Encoding::UTF_8)} (loading #{valid_identifier})"
104
+ end
105
+ end
106
+
107
+ # (see DataSource#load_country_info)
108
+ def load_country_info(code)
109
+ lookup_country_info(@countries, code)
110
+ end
111
+
112
+ private
113
+
114
+ # Requires a zone definition by its identifier (split on /).
115
+ #
116
+ # @param identifier [Array<string>] the component parts of a time zone
117
+ # identifier (split on /). This must have already been validated.
118
+ def require_definition(identifier)
119
+ require_data(*(['definitions'] + identifier))
120
+ end
121
+
122
+ # Requires an index by its name.
123
+ #
124
+ # @param name [String] an index name.
125
+ def require_index(name)
126
+ require_data(*['indexes', name])
127
+ end
128
+
129
+ # Requires a file from tzinfo/data.
130
+ #
131
+ # @param file [Array<String>] a relative path to a file to be required.
132
+ def require_data(*file)
133
+ require(File.join(@base_path, *file))
134
+ end
135
+
136
+ # @return [String] a `String` containing TZInfo::Data version infomation
137
+ # for inclusion in the #to_s and #inspect output.
138
+ def version_info
139
+ # The TZInfo::Data::VERSION constant is only available from v1.2014.8
140
+ # onwards.
141
+ "tzdb v#{TZInfo::Data::Version::TZDATA}#{TZInfo::Data.const_defined?(:VERSION) ? ", tzinfo-data v#{TZInfo::Data::VERSION}" : ''}"
142
+ end
143
+ end
144
+ end
145
+ 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