[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]