tzinfo 1.2.1 → 2.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.yardopts +3 -0
- data/CHANGES.md +693 -315
- data/LICENSE +13 -13
- data/README.md +368 -113
- data/lib/tzinfo/annual_rules.rb +71 -0
- data/lib/tzinfo/country.rb +148 -120
- data/lib/tzinfo/country_timezone.rb +71 -101
- data/lib/tzinfo/data_source.rb +400 -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/posix_time_zone_parser.rb +181 -0
- data/lib/tzinfo/data_sources/ruby_data_source.rb +145 -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 +596 -0
- data/lib/tzinfo/data_sources/zoneinfo_reader.rb +486 -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 +154 -0
- data/lib/tzinfo/timestamp.rb +552 -0
- data/lib/tzinfo/timestamp_with_offset.rb +85 -0
- data/lib/tzinfo/timezone.rb +997 -473
- data/lib/tzinfo/timezone_offset.rb +91 -54
- data/lib/tzinfo/timezone_period.rb +163 -188
- data/lib/tzinfo/timezone_proxy.rb +75 -49
- data/lib/tzinfo/timezone_transition.rb +77 -99
- data/lib/tzinfo/transition_rule.rb +455 -0
- data/lib/tzinfo/transitions_timezone_period.rb +63 -0
- data/lib/tzinfo/untaint_ext.rb +18 -0
- data/lib/tzinfo/version.rb +7 -0
- data/lib/tzinfo/with_offset.rb +61 -0
- data/lib/tzinfo.rb +74 -29
- data.tar.gz.sig +0 -0
- metadata +72 -120
- metadata.gz.sig +0 -0
- data/Rakefile +0 -104
- 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/ruby_core_support.rb +0 -146
- data/lib/tzinfo/ruby_country_info.rb +0 -70
- data/lib/tzinfo/ruby_data_source.rb +0 -136
- data/lib/tzinfo/time_or_datetime.rb +0 -333
- 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 -101
- data/lib/tzinfo/transition_data_timezone_info.rb +0 -274
- data/lib/tzinfo/zoneinfo_country_info.rb +0 -35
- data/lib/tzinfo/zoneinfo_data_source.rb +0 -460
- data/lib/tzinfo/zoneinfo_timezone_info.rb +0 -245
- data/test/tc_country.rb +0 -236
- data/test/tc_country_index_definition.rb +0 -69
- data/test/tc_country_info.rb +0 -16
- data/test/tc_country_timezone.rb +0 -161
- data/test/tc_data_source.rb +0 -192
- 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_ruby_core_support.rb +0 -168
- data/test/tc_ruby_country_info.rb +0 -80
- data/test/tc_ruby_data_source.rb +0 -143
- data/test/tc_time_or_datetime.rb +0 -639
- data/test/tc_timezone.rb +0 -1334
- 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 -548
- data/test/tc_timezone_proxy.rb +0 -113
- data/test/tc_timezone_transition.rb +0 -352
- data/test/tc_timezone_transition_definition.rb +0 -284
- data/test/tc_timezone_utc.rb +0 -27
- data/test/tc_transition_data_timezone_info.rb +0 -423
- data/test/tc_zoneinfo_country_info.rb +0 -64
- data/test/tc_zoneinfo_data_source.rb +0 -985
- data/test/tc_zoneinfo_timezone_info.rb +0 -814
- data/test/test_utils.rb +0 -132
- data/test/ts_all.rb +0 -7
- data/test/ts_all_ruby.rb +0 -5
- data/test/ts_all_zoneinfo.rb +0 -7
- 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 -315
- data/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb +0 -218
- 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 -261
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb +0 -186
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb +0 -321
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb +0 -265
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb +0 -220
- data/test/tzinfo-data/tzinfo/data/definitions/UTC.rb +0 -16
- data/test/tzinfo-data/tzinfo/data/indexes/countries.rb +0 -927
- data/test/tzinfo-data/tzinfo/data/indexes/timezones.rb +0 -594
- data/test/tzinfo-data/tzinfo/data/version.rb +0 -14
- 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 -275
- 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/tzinfo.gemspec +0 -21
@@ -0,0 +1,455 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TZInfo
|
5
|
+
# Base class for rules definining the transition between standard and daylight
|
6
|
+
# savings time.
|
7
|
+
#
|
8
|
+
# @abstract
|
9
|
+
# @private
|
10
|
+
class TransitionRule #:nodoc:
|
11
|
+
# Returns the number of seconds after midnight local time on the day
|
12
|
+
# identified by the rule at which the transition occurs. Can be negative to
|
13
|
+
# denote a time on the prior day. Can be greater than or equal to 86,400 to
|
14
|
+
# denote a time of the following day.
|
15
|
+
#
|
16
|
+
# @return [Integer] the time in seconds after midnight local time at which
|
17
|
+
# the transition occurs.
|
18
|
+
attr_reader :transition_at
|
19
|
+
|
20
|
+
# Initializes a new {TransitionRule}.
|
21
|
+
#
|
22
|
+
# @param transition_at [Integer] the time in seconds after midnight local
|
23
|
+
# time at which the transition occurs.
|
24
|
+
# @raise [ArgumentError] if `transition_at` is not an `Integer`.
|
25
|
+
def initialize(transition_at)
|
26
|
+
raise ArgumentError, 'Invalid transition_at' unless transition_at.kind_of?(Integer)
|
27
|
+
@transition_at = transition_at
|
28
|
+
end
|
29
|
+
|
30
|
+
# Calculates the time of the transition from a given offset on a given year.
|
31
|
+
#
|
32
|
+
# @param offset [TimezoneOffset] the current offset at the time the rule
|
33
|
+
# will transition.
|
34
|
+
# @param year [Integer] the year in which the transition occurs (local
|
35
|
+
# time).
|
36
|
+
# @return [TimestampWithOffset] the time at which the transition occurs.
|
37
|
+
def at(offset, year)
|
38
|
+
day = get_day(offset, year)
|
39
|
+
TimestampWithOffset.set_timezone_offset(Timestamp.for(day + @transition_at), offset)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Determines if this {TransitionRule} is equal to another instance.
|
43
|
+
#
|
44
|
+
# @param r [Object] the instance to test for equality.
|
45
|
+
# @return [Boolean] `true` if `r` is a {TransitionRule} with the same
|
46
|
+
# {transition_at} as this {TransitionRule}, otherwise `false`.
|
47
|
+
def ==(r)
|
48
|
+
r.kind_of?(TransitionRule) && @transition_at == r.transition_at
|
49
|
+
end
|
50
|
+
alias eql? ==
|
51
|
+
|
52
|
+
# @return [Integer] a hash based on {hash_args} (defaulting to
|
53
|
+
# {transition_at}).
|
54
|
+
def hash
|
55
|
+
hash_args.hash
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
# @return [Array] an `Array` of parameters that will influence the output of
|
61
|
+
# {hash}.
|
62
|
+
def hash_args
|
63
|
+
[@transition_at]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
private_constant :TransitionRule
|
67
|
+
|
68
|
+
# A base class for transition rules that activate based on an integer day of
|
69
|
+
# the year.
|
70
|
+
#
|
71
|
+
# @abstract
|
72
|
+
# @private
|
73
|
+
class DayOfYearTransitionRule < TransitionRule #:nodoc:
|
74
|
+
# Initializes a new {DayOfYearTransitionRule}.
|
75
|
+
#
|
76
|
+
# @param day [Integer] the day of the year on which the transition occurs.
|
77
|
+
# The precise meaning is defined by subclasses.
|
78
|
+
# @param transition_at [Integer] the time in seconds after midnight local
|
79
|
+
# time at which the transition occurs.
|
80
|
+
# @raise [ArgumentError] if `transition_at` is not an `Integer`.
|
81
|
+
# @raise [ArgumentError] if `day` is not an `Integer`.
|
82
|
+
def initialize(day, transition_at)
|
83
|
+
super(transition_at)
|
84
|
+
raise ArgumentError, 'Invalid day' unless day.kind_of?(Integer)
|
85
|
+
@seconds = day * 86400
|
86
|
+
end
|
87
|
+
|
88
|
+
# Determines if this {DayOfYearTransitionRule} is equal to another instance.
|
89
|
+
#
|
90
|
+
# @param r [Object] the instance to test for equality.
|
91
|
+
# @return [Boolean] `true` if `r` is a {DayOfYearTransitionRule} with the
|
92
|
+
# same {transition_at} and day as this {DayOfYearTransitionRule},
|
93
|
+
# otherwise `false`.
|
94
|
+
def ==(r)
|
95
|
+
super(r) && r.kind_of?(DayOfYearTransitionRule) && @seconds == r.seconds
|
96
|
+
end
|
97
|
+
alias eql? ==
|
98
|
+
|
99
|
+
protected
|
100
|
+
|
101
|
+
# @return [Integer] the day multipled by the number of seconds in a day.
|
102
|
+
attr_reader :seconds
|
103
|
+
|
104
|
+
# (see TransitionRule#hash_args)
|
105
|
+
def hash_args
|
106
|
+
[@seconds] + super
|
107
|
+
end
|
108
|
+
end
|
109
|
+
private_constant :DayOfYearTransitionRule
|
110
|
+
|
111
|
+
# Defines transitions that occur on the zero-based nth day of the year.
|
112
|
+
#
|
113
|
+
# Day 0 is 1 January.
|
114
|
+
#
|
115
|
+
# Leap days are counted. Day 59 will be 29 February on a leap year and 1 March
|
116
|
+
# on a non-leap year. Day 365 will be 31 December on a leap year and 1 January
|
117
|
+
# the following year on a non-leap year.
|
118
|
+
#
|
119
|
+
# @private
|
120
|
+
class AbsoluteDayOfYearTransitionRule < DayOfYearTransitionRule #:nodoc:
|
121
|
+
# Initializes a new {AbsoluteDayOfYearTransitionRule}.
|
122
|
+
#
|
123
|
+
# @param day [Integer] the zero-based day of the year on which the
|
124
|
+
# transition occurs (0 to 365 inclusive).
|
125
|
+
# @param transition_at [Integer] the time in seconds after midnight local
|
126
|
+
# time at which the transition occurs.
|
127
|
+
# @raise [ArgumentError] if `transition_at` is not an `Integer`.
|
128
|
+
# @raise [ArgumentError] if `day` is not an `Integer`.
|
129
|
+
# @raise [ArgumentError] if `day` is less than 0 or greater than 365.
|
130
|
+
def initialize(day, transition_at = 0)
|
131
|
+
super(day, transition_at)
|
132
|
+
raise ArgumentError, 'Invalid day' unless day >= 0 && day <= 365
|
133
|
+
end
|
134
|
+
|
135
|
+
# @return [Boolean] `true` if the day specified by this transition is the
|
136
|
+
# first in the year (a day number of 0), otherwise `false`.
|
137
|
+
def is_always_first_day_of_year?
|
138
|
+
seconds == 0
|
139
|
+
end
|
140
|
+
|
141
|
+
# @return [Boolean] `false`.
|
142
|
+
def is_always_last_day_of_year?
|
143
|
+
false
|
144
|
+
end
|
145
|
+
|
146
|
+
# Determines if this {AbsoluteDayOfYearTransitionRule} is equal to another
|
147
|
+
# instance.
|
148
|
+
#
|
149
|
+
# @param r [Object] the instance to test for equality.
|
150
|
+
# @return [Boolean] `true` if `r` is a {AbsoluteDayOfYearTransitionRule}
|
151
|
+
# with the same {transition_at} and day as this
|
152
|
+
# {AbsoluteDayOfYearTransitionRule}, otherwise `false`.
|
153
|
+
def ==(r)
|
154
|
+
super(r) && r.kind_of?(AbsoluteDayOfYearTransitionRule)
|
155
|
+
end
|
156
|
+
alias eql? ==
|
157
|
+
|
158
|
+
protected
|
159
|
+
|
160
|
+
# Returns a `Time` representing midnight local time on the day specified by
|
161
|
+
# the rule for the given offset and year.
|
162
|
+
#
|
163
|
+
# @param offset [TimezoneOffset] the current offset at the time of the
|
164
|
+
# transition.
|
165
|
+
# @param year [Integer] the year in which the transition occurs.
|
166
|
+
# @return [Time] midnight local time on the day specified by the rule for
|
167
|
+
# the given offset and year.
|
168
|
+
def get_day(offset, year)
|
169
|
+
Time.new(year, 1, 1, 0, 0, 0, offset.observed_utc_offset) + seconds
|
170
|
+
end
|
171
|
+
|
172
|
+
# (see TransitionRule#hash_args)
|
173
|
+
def hash_args
|
174
|
+
[AbsoluteDayOfYearTransitionRule] + super
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Defines transitions that occur on the one-based nth Julian day of the year.
|
179
|
+
#
|
180
|
+
# Leap days are not counted. Day 1 is 1 January. Day 60 is always 1 March.
|
181
|
+
# Day 365 is always 31 December.
|
182
|
+
#
|
183
|
+
# @private
|
184
|
+
class JulianDayOfYearTransitionRule < DayOfYearTransitionRule #:nodoc:
|
185
|
+
# The 60 days in seconds.
|
186
|
+
LEAP = 60 * 86400
|
187
|
+
private_constant :LEAP
|
188
|
+
|
189
|
+
# The length of a non-leap year in seconds.
|
190
|
+
YEAR = 365 * 86400
|
191
|
+
private_constant :YEAR
|
192
|
+
|
193
|
+
# Initializes a new {JulianDayOfYearTransitionRule}.
|
194
|
+
#
|
195
|
+
# @param day [Integer] the one-based Julian day of the year on which the
|
196
|
+
# transition occurs (1 to 365 inclusive).
|
197
|
+
# @param transition_at [Integer] the time in seconds after midnight local
|
198
|
+
# time at which the transition occurs.
|
199
|
+
# @raise [ArgumentError] if `transition_at` is not an `Integer`.
|
200
|
+
# @raise [ArgumentError] if `day` is not an `Integer`.
|
201
|
+
# @raise [ArgumentError] if `day` is less than 1 or greater than 365.
|
202
|
+
def initialize(day, transition_at = 0)
|
203
|
+
super(day, transition_at)
|
204
|
+
raise ArgumentError, 'Invalid day' unless day >= 1 && day <= 365
|
205
|
+
end
|
206
|
+
|
207
|
+
# @return [Boolean] `true` if the day specified by this transition is the
|
208
|
+
# first in the year (a day number of 1), otherwise `false`.
|
209
|
+
def is_always_first_day_of_year?
|
210
|
+
seconds == 86400
|
211
|
+
end
|
212
|
+
|
213
|
+
# @return [Boolean] `true` if the day specified by this transition is the
|
214
|
+
# last in the year (a day number of 365), otherwise `false`.
|
215
|
+
def is_always_last_day_of_year?
|
216
|
+
seconds == YEAR
|
217
|
+
end
|
218
|
+
|
219
|
+
# Determines if this {JulianDayOfYearTransitionRule} is equal to another
|
220
|
+
# instance.
|
221
|
+
#
|
222
|
+
# @param r [Object] the instance to test for equality.
|
223
|
+
# @return [Boolean] `true` if `r` is a {JulianDayOfYearTransitionRule} with
|
224
|
+
# the same {transition_at} and day as this
|
225
|
+
# {JulianDayOfYearTransitionRule}, otherwise `false`.
|
226
|
+
def ==(r)
|
227
|
+
super(r) && r.kind_of?(JulianDayOfYearTransitionRule)
|
228
|
+
end
|
229
|
+
alias eql? ==
|
230
|
+
|
231
|
+
protected
|
232
|
+
|
233
|
+
# Returns a `Time` representing midnight local time on the day specified by
|
234
|
+
# the rule for the given offset and year.
|
235
|
+
#
|
236
|
+
# @param offset [TimezoneOffset] the current offset at the time of the
|
237
|
+
# transition.
|
238
|
+
# @param year [Integer] the year in which the transition occurs.
|
239
|
+
# @return [Time] midnight local time on the day specified by the rule for
|
240
|
+
# the given offset and year.
|
241
|
+
def get_day(offset, year)
|
242
|
+
# Returns 1 March on non-leap years.
|
243
|
+
leap = Time.new(year, 2, 29, 0, 0, 0, offset.observed_utc_offset)
|
244
|
+
diff = seconds - LEAP
|
245
|
+
diff += 86400 if diff >= 0 && leap.mday == 29
|
246
|
+
leap + diff
|
247
|
+
end
|
248
|
+
|
249
|
+
# (see TransitionRule#hash_args)
|
250
|
+
def hash_args
|
251
|
+
[JulianDayOfYearTransitionRule] + super
|
252
|
+
end
|
253
|
+
end
|
254
|
+
private_constant :JulianDayOfYearTransitionRule
|
255
|
+
|
256
|
+
# A base class for rules that transition on a particular day of week of a
|
257
|
+
# given week (subclasses specify which week of the month).
|
258
|
+
#
|
259
|
+
# @abstract
|
260
|
+
# @private
|
261
|
+
class DayOfWeekTransitionRule < TransitionRule #:nodoc:
|
262
|
+
# Initializes a new {DayOfWeekTransitionRule}.
|
263
|
+
#
|
264
|
+
# @param month [Integer] the month of the year when the transition occurs.
|
265
|
+
# @param day_of_week [Integer] the day of the week when the transition
|
266
|
+
# occurs. 0 is Sunday, 6 is Saturday.
|
267
|
+
# @param transition_at [Integer] the time in seconds after midnight local
|
268
|
+
# time at which the transition occurs.
|
269
|
+
# @raise [ArgumentError] if `transition_at` is not an `Integer`.
|
270
|
+
# @raise [ArgumentError] if `month` is not an `Integer`.
|
271
|
+
# @raise [ArgumentError] if `month` is less than 1 or greater than 12.
|
272
|
+
# @raise [ArgumentError] if `day_of_week` is not an `Integer`.
|
273
|
+
# @raise [ArgumentError] if `day_of_week` is less than 0 or greater than 6.
|
274
|
+
def initialize(month, day_of_week, transition_at)
|
275
|
+
super(transition_at)
|
276
|
+
raise ArgumentError, 'Invalid month' unless month.kind_of?(Integer) && month >= 1 && month <= 12
|
277
|
+
raise ArgumentError, 'Invalid day_of_week' unless day_of_week.kind_of?(Integer) && day_of_week >= 0 && day_of_week <= 6
|
278
|
+
@month = month
|
279
|
+
@day_of_week = day_of_week
|
280
|
+
end
|
281
|
+
|
282
|
+
# @return [Boolean] `false`.
|
283
|
+
def is_always_first_day_of_year?
|
284
|
+
false
|
285
|
+
end
|
286
|
+
|
287
|
+
# @return [Boolean] `false`.
|
288
|
+
def is_always_last_day_of_year?
|
289
|
+
false
|
290
|
+
end
|
291
|
+
|
292
|
+
# Determines if this {DayOfWeekTransitionRule} is equal to another
|
293
|
+
# instance.
|
294
|
+
#
|
295
|
+
# @param r [Object] the instance to test for equality.
|
296
|
+
# @return [Boolean] `true` if `r` is a {DayOfWeekTransitionRule} with the
|
297
|
+
# same {transition_at}, month and day of week as this
|
298
|
+
# {DayOfWeekTransitionRule}, otherwise `false`.
|
299
|
+
def ==(r)
|
300
|
+
super(r) && r.kind_of?(DayOfWeekTransitionRule) && @month == r.month && @day_of_week == r.day_of_week
|
301
|
+
end
|
302
|
+
alias eql? ==
|
303
|
+
|
304
|
+
protected
|
305
|
+
|
306
|
+
# @return [Integer] the month of the year (1 to 12).
|
307
|
+
attr_reader :month
|
308
|
+
|
309
|
+
# @return [Integer] the day of the week (0 to 6 for Sunday to Monday).
|
310
|
+
attr_reader :day_of_week
|
311
|
+
|
312
|
+
# (see TransitionRule#hash_args)
|
313
|
+
def hash_args
|
314
|
+
[@month, @day_of_week] + super
|
315
|
+
end
|
316
|
+
end
|
317
|
+
private_constant :DayOfWeekTransitionRule
|
318
|
+
|
319
|
+
# A rule that transitions on the nth occurrence of a particular day of week
|
320
|
+
# of a calendar month.
|
321
|
+
#
|
322
|
+
# @private
|
323
|
+
class DayOfMonthTransitionRule < DayOfWeekTransitionRule #:nodoc:
|
324
|
+
# Initializes a new {DayOfMonthTransitionRule}.
|
325
|
+
#
|
326
|
+
# @param month [Integer] the month of the year when the transition occurs.
|
327
|
+
# @param week [Integer] the week of the month when the transition occurs (1
|
328
|
+
# to 4).
|
329
|
+
# @param day_of_week [Integer] the day of the week when the transition
|
330
|
+
# occurs. 0 is Sunday, 6 is Saturday.
|
331
|
+
# @param transition_at [Integer] the time in seconds after midnight local
|
332
|
+
# time at which the transition occurs.
|
333
|
+
# @raise [ArgumentError] if `transition_at` is not an `Integer`.
|
334
|
+
# @raise [ArgumentError] if `month` is not an `Integer`.
|
335
|
+
# @raise [ArgumentError] if `month` is less than 1 or greater than 12.
|
336
|
+
# @raise [ArgumentError] if `week` is not an `Integer`.
|
337
|
+
# @raise [ArgumentError] if `week` is less than 1 or greater than 4.
|
338
|
+
# @raise [ArgumentError] if `day_of_week` is not an `Integer`.
|
339
|
+
# @raise [ArgumentError] if `day_of_week` is less than 0 or greater than 6.
|
340
|
+
def initialize(month, week, day_of_week, transition_at = 0)
|
341
|
+
super(month, day_of_week, transition_at)
|
342
|
+
raise ArgumentError, 'Invalid week' unless week.kind_of?(Integer) && week >= 1 && week <= 4
|
343
|
+
@offset_start = (week - 1) * 7 + 1
|
344
|
+
end
|
345
|
+
|
346
|
+
# Determines if this {DayOfMonthTransitionRule} is equal to another
|
347
|
+
# instance.
|
348
|
+
#
|
349
|
+
# @param r [Object] the instance to test for equality.
|
350
|
+
# @return [Boolean] `true` if `r` is a {DayOfMonthTransitionRule} with the
|
351
|
+
# same {transition_at}, month, week and day of week as this
|
352
|
+
# {DayOfMonthTransitionRule}, otherwise `false`.
|
353
|
+
def ==(r)
|
354
|
+
super(r) && r.kind_of?(DayOfMonthTransitionRule) && @offset_start == r.offset_start
|
355
|
+
end
|
356
|
+
alias eql? ==
|
357
|
+
|
358
|
+
protected
|
359
|
+
|
360
|
+
# @return [Integer] the day the week starts on for a month starting on a
|
361
|
+
# Sunday.
|
362
|
+
attr_reader :offset_start
|
363
|
+
|
364
|
+
# Returns a `Time` representing midnight local time on the day specified by
|
365
|
+
# the rule for the given offset and year.
|
366
|
+
#
|
367
|
+
# @param offset [TimezoneOffset] the current offset at the time of the
|
368
|
+
# transition.
|
369
|
+
# @param year [Integer] the year in which the transition occurs.
|
370
|
+
# @return [Time] midnight local time on the day specified by the rule for
|
371
|
+
# the given offset and year.
|
372
|
+
def get_day(offset, year)
|
373
|
+
candidate = Time.new(year, month, @offset_start, 0, 0, 0, offset.observed_utc_offset)
|
374
|
+
diff = day_of_week - candidate.wday
|
375
|
+
|
376
|
+
if diff < 0
|
377
|
+
candidate + (7 + diff) * 86400
|
378
|
+
elsif diff > 0
|
379
|
+
candidate + diff * 86400
|
380
|
+
else
|
381
|
+
candidate
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
# (see TransitionRule#hash_args)
|
386
|
+
def hash_args
|
387
|
+
[@offset_start] + super
|
388
|
+
end
|
389
|
+
end
|
390
|
+
private_constant :DayOfMonthTransitionRule
|
391
|
+
|
392
|
+
# A rule that transitions on the last occurrence of a particular day of week
|
393
|
+
# of a calendar month.
|
394
|
+
#
|
395
|
+
# @private
|
396
|
+
class LastDayOfMonthTransitionRule < DayOfWeekTransitionRule #:nodoc:
|
397
|
+
# Initializes a new {LastDayOfMonthTransitionRule}.
|
398
|
+
#
|
399
|
+
# @param month [Integer] the month of the year when the transition occurs.
|
400
|
+
# @param day_of_week [Integer] the day of the week when the transition
|
401
|
+
# occurs. 0 is Sunday, 6 is Saturday.
|
402
|
+
# @param transition_at [Integer] the time in seconds after midnight local
|
403
|
+
# time at which the transition occurs.
|
404
|
+
# @raise [ArgumentError] if `transition_at` is not an `Integer`.
|
405
|
+
# @raise [ArgumentError] if `month` is not an `Integer`.
|
406
|
+
# @raise [ArgumentError] if `month` is less than 1 or greater than 12.
|
407
|
+
# @raise [ArgumentError] if `day_of_week` is not an `Integer`.
|
408
|
+
# @raise [ArgumentError] if `day_of_week` is less than 0 or greater than 6.
|
409
|
+
def initialize(month, day_of_week, transition_at = 0)
|
410
|
+
super(month, day_of_week, transition_at)
|
411
|
+
end
|
412
|
+
|
413
|
+
# Determines if this {LastDayOfMonthTransitionRule} is equal to another
|
414
|
+
# instance.
|
415
|
+
#
|
416
|
+
# @param r [Object] the instance to test for equality.
|
417
|
+
# @return [Boolean] `true` if `r` is a {LastDayOfMonthTransitionRule} with
|
418
|
+
# the same {transition_at}, month and day of week as this
|
419
|
+
# {LastDayOfMonthTransitionRule}, otherwise `false`.
|
420
|
+
def ==(r)
|
421
|
+
super(r) && r.kind_of?(LastDayOfMonthTransitionRule)
|
422
|
+
end
|
423
|
+
alias eql? ==
|
424
|
+
|
425
|
+
protected
|
426
|
+
|
427
|
+
# Returns a `Time` representing midnight local time on the day specified by
|
428
|
+
# the rule for the given offset and year.
|
429
|
+
#
|
430
|
+
# @param offset [TimezoneOffset] the current offset at the time of the
|
431
|
+
# transition.
|
432
|
+
# @param year [Integer] the year in which the transition occurs.
|
433
|
+
# @return [Time] midnight local time on the day specified by the rule for
|
434
|
+
# the given offset and year.
|
435
|
+
def get_day(offset, year)
|
436
|
+
next_month = month + 1
|
437
|
+
if next_month == 13
|
438
|
+
year += 1
|
439
|
+
next_month = 1
|
440
|
+
end
|
441
|
+
|
442
|
+
candidate = Time.new(year, next_month, 1, 0, 0, 0, offset.observed_utc_offset) - 86400
|
443
|
+
diff = candidate.wday - day_of_week
|
444
|
+
|
445
|
+
if diff < 0
|
446
|
+
candidate - (diff + 7) * 86400
|
447
|
+
elsif diff > 0
|
448
|
+
candidate - diff * 86400
|
449
|
+
else
|
450
|
+
candidate
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
private_constant :LastDayOfMonthTransitionRule
|
455
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TZInfo
|
5
|
+
# Represents a period of time in a time zone where the same offset from UTC
|
6
|
+
# applies. The period of time is bounded at at least one end, either having a
|
7
|
+
# start transition, end transition or both start and end transitions.
|
8
|
+
class TransitionsTimezonePeriod < TimezonePeriod
|
9
|
+
# @return [TimezoneTransition] the transition that defines the start of this
|
10
|
+
# {TimezonePeriod} (`nil` if the start is unbounded).
|
11
|
+
attr_reader :start_transition
|
12
|
+
|
13
|
+
# @return [TimezoneTransition] the transition that defines the end of this
|
14
|
+
# {TimezonePeriod} (`nil` if the end is unbounded).
|
15
|
+
attr_reader :end_transition
|
16
|
+
|
17
|
+
# Initializes a {TransitionsTimezonePeriod}.
|
18
|
+
#
|
19
|
+
# At least one of `start_transition` and `end_transition` must be specified.
|
20
|
+
#
|
21
|
+
# @param start_transition [TimezoneTransition] the transition that defines
|
22
|
+
# the start of the period, or `nil` if the start is unbounded.
|
23
|
+
# @param end_transition [TimezoneTransition] the transition that defines the
|
24
|
+
# end of the period, or `nil` if the end is unbounded.
|
25
|
+
# @raise [ArgumentError] if both `start_transition` and `end_transition` are
|
26
|
+
# `nil`.
|
27
|
+
def initialize(start_transition, end_transition)
|
28
|
+
if start_transition
|
29
|
+
super(start_transition.offset)
|
30
|
+
elsif end_transition
|
31
|
+
super(end_transition.previous_offset)
|
32
|
+
else
|
33
|
+
raise ArgumentError, 'At least one of start_transition and end_transition must be specified'
|
34
|
+
end
|
35
|
+
|
36
|
+
@start_transition = start_transition
|
37
|
+
@end_transition = end_transition
|
38
|
+
end
|
39
|
+
|
40
|
+
# Determines if this {TransitionsTimezonePeriod} is equal to another
|
41
|
+
# instance.
|
42
|
+
#
|
43
|
+
# @param p [Object] the instance to test for equality.
|
44
|
+
# @return [Boolean] `true` if `p` is a {TransitionsTimezonePeriod} with the
|
45
|
+
# same {offset}, {start_transition} and {end_transition}, otherwise
|
46
|
+
# `false`.
|
47
|
+
def ==(p)
|
48
|
+
p.kind_of?(TransitionsTimezonePeriod) && start_transition == p.start_transition && end_transition == p.end_transition
|
49
|
+
end
|
50
|
+
alias eql? ==
|
51
|
+
|
52
|
+
# @return [Integer] a hash based on {start_transition} and {end_transition}.
|
53
|
+
def hash
|
54
|
+
[@start_transition, @end_transition].hash
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [String] the internal object state as a programmer-readable
|
58
|
+
# `String`.
|
59
|
+
def inspect
|
60
|
+
"#<#{self.class}: @start_transition=#{@start_transition.inspect}, @end_transition=#{@end_transition.inspect}>"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TZInfo
|
5
|
+
# Object#untaint is deprecated in Ruby >= 2.7 and will be removed in 3.2.
|
6
|
+
# UntaintExt adds a refinement to make Object#untaint a no-op and avoid the
|
7
|
+
# warning.
|
8
|
+
#
|
9
|
+
# @private
|
10
|
+
module UntaintExt # :nodoc:
|
11
|
+
refine Object do
|
12
|
+
def untaint
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
private_constant :UntaintExt
|
18
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TZInfo
|
5
|
+
# The {WithOffset} module is included in {TimeWithOffset},
|
6
|
+
# {DateTimeWithOffset} and {TimestampWithOffset}. It provides an override for
|
7
|
+
# the {strftime} method that handles expanding the `%Z` directive according to
|
8
|
+
# the {TimezoneOffset#abbreviation abbreviation} of the {TimezoneOffset}
|
9
|
+
# associated with a local time.
|
10
|
+
module WithOffset
|
11
|
+
# Overrides the `Time`, `DateTime` or {Timestamp} version of `strftime`,
|
12
|
+
# replacing `%Z` with the {TimezoneOffset#abbreviation abbreviation} of the
|
13
|
+
# associated {TimezoneOffset}. If there is no associated offset, `%Z` is
|
14
|
+
# expanded by the base class instead.
|
15
|
+
#
|
16
|
+
# All the format directives handled by the base class are supported.
|
17
|
+
#
|
18
|
+
# @param format [String] the format string.
|
19
|
+
# @return [String] the formatted time.
|
20
|
+
# @raise [ArgumentError] if `format` is `nil`.
|
21
|
+
def strftime(format)
|
22
|
+
raise ArgumentError, 'format must be specified' unless format
|
23
|
+
|
24
|
+
if_timezone_offset do |o|
|
25
|
+
abbreviation = nil
|
26
|
+
|
27
|
+
format = format.gsub(/%(%*)Z/) do
|
28
|
+
if $1.length.odd?
|
29
|
+
# Return %%Z so the real strftime treats it as a literal %Z too.
|
30
|
+
"#$1%Z"
|
31
|
+
else
|
32
|
+
"#$1#{abbreviation ||= o.abbreviation.gsub(/%/, '%%')}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
# Performs a calculation if there is an associated {TimezoneOffset}.
|
43
|
+
#
|
44
|
+
# @param result [Object] a result value that can be manipulated by the block
|
45
|
+
# if there is an associated {TimezoneOffset}.
|
46
|
+
# @yield [period, result] if there is an associated {TimezoneOffset}, the
|
47
|
+
# block is yielded to in order to calculate the method result.
|
48
|
+
# @yieldparam period [TimezoneOffset] the associated {TimezoneOffset}.
|
49
|
+
# @yieldparam result [Object] the `result` parameter.
|
50
|
+
# @yieldreturn [Object] the result of the calculation performed if there is
|
51
|
+
# an associated {TimezoneOffset}.
|
52
|
+
# @return [Object] the result of the block if there is an associated
|
53
|
+
# {TimezoneOffset}, otherwise the `result` parameter.
|
54
|
+
#
|
55
|
+
# @private
|
56
|
+
def if_timezone_offset(result = nil) #:nodoc:
|
57
|
+
to = timezone_offset
|
58
|
+
to ? yield(to, result) : result
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|