[前][次][番号順一覧][スレッド一覧]

ruby-changes:2477

From: ko1@a...
Date: 19 Nov 2007 18:10:04 +0900
Subject: [ruby-changes:2477] akr - Ruby:r13968 (trunk): * configure.in: check struct timespec, clock_gettime, utimensat,

akr	2007-11-19 18:09:38 +0900 (Mon, 19 Nov 2007)

  New Revision: 13968

  Modified files:
    trunk/ChangeLog
    trunk/configure.in
    trunk/file.c
    trunk/include/ruby/intern.h
    trunk/include/ruby/missing.h
    trunk/lib/time.rb
    trunk/marshal.c
    trunk/test/ruby/test_time.rb
    trunk/test/yaml/test_yaml.rb
    trunk/time.c
    trunk/variable.c

  Log:
    * configure.in: check struct timespec, clock_gettime, utimensat,
      struct stat.st_atim,
      struct stat.st_atimespec,
      struct stat.st_atimensec,
      struct stat.st_mtim,
      struct stat.st_mtimespec,
      struct stat.st_mtimensec,
      struct stat.st_ctim,
      struct stat.st_ctimespec,
      struct stat.st_ctimensec.
    
    * include/ruby/missing.h: provide struct timespec if not available.
    
    * time.c: support nanosecond-resolution using struct timespec.
    
    * include/ruby/intern.h: provide rb_time_nano_new.
    
    * file.c (utime_internal): use utimensat if available.
      (rb_file_s_utime): refactored.
      (rb_f_test): use stat_atime, stat_mtime, stat_ctime.
      (rb_stat_cmp): check tv_nsec.
      (stat_atimespec): new function.
      (stat_atime): ditto.
      (stat_mtimespec): ditto.
      (stat_mtime): ditto.
      (stat_ctimespec): ditto.
      (stat_ctime): ditto.
      (rb_stat_atime): use stat_atime.
      (rb_file_s_atime): ditto.
      (rb_file_atime): ditto.
      (rb_stat_mtime): use stat_mtime.
      (rb_file_s_mtime): ditto.
      (rb_file_mtime): ditto.
      (rb_file_ctime): use stat_ctime.
      (rb_file_s_ctime): ditto.
      (rb_stat_ctime): ditto.
    
    * variable.c (rb_copy_generic_ivar): clear clone's instance variables
      if obj has no instance variable.
    
    * marshal.c (w_object): dump instance variables of generated string
      for TYPE_USERDEF, even if original object has instance variables.
    
    * lib/time.rb (Time#xmlschema): use nsec instead of usec.


  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/time.c?r1=13968&r2=13967
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/variable.c?r1=13968&r2=13967
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/time.rb?r1=13968&r2=13967
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/file.c?r1=13968&r2=13967
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/include/ruby/missing.h?r1=13968&r2=13967
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/ChangeLog?r1=13968&r2=13967
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/test/yaml/test_yaml.rb?r1=13968&r2=13967
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/test/ruby/test_time.rb?r1=13968&r2=13967
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/marshal.c?r1=13968&r2=13967
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/configure.in?r1=13968&r2=13967
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/include/ruby/intern.h?r1=13968&r2=13967

Index: time.c
===================================================================
--- time.c	(revision 13967)
+++ time.c	(revision 13968)
@@ -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 <i>aTime</i>, or
  *  the given number of <i>seconds</i> (and optional
- *  <i>microseconds</i>) from epoch. A non-portable feature allows the
+ *  <i>microseconds</i>) 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 <i>time</i> 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 <i>time</i> 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 <i>time</i>.
+ *     
+ *     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 <i>time</i> with <i>other_time</i> or with
  *  <i>numeric</i>, 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 @@
 /*
  *  <code>Time</code> is an abstraction of dates and times. Time is
  *  stored internally as the number of seconds and microseconds since
- *  the <em>epoch</em>, January 1, 1970 00:00 UTC. On some operating
+ *  the <em>Epoch</em>, January 1, 1970 00:00 UTC. On some operating
  *  systems, this offset is allowed to be negative. Also see the
  *  library modules <code>Date</code> and <code>ParseDate</code>. The
  *  <code>Time</code> 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 13967)
+++ include/ruby/missing.h	(revision 13968)
@@ -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 13967)
+++ include/ruby/intern.h	(revision 13968)
@@ -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 13967)
+++ configure.in	(revision 13968)
@@ -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: ChangeLog
===================================================================
--- ChangeLog	(revision 13967)
+++ ChangeLog	(revision 13968)
@@ -1,3 +1,50 @@
+Mon Nov 19 17:51:27 2007  Tanaka Akira  <akr@f...>
+
+	* configure.in: check struct timespec, clock_gettime, utimensat,
+	  struct stat.st_atim,
+	  struct stat.st_atimespec,
+	  struct stat.st_atimensec,
+	  struct stat.st_mtim,
+	  struct stat.st_mtimespec,
+	  struct stat.st_mtimensec,
+	  struct stat.st_ctim,
+	  struct stat.st_ctimespec,
+	  struct stat.st_ctimensec.
+
+	* include/ruby/missing.h: provide struct timespec if not available.
+
+	* time.c: support nanosecond-resolution using struct timespec.
+
+	* include/ruby/intern.h: provide rb_time_nano_new.
+
+	* file.c (utime_internal): use utimensat if available.
+	  (rb_file_s_utime): refactored.
+	  (rb_f_test): use stat_atime, stat_mtime, stat_ctime.
+	  (rb_stat_cmp): check tv_nsec.
+	  (stat_atimespec): new function.
+	  (stat_atime): ditto.
+	  (stat_mtimespec): ditto.
+	  (stat_mtime): ditto.
+	  (stat_ctimespec): ditto.
+	  (stat_ctime): ditto.
+	  (rb_stat_atime): use stat_atime.
+	  (rb_file_s_atime): ditto.
+	  (rb_file_atime): ditto.
+	  (rb_stat_mtime): use stat_mtime.
+	  (rb_file_s_mtime): ditto.
+	  (rb_file_mtime): ditto.
+	  (rb_file_ctime): use stat_ctime.
+	  (rb_file_s_ctime): ditto.
+	  (rb_stat_ctime): ditto.
+
+	* variable.c (rb_copy_generic_ivar): clear clone's instance variables
+	  if obj has no instance variable.
+
+	* marshal.c (w_object): dump instance variables of generated string
+	  for TYPE_USERDEF, even if original object has instance variables.
+
+	* lib/time.rb (Time#xmlschema): use nsec instead of usec.
+
 Mon Nov 19 17:48:30 2007  Yukihiro Matsumoto  <matz@r...>
 
 	* object.c (rb_class_superclass): should not raise exception for
Index: variable.c
===================================================================
--- variable.c	(revision 13967)
+++ variable.c	(revision 13968)
@@ -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 13967)
+++ lib/time.rb	(revision 13968)
@@ -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 13967)
+++ marshal.c	(revision 13968)
@@ -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 13967)
+++ test/ruby/test_time.rb	(revision 13968)
@@ -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 13967)
+++ test/yaml/test_yaml.rb	(revision 13968)
@@ -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 13967)
+++ file.c	(revision 13968)
@@ -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);
 	}
     }
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml

[前][次][番号順一覧][スレッド一覧]