tzinfo 1.2.2 → 1.2.3

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.

@@ -47,7 +47,7 @@ class TCCountry < Minitest::Test
47
47
  Country.get('GB')
48
48
 
49
49
  safe_test do
50
- code = 'GB'.taint
50
+ code = 'GB'.dup.taint
51
51
  assert(code.tainted?)
52
52
  country = Country.get(code)
53
53
  assert_equal('GB', country.code)
@@ -59,14 +59,14 @@ class TCCountry < Minitest::Test
59
59
  Country.get('GB')
60
60
 
61
61
  safe_test do
62
- country = Country.get('GB'.taint.freeze)
62
+ country = Country.get('GB'.dup.taint.freeze)
63
63
  assert_equal('GB', country.code)
64
64
  end
65
65
  end
66
66
 
67
67
  def test_get_tainted_not_previously_loaded
68
68
  safe_test do
69
- code = 'GB'.taint
69
+ code = 'GB'.dup.taint
70
70
  assert(code.tainted?)
71
71
  country = Country.get(code)
72
72
  assert_equal('GB', country.code)
@@ -76,7 +76,7 @@ class TCCountry < Minitest::Test
76
76
 
77
77
  def test_get_tainted_and_frozen_not_previously_loaded
78
78
  safe_test do
79
- country = Country.get('GB'.taint.freeze)
79
+ country = Country.get('GB'.dup.taint.freeze)
80
80
  assert_equal('GB', country.code)
81
81
  end
82
82
  end
@@ -56,7 +56,7 @@ class TCRubyDataSource < Minitest::Test
56
56
 
57
57
  def test_load_timezone_info_tainted
58
58
  safe_test do
59
- identifier = 'Europe/Amsterdam'.taint
59
+ identifier = 'Europe/Amsterdam'.dup.taint
60
60
  assert(identifier.tainted?)
61
61
  info = @data_source.load_timezone_info(identifier)
62
62
  assert_equal('Europe/Amsterdam', info.identifier)
@@ -66,7 +66,7 @@ class TCRubyDataSource < Minitest::Test
66
66
 
67
67
  def test_load_timezone_info_tainted_and_frozen
68
68
  safe_test do
69
- info = @data_source.load_timezone_info('Europe/Amsterdam'.taint.freeze)
69
+ info = @data_source.load_timezone_info('Europe/Amsterdam'.dup.taint.freeze)
70
70
  assert_equal('Europe/Amsterdam', info.identifier)
71
71
  end
72
72
  end
@@ -120,7 +120,7 @@ class TCRubyDataSource < Minitest::Test
120
120
 
121
121
  def test_load_country_info_tainted
122
122
  safe_test do
123
- code = 'NL'.taint
123
+ code = 'NL'.dup.taint
124
124
  assert(code.tainted?)
125
125
  info = @data_source.load_country_info(code)
126
126
  assert_equal('NL', info.code)
@@ -130,7 +130,7 @@ class TCRubyDataSource < Minitest::Test
130
130
 
131
131
  def test_load_country_info_tainted_and_frozen
132
132
  safe_test do
133
- info = @data_source.load_country_info('NL'.taint.freeze)
133
+ info = @data_source.load_country_info('NL'.dup.taint.freeze)
134
134
  assert_equal('NL', info.code)
135
135
  end
136
136
  end
@@ -243,7 +243,7 @@ class TCTimezone < Minitest::Test
243
243
  Timezone.get('Europe/Andorra')
244
244
 
245
245
  safe_test do
246
- identifier = 'Europe/Andorra'.taint
246
+ identifier = 'Europe/Andorra'.dup.taint
247
247
  assert(identifier.tainted?)
248
248
  tz = Timezone.get(identifier)
249
249
  assert_equal('Europe/Andorra', tz.identifier)
@@ -255,14 +255,14 @@ class TCTimezone < Minitest::Test
255
255
  Timezone.get('Europe/Andorra')
256
256
 
257
257
  safe_test do
258
- tz = Timezone.get('Europe/Andorra'.taint.freeze)
258
+ tz = Timezone.get('Europe/Andorra'.dup.taint.freeze)
259
259
  assert_equal('Europe/Andorra', tz.identifier)
260
260
  end
261
261
  end
262
262
 
263
263
  def test_get_tainted_not_previously_loaded
264
264
  safe_test do
265
- identifier = 'Europe/Andorra'.taint
265
+ identifier = 'Europe/Andorra'.dup.taint
266
266
  assert(identifier.tainted?)
267
267
  tz = Timezone.get(identifier)
268
268
  assert_equal('Europe/Andorra', tz.identifier)
@@ -272,7 +272,7 @@ class TCTimezone < Minitest::Test
272
272
 
273
273
  def test_get_tainted_and_frozen_not_previously_loaded
274
274
  safe_test do
275
- tz = Timezone.get('Europe/Amsterdam'.taint.freeze)
275
+ tz = Timezone.get('Europe/Amsterdam'.dup.taint.freeze)
276
276
  assert_equal('Europe/Amsterdam', tz.identifier)
277
277
  end
278
278
  end
@@ -436,6 +436,13 @@ class TCTimezone < Minitest::Test
436
436
  assert_equal('UTC', TestTimezone.new('UTC').friendly_identifier)
437
437
  end
438
438
 
439
+ if defined?(Encoding)
440
+ def test_friendly_identifier_non_binary_encoding
441
+ refute_equal(Encoding::ASCII_8BIT, TestTimezone.new('Europe/Paris').friendly_identifier(true).encoding)
442
+ refute_equal(Encoding::ASCII_8BIT, TestTimezone.new('Europe/Paris').friendly_identifier(false).encoding)
443
+ end
444
+ end
445
+
439
446
  def test_to_s
440
447
  assert_equal('Europe - Paris', TestTimezone.new('Europe/Paris').to_s)
441
448
  assert_equal('America - Knox, Indiana', TestTimezone.new('America/Indiana/Knox').to_s)
@@ -1246,26 +1253,35 @@ class TCTimezone < Minitest::Test
1246
1253
 
1247
1254
  def test_strftime_datetime
1248
1255
  tz = Timezone.get('Europe/London')
1249
- assert_equal('23:12:02 BST', tz.strftime('%H:%M:%S %Z', DateTime.new(2006, 7, 15, 22, 12, 2)))
1250
- assert_equal('BST', tz.strftime('%Z', DateTime.new(2006, 7, 15, 22, 12, 2)))
1251
- assert_equal('%ZBST', tz.strftime('%%Z%Z', DateTime.new(2006, 7, 15, 22, 12, 2)))
1252
- assert_equal('BST BST', tz.strftime('%Z %Z', DateTime.new(2006, 7, 15, 22, 12, 2)))
1256
+ dt = DateTime.new(2006, 7, 15, 22, 12, 2)
1257
+ assert_equal('23:12:02 BST', tz.strftime('%H:%M:%S %Z', dt))
1258
+ assert_equal('BST', tz.strftime('%Z', dt))
1259
+ assert_equal('%ZBST', tz.strftime('%%Z%Z', dt))
1260
+ assert_equal('BST BST', tz.strftime('%Z %Z', dt))
1261
+ assert_equal('BST %Z %BST %%Z %%BST', tz.strftime('%Z %%Z %%%Z %%%%Z %%%%%Z', dt))
1262
+ assert_equal('+0100 +01:00 +01:00:00 +01 %::::z', tz.strftime('%z %:z %::z %:::z %::::z', dt))
1253
1263
  end
1254
1264
 
1255
1265
  def test_strftime_time
1256
1266
  tz = Timezone.get('Europe/London')
1257
- assert_equal('23:12:02 BST', tz.strftime('%H:%M:%S %Z', Time.utc(2006, 7, 15, 22, 12, 2)))
1258
- assert_equal('BST', tz.strftime('%Z', Time.utc(2006, 7, 15, 22, 12, 2)))
1259
- assert_equal('%ZBST', tz.strftime('%%Z%Z', Time.utc(2006, 7, 15, 22, 12, 2)))
1260
- assert_equal('BST BST', tz.strftime('%Z %Z', Time.utc(2006, 7, 15, 22, 12, 2)))
1267
+ t = Time.utc(2006, 7, 15, 22, 12, 2)
1268
+ assert_equal('23:12:02 BST', tz.strftime('%H:%M:%S %Z', t))
1269
+ assert_equal('BST', tz.strftime('%Z', t))
1270
+ assert_equal('%ZBST', tz.strftime('%%Z%Z', t))
1271
+ assert_equal('BST BST', tz.strftime('%Z %Z', t))
1272
+ assert_equal('BST %Z %BST %%Z %%BST', tz.strftime('%Z %%Z %%%Z %%%%Z %%%%%Z', t))
1273
+ assert_equal('+0100 +01:00 +01:00:00 +01 %::::z', tz.strftime('%z %:z %::z %:::z %::::z', t))
1261
1274
  end
1262
1275
 
1263
1276
  def test_strftime_int
1264
1277
  tz = Timezone.get('Europe/London')
1265
- assert_equal('23:12:02 BST', tz.strftime('%H:%M:%S %Z', Time.utc(2006, 7, 15, 22, 12, 2).to_i))
1266
- assert_equal('BST', tz.strftime('%Z', Time.utc(2006, 7, 15, 22, 12, 2).to_i))
1267
- assert_equal('%ZBST', tz.strftime('%%Z%Z', Time.utc(2006, 7, 15, 22, 12, 2).to_i))
1268
- assert_equal('BST BST', tz.strftime('%Z %Z', Time.utc(2006, 7, 15, 22, 12, 2).to_i))
1278
+ i = Time.utc(2006, 7, 15, 22, 12, 2).to_i
1279
+ assert_equal('23:12:02 BST', tz.strftime('%H:%M:%S %Z', i))
1280
+ assert_equal('BST', tz.strftime('%Z', i))
1281
+ assert_equal('%ZBST', tz.strftime('%%Z%Z', i))
1282
+ assert_equal('BST BST', tz.strftime('%Z %Z', i))
1283
+ assert_equal('BST %Z %BST %%Z %%BST', tz.strftime('%Z %%Z %%%Z %%%%Z %%%%%Z', i))
1284
+ assert_equal('+0100 +01:00 +01:00:00 +01 %::::z', tz.strftime('%z %:z %::z %:::z %::::z', i))
1269
1285
  end
1270
1286
 
1271
1287
  def test_get_missing_data_source
@@ -5,45 +5,59 @@ include TZInfo
5
5
  class TCTimezoneProxy < Minitest::Test
6
6
  def test_not_exist
7
7
  proxy = TimezoneProxy.new('Nothing/Special')
8
+ t = Time.utc(2006,1,1,0,0,0)
8
9
  assert_equal('Nothing/Special', proxy.identifier)
9
- assert_raises(InvalidTimezoneIdentifier) { proxy.now }
10
+ assert_equal('Nothing/Special', proxy.name)
11
+ assert_equal('Nothing - Special', proxy.friendly_identifier)
12
+ assert_equal('Nothing - Special', proxy.to_s)
13
+
14
+ assert_raises(InvalidTimezoneIdentifier) { proxy.canonical_identifier }
15
+ assert_raises(InvalidTimezoneIdentifier) { proxy.canonical_zone }
10
16
  assert_raises(InvalidTimezoneIdentifier) { proxy.current_period }
11
17
  assert_raises(InvalidTimezoneIdentifier) { proxy.current_period_and_time }
12
18
  assert_raises(InvalidTimezoneIdentifier) { proxy.current_time_and_period }
13
- assert_raises(InvalidTimezoneIdentifier) { proxy.utc_to_local(DateTime.new(2006,1,1,0,0,0)) }
14
- assert_raises(InvalidTimezoneIdentifier) { proxy.local_to_utc(DateTime.new(2006,1,1,0,0,0)) }
15
- assert_raises(InvalidTimezoneIdentifier) { proxy.period_for_utc(DateTime.new(2006,1,1,0,0,0)) }
16
- assert_raises(InvalidTimezoneIdentifier) { proxy.period_for_local(DateTime.new(2006,1,1,0,0,0)) }
17
- assert_raises(InvalidTimezoneIdentifier) { proxy.canonical_identifier }
18
- assert_raises(InvalidTimezoneIdentifier) { proxy.canonical_zone }
19
+ assert_raises(InvalidTimezoneIdentifier) { proxy.local_to_utc(t) }
20
+ assert_raises(InvalidTimezoneIdentifier) { proxy.now }
21
+ assert_raises(InvalidTimezoneIdentifier) { proxy.offsets_up_to(t) }
22
+ assert_raises(InvalidTimezoneIdentifier) { proxy.period_for_local(t) }
23
+ assert_raises(InvalidTimezoneIdentifier) { proxy.period_for_utc(t) }
24
+ assert_raises(InvalidTimezoneIdentifier) { proxy.periods_for_local(t) }
25
+ assert_raises(InvalidTimezoneIdentifier) { proxy.strftime('%Z', t) }
26
+ assert_raises(InvalidTimezoneIdentifier) { proxy.transitions_up_to(t) }
27
+ assert_raises(InvalidTimezoneIdentifier) { proxy.utc_to_local(t) }
19
28
  end
20
29
 
21
30
  def test_valid
22
31
  proxy = TimezoneProxy.new('Europe/London')
23
- assert_equal('Europe/London', proxy.identifier)
24
-
25
- assert_nothing_raised { proxy.now }
32
+ real = Timezone.get('Europe/London')
33
+
34
+ t1 = Time.utc(2005,8,1,0,0,0)
35
+ t2 = Time.utc(2004,8,1,0,0,0)
36
+
37
+ assert_equal(real.canonical_identifier, proxy.canonical_identifier)
38
+ assert_same(real.canonical_zone, proxy.canonical_zone)
26
39
  assert_nothing_raised { proxy.current_period }
27
40
  assert_nothing_raised { proxy.current_period_and_time }
28
41
  assert_nothing_raised { proxy.current_time_and_period }
29
-
30
- real = Timezone.get('Europe/London')
31
-
32
- assert_equal(real.utc_to_local(DateTime.new(2005,8,1,0,0,0)), proxy.utc_to_local(DateTime.new(2005,8,1,0,0,0)))
33
- assert_equal(real.local_to_utc(DateTime.new(2005,8,1,0,0,0)), proxy.local_to_utc(DateTime.new(2005,8,1,0,0,0)))
34
- assert_equal(real.period_for_utc(DateTime.new(2005,8,1,0,0,0)), proxy.period_for_utc(DateTime.new(2005,8,1,0,0,0)))
35
- assert_equal(real.period_for_local(DateTime.new(2005,8,1,0,0,0)), proxy.period_for_local(DateTime.new(2005,8,1,0,0,0)))
36
- assert_equal(real.identifier, proxy.identifier)
37
- assert_equal(real.name, proxy.name)
38
- assert_equal(real.to_s, proxy.to_s)
39
42
  assert_equal(real.friendly_identifier(true), proxy.friendly_identifier(true))
40
43
  assert_equal(real.friendly_identifier(false), proxy.friendly_identifier(false))
41
44
  assert_equal(real.friendly_identifier, proxy.friendly_identifier)
42
- assert_equal(real.canonical_identifier, proxy.canonical_identifier)
43
- assert_same(real.canonical_zone, proxy.canonical_zone)
44
-
45
- assert_equal('Europe/London', proxy.identifier)
46
-
45
+ assert_equal(real.identifier, proxy.identifier)
46
+ assert_equal(real.local_to_utc(t1), proxy.local_to_utc(t1))
47
+ assert_equal(real.name, proxy.name)
48
+ assert_nothing_raised { proxy.now }
49
+ assert_equal(real.offsets_up_to(t1), proxy.offsets_up_to(t1))
50
+ assert_equal(real.offsets_up_to(t1, t2), proxy.offsets_up_to(t1, t2))
51
+ assert_equal(real.period_for_local(t1), proxy.period_for_local(t1))
52
+ assert_equal(real.period_for_utc(t1), proxy.period_for_utc(t1))
53
+ assert_equal(real.periods_for_local(t1), proxy.periods_for_local(t1))
54
+ assert_equal(real.strftime('%Z', t1), proxy.strftime('%Z', t1))
55
+ assert_equal(real.to_s, proxy.to_s)
56
+ assert_equal(real.transitions_up_to(t1), proxy.transitions_up_to(t1))
57
+ assert_equal(real.transitions_up_to(t1, t2), proxy.transitions_up_to(t1, t2))
58
+ assert_equal(real.utc_to_local(t1), proxy.utc_to_local(t1))
59
+
60
+
47
61
  assert(real == proxy)
48
62
  assert(proxy == real)
49
63
  assert_equal(0, real <=> proxy)
@@ -395,6 +395,41 @@ class TCZoneinfoDataSource < Minitest::Test
395
395
  end
396
396
  end
397
397
 
398
+ def test_load_timezone_info_ignored_timeconfig_symlink
399
+ # Slackware includes a symlink named timeconfig that points at /usr/sbin/timeconfig.
400
+
401
+ Dir.mktmpdir('tzinfo_test_target') do |target_dir|
402
+ target_path = File.join(target_dir, 'timeconfig')
403
+
404
+ File.open(target_path, 'w') do |f|
405
+ f.write("#!/bin/sh\n")
406
+ f.write("#\n")
407
+ f.write('# timeconfig Slackware Linux timezone configuration utility.\n')
408
+ end
409
+
410
+ Dir.mktmpdir('tzinfo_test') do |dir|
411
+ FileUtils.touch(File.join(dir, 'zone.tab'))
412
+ FileUtils.touch(File.join(dir, 'iso3166.tab'))
413
+ FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(dir, 'EST'))
414
+
415
+ symlink_path = File.join(dir, 'timeconfig')
416
+ begin
417
+ FileUtils.ln_s(target_path, symlink_path)
418
+ rescue NotImplementedError, Errno::EACCES
419
+ # Symlinks not supported on this platform, or permission denied
420
+ # (administrative rights are required on Windows). Copy instead.
421
+ FileUtils.cp(target_path, symlink_path)
422
+ end
423
+
424
+ data_source = ZoneinfoDataSource.new(dir)
425
+
426
+ assert_raises(InvalidTimezoneIdentifier) do
427
+ data_source.load_timezone_info('timeconfig')
428
+ end
429
+ end
430
+ end
431
+ end
432
+
398
433
  def test_load_timezone_info_nil
399
434
  assert_raises(InvalidTimezoneIdentifier) do
400
435
  @data_source.load_timezone_info(nil)
@@ -453,8 +488,9 @@ class TCZoneinfoDataSource < Minitest::Test
453
488
 
454
489
  begin
455
490
  FileUtils.ln_s(outside_file, file)
456
- rescue NotImplementedError
457
- # Symlinks not supported on this platform - skip test
491
+ rescue NotImplementedError, Errno::EACCES
492
+ # Symlinks not supported on this platform, or permission denied
493
+ # (administrative rights are required on Windows). Skip test.
458
494
  return
459
495
  end
460
496
 
@@ -478,8 +514,9 @@ class TCZoneinfoDataSource < Minitest::Test
478
514
 
479
515
  begin
480
516
  FileUtils.ln_s(File.join(File.expand_path(dir), 'EST'), link)
481
- rescue NotImplementedError
482
- # Symlinks not supported on this platform - skip test
517
+ rescue NotImplementedError, Errno::EACCES
518
+ # Symlinks not supported on this platform, or permission denied
519
+ # (administrative rights are required on Windows). Skip test.
483
520
  return
484
521
  end
485
522
 
@@ -505,8 +542,9 @@ class TCZoneinfoDataSource < Minitest::Test
505
542
 
506
543
  begin
507
544
  FileUtils.ln_s('../outside', link)
508
- rescue NotImplementedError
509
- # Symlinks not supported on this platform - skip test
545
+ rescue NotImplementedError, Errno::EACCES
546
+ # Symlinks not supported on this platform, or permission denied
547
+ # (administrative rights are required on Windows). Skip test.
510
548
  return
511
549
  end
512
550
 
@@ -541,8 +579,9 @@ class TCZoneinfoDataSource < Minitest::Test
541
579
  subdir_link = File.join(subdir, 'Link')
542
580
  begin
543
581
  FileUtils.ln_s('../Subdir/EST', subdir_link)
544
- rescue NotImplementedError
545
- # Symlinks not supported on this platform - skip test
582
+ rescue NotImplementedError, Errno::EACCES
583
+ # Symlinks not supported on this platform, or permission denied
584
+ # (administrative rights are required on Windows). Skip test.
546
585
  return
547
586
  end
548
587
 
@@ -613,7 +652,7 @@ class TCZoneinfoDataSource < Minitest::Test
613
652
 
614
653
  def test_load_timezone_info_tainted
615
654
  safe_test do
616
- identifier = 'Europe/Amsterdam'.taint
655
+ identifier = 'Europe/Amsterdam'.dup.taint
617
656
  assert(identifier.tainted?)
618
657
  info = @data_source.load_timezone_info(identifier)
619
658
  assert_equal('Europe/Amsterdam', info.identifier)
@@ -623,7 +662,7 @@ class TCZoneinfoDataSource < Minitest::Test
623
662
 
624
663
  def test_load_timezone_info_tainted_and_frozen
625
664
  safe_test do
626
- info = @data_source.load_timezone_info('Europe/Amsterdam'.taint.freeze)
665
+ info = @data_source.load_timezone_info('Europe/Amsterdam'.dup.taint.freeze)
627
666
  assert_equal('Europe/Amsterdam', info.identifier)
628
667
  end
629
668
  end
@@ -653,11 +692,11 @@ class TCZoneinfoDataSource < Minitest::Test
653
692
 
654
693
  entries = entries.collect {|file| file[directory.length + File::SEPARATOR.length, file.length - directory.length - File::SEPARATOR.length]}
655
694
 
656
- # Exclude right (with leapseconds) and posix (copy) directories; .tab files; localtime, posixrules and Factory zones
695
+ # Exclude right (with leapseconds) and posix (copy) directories; .tab files; localtime and posixrules files.
657
696
  entries = entries.select do |file|
658
697
  file !~ /\A(posix|right)\// &&
659
698
  file !~ /\.tab\z/ &&
660
- !%w(localtime posixrules Factory).include?(file)
699
+ !%w(localtime posixrules).include?(file)
661
700
  end
662
701
 
663
702
  entries.sort
@@ -714,6 +753,38 @@ class TCZoneinfoDataSource < Minitest::Test
714
753
  end
715
754
  end
716
755
 
756
+ def test_timezone_identifiers_ignored_timeconfig_symlink
757
+ # Slackware includes a symlink named timeconfig that points at /usr/sbin/timeconfig.
758
+
759
+ Dir.mktmpdir('tzinfo_test_target') do |target_dir|
760
+ target_path = File.join(target_dir, 'timeconfig')
761
+
762
+ File.open(target_path, 'w') do |f|
763
+ f.write("#!/bin/sh\n")
764
+ f.write("#\n")
765
+ f.write('# timeconfig Slackware Linux timezone configuration utility.\n')
766
+ end
767
+
768
+ Dir.mktmpdir('tzinfo_test') do |dir|
769
+ FileUtils.touch(File.join(dir, 'zone.tab'))
770
+ FileUtils.touch(File.join(dir, 'iso3166.tab'))
771
+ FileUtils.cp(File.join(@data_source.zoneinfo_dir, 'EST'), File.join(dir, 'EST'))
772
+
773
+ symlink_path = File.join(dir, 'timeconfig')
774
+ begin
775
+ FileUtils.ln_s(target_path, symlink_path)
776
+ rescue NotImplementedError, Errno::EACCES
777
+ # Symlinks not supported on this platform, or permission denied
778
+ # (administrative rights are required on Windows). Copy instead.
779
+ FileUtils.cp(target_path, symlink_path)
780
+ end
781
+
782
+ data_source = ZoneinfoDataSource.new(dir)
783
+ assert_array_same_items(['EST'], data_source.timezone_identifiers)
784
+ end
785
+ end
786
+ end
787
+
717
788
  def test_timezone_identifiers_ignored_src_directory
718
789
  # Solaris includes a src directory containing the source timezone data files
719
790
  # from the tzdata distribution. These should be ignored.
@@ -768,7 +839,7 @@ class TCZoneinfoDataSource < Minitest::Test
768
839
 
769
840
  def test_load_country_info_tainted
770
841
  safe_test do
771
- code = 'NL'.taint
842
+ code = 'NL'.dup.taint
772
843
  assert(code.tainted?)
773
844
  info = @data_source.load_country_info(code)
774
845
  assert_equal('NL', info.code)
@@ -778,7 +849,7 @@ class TCZoneinfoDataSource < Minitest::Test
778
849
 
779
850
  def test_load_country_info_tainted_and_frozen
780
851
  safe_test do
781
- info = @data_source.load_country_info('NL'.taint.freeze)
852
+ info = @data_source.load_country_info('NL'.dup.taint.freeze)
782
853
  assert_equal('NL', info.code)
783
854
  end
784
855
  end
@@ -286,6 +286,17 @@ class TCZoneinfoTimezoneInfo < Minitest::Test
286
286
  end
287
287
  end
288
288
 
289
+ def test_load_no_offsets
290
+ offsets = []
291
+ transitions = [{:at => Time.utc(2000, 12, 31), :offset_index => 0}]
292
+
293
+ tzif_test(offsets, transitions) do |path, format|
294
+ assert_raises(InvalidZoneinfoFile) do
295
+ ZoneinfoTimezoneInfo.new('Zone', path)
296
+ end
297
+ end
298
+ end
299
+
289
300
  def test_load_invalid_offset_index
290
301
  offsets = [{:gmtoff => -0, :isdst => false, :abbrev => 'LMT'}]
291
302
  transitions = [{:at => Time.utc(2000, 12, 31), :offset_index => 2}]
@@ -707,27 +718,39 @@ class TCZoneinfoTimezoneInfo < Minitest::Test
707
718
  end
708
719
  end
709
720
 
710
- def test_load_starts_all_same_dst_offset
721
+ def test_load_starts_only_dst_transition_with_lmt
711
722
  # The zoneinfo files don't include the offset from standard time, so this
712
723
  # has to be derived by looking at changes in the total UTC offset.
713
- #
714
- # If there are no changes in the UTC offset (ignoring the first offset,
715
- # which is usually local mean time), then a value of 1 hour is used as the
716
- # standard time offset.
717
-
724
+
718
725
  offsets = [
719
726
  {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'},
720
- {:gmtoff => 7200, :isdst => true, :abbrev => 'XDDT'}]
721
-
722
- transitions = [
723
- {:at => Time.utc(2000, 1, 1), :offset_index => 1}]
724
-
725
- tzif_test(offsets, transitions) do |path, format|
726
- info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path)
727
- assert_equal('Zone/DoubleDaylight', info.identifier)
728
-
729
- assert_period(:LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info)
730
- assert_period(:XDDT, 3600, 3600, true, Time.utc(2000, 1, 1), nil, info)
727
+ {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}]
728
+
729
+ transitions = [{:at => Time.utc(2000, 1, 1), :offset_index => 1}]
730
+
731
+ tzif_test(offsets, transitions) do |path, format|
732
+ info = ZoneinfoTimezoneInfo.new('Zone/OnlyDST', path)
733
+ assert_equal('Zone/OnlyDST', info.identifier)
734
+
735
+ assert_period(:LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info)
736
+ assert_period(:XDT, 3542, 3658, true, Time.utc(2000, 1, 1), nil, info)
737
+ end
738
+ end
739
+
740
+ def test_load_starts_only_dst_transition_without_lmt
741
+ # The zoneinfo files don't include the offset from standard time, so this
742
+ # has to be derived by looking at changes in the total UTC offset.
743
+
744
+ offsets = [{:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}]
745
+
746
+ transitions = [{:at => Time.utc(2000, 1, 1), :offset_index => 0}]
747
+
748
+ tzif_test(offsets, transitions) do |path, format|
749
+ info = ZoneinfoTimezoneInfo.new('Zone/OnlyDST', path)
750
+ assert_equal('Zone/OnlyDST', info.identifier)
751
+
752
+ assert_period(:XDT, 3600, 3600, true, nil, Time.utc(2000, 1, 1), info)
753
+ assert_period(:XDT, 3600, 3600, true, Time.utc(2000, 1, 1), nil, info)
731
754
  end
732
755
  end
733
756
 
@@ -756,6 +779,297 @@ class TCZoneinfoTimezoneInfo < Minitest::Test
756
779
  assert_period(:XDT, 0, 3600, true, Time.utc(2000, 2, 1), nil, info)
757
780
  end
758
781
  end
782
+
783
+ def test_load_apia_international_dateline_change
784
+ # The zoneinfo files don't include the offset from standard time, so this
785
+ # has to be derived by looking at changes in the total UTC offset.
786
+
787
+ # Pacific/Apia moved across the International Date Line whilst observing
788
+ # daylight savings time.
789
+
790
+ offsets = [
791
+ {:gmtoff => 45184, :isdst => false, :abbrev => 'LMT'},
792
+ {:gmtoff => -39600, :isdst => false, :abbrev => '-11'},
793
+ {:gmtoff => -36000, :isdst => true, :abbrev => '-10'},
794
+ {:gmtoff => 50400, :isdst => true, :abbrev => '+14'},
795
+ {:gmtoff => 46800, :isdst => false, :abbrev => '+13'}]
796
+
797
+ transitions = [
798
+ {:at => Time.utc(2011, 4, 2, 14, 0, 0), :offset_index => 1},
799
+ {:at => Time.utc(2011, 9, 24, 14, 0, 0), :offset_index => 2},
800
+ {:at => Time.utc(2011, 12, 30, 10, 0, 0), :offset_index => 3},
801
+ {:at => Time.utc(2012, 3, 31, 14, 0, 0), :offset_index => 4}]
802
+
803
+ tzif_test(offsets, transitions) do |path, format|
804
+ info = ZoneinfoTimezoneInfo.new('Test/Pacific/Apia', path)
805
+ assert_equal('Test/Pacific/Apia', info.identifier)
806
+
807
+ assert_period( :LMT, 45184, 0, false, nil, Time.utc(2011, 4, 2, 14, 0, 0), info)
808
+ assert_period(:'-11', -39600, 0, false, Time.utc(2011, 4, 2, 14, 0, 0), Time.utc(2011, 9, 24, 14, 0, 0), info)
809
+ assert_period(:'-10', -39600, 3600, true, Time.utc(2011, 9, 24, 14, 0, 0), Time.utc(2011, 12, 30, 10, 0, 0), info)
810
+ assert_period(:'+14', 46800, 3600, true, Time.utc(2011, 12, 30, 10, 0, 0), Time.utc(2012, 3, 31, 14, 0, 0), info)
811
+ assert_period(:'+13', 46800, 0, false, Time.utc(2012, 3, 31, 14, 0, 0), nil, info)
812
+ end
813
+ end
814
+
815
+ def test_load_offset_split_for_different_utc_offset
816
+ # The zoneinfo files don't include the offset from standard time, so this
817
+ # has to be derived by looking at changes in the total UTC offset.
818
+
819
+ offsets = [
820
+ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'},
821
+ {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'},
822
+ {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'},
823
+ {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'}]
824
+
825
+ transitions = [
826
+ {:at => Time.utc(2000, 1, 1), :offset_index => 1},
827
+ {:at => Time.utc(2000, 2, 1), :offset_index => 3},
828
+ {:at => Time.utc(2000, 3, 1), :offset_index => 1},
829
+ {:at => Time.utc(2000, 4, 1), :offset_index => 2},
830
+ {:at => Time.utc(2000, 5, 1), :offset_index => 3},
831
+ {:at => Time.utc(2000, 6, 1), :offset_index => 2},
832
+ {:at => Time.utc(2000, 7, 1), :offset_index => 1},
833
+ {:at => Time.utc(2000, 8, 1), :offset_index => 3},
834
+ {:at => Time.utc(2000, 9, 1), :offset_index => 1},
835
+ {:at => Time.utc(2000, 10, 1), :offset_index => 2},
836
+ {:at => Time.utc(2000, 11, 1), :offset_index => 3},
837
+ {:at => Time.utc(2000, 12, 1), :offset_index => 2}]
838
+
839
+ # XDT will be split and defined according to its surrounding standard time
840
+ # offsets.
841
+
842
+ tzif_test(offsets, transitions) do |path, format|
843
+ info = ZoneinfoTimezoneInfo.new('Zone/SplitUtcOffset', path)
844
+ assert_equal('Zone/SplitUtcOffset', info.identifier)
845
+
846
+ assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info)
847
+ assert_period(:XST1, 3600, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info)
848
+ assert_period( :XDT, 3600, 7200, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info)
849
+ assert_period(:XST1, 3600, 0, false, Time.utc(2000, 3, 1), Time.utc(2000, 4, 1), info)
850
+ assert_period(:XST2, 7200, 0, false, Time.utc(2000, 4, 1), Time.utc(2000, 5, 1), info)
851
+ assert_period( :XDT, 7200, 3600, true, Time.utc(2000, 5, 1), Time.utc(2000, 6, 1), info)
852
+ assert_period(:XST2, 7200, 0, false, Time.utc(2000, 6, 1), Time.utc(2000, 7, 1), info)
853
+ assert_period(:XST1, 3600, 0, false, Time.utc(2000, 7, 1), Time.utc(2000, 8, 1), info)
854
+ assert_period( :XDT, 3600, 7200, true, Time.utc(2000, 8, 1), Time.utc(2000, 9, 1), info)
855
+ assert_period(:XST1, 3600, 0, false, Time.utc(2000, 9, 1), Time.utc(2000, 10, 1), info)
856
+ assert_period(:XST2, 7200, 0, false, Time.utc(2000, 10, 1), Time.utc(2000, 11, 1), info)
857
+ assert_period( :XDT, 7200, 3600, true, Time.utc(2000, 11, 1), Time.utc(2000, 12, 1), info)
858
+ assert_period(:XST2, 7200, 0, false, Time.utc(2000, 12, 1), nil, info)
859
+
860
+ 1.upto(6) do |i|
861
+ assert_same(info.period_for_utc(Time.utc(2000, i, 1)).offset, info.period_for_utc(Time.utc(2000, i + 6, 1)).offset)
862
+ end
863
+ end
864
+ end
865
+
866
+ def test_load_offset_utc_offset_taken_from_minimum_difference_minimum_after
867
+ # The zoneinfo files don't include the offset from standard time, so this
868
+ # has to be derived by looking at changes in the total UTC offset.
869
+
870
+ offsets = [
871
+ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'},
872
+ {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'},
873
+ {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'},
874
+ {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'}]
875
+
876
+ transitions = [
877
+ {:at => Time.utc(2000, 1, 1), :offset_index => 1},
878
+ {:at => Time.utc(2000, 2, 1), :offset_index => 3},
879
+ {:at => Time.utc(2000, 3, 1), :offset_index => 2}]
880
+
881
+ # XDT should use the closest utc_offset (7200) (and not an equivalent
882
+ # utc_offset of 3600 and std_offset of 7200).
883
+
884
+ tzif_test(offsets, transitions) do |path, format|
885
+ info = ZoneinfoTimezoneInfo.new('Zone/MinimumUtcOffset', path)
886
+ assert_equal('Zone/MinimumUtcOffset', info.identifier)
887
+
888
+ assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info)
889
+ assert_period(:XST1, 3600, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info)
890
+ assert_period( :XDT, 7200, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info)
891
+ assert_period(:XST2, 7200, 0, false, Time.utc(2000, 3, 1), nil, info)
892
+ end
893
+ end
894
+
895
+ def test_load_offset_utc_offset_taken_from_minimum_difference_minimum_before
896
+ # The zoneinfo files don't include the offset from standard time, so this
897
+ # has to be derived by looking at changes in the total UTC offset.
898
+
899
+ offsets = [
900
+ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'},
901
+ {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'},
902
+ {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'},
903
+ {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'}]
904
+
905
+ transitions = [
906
+ {:at => Time.utc(2000, 1, 1), :offset_index => 2},
907
+ {:at => Time.utc(2000, 2, 1), :offset_index => 3},
908
+ {:at => Time.utc(2000, 3, 1), :offset_index => 1}]
909
+
910
+ # XDT should use the closest utc_offset (7200) (and not an equivalent
911
+ # utc_offset of 3600 and std_offset of 7200).
912
+
913
+ tzif_test(offsets, transitions) do |path, format|
914
+ info = ZoneinfoTimezoneInfo.new('Zone/MinimumUtcOffset', path)
915
+ assert_equal('Zone/MinimumUtcOffset', info.identifier)
916
+
917
+ assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info)
918
+ assert_period(:XST2, 7200, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info)
919
+ assert_period( :XDT, 7200, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info)
920
+ assert_period(:XST1, 3600, 0, false, Time.utc(2000, 3, 1), nil, info)
921
+ end
922
+ end
923
+
924
+ def test_load_offset_does_not_use_equal_utc_total_offset_equal_after
925
+ # The zoneinfo files don't include the offset from standard time, so this
926
+ # has to be derived by looking at changes in the total UTC offset.
927
+
928
+ offsets = [
929
+ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'},
930
+ {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'},
931
+ {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'},
932
+ {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}]
933
+
934
+ transitions = [
935
+ {:at => Time.utc(2000, 1, 1), :offset_index => 1},
936
+ {:at => Time.utc(2000, 2, 1), :offset_index => 3},
937
+ {:at => Time.utc(2000, 3, 1), :offset_index => 2}]
938
+
939
+ # XDT will be based on the utc_offset of XST1 even though XST2 has an
940
+ # equivalent (or greater) utc_total_offset.
941
+
942
+ tzif_test(offsets, transitions) do |path, format|
943
+ info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetEqual', path)
944
+ assert_equal('Zone/UtcOffsetEqual', info.identifier)
945
+
946
+ assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info)
947
+ assert_period(:XST1, 3600, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info)
948
+ assert_period( :XDT, 3600, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info)
949
+ assert_period(:XST2, 7200, 0, false, Time.utc(2000, 3, 1), nil, info)
950
+ end
951
+ end
952
+
953
+ def test_load_offset_does_not_use_equal_utc_total_offset_equal_before
954
+ # The zoneinfo files don't include the offset from standard time, so this
955
+ # has to be derived by looking at changes in the total UTC offset.
956
+
957
+ offsets = [
958
+ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'},
959
+ {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'},
960
+ {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'},
961
+ {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}]
962
+
963
+ transitions = [
964
+ {:at => Time.utc(2000, 1, 1), :offset_index => 2},
965
+ {:at => Time.utc(2000, 2, 1), :offset_index => 3},
966
+ {:at => Time.utc(2000, 3, 1), :offset_index => 1}]
967
+
968
+ # XDT will be based on the utc_offset of XST1 even though XST2 has an
969
+ # equivalent (or greater) utc_total_offset.
970
+
971
+ tzif_test(offsets, transitions) do |path, format|
972
+ info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetEqual', path)
973
+ assert_equal('Zone/UtcOffsetEqual', info.identifier)
974
+
975
+ assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info)
976
+ assert_period(:XST2, 7200, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info)
977
+ assert_period( :XDT, 3600, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info)
978
+ assert_period(:XST1, 3600, 0, false, Time.utc(2000, 3, 1), nil, info)
979
+ end
980
+ end
981
+
982
+ def test_load_offset_both_adjacent_non_dst_equal_utc_total_offset
983
+ # The zoneinfo files don't include the offset from standard time, so this
984
+ # has to be derived by looking at changes in the total UTC offset.
985
+
986
+ offsets = [
987
+ {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'},
988
+ {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'},
989
+ {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}]
990
+
991
+ transitions = [
992
+ {:at => Time.utc(2000, 1, 1), :offset_index => 1},
993
+ {:at => Time.utc(2000, 2, 1), :offset_index => 2},
994
+ {:at => Time.utc(2000, 3, 1), :offset_index => 1}]
995
+
996
+ # XDT will just assume an std_offset of +1 hour and calculate the utc_offset
997
+ # from utc_total_offset - std_offset.
998
+
999
+ tzif_test(offsets, transitions) do |path, format|
1000
+ info = ZoneinfoTimezoneInfo.new('Zone/AdjacentEqual', path)
1001
+ assert_equal('Zone/AdjacentEqual', info.identifier)
1002
+
1003
+ assert_period(:LMT, 7142, 0, false, nil, Time.utc(2000, 1, 1), info)
1004
+ assert_period(:XST, 7200, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info)
1005
+ assert_period(:XDT, 3600, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info)
1006
+ assert_period(:XST, 7200, 0, false, Time.utc(2000, 3, 1), nil, info)
1007
+ end
1008
+ end
1009
+
1010
+ def test_load_offset_utc_offset_preserved_from_next
1011
+ # The zoneinfo files don't include the offset from standard time, so this
1012
+ # has to be derived by looking at changes in the total UTC offset.
1013
+
1014
+ offsets = [
1015
+ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'},
1016
+ {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'},
1017
+ {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'},
1018
+ {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT1'},
1019
+ {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT2'}]
1020
+
1021
+ transitions = [
1022
+ {:at => Time.utc(2000, 1, 1), :offset_index => 1},
1023
+ {:at => Time.utc(2000, 2, 1), :offset_index => 3},
1024
+ {:at => Time.utc(2000, 3, 1), :offset_index => 4},
1025
+ {:at => Time.utc(2000, 4, 1), :offset_index => 2}]
1026
+
1027
+ # Both XDT1 and XDT2 should both use the closest utc_offset (7200) (and not
1028
+ # an equivalent utc_offset of 3600 and std_offset of 7200).
1029
+
1030
+ tzif_test(offsets, transitions) do |path, format|
1031
+ info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetPreserved', path)
1032
+ assert_equal('Zone/UtcOffsetPreserved', info.identifier)
1033
+
1034
+ assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info)
1035
+ assert_period(:XST1, 3600, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info)
1036
+ assert_period(:XDT1, 7200, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info)
1037
+ assert_period(:XDT2, 7200, 3600, true, Time.utc(2000, 3, 1), Time.utc(2000, 4, 1), info)
1038
+ assert_period(:XST2, 7200, 0, false, Time.utc(2000, 4, 1), nil, info)
1039
+ end
1040
+ end
1041
+
1042
+ def test_load_offset_utc_offset_preserved_from_previous
1043
+ # The zoneinfo files don't include the offset from standard time, so this
1044
+ # has to be derived by looking at changes in the total UTC offset.
1045
+
1046
+ offsets = [
1047
+ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'},
1048
+ {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'},
1049
+ {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'},
1050
+ {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT1'},
1051
+ {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT2'}]
1052
+
1053
+ transitions = [
1054
+ {:at => Time.utc(2000, 1, 1), :offset_index => 2},
1055
+ {:at => Time.utc(2000, 2, 1), :offset_index => 3},
1056
+ {:at => Time.utc(2000, 3, 1), :offset_index => 4},
1057
+ {:at => Time.utc(2000, 4, 1), :offset_index => 1}]
1058
+
1059
+ # Both XDT1 and XDT2 should both use the closest utc_offset (7200) (and not
1060
+ # an equivalent utc_offset of 3600 and std_offset of 7200).
1061
+
1062
+ tzif_test(offsets, transitions) do |path, format|
1063
+ info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetPreserved', path)
1064
+ assert_equal('Zone/UtcOffsetPreserved', info.identifier)
1065
+
1066
+ assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info)
1067
+ assert_period(:XST2, 7200, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info)
1068
+ assert_period(:XDT1, 7200, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info)
1069
+ assert_period(:XDT2, 7200, 3600, true, Time.utc(2000, 3, 1), Time.utc(2000, 4, 1), info)
1070
+ assert_period(:XST1, 3600, 0, false, Time.utc(2000, 4, 1), nil, info)
1071
+ end
1072
+ end
759
1073
 
760
1074
  def test_load_in_safe_mode
761
1075
  offsets = [{:gmtoff => -12094, :isdst => false, :abbrev => 'LT'}]