tzinfo 1.2.7 → 1.2.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of tzinfo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGES.md +11 -0
- data/README.md +1 -1
- data/lib/tzinfo.rb +3 -0
- data/lib/tzinfo/annual_rules.rb +51 -0
- data/lib/tzinfo/posix_time_zone_parser.rb +136 -0
- data/lib/tzinfo/time_or_datetime.rb +11 -0
- data/lib/tzinfo/transition_rule.rb +325 -0
- data/lib/tzinfo/zoneinfo_data_source.rb +2 -1
- data/lib/tzinfo/zoneinfo_timezone_info.rb +255 -40
- data/test/tc_annual_rules.rb +95 -0
- data/test/tc_posix_time_zone_parser.rb +261 -0
- data/test/tc_time_or_datetime.rb +14 -0
- data/test/tc_transition_rule.rb +663 -0
- data/test/tc_zoneinfo_timezone_info.rb +952 -113
- data/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb +5 -5
- data/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb +13 -1
- data/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb +13 -1
- data/test/tzinfo-data/tzinfo/data/definitions/EST.rb +1 -1
- data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb +2 -2
- data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb +2 -2
- data/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb +1 -1
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb +15 -3
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb +13 -1
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb +13 -1
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb +15 -3
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb +19 -4
- data/test/tzinfo-data/tzinfo/data/definitions/UTC.rb +1 -1
- data/test/tzinfo-data/tzinfo/data/indexes/countries.rb +197 -184
- data/test/tzinfo-data/tzinfo/data/indexes/timezones.rb +60 -47
- data/test/tzinfo-data/tzinfo/data/version.rb +9 -3
- 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 +13 -14
- data/test/zoneinfo/leapseconds +38 -21
- 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 +172 -159
- data/test/zoneinfo/zone1970.tab +185 -170
- data/tzinfo.gemspec +1 -1
- metadata +9 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9569ae7387d4f2847ba35f6a1268fbbc9019f9bbef1cc72e70569757320db079
|
4
|
+
data.tar.gz: bb66169c01610b980c35be5c7102d780d07167b785acd6ea5887b89d15eb107d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9847f779c051d96c2457d364b18bdc9bc74e9b17ffd8e84106ce9c6562b2634e60f08b81af9fff6ba7c0af3b898ba4483fe8e9fa31b3e004251b203c1bee933
|
7
|
+
data.tar.gz: 8737907d69e81c06650a1f01b1ba45afc83f38a66b44fca093431cfea65208a92f56ada755e9f87724a6b866ecbfed11d1ef09552bbf983ce47c15c90269a322
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
Version 1.2.8 - 8-Nov-2020
|
2
|
+
--------------------------
|
3
|
+
|
4
|
+
* Added support for handling "slim" format zoneinfo files that are produced by
|
5
|
+
default by zic version 2020b and later. The POSIX-style TZ string is now used
|
6
|
+
calculate DST transition times after the final defined transition in the file.
|
7
|
+
The 64-bit section is now always used regardless of whether Time has support
|
8
|
+
for 64-bit times.
|
9
|
+
* Rubinius is no longer supported.
|
10
|
+
|
11
|
+
|
1
12
|
Version 1.2.7 - 2-Apr-2020
|
2
13
|
--------------------------
|
3
14
|
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
TZInfo - Ruby Timezone Library
|
2
2
|
==============================
|
3
3
|
|
4
|
-
[![RubyGems](https://img.shields.io/gem/v/tzinfo)](https://rubygems.org/gems/tzinfo) [![Travis CI Build](https://img.shields.io/travis/tzinfo/tzinfo/1.2?logo=travis)](https://travis-ci.
|
4
|
+
[![RubyGems](https://img.shields.io/gem/v/tzinfo)](https://rubygems.org/gems/tzinfo) [![Travis CI Build](https://img.shields.io/travis/com/tzinfo/tzinfo/1.2?logo=travis)](https://travis-ci.com/tzinfo/tzinfo) [![AppVeyor Build](https://img.shields.io/appveyor/build/philr/tzinfo/1.2?logo=appveyor)](https://ci.appveyor.com/project/philr/tzinfo/branch/1.2)
|
5
5
|
|
6
6
|
[TZInfo](https://tzinfo.github.io) provides daylight savings aware
|
7
7
|
transformations between times in different timezones.
|
data/lib/tzinfo.rb
CHANGED
@@ -10,6 +10,8 @@ require 'tzinfo/timezone_definition'
|
|
10
10
|
|
11
11
|
require 'tzinfo/timezone_offset'
|
12
12
|
require 'tzinfo/timezone_transition'
|
13
|
+
require 'tzinfo/transition_rule'
|
14
|
+
require 'tzinfo/annual_rules'
|
13
15
|
require 'tzinfo/timezone_transition_definition'
|
14
16
|
|
15
17
|
require 'tzinfo/timezone_index_definition'
|
@@ -22,6 +24,7 @@ require 'tzinfo/zoneinfo_timezone_info'
|
|
22
24
|
|
23
25
|
require 'tzinfo/data_source'
|
24
26
|
require 'tzinfo/ruby_data_source'
|
27
|
+
require 'tzinfo/posix_time_zone_parser'
|
25
28
|
require 'tzinfo/zoneinfo_data_source'
|
26
29
|
|
27
30
|
require 'tzinfo/timezone_period'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module TZInfo
|
2
|
+
# A set of rules that define when transitions occur in time zones with
|
3
|
+
# annually occurring daylight savings time.
|
4
|
+
#
|
5
|
+
# @private
|
6
|
+
class AnnualRules #:nodoc:
|
7
|
+
# Returned by #transitions. #offset is the TimezoneOffset that applies
|
8
|
+
# from the UTC TimeOrDateTime #at. #previous_offset is the prior
|
9
|
+
# TimezoneOffset.
|
10
|
+
Transition = Struct.new(:offset, :previous_offset, :at)
|
11
|
+
|
12
|
+
# The standard offset that applies when daylight savings time is not in
|
13
|
+
# force.
|
14
|
+
attr_reader :std_offset
|
15
|
+
|
16
|
+
# The offset that applies when daylight savings time is in force.
|
17
|
+
attr_reader :dst_offset
|
18
|
+
|
19
|
+
# The rule that determines when daylight savings time starts.
|
20
|
+
attr_reader :dst_start_rule
|
21
|
+
|
22
|
+
# The rule that determines when daylight savings time ends.
|
23
|
+
attr_reader :dst_end_rule
|
24
|
+
|
25
|
+
# Initializes a new {AnnualRules} instance.
|
26
|
+
def initialize(std_offset, dst_offset, dst_start_rule, dst_end_rule)
|
27
|
+
@std_offset = std_offset
|
28
|
+
@dst_offset = dst_offset
|
29
|
+
@dst_start_rule = dst_start_rule
|
30
|
+
@dst_end_rule = dst_end_rule
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the transitions between standard and daylight savings time for a
|
34
|
+
# given year. The results are ordered by time of occurrence (earliest to
|
35
|
+
# latest).
|
36
|
+
def transitions(year)
|
37
|
+
start_dst = apply_rule(@dst_start_rule, @std_offset, @dst_offset, year)
|
38
|
+
end_dst = apply_rule(@dst_end_rule, @dst_offset, @std_offset, year)
|
39
|
+
|
40
|
+
end_dst.at < start_dst.at ? [end_dst, start_dst] : [start_dst, end_dst]
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Applies a given rule between offsets on a year.
|
46
|
+
def apply_rule(rule, from_offset, to_offset, year)
|
47
|
+
at = rule.at(from_offset, year)
|
48
|
+
Transition.new(to_offset, from_offset, at)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'strscan'
|
5
|
+
|
6
|
+
module TZInfo
|
7
|
+
# An {InvalidPosixTimeZone} exception is raised if an invalid POSIX-style
|
8
|
+
# time zone string is encountered.
|
9
|
+
#
|
10
|
+
# @private
|
11
|
+
class InvalidPosixTimeZone < StandardError #:nodoc:
|
12
|
+
end
|
13
|
+
|
14
|
+
# A parser for POSIX-style TZ strings used in zoneinfo files and specified
|
15
|
+
# by tzfile.5 and tzset.3.
|
16
|
+
#
|
17
|
+
# @private
|
18
|
+
class PosixTimeZoneParser #:nodoc:
|
19
|
+
# Parses a POSIX-style TZ string, returning either a TimezoneOffset or
|
20
|
+
# an AnnualRules instance.
|
21
|
+
def parse(tz_string)
|
22
|
+
raise InvalidPosixTimeZone unless tz_string.kind_of?(String)
|
23
|
+
return nil if tz_string.empty?
|
24
|
+
|
25
|
+
s = StringScanner.new(tz_string)
|
26
|
+
check_scan(s, /([^-+,\d<][^-+,\d]*) | <([^>]+)>/x)
|
27
|
+
std_abbrev = s[1] || s[2]
|
28
|
+
check_scan(s, /([-+]?\d+)(?::(\d+)(?::(\d+))?)?/)
|
29
|
+
std_offset = get_offset_from_hms(s[1], s[2], s[3])
|
30
|
+
|
31
|
+
if s.scan(/([^-+,\d<][^-+,\d]*) | <([^>]+)>/x)
|
32
|
+
dst_abbrev = s[1] || s[2]
|
33
|
+
|
34
|
+
if s.scan(/([-+]?\d+)(?::(\d+)(?::(\d+))?)?/)
|
35
|
+
dst_offset = get_offset_from_hms(s[1], s[2], s[3])
|
36
|
+
else
|
37
|
+
# POSIX is negative for ahead of UTC.
|
38
|
+
dst_offset = std_offset - 3600
|
39
|
+
end
|
40
|
+
|
41
|
+
dst_difference = std_offset - dst_offset
|
42
|
+
|
43
|
+
start_rule = parse_rule(s, 'start')
|
44
|
+
end_rule = parse_rule(s, 'end')
|
45
|
+
|
46
|
+
raise InvalidPosixTimeZone, "Expected the end of a POSIX-style time zone string but found '#{s.rest}'." if s.rest?
|
47
|
+
|
48
|
+
if start_rule.is_always_first_day_of_year? && start_rule.transition_at == 0 &&
|
49
|
+
end_rule.is_always_last_day_of_year? && end_rule.transition_at == 86400 + dst_difference
|
50
|
+
# Constant daylight savings time.
|
51
|
+
# POSIX is negative for ahead of UTC.
|
52
|
+
TimezoneOffset.new(-std_offset, dst_difference, dst_abbrev.to_sym)
|
53
|
+
else
|
54
|
+
AnnualRules.new(
|
55
|
+
TimezoneOffset.new(-std_offset, 0, std_abbrev.to_sym),
|
56
|
+
TimezoneOffset.new(-std_offset, dst_difference, dst_abbrev.to_sym),
|
57
|
+
start_rule,
|
58
|
+
end_rule)
|
59
|
+
end
|
60
|
+
elsif !s.rest?
|
61
|
+
# Constant standard time.
|
62
|
+
# POSIX is negative for ahead of UTC.
|
63
|
+
TimezoneOffset.new(-std_offset, 0, std_abbrev.to_sym)
|
64
|
+
else
|
65
|
+
raise InvalidPosixTimeZone, "Expected the end of a POSIX-style time zone string but found '#{s.rest}'."
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Parses the rule from the TZ string, returning a TransitionRule.
|
72
|
+
def parse_rule(s, type)
|
73
|
+
check_scan(s, /,(?: (?: J(\d+) ) | (\d+) | (?: M(\d+)\.(\d)\.(\d) ) )/x)
|
74
|
+
julian_day_of_year = s[1]
|
75
|
+
absolute_day_of_year = s[2]
|
76
|
+
month = s[3]
|
77
|
+
week = s[4]
|
78
|
+
day_of_week = s[5]
|
79
|
+
|
80
|
+
if s.scan(/\//)
|
81
|
+
check_scan(s, /([-+]?\d+)(?::(\d+)(?::(\d+))?)?/)
|
82
|
+
transition_at = get_seconds_after_midnight_from_hms(s[1], s[2], s[3])
|
83
|
+
else
|
84
|
+
transition_at = 7200
|
85
|
+
end
|
86
|
+
|
87
|
+
begin
|
88
|
+
if julian_day_of_year
|
89
|
+
JulianDayOfYearTransitionRule.new(julian_day_of_year.to_i, transition_at)
|
90
|
+
elsif absolute_day_of_year
|
91
|
+
AbsoluteDayOfYearTransitionRule.new(absolute_day_of_year.to_i, transition_at)
|
92
|
+
elsif week == '5'
|
93
|
+
LastDayOfMonthTransitionRule.new(month.to_i, day_of_week.to_i, transition_at)
|
94
|
+
else
|
95
|
+
DayOfMonthTransitionRule.new(month.to_i, week.to_i, day_of_week.to_i, transition_at)
|
96
|
+
end
|
97
|
+
rescue ArgumentError => e
|
98
|
+
raise InvalidPosixTimeZone, "Invalid #{type} rule in POSIX-style time zone string: #{e}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns an offset in seconds from hh:mm:ss values. The value can be
|
103
|
+
# negative. -02:33:12 would represent 2 hours, 33 minutes and 12 seconds
|
104
|
+
# ahead of UTC.
|
105
|
+
def get_offset_from_hms(h, m, s)
|
106
|
+
h = h.to_i
|
107
|
+
m = m.to_i
|
108
|
+
s = s.to_i
|
109
|
+
raise InvalidPosixTimeZone, "Invalid minute #{m} in offset for POSIX-style time zone string." if m > 59
|
110
|
+
raise InvalidPosixTimeZone, "Invalid second #{s} in offset for POSIX-style time zone string." if s > 59
|
111
|
+
magnitude = (h.abs * 60 + m) * 60 + s
|
112
|
+
h < 0 ? -magnitude : magnitude
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns the seconds from midnight from hh:mm:ss values. Hours can exceed
|
116
|
+
# 24 for a time on the following day. Hours can be negative to subtract
|
117
|
+
# hours from midnight on the given day. -02:33:12 represents 22:33:12 on
|
118
|
+
# the prior day.
|
119
|
+
def get_seconds_after_midnight_from_hms(h, m, s)
|
120
|
+
h = h.to_i
|
121
|
+
m = m.to_i
|
122
|
+
s = s.to_i
|
123
|
+
raise InvalidPosixTimeZone, "Invalid minute #{m} in time for POSIX-style time zone string." if m > 59
|
124
|
+
raise InvalidPosixTimeZone, "Invalid second #{s} in time for POSIX-style time zone string." if s > 59
|
125
|
+
(h * 3600) + m * 60 + s
|
126
|
+
end
|
127
|
+
|
128
|
+
# Scans for a pattern and raises an exception if the pattern does not
|
129
|
+
# match the input.
|
130
|
+
def check_scan(s, pattern)
|
131
|
+
result = s.scan(pattern)
|
132
|
+
raise InvalidPosixTimeZone, "Expected '#{s.rest}' to match #{pattern} in POSIX-style time zone string." unless result
|
133
|
+
result
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -160,6 +160,17 @@ module TZInfo
|
|
160
160
|
end
|
161
161
|
end
|
162
162
|
alias :day :mday
|
163
|
+
|
164
|
+
# Returns the day of the week (0..6 for Sunday to Saturday).
|
165
|
+
def wday
|
166
|
+
if @time
|
167
|
+
@time.wday
|
168
|
+
elsif @datetime
|
169
|
+
@datetime.wday
|
170
|
+
else
|
171
|
+
to_time.wday
|
172
|
+
end
|
173
|
+
end
|
163
174
|
|
164
175
|
# Returns the hour of the day (0..23).
|
165
176
|
def hour
|
@@ -0,0 +1,325 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module TZInfo
|
4
|
+
# Base class for rules definining the transition between standard and daylight
|
5
|
+
# savings time.
|
6
|
+
class TransitionRule #:nodoc:
|
7
|
+
# Returns the number of seconds after midnight local time on the day
|
8
|
+
# identified by the rule at which the transition occurs. Can be negative to
|
9
|
+
# denote a time on the prior day. Can be greater than or equal to 86,400 to
|
10
|
+
# denote a time of the following day.
|
11
|
+
attr_reader :transition_at
|
12
|
+
|
13
|
+
# Initializes a new TransitionRule.
|
14
|
+
def initialize(transition_at)
|
15
|
+
raise ArgumentError, 'Invalid transition_at' unless transition_at.kind_of?(Integer)
|
16
|
+
@transition_at = transition_at
|
17
|
+
end
|
18
|
+
|
19
|
+
# Calculates the UTC time of the transition from a given offset on a given
|
20
|
+
# year.
|
21
|
+
def at(offset, year)
|
22
|
+
day = get_day(year)
|
23
|
+
day.add_with_convert(@transition_at - offset.utc_total_offset)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Determines if this TransitionRule is equal to another instance.
|
27
|
+
def ==(r)
|
28
|
+
r.kind_of?(TransitionRule) && @transition_at == r.transition_at
|
29
|
+
end
|
30
|
+
alias eql? ==
|
31
|
+
|
32
|
+
# Returns a hash based on hash_args (defaulting to transition_at).
|
33
|
+
def hash
|
34
|
+
hash_args.hash
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
# Returns an Array of parameters that will influence the output of hash.
|
40
|
+
def hash_args
|
41
|
+
[@transition_at]
|
42
|
+
end
|
43
|
+
|
44
|
+
def new_time_or_datetime(year, month = 1, day = 1)
|
45
|
+
result = if ((year >= 2039 || (year == 2038 && (month >= 2 || (month == 1 && day >= 20)))) && !RubyCoreSupport.time_supports_64bit) ||
|
46
|
+
(year < 1970 && !RubyCoreSupport.time_supports_negative)
|
47
|
+
|
48
|
+
# Time handles 29 February on a non-leap year as 1 March.
|
49
|
+
# DateTime rejects. Advance manually.
|
50
|
+
if month == 2 && day == 29 && !Date.gregorian_leap?(year)
|
51
|
+
month = 3
|
52
|
+
day = 1
|
53
|
+
end
|
54
|
+
|
55
|
+
RubyCoreSupport.datetime_new(year, month, day)
|
56
|
+
else
|
57
|
+
Time.utc(year, month, day)
|
58
|
+
end
|
59
|
+
|
60
|
+
TimeOrDateTime.wrap(result)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# A base class for transition rules that activate based on an integer day of
|
65
|
+
# the year.
|
66
|
+
#
|
67
|
+
# @private
|
68
|
+
class DayOfYearTransitionRule < TransitionRule #:nodoc:
|
69
|
+
# Initializes a new DayOfYearTransitionRule.
|
70
|
+
def initialize(day, transition_at)
|
71
|
+
super(transition_at)
|
72
|
+
raise ArgumentError, 'Invalid day' unless day.kind_of?(Integer)
|
73
|
+
@seconds = day * 86400
|
74
|
+
end
|
75
|
+
|
76
|
+
# Determines if this DayOfYearTransitionRule is equal to another instance.
|
77
|
+
def ==(r)
|
78
|
+
super(r) && r.kind_of?(DayOfYearTransitionRule) && @seconds == r.seconds
|
79
|
+
end
|
80
|
+
alias eql? ==
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
# @return [Integer] the day multipled by the number of seconds in a day.
|
85
|
+
attr_reader :seconds
|
86
|
+
|
87
|
+
# Returns an Array of parameters that will influence the output of hash.
|
88
|
+
def hash_args
|
89
|
+
[@seconds] + super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Defines transitions that occur on the zero-based nth day of the year.
|
94
|
+
#
|
95
|
+
# Day 0 is 1 January.
|
96
|
+
#
|
97
|
+
# Leap days are counted. Day 59 will be 29 February on a leap year and 1 March
|
98
|
+
# on a non-leap year. Day 365 will be 31 December on a leap year and 1 January
|
99
|
+
# the following year on a non-leap year.
|
100
|
+
#
|
101
|
+
# @private
|
102
|
+
class AbsoluteDayOfYearTransitionRule < DayOfYearTransitionRule #:nodoc:
|
103
|
+
# Initializes a new AbsoluteDayOfYearTransitionRule.
|
104
|
+
def initialize(day, transition_at = 0)
|
105
|
+
super(day, transition_at)
|
106
|
+
raise ArgumentError, 'Invalid day' unless day >= 0 && day <= 365
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns true if the day specified by this transition is the first in the
|
110
|
+
# year (a day number of 0), otherwise false.
|
111
|
+
def is_always_first_day_of_year?
|
112
|
+
seconds == 0
|
113
|
+
end
|
114
|
+
|
115
|
+
# @returns false.
|
116
|
+
def is_always_last_day_of_year?
|
117
|
+
false
|
118
|
+
end
|
119
|
+
|
120
|
+
# Determines if this AbsoluteDayOfYearTransitionRule is equal to another
|
121
|
+
# instance.
|
122
|
+
def ==(r)
|
123
|
+
super(r) && r.kind_of?(AbsoluteDayOfYearTransitionRule)
|
124
|
+
end
|
125
|
+
alias eql? ==
|
126
|
+
|
127
|
+
protected
|
128
|
+
|
129
|
+
# Returns a TimeOrDateTime representing midnight local time on the day
|
130
|
+
# specified by the rule for the given offset and year.
|
131
|
+
def get_day(year)
|
132
|
+
new_time_or_datetime(year).add_with_convert(seconds)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns an Array of parameters that will influence the output of hash.
|
136
|
+
def hash_args
|
137
|
+
[AbsoluteDayOfYearTransitionRule] + super
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Defines transitions that occur on the one-based nth Julian day of the year.
|
142
|
+
#
|
143
|
+
# Leap days are not counted. Day 1 is 1 January. Day 60 is always 1 March.
|
144
|
+
# Day 365 is always 31 December.
|
145
|
+
#
|
146
|
+
# @private
|
147
|
+
class JulianDayOfYearTransitionRule < DayOfYearTransitionRule #:nodoc:
|
148
|
+
# The 60 days in seconds.
|
149
|
+
LEAP = 60 * 86400
|
150
|
+
|
151
|
+
# The length of a non-leap year in seconds.
|
152
|
+
YEAR = 365 * 86400
|
153
|
+
|
154
|
+
# Initializes a new JulianDayOfYearTransitionRule.
|
155
|
+
def initialize(day, transition_at = 0)
|
156
|
+
super(day, transition_at)
|
157
|
+
raise ArgumentError, 'Invalid day' unless day >= 1 && day <= 365
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns true if the day specified by this transition is the first in the
|
161
|
+
# year (a day number of 1), otherwise false.
|
162
|
+
def is_always_first_day_of_year?
|
163
|
+
seconds == 86400
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns true if the day specified by this transition is the last in the
|
167
|
+
# year (a day number of 365), otherwise false.
|
168
|
+
def is_always_last_day_of_year?
|
169
|
+
seconds == YEAR
|
170
|
+
end
|
171
|
+
|
172
|
+
# Determines if this JulianDayOfYearTransitionRule is equal to another
|
173
|
+
# instance.
|
174
|
+
def ==(r)
|
175
|
+
super(r) && r.kind_of?(JulianDayOfYearTransitionRule)
|
176
|
+
end
|
177
|
+
alias eql? ==
|
178
|
+
|
179
|
+
protected
|
180
|
+
|
181
|
+
# Returns a TimeOrDateTime representing midnight local time on the day
|
182
|
+
# specified by the rule for the given offset and year.
|
183
|
+
def get_day(year)
|
184
|
+
# Returns 1 March on non-leap years.
|
185
|
+
leap = new_time_or_datetime(year, 2, 29)
|
186
|
+
diff = seconds - LEAP
|
187
|
+
diff += 86400 if diff >= 0 && leap.mday == 29
|
188
|
+
leap.add_with_convert(diff)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Returns an Array of parameters that will influence the output of hash.
|
192
|
+
def hash_args
|
193
|
+
[JulianDayOfYearTransitionRule] + super
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# A base class for rules that transition on a particular day of week of a
|
198
|
+
# given week (subclasses specify which week of the month).
|
199
|
+
#
|
200
|
+
# @private
|
201
|
+
class DayOfWeekTransitionRule < TransitionRule #:nodoc:
|
202
|
+
# Initializes a new DayOfWeekTransitionRule.
|
203
|
+
def initialize(month, day_of_week, transition_at)
|
204
|
+
super(transition_at)
|
205
|
+
raise ArgumentError, 'Invalid month' unless month.kind_of?(Integer) && month >= 1 && month <= 12
|
206
|
+
raise ArgumentError, 'Invalid day_of_week' unless day_of_week.kind_of?(Integer) && day_of_week >= 0 && day_of_week <= 6
|
207
|
+
@month = month
|
208
|
+
@day_of_week = day_of_week
|
209
|
+
end
|
210
|
+
|
211
|
+
# Returns false.
|
212
|
+
def is_always_first_day_of_year?
|
213
|
+
false
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns false.
|
217
|
+
def is_always_last_day_of_year?
|
218
|
+
false
|
219
|
+
end
|
220
|
+
|
221
|
+
# Determines if this DayOfWeekTransitionRule is equal to another instance.
|
222
|
+
def ==(r)
|
223
|
+
super(r) && r.kind_of?(DayOfWeekTransitionRule) && @month == r.month && @day_of_week == r.day_of_week
|
224
|
+
end
|
225
|
+
alias eql? ==
|
226
|
+
|
227
|
+
protected
|
228
|
+
|
229
|
+
# Returns the month of the year (1 to 12).
|
230
|
+
attr_reader :month
|
231
|
+
|
232
|
+
# Returns the day of the week (0 to 6 for Sunday to Monday).
|
233
|
+
attr_reader :day_of_week
|
234
|
+
|
235
|
+
# Returns an Array of parameters that will influence the output of hash.
|
236
|
+
def hash_args
|
237
|
+
[@month, @day_of_week] + super
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# A rule that transitions on the nth occurrence of a particular day of week
|
242
|
+
# of a calendar month.
|
243
|
+
#
|
244
|
+
# @private
|
245
|
+
class DayOfMonthTransitionRule < DayOfWeekTransitionRule #:nodoc:
|
246
|
+
# Initializes a new DayOfMonthTransitionRule.
|
247
|
+
def initialize(month, week, day_of_week, transition_at = 0)
|
248
|
+
super(month, day_of_week, transition_at)
|
249
|
+
raise ArgumentError, 'Invalid week' unless week.kind_of?(Integer) && week >= 1 && week <= 4
|
250
|
+
@offset_start = (week - 1) * 7 + 1
|
251
|
+
end
|
252
|
+
|
253
|
+
# Determines if this DayOfMonthTransitionRule is equal to another instance.
|
254
|
+
def ==(r)
|
255
|
+
super(r) && r.kind_of?(DayOfMonthTransitionRule) && @offset_start == r.offset_start
|
256
|
+
end
|
257
|
+
alias eql? ==
|
258
|
+
|
259
|
+
protected
|
260
|
+
|
261
|
+
# Returns the day the week starts on for a month starting on a Sunday.
|
262
|
+
attr_reader :offset_start
|
263
|
+
|
264
|
+
# Returns a TimeOrDateTime representing midnight local time on the day
|
265
|
+
# specified by the rule for the given offset and year.
|
266
|
+
def get_day(year)
|
267
|
+
candidate = new_time_or_datetime(year, month, @offset_start)
|
268
|
+
diff = day_of_week - candidate.wday
|
269
|
+
|
270
|
+
if diff < 0
|
271
|
+
candidate.add_with_convert((7 + diff) * 86400)
|
272
|
+
elsif diff > 0
|
273
|
+
candidate.add_with_convert(diff * 86400)
|
274
|
+
else
|
275
|
+
candidate
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Returns an Array of parameters that will influence the output of hash.
|
280
|
+
def hash_args
|
281
|
+
[@offset_start] + super
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# A rule that transitions on the last occurrence of a particular day of week
|
286
|
+
# of a calendar month.
|
287
|
+
#
|
288
|
+
# @private
|
289
|
+
class LastDayOfMonthTransitionRule < DayOfWeekTransitionRule #:nodoc:
|
290
|
+
# Initializes a new LastDayOfMonthTransitionRule.
|
291
|
+
def initialize(month, day_of_week, transition_at = 0)
|
292
|
+
super(month, day_of_week, transition_at)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Determines if this LastDayOfMonthTransitionRule is equal to another
|
296
|
+
# instance.
|
297
|
+
def ==(r)
|
298
|
+
super(r) && r.kind_of?(LastDayOfMonthTransitionRule)
|
299
|
+
end
|
300
|
+
alias eql? ==
|
301
|
+
|
302
|
+
protected
|
303
|
+
|
304
|
+
# Returns a TimeOrDateTime representing midnight local time on the day
|
305
|
+
# specified by the rule for the given offset and year.
|
306
|
+
def get_day(year)
|
307
|
+
next_month = month + 1
|
308
|
+
if next_month == 13
|
309
|
+
year += 1
|
310
|
+
next_month = 1
|
311
|
+
end
|
312
|
+
|
313
|
+
candidate = new_time_or_datetime(year, next_month).add_with_convert(-86400)
|
314
|
+
diff = candidate.wday - day_of_week
|
315
|
+
|
316
|
+
if diff < 0
|
317
|
+
candidate - (diff + 7) * 86400
|
318
|
+
elsif diff > 0
|
319
|
+
candidate - diff * 86400
|
320
|
+
else
|
321
|
+
candidate
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|