tzinfo 1.2.6 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -1
  4. data/.yardopts +3 -0
  5. data/CHANGES.md +494 -380
  6. data/LICENSE +13 -13
  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 +44 -100
  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 -176
  63. data/lib/tzinfo/ruby_country_info.rb +0 -74
  64. data/lib/tzinfo/ruby_data_source.rb +0 -138
  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 -298
  74. data/test/tc_country.rb +0 -236
  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 -165
  88. data/test/tc_time_or_datetime.rb +0 -660
  89. data/test/tc_timezone.rb +0 -1359
  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 -1234
  106. data/test/test_utils.rb +0 -188
  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