From edf7cf016cb285a9f1353cf4f27e420f9f4b08dd Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Fri, 28 Aug 2026 22:32:42 +0900 Subject: [PATCH] Preserve input encoding when parsing time zones Pass the input encoding through `date__strptime` so non-ASCII trailing data is treated as a parse failure instead of raising an encoding error. Fixes #179. --- ext/date/date_core.c | 25 ++++++------------------- ext/date/date_strptime.c | 20 ++++++++++---------- test/date/test_date_strptime.rb | 7 +++++++ 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/ext/date/date_core.c b/ext/date/date_core.c index 72d697c..e9c169f 100644 --- a/ext/date/date_core.c +++ b/ext/date/date_core.c @@ -4515,7 +4515,8 @@ d_new_by_frags(VALUE klass, VALUE hash, VALUE sg) } VALUE date__strptime(const char *str, size_t slen, - const char *fmt, size_t flen, VALUE hash); + const char *fmt, size_t flen, + VALUE hash, rb_encoding *enc); static VALUE date_s__strptime_internal(int argc, VALUE *argv, VALUE klass, @@ -4524,6 +4525,7 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass, VALUE vstr, vfmt, hash; const char *str, *fmt; size_t slen, flen; + rb_encoding *enc; rb_scan_args(argc, argv, "11", &vstr, &vfmt); @@ -4537,33 +4539,18 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass, if (argc < 2) { fmt = default_fmt; flen = strlen(default_fmt); + enc = rb_enc_get(vstr); } else { if (!rb_enc_str_asciicompat_p(vfmt)) rb_raise(rb_eArgError, "format should have ASCII compatible encoding"); + enc = rb_enc_check(vstr, vfmt); fmt = RSTRING_PTR(vfmt); flen = RSTRING_LEN(vfmt); } hash = rb_hash_new(); - if (NIL_P(date__strptime(str, slen, fmt, flen, hash))) - return Qnil; - - { - VALUE zone = ref_hash("zone"); - VALUE left = ref_hash("leftover"); - - if (!NIL_P(zone)) { - rb_enc_copy(zone, vstr); - set_hash("zone", zone); - } - if (!NIL_P(left)) { - rb_enc_copy(left, vstr); - set_hash("leftover", left); - } - } - - return hash; + return date__strptime(str, slen, fmt, flen, hash, enc); } /* diff --git a/ext/date/date_strptime.c b/ext/date/date_strptime.c index 1dde5fa..0ad03a0 100644 --- a/ext/date/date_strptime.c +++ b/ext/date/date_strptime.c @@ -41,7 +41,6 @@ static const int ABBREVIATED_MONTH_NAME_LENGTH = 3; #define f_match(r,s) rb_funcall(r, rb_intern("match"), 1, s) #define f_aref(o,i) rb_funcall(o, rb_intern("[]"), 1, i) -#define f_end(o,i) rb_funcall(o, rb_intern("end"), 1, i) #define issign(c) ((c) == '-' || (c) == '+') @@ -144,7 +143,7 @@ valid_range_p(VALUE v, int a, int b) do { \ size_t l; \ l = date__strptime_internal(&str[si], slen - si, \ - fmt, sizeof fmt - 1, hash); \ + fmt, sizeof fmt - 1, hash, enc); \ if (fail_p()) \ return 0; \ si += l; \ @@ -160,7 +159,8 @@ head_match_p(size_t len, const char *name, const char *str, size_t slen, size_t static size_t date__strptime_internal(const char *str, size_t slen, - const char *fmt, size_t flen, VALUE hash) + const char *fmt, size_t flen, + VALUE hash, rb_encoding *enc) { size_t si, fi; int c; @@ -597,15 +597,14 @@ date__strptime_internal(const char *str, size_t slen, b = rb_backref_get(); rb_match_busy(b); - m = f_match(pat, rb_usascii_str_new(&str[si], slen - si)); + m = f_match(pat, rb_enc_str_new(&str[si], slen - si, enc)); if (!NIL_P(m)) { - VALUE s, l, o; + VALUE s, o; s = rb_reg_nth_match(1, m); - l = f_end(m, INT2FIX(0)); o = date_zone_to_diff(s); - si += NUM2LONG(l); + si += RSTRING_LEN(s); set_hash("zone", s); set_hash("offset", o); rb_backref_set(b); @@ -654,12 +653,13 @@ date__strptime_internal(const char *str, size_t slen, VALUE date__strptime(const char *str, size_t slen, - const char *fmt, size_t flen, VALUE hash) + const char *fmt, size_t flen, + VALUE hash, rb_encoding *enc) { size_t si; VALUE cent, merid; - si = date__strptime_internal(str, slen, fmt, flen, hash); + si = date__strptime_internal(str, slen, fmt, flen, hash, enc); if (fail_p()) return Qnil; @@ -667,7 +667,7 @@ date__strptime(const char *str, size_t slen, if (slen > si) { VALUE s; - s = rb_usascii_str_new(&str[si], slen - si); + s = rb_enc_str_new(&str[si], slen - si, enc); set_hash("leftover", s); } diff --git a/test/date/test_date_strptime.rb b/test/date/test_date_strptime.rb index 6aa7db2..f68f749 100644 --- a/test/date/test_date_strptime.rb +++ b/test/date/test_date_strptime.rb @@ -533,4 +533,11 @@ def to_str assert_equal(6, d[:mon]) assert_equal(1, d[:mday]) end + + def test_nonascii_string + nonalpha = "\u{2600 fe0f}" + s = "2011-10-05T22:26:12#{nonalpha}" + + assert_nil(DateTime._strptime(s)) + end end