tzinfo 1.0.1 → 1.1.0
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/.yardopts +6 -0
- data/{CHANGES → CHANGES.md} +121 -50
- data/{README → README.md} +48 -34
- data/Rakefile +45 -22
- data/lib/tzinfo.rb +7 -2
- data/lib/tzinfo/country.rb +23 -1
- data/lib/tzinfo/country_index_definition.rb +6 -1
- data/lib/tzinfo/country_timezone.rb +9 -1
- data/lib/tzinfo/data_source.rb +26 -5
- data/lib/tzinfo/data_timezone.rb +30 -2
- data/lib/tzinfo/data_timezone_info.rb +26 -0
- data/lib/tzinfo/info_timezone.rb +3 -1
- data/lib/tzinfo/linked_timezone.rb +30 -1
- data/lib/tzinfo/offset_rationals.rb +5 -3
- data/lib/tzinfo/ruby_core_support.rb +2 -0
- data/lib/tzinfo/ruby_country_info.rb +14 -0
- data/lib/tzinfo/ruby_data_source.rb +5 -0
- data/lib/tzinfo/time_or_datetime.rb +16 -1
- data/lib/tzinfo/timezone.rb +107 -5
- data/lib/tzinfo/timezone_definition.rb +5 -1
- data/lib/tzinfo/timezone_index_definition.rb +7 -1
- data/lib/tzinfo/{timezone_offset_info.rb → timezone_offset.rb} +12 -10
- data/lib/tzinfo/timezone_period.rb +19 -15
- data/lib/tzinfo/timezone_proxy.rb +5 -1
- data/lib/tzinfo/timezone_transition.rb +136 -0
- data/lib/tzinfo/{timezone_transition_info.rb → timezone_transition_definition.rb} +21 -57
- data/lib/tzinfo/transition_data_timezone_info.rb +81 -8
- data/lib/tzinfo/zoneinfo_country_info.rb +7 -0
- data/lib/tzinfo/zoneinfo_data_source.rb +104 -57
- data/lib/tzinfo/zoneinfo_timezone_info.rb +18 -10
- data/test/tc_country.rb +39 -1
- data/test/tc_data_timezone.rb +28 -5
- data/test/tc_linked_timezone.rb +24 -3
- data/test/tc_ruby_data_source.rb +22 -2
- data/test/tc_time_or_datetime.rb +3 -3
- data/test/tc_timezone.rb +364 -117
- data/test/tc_timezone_london.rb +25 -0
- data/test/tc_timezone_melbourne.rb +24 -0
- data/test/tc_timezone_new_york.rb +24 -0
- data/test/{tc_timezone_offset_info.rb → tc_timezone_offset.rb} +27 -27
- data/test/tc_timezone_period.rb +113 -90
- data/test/tc_timezone_transition.rb +374 -0
- data/test/tc_timezone_transition_definition.rb +306 -0
- data/test/tc_transition_data_timezone_info.rb +143 -43
- data/test/tc_zoneinfo_data_source.rb +69 -5
- data/test/tc_zoneinfo_timezone_info.rb +63 -20
- data/test/test_utils.rb +27 -7
- data/tzinfo.gemspec +21 -0
- metadata +61 -38
- metadata.gz.sig +3 -0
- data/test/tc_timezone_transition_info.rb +0 -471
- data/test/zoneinfo/UTC +0 -0
- data/test/zoneinfo/localtime +0 -0
@@ -22,6 +22,8 @@
|
|
22
22
|
|
23
23
|
module TZInfo
|
24
24
|
# Represents information about a country returned by ZoneinfoDataSource.
|
25
|
+
#
|
26
|
+
# @private
|
25
27
|
class ZoneinfoCountryInfo < CountryInfo #:nodoc:
|
26
28
|
# Constructs a new CountryInfo with an ISO 3166 country code, name and
|
27
29
|
# an array of CountryTimezones.
|
@@ -34,6 +36,11 @@ module TZInfo
|
|
34
36
|
# Returns a frozen array of all the zone identifiers for the country ordered
|
35
37
|
# geographically, most populous first.
|
36
38
|
def zone_identifiers
|
39
|
+
# Thread-safey: It is possible that the value of @zone_identifiers may be
|
40
|
+
# calculated multiple times in concurrently executing threads. It is not
|
41
|
+
# worth the overhead of locking to ensure that @zone_identifiers is only
|
42
|
+
# calculated once.
|
43
|
+
|
37
44
|
unless @zone_identifiers
|
38
45
|
@zone_identifiers = zones.collect {|zone| zone.identifier}.freeze
|
39
46
|
end
|
@@ -48,7 +48,52 @@ module TZInfo
|
|
48
48
|
# To load zoneinfo files from a particular directory, pass the directory to
|
49
49
|
# TZInfo::DataSource.set:
|
50
50
|
#
|
51
|
-
# TZInfo::DataSource.set(:zoneinfo, directory)
|
51
|
+
# TZInfo::DataSource.set(:zoneinfo, directory)
|
52
|
+
#
|
53
|
+
# Note that the platform used at runtime may limit the range of available
|
54
|
+
# transition data that can be loaded from zoneinfo files. There are two
|
55
|
+
# factors to consider:
|
56
|
+
#
|
57
|
+
# First of all, the zoneinfo support in TZInfo makes use of Ruby's Time class.
|
58
|
+
# On 32-bit builds of Ruby 1.8, the Time class only supports 32-bit
|
59
|
+
# timestamps. This means that only Times between 1901-12-13 20:45:52 and
|
60
|
+
# 2038-01-19 03:14:07 can be represented. Furthermore, certain platforms only
|
61
|
+
# allow for positive 32-bit timestamps (notably Windows), making the earliest
|
62
|
+
# representable time 1970-01-01 00:00:00.
|
63
|
+
#
|
64
|
+
# 64-bit builds of Ruby 1.8 and all builds of Ruby 1.9 support 64-bit
|
65
|
+
# timestamps. This means that there is no practical restriction on the range
|
66
|
+
# of the Time class on these platforms.
|
67
|
+
#
|
68
|
+
# TZInfo will only load transitions that fall within the supported range of
|
69
|
+
# the Time class. Any queries performed on times outside of this range may
|
70
|
+
# give inaccurate results.
|
71
|
+
#
|
72
|
+
# The second factor concerns the zoneinfo files. Versions of the 'zic' tool
|
73
|
+
# (used to build zoneinfo files) that were released prior to February 2006
|
74
|
+
# created zoneinfo files that used 32-bit integers for transition timestamps.
|
75
|
+
# Later versions of zic produce zoneinfo files that use 64-bit integers. If
|
76
|
+
# you have 32-bit zoneinfo files on your system, then any queries falling
|
77
|
+
# outside of the range 1901-12-13 20:45:52 to 2038-01-19 03:14:07 may be
|
78
|
+
# inaccurate.
|
79
|
+
#
|
80
|
+
# Most modern platforms include 64-bit zoneinfo files. However, Mac OS X (up
|
81
|
+
# to at least 10.8.4) still uses 32-bit zoneinfo files.
|
82
|
+
#
|
83
|
+
# To check whether your zoneinfo files contain 32-bit or 64-bit transition
|
84
|
+
# data, you can run the following code (substituting the identifier of the
|
85
|
+
# zone you want to test for zone_identifier):
|
86
|
+
#
|
87
|
+
# TZInfo::DataSource.set(:zoneinfo)
|
88
|
+
# dir = TZInfo::DataSource.get.zoneinfo_dir
|
89
|
+
# File.open(File.join(dir, zone_identifier), 'r') {|f| f.read(5) }
|
90
|
+
#
|
91
|
+
# If the last line returns "TZif\\x00", then you have a 32-bit zoneinfo file.
|
92
|
+
# If it returns "TZif2" or "TZif3" then you have a 64-bit zoneinfo file.
|
93
|
+
#
|
94
|
+
# If you require support for 64-bit transitions, but are restricted to 32-bit
|
95
|
+
# zoneinfo support, then you may want to consider using TZInfo::RubyDataSource
|
96
|
+
# instead.
|
52
97
|
class ZoneinfoDataSource < DataSource
|
53
98
|
# The default value of ZoneInfoDataSource.search_path.
|
54
99
|
DEFAULT_SEARCH_PATH = ['/usr/share/zoneinfo', '/usr/share/lib/zoneinfo', '/etc/zoneinfo'].freeze
|
@@ -118,19 +163,23 @@ module TZInfo
|
|
118
163
|
|
119
164
|
@zoneinfo_dir = File.expand_path(@zoneinfo_dir).freeze
|
120
165
|
@zoneinfo_prefix = (@zoneinfo_dir + File::SEPARATOR).freeze
|
166
|
+
@timezone_index = load_timezone_index.freeze
|
167
|
+
@country_index = load_country_index.freeze
|
121
168
|
end
|
122
169
|
|
123
170
|
# Returns a TimezoneInfo instance for a given identifier.
|
124
171
|
# Raises InvalidTimezoneIdentifier if the timezone is not found or the
|
125
172
|
# identifier is invalid.
|
126
173
|
def load_timezone_info(identifier)
|
127
|
-
load_timezone_index
|
128
|
-
|
129
174
|
begin
|
130
175
|
if @timezone_index.include?(identifier)
|
131
|
-
identifier.untaint
|
132
176
|
path = File.join(@zoneinfo_dir, identifier)
|
133
177
|
|
178
|
+
# Untaint path rather than identifier. We don't want to modify
|
179
|
+
# identifier. identifier may also be frozen and therefore cannot be
|
180
|
+
# untainted.
|
181
|
+
path.untaint
|
182
|
+
|
134
183
|
begin
|
135
184
|
ZoneinfoTimezoneInfo.new(identifier, path)
|
136
185
|
rescue InvalidZoneinfoFile => e
|
@@ -148,7 +197,6 @@ module TZInfo
|
|
148
197
|
|
149
198
|
# Returns an array of all the available timezone identifiers.
|
150
199
|
def timezone_identifiers
|
151
|
-
load_timezone_index
|
152
200
|
@timezone_index
|
153
201
|
end
|
154
202
|
|
@@ -158,7 +206,6 @@ module TZInfo
|
|
158
206
|
# For ZoneinfoDataSource, this will always be identical to
|
159
207
|
# timezone_identifers.
|
160
208
|
def data_timezone_identifiers
|
161
|
-
load_timezone_index
|
162
209
|
@timezone_index
|
163
210
|
end
|
164
211
|
|
@@ -174,7 +221,6 @@ module TZInfo
|
|
174
221
|
# country code. Raises InvalidCountryCode if the country could not be found
|
175
222
|
# or the code is invalid.
|
176
223
|
def load_country_info(code)
|
177
|
-
load_country_index
|
178
224
|
info = @country_index[code]
|
179
225
|
raise InvalidCountryCode.new, 'Invalid country code' unless info
|
180
226
|
info
|
@@ -183,7 +229,6 @@ module TZInfo
|
|
183
229
|
# Returns an array of all the available ISO 3166-1 alpha-2
|
184
230
|
# country codes.
|
185
231
|
def country_codes
|
186
|
-
load_country_index
|
187
232
|
@country_index.keys.freeze
|
188
233
|
end
|
189
234
|
|
@@ -205,18 +250,22 @@ module TZInfo
|
|
205
250
|
File.directory?(path) && File.file?(File.join(path, 'zone.tab')) && File.file?(File.join(path, 'iso3166.tab'))
|
206
251
|
end
|
207
252
|
|
208
|
-
#
|
209
|
-
#
|
253
|
+
# Scans @zoneinfo_dir and returns an Array of available timezone
|
254
|
+
# identifiers.
|
210
255
|
def load_timezone_index
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
256
|
+
index = []
|
257
|
+
|
258
|
+
# Ignoring particular files:
|
259
|
+
# +VERSION is included in Mac OS X.
|
260
|
+
# localtime current local timezone (may be a link).
|
261
|
+
# posix, posixrules and right are directories containing other versions of the zoneinfo files.
|
262
|
+
# Factory is the compiled in default timezone.
|
263
|
+
|
264
|
+
enum_timezones(nil, ['+VERSION', 'localtime', 'posix', 'posixrules', 'right', 'Factory']) do |identifier|
|
265
|
+
index << identifier
|
219
266
|
end
|
267
|
+
|
268
|
+
index.sort
|
220
269
|
end
|
221
270
|
|
222
271
|
# Recursively scans a directory of timezones, calling the passed in block
|
@@ -237,53 +286,51 @@ module TZInfo
|
|
237
286
|
end
|
238
287
|
end
|
239
288
|
|
240
|
-
#
|
241
|
-
#
|
289
|
+
# Uses the iso3166.tab and zone.tab files to build an index of the
|
290
|
+
# available countries and their timezones.
|
242
291
|
def load_country_index
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
292
|
+
zones = {}
|
293
|
+
|
294
|
+
File.open(File.join(@zoneinfo_dir, 'zone.tab')) do |file|
|
295
|
+
file.each_line do |line|
|
296
|
+
line.chomp!
|
297
|
+
|
298
|
+
if line =~ /\A([A-Z]{2})\t(?:([+\-])(\d{2})(\d{2})([+\-])(\d{3})(\d{2})|([+\-])(\d{2})(\d{2})(\d{2})([+\-])(\d{3})(\d{2})(\d{2}))\t([^\t]+)(?:\t([^\t]+))?\z/
|
299
|
+
code = $1
|
249
300
|
|
250
|
-
if
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
else
|
257
|
-
latitude = dms_to_rational($8, $9, $10, $11)
|
258
|
-
longitude = dms_to_rational($12, $13, $14, $15)
|
259
|
-
end
|
260
|
-
|
261
|
-
zone_identifier = $16
|
262
|
-
description = $17
|
263
|
-
|
264
|
-
(zones[code] ||= []) <<
|
265
|
-
CountryTimezone.new(zone_identifier, latitude.numerator, latitude.denominator,
|
266
|
-
longitude.numerator, longitude.denominator, description)
|
301
|
+
if $2
|
302
|
+
latitude = dms_to_rational($2, $3, $4)
|
303
|
+
longitude = dms_to_rational($5, $6, $7)
|
304
|
+
else
|
305
|
+
latitude = dms_to_rational($8, $9, $10, $11)
|
306
|
+
longitude = dms_to_rational($12, $13, $14, $15)
|
267
307
|
end
|
308
|
+
|
309
|
+
zone_identifier = $16
|
310
|
+
description = $17
|
311
|
+
|
312
|
+
(zones[code] ||= []) <<
|
313
|
+
CountryTimezone.new(zone_identifier, latitude.numerator, latitude.denominator,
|
314
|
+
longitude.numerator, longitude.denominator, description)
|
268
315
|
end
|
269
316
|
end
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
317
|
+
end
|
318
|
+
|
319
|
+
countries = {}
|
320
|
+
|
321
|
+
File.open(File.join(@zoneinfo_dir, 'iso3166.tab')) do |file|
|
322
|
+
file.each_line do |line|
|
323
|
+
line.chomp!
|
324
|
+
|
325
|
+
if line =~ /\A([A-Z]{2})\t(.+)\z/
|
326
|
+
code = $1
|
327
|
+
name = $2
|
328
|
+
countries[code] = ZoneinfoCountryInfo.new(code, name, zones[code] || [])
|
282
329
|
end
|
283
330
|
end
|
284
|
-
|
285
|
-
@country_index = countries
|
286
331
|
end
|
332
|
+
|
333
|
+
countries
|
287
334
|
end
|
288
335
|
|
289
336
|
# Converts degrees, miunutes and seconds to a Rational
|
@@ -27,6 +27,8 @@ module TZInfo
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# Represents a timezone defined by a compiled zoneinfo TZif (\0 or 2) file.
|
30
|
+
#
|
31
|
+
# @private
|
30
32
|
class ZoneinfoTimezoneInfo < TransitionDataTimezoneInfo #:nodoc:
|
31
33
|
|
32
34
|
# Constructs the new ZoneinfoTimezoneInfo with an identifier and path
|
@@ -103,23 +105,29 @@ module TZInfo
|
|
103
105
|
|
104
106
|
# Parses a zoneinfo file and intializes the DataTimezoneInfo structures.
|
105
107
|
def parse(file)
|
106
|
-
magic, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt =
|
107
|
-
check_read(file, 44).unpack('
|
108
|
+
magic, version, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt =
|
109
|
+
check_read(file, 44).unpack('a4 a x15 NNNNNN')
|
108
110
|
|
109
|
-
if magic
|
111
|
+
if magic != 'TZif'
|
112
|
+
raise InvalidZoneinfoFile, "The file '#{file.path}' does not start with the expected header."
|
113
|
+
end
|
114
|
+
|
115
|
+
if (version == '2' || version == '3') && RubyCoreSupport.time_supports_64bit
|
110
116
|
# Skip the first 32-bit section and read the header of the second 64-bit section
|
111
117
|
check_read(file, timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisgmtcnt + ttisstdcnt)
|
112
118
|
|
113
|
-
|
114
|
-
|
119
|
+
prev_version = version
|
120
|
+
|
121
|
+
magic, version, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt =
|
122
|
+
check_read(file, 44).unpack('a4 a x15 NNNNNN')
|
115
123
|
|
116
|
-
unless magic == '
|
117
|
-
raise InvalidZoneinfoFile, "
|
124
|
+
unless magic == 'TZif' && (version == prev_version)
|
125
|
+
raise InvalidZoneinfoFile, "The file '#{file.path}' contains an invalid 64-bit section header."
|
118
126
|
end
|
119
127
|
|
120
|
-
using_64bit = true
|
121
|
-
elsif
|
122
|
-
raise InvalidZoneinfoFile, "
|
128
|
+
using_64bit = true
|
129
|
+
elsif version != '3' && version != '2' && version != "\0"
|
130
|
+
raise InvalidZoneinfoFile, "The file '#{file.path}' contains a version of the zoneinfo format that is not currently supported."
|
123
131
|
else
|
124
132
|
using_64bit = false
|
125
133
|
end
|
data/test/tc_country.rb
CHANGED
@@ -27,7 +27,7 @@ include TZInfo
|
|
27
27
|
class TCCountry < Test::Unit::TestCase
|
28
28
|
def setup
|
29
29
|
@orig_data_source = DataSource.get
|
30
|
-
Country.send :
|
30
|
+
Country.send :init_countries
|
31
31
|
end
|
32
32
|
|
33
33
|
def teardown
|
@@ -64,6 +64,44 @@ class TCCountry < Test::Unit::TestCase
|
|
64
64
|
Country.get('gb')
|
65
65
|
}
|
66
66
|
end
|
67
|
+
|
68
|
+
def test_get_tainted_loaded
|
69
|
+
Country.get('GB')
|
70
|
+
|
71
|
+
safe_test do
|
72
|
+
code = 'GB'.taint
|
73
|
+
assert(code.tainted?)
|
74
|
+
country = Country.get(code)
|
75
|
+
assert_equal('GB', country.code)
|
76
|
+
assert(code.tainted?)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_get_tainted_and_frozen_loaded
|
81
|
+
Country.get('GB')
|
82
|
+
|
83
|
+
safe_test do
|
84
|
+
country = Country.get('GB'.taint.freeze)
|
85
|
+
assert_equal('GB', country.code)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_get_tainted_not_previously_loaded
|
90
|
+
safe_test do
|
91
|
+
code = 'GB'.taint
|
92
|
+
assert(code.tainted?)
|
93
|
+
country = Country.get(code)
|
94
|
+
assert_equal('GB', country.code)
|
95
|
+
assert(code.tainted?)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_get_tainted_and_frozen_not_previously_loaded
|
100
|
+
safe_test do
|
101
|
+
country = Country.get('GB'.taint.freeze)
|
102
|
+
assert_equal('GB', country.code)
|
103
|
+
end
|
104
|
+
end
|
67
105
|
|
68
106
|
def test_new_nil
|
69
107
|
assert_raises(InvalidCountryCode) {
|
data/test/tc_data_timezone.rb
CHANGED
@@ -29,11 +29,14 @@ class TCDataTimezone < Test::Unit::TestCase
|
|
29
29
|
class TestTimezoneInfo < TimezoneInfo
|
30
30
|
attr_reader :utc
|
31
31
|
attr_reader :local
|
32
|
+
attr_reader :utc_to
|
33
|
+
attr_reader :utc_from
|
32
34
|
|
33
|
-
def initialize(identifier, utc_period, local_periods)
|
35
|
+
def initialize(identifier, utc_period, local_periods, transitions_up_to)
|
34
36
|
super(identifier)
|
35
37
|
@utc_period = utc_period
|
36
38
|
@local_periods = local_periods || []
|
39
|
+
@transitions_up_to = transitions_up_to
|
37
40
|
end
|
38
41
|
|
39
42
|
def period_for_utc(utc)
|
@@ -45,10 +48,16 @@ class TCDataTimezone < Test::Unit::TestCase
|
|
45
48
|
@local = local
|
46
49
|
@local_periods
|
47
50
|
end
|
51
|
+
|
52
|
+
def transitions_up_to(utc_to, utc_from = nil)
|
53
|
+
@utc_to = utc_to
|
54
|
+
@utc_from = utc_from
|
55
|
+
@transitions_up_to
|
56
|
+
end
|
48
57
|
end
|
49
58
|
|
50
59
|
def test_identifier
|
51
|
-
tz = DataTimezone.new(TestTimezoneInfo.new('Test/Zone', nil, []))
|
60
|
+
tz = DataTimezone.new(TestTimezoneInfo.new('Test/Zone', nil, [], []))
|
52
61
|
assert_equal('Test/Zone', tz.identifier)
|
53
62
|
end
|
54
63
|
|
@@ -56,7 +65,7 @@ class TCDataTimezone < Test::Unit::TestCase
|
|
56
65
|
# Don't need actual TimezonePeriods. DataTimezone isn't supposed to do
|
57
66
|
# anything with them apart from return them.
|
58
67
|
period = Object.new
|
59
|
-
tti = TestTimezoneInfo.new('Test/Zone', period, [])
|
68
|
+
tti = TestTimezoneInfo.new('Test/Zone', period, [], [])
|
60
69
|
tz = DataTimezone.new(tti)
|
61
70
|
|
62
71
|
t = Time.utc(2006, 6, 27, 22, 50, 12)
|
@@ -68,7 +77,7 @@ class TCDataTimezone < Test::Unit::TestCase
|
|
68
77
|
# Don't need actual TimezonePeriods. DataTimezone isn't supposed to do
|
69
78
|
# anything with them apart from return them.
|
70
79
|
periods = [Object.new, Object.new]
|
71
|
-
tti = TestTimezoneInfo.new('Test/Zone', nil, periods)
|
80
|
+
tti = TestTimezoneInfo.new('Test/Zone', nil, periods, [])
|
72
81
|
tz = DataTimezone.new(tti)
|
73
82
|
|
74
83
|
t = Time.utc(2006, 6, 27, 22, 50, 12)
|
@@ -78,11 +87,25 @@ class TCDataTimezone < Test::Unit::TestCase
|
|
78
87
|
|
79
88
|
def test_periods_for_local_not_found
|
80
89
|
periods = []
|
81
|
-
tti = TestTimezoneInfo.new('Test/Zone', nil, periods)
|
90
|
+
tti = TestTimezoneInfo.new('Test/Zone', nil, periods, [])
|
82
91
|
tz = DataTimezone.new(tti)
|
83
92
|
|
84
93
|
t = Time.utc(2006, 6, 27, 22, 50, 12)
|
85
94
|
assert_same(periods, tz.periods_for_local(t))
|
86
95
|
assert_same(t, tti.local)
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_transitions_up_to
|
99
|
+
# Don't need actual TimezoneTransition instances. DataTimezone isn't
|
100
|
+
# supposed to do anything with them apart from return them.
|
101
|
+
transitions = [Object.new, Object.new]
|
102
|
+
tti = TestTimezoneInfo.new('Test/Zone', nil, nil, transitions)
|
103
|
+
tz = DataTimezone.new(tti)
|
104
|
+
|
105
|
+
utc_to = Time.utc(2013, 1, 1, 0, 0, 0)
|
106
|
+
utc_from = Time.utc(2012, 1, 1, 0, 0, 0)
|
107
|
+
assert_same(transitions, tz.transitions_up_to(utc_to, utc_from))
|
108
|
+
assert_same(utc_to, tti.utc_to)
|
109
|
+
assert_same(utc_from, tti.utc_from)
|
87
110
|
end
|
88
111
|
end
|
data/test/tc_linked_timezone.rb
CHANGED
@@ -29,8 +29,11 @@ class TCLinkedTimezone < Test::Unit::TestCase
|
|
29
29
|
class TestTimezone < Timezone
|
30
30
|
attr_reader :utc_period
|
31
31
|
attr_reader :local_periods
|
32
|
+
attr_reader :up_to_transitions
|
32
33
|
attr_reader :utc
|
33
|
-
attr_reader :local
|
34
|
+
attr_reader :local
|
35
|
+
attr_reader :utc_to
|
36
|
+
attr_reader :utc_from
|
34
37
|
|
35
38
|
def self.new(identifier, no_local_periods = false)
|
36
39
|
tz = super()
|
@@ -53,14 +56,22 @@ class TCLinkedTimezone < Test::Unit::TestCase
|
|
53
56
|
@local_periods
|
54
57
|
end
|
55
58
|
|
59
|
+
def transitions_up_to(utc_to, utc_from = nil)
|
60
|
+
@utc_to = utc_to
|
61
|
+
@utc_from = utc_from
|
62
|
+
@up_to_transitions
|
63
|
+
end
|
64
|
+
|
56
65
|
private
|
57
66
|
def setup(identifier, no_local_periods)
|
58
67
|
@identifier = identifier
|
59
68
|
@no_local_periods = no_local_periods
|
60
69
|
|
61
|
-
#
|
70
|
+
# Don't have to be real TimezonePeriod or TimezoneTransition objects
|
71
|
+
# (nothing will use them).
|
62
72
|
@utc_period = Object.new
|
63
73
|
@local_periods = [Object.new, Object.new]
|
74
|
+
@up_to_transitions = [Object.new, Object.new]
|
64
75
|
end
|
65
76
|
end
|
66
77
|
|
@@ -117,5 +128,15 @@ class TCLinkedTimezone < Test::Unit::TestCase
|
|
117
128
|
t = Time.utc(2006, 6, 27, 23, 12, 28)
|
118
129
|
assert_raises(PeriodNotFound) { tz.periods_for_local(t) }
|
119
130
|
assert_same(t, linked_tz.local)
|
120
|
-
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_transitions_up_to
|
134
|
+
tz = LinkedTimezone.new(LinkedTimezoneInfo.new('Test/Zone', 'Test/Linked'))
|
135
|
+
linked_tz = Timezone.get('Test/Linked')
|
136
|
+
utc_to = Time.utc(2013, 1, 1, 0, 0, 0)
|
137
|
+
utc_from = Time.utc(2012, 1, 1, 0, 0, 0)
|
138
|
+
assert_same(linked_tz.up_to_transitions, tz.transitions_up_to(utc_to, utc_from))
|
139
|
+
assert_same(utc_to, linked_tz.utc_to)
|
140
|
+
assert_same(utc_from, linked_tz.utc_from)
|
141
|
+
end
|
121
142
|
end
|