[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(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.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. ") {
|
assert_raise_with_message(ArgumentError, "subsecond expected after dot: 00:56:17. ") {
|
||||||
Time.new("2020-12-25 00:56:17. +0900")
|
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
|
static VALUE
|
||||||
num_exact(VALUE v)
|
num_exact_check(VALUE v)
|
||||||
{
|
{
|
||||||
VALUE tmp;
|
VALUE tmp;
|
||||||
|
|
||||||
switch (TYPE(v)) {
|
switch (TYPE(v)) {
|
||||||
case T_FIXNUM:
|
case T_FIXNUM:
|
||||||
case T_BIGNUM:
|
case T_BIGNUM:
|
||||||
return v;
|
tmp = v;
|
||||||
|
break;
|
||||||
|
|
||||||
case T_RATIONAL:
|
case T_RATIONAL:
|
||||||
return rb_rational_canonicalize(v);
|
tmp = rb_rational_canonicalize(v);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (!UNDEF_P(tmp = rb_check_funcall(v, idTo_r, 0, NULL))) {
|
if (!UNDEF_P(tmp = rb_check_funcall(v, idTo_r, 0, NULL))) {
|
||||||
@ -535,10 +537,11 @@ num_exact(VALUE v)
|
|||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
}
|
}
|
||||||
else if (RB_INTEGER_TYPE_P(tmp)) {
|
else if (RB_INTEGER_TYPE_P(tmp)) {
|
||||||
return tmp;
|
break;
|
||||||
}
|
}
|
||||||
else if (RB_TYPE_P(tmp, T_RATIONAL)) {
|
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))) {
|
else if (!NIL_P(tmp = rb_check_to_int(v))) {
|
||||||
@ -547,9 +550,26 @@ num_exact(VALUE v)
|
|||||||
|
|
||||||
case T_NIL:
|
case T_NIL:
|
||||||
case T_STRING:
|
case T_STRING:
|
||||||
rb_raise(rb_eTypeError, "can't convert %"PRIsVALUE" into an exact number",
|
return Qnil;
|
||||||
rb_obj_class(v));
|
|
||||||
}
|
}
|
||||||
|
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 */
|
/* time_t */
|
||||||
@ -2349,13 +2369,13 @@ vtm_day_wraparound(struct vtm *vtm)
|
|||||||
vtm_add_day(vtm, 1);
|
vtm_add_day(vtm, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE time_init_vtm(VALUE time, struct vtm vtm, VALUE zone);
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
time_init_args(rb_execution_context_t *ec, VALUE time, VALUE year, VALUE mon, VALUE mday,
|
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;
|
struct vtm vtm;
|
||||||
VALUE utc = Qnil;
|
|
||||||
struct time_object *tobj;
|
|
||||||
|
|
||||||
vtm.wday = VTM_WDAY_INITVAL;
|
vtm.wday = VTM_WDAY_INITVAL;
|
||||||
vtm.yday = 0;
|
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.sec = 0;
|
||||||
vtm.subsecx = INT2FIX(0);
|
vtm.subsecx = INT2FIX(0);
|
||||||
}
|
}
|
||||||
else if (!NIL_P(subsec)) {
|
|
||||||
vtm.sec = obj2ubits(sec, 6);
|
|
||||||
vtm.subsecx = subsec;
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
VALUE subsecx;
|
VALUE subsecx;
|
||||||
vtm.sec = obj2subsecx(sec, &subsecx);
|
vtm.sec = obj2subsecx(sec, &subsecx);
|
||||||
vtm.subsecx = 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.isdst = VTM_ISDST_INITVAL;
|
||||||
vtm.utc_offset = Qnil;
|
vtm.utc_offset = Qnil;
|
||||||
const VALUE arg = zone;
|
const VALUE arg = zone;
|
||||||
@ -2474,7 +2499,7 @@ parse_int(const char *ptr, const char *end, const char **endp, size_t *ndigits,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
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 (NIL_P(str = rb_check_string_type(str))) return Qnil;
|
||||||
if (!rb_enc_str_asciicompat_p(str)) {
|
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;
|
VALUE year = Qnil, subsec = Qnil;
|
||||||
int mon = -1, mday = -1, hour = -1, min = -1, sec = -1;
|
int mon = -1, mday = -1, hour = -1, min = -1, sec = -1;
|
||||||
size_t ndigits;
|
size_t ndigits;
|
||||||
|
size_t prec = NIL_P(precision) ? SIZE_MAX : NUM2SIZET(precision);
|
||||||
|
|
||||||
while ((ptr < end) && ISSPACE(*ptr)) ptr++;
|
while ((ptr < end) && ISSPACE(*ptr)) ptr++;
|
||||||
year = parse_int(ptr, end, &ptr, &ndigits, true);
|
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);
|
expect_two_digits(sec);
|
||||||
if (peek('.')) {
|
if (peek('.')) {
|
||||||
ptr++;
|
ptr++;
|
||||||
for (ndigits = 0; ndigits < 45 && ISDIGIT(peekc_n(ndigits)); ++ndigits);
|
for (ndigits = 0; ndigits < prec && ISDIGIT(peekc_n(ndigits)); ++ndigits);
|
||||||
if (!ndigits) {
|
if (!ndigits) {
|
||||||
int clen = rb_enc_precise_mbclen(ptr, end, rb_enc_get(str));
|
int clen = rb_enc_precise_mbclen(ptr, end, rb_enc_get(str));
|
||||||
if (clen < 0) clen = 0;
|
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))
|
struct vtm vtm = {
|
||||||
return time_init_args(ec, time, year,
|
.wday = VTM_WDAY_INITVAL,
|
||||||
non_negative(mon),
|
.yday = 0,
|
||||||
non_negative(mday),
|
.zone = str_empty,
|
||||||
non_negative(hour),
|
.year = year,
|
||||||
non_negative(min),
|
.mon = (mon < 0) ? 1 : mon,
|
||||||
non_negative(sec),
|
.mday = (mday < 0) ? 1 : mday,
|
||||||
subsec, zone);
|
.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
|
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') # => 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 +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', 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
|
# With one to six arguments, returns a new \Time object
|
||||||
# based on the given arguments, in the local timezone.
|
# based on the given arguments, in the local timezone.
|
||||||
@ -375,7 +377,12 @@ class Time
|
|||||||
# Time.new(in: '-12:00')
|
# Time.new(in: '-12:00')
|
||||||
# # => 2022-08-23 08:49:26.1941467 -1200
|
# # => 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 zone
|
||||||
if Primitive.arg!(:in)
|
if Primitive.arg!(:in)
|
||||||
raise ArgumentError, "timezone argument given as positional and keyword arguments"
|
raise ArgumentError, "timezone argument given as positional and keyword arguments"
|
||||||
@ -388,10 +395,10 @@ class Time
|
|||||||
return Primitive.time_init_now(zone)
|
return Primitive.time_init_now(zone)
|
||||||
end
|
end
|
||||||
|
|
||||||
if str and Primitive.time_init_parse(str, zone)
|
if str and Primitive.time_init_parse(str, zone, precision)
|
||||||
return self
|
return self
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user