Nanosecond Time and stat

e$B:G6a!“e(Bnanosecond e$BC10L$Je(B timestamp e$B$,$”$k$h$&$G$9!#e(B

e$B$?$H$($Pe(B boron e$B$@$He(B timestamp e$B$,e(B nanosecond
e$BC10L$G$9!#e(B

% stat .
File: `.’
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: fe02h/65026d Inode: 12583040 Links: 14
Access: (0755/drwxr-xr-x) Uid: ( 1033/ akr) Gid: ( 100/ users)
Access: 2007-11-17 17:38:05.613931200 +0900
Modify: 2007-11-17 14:31:00.182456176 +0900
Change: 2007-11-17 14:31:00.182456176 +0900

e$B>e5-$Ne(B filesystem e$B$Oe(B XFS e$B$G$9$,!"e(BGNU/Linux
e$B$@$HB>$Ke(B JFS e$B$,%5e(B
e$B%]!<%H$7$F$$$^$9!#$^$?!"e(Bext4 e$B$b%5%]!<%H$9$k$h$&$G$9!#e(B

NetBSD, FreeBSD, OpenBSD e$B$b!“e(Bstruct stat e$B$K$O$b$&MQ0U$,$”$je(B
e$B$^$9!#e(B(e$B<B:]$K$=$l$r;H$&e(B filesystem
e$B$,$"$k$+$OCN$j$^$;$se(B)

e$B$G!"$3$l$re(B Ruby e$B$G07$C$F$_$h$&$+$H;W$C$F!"e(B

  • Time e$B$N@:EY$re(B microsecond e$B$+$ie(B nanosecond e$B$K$"$2$Fe(B
  • File.mtime e$BEy$Ge(B nanosecond e$BC10L$Ne(B timestamp
    e$B$r<h$j=P$9e(B
    e$B$H$7$F$_$^$7$?!#e(B

e$B$=$l$K$H$b$J$C$F!"e(B

  • Time#tv_nsec, Time#nsec e$B$NDI2Ce(B
  • Time.at e$B$N:G8e$N0z?t$Ge(B Float e$B$de(B Rational
    e$B$r<u$1IU$1$ke(B
  • Time.local, Time.utc e$B$N:G8e$N0z?t$,e(B sec
    e$B$N$H$-$bF1MM$K<u$1IU$1$ke(B
    e$B$H$7$F$"$j$^$9!#e(B

Rational e$B$r07$($ke(B (e$B$H$$$&$+!"%/%i%9$r5$$K$;$:$KC1$Ke(B divmod
e$B$H$+$r8F$S=P$7$F@0?tItJ,$d>.?tItJ,$r<h$j=P$9e(B)
e$B$h$&$K$7$?$N$O!"e(B
IEEE 754 double e$B$@$H!"8=;~E@$N;~9o$re(B nanosecond e$BC10L$^$G$N@:e(B
e$BEY$G$OI=8=$G$-$J$$$+$i$G$9!#e(BTime.at
e$B$KBhe(B3e$B0z?t$r$D$1$k$H$+e(B
Time.utc e$B$N0z?t$r$5$i$KJ#;($K$9$k$H$$$&$N$O$J$s$G$9$7!#e(B

e$B$"$H!“e(BMarshal e$B$OG:$s$@$N$G<j$r$D$1$F$J$/$F!”:#$^$G$I$*$je(B
microsecond e$B$^$G$7$+=q$-=P$5$l$^$;$s!#FI$_=P$9$H$-$Oe(B
microsecond e$BL$K~$Oe(B 0 e$B$K$J$j$^$9!#e(B

e$B$I$&$G$9$+$M!#e(B

Index: time.c

— time.c (revision 13950)
+++ time.c (working copy)
@@ -24,7 +24,7 @@
static VALUE time_utc_offset _((VALUE));

struct time_object {

  • struct timeval tv;
  • struct timespec ts;
    struct tm tm;
    int gmt;
    int tm_got;
    @@ -47,8 +47,8 @@

    obj = Data_Make_Struct(klass, struct time_object, 0, time_free,
    tobj);
    tobj->tm_got=0;

  • tobj->tv.tv_sec = 0;
  • tobj->tv.tv_usec = 0;
  • tobj->ts.tv_sec = 0;

  • tobj->ts.tv_nsec = 0;

    return obj;
    }
    @@ -93,11 +93,22 @@
    time_modify(time);
    GetTimeval(time, tobj);
    tobj->tm_got=0;

  • tobj->tv.tv_sec = 0;
  • tobj->tv.tv_usec = 0;
  • if (gettimeofday(&tobj->tv, 0) < 0) {
  • rb_sys_fail(“gettimeofday”);
  • tobj->ts.tv_sec = 0;

  • tobj->ts.tv_nsec = 0;
    +#ifdef HAVE_CLOCK_GETTIME

  • if (clock_gettime(CLOCK_REALTIME, &tobj->ts) == -1) {

  • rb_sys_fail(“clock_gettime”);
    }
    +#else

  • {

  •    struct timeval tv;
    
  •    if (gettimeofday(&tv, 0) < 0) {
    
  •        rb_sys_fail("gettimeofday");
    
  •    }
    
  •    tobj->ts.tv_sec = tv.tv_sec;
    
  •    tobj->ts.tv_nsec = tv.tv_usec * 1000;
    
  • }
    +#endif

    return time;
    }
    @@ -106,9 +117,10 @@
    #define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)

static void
-time_overflow_p(time_t *secp, time_t *usecp)
+time_overflow_p(time_t *secp, long *usecp)
{

  • time_t tmp, sec = *secp, usec = *usecp;
  • time_t tmp, sec = *secp;

  • long usec = *usecp;

    if (usec >= 1000000) { /* usec positive overflow */
    tmp = sec + usec / 1000000;
    @@ -134,78 +146,152 @@
    *usecp = usec;
    }

+static void
+time_nano_overflow_p(time_t *secp, long *nsecp)
+{

  • time_t tmp, sec = *secp;
  • long nsec = *nsecp;
  • if (nsec >= 1000000000) { /* nsec positive overflow */
  • tmp = sec + nsec / 1000000000;
  • nsec %= 1000000000;
  • if (sec > 0 && tmp < 0) {
  •  rb_raise(rb_eRangeError, "out of Time range");
    
  • }
  • sec = tmp;
  • }
  • if (nsec < 0) { /* nsec negative overflow */
  • tmp = sec + NDIV(nsec,1000000000); /* negative div */
  • nsec = NMOD(nsec,1000000000); /* negative mod */
  • if (sec < 0 && tmp > 0) {
  •  rb_raise(rb_eRangeError, "out of Time range");
    
  • }
  • sec = tmp;
  • }
    +#ifndef NEGATIVE_TIME_T
  • if (sec < 0)
  • rb_raise(rb_eArgError, “time must be positive”);
    +#endif
  • *secp = sec;
  • *nsecp = nsec;
    +}

static VALUE
-time_new_internal(VALUE klass, time_t sec, time_t usec)
+time_new_internal(VALUE klass, time_t sec, long usec)
{
VALUE time = time_s_alloc(klass);
struct time_object *tobj;

 GetTimeval(time, tobj);
 time_overflow_p(&sec, &usec);
  • tobj->tv.tv_sec = sec;
  • tobj->tv.tv_usec = usec;
  • tobj->ts.tv_sec = sec;

  • tobj->ts.tv_nsec = usec * 1000;

    return time;
    }

+static VALUE
+time_nano_new_internal(VALUE klass, time_t sec, long nsec)
+{

  • VALUE time = time_s_alloc(klass);
  • struct time_object *tobj;
  • GetTimeval(time, tobj);
  • time_nano_overflow_p(&sec, &nsec);
  • tobj->ts.tv_sec = sec;
  • tobj->ts.tv_nsec = nsec;
  • return time;
    +}

VALUE
-rb_time_new(time_t sec, time_t usec)
+rb_time_new(time_t sec, long usec)
{
return time_new_internal(rb_cTime, sec, usec);
}

-static struct timeval
-time_timeval(VALUE time, int interval)
+VALUE
+rb_time_nano_new(time_t sec, long nsec)
{

  • struct timeval t;
  • return time_nano_new_internal(rb_cTime, sec, nsec);
    +}

+static struct timespec
+time_timespec(VALUE num, int interval)
+{

  • struct timespec t;
    const char *tstr = interval ? “time interval” : “time”;
  • VALUE i, f, ary;

#ifndef NEGATIVE_TIME_T
interval = 1;
#endif

  • switch (TYPE(time)) {
  • switch (TYPE(num)) {
    case T_FIXNUM:
  • t.tv_sec = FIX2LONG(time);
  • t.tv_sec = FIX2LONG(num);
    if (interval && t.tv_sec < 0)
    rb_raise(rb_eArgError, “%s must be positive”, tstr);
  • t.tv_usec = 0;
  • t.tv_nsec = 0;
    break;

    case T_FLOAT:
    
  • if (interval && RFLOAT_VALUE(time) < 0.0)
  • if (interval && RFLOAT_VALUE(num) < 0.0)
    rb_raise(rb_eArgError, “%s must be positive”, tstr);
    else {
    double f, d;
  •  d = modf(RFLOAT_VALUE(time), &f);
    
  •  d = modf(RFLOAT_VALUE(num), &f);
     t.tv_sec = (time_t)f;
     if (f != t.tv_sec) {
    
  • rb_raise(rb_eRangeError, “%f out of Time range”,
    RFLOAT_VALUE(time));
  • rb_raise(rb_eRangeError, “%f out of Time range”,
    RFLOAT_VALUE(num));
    }
  •  t.tv_usec = (time_t)(d*1e6+0.5);
    
  •  t.tv_nsec = (long)(d*1e9+0.5);
    

    }
    break;

     case T_BIGNUM:
    
  • t.tv_sec = NUM2LONG(time);
  • t.tv_sec = NUM2LONG(num);
    if (interval && t.tv_sec < 0)
    rb_raise(rb_eArgError, “%s must be positive”, tstr);
  • t.tv_usec = 0;
  • t.tv_nsec = 0;
    break;

    default:
    
  • rb_raise(rb_eTypeError, “can’t convert %s into %s”,
  • rb_obj_classname(time), tstr);
    
  •    ary = rb_check_array_type(rb_funcall(num, rb_intern("divmod"), 
    

1, INT2FIX(1)));

  •    if (NIL_P(ary)) {
    
  •        rb_raise(rb_eTypeError, "can't convert %s into %s",
    
  •                 rb_obj_classname(num), tstr);
    
  •    }
    
  •    i = rb_ary_entry(ary, 0);
    
  •    f = rb_ary_entry(ary, 1);
    
  •    t.tv_sec = NUM2LONG(i);
    
  • if (interval && t.tv_sec < 0)
  •  rb_raise(rb_eArgError, "%s must be positive", tstr);
    
  •    f = rb_funcall(f, rb_intern("*"), 1, INT2FIX(1000000000));
    
  •    t.tv_nsec = NUM2LONG(f);
    
    break;
    }
    return t;
    }

+static struct timeval
+time_timeval(VALUE num, int interval)
+{

  • struct timespec ts;
  • struct timeval tv;
  • ts = time_timespec(num, interval);
  • tv.tv_sec = ts.tv_sec;
  • tv.tv_usec = ts.tv_nsec / 1000;
  • return tv;
    +}

struct timeval
-rb_time_interval(VALUE time)
+rb_time_interval(VALUE num)
{

  • return time_timeval(time, Qtrue);
  • return time_timeval(num, Qtrue);
    }

struct timeval
@@ -216,12 +302,27 @@

 if (TYPE(time) == T_DATA && RDATA(time)->dfree == time_free) {

GetTimeval(time, tobj);

  • t = tobj->tv;
  •    t.tv_sec = tobj->ts.tv_sec;
    
  •    t.tv_usec = tobj->ts.tv_nsec / 1000;
    
    return t;
    }
    return time_timeval(time, Qfalse);
    }

+struct timespec
+rb_time_timespec(VALUE time)
+{

  • struct time_object *tobj;
  • struct timespec t;
  • if (TYPE(time) == T_DATA && RDATA(time)->dfree == time_free) {
  • GetTimeval(time, tobj);
  •    t = tobj->ts;
    
  • return t;
  • }
  • return time_timespec(time, Qfalse);
    +}

/*

  • call-seq:
  • Time.at( aTime ) => time
    

@@ -229,7 +330,7 @@
*

  • Creates a new time object with the value given by aTime, or
  • the given number of seconds (and optional
    • microseconds) from epoch. A non-portable feature allows the
    • microseconds) from the Epoch. A non-portable feature allows
      the
    • offset to be negative on some systems.
    • Time.at(0)            #=> Wed Dec 31 18:00:00 CST 1969
      

@@ -240,17 +341,17 @@
static VALUE
time_s_at(int argc, VALUE *argv, VALUE klass)
{

  • struct timeval tv;
  • struct timespec ts;
    VALUE time, t;

    if (rb_scan_args(argc, argv, “11”, &time, &t) == 2) {

  • tv.tv_sec = NUM2LONG(time);
  • tv.tv_usec = NUM2LONG(t);
  • ts.tv_sec = NUM2LONG(time);
  • ts.tv_nsec = NUM2LONG(rb_funcall(t, rb_intern("*"), 1,
    INT2FIX(1000)));
    }
    else {
  • tv = rb_time_timeval(time);
  • ts = rb_time_timespec(time);
    }
  • t = time_new_internal(klass, tv.tv_sec, tv.tv_usec);
  • t = time_nano_new_internal(klass, ts.tv_sec, ts.tv_nsec);
    if (TYPE(time) == T_DATA && RDATA(time)->dfree == time_free) {
    struct time_object *tobj, *tobj2;

@@ -276,15 +377,42 @@
return NUM2LONG(obj);
}

+static long
+obj2nsec(VALUE obj, long *nsec)
+{

  • struct timespec ts;
  • if (TYPE(obj) == T_STRING) {
  • obj = rb_str_to_inum(obj, 10, Qfalse);
  •    *nsec = 0;
    
  •    return NUM2LONG(obj) * 1000;
    
  • }
  • ts = time_timespec(obj, 1);
  • *nsec = ts.tv_nsec;
  • return ts.tv_sec;
    +}

+static long
+obj2long1000(VALUE obj)
+{

  • if (TYPE(obj) == T_STRING) {
  • obj = rb_str_to_inum(obj, 10, Qfalse);
  •    return NUM2LONG(obj) * 1000;
    
  • }
  • return NUM2LONG(rb_funcall(obj, rb_intern("*"), 1, INT2FIX(1000)));
    +}

static void
-time_arg(int argc, VALUE *argv, struct tm *tm, time_t *usec)
+time_arg(int argc, VALUE *argv, struct tm *tm, long *nsec)
{
VALUE v[8];
int i;
long year;

 MEMZERO(tm, struct tm, 1);
  • *usec = 0;
  • *nsec = 0;
    if (argc == 10) {
    v[0] = argv[5];
    v[1] = argv[4];
    @@ -352,12 +480,13 @@
    }
    tm->tm_hour = NIL_P(v[3])?0:obj2long(v[3]);
    tm->tm_min = NIL_P(v[4])?0:obj2long(v[4]);
  • tm->tm_sec = NIL_P(v[5])?0:obj2long(v[5]);
  • if (!NIL_P(v[6])) {
  • if (!NIL_P(v[6]) && argc == 7) {
  •    tm->tm_sec  = NIL_P(v[5])?0:obj2long(v[5]);
    
  •    *nsec = obj2long1000(v[6]);
    
  • }
  • else {
    /* when argc == 8, v[6] is timezone, but ignored */
  • if (argc == 7) {
  •  *usec = obj2long(v[6]);
    
  • }
  •    tm->tm_sec  = NIL_P(v[5])?0:obj2nsec(v[5], nsec);
    

    }

    /* value validation */
    @@ -774,10 +903,10 @@
    {
    struct tm tm;
    VALUE time;

  • time_t usec;
  • long nsec;
  • time_arg(argc, argv, &tm, &usec);
  • time = time_new_internal(klass, make_time_t(&tm, utc_p), usec);
  • time_arg(argc, argv, &tm, &nsec);
  • time = time_nano_new_internal(klass, make_time_t(&tm, utc_p),
    nsec);
    if (utc_p) return time_gmtime(time);
    return time_localtime(time);
    }
    @@ -834,7 +963,7 @@
  • time.tv_sec => int
    
  • Returns the value of time as an integer number of seconds
    • since epoch.
    • since the Epoch.
    • t = Time.now
      
    • "%10.5f" % t.to_f   #=> "1049896564.17839"
      

@@ -847,7 +976,7 @@
struct time_object *tobj;

 GetTimeval(time, tobj);
  • return LONG2NUM(tobj->tv.tv_sec);
  • return LONG2NUM(tobj->ts.tv_sec);
    }

/*
@@ -855,11 +984,14 @@

  • time.to_f => float
    
  • Returns the value of time as a floating point number of
    • seconds since epoch.
    • seconds since the Epoch.
    • t = Time.now
      
    • "%10.5f" % t.to_f   #=> "1049896564.13654"
      
    • t.to_i              #=> 1049896564
      
    • Note that IEEE 754 double is not accurate enough to represent
    • nanoseconds from the Epoch.
      */

static VALUE
@@ -868,7 +1000,7 @@
struct time_object *tobj;

 GetTimeval(time, tobj);
  • return
    DOUBLE2NUM((double)tobj->tv.tv_sec+(double)tobj->tv.tv_usec/1e6);
  • return
    DOUBLE2NUM((double)tobj->ts.tv_sec+(double)tobj->ts.tv_nsec/1e9);
    }

/*
@@ -889,17 +1021,41 @@
struct time_object *tobj;

 GetTimeval(time, tobj);
  • return LONG2NUM(tobj->tv.tv_usec);
  • return LONG2NUM(tobj->ts.tv_nsec/1000);
    }

/*

  • call-seq:
    • time.nsec    => int
      
    • time.tv_nsec => int
      
    • Returns just the number of nanoseconds for time.
    • t = Time.now        #=> 2007-11-17 15:18:03 +0900
      
    • "%10.9f" % t.to_f   #=> "1195280283.536151409"
      
    • t.nsec              #=> 536151406
      
    • Note that IEEE 754 double is not accurate enough to represent
    • nanoseconds from the Epoch.
  • */

+static VALUE
+time_nsec(VALUE time)
+{

  • struct time_object *tobj;
  • GetTimeval(time, tobj);
  • return LONG2NUM(tobj->ts.tv_nsec);
    +}

+/*

    • call-seq:
    • time <=> other_time => -1, 0, +1
      
    • time <=> numeric    => -1, 0, +1
      
    • Comparison—Compares time with other_time or with
    • numeric, which is the number of seconds (possibly
    • fractional) since epoch.
    • fractional) since the Epoch.
    • t = Time.now       #=> Wed Apr 09 08:56:03 CDT 2003
      
    • t2 = t + 2592000   #=> Fri May 09 08:56:03 CDT 2003
      

@@ -916,12 +1072,12 @@
GetTimeval(time1, tobj1);
if (TYPE(time2) == T_DATA && RDATA(time2)->dfree == time_free) {
GetTimeval(time2, tobj2);

  • if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) {
  •  if (tobj1->tv.tv_usec == tobj2->tv.tv_usec) return INT2FIX(0);
    
  •  if (tobj1->tv.tv_usec > tobj2->tv.tv_usec) return INT2FIX(1);
    
  • if (tobj1->ts.tv_sec == tobj2->ts.tv_sec) {
  •  if (tobj1->ts.tv_nsec == tobj2->ts.tv_nsec) return INT2FIX(0);
    
  •  if (tobj1->ts.tv_nsec > tobj2->ts.tv_nsec) return INT2FIX(1);
     return INT2FIX(-1);
    
    }
  • if (tobj1->tv.tv_sec > tobj2->tv.tv_sec) return INT2FIX(1);
  • if (tobj1->ts.tv_sec > tobj2->ts.tv_sec) return INT2FIX(1);
    return INT2FIX(-1);
    }

@@ -945,8 +1101,8 @@
GetTimeval(time1, tobj1);
if (TYPE(time2) == T_DATA && RDATA(time2)->dfree == time_free) {
GetTimeval(time2, tobj2);

  • if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) {
  •  if (tobj1->tv.tv_usec == tobj2->tv.tv_usec) return Qtrue;
    
  • if (tobj1->ts.tv_sec == tobj2->ts.tv_sec) {

  •  if (tobj1->ts.tv_nsec == tobj2->ts.tv_nsec) return Qtrue;
    

    }
    }
    return Qfalse;
    @@ -995,7 +1151,7 @@
    long hash;

    GetTimeval(time, tobj);

  • hash = tobj->tv.tv_sec ^ tobj->tv.tv_usec;
  • hash = tobj->ts.tv_sec ^ tobj->ts.tv_nsec;
    return LONG2FIX(hash);
    }

@@ -1053,7 +1209,7 @@
else {
time_modify(time);
}

  • t = tobj->tv.tv_sec;
  • t = tobj->ts.tv_sec;
    tm_tmp = localtime(&t);
    if (!tm_tmp)
    rb_raise(rb_eArgError, “localtime error”);
    @@ -1096,7 +1252,7 @@
    else {
    time_modify(time);
    }
  • t = tobj->tv.tv_sec;
  • t = tobj->ts.tv_sec;
    tm_tmp = gmtime(&t);
    if (!tm_tmp)
    rb_raise(rb_eArgError, “gmtime error”);
    @@ -1211,7 +1367,7 @@
    len = strftime(buf, 128, “%Y-%m-%d %H:%M:%S UTC”, &tobj->tm);
    }
    else {
  • time_t off;
  • long off;
    char buf2[32];
    char sign = ‘+’;
    #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
    @@ -1237,7 +1393,8 @@
    double v = NUM2DBL(offset);
    double f, d;
    unsigned_time_t sec_off;
  • time_t usec_off, sec, usec;
  • time_t sec;

  • long nsec_off, nsec;
    VALUE result;

    if (v < 0) {
    @@ -1249,21 +1406,21 @@
    if (f != (double)sec_off)
    rb_raise(rb_eRangeError, “time %s %f out of Time range”,
    sign < 0 ? “-” : “+”, v);

  • usec_off = (time_t)(d*1e6+0.5);
  • nsec_off = (long)(d*1e9+0.5);

    if (sign < 0) {

  • sec = tobj->tv.tv_sec - sec_off;
  • usec = tobj->tv.tv_usec - usec_off;
  • if (sec > tobj->tv.tv_sec)
  • sec = tobj->ts.tv_sec - sec_off;
  • nsec = tobj->ts.tv_nsec - nsec_off;
  • if (sec > tobj->ts.tv_sec)
    rb_raise(rb_eRangeError, “time - %f out of Time range”, v);
    }
    else {
  • sec = tobj->tv.tv_sec + sec_off;
  • usec = tobj->tv.tv_usec + usec_off;
  • if (sec < tobj->tv.tv_sec)
  • sec = tobj->ts.tv_sec + sec_off;
  • nsec = tobj->ts.tv_nsec + nsec_off;
  • if (sec < tobj->ts.tv_sec)
    rb_raise(rb_eRangeError, “time + %f out of Time range”, v);
    }
  • result = rb_time_new(sec, usec);
  • result = rb_time_nano_new(sec, nsec);
    if (tobj->gmt) {
    GetTimeval(result, tobj);
    tobj->gmt = 1;
    @@ -1320,9 +1477,10 @@
    double f;

GetTimeval(time2, tobj2);

  • f = (double)tobj->tv.tv_sec - (double)tobj2->tv.tv_sec;
  • f += ((double)tobj->tv.tv_usec - (double)tobj2->tv.tv_usec)*1e-6;
  • f = (double)tobj->ts.tv_sec - (double)tobj2->ts.tv_sec;

  • f += ((double)tobj->ts.tv_nsec - (double)tobj2->ts.tv_nsec)1e-9;
    /
    XXX: should check float overflow on 64bit time_t platforms */

  •    /* XXX: double is not enough to represent nsec */
    

    return DOUBLE2NUM(f);
    }
    @@ -1344,7 +1502,7 @@

    GetTimeval(time, tobj);
    gmt = tobj->gmt;

  • time = rb_time_new(tobj->tv.tv_sec + 1, tobj->tv.tv_usec);
  • time = rb_time_nano_new(tobj->ts.tv_sec + 1, tobj->ts.tv_nsec);
    GetTimeval(time, tobj);
    tobj->gmt = gmt;
    return time;
    @@ -1745,7 +1903,7 @@
    time_t t;
    long off;
    l = &tobj->tm;
  • t = tobj->tv.tv_sec;
  • t = tobj->ts.tv_sec;
    u = gmtime(&t);
    if (!u)
    rb_raise(rb_eArgError, “gmtime error”);
    @@ -1935,7 +2093,7 @@

    GetTimeval(time, tobj);

  • t = tobj->tv.tv_sec;
  • t = tobj->ts.tv_sec;
    tm = gmtime(&t);

    if ((tm->tm_year & 0xffff) != tm->tm_year)
    @@ -1949,7 +2107,7 @@
    tm->tm_hour; /* 5 /
    s = tm->tm_min << 26 | /
    6 /
    tm->tm_sec << 20 | /
    6 */

  • tobj->tv.tv_usec; /* 20 */
  • tobj->ts.tv_nsec / 1000; /* 20 */

    for (i=0; i<4; i++) {
    buf[i] = p & 0xff;
    @@ -1991,7 +2149,8 @@
    {
    struct time_object *tobj;
    unsigned long p, s;

  • time_t sec, usec;
  • time_t sec;
  • long usec;
    unsigned char *buf;
    struct tm tm;
    int i, gmt;
    @@ -2028,15 +2187,15 @@
    tm.tm_isdst = 0;

sec = make_time_t(&tm, Qtrue);

  • usec = (time_t)(s & 0xfffff);
  • usec = (long)(s & 0xfffff);
    }
    time_overflow_p(&sec, &usec);

    GetTimeval(time, tobj);
    tobj->tm_got = 0;
    tobj->gmt = gmt;

  • tobj->tv.tv_sec = sec;
  • tobj->tv.tv_usec = usec;
  • tobj->ts.tv_sec = sec;
  • tobj->ts.tv_nsec = usec * 1000;
    return time;
    }

@@ -2060,7 +2219,7 @@
/*

  • Time is an abstraction of dates and times. Time is
  • stored internally as the number of seconds and microseconds since
    • the epoch, January 1, 1970 00:00 UTC. On some operating
    • the Epoch, January 1, 1970 00:00 UTC. On some operating
    • systems, this offset is allowed to be negative. Also see the
    • library modules Date and ParseDate. The
    • Time class treats GMT (Greenwich Mean Time) and UTC
      @@ -2145,6 +2304,8 @@
      rb_define_method(rb_cTime, “tv_sec”, time_to_i, 0);
      rb_define_method(rb_cTime, “tv_usec”, time_usec, 0);
      rb_define_method(rb_cTime, “usec”, time_usec, 0);
  • rb_define_method(rb_cTime, “tv_nsec”, time_nsec, 0);

  • rb_define_method(rb_cTime, “nsec”, time_nsec, 0);

    rb_define_method(rb_cTime, “strftime”, time_strftime, 1);

Index: include/ruby/missing.h

— include/ruby/missing.h (revision 13950)
+++ include/ruby/missing.h (working copy)
@@ -25,13 +25,20 @@

define time_t long

struct timeval {
time_t tv_sec; /* seconds */

  • time_t tv_usec; /* microseconds */
  • long tv_usec; /* microseconds */
    };
    #endif
    #if defined(HAVE_SYS_TYPES_H)

include <sys/types.h>

#endif

+#if !defined(HAVE_STRUCT_TIMESPEC)
+struct timespec {

  • time_t tv_sec; /* seconds */
  • long tv_nsec; /* nanoseconds */
    +};
    +#endif

#ifndef HAVE_ACOSH
extern double acosh(double);
extern double asinh(double);
Index: include/ruby/intern.h

— include/ruby/intern.h (revision 13950)
+++ include/ruby/intern.h (working copy)
@@ -560,7 +560,8 @@
VALUE rb_barrier_wait(VALUE self);
VALUE rb_barrier_release(VALUE self);
/* time.c /
-VALUE rb_time_new(time_t, time_t);
+VALUE rb_time_new(time_t, long);
+VALUE rb_time_nano_new(time_t, long);
/
variable.c */
VALUE rb_mod_name(VALUE);
VALUE rb_class_path(VALUE);
Index: configure.in

— configure.in (revision 13950)
+++ configure.in (working copy)
@@ -513,6 +513,7 @@
AC_CHECK_LIB(dl, dlopen) # Dynamic linking for SunOS/Solaris and SYSV
AC_CHECK_LIB(dld, shl_load) # Dynamic linking for HP-UX
AC_CHECK_LIB(socket, socketpair) # SunOS/Solaris
+AC_CHECK_LIB(rt, clock_gettime) # GNU/Linux

case “$target_cpu” in
alpha*) case “$target_os”::"$GCC" in
@@ -553,7 +554,12 @@
AC_STRUCT_ST_BLKSIZE
AC_STRUCT_ST_BLOCKS
AC_STRUCT_ST_RDEV
+AC_CHECK_MEMBERS([struct stat.st_mtim])
+AC_CHECK_MEMBERS([struct stat.st_mtimespec])
+AC_CHECK_MEMBERS([struct stat.st_mtimensec])

+AC_CHECK_TYPES(struct timespec)
+
AC_CHECK_TYPE(fd_mask, [AC_DEFINE(HAVE_RB_FD_INIT, 1)])

AC_CACHE_CHECK(for stack end address, rb_cv_stack_end_address,
@@ -593,8 +599,9 @@
getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups
getpriority getrlimit setrlimit sysconf group_member
dlopen sigprocmask sigaction _setjmp vsnprintf snprintf\

  •    setsid telldir seekdir fchmod mktime timegm cosh sinh tanh log2 
    

round\

  •    setuid setgid daemon select_large_fdset setenv unsetenv)
    
  •    setsid telldir seekdir fchmod cosh sinh tanh log2 round\
    
  •    setuid setgid daemon select_large_fdset setenv unsetenv\
    
  •          mktime timegm clock_gettime)
    

AC_ARG_ENABLE(setreuid,
[ --enable-setreuid use setreuid()/setregid() according
to need even if obsolete.],
[use_setreuid=$enableval])
Index: lib/time.rb

— lib/time.rb (revision 13950)
+++ lib/time.rb (working copy)
@@ -461,10 +461,10 @@
year, mon, day, hour, min, sec) +
if fraction_digits == 0
‘’

  • elsif fraction_digits <= 6
  •  '.' + sprintf('%06d', usec)[0, fraction_digits]
    
  • elsif fraction_digits <= 9
  •  '.' + sprintf('%09d', nsec)[0, fraction_digits]
    
    else
  •  '.' + sprintf('%06d', usec) + '0' * (fraction_digits - 6)
    
  •  '.' + sprintf('%09d', nsec) + '0' * (fraction_digits - 9)
    
    end +
    if utc?
    ‘Z’
    Index: test/ruby/test_time.rb
    ===================================================================
    — test/ruby/test_time.rb (revision 13950)
    +++ test/ruby/test_time.rb (working copy)
    @@ -1,4 +1,5 @@
    require ‘test/unit’
    +require ‘rational’

class TestTime < Test::Unit::TestCase
def test_time_add()
@@ -71,4 +72,38 @@
assert_equal(Time.at(0x7fffffff), Time.at(-0x80000000) -
(-0xffffffff))
end
end
+

  • def test_at
  • assert_equal(100000, Time.at(0.1).usec)
  • assert_equal(10000, Time.at(0.01).usec)
  • assert_equal(1000, Time.at(0.001).usec)
  • assert_equal(100, Time.at(0.0001).usec)
  • assert_equal(10, Time.at(0.00001).usec)
  • assert_equal(1, Time.at(0.000001).usec)
  • assert_equal(100000000, Time.at(0.1).nsec)
  • assert_equal(10000000, Time.at(0.01).nsec)
  • assert_equal(1000000, Time.at(0.001).nsec)
  • assert_equal(100000, Time.at(0.0001).nsec)
  • assert_equal(10000, Time.at(0.00001).nsec)
  • assert_equal(1000, Time.at(0.000001).nsec)
  • assert_equal(100, Time.at(0.0000001).nsec)
  • assert_equal(10, Time.at(0.00000001).nsec)
  • assert_equal(1, Time.at(0.000000001).nsec)
  • end
  • def test_at2
  • assert_equal(100, Time.at(0, 0.1).nsec)
  • assert_equal(10, Time.at(0, 0.01).nsec)
  • assert_equal(1, Time.at(0, 0.001).nsec)
  • end
  • def test_at_rational
  • assert_equal(1, Time.at(Rational(1,1) / 1000000000).nsec)
  • assert_equal(1, Time.at(1167609600 + Rational(1,1) /
    1000000000).nsec)
  • end
  • def test_utc_subsecond
  • assert_equal(100000, Time.utc(2007,1,1,0,0,1.1).usec)
  • assert_equal(100000, Time.utc(2007,1,1,0,0,Rational(11,10)).usec)
  • end
    end
    Index: test/yaml/test_yaml.rb
    ===================================================================
    — test/yaml/test_yaml.rb (revision 13950)
    +++ test/yaml/test_yaml.rb (working copy)
    @@ -54,7 +54,7 @@
    hour = zone[0,3].to_i * 3600
    min = zone[3,2].to_i * 60
    ofs = (hour + min)
  •  val = Time.at( val.to_f - ofs )
    
  •  val = Time.at( val.tv_sec - ofs, val.tv_nsec / 1000.0 )
    
    end
    return val
    end
    Index: file.c
    ===================================================================
    — file.c (revision 13950)
    +++ file.c (working copy)
    @@ -43,8 +43,6 @@

#include <time.h>

-VALUE rb_time_new(time_t, time_t);

#ifdef HAVE_UTIME_H
#include <utime.h>
#elif defined HAVE_SYS_UTIME_H
@@ -494,7 +492,57 @@
#endif
}

+static VALUE
+stat_atime(struct stat *st)
+{

  • time_t sec = st->st_atime;
  • long nsec;
    +#if defined(HAVE_STRUCT_STAT_ST_MTIM)
  • nsec = st->st_atim.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
  • nsec = st->st_atimespec.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
  • nsec = st->st_atimensec;
    +#else
  • nsec = 0;
    +#endif
  • return rb_time_nano_new(sec, nsec);
    +}

+static VALUE
+stat_mtime(struct stat *st)
+{

  • time_t sec = st->st_mtime;
  • long nsec;
    +#if defined(HAVE_STRUCT_STAT_ST_MTIM)
  • nsec = st->st_mtim.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
  • nsec = st->st_mtimespec.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
  • nsec = st->st_mtimensec;
    +#else
  • nsec = 0;
    +#endif
  • return rb_time_nano_new(sec, nsec);
    +}

+static VALUE
+stat_ctime(struct stat *st)
+{

  • time_t sec = st->st_ctime;
  • long nsec;
    +#if defined(HAVE_STRUCT_STAT_ST_MTIM)
  • nsec = st->st_ctim.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
  • nsec = st->st_ctimespec.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
  • nsec = st->st_ctimensec;
    +#else
  • nsec = 0;
    +#endif
  • return rb_time_nano_new(sec, nsec);
    +}

/*

  • call-seq:
  • stat.atime   => time
    

@@ -509,7 +557,7 @@
static VALUE
rb_stat_atime(VALUE self)
{

  • return rb_time_new(get_stat(self)->st_atime, 0);
  • return stat_atime(get_stat(self));
    }

/*
@@ -525,7 +573,7 @@
static VALUE
rb_stat_mtime(VALUE self)
{

  • return rb_time_new(get_stat(self)->st_mtime, 0);
  • return stat_mtime(get_stat(self));
    }

/*
@@ -543,7 +591,7 @@
static VALUE
rb_stat_ctime(VALUE self)
{

  • return rb_time_new(get_stat(self)->st_ctime, 0);
  • return stat_ctime(get_stat(self));
    }

/*
@@ -1594,7 +1642,7 @@

 if (rb_stat(fname, &st) < 0)

rb_sys_fail(StringValueCStr(fname));

  • return rb_time_new(st.st_atime, 0);
  • return stat_atime(&st);
    }

/*
@@ -1618,7 +1666,7 @@
if (fstat(fptr->fd, &st) == -1) {
rb_sys_fail(fptr->path);
}

  • return rb_time_new(st.st_atime, 0);
  • return stat_atime(&st);
    }

/*
@@ -1638,7 +1686,7 @@

 if (rb_stat(fname, &st) < 0)

rb_sys_fail(RSTRING_PTR(fname));

  • return rb_time_new(st.st_mtime, 0);
  • return stat_mtime(&st);
    }

/*
@@ -1661,7 +1709,7 @@
if (fstat(fptr->fd, &st) == -1) {
rb_sys_fail(fptr->path);
}

  • return rb_time_new(st.st_mtime, 0);
  • return stat_mtime(&st);
    }

/*
@@ -1683,7 +1731,7 @@

 if (rb_stat(fname, &st) < 0)

rb_sys_fail(RSTRING_PTR(fname));

  • return rb_time_new(st.st_ctime, 0);
  • return stat_ctime(&st);
    }

/*
@@ -1707,7 +1755,7 @@
if (fstat(fptr->fd, &st) == -1) {
rb_sys_fail(fptr->path);
}

  • return rb_time_new(st.st_ctime, 0);
  • return stat_ctime(&st);
    }

static void
@@ -3346,11 +3394,11 @@

switch (cmd) {
case ‘A’:

  •  return rb_time_new(st.st_atime, 0);
    
  •  return stat_atime(&st);
    
    case ‘M’:
  •  return rb_time_new(st.st_mtime, 0);
    
  •  return stat_mtime(&st);
    
    case ‘C’:
  •  return rb_time_new(st.st_ctime, 0);
    
  •  return stat_ctime(&st);
    
    }
    }

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:32310] Re: nanosecond Time and stat”
on Mon, 19 Nov 2007 12:56:30 +0900, Tanaka A. [email protected]
writes:

|> e$B:G6a!“e(Bnanosecond e$BC10L$Je(B timestamp e$B$,$”$k$h$&$G$9!#e(B
|
|e$B$3$l$C$F<!$Ne(B POSIX e$B$KF~$k$_$?$$$G$9$M!#e(B
|http://lkml.org/lkml/2007/4/26/622

e$B<h$j9~$s$G$b$h$$$H$O;W$$$^$9$,!"8E$$4D6-$G$bF0:n$9$k$s$G$7$ge(B
e$B$&$+!#e(Bmisssin.he$B$GBP1~$7$F$$$k$N$+$7$i!#e(B

|e$B$“$H!”$3$l$r;n$7$F$$$k$H$-$Ke(B Time e$B$N%$%s%9%?%s%9$K$9$G$K%$%se(B
|e$B%9%?%s%9JQ?t$,$“$k$H!“e(BTime#_dump e$B$G@8@.$7$?J8;zNs$N%$%s%9%?e(B
|e$B%s%9JQ?t$,L5;k$5$l$k$H$$$&5sF0$G:$$C$?$N$G!”$=$l$OJQ$($F$”$je(B
|e$B$^$9!#e(B
|
|e$B$“$H!“e(Brb_copy_generic_ivar e$B$G%3%T!<85$K%$%s%9%?%s%9JQ?t$,$Je(B
|e$B$+$C$?;~!”%3%T!<@h$N%$%s%9%?%s%9JQ?t$r>C$5$J$$$H$$$&5sF0$K$be(B
|e$B5$$,$D$$$F!”>C$9$h$&$KJQ$($F$"$j$^$9!#e(B

e$B$3$l$i$O%P%0$G$7$g$&$+$i!"$$$:$l$K$7$F$b<h$j9~$_$?$$$G$9!#e(B

In message “Re: [ruby-dev:32314] Re: nanosecond Time and stat”
on Mon, 19 Nov 2007 18:15:33 +0900, Tanaka A. [email protected]
writes:
|
|In article E1Iu2GD-0004Wh-1I@x31,
| Yukihiro M. [email protected] writes:
|
|> e$B<h$j9~$s$G$b$h$$$H$O;W$$$^$9$,!"8E$$4D6-$G$bF0:n$9$k$s$G$7$ge(B
|> e$B$&$+!#e(Bmisssin.he$B$GBP1~$7$F$$$k$N$+$7$i!#e(B
|
|e$BF0:n$9$k$O$:$G$9!#e(B

e$B;d$N$H$3$me(B(Linux 2.6.18 Debian sid)e$B$G$Oe(B

/home/matz/work/ruby/lib/fileutils.rb:1023:in `utime’: Function not
implemented - .rbconfig.time (Errno::ENOSYS)

e$B$H$$$&%(%i!<$,=P$Fe(Bminirubye$B$h$j@h$O%3%s%Q%$%k$G$-$^$;$s$G$7$?!#e(B

In article E1Iu2ud-0005en-TH@x31,
Yukihiro M. [email protected] writes:

e$B;d$N$H$3$me(B(Linux 2.6.18 Debian sid)e$B$G$Oe(B

/home/matz/work/ruby/lib/fileutils.rb:1023:in `utime’: Function not implemented - .rbconfig.time (Errno::ENOSYS)

e$B$H$$$&%(%i!<$,=P$Fe(Bminirubye$B$h$j@h$O%3%s%Q%$%k$G$-$^$;$s$G$7$?!#e(B

Linux e$B$re(B 2.6.22 e$B$K$9$k$H$I$&$G$9$+$M!#e(B

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:32316] Re: nanosecond Time and stat”
on Mon, 19 Nov 2007 18:31:51 +0900, Tanaka A. [email protected]
writes:

|> e$B;d$N$H$3$me(B(Linux 2.6.18 Debian sid)e$B$G$Oe(B
|>
|> /home/matz/work/ruby/lib/fileutils.rb:1023:in `utime’: Function not implemented - .rbconfig.time (Errno::ENOSYS)
|>
|> e$B$H$$$&%(%i!<$,=P$Fe(Bminirubye$B$h$j@h$O%3%s%Q%$%k$G$-$^$;$s$G$7$?!#e(B
|
|Linux e$B$re(B 2.6.22 e$B$K$9$k$H$I$&$G$9$+$M!#e(B

e$B$3$N%^%7%s$O$A$g$C$H%j%V!<%H$7$K$/$$%^%7%s$J$N$G!"3NG’$O$7$Pe(B
e$B$i$/BT$C$F$/$@$5$$$^$;!#$A$J$_$Ke(Bglibce$B$N%P!<%8%g%s$Oe(B
(glibc-2.3.2.ds1-22sarge6)e$B$G$7$?!#e(B

In article E1Iu2GD-0004Wh-1I@x31,
Yukihiro M. [email protected] writes:

e$B<h$j9~$s$G$b$h$$$H$O;W$$$^$9$,!"8E$$4D6-$G$bF0:n$9$k$s$G$7$ge(B
e$B$&$+!#e(Bmisssin.he$B$GBP1~$7$F$$$k$N$+$7$i!#e(B

e$BF0:n$9$k$O$:$G$9!#e(B

e$B$^$D$b$He(B e$B$f$-$R$m$G$9e(B

In message “Re: [ruby-dev:32318] Re: nanosecond Time and stat”
on Mon, 19 Nov 2007 18:52:14 +0900, Tanaka A. [email protected]
writes:
|
|In article E1Iu3Ba-00068q-Vd@x31,
| Yukihiro M. [email protected] writes:
|
|> |Linux e$B$re(B 2.6.22 e$B$K$9$k$H$I$&$G$9$+$M!#e(B
|>
|> e$B$3$N%^%7%s$O$A$g$C$H%j%V!<%H$7$K$/$$%^%7%s$J$N$G!“3NG’$O$7$Pe(B
|> e$B$i$/BT$C$F$/$@$5$$$^$;!#$A$J$_$Ke(Bglibce$B$N%P!<%8%g%s$Oe(B
|> (glibc-2.3.2.ds1-22sarge6)e$B$G$7$?!#e(B
|
|sid e$B$Ge(B glibc-2.3 e$B$C$F$$$&$N$OK\Ev$+$I$&$+2x$7$$$h$&$J5$$,$7e(B
|e$B$^$9$,!”$=$N>I>u$Oe(B utimensat e$B$re(B glibc e$B$OCN$C$F$$$k$,e(B kernel
|e$B$OCN$i$J$$$H$$$&$3$H$G$9!#e(B

e$B$9$_$^$;$s!“e(B2.6.1e$B$G$7$?!#0c$&$H$3$m8+$F$^$7$?!#e(B
debiane$B$@$He(Bkernele$B$r:9$7BX$($i$l$k$s$Ge(Bglibce$B$He(Bkernele$B$N%:%l$,H/@8e(B
e$B$9$k$3$H$O$”$j$($^$9!#e(B

|Linux e$B$Ke(B utimensat e$B$,F~$C$?$N$Oe(B 2.6.22 e$B$J$s$G$"$2$l$PD>$j$^e(B
|e$B$9$,!"e(BLinux e$B$rF~$l49$($m$H$$$&$N$O$A$g$C$H$J$K$+$J$H;W$$D>$7e(B
|e$B$F!"e(BENOSYS e$B$J$ie(B utmies e$B$Ke(B fallback e$B$9$k$h$&$K$7$F$_$?$s$G;ne(B
|e$B$7$F$/$@$5$$!#e(B

e$BF0$/$h$&$K$J$j$^$7$?!#$"$j$,$H$&$4$6$$$^$9!#e(B

In article E1Iu3Ba-00068q-Vd@x31,
Yukihiro M. [email protected] writes:

|Linux e$B$re(B 2.6.22 e$B$K$9$k$H$I$&$G$9$+$M!#e(B

e$B$3$N%^%7%s$O$A$g$C$H%j%V!<%H$7$K$/$$%^%7%s$J$N$G!"3NG’$O$7$Pe(B
e$B$i$/BT$C$F$/$@$5$$$^$;!#$A$J$_$Ke(Bglibce$B$N%P!<%8%g%s$Oe(B
(glibc-2.3.2.ds1-22sarge6)e$B$G$7$?!#e(B

sid e$B$Ge(B glibc-2.3 e$B$C$F$$$&$N$OK\Ev$+$I$&$+2x$7$$$h$&$J5$$,$7e(B
e$B$^$9$,!"$=$N>I>u$Oe(B utimensat e$B$re(B glibc e$B$OCN$C$F$$$k$,e(B
kernel
e$B$OCN$i$J$$$H$$$&$3$H$G$9!#e(B

Linux e$B$Ke(B utimensat e$B$,F~$C$?$N$Oe(B 2.6.22
e$B$J$s$G$"$2$l$PD>$j$^e(B
e$B$9$,!"e(BLinux e$B$rF~$l49$($m$H$$$&$N$O$A$g$C$H$J$K$+$J$H;W$$D>$7e(B
e$B$F!"e(BENOSYS e$B$J$ie(B utmies e$B$Ke(B fallback
e$B$9$k$h$&$K$7$F$_$?$s$G;ne(B
e$B$7$F$/$@$5$$!#e(B

In article [email protected],
Tanaka A. [email protected] writes:

e$B:G6a!“e(Bnanosecond e$BC10L$Je(B timestamp e$B$,$”$k$h$&$G$9!#e(B

e$B$3$l$C$F<!$Ne(B POSIX e$B$KF~$k$_$?$$$G$9$M!#e(B
http://lkml.org/lkml/2007/4/26/622

e$B9M$($F$$k$HEvA3$G$9$,!“e(Btimestamp e$B$r;2>H$9$k$@$1$8$c$J$/$F@e(B
e$BDj$9$k$N$b$“$C$F!”$=$l$Oe(B utimensat e$B$
$?$$$G$9!#e(BDebian sid
e$B$Ge(B
e$B;H$($?$N$G!”$=$l$bBP1~$7$F$
$^$7$?!#e(B(e$BMWe(B Linux 2.6.22
e$B0J9_e(B)

e$B$"$H!“e(BMarshal e$B$OG:$s$@$N$G<j$r$D$1$F$J$/$F!”:#$^$G$I$*$je(B
microsecond e$B$^$G$7$+=q$-=P$5$l$^$;$s!#FI$_=P$9$H$-$Oe(B
microsecond e$BL$K~$Oe(B 0 e$B$K$J$j$^$9!#e(B

e$B$$$m$$$m$H9M$($?$s$G$9$,!"%$%s%9%?%s%9JQ?t$Ge(B microsecond e$B0Je(B
e$B2<$rI=8=$9$k$3$H$K$7$F$_$^$7$?!#e(B

1.9 e$B$Ge(B dump e$B$7$?$b$N$re(B 1.8 e$B$Ge(B load e$B$9$k$He(B
microsecond e$B0J2<$,e(B
e$B$J$/$J$j$^$9!#e(B

e$B$“$H!”$3$l$r;n$7$F$$$k$H$-$Ke(B Time e$B$N%$%s%9%?%s%9$K$9$G$K%$%se(B
e$B%9%?%s%9JQ?t$,$“$k$H!“e(BTime#_dump e$B$G@8@.$7$?J8;zNs$N%$%s%9%?e(B
e$B%s%9JQ?t$,L5;k$5$l$k$H$$$&5sF0$G:$$C$?$N$G!”$=$l$OJQ$($F$”$je(B
e$B$^$9!#e(B

e$B$“$H!“e(Brb_copy_generic_ivar e$B$G%3%T!<85$K%$%s%9%?%s%9JQ?t$,$Je(B
e$B$+$C$?;~!”%3%T!<@h$N%$%s%9%?%s%9JQ?t$r>C$5$J$$$H$$$&5sF0$K$be(B
e$B5$$,$D$$$F!”>C$9$h$&$KJQ$($F$"$j$^$9!#e(B

Index: time.c

— time.c (revision 13961)
+++ time.c (working copy)
@@ -23,8 +23,10 @@
VALUE rb_cTime;
static VALUE time_utc_offset _((VALUE));

+static ID id_divmod, id_mul, id_submicro;
+
struct time_object {

  • struct timeval tv;
  • struct timespec ts;
    struct tm tm;
    int gmt;
    int tm_got;
    @@ -47,8 +49,8 @@

    obj = Data_Make_Struct(klass, struct time_object, 0, time_free,
    tobj);
    tobj->tm_got=0;

  • tobj->tv.tv_sec = 0;
  • tobj->tv.tv_usec = 0;
  • tobj->ts.tv_sec = 0;

  • tobj->ts.tv_nsec = 0;

    return obj;
    }
    @@ -93,11 +95,22 @@
    time_modify(time);
    GetTimeval(time, tobj);
    tobj->tm_got=0;

  • tobj->tv.tv_sec = 0;
  • tobj->tv.tv_usec = 0;
  • if (gettimeofday(&tobj->tv, 0) < 0) {
  • rb_sys_fail(“gettimeofday”);
  • tobj->ts.tv_sec = 0;

  • tobj->ts.tv_nsec = 0;
    +#ifdef HAVE_CLOCK_GETTIME

  • if (clock_gettime(CLOCK_REALTIME, &tobj->ts) == -1) {

  • rb_sys_fail(“clock_gettime”);
    }
    +#else

  • {

  •    struct timeval tv;
    
  •    if (gettimeofday(&tv, 0) < 0) {
    
  •        rb_sys_fail("gettimeofday");
    
  •    }
    
  •    tobj->ts.tv_sec = tv.tv_sec;
    
  •    tobj->ts.tv_nsec = tv.tv_usec * 1000;
    
  • }
    +#endif

    return time;
    }
    @@ -106,106 +119,137 @@
    #define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)

static void
-time_overflow_p(time_t *secp, time_t *usecp)
+time_overflow_p(time_t *secp, long *nsecp)
{

  • time_t tmp, sec = *secp, usec = *usecp;
  • time_t tmp, sec = *secp;
  • long nsec = *nsecp;
  • if (usec >= 1000000) { /* usec positive overflow */
  • tmp = sec + usec / 1000000;
  • usec %= 1000000;
  • if (nsec >= 1000000000) { /* nsec positive overflow */
  • tmp = sec + nsec / 1000000000;
  • nsec %= 1000000000;
    if (sec > 0 && tmp < 0) {
    rb_raise(rb_eRangeError, “out of Time range”);
    }
    sec = tmp;
    }
  • if (usec < 0) { /* usec negative overflow */
  • tmp = sec + NDIV(usec,1000000); /* negative div */
  • usec = NMOD(usec,1000000); /* negative mod */
  • if (nsec < 0) { /* nsec negative overflow */
  • tmp = sec + NDIV(nsec,1000000000); /* negative div */
  • nsec = NMOD(nsec,1000000000); /* negative mod */
    if (sec < 0 && tmp > 0) {
    rb_raise(rb_eRangeError, “out of Time range”);
    }
    sec = tmp;
    }
    #ifndef NEGATIVE_TIME_T
  • if (sec < 0 || (sec == 0 && usec < 0))
  • if (sec < 0)
    rb_raise(rb_eArgError, “time must be positive”);
    #endif
    *secp = sec;
  • *usecp = usec;
  • *nsecp = nsec;
    }

static VALUE
-time_new_internal(VALUE klass, time_t sec, time_t usec)
+time_new_internal(VALUE klass, time_t sec, long nsec)
{
VALUE time = time_s_alloc(klass);
struct time_object *tobj;

 GetTimeval(time, tobj);
  • time_overflow_p(&sec, &usec);
  • tobj->tv.tv_sec = sec;
  • tobj->tv.tv_usec = usec;
  • time_overflow_p(&sec, &nsec);

  • tobj->ts.tv_sec = sec;

  • tobj->ts.tv_nsec = nsec;

    return time;
    }

VALUE
-rb_time_new(time_t sec, time_t usec)
+rb_time_new(time_t sec, long usec)
{

  • return time_new_internal(rb_cTime, sec, usec);
  • return time_new_internal(rb_cTime, sec, usec * 1000);
    }

-static struct timeval
-time_timeval(VALUE time, int interval)
+VALUE
+rb_time_nano_new(time_t sec, long nsec)
{

  • struct timeval t;
  • return time_new_internal(rb_cTime, sec, nsec);
    +}

+static struct timespec
+time_timespec(VALUE num, int interval)
+{

  • struct timespec t;
    const char *tstr = interval ? “time interval” : “time”;
  • VALUE i, f, ary;

#ifndef NEGATIVE_TIME_T
interval = 1;
#endif

  • switch (TYPE(time)) {
  • switch (TYPE(num)) {
    case T_FIXNUM:
  • t.tv_sec = FIX2LONG(time);
  • t.tv_sec = FIX2LONG(num);
    if (interval && t.tv_sec < 0)
    rb_raise(rb_eArgError, “%s must be positive”, tstr);
  • t.tv_usec = 0;
  • t.tv_nsec = 0;
    break;

    case T_FLOAT:
    
  • if (interval && RFLOAT_VALUE(time) < 0.0)
  • if (interval && RFLOAT_VALUE(num) < 0.0)
    rb_raise(rb_eArgError, “%s must be positive”, tstr);
    else {
    double f, d;
  •  d = modf(RFLOAT_VALUE(time), &f);
    
  •  d = modf(RFLOAT_VALUE(num), &f);
     t.tv_sec = (time_t)f;
     if (f != t.tv_sec) {
    
  • rb_raise(rb_eRangeError, “%f out of Time range”,
    RFLOAT_VALUE(time));
  • rb_raise(rb_eRangeError, “%f out of Time range”,
    RFLOAT_VALUE(num));
    }
  •  t.tv_usec = (time_t)(d*1e6+0.5);
    
  •  t.tv_nsec = (long)(d*1e9+0.5);
    

    }
    break;

     case T_BIGNUM:
    
  • t.tv_sec = NUM2LONG(time);
  • t.tv_sec = NUM2LONG(num);
    if (interval && t.tv_sec < 0)
    rb_raise(rb_eArgError, “%s must be positive”, tstr);
  • t.tv_usec = 0;
  • t.tv_nsec = 0;
    break;

    default:
    
  • rb_raise(rb_eTypeError, “can’t convert %s into %s”,
  • rb_obj_classname(time), tstr);
    
  •    ary = rb_check_array_type(rb_funcall(num, id_divmod, 1, 
    

INT2FIX(1)));

  •    if (NIL_P(ary)) {
    
  •        rb_raise(rb_eTypeError, "can't convert %s into %s",
    
  •                 rb_obj_classname(num), tstr);
    
  •    }
    
  •    i = rb_ary_entry(ary, 0);
    
  •    f = rb_ary_entry(ary, 1);
    
  •    t.tv_sec = NUM2LONG(i);
    
  • if (interval && t.tv_sec < 0)
  •  rb_raise(rb_eArgError, "%s must be positive", tstr);
    
  •    f = rb_funcall(f, id_mul, 1, INT2FIX(1000000000));
    
  •    t.tv_nsec = NUM2LONG(f);
    
    break;
    }
    return t;
    }

+static struct timeval
+time_timeval(VALUE num, int interval)
+{

  • struct timespec ts;
  • struct timeval tv;
  • ts = time_timespec(num, interval);
  • tv.tv_sec = ts.tv_sec;
  • tv.tv_usec = ts.tv_nsec / 1000;
  • return tv;
    +}

struct timeval
-rb_time_interval(VALUE time)
+rb_time_interval(VALUE num)
{

  • return time_timeval(time, Qtrue);
  • return time_timeval(num, Qtrue);
    }

struct timeval
@@ -216,12 +260,27 @@

 if (TYPE(time) == T_DATA && RDATA(time)->dfree == time_free) {

GetTimeval(time, tobj);

  • t = tobj->tv;
  •    t.tv_sec = tobj->ts.tv_sec;
    
  •    t.tv_usec = tobj->ts.tv_nsec / 1000;
    
    return t;
    }
    return time_timeval(time, Qfalse);
    }

+struct timespec
+rb_time_timespec(VALUE time)
+{

  • struct time_object *tobj;
  • struct timespec t;
  • if (TYPE(time) == T_DATA && RDATA(time)->dfree == time_free) {
  • GetTimeval(time, tobj);
  •    t = tobj->ts;
    
  • return t;
  • }
  • return time_timespec(time, Qfalse);
    +}

/*

  • call-seq:
  • Time.at( aTime ) => time
    

@@ -229,7 +288,7 @@
*

  • Creates a new time object with the value given by aTime, or
  • the given number of seconds (and optional
    • microseconds) from epoch. A non-portable feature allows the
    • microseconds) from the Epoch. A non-portable feature allows
      the
    • offset to be negative on some systems.
    • Time.at(0)            #=> Wed Dec 31 18:00:00 CST 1969
      

@@ -240,17 +299,17 @@
static VALUE
time_s_at(int argc, VALUE *argv, VALUE klass)
{

  • struct timeval tv;
  • struct timespec ts;
    VALUE time, t;

    if (rb_scan_args(argc, argv, “11”, &time, &t) == 2) {

  • tv.tv_sec = NUM2LONG(time);
  • tv.tv_usec = NUM2LONG(t);
  • ts.tv_sec = NUM2LONG(time);
  • ts.tv_nsec = NUM2LONG(rb_funcall(t, id_mul, 1, INT2FIX(1000)));
    }
    else {
  • tv = rb_time_timeval(time);
  • ts = rb_time_timespec(time);
    }
  • t = time_new_internal(klass, tv.tv_sec, tv.tv_usec);
  • t = time_new_internal(klass, ts.tv_sec, ts.tv_nsec);
    if (TYPE(time) == T_DATA && RDATA(time)->dfree == time_free) {
    struct time_object *tobj, *tobj2;

@@ -276,15 +335,42 @@
return NUM2LONG(obj);
}

+static long
+obj2nsec(VALUE obj, long *nsec)
+{

  • struct timespec ts;
  • if (TYPE(obj) == T_STRING) {
  • obj = rb_str_to_inum(obj, 10, Qfalse);
  •    *nsec = 0;
    
  •    return NUM2LONG(obj) * 1000;
    
  • }
  • ts = time_timespec(obj, 1);
  • *nsec = ts.tv_nsec;
  • return ts.tv_sec;
    +}

+static long
+obj2long1000(VALUE obj)
+{

  • if (TYPE(obj) == T_STRING) {
  • obj = rb_str_to_inum(obj, 10, Qfalse);
  •    return NUM2LONG(obj) * 1000;
    
  • }
  • return NUM2LONG(rb_funcall(obj, id_mul, 1, INT2FIX(1000)));
    +}

static void
-time_arg(int argc, VALUE *argv, struct tm *tm, time_t *usec)
+time_arg(int argc, VALUE *argv, struct tm *tm, long *nsec)
{
VALUE v[8];
int i;
long year;

 MEMZERO(tm, struct tm, 1);
  • *usec = 0;
  • *nsec = 0;
    if (argc == 10) {
    v[0] = argv[5];
    v[1] = argv[4];
    @@ -352,12 +438,13 @@
    }
    tm->tm_hour = NIL_P(v[3])?0:obj2long(v[3]);
    tm->tm_min = NIL_P(v[4])?0:obj2long(v[4]);
  • tm->tm_sec = NIL_P(v[5])?0:obj2long(v[5]);
  • if (!NIL_P(v[6])) {
  • if (!NIL_P(v[6]) && argc == 7) {
  •    tm->tm_sec  = NIL_P(v[5])?0:obj2long(v[5]);
    
  •    *nsec = obj2long1000(v[6]);
    
  • }
  • else {
    /* when argc == 8, v[6] is timezone, but ignored */
  • if (argc == 7) {
  •  *usec = obj2long(v[6]);
    
  • }
  •    tm->tm_sec  = NIL_P(v[5])?0:obj2nsec(v[5], nsec);
    

    }

    /* value validation */
    @@ -774,10 +861,10 @@
    {
    struct tm tm;
    VALUE time;

  • time_t usec;
  • long nsec;
  • time_arg(argc, argv, &tm, &usec);
  • time = time_new_internal(klass, make_time_t(&tm, utc_p), usec);
  • time_arg(argc, argv, &tm, &nsec);
  • time = time_new_internal(klass, make_time_t(&tm, utc_p), nsec);
    if (utc_p) return time_gmtime(time);
    return time_localtime(time);
    }
    @@ -834,7 +921,7 @@
  • time.tv_sec => int
    
  • Returns the value of time as an integer number of seconds
    • since epoch.
    • since the Epoch.
    • t = Time.now
      
    • "%10.5f" % t.to_f   #=> "1049896564.17839"
      

@@ -847,7 +934,7 @@
struct time_object *tobj;

 GetTimeval(time, tobj);
  • return LONG2NUM(tobj->tv.tv_sec);
  • return LONG2NUM(tobj->ts.tv_sec);
    }

/*
@@ -855,11 +942,14 @@

  • time.to_f => float
    
  • Returns the value of time as a floating point number of
    • seconds since epoch.
    • seconds since the Epoch.
    • t = Time.now
      
    • "%10.5f" % t.to_f   #=> "1049896564.13654"
      
    • t.to_i              #=> 1049896564
      
    • Note that IEEE 754 double is not accurate enough to represent
    • nanoseconds from the Epoch.
      */

static VALUE
@@ -868,7 +958,7 @@
struct time_object *tobj;

 GetTimeval(time, tobj);
  • return
    DOUBLE2NUM((double)tobj->tv.tv_sec+(double)tobj->tv.tv_usec/1e6);
  • return
    DOUBLE2NUM((double)tobj->ts.tv_sec+(double)tobj->ts.tv_nsec/1e9);
    }

/*
@@ -889,17 +979,43 @@
struct time_object *tobj;

 GetTimeval(time, tobj);
  • return LONG2NUM(tobj->tv.tv_usec);
  • return LONG2NUM(tobj->ts.tv_nsec/1000);
    }

/*

  • call-seq:
    • time.nsec    => int
      
    • time.tv_nsec => int
      
    • Returns just the number of nanoseconds for time.
    • t = Time.now        #=> 2007-11-17 15:18:03 +0900
      
    • "%10.9f" % t.to_f   #=> "1195280283.536151409"
      
    • t.nsec              #=> 536151406
      
    • The lowest digit of to_f and nsec is different because
    • IEEE 754 double is not accurate enough to represent
    • nanoseconds from the Epoch.
    • The correct value is returned by nsec.
  • */

+static VALUE
+time_nsec(VALUE time)
+{

  • struct time_object *tobj;
  • GetTimeval(time, tobj);
  • return LONG2NUM(tobj->ts.tv_nsec);
    +}

+/*

    • call-seq:
    • time <=> other_time => -1, 0, +1
      
    • time <=> numeric    => -1, 0, +1
      
    • Comparison—Compares time with other_time or with
    • numeric, which is the number of seconds (possibly
    • fractional) since epoch.
    • fractional) since the Epoch.
    • t = Time.now       #=> Wed Apr 09 08:56:03 CDT 2003
      
    • t2 = t + 2592000   #=> Fri May 09 08:56:03 CDT 2003
      

@@ -916,12 +1032,12 @@
GetTimeval(time1, tobj1);
if (TYPE(time2) == T_DATA && RDATA(time2)->dfree == time_free) {
GetTimeval(time2, tobj2);

  • if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) {
  •  if (tobj1->tv.tv_usec == tobj2->tv.tv_usec) return INT2FIX(0);
    
  •  if (tobj1->tv.tv_usec > tobj2->tv.tv_usec) return INT2FIX(1);
    
  • if (tobj1->ts.tv_sec == tobj2->ts.tv_sec) {
  •  if (tobj1->ts.tv_nsec == tobj2->ts.tv_nsec) return INT2FIX(0);
    
  •  if (tobj1->ts.tv_nsec > tobj2->ts.tv_nsec) return INT2FIX(1);
     return INT2FIX(-1);
    
    }
  • if (tobj1->tv.tv_sec > tobj2->tv.tv_sec) return INT2FIX(1);
  • if (tobj1->ts.tv_sec > tobj2->ts.tv_sec) return INT2FIX(1);
    return INT2FIX(-1);
    }

@@ -945,8 +1061,8 @@
GetTimeval(time1, tobj1);
if (TYPE(time2) == T_DATA && RDATA(time2)->dfree == time_free) {
GetTimeval(time2, tobj2);

  • if (tobj1->tv.tv_sec == tobj2->tv.tv_sec) {
  •  if (tobj1->tv.tv_usec == tobj2->tv.tv_usec) return Qtrue;
    
  • if (tobj1->ts.tv_sec == tobj2->ts.tv_sec) {

  •  if (tobj1->ts.tv_nsec == tobj2->ts.tv_nsec) return Qtrue;
    

    }
    }
    return Qfalse;
    @@ -995,7 +1111,7 @@
    long hash;

    GetTimeval(time, tobj);

  • hash = tobj->tv.tv_sec ^ tobj->tv.tv_usec;
  • hash = tobj->ts.tv_sec ^ tobj->ts.tv_nsec;
    return LONG2FIX(hash);
    }

@@ -1053,7 +1169,7 @@
else {
time_modify(time);
}

  • t = tobj->tv.tv_sec;
  • t = tobj->ts.tv_sec;
    tm_tmp = localtime(&t);
    if (!tm_tmp)
    rb_raise(rb_eArgError, “localtime error”);
    @@ -1096,7 +1212,7 @@
    else {
    time_modify(time);
    }
  • t = tobj->tv.tv_sec;
  • t = tobj->ts.tv_sec;
    tm_tmp = gmtime(&t);
    if (!tm_tmp)
    rb_raise(rb_eArgError, “gmtime error”);
    @@ -1211,8 +1327,7 @@
    len = strftime(buf, 128, “%Y-%m-%d %H:%M:%S UTC”, &tobj->tm);
    }
    else {
  • time_t off;
  • char buf2[32];
  • long off;
    char sign = ‘+’;
    #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
    off = tobj->tm.tm_gmtoff;
    @@ -1224,9 +1339,9 @@
    sign = ‘-’;
    off = -off;
    }
  • sprintf(buf2, “%%Y-%%m-%%d %%H:%%M:%%S %c%02d%02d”,
  • sign, (int)(off/3600), (int)(off%3600/60));
  • len = strftime(buf, 128, buf2, &tobj->tm);
  • len = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S ", &tobj->tm);
  •    len += snprintf(buf+len, sizeof(buf)-len, "%c%02d%02d", sign,
    
  •                    (int)(off/3600), (int)(off%3600/60));
    
    }
    return rb_str_new(buf, len);
    }
    @@ -1237,7 +1352,8 @@
    double v = NUM2DBL(offset);
    double f, d;
    unsigned_time_t sec_off;
  • time_t usec_off, sec, usec;
  • time_t sec;

  • long nsec_off, nsec;
    VALUE result;

    if (v < 0) {
    @@ -1249,21 +1365,21 @@
    if (f != (double)sec_off)
    rb_raise(rb_eRangeError, “time %s %f out of Time range”,
    sign < 0 ? “-” : “+”, v);

  • usec_off = (time_t)(d*1e6+0.5);
  • nsec_off = (long)(d*1e9+0.5);

    if (sign < 0) {

  • sec = tobj->tv.tv_sec - sec_off;
  • usec = tobj->tv.tv_usec - usec_off;
  • if (sec > tobj->tv.tv_sec)
  • sec = tobj->ts.tv_sec - sec_off;
  • nsec = tobj->ts.tv_nsec - nsec_off;
  • if (sec > tobj->ts.tv_sec)
    rb_raise(rb_eRangeError, “time - %f out of Time range”, v);
    }
    else {
  • sec = tobj->tv.tv_sec + sec_off;
  • usec = tobj->tv.tv_usec + usec_off;
  • if (sec < tobj->tv.tv_sec)
  • sec = tobj->ts.tv_sec + sec_off;
  • nsec = tobj->ts.tv_nsec + nsec_off;
  • if (sec < tobj->ts.tv_sec)
    rb_raise(rb_eRangeError, “time + %f out of Time range”, v);
    }
  • result = rb_time_new(sec, usec);
  • result = rb_time_nano_new(sec, nsec);
    if (tobj->gmt) {
    GetTimeval(result, tobj);
    tobj->gmt = 1;
    @@ -1320,11 +1436,11 @@
    double f;

GetTimeval(time2, tobj2);

  •    if (tobj->tv.tv_sec < tobj2->tv.tv_sec)
    
  •        f = -(double)(unsigned_time_t)(tobj2->tv.tv_sec - 
    

tobj->tv.tv_sec);

  •    if (tobj->ts.tv_sec < tobj2->ts.tv_sec)
    
  •        f = -(double)(unsigned_time_t)(tobj2->ts.tv_sec - 
    

tobj->ts.tv_sec);
else

  •        f = (double)(unsigned_time_t)(tobj->tv.tv_sec - 
    

tobj2->tv.tv_sec);

  • f += ((double)tobj->tv.tv_usec - (double)tobj2->tv.tv_usec)*1e-6;
  •        f = (double)(unsigned_time_t)(tobj->ts.tv_sec - 
    

tobj2->ts.tv_sec);

  • f += ((double)tobj->ts.tv_nsec - (double)tobj2->ts.tv_nsec)*1e-9;

    return DOUBLE2NUM(f);
    }
    @@ -1346,7 +1462,7 @@

    GetTimeval(time, tobj);
    gmt = tobj->gmt;

  • time = rb_time_new(tobj->tv.tv_sec + 1, tobj->tv.tv_usec);
  • time = rb_time_nano_new(tobj->ts.tv_sec + 1, tobj->ts.tv_nsec);
    GetTimeval(time, tobj);
    tobj->gmt = gmt;
    return time;
    @@ -1747,7 +1863,7 @@
    time_t t;
    long off;
    l = &tobj->tm;
  • t = tobj->tv.tv_sec;
  • t = tobj->ts.tv_sec;
    u = gmtime(&t);
    if (!u)
    rb_raise(rb_eArgError, “gmtime error”);
    @@ -1933,11 +2049,13 @@
    unsigned long p, s;
    char buf[8];
    time_t t;

  • int nsec;
    int i;

  • VALUE str;

    GetTimeval(time, tobj);

  • t = tobj->tv.tv_sec;
  • t = tobj->ts.tv_sec;
    tm = gmtime(&t);

    if ((tm->tm_year & 0xffff) != tm->tm_year)
    @@ -1951,7 +2069,8 @@
    tm->tm_hour; /* 5 /
    s = tm->tm_min << 26 | /
    6 /
    tm->tm_sec << 20 | /
    6 */

  • tobj->tv.tv_usec; /* 20 */
  • tobj->ts.tv_nsec / 1000; /* 20 */

  • nsec = tobj->ts.tv_nsec % 1000;

    for (i=0; i<4; i++) {
    buf[i] = p & 0xff;
    @@ -1962,7 +2081,28 @@
    s = RSHIFT(s, 8);
    }

  • return rb_str_new(buf, 8);
  • str = rb_str_new(buf, 8);
  • rb_copy_generic_ivar(str, time);
  • if (nsec) {
  •    /*
    
  •     * submicro is formatted in fixed-point packed BCD (without 
    

sign).

  •     * It represent digits under microsecond.
    
  •     * For nanosecond resolution, 3 digits (2 bytes) are used.
    
  •     * However it can be longer.
    
  •     * Extra digits are ignored for loading.
    
  •     */
    
  •    unsigned char buf[2];
    
  •    int len = sizeof(buf);
    
  •    buf[1] = (nsec % 10) << 4;
    
  •    nsec /= 10;
    
  •    buf[0] = nsec % 10;
    
  •    nsec /= 10;
    
  •    buf[0] |= (nsec % 10) << 4;
    
  •    if (buf[1] == 0)
    
  •        len = 1;
    
  •    rb_ivar_set(str, id_submicro, rb_str_new((char *)buf, len));
    
  • }
  • return str;
    }

/*
@@ -1979,7 +2119,6 @@

 rb_scan_args(argc, argv, "01", 0);
 str = time_mdump(time);
  • rb_copy_generic_ivar(str, time);

    return str;
    }
    @@ -1993,12 +2132,22 @@
    {
    struct time_object *tobj;
    unsigned long p, s;

  • time_t sec, usec;

  • time_t sec;

  • long usec;
    unsigned char *buf;
    struct tm tm;
    int i, gmt;

  • long nsec;

  • VALUE submicro;

    time_modify(time);

  • submicro = rb_attr_get(str, id_submicro);

  • if (submicro != Qnil) {

  •    st_delete(rb_generic_ivar_table(str), (st_data_t*)&id_submicro, 
    

0);

  • }

  • rb_copy_generic_ivar(time, str);

  • StringValue(str);
    buf = (unsigned char *)RSTRING_PTR(str);
    if (RSTRING_LEN(str) != 8) {
    @@ -2017,8 +2166,12 @@
    gmt = 0;
    sec = p;
    usec = s;

  •    nsec = usec * 1000;
    

    }
    else {

  •    unsigned char *ptr;
    
  •    long len;
    
  • p &= ~(1UL<<31);
    gmt = (p >> 30) & 0x1;
    tm.tm_year = (p >> 14) & 0xffff;
    @@ -2030,15 +2183,29 @@
    tm.tm_isdst = 0;

    sec = make_time_t(&tm, Qtrue);

  • usec = (time_t)(s & 0xfffff);
  • usec = (long)(s & 0xfffff);
  •    nsec = usec * 1000;
    
  •    if (submicro != Qnil) {
    
  •        ptr = (unsigned char*)StringValuePtr(submicro);
    
  •        len = RSTRING_LEN(submicro);
    
  •        if (0 < len) {
    
  •            nsec += (ptr[0] >> 4) * 100;
    
  •            nsec += (ptr[0] & 0xf) * 10;
    
  •        }
    
  •        if (1 < len) {
    
  •            nsec += (ptr[1] >> 4);
    
  •        }
    
  •    }
    
    }
  • time_overflow_p(&sec, &usec);
  • time_overflow_p(&sec, &nsec);

    GetTimeval(time, tobj);
    tobj->tm_got = 0;
    tobj->gmt = gmt;

  • tobj->tv.tv_sec = sec;
  • tobj->tv.tv_usec = usec;
  • tobj->ts.tv_sec = sec;
  • tobj->ts.tv_nsec = nsec;
  • return time;
    }

@@ -2054,7 +2221,6 @@
{
VALUE time = time_s_alloc(klass);

  • rb_copy_generic_ivar(time, str);
    time_mload(time, str);
    return time;
    }
    @@ -2062,7 +2228,7 @@
    /*
  • Time is an abstraction of dates and times. Time is
  • stored internally as the number of seconds and microseconds since
    • the epoch, January 1, 1970 00:00 UTC. On some operating
    • the Epoch, January 1, 1970 00:00 UTC. On some operating
    • systems, this offset is allowed to be negative. Also see the
    • library modules Date and ParseDate. The
    • Time class treats GMT (Greenwich Mean Time) and UTC
      @@ -2080,6 +2246,10 @@
      void
      Init_Time(void)
      {
  • id_divmod = rb_intern(“divmod”);
  • id_mul = rb_intern(“*”);
  • id_submicro = rb_intern(“submicro”);
  • rb_cTime = rb_define_class(“Time”, rb_cObject);
    rb_include_module(rb_cTime, rb_mComparable);

@@ -2147,6 +2317,8 @@
rb_define_method(rb_cTime, “tv_sec”, time_to_i, 0);
rb_define_method(rb_cTime, “tv_usec”, time_usec, 0);
rb_define_method(rb_cTime, “usec”, time_usec, 0);

  • rb_define_method(rb_cTime, “tv_nsec”, time_nsec, 0);

  • rb_define_method(rb_cTime, “nsec”, time_nsec, 0);

    rb_define_method(rb_cTime, “strftime”, time_strftime, 1);

Index: include/ruby/missing.h

— include/ruby/missing.h (revision 13961)
+++ include/ruby/missing.h (working copy)
@@ -25,13 +25,20 @@

define time_t long

struct timeval {
time_t tv_sec; /* seconds */

  • time_t tv_usec; /* microseconds */
  • long tv_usec; /* microseconds */
    };
    #endif
    #if defined(HAVE_SYS_TYPES_H)

include <sys/types.h>

#endif

+#if !defined(HAVE_STRUCT_TIMESPEC)
+struct timespec {

  • time_t tv_sec; /* seconds */
  • long tv_nsec; /* nanoseconds */
    +};
    +#endif

#ifndef HAVE_ACOSH
extern double acosh(double);
extern double asinh(double);
Index: include/ruby/intern.h

— include/ruby/intern.h (revision 13961)
+++ include/ruby/intern.h (working copy)
@@ -560,7 +560,8 @@
VALUE rb_barrier_wait(VALUE self);
VALUE rb_barrier_release(VALUE self);
/* time.c /
-VALUE rb_time_new(time_t, time_t);
+VALUE rb_time_new(time_t, long);
+VALUE rb_time_nano_new(time_t, long);
/
variable.c */
VALUE rb_mod_name(VALUE);
VALUE rb_class_path(VALUE);
Index: configure.in

— configure.in (revision 13961)
+++ configure.in (working copy)
@@ -513,6 +513,7 @@
AC_CHECK_LIB(dl, dlopen) # Dynamic linking for SunOS/Solaris and SYSV
AC_CHECK_LIB(dld, shl_load) # Dynamic linking for HP-UX
AC_CHECK_LIB(socket, socketpair) # SunOS/Solaris
+AC_CHECK_LIB(rt, clock_gettime) # GNU/Linux

case “$target_cpu” in
alpha*) case “$target_os”::“$GCC” in
@@ -553,7 +554,18 @@
AC_STRUCT_ST_BLKSIZE
AC_STRUCT_ST_BLOCKS
AC_STRUCT_ST_RDEV
+AC_CHECK_MEMBERS([struct stat.st_atim])
+AC_CHECK_MEMBERS([struct stat.st_atimespec])
+AC_CHECK_MEMBERS([struct stat.st_atimensec])
+AC_CHECK_MEMBERS([struct stat.st_mtim])
+AC_CHECK_MEMBERS([struct stat.st_mtimespec])
+AC_CHECK_MEMBERS([struct stat.st_mtimensec])
+AC_CHECK_MEMBERS([struct stat.st_ctim])
+AC_CHECK_MEMBERS([struct stat.st_ctimespec])
+AC_CHECK_MEMBERS([struct stat.st_ctimensec])

+AC_CHECK_TYPES(struct timespec)
+
AC_CHECK_TYPE(fd_mask, [AC_DEFINE(HAVE_RB_FD_INIT, 1)])

AC_CACHE_CHECK(for stack end address, rb_cv_stack_end_address,
@@ -587,14 +599,16 @@
strchr strstr strtoul crypt flock vsnprintf
isnan finite isinf hypot acosh erf strlcpy strlcat)
AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall chroot
fsync getcwd eaccess\

  •    truncate chsize times utimes fcntl lockf lstat link symlink 
    

readlink\

  •    truncate chsize times utimes utimensat fcntl lockf lstat\
    
  •          link symlink readlink\
       setitimer setruid seteuid setreuid setresuid setproctitle 
    

socketpair
setrgid setegid setregid setresgid issetugid pause lchown
lchmod
getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups
getpriority getrlimit setrlimit sysconf group_member
dlopen sigprocmask sigaction _setjmp vsnprintf snprintf\

  •    setsid telldir seekdir fchmod mktime timegm cosh sinh tanh log2 
    

round\

  •    setuid setgid daemon select_large_fdset setenv unsetenv)
    
  •    setsid telldir seekdir fchmod cosh sinh tanh log2 round\
    
  •    setuid setgid daemon select_large_fdset setenv unsetenv\
    
  •          mktime timegm clock_gettime)
    

AC_ARG_ENABLE(setreuid,
[ --enable-setreuid use setreuid()/setregid() according
to need even if obsolete.],
[use_setreuid=$enableval])
Index: variable.c

— variable.c (revision 13961)
+++ variable.c (working copy)
@@ -907,10 +907,20 @@
st_data_t data;

 if (!generic_iv_tbl) return;
  • if (!FL_TEST(obj, FL_EXIVAR)) return;
  • if (!FL_TEST(obj, FL_EXIVAR)) {
    +clear:

  •    if (FL_TEST(clone, FL_EXIVAR)) {
    
  •        rb_free_generic_ivar(clone);
    
  •        FL_UNSET(clone, FL_EXIVAR);
    
  •    }
    
  •    return;
    
  • }
    if (st_lookup(generic_iv_tbl, obj, &data)) {
    st_table *tbl = (st_table *)data;

  •    if (tbl->num_entries == 0)
    
  •        goto clear;
    
  • if (st_lookup(generic_iv_tbl, clone, &data)) {
    st_free_table((st_table *)data);
    st_insert(generic_iv_tbl, clone, (st_data_t)st_copy(tbl));
    Index: lib/time.rb
    ===================================================================
    — lib/time.rb (revision 13961)
    +++ lib/time.rb (working copy)
    @@ -461,10 +461,10 @@
    year, mon, day, hour, min, sec) +
    if fraction_digits == 0
    ‘’

  • elsif fraction_digits <= 6
  •  '.' + sprintf('%06d', usec)[0, fraction_digits]
    
  • elsif fraction_digits <= 9
  •  '.' + sprintf('%09d', nsec)[0, fraction_digits]
    
    else
  •  '.' + sprintf('%06d', usec) + '0' * (fraction_digits - 6)
    
  •  '.' + sprintf('%09d', nsec) + '0' * (fraction_digits - 9)
    
    end +
    if utc?
    ‘Z’
    Index: marshal.c
    ===================================================================
    — marshal.c (revision 13961)
    +++ marshal.c (working copy)
    @@ -601,17 +601,22 @@
    }
    if (rb_respond_to(obj, s_dump)) {
    VALUE v;
  •        st_table *ivtbl2 = 0;
    
  •        int hasiv2;
    
     v = rb_funcall(obj, s_dump, 1, INT2NUM(limit));
     if (TYPE(v) != T_STRING) {
    
    rb_raise(rb_eTypeError, “_dump() must return string”);
    }
  •  if (!hasiv && (hasiv = has_ivars(v, ivtbl)) != 0) {
    
  •  if ((hasiv2 = has_ivars(v, ivtbl2)) != 0 && !hasiv) {
    
    w_byte(TYPE_IVAR, arg);
    }
    w_class(TYPE_USERDEF, obj, arg, Qfalse);
    w_bytes(RSTRING_PTR(v), RSTRING_LEN(v), arg);
  •  if (hasiv) {
    
  •        if (hasiv2) {
    
  • w_ivar(obj, ivtbl2, &c_arg);
  •        }
    
  •        else if (hasiv) {
    
    w_ivar(obj, ivtbl, &c_arg);
    }
    return;
    Index: test/ruby/test_time.rb
    ===================================================================
    — test/ruby/test_time.rb (revision 13961)
    +++ test/ruby/test_time.rb (working copy)
    @@ -1,4 +1,5 @@
    require ‘test/unit’
    +require ‘rational’

class TestTime < Test::Unit::TestCase
def test_time_add()
@@ -81,4 +82,67 @@
end
assert_equal(1.0, bigtime1 - bigtime0)
end
+

  • def test_at
  • assert_equal(100000, Time.at(0.1).usec)
  • assert_equal(10000, Time.at(0.01).usec)
  • assert_equal(1000, Time.at(0.001).usec)
  • assert_equal(100, Time.at(0.0001).usec)
  • assert_equal(10, Time.at(0.00001).usec)
  • assert_equal(1, Time.at(0.000001).usec)
  • assert_equal(100000000, Time.at(0.1).nsec)
  • assert_equal(10000000, Time.at(0.01).nsec)
  • assert_equal(1000000, Time.at(0.001).nsec)
  • assert_equal(100000, Time.at(0.0001).nsec)
  • assert_equal(10000, Time.at(0.00001).nsec)
  • assert_equal(1000, Time.at(0.000001).nsec)
  • assert_equal(100, Time.at(0.0000001).nsec)
  • assert_equal(10, Time.at(0.00000001).nsec)
  • assert_equal(1, Time.at(0.000000001).nsec)
  • end
  • def test_at2
  • assert_equal(100, Time.at(0, 0.1).nsec)
  • assert_equal(10, Time.at(0, 0.01).nsec)
  • assert_equal(1, Time.at(0, 0.001).nsec)
  • end
  • def test_at_rational
  • assert_equal(1, Time.at(Rational(1,1) / 1000000000).nsec)
  • assert_equal(1, Time.at(1167609600 + Rational(1,1) /
    1000000000).nsec)
  • end
  • def test_utc_subsecond
  • assert_equal(100000, Time.utc(2007,1,1,0,0,1.1).usec)
  • assert_equal(100000, Time.utc(2007,1,1,0,0,Rational(11,10)).usec)
  • end
  • def test_eq_nsec
  • assert_equal(Time.at(0, 0.123), Time.at(0, 0.123))
  • assert_not_equal(Time.at(0, 0.123), Time.at(0, 0.124))
  • end
  • def assert_marshal_roundtrip(t)
  • iv_names = t.instance_variables
  • iv_vals1 = iv_names.map {|n| t.instance_variable_get n }
  • m = Marshal.dump(t)
  • t2 = Marshal.load(m)
  • iv_vals2 = iv_names.map {|n| t2.instance_variable_get n }
  • assert_equal(t, t2)
  • assert_equal(iv_vals1, iv_vals2)
  • t2
  • end
  • def test_marshal_nsec
  • assert_marshal_roundtrip(Time.at(0, 0.123))
  • assert_marshal_roundtrip(Time.at(0, 0.120))
  • end
  • def test_marshal_ivar
  • t = Time.at(123456789, 987654.321)
  • t.instance_eval { @var = 135 }
  • assert_marshal_roundtrip(t)
  • assert_marshal_roundtrip(Marshal.load(Marshal.dump(t)))
  • end

end
Index: test/yaml/test_yaml.rb

— test/yaml/test_yaml.rb (revision 13961)
+++ test/yaml/test_yaml.rb (working copy)
@@ -54,7 +54,7 @@
hour = zone[0,3].to_i * 3600
min = zone[3,2].to_i * 60
ofs = (hour + min)

  •  val = Time.at( val.to_f - ofs )
    
  •  val = Time.at( val.tv_sec - ofs, val.tv_nsec / 1000.0 )
    
    end
    return val
    end
    Index: file.c
    ===================================================================
    — file.c (revision 13961)
    +++ file.c (working copy)
    @@ -43,8 +43,6 @@

#include <time.h>

-VALUE rb_time_new(time_t, time_t);

#ifdef HAVE_UTIME_H
#include <utime.h>
#elif defined HAVE_SYS_UTIME_H
@@ -186,6 +184,8 @@
return st;
}

+static struct timespec stat_mtimespec(struct stat st);
+
/

  • call-seq:
  • stat <=> other_stat    => -1, 0, 1
    

@@ -203,14 +203,15 @@
rb_stat_cmp(VALUE self, VALUE other)
{
if (rb_obj_is_kind_of(other, rb_obj_class(self))) {

  • time_t t1 = get_stat(self)->st_mtime;
  • time_t t2 = get_stat(other)->st_mtime;
  • if (t1 == t2)
  •  return INT2FIX(0);
    
  • else if (t1 < t2)
  •  return INT2FIX(-1);
    
  • else
  •  return INT2FIX(1);
    
  •    struct timespec ts1 = stat_mtimespec(get_stat(self));
    
  •    struct timespec ts2 = stat_mtimespec(get_stat(other));
    
  •    if (ts1.tv_sec == ts2.tv_sec) {
    
  •        if (ts1.tv_nsec == ts2.tv_nsec) return INT2FIX(0);
    
  •        if (ts1.tv_nsec < ts2.tv_nsec) return INT2FIX(-1);
    
  •        return INT2FIX(1);
    
  •    }
    
  •    if (ts1.tv_sec < ts2.tv_sec) return INT2FIX(-1);
    
  •    return INT2FIX(1);
    
    }
    return Qnil;
    }
    @@ -494,7 +495,78 @@
    #endif
    }

+static struct timespec
+stat_atimespec(struct stat *st)
+{

  • struct timespec ts;
  • ts.tv_sec = st->st_atime;
    +#if defined(HAVE_STRUCT_STAT_ST_ATIM)
  • ts.tv_nsec = st->st_atim.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC)
  • ts.tv_nsec = st->st_atimespec.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
  • ts.tv_nsec = st->st_atimensec;
    +#else
  • ts.tv_nsec = 0;
    +#endif
  • return ts;
    +}

+static VALUE
+stat_atime(struct stat *st)
+{

  • struct timespec ts = stat_atimespec(st);
  • return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
    +}

+static struct timespec
+stat_mtimespec(struct stat *st)
+{

  • struct timespec ts;
  • ts.tv_sec = st->st_mtime;
    +#if defined(HAVE_STRUCT_STAT_ST_MTIM)
  • ts.tv_nsec = st->st_mtim.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
  • ts.tv_nsec = st->st_mtimespec.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
  • ts.tv_nsec = st->st_mtimensec;
    +#else
  • ts.tv_nsec = 0;
    +#endif
  • return ts;
    +}

+static VALUE
+stat_mtime(struct stat *st)
+{

  • struct timespec ts = stat_mtimespec(st);
  • return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
    +}

+static struct timespec
+stat_ctimespec(struct stat *st)
+{

  • struct timespec ts;
  • ts.tv_sec = st->st_ctime;
    +#if defined(HAVE_STRUCT_STAT_ST_CTIM)
  • ts.tv_nsec = st->st_ctim.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_CTIMESPEC)
  • ts.tv_nsec = st->st_ctimespec.tv_nsec;
    +#elif defined(HAVE_STRUCT_STAT_ST_CTIMENSEC)
  • ts.tv_nsec = st->st_ctimensec;
    +#else
  • ts.tv_nsec = 0;
    +#endif
  • return ts;
    +}

+static VALUE
+stat_ctime(struct stat *st)
+{

  • struct timespec ts = stat_ctimespec(st);
  • return rb_time_nano_new(ts.tv_sec, ts.tv_nsec);
    +}

/*

  • call-seq:
  • stat.atime   => time
    

@@ -509,7 +581,7 @@
static VALUE
rb_stat_atime(VALUE self)
{

  • return rb_time_new(get_stat(self)->st_atime, 0);
  • return stat_atime(get_stat(self));
    }

/*
@@ -525,7 +597,7 @@
static VALUE
rb_stat_mtime(VALUE self)
{

  • return rb_time_new(get_stat(self)->st_mtime, 0);
  • return stat_mtime(get_stat(self));
    }

/*
@@ -543,7 +615,7 @@
static VALUE
rb_stat_ctime(VALUE self)
{

  • return rb_time_new(get_stat(self)->st_ctime, 0);
  • return stat_ctime(get_stat(self));
    }

/*
@@ -1594,7 +1666,7 @@

 if (rb_stat(fname, &st) < 0)

rb_sys_fail(StringValueCStr(fname));

  • return rb_time_new(st.st_atime, 0);
  • return stat_atime(&st);
    }

/*
@@ -1618,7 +1690,7 @@
if (fstat(fptr->fd, &st) == -1) {
rb_sys_fail(fptr->path);
}

  • return rb_time_new(st.st_atime, 0);
  • return stat_atime(&st);
    }

/*
@@ -1638,7 +1710,7 @@

 if (rb_stat(fname, &st) < 0)

rb_sys_fail(RSTRING_PTR(fname));

  • return rb_time_new(st.st_mtime, 0);
  • return stat_mtime(&st);
    }

/*
@@ -1661,7 +1733,7 @@
if (fstat(fptr->fd, &st) == -1) {
rb_sys_fail(fptr->path);
}

  • return rb_time_new(st.st_mtime, 0);
  • return stat_mtime(&st);
    }

/*
@@ -1683,7 +1755,7 @@

 if (rb_stat(fname, &st) < 0)

rb_sys_fail(RSTRING_PTR(fname));

  • return rb_time_new(st.st_ctime, 0);
  • return stat_ctime(&st);
    }

/*
@@ -1707,7 +1779,7 @@
if (fstat(fptr->fd, &st) == -1) {
rb_sys_fail(fptr->path);
}

  • return rb_time_new(st.st_ctime, 0);
  • return stat_ctime(&st);
    }

static void
@@ -1967,44 +2039,34 @@
}
#endif

-struct timeval rb_time_timeval(VALUE time);
+struct timespec rb_time_timespec(VALUE time);

-#if defined(HAVE_UTIMES) && !defined(CHECKER)
+#if defined(HAVE_UTIMENSAT)

static void
utime_internal(const char *path, void *arg)
{

  • struct timeval *tvp = arg;
  • if (utimes(path, tvp) < 0)
  • struct timespec *tsp = arg;
  • if (utimensat(AT_FDCWD, path, tsp, 0) < 0)
    rb_sys_fail(path);
    }

-/*

    • call-seq:
    • File.utime(atime, mtime, file_name,…) => integer
    • Sets the access and modification times of each
    • named file to the first two arguments. Returns
    • the number of file names in the argument list.
  • */
    +#elif defined(HAVE_UTIMES)

-static VALUE
-rb_file_s_utime(int argc, VALUE *argv)
+static void
+utime_internal(const char *path, void *arg)
{

  • VALUE atime, mtime, rest;
  • struct timeval tvs[2], *tvp = NULL;
  • long n;
  • rb_scan_args(argc, argv, “2*”, &atime, &mtime, &rest);
  • if (!NIL_P(atime) || !NIL_P(mtime)) {
  • tvp = tvs;
  • tvp[0] = rb_time_timeval(atime);
  • tvp[1] = rb_time_timeval(mtime);
  • struct timespec *tsp = arg;
  • struct timeval tvbuf[2], *tvp = arg;
  • if (tsp) {
  •    tvbuf[0].tv_sec = tsp[0].tv_sec;
    
  •    tvbuf[0].tv_usec = tsp[0].tv_nsec / 1000;
    
  •    tvbuf[1].tv_sec = tsp[1].tv_sec;
    
  •    tvbuf[1].tv_usec = tsp[1].tv_nsec / 1000;
    
  •    tvp = tvbuf;
    
    }
  • n = apply2files(utime_internal, rest, tvp);
  • return LONG2FIX(n);
  • if (utimes(path, tvp) < 0)
  • rb_sys_fail(path);
    }

#else
@@ -2019,34 +2081,47 @@
static void
utime_internal(const char *path, void *arg)
{

  • struct utimbuf *utp = arg;
  • struct timespec *tsp = arg;
  • struct utimbuf utbuf, *utp = NULL;
  • if (tsp) {
  •    utbuf.actime = tsp[0].tv_sec;
    
  •    utbuf.modtime = tsp[1].tv_sec;
    
  •    utp = &utbuf;
    
  • }
    if (utime(path, utp) < 0)
    rb_sys_fail(path);
    }

+#endif
+
+/*

    • call-seq:
    • File.utime(atime, mtime, file_name,…) => integer
    • Sets the access and modification times of each
    • named file to the first two arguments. Returns
    • the number of file names in the argument list.
  • */

static VALUE
rb_file_s_utime(int argc, VALUE *argv)
{
VALUE atime, mtime, rest;

  • struct timespec tss[2], *tsp = NULL;
    long n;
  • struct timeval tv;

  • struct utimbuf utbuf, *utp = NULL;

    rb_scan_args(argc, argv, “2*”, &atime, &mtime, &rest);

    if (!NIL_P(atime) || !NIL_P(mtime)) {

  • utp = &utbuf;

  • tv = rb_time_timeval(atime);

  • utp->actime = tv.tv_sec;

  • tv = rb_time_timeval(mtime);

  • utp->modtime = tv.tv_sec;

  • tsp = tss;
  • tsp[0] = rb_time_timespec(atime);
  • tsp[1] = rb_time_timespec(mtime);
    }
  • n = apply2files(utime_internal, rest, utp);
  • n = apply2files(utime_internal, rest, tsp);
    return LONG2FIX(n);
    }

-#endif

NORETURN(static void sys_fail2(VALUE,VALUE));
static void
@@ -3346,11 +3421,11 @@

switch (cmd) {
case ‘A’:

  •  return rb_time_new(st.st_atime, 0);
    
  •  return stat_atime(&st);
    
    case ‘M’:
  •  return rb_time_new(st.st_mtime, 0);
    
  •  return stat_mtime(&st);
    
    case ‘C’:
  •  return rb_time_new(st.st_ctime, 0);
    
  •  return stat_ctime(&st);
    
    }
    }