[#32185] Date#+に大きな数字を与えるとおかしな日付に — "madoka yamamoto" <yamamotomadoka@...>

こんにちは、山本と申します。

26 messages 2007/11/08
[#32186] Re: Date#+に大きな数字を与えるとおかしな日付に — Tadayoshi Funaba <tadf@...> 2007/11/08

> Dateオブジェクトに+で大きな数字を与えるとおかしくなるようです。

[#32188] Re: Date#+に大きな数字を与えるとおかしな日付に — "madoka yamamoto" <yamamotomadoka@...> 2007/11/09

山本です。

[#32191] Re: Date#+に大きな数字を与えるとおかしな日付に — tadf@... 2007/11/09

> アルゴリズムの意味がわからないで書いた、表層的なパッチなので

[#32194] Re: Date#+に大きな数字を与えるとおかしな日付に — Yukihiro Matsumoto <matz@...> 2007/11/09

Hi,

[#32200] Re: rational (Re: Date#+に大きな数字を与えるとおかしな日付に) — Tadayoshi Funaba <tadf@...> 2007/11/10

> 1.9ではRationalとComplexを組み込みに、という話はありましたが、

[#32225] Re: rational (Re: Date#+に大きな数字を与えるとおかしな日付に) — Shin-ichiro HARA <sinara@...> 2007/11/12

原です。

[#32198] [提案] Array#tail — "Yusuke ENDOH" <mame@...>

遠藤と申します。

21 messages 2007/11/09
[#32199] Re: [提案] Array#tail — Yukihiro Matsumoto <matz@...> 2007/11/10

まつもと ゆきひろです

[#32352] 1.9.1のリリース時期について — KIMURA Koichi <hogemuta@...>

木村です。

16 messages 2007/11/24
[#32353] Re: 1.9.1のリリース時期について — Yukihiro Matsumoto <matz@...> 2007/11/24

まつもと ゆきひろです

[#32403] Next 1.8.6 patch release? (was Re: 1.9.1のリリース時期について) — Takahiro Kambe <taca@...>

こんばんは。

32 messages 2007/11/29
[#32414] Re: Next 1.8.6 patch release? (was Re: 1.9.1のリリース時期について) — Urabe Shyouhei <shyouhei@...> 2007/11/30

卜部です。

[#32444] Re: Next 1.8.6 patch release? (was Re: 1.9.1のリリース時期について) — Yukihiro Matsumoto <matz@...> 2007/12/03

まつもと ゆきひろです

[#32488] Re: Next 1.8.6 patch release? (was Re: 1.9.1のリリース時期について) — Urabe Shyouhei <shyouhei@...> 2007/12/08

卜部です。

[#32525] Re: Next 1.8.6 patch release? (was Re: 1.9.1のリリース時期について) — "Yusuke ENDOH" <mame@...> 2007/12/10

遠藤と申します。

[#32643] Re: Next 1.8.6 patch release? (was Re: 1.9.1のリリース時期について) — "Yusuke ENDOH" <mame@...> 2007/12/19

遠藤です。

[#32409] Re: [ruby-cvs:21293] Ruby:r14056 (trunk): * signal.c (trap_signm): SIGVTALRM no longer used for green — SASADA Koichi <ko1@...>

 ささだです.

10 messages 2007/11/30

[ruby-dev:32306] nanosecond Time and stat

From: Tanaka Akira <akr@...>
Date: 2007-11-17 08:55:55 UTC
List: ruby-dev #32306
最近、nanosecond 単位な timestamp があるようです。

たとえば boron だと timestamp が nanosecond 単位です。

% 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

上記の filesystem は XFS ですが、GNU/Linux だと他に JFS がサ
ポートしています。また、ext4 もサポートするようです。

NetBSD, FreeBSD, OpenBSD も、struct stat にはもう用意があり
ます。(実際にそれを使う filesystem があるかは知りません)

で、これを Ruby で扱ってみようかと思って、
* Time の精度を microsecond から nanosecond にあげて
* File.mtime 等で nanosecond 単位の timestamp を取り出す
としてみました。

それにともなって、
* Time#tv_nsec, Time#nsec の追加
* Time.at の最後の引数で Float や Rational を受け付ける
* Time.local, Time.utc の最後の引数が sec のときも同様に受け付ける
としてあります。

Rational を扱える (というか、クラスを気にせずに単に divmod
とかを呼び出して整数部分や小数部分を取り出す) ようにしたのは、
IEEE 754 double だと、現時点の時刻を nanosecond 単位までの精
度では表現できないからです。Time.at に第3引数をつけるとか
Time.utc の引数をさらに複雑にするというのはなんですし。

あと、Marshal は悩んだので手をつけてなくて、今までどおり
microsecond までしか書き出されません。読み出すときは
microsecond 未満は 0 になります。

どうですかね。

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 <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 +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 <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 +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 <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 +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 <i>time</i>.
+ *     
+ *     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 <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 +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 @@
 /*
  *  <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
@@ -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);
 	}
     }
 
-- 
[田中 哲][たなか あきら][Tanaka Akira]

In This Thread

Prev Next