Skip to content

Commit edf7cf0

Browse files
committed
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.
1 parent 2140246 commit edf7cf0

3 files changed

Lines changed: 23 additions & 29 deletions

File tree

ext/date/date_core.c

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4515,7 +4515,8 @@ d_new_by_frags(VALUE klass, VALUE hash, VALUE sg)
45154515
}
45164516

45174517
VALUE date__strptime(const char *str, size_t slen,
4518-
const char *fmt, size_t flen, VALUE hash);
4518+
const char *fmt, size_t flen,
4519+
VALUE hash, rb_encoding *enc);
45194520

45204521
static VALUE
45214522
date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
@@ -4524,6 +4525,7 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
45244525
VALUE vstr, vfmt, hash;
45254526
const char *str, *fmt;
45264527
size_t slen, flen;
4528+
rb_encoding *enc;
45274529

45284530
rb_scan_args(argc, argv, "11", &vstr, &vfmt);
45294531

@@ -4537,33 +4539,18 @@ date_s__strptime_internal(int argc, VALUE *argv, VALUE klass,
45374539
if (argc < 2) {
45384540
fmt = default_fmt;
45394541
flen = strlen(default_fmt);
4542+
enc = rb_enc_get(vstr);
45404543
}
45414544
else {
45424545
if (!rb_enc_str_asciicompat_p(vfmt))
45434546
rb_raise(rb_eArgError,
45444547
"format should have ASCII compatible encoding");
4548+
enc = rb_enc_check(vstr, vfmt);
45454549
fmt = RSTRING_PTR(vfmt);
45464550
flen = RSTRING_LEN(vfmt);
45474551
}
45484552
hash = rb_hash_new();
4549-
if (NIL_P(date__strptime(str, slen, fmt, flen, hash)))
4550-
return Qnil;
4551-
4552-
{
4553-
VALUE zone = ref_hash("zone");
4554-
VALUE left = ref_hash("leftover");
4555-
4556-
if (!NIL_P(zone)) {
4557-
rb_enc_copy(zone, vstr);
4558-
set_hash("zone", zone);
4559-
}
4560-
if (!NIL_P(left)) {
4561-
rb_enc_copy(left, vstr);
4562-
set_hash("leftover", left);
4563-
}
4564-
}
4565-
4566-
return hash;
4553+
return date__strptime(str, slen, fmt, flen, hash, enc);
45674554
}
45684555

45694556
/*

ext/date/date_strptime.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ static const int ABBREVIATED_MONTH_NAME_LENGTH = 3;
4141

4242
#define f_match(r,s) rb_funcall(r, rb_intern("match"), 1, s)
4343
#define f_aref(o,i) rb_funcall(o, rb_intern("[]"), 1, i)
44-
#define f_end(o,i) rb_funcall(o, rb_intern("end"), 1, i)
4544

4645
#define issign(c) ((c) == '-' || (c) == '+')
4746

@@ -144,7 +143,7 @@ valid_range_p(VALUE v, int a, int b)
144143
do { \
145144
size_t l; \
146145
l = date__strptime_internal(&str[si], slen - si, \
147-
fmt, sizeof fmt - 1, hash); \
146+
fmt, sizeof fmt - 1, hash, enc); \
148147
if (fail_p()) \
149148
return 0; \
150149
si += l; \
@@ -160,7 +159,8 @@ head_match_p(size_t len, const char *name, const char *str, size_t slen, size_t
160159

161160
static size_t
162161
date__strptime_internal(const char *str, size_t slen,
163-
const char *fmt, size_t flen, VALUE hash)
162+
const char *fmt, size_t flen,
163+
VALUE hash, rb_encoding *enc)
164164
{
165165
size_t si, fi;
166166
int c;
@@ -597,15 +597,14 @@ date__strptime_internal(const char *str, size_t slen,
597597

598598
b = rb_backref_get();
599599
rb_match_busy(b);
600-
m = f_match(pat, rb_usascii_str_new(&str[si], slen - si));
600+
m = f_match(pat, rb_enc_str_new(&str[si], slen - si, enc));
601601

602602
if (!NIL_P(m)) {
603-
VALUE s, l, o;
603+
VALUE s, o;
604604

605605
s = rb_reg_nth_match(1, m);
606-
l = f_end(m, INT2FIX(0));
607606
o = date_zone_to_diff(s);
608-
si += NUM2LONG(l);
607+
si += RSTRING_LEN(s);
609608
set_hash("zone", s);
610609
set_hash("offset", o);
611610
rb_backref_set(b);
@@ -654,20 +653,21 @@ date__strptime_internal(const char *str, size_t slen,
654653

655654
VALUE
656655
date__strptime(const char *str, size_t slen,
657-
const char *fmt, size_t flen, VALUE hash)
656+
const char *fmt, size_t flen,
657+
VALUE hash, rb_encoding *enc)
658658
{
659659
size_t si;
660660
VALUE cent, merid;
661661

662-
si = date__strptime_internal(str, slen, fmt, flen, hash);
662+
si = date__strptime_internal(str, slen, fmt, flen, hash, enc);
663663

664664
if (fail_p())
665665
return Qnil;
666666

667667
if (slen > si) {
668668
VALUE s;
669669

670-
s = rb_usascii_str_new(&str[si], slen - si);
670+
s = rb_enc_str_new(&str[si], slen - si, enc);
671671
set_hash("leftover", s);
672672
}
673673

test/date/test_date_strptime.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,4 +533,11 @@ def to_str
533533
assert_equal(6, d[:mon])
534534
assert_equal(1, d[:mday])
535535
end
536+
537+
def test_nonascii_string
538+
nonalpha = "\u{2600 fe0f}"
539+
s = "2011-10-05T22:26:12#{nonalpha}"
540+
541+
assert_nil(DateTime._strptime(s))
542+
end
536543
end

0 commit comments

Comments
 (0)