1# frozen_string_literal: false 2require 'test/unit' 3require '-test-/time' 4 5class TestTimeTZ < Test::Unit::TestCase 6 has_right_tz = true 7 has_lisbon_tz = true 8 force_tz_test = ENV["RUBY_FORCE_TIME_TZ_TEST"] == "yes" 9 case RUBY_PLATFORM 10 when /linux/ 11 force_tz_test = true 12 when /darwin|freebsd/ 13 has_lisbon_tz = false 14 force_tz_test = true 15 end 16 17 if force_tz_test 18 module Util 19 def with_tz(tz) 20 old = ENV["TZ"] 21 begin 22 ENV["TZ"] = tz 23 yield 24 ensure 25 ENV["TZ"] = old 26 end 27 end 28 end 29 else 30 module Util 31 def with_tz(tz) 32 if ENV["TZ"] == tz 33 yield 34 end 35 end 36 end 37 end 38 39 module Util 40 def have_tz_offset?(tz) 41 with_tz(tz) {!Time.now.utc_offset.zero?} 42 end 43 44 def format_gmtoff(gmtoff, colon=false) 45 if gmtoff < 0 46 expected = "-" 47 gmtoff = -gmtoff 48 else 49 expected = "+" 50 end 51 gmtoff /= 60 52 expected << "%02d" % [gmtoff / 60] 53 expected << ":" if colon 54 expected << "%02d" % [gmtoff % 60] 55 expected 56 end 57 58 def format_gmtoff2(gmtoff) 59 if gmtoff < 0 60 expected = "-" 61 gmtoff = -gmtoff 62 else 63 expected = "+" 64 end 65 expected << "%02d:%02d:%02d" % [gmtoff / 3600, gmtoff % 3600 / 60, gmtoff % 60] 66 expected 67 end 68 69 def group_by(e, &block) 70 if e.respond_to? :group_by 71 e.group_by(&block) 72 else 73 h = {} 74 e.each {|o| 75 (h[yield(o)] ||= []) << o 76 } 77 h 78 end 79 end 80 81 end 82 83 include Util 84 extend Util 85 86 has_right_tz &&= have_tz_offset?("right/America/Los_Angeles") 87 has_lisbon_tz &&= have_tz_offset?("Europe/Lisbon") 88 CORRECT_TOKYO_DST_1951 = with_tz("Asia/Tokyo") { 89 if Time.local(1951, 5, 6, 12, 0, 0).dst? # noon, DST 90 if Time.local(1951, 5, 6, 1, 0, 0).dst? # DST with fixed tzdata 91 Time.local(1951, 9, 8, 23, 0, 0).dst? ? "2018f" : "2018e" 92 end 93 end 94 } 95 CORRECT_KIRITIMATI_SKIP_1994 = with_tz("Pacific/Kiritimati") { 96 Time.local(1994, 12, 31, 0, 0, 0).year == 1995 97 } 98 99 def time_to_s(t) 100 t.to_s 101 end 102 103 104 def assert_time_constructor(tz, expected, method, args, message=nil) 105 m = message ? "#{message}\n" : "" 106 m << "TZ=#{tz} Time.#{method}(#{args.map {|arg| arg.inspect }.join(', ')})" 107 real = time_to_s(Time.send(method, *args)) 108 assert_equal(expected, real, m) 109 end 110 111 def test_localtime_zone 112 t = with_tz("America/Los_Angeles") { 113 Time.local(2000, 1, 1) 114 } 115 skip "force_tz_test is false on this environment" unless t 116 z1 = t.zone 117 z2 = with_tz(tz="Asia/Singapore") { 118 t.localtime.zone 119 } 120 assert_equal(z2, z1) 121 end 122 123 def test_america_los_angeles 124 with_tz(tz="America/Los_Angeles") { 125 assert_time_constructor(tz, "2007-03-11 03:00:00 -0700", :local, [2007,3,11,2,0,0]) 126 assert_time_constructor(tz, "2007-03-11 03:59:59 -0700", :local, [2007,3,11,2,59,59]) 127 assert_equal("PST", Time.new(0x1_0000_0000_0000_0000, 1).zone) 128 assert_equal("PDT", Time.new(0x1_0000_0000_0000_0000, 8).zone) 129 assert_equal(false, Time.new(0x1_0000_0000_0000_0000, 1).isdst) 130 assert_equal(true, Time.new(0x1_0000_0000_0000_0000, 8).isdst) 131 } 132 end 133 134 def test_america_managua 135 with_tz(tz="America/Managua") { 136 assert_time_constructor(tz, "1993-01-01 01:00:00 -0500", :local, [1993,1,1,0,0,0]) 137 assert_time_constructor(tz, "1993-01-01 01:59:59 -0500", :local, [1993,1,1,0,59,59]) 138 } 139 end 140 141 def test_asia_singapore 142 with_tz(tz="Asia/Singapore") { 143 assert_time_constructor(tz, "1981-12-31 23:59:59 +0730", :local, [1981,12,31,23,59,59]) 144 assert_time_constructor(tz, "1982-01-01 00:30:00 +0800", :local, [1982,1,1,0,0,0]) 145 assert_time_constructor(tz, "1982-01-01 00:59:59 +0800", :local, [1982,1,1,0,29,59]) 146 assert_time_constructor(tz, "1982-01-01 00:30:00 +0800", :local, [1982,1,1,0,30,0]) 147 } 148 end 149 150 def test_asia_tokyo 151 with_tz(tz="Asia/Tokyo") { 152 h = CORRECT_TOKYO_DST_1951 ? 0 : 2 153 assert_time_constructor(tz, "1951-05-06 0#{h+1}:00:00 +1000", :local, [1951,5,6,h,0,0]) 154 assert_time_constructor(tz, "1951-05-06 0#{h+1}:59:59 +1000", :local, [1951,5,6,h,59,59]) 155 assert_time_constructor(tz, "2010-06-10 06:13:28 +0900", :local, [2010,6,10,6,13,28]) 156 } 157 end 158 159 def test_asia_kuala_lumpur 160 with_tz(tz="Asia/Kuala_Lumpur") { 161 assert_time_constructor(tz, "1933-01-01 00:20:00 +0720", :local, [1933]) 162 } 163 end 164 165 def test_canada_newfoundland 166 with_tz(tz="America/St_Johns") { 167 assert_time_constructor(tz, "2007-11-03 23:00:59 -0230", :new, [2007,11,3,23,0,59,:dst]) 168 assert_time_constructor(tz, "2007-11-03 23:01:00 -0230", :new, [2007,11,3,23,1,0,:dst]) 169 assert_time_constructor(tz, "2007-11-03 23:59:59 -0230", :new, [2007,11,3,23,59,59,:dst]) 170 assert_time_constructor(tz, "2007-11-04 00:00:00 -0230", :new, [2007,11,4,0,0,0,:dst]) 171 assert_time_constructor(tz, "2007-11-04 00:00:59 -0230", :new, [2007,11,4,0,0,59,:dst]) 172 assert_time_constructor(tz, "2007-11-03 23:01:00 -0330", :new, [2007,11,3,23,1,0,:std]) 173 assert_time_constructor(tz, "2007-11-03 23:59:59 -0330", :new, [2007,11,3,23,59,59,:std]) 174 assert_time_constructor(tz, "2007-11-04 00:00:59 -0330", :new, [2007,11,4,0,0,59,:std]) 175 assert_time_constructor(tz, "2007-11-04 00:01:00 -0330", :new, [2007,11,4,0,1,0,:std]) 176 } 177 end 178 179 def test_europe_brussels 180 with_tz(tz="Europe/Brussels") { 181 assert_time_constructor(tz, "1916-04-30 23:59:59 +0100", :local, [1916,4,30,23,59,59]) 182 assert_time_constructor(tz, "1916-05-01 01:00:00 +0200", :local, [1916,5,1], "[ruby-core:30672] [Bug #3411]") 183 assert_time_constructor(tz, "1916-05-01 01:59:59 +0200", :local, [1916,5,1,0,59,59]) 184 assert_time_constructor(tz, "1916-05-01 01:00:00 +0200", :local, [1916,5,1,1,0,0]) 185 assert_time_constructor(tz, "1916-05-01 01:59:59 +0200", :local, [1916,5,1,1,59,59]) 186 } 187 end 188 189 def test_europe_berlin 190 with_tz(tz="Europe/Berlin") { 191 assert_time_constructor(tz, "2011-10-30 02:00:00 +0100", :local, [2011,10,30,2,0,0], "[ruby-core:67345] [Bug #10698]") 192 assert_time_constructor(tz, "2011-10-30 02:00:00 +0100", :local, [0,0,2,30,10,2011,nil,nil,false,nil]) 193 assert_time_constructor(tz, "2011-10-30 02:00:00 +0200", :local, [0,0,2,30,10,2011,nil,nil,true,nil]) 194 } 195 end 196 197 def test_europe_lisbon 198 with_tz("Europe/Lisbon") { 199 assert_equal("LMT", Time.new(-0x1_0000_0000_0000_0000).zone) 200 } 201 end if has_lisbon_tz 202 203 def test_pacific_kiritimati 204 with_tz(tz="Pacific/Kiritimati") { 205 assert_time_constructor(tz, "1994-12-30 00:00:00 -1000", :local, [1994,12,30,0,0,0]) 206 assert_time_constructor(tz, "1994-12-30 23:59:59 -1000", :local, [1994,12,30,23,59,59]) 207 if CORRECT_KIRITIMATI_SKIP_1994 208 assert_time_constructor(tz, "1995-01-01 00:00:00 +1400", :local, [1994,12,31,0,0,0]) 209 assert_time_constructor(tz, "1995-01-01 23:59:59 +1400", :local, [1994,12,31,23,59,59]) 210 assert_time_constructor(tz, "1995-01-01 00:00:00 +1400", :local, [1995,1,1,0,0,0]) 211 else 212 assert_time_constructor(tz, "1994-12-31 23:59:59 -1000", :local, [1994,12,31,23,59,59]) 213 assert_time_constructor(tz, "1995-01-02 00:00:00 +1400", :local, [1995,1,1,0,0,0]) 214 assert_time_constructor(tz, "1995-01-02 23:59:59 +1400", :local, [1995,1,1,23,59,59]) 215 end 216 assert_time_constructor(tz, "1995-01-02 00:00:00 +1400", :local, [1995,1,2,0,0,0]) 217 } 218 end 219 220 def test_right_utc 221 with_tz(tz="right/UTC") { 222 ::Bug::Time.reset_leap_second_info 223 assert_time_constructor(tz, "2008-12-31 23:59:59 UTC", :utc, [2008,12,31,23,59,59]) 224 assert_time_constructor(tz, "2008-12-31 23:59:60 UTC", :utc, [2008,12,31,23,59,60]) 225 assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,24,0,0]) 226 assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2009,1,1,0,0,0]) 227 } 228 end if has_right_tz 229 230 def test_right_utc_switching 231 with_tz("UTC") { # ensure no leap second timezone 232 ::Bug::Time.reset_leap_second_info 233 assert_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i) 234 with_tz(tz="right/UTC") { 235 assert_time_constructor(tz, "2008-12-31 23:59:59 UTC", :utc, [2008,12,31,23,59,59]) 236 assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,23,59,60]) 237 assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,24,0,0]) 238 assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2009,1,1,0,0,0]) 239 assert_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i) 240 } 241 } 242 with_tz("right/UTC") { 243 ::Bug::Time.reset_leap_second_info 244 assert_not_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i) 245 with_tz(tz="UTC") { 246 assert_time_constructor(tz, "2008-12-31 23:59:59 UTC", :utc, [2008,12,31,23,59,59]) 247 assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,23,59,60]) 248 assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2008,12,31,24,0,0]) 249 assert_time_constructor(tz, "2009-01-01 00:00:00 UTC", :utc, [2009,1,1,0,0,0]) 250 assert_not_equal(4102444800, Time.utc(2100,1,1,0,0,0).to_i) 251 } 252 } 253 end if has_right_tz 254 255 def test_right_america_los_angeles 256 with_tz(tz="right/America/Los_Angeles") { 257 assert_time_constructor(tz, "2008-12-31 15:59:59 -0800", :local, [2008,12,31,15,59,59]) 258 assert_time_constructor(tz, "2008-12-31 15:59:60 -0800", :local, [2008,12,31,15,59,60]) 259 assert_time_constructor(tz, "2008-12-31 16:00:00 -0800", :local, [2008,12,31,16,0,0]) 260 } 261 end if has_right_tz 262 263 MON2NUM = { 264 "Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4, "May" => 5, "Jun" => 6, 265 "Jul" => 7, "Aug" => 8, "Sep" => 9, "Oct" => 10, "Nov" => 11, "Dec" => 12 266 } 267 268 @testnum = 0 269 def self.gen_test_name(hint) 270 @testnum += 1 271 s = "test_gen_#{@testnum}" 272 s.sub(/gen_/) { "gen" + "_#{hint}_".gsub(/[^0-9A-Za-z]+/, '_') } 273 end 274 275 def self.parse_zdump_line(line) 276 return nil if /\A\#/ =~ line || /\A\s*\z/ =~ line 277 if /\A(\S+)\s+ 278 \S+\s+(\S+)\s+(\d+)\s+(\d\d):(\d\d):(\d\d)\s+(\d+)\s+UTC? 279 \s+=\s+ 280 \S+\s+(\S+)\s+(\d+)\s+(\d\d):(\d\d):(\d\d)\s+(\d+)\s+\S+ 281 \s+isdst=\d+\s+gmtoff=(-?\d+)\n 282 \z/x !~ line 283 raise "unexpected zdump line: #{line.inspect}" 284 end 285 tz, u_mon, u_day, u_hour, u_min, u_sec, u_year, 286 l_mon, l_day, l_hour, l_min, l_sec, l_year, gmtoff = $~.captures 287 u_year = u_year.to_i 288 u_mon = MON2NUM[u_mon] 289 u_day = u_day.to_i 290 u_hour = u_hour.to_i 291 u_min = u_min.to_i 292 u_sec = u_sec.to_i 293 l_year = l_year.to_i 294 l_mon = MON2NUM[l_mon] 295 l_day = l_day.to_i 296 l_hour = l_hour.to_i 297 l_min = l_min.to_i 298 l_sec = l_sec.to_i 299 gmtoff = gmtoff.to_i 300 [tz, 301 [u_year, u_mon, u_day, u_hour, u_min, u_sec], 302 [l_year, l_mon, l_day, l_hour, l_min, l_sec], 303 gmtoff] 304 end 305 306 def self.gen_zdump_test(data) 307 sample = [] 308 data.each_line {|line| 309 s = parse_zdump_line(line) 310 sample << s if s 311 } 312 sample.each {|tz, u, l, gmtoff| 313 expected_utc = "%04d-%02d-%02d %02d:%02d:%02d UTC" % u 314 expected = "%04d-%02d-%02d %02d:%02d:%02d %s" % (l+[format_gmtoff(gmtoff)]) 315 mesg_utc = "TZ=#{tz} Time.utc(#{u.map {|arg| arg.inspect }.join(', ')})" 316 mesg = "#{mesg_utc}.localtime" 317 define_method(gen_test_name(tz)) { 318 with_tz(tz) { 319 ::Bug::Time.reset_leap_second_info 320 t = nil 321 assert_nothing_raised(mesg) { t = Time.utc(*u) } 322 assert_equal(expected_utc, time_to_s(t), mesg_utc) 323 assert_nothing_raised(mesg) { t.localtime } 324 assert_equal(expected, time_to_s(t), mesg) 325 assert_equal(gmtoff, t.gmtoff) 326 assert_equal(format_gmtoff(gmtoff), t.strftime("%z")) 327 assert_equal(format_gmtoff(gmtoff, true), t.strftime("%:z")) 328 assert_equal(format_gmtoff2(gmtoff), t.strftime("%::z")) 329 assert_equal(Encoding::US_ASCII, t.zone.encoding) 330 } 331 } 332 } 333 334 group_by(sample) {|tz, _, _, _| tz }.each {|tz, a| 335 a.each_with_index {|(_, _, l, gmtoff), i| 336 expected = "%04d-%02d-%02d %02d:%02d:%02d %s" % (l+[format_gmtoff(gmtoff)]) 337 monotonic_to_past = i == 0 || (a[i-1][2] <=> l) < 0 338 monotonic_to_future = i == a.length-1 || (l <=> a[i+1][2]) < 0 339 if monotonic_to_past && monotonic_to_future 340 define_method(gen_test_name(tz)) { 341 with_tz(tz) { 342 assert_time_constructor(tz, expected, :local, l) 343 assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, false, nil]) 344 assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, true, nil]) 345 assert_time_constructor(tz, expected, :new, l) 346 assert_time_constructor(tz, expected, :new, l+[:std]) 347 assert_time_constructor(tz, expected, :new, l+[:dst]) 348 } 349 } 350 elsif monotonic_to_past && !monotonic_to_future 351 define_method(gen_test_name(tz)) { 352 with_tz(tz) { 353 assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, true, nil]) 354 assert_time_constructor(tz, expected, :new, l+[:dst]) 355 } 356 } 357 elsif !monotonic_to_past && monotonic_to_future 358 define_method(gen_test_name(tz)) { 359 with_tz(tz) { 360 assert_time_constructor(tz, expected, :local, l.reverse+[nil, nil, false, nil]) 361 assert_time_constructor(tz, expected, :new, l+[:std]) 362 } 363 } 364 else 365 define_method(gen_test_name(tz)) { 366 flunk("time in reverse order: TZ=#{tz} #{expected}") 367 } 368 end 369 } 370 } 371 end 372 373 gen_zdump_test <<'End' 374America/Lima Sun Apr 1 03:59:59 1990 UTC = Sat Mar 31 23:59:59 1990 PEST isdst=1 gmtoff=-14400 375America/Lima Sun Apr 1 04:00:00 1990 UTC = Sat Mar 31 23:00:00 1990 PET isdst=0 gmtoff=-18000 376America/Lima Sat Jan 1 04:59:59 1994 UTC = Fri Dec 31 23:59:59 1993 PET isdst=0 gmtoff=-18000 377America/Lima Sat Jan 1 05:00:00 1994 UTC = Sat Jan 1 01:00:00 1994 PEST isdst=1 gmtoff=-14400 378America/Lima Fri Apr 1 03:59:59 1994 UTC = Thu Mar 31 23:59:59 1994 PEST isdst=1 gmtoff=-14400 379America/Lima Fri Apr 1 04:00:00 1994 UTC = Thu Mar 31 23:00:00 1994 PET isdst=0 gmtoff=-18000 380America/Los_Angeles Sun Apr 2 09:59:59 2006 UTC = Sun Apr 2 01:59:59 2006 PST isdst=0 gmtoff=-28800 381America/Los_Angeles Sun Apr 2 10:00:00 2006 UTC = Sun Apr 2 03:00:00 2006 PDT isdst=1 gmtoff=-25200 382America/Los_Angeles Sun Oct 29 08:59:59 2006 UTC = Sun Oct 29 01:59:59 2006 PDT isdst=1 gmtoff=-25200 383America/Los_Angeles Sun Oct 29 09:00:00 2006 UTC = Sun Oct 29 01:00:00 2006 PST isdst=0 gmtoff=-28800 384America/Los_Angeles Sun Mar 11 09:59:59 2007 UTC = Sun Mar 11 01:59:59 2007 PST isdst=0 gmtoff=-28800 385America/Los_Angeles Sun Mar 11 10:00:00 2007 UTC = Sun Mar 11 03:00:00 2007 PDT isdst=1 gmtoff=-25200 386America/Los_Angeles Sun Nov 4 08:59:59 2007 UTC = Sun Nov 4 01:59:59 2007 PDT isdst=1 gmtoff=-25200 387America/Los_Angeles Sun Nov 4 09:00:00 2007 UTC = Sun Nov 4 01:00:00 2007 PST isdst=0 gmtoff=-28800 388America/Managua Thu Sep 24 04:59:59 1992 UTC = Wed Sep 23 23:59:59 1992 EST isdst=0 gmtoff=-18000 389America/Managua Thu Sep 24 05:00:00 1992 UTC = Wed Sep 23 23:00:00 1992 CST isdst=0 gmtoff=-21600 390America/Managua Fri Jan 1 05:59:59 1993 UTC = Thu Dec 31 23:59:59 1992 CST isdst=0 gmtoff=-21600 391America/Managua Fri Jan 1 06:00:00 1993 UTC = Fri Jan 1 01:00:00 1993 EST isdst=0 gmtoff=-18000 392America/Managua Wed Jan 1 04:59:59 1997 UTC = Tue Dec 31 23:59:59 1996 EST isdst=0 gmtoff=-18000 393America/Managua Wed Jan 1 05:00:00 1997 UTC = Tue Dec 31 23:00:00 1996 CST isdst=0 gmtoff=-21600 394Asia/Singapore Sun Aug 8 16:30:00 1965 UTC = Mon Aug 9 00:00:00 1965 SGT isdst=0 gmtoff=27000 395Asia/Singapore Thu Dec 31 16:29:59 1981 UTC = Thu Dec 31 23:59:59 1981 SGT isdst=0 gmtoff=27000 396Asia/Singapore Thu Dec 31 16:30:00 1981 UTC = Fri Jan 1 00:30:00 1982 SGT isdst=0 gmtoff=28800 397End 398 gen_zdump_test CORRECT_TOKYO_DST_1951 ? <<'End' + (CORRECT_TOKYO_DST_1951 < "2018f" ? <<'2018e' : <<'2018f') : <<'End' 399Asia/Tokyo Sat May 5 14:59:59 1951 UTC = Sat May 5 23:59:59 1951 JST isdst=0 gmtoff=32400 400Asia/Tokyo Sat May 5 15:00:00 1951 UTC = Sun May 6 01:00:00 1951 JDT isdst=1 gmtoff=36000 401End 402Asia/Tokyo Sat Sep 8 13:59:59 1951 UTC = Sat Sep 8 23:59:59 1951 JDT isdst=1 gmtoff=36000 403Asia/Tokyo Sat Sep 8 14:00:00 1951 UTC = Sat Sep 8 23:00:00 1951 JST isdst=0 gmtoff=32400 4042018e 405Asia/Tokyo Sat Sep 8 14:59:59 1951 UTC = Sun Sep 9 00:59:59 1951 JDT isdst=1 gmtoff=36000 406Asia/Tokyo Sat Sep 8 15:00:00 1951 UTC = Sun Sep 9 00:00:00 1951 JST isdst=0 gmtoff=32400 4072018f 408Asia/Tokyo Sat May 5 16:59:59 1951 UTC = Sun May 6 01:59:59 1951 JST isdst=0 gmtoff=32400 409Asia/Tokyo Sat May 5 17:00:00 1951 UTC = Sun May 6 03:00:00 1951 JDT isdst=1 gmtoff=36000 410Asia/Tokyo Fri Sep 7 15:59:59 1951 UTC = Sat Sep 8 01:59:59 1951 JDT isdst=1 gmtoff=36000 411Asia/Tokyo Fri Sep 7 16:00:00 1951 UTC = Sat Sep 8 01:00:00 1951 JST isdst=0 gmtoff=32400 412End 413 gen_zdump_test <<'End' 414America/St_Johns Sun Mar 11 03:30:59 2007 UTC = Sun Mar 11 00:00:59 2007 NST isdst=0 gmtoff=-12600 415America/St_Johns Sun Mar 11 03:31:00 2007 UTC = Sun Mar 11 01:01:00 2007 NDT isdst=1 gmtoff=-9000 416America/St_Johns Sun Nov 4 02:30:59 2007 UTC = Sun Nov 4 00:00:59 2007 NDT isdst=1 gmtoff=-9000 417America/St_Johns Sun Nov 4 02:31:00 2007 UTC = Sat Nov 3 23:01:00 2007 NST isdst=0 gmtoff=-12600 418Europe/Brussels Sun Apr 30 22:59:59 1916 UTC = Sun Apr 30 23:59:59 1916 CET isdst=0 gmtoff=3600 419Europe/Brussels Sun Apr 30 23:00:00 1916 UTC = Mon May 1 01:00:00 1916 CEST isdst=1 gmtoff=7200 420Europe/Brussels Sat Sep 30 22:59:59 1916 UTC = Sun Oct 1 00:59:59 1916 CEST isdst=1 gmtoff=7200 421Europe/Brussels Sat Sep 30 23:00:00 1916 UTC = Sun Oct 1 00:00:00 1916 CET isdst=0 gmtoff=3600 422Europe/London Sun Mar 16 01:59:59 1947 UTC = Sun Mar 16 01:59:59 1947 GMT isdst=0 gmtoff=0 423Europe/London Sun Mar 16 02:00:00 1947 UTC = Sun Mar 16 03:00:00 1947 BST isdst=1 gmtoff=3600 424Europe/London Sun Apr 13 00:59:59 1947 UTC = Sun Apr 13 01:59:59 1947 BST isdst=1 gmtoff=3600 425Europe/London Sun Apr 13 01:00:00 1947 UTC = Sun Apr 13 03:00:00 1947 BDST isdst=1 gmtoff=7200 426Europe/London Sun Aug 10 00:59:59 1947 UTC = Sun Aug 10 02:59:59 1947 BDST isdst=1 gmtoff=7200 427Europe/London Sun Aug 10 01:00:00 1947 UTC = Sun Aug 10 02:00:00 1947 BST isdst=1 gmtoff=3600 428Europe/London Sun Nov 2 01:59:59 1947 UTC = Sun Nov 2 02:59:59 1947 BST isdst=1 gmtoff=3600 429Europe/London Sun Nov 2 02:00:00 1947 UTC = Sun Nov 2 02:00:00 1947 GMT isdst=0 gmtoff=0 430End 431 if CORRECT_KIRITIMATI_SKIP_1994 432 gen_zdump_test <<'End' 433Pacific/Kiritimati Sat Dec 31 09:59:59 1994 UTC = Fri Dec 30 23:59:59 1994 LINT isdst=0 gmtoff=-36000 434Pacific/Kiritimati Sat Dec 31 10:00:00 1994 UTC = Sun Jan 1 00:00:00 1995 LINT isdst=0 gmtoff=50400 435End 436 else 437 gen_zdump_test <<'End' 438Pacific/Kiritimati Sun Jan 1 09:59:59 1995 UTC = Sat Dec 31 23:59:59 1994 LINT isdst=0 gmtoff=-36000 439Pacific/Kiritimati Sun Jan 1 10:00:00 1995 UTC = Mon Jan 2 00:00:00 1995 LINT isdst=0 gmtoff=50400 440End 441 end 442 gen_zdump_test <<'End' if has_right_tz 443right/America/Los_Angeles Fri Jun 30 23:59:60 1972 UTC = Fri Jun 30 16:59:60 1972 PDT isdst=1 gmtoff=-25200 444right/America/Los_Angeles Wed Dec 31 23:59:60 2008 UTC = Wed Dec 31 15:59:60 2008 PST isdst=0 gmtoff=-28800 445#right/Asia/Tokyo Fri Jun 30 23:59:60 1972 UTC = Sat Jul 1 08:59:60 1972 JST isdst=0 gmtoff=32400 446#right/Asia/Tokyo Sat Dec 31 23:59:60 2005 UTC = Sun Jan 1 08:59:60 2006 JST isdst=0 gmtoff=32400 447right/Europe/Paris Fri Jun 30 23:59:60 1972 UTC = Sat Jul 1 00:59:60 1972 CET isdst=0 gmtoff=3600 448right/Europe/Paris Wed Dec 31 23:59:60 2008 UTC = Thu Jan 1 00:59:60 2009 CET isdst=0 gmtoff=3600 449End 450 451 def self.gen_variational_zdump_test(hint, data) 452 sample = [] 453 data.each_line {|line| 454 s = parse_zdump_line(line) 455 sample << s if s 456 } 457 458 define_method(gen_test_name(hint)) { 459 results = [] 460 sample.each {|tz, u, l, gmtoff| 461 expected_utc = "%04d-%02d-%02d %02d:%02d:%02d UTC" % u 462 expected = "%04d-%02d-%02d %02d:%02d:%02d %s" % (l+[format_gmtoff(gmtoff)]) 463 mesg_utc = "TZ=#{tz} Time.utc(#{u.map {|arg| arg.inspect }.join(', ')})" 464 mesg = "#{mesg_utc}.localtime" 465 with_tz(tz) { 466 t = nil 467 assert_nothing_raised(mesg) { t = Time.utc(*u) } 468 assert_equal(expected_utc, time_to_s(t), mesg_utc) 469 assert_nothing_raised(mesg) { t.localtime } 470 471 results << [ 472 expected == time_to_s(t), 473 gmtoff == t.gmtoff, 474 format_gmtoff(gmtoff) == t.strftime("%z"), 475 format_gmtoff(gmtoff, true) == t.strftime("%:z"), 476 format_gmtoff2(gmtoff) == t.strftime("%::z") 477 ] 478 } 479 } 480 assert_include(results, [true, true, true, true, true]) 481 } 482 end 483 484 # tzdata-2014g fixed the offset for lisbon from -0:36:32 to -0:36:45. 485 # [ruby-core:65058] [Bug #10245] 486 gen_variational_zdump_test "lisbon", <<'End' if has_lisbon_tz 487Europe/Lisbon Mon Jan 1 00:36:31 1912 UTC = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2192 488Europe/Lisbon Mon Jan 1 00:36:44 1912 UT = Sun Dec 31 23:59:59 1911 LMT isdst=0 gmtoff=-2205 489Europe/Lisbon Sun Dec 31 23:59:59 1911 UT = Sun Dec 31 23:23:14 1911 LMT isdst=0 gmtoff=-2205 490End 491 492 class TZ 493 attr_reader :name, :abbr, :offset 494 495 def initialize(name, abbr, offset) 496 @name = name 497 @abbr = abbr 498 @offset = offset 499 end 500 501 def local_to_utc(t) 502 t - @offset 503 end 504 505 def utc_to_local(t) 506 t + @offset 507 end 508 509 def abbr(t) 510 @abbr 511 end 512 513 def ==(other) 514 @name == other.name and @abbr == other.abbr(0) and @offset == other.offset 515 end 516 517 def inspect 518 "#<TZ: #@name #@abbr #@offset>" 519 end 520 end 521end 522 523module TestTimeTZ::WithTZ 524 def subtest_new(time_class, tz, tzarg, tzname, abbr, utc_offset) 525 t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg) 526 assert_equal([2018, 9, 1, 12, 0, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone]) 527 h, m = (-utc_offset / 60).divmod(60) 528 assert_equal(time_class.utc(2018, 9, 1, 12+h, m, 0).to_i, t.to_i) 529 end 530 531 def subtest_getlocal(time_class, tz, tzarg, tzname, abbr, utc_offset) 532 t = time_class.utc(2018, 9, 1, 12, 0, 0).getlocal(tzarg) 533 h, m = (utc_offset / 60).divmod(60) 534 assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone]) 535 assert_equal(time_class.utc(2018, 9, 1, 12, 0, 0), t) 536 end 537 538 def subtest_strftime(time_class, tz, tzarg, tzname, abbr, utc_offset) 539 t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg) 540 h, m = (utc_offset.abs / 60).divmod(60) 541 h = -h if utc_offset < 0 542 assert_equal("%+.2d%.2d %s" % [h, m, abbr], t.strftime("%z %Z")) 543 end 544 545 def subtest_plus(time_class, tz, tzarg, tzname, abbr, utc_offset) 546 t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg) + 4000 547 assert_equal([2018, 9, 1, 13, 6, 40, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone]) 548 m, s = (4000-utc_offset).divmod(60) 549 h, m = m.divmod(60) 550 assert_equal(time_class.utc(2018, 9, 1, 12+h, m, s), t) 551 assert_equal(6, t.wday) 552 assert_equal(244, t.yday) 553 end 554 555 def subtest_at(time_class, tz, tzarg, tzname, abbr, utc_offset) 556 h, m = (utc_offset / 60).divmod(60) 557 utc = time_class.utc(2018, 9, 1, 12, 0, 0) 558 t = time_class.at(utc, in: tzarg) 559 assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone]) 560 assert_equal(utc.to_i, t.to_i) 561 utc = utc.to_i 562 t = time_class.at(utc, in: tzarg) 563 assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone]) 564 assert_equal(utc, t.to_i) 565 end 566 567 def subtest_marshal(time_class, tz, tzarg, tzname, abbr, utc_offset) 568 t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg) 569 t2 = Marshal.load(Marshal.dump(t)) 570 assert_equal(t, t2) 571 assert_equal(t.utc_offset, t2.utc_offset) 572 assert_equal(t.utc_offset, (t2+1).utc_offset) 573 assert_instance_of(t.zone.class, t2.zone) 574 end 575 576 def test_invalid_zone 577 make_timezone("INVALID", "INV", 0) 578 rescue => e 579 assert_kind_of(StandardError, e) 580 else 581 assert false, "ArgumentError expected but nothing was raised." 582 end 583 584 def nametest_marshal_compatibility(time_class, tzname, abbr, utc_offset) 585 data = [ 586 "\x04\x08Iu:".b, Marshal.dump(time_class)[3..-1], 587 "\x0d""\xEF\xA7\x1D\x80\x00\x00\x00\x00".b, 588 Marshal.dump({offset: utc_offset, zone: abbr})[3..-1], 589 ].join('') 590 t = Marshal.load(data) 591 assert_equal(utc_offset, t.utc_offset) 592 assert_equal(utc_offset, (t+1).utc_offset) 593 # t.zone may be a mere String or timezone object. 594 end 595 596 ZONES = { 597 "Asia/Tokyo" => ["JST", +9*3600], 598 "America/Los_Angeles" => ["PDT", -7*3600], 599 "Africa/Ndjamena" => ["WAT", +1*3600], 600 } 601 602 def make_timezone(tzname, abbr, utc_offset) 603 self.class::TIME_CLASS.find_timezone(tzname) 604 end 605 606 instance_methods(false).grep(/\Asub(?=test_)/) do |subtest| 607 test = $' 608 ZONES.each_pair do |tzname, (abbr, utc_offset)| 609 define_method("#{test}@#{tzname}") do 610 tz = make_timezone(tzname, abbr, utc_offset) 611 time_class = self.class::TIME_CLASS 612 __send__(subtest, time_class, tz, tz, tzname, abbr, utc_offset) 613 __send__(subtest, time_class, tz, tzname, tzname, abbr, utc_offset) 614 end 615 end 616 end 617 618 instance_methods(false).grep(/\Aname(?=test_)/) do |subtest| 619 test = $' 620 ZONES.each_pair do |tzname, (abbr, utc_offset)| 621 define_method("#{test}@#{tzname}") do 622 time_class = self.class::TIME_CLASS 623 __send__(subtest, time_class, tzname, abbr, utc_offset) 624 end 625 end 626 end 627end 628 629class TestTimeTZ::DummyTZ < Test::Unit::TestCase 630 include TestTimeTZ::WithTZ 631 632 class TIME_CLASS < ::Time 633 ZONES = TestTimeTZ::WithTZ::ZONES 634 def self.find_timezone(tzname) 635 tz = ZONES[tzname] or raise ArgumentError, "Unknown timezone: #{name}" 636 TestTimeTZ::TZ.new(tzname, *tz) 637 end 638 end 639 640 def self.make_timezone(tzname, abbr, utc_offset) 641 TestTimeTZ::TZ.new(tzname, abbr, utc_offset) 642 end 643end 644 645begin 646 require "tzinfo" 647rescue LoadError 648else 649 class TestTimeTZ::GemTZInfo < Test::Unit::TestCase 650 include TestTimeTZ::WithTZ 651 652 class TIME_CLASS < ::Time 653 def self.find_timezone(tzname) 654 TZInfo::Timezone.get(tzname) 655 end 656 end 657 658 def tz 659 @tz ||= TZInfo::Timezone.get(tzname) 660 end 661 end 662 663 def test_fractional_second 664 x = Object.new 665 def x.local_to_utc(t); t + 8*3600; end 666 def x.utc_to_local(t); t - 8*3600; end 667 668 t1 = Time.new(2020,11,11,12,13,14.124r, '-08:00') 669 t2 = Time.new(2020,11,11,12,13,14.124r, x) 670 assert_equal(t1, t2) 671 end 672end 673 674begin 675 require "timezone" 676rescue LoadError 677else 678 class TestTimeTZ::GemTimezone < Test::Unit::TestCase 679 include TestTimeTZ::WithTZ 680 681 class TIME_CLASS < ::Time 682 def self.find_timezone(name) 683 Timezone.fetch(name) 684 end 685 end 686 687 def tz 688 @tz ||= Timezone[tzname] 689 end 690 end 691end 692