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)) # define FIXWV2WINT(w) FIX2LONG(WIDEVAL_GET(w))
#endif #endif
#define SIZEOF_WIDEINT SIZEOF_INT64_T
#define POSFIXWVABLE(wi) ((wi) < FIXWV_MAX+1) #define POSFIXWVABLE(wi) ((wi) < FIXWV_MAX+1)
#define NEGFIXWVABLE(wi) ((wi) >= FIXWV_MIN) #define NEGFIXWVABLE(wi) ((wi) >= FIXWV_MIN)
#define FIXWV_P(w) FIXWINT_P(WIDEVAL_GET(w)) #define FIXWV_P(w) FIXWINT_P(WIDEVAL_GET(w))
@ -1968,11 +1969,11 @@ time_modify(VALUE time)
} }
static wideval_t static wideval_t
timenano2timew(time_t sec, long nsec) timenano2timew(wideint_t sec, long nsec)
{ {
wideval_t timew; wideval_t timew;
timew = rb_time_magnify(TIMET2WV(sec)); timew = rb_time_magnify(WINT2WV(sec));
if (nsec) if (nsec)
timew = wadd(timew, wmulquoll(WINT2WV(nsec), TIME_SCALE, 1000000000)); timew = wadd(timew, wmulquoll(WINT2WV(nsec), TIME_SCALE, 1000000000));
return timew; return timew;
@ -2747,15 +2748,15 @@ only_year:
} }
static void 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 subsec = *subsecp;
long sec2; long sec2;
if (UNLIKELY(subsec >= maxsubsec)) { /* subsec positive overflow */ if (UNLIKELY(subsec >= maxsubsec)) { /* subsec positive overflow */
sec2 = subsec / maxsubsec; sec2 = subsec / maxsubsec;
if (TIMET_MAX - sec2 < sec) { if (WIDEINT_MAX - sec2 < sec) {
rb_raise(rb_eRangeError, "out of Time range"); rb_raise(rb_eRangeError, "out of Time range");
} }
subsec -= sec2 * maxsubsec; 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 */ else if (UNLIKELY(subsec < 0)) { /* subsec negative overflow */
sec2 = NDIV(subsec, maxsubsec); /* negative div */ sec2 = NDIV(subsec, maxsubsec); /* negative div */
if (sec < TIMET_MIN - sec2) { if (sec < WIDEINT_MIN - sec2) {
rb_raise(rb_eRangeError, "out of Time range"); rb_raise(rb_eRangeError, "out of Time range");
} }
subsec -= sec2 * maxsubsec; subsec -= sec2 * maxsubsec;
sec += sec2; sec += sec2;
} }
#ifndef NEGATIVE_TIME_T
if (sec < 0)
rb_raise(rb_eArgError, "time must be positive");
#endif
*secp = sec; *secp = sec;
*subsecp = subsec; *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_usec_normalize(secp, usecp) subsec_normalize(secp, usecp, 1000000)
#define time_nsec_normalize(secp, nsecp) subsec_normalize(secp, nsecp, 1000000000) #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 static VALUE
time_new_timew(VALUE klass, wideval_t timew) time_new_timew(VALUE klass, wideval_t timew)
{ {
@ -2800,25 +2790,39 @@ time_new_timew(VALUE klass, wideval_t timew)
return time; 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 VALUE
rb_time_new(time_t sec, long usec) rb_time_new(time_t sec, long usec)
{ {
time_usec_normalize(&sec, &usec); wideint_t isec = TIMETtoWIDEINT(sec);
return time_new_timew(rb_cTime, timenano2timew(sec, usec * 1000)); time_usec_normalize(&isec, &usec);
return time_new_timew(rb_cTime, timenano2timew(isec, usec * 1000));
} }
/* returns localtime time object */ /* returns localtime time object */
VALUE VALUE
rb_time_nano_new(time_t sec, long nsec) 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 VALUE
rb_time_timespec_new(const struct timespec *ts, int offset) rb_time_timespec_new(const struct timespec *ts, int offset)
{ {
struct time_object *tobj; 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 */ if (-86400 < offset && offset < 86400) { /* fixoff */
GetTimeval(time, tobj); GetTimeval(time, tobj);