From 149e414ed529d27aaeb0543bc133e08c782d8d41 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 23 Apr 2019 13:09:34 +0900 Subject: [PATCH] Initialize DST flag * time.c (zone_timelocal): initialize DST flag by asking the timezone object. [Bug #15988] --- spec/ruby/core/time/fixtures/classes.rb | 2 +- test/ruby/test_time_tz.rb | 80 ++++++++++++++++++++----- time.c | 13 ++++ 3 files changed, 78 insertions(+), 17 deletions(-) diff --git a/spec/ruby/core/time/fixtures/classes.rb b/spec/ruby/core/time/fixtures/classes.rb index ece7ed2bcab065..a648171b324796 100644 --- a/spec/ruby/core/time/fixtures/classes.rb +++ b/spec/ruby/core/time/fixtures/classes.rb @@ -79,7 +79,7 @@ def name class TimeWithFindTimezone < Time def self.find_timezone(name) - TimezoneWithName.new(name: name.to_s, offset: -10*60*60) + TimezoneWithName.new(name: name.to_s, offset: 5*3600+30*60) end end end diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb index 68ce845796e83c..2a460aad7a5c34 100644 --- a/test/ruby/test_time_tz.rb +++ b/test/ruby/test_time_tz.rb @@ -544,28 +544,47 @@ def self.gen_variational_zdump_test(hint, data) End class TZ - attr_reader :name, :offset + attr_reader :name, :offset, :offset2 - def initialize(name, abbr, offset) + def initialize(name, abbr, offset, abbr2 = nil, offset2 = nil) @name = name @abbr = abbr @offset = offset + @abbr2 = abbr2 + @offset2 = offset2 + end + + def dst?(t) + return false unless @offset2 + case t when Integer + return nil + end + case t.mon + when 4..9 + true + else + false + end + end + + def offset(t) + (dst?(t) ? @offset2 : @offset) end def local_to_utc(t) - t - @offset + t - offset(t) end def utc_to_local(t) - t + @offset + t + offset(t) end def abbr(t) - @abbr + dst?(t) ? @abbr2 : @abbr end def ==(other) - @name == other.name and @abbr == other.abbr(0) and @offset == other.offset + @name == other.name and abbr(0) == other.abbr(0) and offset(0) == other.offset(0) end def inspect @@ -576,33 +595,48 @@ def inspect module TestTimeTZ::WithTZ def subtest_new(time_class, tz, tzarg, tzname, abbr, utc_offset) + abbr, abbr2 = *abbr + utc_offset, utc_offset2 = *utc_offset t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg) + utc_offset, abbr = utc_offset2, abbr2 if tz.dst?(t) assert_equal([2018, 9, 1, 12, 0, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone]) h, m = (-utc_offset / 60).divmod(60) assert_equal(time_class.utc(2018, 9, 1, 12+h, m, 0).to_i, t.to_i) end def subtest_now(time_class, tz, tzarg, tzname, abbr, utc_offset) + abbr, abbr2 = *abbr + utc_offset, utc_offset2 = *utc_offset t = time_class.now(in: tzarg) assert_equal(tz, t.zone) end def subtest_getlocal(time_class, tz, tzarg, tzname, abbr, utc_offset) - t = time_class.utc(2018, 9, 1, 12, 0, 0).getlocal(tzarg) + abbr, abbr2 = *abbr + utc_offset, utc_offset2 = *utc_offset + utc = time_class.utc(2018, 9, 1, 12, 0, 0) + utc_offset, abbr = utc_offset2, abbr2 if tz.dst?(utc) + t = utc.getlocal(tzarg) h, m = (utc_offset / 60).divmod(60) assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone]) assert_equal(time_class.utc(2018, 9, 1, 12, 0, 0), t) end def subtest_strftime(time_class, tz, tzarg, tzname, abbr, utc_offset) + abbr, abbr2 = *abbr + utc_offset, utc_offset2 = *utc_offset t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg) + utc_offset, abbr = utc_offset2, abbr2 if tz.dst?(t) h, m = (utc_offset.abs / 60).divmod(60) h = -h if utc_offset < 0 assert_equal("%+.2d%.2d %s" % [h, m, abbr], t.strftime("%z %Z")) end def subtest_plus(time_class, tz, tzarg, tzname, abbr, utc_offset) + abbr, abbr2 = *abbr + utc_offset, utc_offset2 = *utc_offset t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg) + 4000 + utc_offset, abbr = utc_offset2, abbr2 if tz.dst?(t) assert_equal([2018, 9, 1, 13, 6, 40, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone]) m, s = (4000-utc_offset).divmod(60) h, m = m.divmod(60) @@ -610,8 +644,11 @@ def subtest_plus(time_class, tz, tzarg, tzname, abbr, utc_offset) end def subtest_at(time_class, tz, tzarg, tzname, abbr, utc_offset) - h, m = (utc_offset / 60).divmod(60) + abbr, abbr2 = *abbr + utc_offset, utc_offset2 = *utc_offset utc = time_class.utc(2018, 9, 1, 12, 0, 0) + utc_offset, abbr = utc_offset2, abbr2 if tz.dst?(utc) + h, m = (utc_offset / 60).divmod(60) t = time_class.at(utc, in: tzarg) assert_equal([2018, 9, 1, 12+h, m, 0, tz], [t.year, t.mon, t.mday, t.hour, t.min, t.sec, t.zone]) assert_equal(utc.to_i, t.to_i) @@ -622,12 +659,15 @@ def subtest_at(time_class, tz, tzarg, tzname, abbr, utc_offset) end def subtest_marshal(time_class, tz, tzarg, tzname, abbr, utc_offset) + abbr, abbr2 = *abbr + utc_offset, utc_offset2 = *utc_offset t = time_class.new(2018, 9, 1, 12, 0, 0, tzarg) t2 = Marshal.load(Marshal.dump(t)) assert_equal(t, t2) assert_equal(t.utc_offset, t2.utc_offset) assert_equal(t.utc_offset, (t2+1).utc_offset) assert_instance_of(t.zone.class, t2.zone) + assert_equal(t.dst?, t2.dst?) end def test_invalid_zone @@ -652,22 +692,30 @@ def nametest_marshal_compatibility(time_class, tzname, abbr, utc_offset) ZONES = { "Asia/Tokyo" => ["JST", +9*3600], - "America/Los_Angeles" => ["PDT", -7*3600], + "America/Los_Angeles" => ["PST", -8*3600, "PDT", -7*3600], "Africa/Ndjamena" => ["WAT", +1*3600], } - def make_timezone(tzname, abbr, utc_offset) + def make_timezone(tzname, abbr, utc_offset, abbr2 = nil, utc_offset2 = nil) self.class::TIME_CLASS.find_timezone(tzname) end + def subtest_dst?(time_class, tz, tzarg, tzname, abbr, utc_offset) + t = time_class.new(2018, 6, 22, 12, 0, 0, tzarg) + return unless tz.dst?(t) + assert_predicate t, :dst? + t = time_class.new(2018, 12, 22, 12, 0, 0, tzarg) + assert_not_predicate t, :dst? + end + instance_methods(false).grep(/\Asub(?=test_)/) do |subtest| test = $' - ZONES.each_pair do |tzname, (abbr, utc_offset)| + ZONES.each_pair do |tzname, (abbr, utc_offset, abbr2, utc_offset2)| define_method("#{test}@#{tzname}") do - tz = make_timezone(tzname, abbr, utc_offset) + tz = make_timezone(tzname, abbr, utc_offset, abbr2, utc_offset2) time_class = self.class::TIME_CLASS - __send__(subtest, time_class, tz, tz, tzname, abbr, utc_offset) - __send__(subtest, time_class, tz, tzname, tzname, abbr, utc_offset) + __send__(subtest, time_class, tz, tz, tzname, [abbr, abbr2], [utc_offset, utc_offset2]) + __send__(subtest, time_class, tz, tzname, tzname, [abbr, abbr2], [utc_offset, utc_offset2]) end end end @@ -694,8 +742,8 @@ def self.find_timezone(tzname) end end - def self.make_timezone(tzname, abbr, utc_offset) - TestTimeTZ::TZ.new(tzname, abbr, utc_offset) + def self.make_timezone(tzname, abbr, utc_offset, abbr2 = nil, utc_offset2 = nil) + TestTimeTZ::TZ.new(tzname, abbr, utc_offset, abbr2, utc_offset2) end end diff --git a/time.c b/time.c index 18f1b246f04167..ed7928848c5361 100644 --- a/time.c +++ b/time.c @@ -2201,6 +2201,16 @@ extract_vtm(VALUE time, struct vtm *vtm, VALUE subsecx) return t; } +static void +zone_set_dst(VALUE zone, struct time_object *tobj, VALUE tm) +{ + ID id_dst_p; + VALUE dst; + CONST_ID(id_dst_p, "dst?"); + dst = rb_check_funcall(zone, id_dst_p, 1, &tm); + tobj->vtm.isdst = (dst != Qundef && RTEST(dst)); +} + static int zone_timelocal(VALUE zone, VALUE time) { @@ -2220,6 +2230,7 @@ zone_timelocal(VALUE zone, VALUE time) s = wadd(s, v2w(tobj->vtm.subsecx)); } tobj->timew = s; + zone_set_dst(zone, tobj, tm); return 1; } @@ -2239,6 +2250,7 @@ zone_localtime(VALUE zone, VALUE time) s = extract_vtm(local, &tobj->vtm, subsecx); tobj->tm_got = 1; zone_set_offset(zone, tobj, s, t); + zone_set_dst(zone, tobj, tm); return 1; } @@ -5323,6 +5335,7 @@ end_submicro: ; if (!NIL_P(zone)) { zone = mload_zone(time, zone); tobj->vtm.zone = zone; + zone_localtime(zone, time); } return time;