summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/ruby/test_time.rb36
-rw-r--r--time.c36
2 files changed, 63 insertions, 9 deletions
diff --git a/test/ruby/test_time.rb b/test/ruby/test_time.rb
index ceeb3fbe55..9f79dfd1a7 100644
--- a/test/ruby/test_time.rb
+++ b/test/ruby/test_time.rb
@@ -76,6 +76,42 @@ class TestTime < Test::Unit::TestCase
assert_raise_with_message(ArgumentError, "subsecond expected after dot: 00:56:17. ") {
Time.new("2020-12-25 00:56:17. +0900")
}
+ assert_raise_with_message(ArgumentError, /year must be 2 or 4\+/) {
+ Time.new("021-12-25 00:00:00.123456 +09:00")
+ }
+ assert_raise_with_message(ArgumentError, /fraction min is.*56\./) {
+ Time.new("2020-12-25 00:56. +0900")
+ }
+ assert_raise_with_message(ArgumentError, /fraction hour is.*00\./) {
+ Time.new("2020-12-25 00. +0900")
+ }
+ assert_raise_with_message(ArgumentError, /two digits sec.*:017\b/) {
+ Time.new("2020-12-25 00:56:017 +0900")
+ }
+ assert_raise_with_message(ArgumentError, /two digits sec.*:9\b/) {
+ Time.new("2020-12-25 00:56:9 +0900")
+ }
+ assert_raise_with_message(ArgumentError, /two digits min.*:056\b/) {
+ Time.new("2020-12-25 00:056:17 +0900")
+ }
+ assert_raise_with_message(ArgumentError, /two digits min.*:5\b/) {
+ Time.new("2020-12-25 00:5:17 +0900")
+ }
+ assert_raise_with_message(ArgumentError, /two digits hour.*\b000\b/) {
+ Time.new("2020-12-25 000:56:17 +0900")
+ }
+ assert_raise_with_message(ArgumentError, /two digits hour.*\b0\b/) {
+ Time.new("2020-12-25 0:56:17 +0900")
+ }
+ assert_raise_with_message(ArgumentError, /two digits mday.*\b025\b/) {
+ Time.new("2020-12-025 00:56:17 +0900")
+ }
+ assert_raise_with_message(ArgumentError, /two digits mon.*\b012\b/) {
+ Time.new("2020-012-25 00:56:17 +0900")
+ }
+ assert_raise_with_message(ArgumentError, /two digits mon.*\b1\b/) {
+ Time.new("2020-1-25 00:56:17 +0900")
+ }
end
def test_time_add()
diff --git a/time.c b/time.c
index 593941c29e..d37ef8f728 100644
--- a/time.c
+++ b/time.c
@@ -2449,12 +2449,18 @@ time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VA
}
static int
-two_digits(const char *ptr, const char *end, const char **endp)
+two_digits(const char *ptr, const char *end, const char **endp, const char *name)
{
ssize_t len = end - ptr;
- if (len < 2) return -1;
- if (!ISDIGIT(ptr[0]) || !ISDIGIT(ptr[1])) return -1;
- if ((len > 2) && ISDIGIT(ptr[2])) return -1;
+ if (len < 2 || (!ISDIGIT(ptr[0]) || !ISDIGIT(ptr[1])) ||
+ ((len > 2) && ISDIGIT(ptr[2]))) {
+ VALUE mesg = rb_sprintf("two digits %s is expected", name);
+ if (ptr[-1] == '-' || ptr[-1] == ':') {
+ rb_str_catf(mesg, " after `%c'", ptr[-1]);
+ }
+ rb_str_catf(mesg, ": %.*s", ((len > 10) ? 10 : (int)(end - ptr)) + 1, ptr - 1);
+ rb_exc_raise(rb_exc_new_str(rb_eArgError, mesg));
+ }
*endp = ptr + 2;
return (ptr[0] - '0') * 10 + (ptr[1] - '0');
}
@@ -2487,22 +2493,34 @@ time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone)
if (NIL_P(year)) {
rb_raise(rb_eArgError, "can't parse: %+"PRIsVALUE, str);
}
+ else if (ndigits != 2 && ndigits < 4) {
+ rb_raise(rb_eArgError, "year must be 2 or 4+ digits: %.*s", (int)ndigits, ptr - ndigits);
+ }
do {
#define peek_n(c, n) ((ptr + n < end) && ((unsigned char)ptr[n] == (c)))
#define peek(c) peek_n(c, 0)
#define peekc_n(n) ((ptr + n < end) ? (int)(unsigned char)ptr[n] : -1)
#define peekc() peekc_n(0)
+#define expect_two_digits(x) (x = two_digits(ptr + 1, end, &ptr, #x))
if (!peek('-')) break;
- if ((mon = two_digits(ptr + 1, end, &ptr)) < 0) break;
+ expect_two_digits(mon);
if (!peek('-')) break;
- if ((mday = two_digits(ptr + 1, end, &ptr)) < 0) break;
+ expect_two_digits(mday);
if (!peek(' ') && !peek('T')) break;
const char *const time_part = ptr + 1;
- if ((hour = two_digits(ptr + 1, end, &ptr)) < 0) break;
+ if (!ISDIGIT(peekc_n(1))) break;
+#define nofraction(x) \
+ if (peek('.')) { \
+ rb_raise(rb_eArgError, "fraction %s is not supported: %.*s", #x, \
+ (int)(ptr + 1 - time_part), time_part); \
+ }
+ expect_two_digits(hour);
+ nofraction(hour);
if (!peek(':')) break;
- if ((min = two_digits(ptr + 1, end, &ptr)) < 0) break;
+ expect_two_digits(min);
+ nofraction(min);
if (!peek(':')) break;
- if ((sec = two_digits(ptr + 1, end, &ptr)) < 0) break;
+ expect_two_digits(sec);
if (peek('.')) {
ptr++;
for (ndigits = 0; ndigits < 45 && ISDIGIT(peekc_n(ndigits)); ++ndigits);