@@ -473,27 +473,53 @@ date_zone_to_diff(VALUE str)
473473 s ++ ;
474474 l -- ;
475475
476+ #define out_of_range (v , min , max ) ((v) < (min) || (max) < (v))
476477 hour = STRTOUL (s , & p , 10 );
477478 if (* p == ':' ) {
479+ if (out_of_range (sec , 0 , 59 )) return Qnil ;
478480 s = ++ p ;
479481 min = STRTOUL (s , & p , 10 );
482+ if (out_of_range (min , 0 , 59 )) return Qnil ;
480483 if (* p == ':' ) {
481484 s = ++ p ;
482485 sec = STRTOUL (s , & p , 10 );
486+ if (out_of_range (hour , 0 , 23 )) return Qnil ;
483487 }
484- goto num ;
485488 }
486- if (* p == ',' || * p == '.' ) {
487- char * e = 0 ;
488- p ++ ;
489- min = STRTOUL (p , & e , 10 ) * 3600 ;
489+ else if (* p == ',' || * p == '.' ) {
490+ /* fractional hour */
491+ size_t n ;
492+ int ov ;
493+ /* no over precision for offset; 10**-7 hour = 0.36
494+ * milliseconds should be enough. */
495+ const size_t max_digits = 7 ; /* 36 * 10**7 < 32-bit FIXNUM_MAX */
496+
497+ if (out_of_range (hour , 0 , 23 )) return Qnil ;
498+
499+ n = (s + l ) - ++ p ;
500+ if (n > max_digits ) n = max_digits ;
501+ sec = ruby_scan_digits (p , n , 10 , & n , & ov );
502+ if ((p += n ) < s + l && * p >= ('5' + !(sec & 1 )) && * p <= '9' ) {
503+ /* round half to even */
504+ sec ++ ;
505+ }
506+ sec *= 36 ;
490507 if (sign ) {
491508 hour = - hour ;
492- min = - min ;
509+ sec = - sec ;
510+ }
511+ if (n <= 2 ) {
512+ /* HH.nn or HH.n */
513+ if (n == 1 ) sec *= 10 ;
514+ offset = INT2FIX (sec + hour * 3600 );
515+ }
516+ else {
517+ VALUE denom = rb_int_positive_pow (10 , (int )(n - 2 ));
518+ offset = f_add (rb_rational_new (INT2FIX (sec ), denom ), INT2FIX (hour * 3600 ));
519+ if (rb_rational_den (offset ) == INT2FIX (1 )) {
520+ offset = rb_rational_num (offset );
521+ }
493522 }
494- offset = rb_rational_new (INT2FIX (min ),
495- rb_int_positive_pow (10 , (int )(e - p )));
496- offset = f_add (INT2FIX (hour * 3600 ), offset );
497523 goto ok ;
498524 }
499525 else if (l > 2 ) {
@@ -506,12 +532,11 @@ date_zone_to_diff(VALUE str)
506532 min = ruby_scan_digits (& s [2 - l % 2 ], 2 , 10 , & n , & ov );
507533 if (l >= 5 )
508534 sec = ruby_scan_digits (& s [4 - l % 2 ], 2 , 10 , & n , & ov );
509- goto num ;
510535 }
511- num :
512536 sec += min * 60 + hour * 3600 ;
513537 if (sign ) sec = - sec ;
514538 offset = INT2FIX (sec );
539+ #undef out_of_range
515540 }
516541 }
517542 }
0 commit comments