From 9441f3f97087a4325ee80911859d37da41fa5050 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 4 Jan 2021 00:07:10 +0900 Subject: [PATCH] Allow UTC offset without colons per ISO-8601 [Bug #17504] --- test/ruby/test_time.rb | 6 ++++++ time.c | 44 ++++++++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb index e5da0d6343..b264e7f73c 100644 --- a/test/ruby/test_time.rb +++ b/test/ruby/test_time.rb @@ -1223,6 +1223,12 @@ class TestTime < Test::Unit::TestCase assert_equal [00, 00, 9, 1, 1, 2000], t.getlocal("+09:00").to_a[0, 6] assert_equal [20, 29, 21, 31, 12, 1999], t.getlocal("-02:30:40").to_a[0, 6] assert_equal [35, 10, 9, 1, 1, 2000], t.getlocal("+09:10:35").to_a[0, 6] + assert_equal [00, 30, 21, 31, 12, 1999], t.getlocal("-0230").to_a[0, 6] + assert_equal [00, 00, 9, 1, 1, 2000], t.getlocal("+0900").to_a[0, 6] + assert_equal [20, 29, 21, 31, 12, 1999], t.getlocal("-023040").to_a[0, 6] + assert_equal [35, 10, 9, 1, 1, 2000], t.getlocal("+091035").to_a[0, 6] + assert_raise(ArgumentError) {t.getlocal("-02:3040")} + assert_raise(ArgumentError) {t.getlocal("+0910:35")} end def test_getlocal_nil diff --git a/time.c b/time.c index e7a211baa5..6d3352ef92 100644 --- a/time.c +++ b/time.c @@ -2087,7 +2087,7 @@ utc_offset_arg(VALUE arg) VALUE tmp; if (!NIL_P(tmp = rb_check_string_type(arg))) { int n = 0; - char *s = RSTRING_PTR(tmp); + const char *s = RSTRING_PTR(tmp), *min = NULL, *sec = NULL; if (!rb_enc_str_asciicompat_p(tmp)) { goto invalid_utc_offset; } @@ -2115,24 +2115,38 @@ utc_offset_arg(VALUE arg) if (STRNCASECMP("UTC", s, 3) == 0) { return UTC_ZONE; } - goto invalid_utc_offset; - case 9: - if (s[6] != ':') goto invalid_utc_offset; - if (!ISDIGIT(s[7]) || !ISDIGIT(s[8])) goto invalid_utc_offset; - n += (s[7] * 10 + s[8] - '0' * 11); - /* fall through */ - case 6: - if (s[0] != '+' && s[0] != '-') goto invalid_utc_offset; - if (!ISDIGIT(s[1]) || !ISDIGIT(s[2])) goto invalid_utc_offset; - if (s[3] != ':') goto invalid_utc_offset; - if (!ISDIGIT(s[4]) || !ISDIGIT(s[5])) goto invalid_utc_offset; - if (s[4] > '5') goto invalid_utc_offset; - break; + break; /* +HH */ + case 5: /* +HHMM */ + min = s+3; + break; + case 6: /* +HH:MM */ + min = s+4; + break; + case 7: /* +HHMMSS */ + sec = s+5; + min = s+3; + break; + case 9: /* +HH:MM:SS */ + sec = s+7; + min = s+4; + break; default: goto invalid_utc_offset; } + if (sec) { + if (sec == s+7 && *(sec-1) != ':') goto invalid_utc_offset; + if (!ISDIGIT(sec[0]) || !ISDIGIT(sec[1])) goto invalid_utc_offset; + n += (sec[0] * 10 + sec[1] - '0' * 11); + } + if (min) { + if (min == s+4 && *(min-1) != ':') goto invalid_utc_offset; + if (!ISDIGIT(min[0]) || !ISDIGIT(min[1])) goto invalid_utc_offset; + if (min[0] > '5') goto invalid_utc_offset; + n += (min[0] * 10 + min[1] - '0' * 11) * 60; + } + if (s[0] != '+' && s[0] != '-') goto invalid_utc_offset; + if (!ISDIGIT(s[1]) || !ISDIGIT(s[2])) goto invalid_utc_offset; n += (s[1] * 10 + s[2] - '0' * 11) * 3600; - n += (s[4] * 10 + s[5] - '0' * 11) * 60; if (s[0] == '-') n = -n; return INT2FIX(n);