tzinfo 1.2.7 → 1.2.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -3
- data/CHANGES.md +30 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/lib/tzinfo/annual_rules.rb +51 -0
- data/lib/tzinfo/posix_time_zone_parser.rb +136 -0
- data/lib/tzinfo/ruby_data_source.rb +1 -1
- data/lib/tzinfo/time_or_datetime.rb +11 -0
- data/lib/tzinfo/transition_rule.rb +325 -0
- data/lib/tzinfo/zoneinfo_data_source.rb +27 -11
- data/lib/tzinfo/zoneinfo_timezone_info.rb +260 -40
- data/lib/tzinfo.rb +3 -0
- data/test/assets/payload.rb +1 -0
- data/test/tc_annual_rules.rb +95 -0
- data/test/tc_posix_time_zone_parser.rb +261 -0
- data/test/tc_ruby_data_source.rb +7 -1
- data/test/tc_time_or_datetime.rb +14 -0
- data/test/tc_timezone.rb +1 -1
- data/test/tc_transition_rule.rb +663 -0
- data/test/tc_zoneinfo_data_source.rb +20 -1
- data/test/tc_zoneinfo_timezone_info.rb +1030 -113
- data/test/test_utils.rb +16 -0
- 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
- data.tar.gz.sig +0 -0
- metadata +10 -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: e6364432a0aef34ccf3b6b1ecad65dd6f7f13843ac503cbcea1f693b74c96b46
|
4
|
+
data.tar.gz: 825fd6905101f51fa700dfa682490851952de8a692c03954d12f38944f8814c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef4b1b6a189bbf011294210d2e0651f41bc82e1db8fe342c9f8dbcefd473e8b49b9affa67bc9a395a5831b376db8d37b5942cfade1dacf5485f23ce3d6f78a46
|
7
|
+
data.tar.gz: 2871fbd7aded391c88a74724138073675690710dfca6adbbbe610ec4395e8d6631fad93b22d684650d04d9affeed0ab64a1d7489f766eb9ab1996556329c6ddc
|
checksums.yaml.gz.sig
CHANGED
@@ -1,3 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
ѿs!�BS���H��3�yD��q�X�g�
|
1
|
+
Y�1]Q�ti�t���mPo���ڊ -O��D�cu���[<��oͽc5�}�x`��[^�J?7��s����+��ȶ��M�v��ǐ��9�e���l9J��ۑ�3?e�V~�E����6E����Eb�)��xdTk�^�BdAC?�����=Jcr�%�����l�~��)�aPʃ\�=[ݪ��{l��fFBݦ��]�_����<v�S��0��7�z|�-�$���ؑ����^ڐ��(�ReaK�s�|�c��d
|
2
|
+
�K�W��
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
Version 1.2.10 - 19-Jul-2022
|
2
|
+
----------------------------
|
3
|
+
|
4
|
+
* Fixed a relative path traversal bug that could cause arbitrary files to be
|
5
|
+
loaded with require when used with RubyDataSource. Please refer to
|
6
|
+
https://github.com/tzinfo/tzinfo/security/advisories/GHSA-5cm2-9h8c-rvfx for
|
7
|
+
details. CVE-2022-31163.
|
8
|
+
* Ignore the SECURITY file from Arch Linux's tzdata package. #134.
|
9
|
+
|
10
|
+
|
11
|
+
Version 1.2.9 - 16-Dec-2020
|
12
|
+
---------------------------
|
13
|
+
|
14
|
+
* Fixed an incorrect InvalidTimezoneIdentifier exception raised when loading a
|
15
|
+
zoneinfo file that includes rules specifying an additional transition to the
|
16
|
+
final defined offset (for example, Africa/Casablanca in version 2018e of the
|
17
|
+
Time Zone Database). #123.
|
18
|
+
|
19
|
+
|
20
|
+
Version 1.2.8 - 8-Nov-2020
|
21
|
+
--------------------------
|
22
|
+
|
23
|
+
* Added support for handling "slim" format zoneinfo files that are produced by
|
24
|
+
default by zic version 2020b and later. The POSIX-style TZ string is now used
|
25
|
+
calculate DST transition times after the final defined transition in the file.
|
26
|
+
The 64-bit section is now always used regardless of whether Time has support
|
27
|
+
for 64-bit times. #120.
|
28
|
+
* Rubinius is no longer supported.
|
29
|
+
|
30
|
+
|
1
31
|
Version 1.2.7 - 2-Apr-2020
|
2
32
|
--------------------------
|
3
33
|
|
data/LICENSE
CHANGED
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) [![
|
4
|
+
[![RubyGems](https://img.shields.io/gem/v/tzinfo?logo=rubygems&label=Gem)](https://rubygems.org/gems/tzinfo) [![Tests](https://github.com/tzinfo/tzinfo/workflows/Tests/badge.svg?branch=1.2&event=push)](https://github.com/tzinfo/tzinfo/actions?query=workflow%3ATests+branch%3A1.2+event%3Apush)
|
5
5
|
|
6
6
|
[TZInfo](https://tzinfo.github.io) provides daylight savings aware
|
7
7
|
transformations between times in different timezones.
|
@@ -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
|
@@ -38,7 +38,7 @@ module TZInfo
|
|
38
38
|
# Raises InvalidTimezoneIdentifier if the timezone is not found or the
|
39
39
|
# identifier is invalid.
|
40
40
|
def load_timezone_info(identifier)
|
41
|
-
raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~
|
41
|
+
raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~ /\A[A-Za-z0-9+\-_]+(\/[A-Za-z0-9+\-_]+)*\z/
|
42
42
|
|
43
43
|
identifier = identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__')
|
44
44
|
|
@@ -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
|
@@ -87,6 +87,29 @@ module TZInfo
|
|
87
87
|
# The default value of ZoneinfoDataSource.alternate_iso3166_tab_search_path.
|
88
88
|
DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH = ['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'].freeze
|
89
89
|
|
90
|
+
# File and directories in the top level zoneinfo directory that will be
|
91
|
+
# excluded from the list of available time zones:
|
92
|
+
#
|
93
|
+
# - +VERSION is included on Mac OS X.
|
94
|
+
# - leapseconds is a list of leap seconds.
|
95
|
+
# - localtime is the current local timezone (may be a link).
|
96
|
+
# - posix, posixrules and right are directories containing other versions
|
97
|
+
# of the zoneinfo files.
|
98
|
+
# - SECURITY is included in the Arch Linux tzdata package.
|
99
|
+
# - src is a directory containing the tzdata source included on Solaris.
|
100
|
+
# - timeconfig is a symlink included on Slackware.
|
101
|
+
EXCLUDED_FILENAMES = [
|
102
|
+
'+VERSION',
|
103
|
+
'leapseconds',
|
104
|
+
'localtime',
|
105
|
+
'posix',
|
106
|
+
'posixrules',
|
107
|
+
'right',
|
108
|
+
'SECURITY',
|
109
|
+
'src',
|
110
|
+
'timeconfig'
|
111
|
+
].freeze
|
112
|
+
|
90
113
|
# Paths to be checked to find the system zoneinfo directory.
|
91
114
|
@@search_path = DEFAULT_SEARCH_PATH.dup
|
92
115
|
|
@@ -192,6 +215,7 @@ module TZInfo
|
|
192
215
|
@zoneinfo_dir = File.expand_path(@zoneinfo_dir).freeze
|
193
216
|
@timezone_index = load_timezone_index.freeze
|
194
217
|
@country_index = load_country_index(iso3166_tab_path, zone_tab_path).freeze
|
218
|
+
@posix_tz_parser = PosixTimeZoneParser.new
|
195
219
|
end
|
196
220
|
|
197
221
|
# Returns a TimezoneInfo instance for a given identifier.
|
@@ -208,7 +232,7 @@ module TZInfo
|
|
208
232
|
path.untaint
|
209
233
|
|
210
234
|
begin
|
211
|
-
ZoneinfoTimezoneInfo.new(identifier, path)
|
235
|
+
ZoneinfoTimezoneInfo.new(identifier, path, @posix_tz_parser)
|
212
236
|
rescue InvalidZoneinfoFile => e
|
213
237
|
raise InvalidTimezoneIdentifier, e.message
|
214
238
|
end
|
@@ -351,16 +375,8 @@ module TZInfo
|
|
351
375
|
# identifiers.
|
352
376
|
def load_timezone_index
|
353
377
|
index = []
|
354
|
-
|
355
|
-
|
356
|
-
# +VERSION is included on Mac OS X.
|
357
|
-
# leapseconds is a list of leap seconds.
|
358
|
-
# localtime is the current local timezone (may be a link).
|
359
|
-
# posix, posixrules and right are directories containing other versions of the zoneinfo files.
|
360
|
-
# src is a directory containing the tzdata source included on Solaris.
|
361
|
-
# timeconfig is a symlink included on Slackware.
|
362
|
-
|
363
|
-
enum_timezones(nil, ['+VERSION', 'leapseconds', 'localtime', 'posix', 'posixrules', 'right', 'src', 'timeconfig']) do |identifier|
|
378
|
+
|
379
|
+
enum_timezones(nil, EXCLUDED_FILENAMES) do |identifier|
|
364
380
|
index << identifier
|
365
381
|
end
|
366
382
|
|