Normalize subseconds using wideint_t instead of time_t

This commit is contained in:
Nobuyoshi Nakada 2025-06-13 18:25:14 +09:00 committed by Nobuyoshi Nakada
parent b8de3cfb04
commit 583ce06c0e
Notes: git 2025-06-13 10:36:05 +00:00

46
time.c
View file

@ -249,6 +249,7 @@ divmodv(VALUE n, VALUE d, VALUE *q, VALUE *r)
# define FIXWV2WINT(w) FIX2LONG(WIDEVAL_GET(w))
#endif
#define SIZEOF_WIDEINT SIZEOF_INT64_T
#define POSFIXWVABLE(wi) ((wi) < FIXWV_MAX+1)
#define NEGFIXWVABLE(wi) ((wi) >= FIXWV_MIN)
#define FIXWV_P(w) FIXWINT_P(WIDEVAL_GET(w))
@ -1968,11 +1969,11 @@ time_modify(VALUE time)
}
static wideval_t
timenano2timew(time_t sec, long nsec)
timenano2timew(wideint_t sec, long nsec)
{
wideval_t timew;
timew = rb_time_magnify(TIMET2WV(sec));
timew = rb_time_magnify(WINT2WV(sec));
if (nsec)
timew = wadd(timew, wmulquoll(WINT2WV(nsec), TIME_SCALE, 1000000000));
return timew;
@ -2747,15 +2748,15 @@ only_year:
}
static void
subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec)
subsec_normalize(wideint_t *secp, long *subsecp, const long maxsubsec)
{
time_t sec = *secp;
wideint_t sec = *secp;
long subsec = *subsecp;
long sec2;
if (UNLIKELY(subsec >= maxsubsec)) { /* subsec positive overflow */
sec2 = subsec / maxsubsec;
if (TIMET_MAX - sec2 < sec) {
if (WIDEINT_MAX - sec2 < sec) {
rb_raise(rb_eRangeError, "out of Time range");
}
subsec -= sec2 * maxsubsec;
@ -2763,16 +2764,12 @@ subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec)
}
else if (UNLIKELY(subsec < 0)) { /* subsec negative overflow */
sec2 = NDIV(subsec, maxsubsec); /* negative div */
if (sec < TIMET_MIN - sec2) {
if (sec < WIDEINT_MIN - sec2) {
rb_raise(rb_eRangeError, "out of Time range");
}
subsec -= sec2 * maxsubsec;
sec += sec2;
}
#ifndef NEGATIVE_TIME_T
if (sec < 0)
rb_raise(rb_eArgError, "time must be positive");
#endif
*secp = sec;
*subsecp = subsec;
}
@ -2780,13 +2777,6 @@ subsec_normalize(time_t *secp, long *subsecp, const long maxsubsec)
#define time_usec_normalize(secp, usecp) subsec_normalize(secp, usecp, 1000000)
#define time_nsec_normalize(secp, nsecp) subsec_normalize(secp, nsecp, 1000000000)
static wideval_t
nsec2timew(time_t sec, long nsec)
{
time_nsec_normalize(&sec, &nsec);
return timenano2timew(sec, nsec);
}
static VALUE
time_new_timew(VALUE klass, wideval_t timew)
{
@ -2800,25 +2790,39 @@ time_new_timew(VALUE klass, wideval_t timew)
return time;
}
static wideint_t
TIMETtoWIDEINT(time_t t)
{
#if SIZEOF_TIME_T * CHAR_BIT - (SIGNEDNESS_OF_TIME_T < 0) > \
SIZEOF_WIDEINT * CHAR_BIT - 1
/* compare in bit size without sign bit */
if (t > WIDEINT_MAX) rb_raise(rb_eArgError, "out of Time range");
#endif
return (wideint_t)t;
}
VALUE
rb_time_new(time_t sec, long usec)
{
time_usec_normalize(&sec, &usec);
return time_new_timew(rb_cTime, timenano2timew(sec, usec * 1000));
wideint_t isec = TIMETtoWIDEINT(sec);
time_usec_normalize(&isec, &usec);
return time_new_timew(rb_cTime, timenano2timew(isec, usec * 1000));
}
/* returns localtime time object */
VALUE
rb_time_nano_new(time_t sec, long nsec)
{
return time_new_timew(rb_cTime, nsec2timew(sec, nsec));
wideint_t isec = TIMETtoWIDEINT(sec);
time_nsec_normalize(&isec, &nsec);
return time_new_timew(rb_cTime, timenano2timew(isec, nsec));
}
VALUE
rb_time_timespec_new(const struct timespec *ts, int offset)
{
struct time_object *tobj;
VALUE time = time_new_timew(rb_cTime, nsec2timew(ts->tv_sec, ts->tv_nsec));
VALUE time = rb_time_nano_new(ts->tv_sec, ts->tv_nsec);
if (-86400 < offset && offset < 86400) { /* fixoff */
GetTimeval(time, tobj);