Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 36 additions & 11 deletions ext/date/date_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -473,27 +473,53 @@ date_zone_to_diff(VALUE str)
s++;
l--;

#define out_of_range(v, min, max) ((v) < (min) || (max) < (v))
hour = STRTOUL(s, &p, 10);
if (*p == ':') {
if (out_of_range(sec, 0, 59)) return Qnil;
s = ++p;
min = STRTOUL(s, &p, 10);
if (out_of_range(min, 0, 59)) return Qnil;
if (*p == ':') {
s = ++p;
sec = STRTOUL(s, &p, 10);
if (out_of_range(hour, 0, 23)) return Qnil;
}
goto num;
}
if (*p == ',' || *p == '.') {
char *e = 0;
p++;
min = STRTOUL(p, &e, 10) * 3600;
else if (*p == ',' || *p == '.') {
/* fractional hour */
size_t n;
int ov;
/* no over precision for offset; 10**-7 hour = 0.36
* milliseconds should be enough. */
const size_t max_digits = 7; /* 36 * 10**7 < 32-bit FIXNUM_MAX */

if (out_of_range(hour, 0, 23)) return Qnil;

n = (s + l) - ++p;
if (n > max_digits) n = max_digits;
sec = ruby_scan_digits(p, n, 10, &n, &ov);
if ((p += n) < s + l && *p >= ('5' + !(sec & 1)) && *p <= '9') {
/* round half to even */
sec++;
}
sec *= 36;
if (sign) {
hour = -hour;
min = -min;
sec = -sec;
}
if (n <= 2) {
/* HH.nn or HH.n */
if (n == 1) sec *= 10;
offset = INT2FIX(sec + hour * 3600);
}
else {
VALUE denom = rb_int_positive_pow(10, (int)(n - 2));
offset = f_add(rb_rational_new(INT2FIX(sec), denom), INT2FIX(hour * 3600));
if (rb_rational_den(offset) == INT2FIX(1)) {
offset = rb_rational_num(offset);
}
}
offset = rb_rational_new(INT2FIX(min),
rb_int_positive_pow(10, (int)(e - p)));
offset = f_add(INT2FIX(hour * 3600), offset);
goto ok;
}
else if (l > 2) {
Expand All @@ -506,12 +532,11 @@ date_zone_to_diff(VALUE str)
min = ruby_scan_digits(&s[2 - l % 2], 2, 10, &n, &ov);
if (l >= 5)
sec = ruby_scan_digits(&s[4 - l % 2], 2, 10, &n, &ov);
goto num;
}
num:
sec += min * 60 + hour * 3600;
if (sign) sec = -sec;
offset = INT2FIX(sec);
#undef out_of_range
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions test/date/test_date_strptime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,10 @@ def test__strptime__3

[['fri1feb034pm+5', '%a%d%b%y%H%p%Z'], [2003,2,1,16,nil,nil,'+5',5*3600,5]],
[['E. Australia Standard Time', '%Z'], [nil,nil,nil,nil,nil,nil,'E. Australia Standard Time',10*3600,nil], __LINE__],

# out of range
[['+0.9999999999999999999999', '%Z'], [nil,nil,nil,nil,nil,nil,'+0.9999999999999999999999',+1*3600,nil], __LINE__],
[['+9999999999999999999999.0', '%Z'], [nil,nil,nil,nil,nil,nil,'+9999999999999999999999.0',nil,nil], __LINE__],
].each do |x, y|
h = Date._strptime(*x)
a = h.values_at(:year,:mon,:mday,:hour,:min,:sec,:zone,:offset,:wday)
Expand Down