From 011d4c57d21220249600dfb76db84840550da019 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 7 Nov 2022 13:23:23 +0900 Subject: [PATCH] [Bug #19106] Normalize time at 24:00:00 with a timezone object --- test/ruby/test_time_tz.rb | 5 +++++ time.c | 24 ++++++++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/test/ruby/test_time_tz.rb b/test/ruby/test_time_tz.rb index 6ae12dea5d..6fdb95bafe 100644 --- a/test/ruby/test_time_tz.rb +++ b/test/ruby/test_time_tz.rb @@ -612,6 +612,11 @@ module TestTimeTZ::WithTZ assert_raise(ArgumentError) {time_class.new(2018, 9, 1, 12, 0, 0, tzarg, in: tzarg)} end + def subtest_hour24(time_class, tz, tzarg, tzname, abbr, utc_offset) + t = time_class.new(2000, 1, 1, 24, 0, 0, tzarg) + assert_equal([0, 0, 0, 2, 1, 2000], [t.sec, t.min, t.hour, t.mday, t.mon, t.year]) + end + def subtest_now(time_class, tz, tzarg, tzname, abbr, utc_offset) t = time_class.now(in: tzarg) assert_equal(tz, t.zone) diff --git a/time.c b/time.c index b16a086537..b3604dd488 100644 --- a/time.c +++ b/time.c @@ -2331,6 +2331,19 @@ find_timezone(VALUE time, VALUE zone) return rb_check_funcall_default(klass, id_find_timezone, 1, &zone, Qnil); } +/* Turn the special case 24:00:00 of already validated vtm into + * 00:00:00 the next day */ +static void +vtm_day_wraparound(struct vtm *vtm) +{ + if (vtm->hour < 24) return; + + /* Assuming UTC and no care of DST, just reset hour and advance + * date, not to discard the validated vtm. */ + vtm->hour = 0; + vtm_add_day(vtm, 1); +} + static VALUE time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VALUE mday, VALUE hour, VALUE min, VALUE sec, VALUE zone) { @@ -2386,6 +2399,7 @@ time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VA if (!NIL_P(zone)) { tobj->timew = timegmw(&vtm); + vtm_day_wraparound(&vtm); tobj->vtm = vtm; tobj->tm_got = 1; TZMODE_SET_LOCALTIME(tobj); @@ -2400,13 +2414,7 @@ time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VA if (utc == UTC_ZONE) { tobj->timew = timegmw(&vtm); - if (vtm.hour == 24) { /* special case: 24:00:00 only */ - /* Since no need to take care of DST in UTC, just reset - * hour and advance date, not to discard the validated - * vtm. */ - vtm.hour = 0; - vtm_add_day(&vtm, 1); - } + vtm_day_wraparound(&vtm); tobj->vtm = vtm; tobj->tm_got = 1; TZMODE_SET_UTC(tobj); @@ -4105,7 +4113,7 @@ time_inspect(VALUE time) GetTimeval(time, tobj); str = strftimev("%Y-%m-%d %H:%M:%S", time, rb_usascii_encoding()); subsec = w2v(wmod(tobj->timew, WINT2FIXWV(TIME_SCALE))); - if (FIXNUM_P(subsec) && FIX2LONG(subsec) == 0) { + if (subsec == INT2FIX(0)) { } else if (FIXNUM_P(subsec) && FIX2LONG(subsec) < TIME_SCALE) { long len;