[Feature #18033] Add precision:
option
Which limits the precision of subsecond. Defaulted to 9, that means nanosecond.
This commit is contained in:
parent
67c589afa0
commit
9515179d74
Notes:
git
2022-12-16 13:53:19 +00:00
@ -73,6 +73,9 @@ class TestTime < Test::Unit::TestCase
|
||||
assert_equal(Time.new(2021, 12, 25, in: "+09:00"), Time.new("2021-12-25+09:00"))
|
||||
|
||||
assert_equal(0.123456r, Time.new("2021-12-25 00:00:00.123456 +09:00").subsec)
|
||||
assert_equal(0.123456789r, Time.new("2021-12-25 00:00:00.123456789876 +09:00").subsec)
|
||||
assert_equal(0.123r, Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: 3).subsec)
|
||||
assert_equal(0.123456789876r, Time.new("2021-12-25 00:00:00.123456789876 +09:00", precision: nil).subsec)
|
||||
assert_raise_with_message(ArgumentError, "subsecond expected after dot: 00:56:17. ") {
|
||||
Time.new("2020-12-25 00:56:17. +0900")
|
||||
}
|
||||
|
79
time.c
79
time.c
@ -515,17 +515,19 @@ wmod(wideval_t wx, wideval_t wy)
|
||||
}
|
||||
|
||||
static VALUE
|
||||
num_exact(VALUE v)
|
||||
num_exact_check(VALUE v)
|
||||
{
|
||||
VALUE tmp;
|
||||
|
||||
switch (TYPE(v)) {
|
||||
case T_FIXNUM:
|
||||
case T_BIGNUM:
|
||||
return v;
|
||||
tmp = v;
|
||||
break;
|
||||
|
||||
case T_RATIONAL:
|
||||
return rb_rational_canonicalize(v);
|
||||
tmp = rb_rational_canonicalize(v);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!UNDEF_P(tmp = rb_check_funcall(v, idTo_r, 0, NULL))) {
|
||||
@ -535,10 +537,11 @@ num_exact(VALUE v)
|
||||
/* FALLTHROUGH */
|
||||
}
|
||||
else if (RB_INTEGER_TYPE_P(tmp)) {
|
||||
return tmp;
|
||||
break;
|
||||
}
|
||||
else if (RB_TYPE_P(tmp, T_RATIONAL)) {
|
||||
return rb_rational_canonicalize(tmp);
|
||||
tmp = rb_rational_canonicalize(tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!NIL_P(tmp = rb_check_to_int(v))) {
|
||||
@ -547,9 +550,26 @@ num_exact(VALUE v)
|
||||
|
||||
case T_NIL:
|
||||
case T_STRING:
|
||||
rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into an exact number",
|
||||
rb_obj_class(v));
|
||||
return Qnil;
|
||||
}
|
||||
ASSUME(!NIL_P(tmp));
|
||||
return tmp;
|
||||
}
|
||||
|
||||
NORETURN(static void num_exact_fail(VALUE v));
|
||||
static void
|
||||
num_exact_fail(VALUE v)
|
||||
{
|
||||
rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into an exact number",
|
||||
rb_obj_class(v));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
num_exact(VALUE v)
|
||||
{
|
||||
VALUE num = num_exact_check(v);
|
||||
if (NIL_P(num)) num_exact_fail(v);
|
||||
return num;
|
||||
}
|
||||
|
||||
/* time_t */
|
||||
@ -2349,13 +2369,13 @@ vtm_day_wraparound(struct vtm *vtm)
|
||||
vtm_add_day(vtm, 1);
|
||||
}
|
||||
|
||||
static VALUE time_init_vtm(VALUE time, struct vtm vtm, VALUE zone);
|
||||
|
||||
static VALUE
|
||||
time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VALUE mday,
|
||||
VALUE hour, VALUE min, VALUE sec, VALUE subsec, VALUE zone)
|
||||
VALUE hour, VALUE min, VALUE sec, VALUE zone)
|
||||
{
|
||||
struct vtm vtm;
|
||||
VALUE utc = Qnil;
|
||||
struct time_object *tobj;
|
||||
|
||||
vtm.wday = VTM_WDAY_INITVAL;
|
||||
vtm.yday = 0;
|
||||
@ -2375,16 +2395,21 @@ time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VA
|
||||
vtm.sec = 0;
|
||||
vtm.subsecx = INT2FIX(0);
|
||||
}
|
||||
else if (!NIL_P(subsec)) {
|
||||
vtm.sec = obj2ubits(sec, 6);
|
||||
vtm.subsecx = subsec;
|
||||
}
|
||||
else {
|
||||
VALUE subsecx;
|
||||
vtm.sec = obj2subsecx(sec, &subsecx);
|
||||
vtm.subsecx = subsecx;
|
||||
}
|
||||
|
||||
return time_init_vtm(time, vtm, zone);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
time_init_vtm(VALUE time, struct vtm vtm, VALUE zone)
|
||||
{
|
||||
VALUE utc = Qnil;
|
||||
struct time_object *tobj;
|
||||
|
||||
vtm.isdst = VTM_ISDST_INITVAL;
|
||||
vtm.utc_offset = Qnil;
|
||||
const VALUE arg = zone;
|
||||
@ -2474,7 +2499,7 @@ parse_int(const char *ptr, const char *end, const char **endp, size_t *ndigits,
|
||||
}
|
||||
|
||||
static VALUE
|
||||
time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone)
|
||||
time_init_parse(rb_execution_context_t *ec, VALUE klass, VALUE str, VALUE zone, VALUE precision)
|
||||
{
|
||||
if (NIL_P(str = rb_check_string_type(str))) return Qnil;
|
||||
if (!rb_enc_str_asciicompat_p(str)) {
|
||||
@ -2487,6 +2512,7 @@ time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone)
|
||||
VALUE year = Qnil, subsec = Qnil;
|
||||
int mon = -1, mday = -1, hour = -1, min = -1, sec = -1;
|
||||
size_t ndigits;
|
||||
size_t prec = NIL_P(precision) ? SIZE_MAX : NUM2SIZET(precision);
|
||||
|
||||
while ((ptr < end) && ISSPACE(*ptr)) ptr++;
|
||||
year = parse_int(ptr, end, &ptr, &ndigits, true);
|
||||
@ -2523,7 +2549,7 @@ time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone)
|
||||
expect_two_digits(sec);
|
||||
if (peek('.')) {
|
||||
ptr++;
|
||||
for (ndigits = 0; ndigits < 45 && ISDIGIT(peekc_n(ndigits)); ++ndigits);
|
||||
for (ndigits = 0; ndigits < prec && ISDIGIT(peekc_n(ndigits)); ++ndigits);
|
||||
if (!ndigits) {
|
||||
int clen = rb_enc_precise_mbclen(ptr, end, rb_enc_get(str));
|
||||
if (clen < 0) clen = 0;
|
||||
@ -2564,14 +2590,19 @@ time_init_parse(rb_execution_context_t *ec, VALUE time, VALUE str, VALUE zone)
|
||||
}
|
||||
}
|
||||
|
||||
#define non_negative(x) ((x) < 0 ? Qnil : INT2FIX(x))
|
||||
return time_init_args(ec, time, year,
|
||||
non_negative(mon),
|
||||
non_negative(mday),
|
||||
non_negative(hour),
|
||||
non_negative(min),
|
||||
non_negative(sec),
|
||||
subsec, zone);
|
||||
struct vtm vtm = {
|
||||
.wday = VTM_WDAY_INITVAL,
|
||||
.yday = 0,
|
||||
.zone = str_empty,
|
||||
.year = year,
|
||||
.mon = (mon < 0) ? 1 : mon,
|
||||
.mday = (mday < 0) ? 1 : mday,
|
||||
.hour = (hour < 0) ? 0 : hour,
|
||||
.min = (min < 0) ? 0 : min,
|
||||
.sec = (sec < 0) ? 0 : sec,
|
||||
.subsecx = NIL_P(subsec) ? INT2FIX(0) : subsec,
|
||||
};
|
||||
return time_init_vtm(klass, vtm, zone);
|
||||
}
|
||||
|
||||
static void
|
||||
|
13
timev.rb
13
timev.rb
@ -300,6 +300,8 @@ class Time
|
||||
# Time.new('2000-12-31 23:59:59.5') # => 2000-12-31 23:59:59.5 -0600
|
||||
# Time.new('2000-12-31 23:59:59.5 +0900') # => 2000-12-31 23:59:59.5 +0900
|
||||
# Time.new('2000-12-31 23:59:59.5', in: '+0900') # => 2000-12-31 23:59:59.5 +0900
|
||||
# Time.new('2000-12-31 23:59:59.5') # => 2000-12-31 23:59:59.5 -0600
|
||||
# Time.new('2000-12-31 23:59:59.56789', precision: 3) # => 2000-12-31 23:59:59.567 -0600
|
||||
#
|
||||
# With one to six arguments, returns a new \Time object
|
||||
# based on the given arguments, in the local timezone.
|
||||
@ -375,7 +377,12 @@ class Time
|
||||
# Time.new(in: '-12:00')
|
||||
# # => 2022-08-23 08:49:26.1941467 -1200
|
||||
#
|
||||
def initialize(year = (now = true), mon = (str = year; nil), mday = nil, hour = nil, min = nil, sec = nil, zone = nil, in: nil)
|
||||
# - +precision+: maximum effective digits in sub-second part, default is 9.
|
||||
# More digits will be truncated, as other operations of \Time.
|
||||
# Ignored unless the first argument is a string.
|
||||
#
|
||||
def initialize(year = (now = true), mon = (str = year; nil), mday = nil, hour = nil, min = nil, sec = nil, zone = nil,
|
||||
in: nil, precision: 9)
|
||||
if zone
|
||||
if Primitive.arg!(:in)
|
||||
raise ArgumentError, "timezone argument given as positional and keyword arguments"
|
||||
@ -388,10 +395,10 @@ class Time
|
||||
return Primitive.time_init_now(zone)
|
||||
end
|
||||
|
||||
if str and Primitive.time_init_parse(str, zone)
|
||||
if str and Primitive.time_init_parse(str, zone, precision)
|
||||
return self
|
||||
end
|
||||
|
||||
Primitive.time_init_args(year, mon, mday, hour, min, sec, nil, zone)
|
||||
Primitive.time_init_args(year, mon, mday, hour, min, sec, zone)
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user