mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@13968 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
a2946d85ec
commit
a070c4fbe3
11 changed files with 569 additions and 174 deletions
47
ChangeLog
47
ChangeLog
|
@ -1,3 +1,50 @@
|
|||
Mon Nov 19 17:51:27 2007 Tanaka Akira <akr@fsij.org>
|
||||
|
||||
* 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@ruby-lang.org>
|
||||
|
||||
* object.c (rb_class_superclass): should not raise exception for
|
||||
|
|
20
configure.in
20
configure.in
|
@ -513,6 +513,7 @@ AC_CHECK_LIB(crypt, crypt)
|
|||
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,6 +554,17 @@ AC_TYPE_SIZE_T
|
|||
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)])
|
||||
|
||||
|
@ -587,14 +599,16 @@ AC_REPLACE_FUNCS(dup2 memmove strcasecmp strncasecmp strerror strftime\
|
|||
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])
|
||||
|
|
193
file.c
193
file.c
|
@ -43,8 +43,6 @@ int flock(int, int);
|
|||
|
||||
#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 @@ get_stat(VALUE self)
|
|||
return st;
|
||||
}
|
||||
|
||||
static struct timespec stat_mtimespec(struct stat *st);
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* stat <=> other_stat => -1, 0, 1
|
||||
|
@ -203,14 +203,15 @@ static VALUE
|
|||
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,6 +495,77 @@ rb_stat_blocks(VALUE self)
|
|||
#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:
|
||||
|
@ -509,7 +581,7 @@ rb_stat_blocks(VALUE self)
|
|||
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 @@ rb_stat_atime(VALUE self)
|
|||
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 @@ rb_stat_mtime(VALUE self)
|
|||
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 @@ rb_file_s_atime(VALUE klass, VALUE fname)
|
|||
|
||||
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 @@ rb_file_atime(VALUE obj)
|
|||
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 @@ rb_file_s_mtime(VALUE klass, VALUE fname)
|
|||
|
||||
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 @@ rb_file_mtime(VALUE obj)
|
|||
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 @@ rb_file_s_ctime(VALUE klass, VALUE fname)
|
|||
|
||||
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 @@ rb_file_ctime(VALUE obj)
|
|||
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 @@ rb_file_s_lchown(int argc, VALUE *argv)
|
|||
}
|
||||
#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 @@ struct utimbuf {
|
|||
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 @@ rb_f_test(int argc, VALUE *argv)
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -560,7 +560,8 @@ VALUE rb_barrier_new(void);
|
|||
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);
|
||||
|
|
|
@ -25,13 +25,20 @@ extern "C" {
|
|||
# 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);
|
||||
|
|
|
@ -461,10 +461,10 @@ class Time
|
|||
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'
|
||||
|
|
|
@ -601,17 +601,22 @@ w_object(VALUE obj, struct dump_arg *arg, int limit)
|
|||
}
|
||||
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;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'test/unit'
|
||||
require 'rational'
|
||||
|
||||
class TestTime < Test::Unit::TestCase
|
||||
def test_time_add()
|
||||
|
@ -81,4 +82,67 @@ class TestTime < Test::Unit::TestCase
|
|||
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
|
||||
|
|
|
@ -54,7 +54,7 @@ class YAML_Unit_Tests < Test::Unit::TestCase
|
|||
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
|
||||
|
|
378
time.c
378
time.c
|
@ -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 @@ time_s_alloc(VALUE klass)
|
|||
|
||||
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_init(VALUE time)
|
|||
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 @@ time_init(VALUE time)
|
|||
#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;
|
||||
}
|
||||
|
||||
struct timeval
|
||||
rb_time_interval(VALUE time)
|
||||
static struct timeval
|
||||
time_timeval(VALUE num, int interval)
|
||||
{
|
||||
return time_timeval(time, Qtrue);
|
||||
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 num)
|
||||
{
|
||||
return time_timeval(num, Qtrue);
|
||||
}
|
||||
|
||||
struct timeval
|
||||
|
@ -216,12 +260,27 @@ rb_time_timeval(VALUE time)
|
|||
|
||||
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 @@ rb_time_timeval(VALUE time)
|
|||
*
|
||||
* 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 @@ rb_time_timeval(VALUE time)
|
|||
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 @@ obj2long(VALUE obj)
|
|||
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 @@ time_arg(int argc, VALUE *argv, struct tm *tm, time_t *usec)
|
|||
}
|
||||
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 @@ time_utc_or_local(int argc, VALUE *argv, int utc_p, VALUE klass)
|
|||
{
|
||||
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_s_mktime(int argc, VALUE *argv, VALUE klass)
|
|||
* 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 @@ time_to_i(VALUE time)
|
|||
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_i(VALUE time)
|
|||
* 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 @@ time_to_f(VALUE time)
|
|||
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,7 +979,33 @@ time_usec(VALUE time)
|
|||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -899,7 +1015,7 @@ time_usec(VALUE time)
|
|||
*
|
||||
* 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 @@ time_cmp(VALUE time1, VALUE time2)
|
|||
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 @@ time_eql(VALUE time1, VALUE time2)
|
|||
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 @@ time_hash(VALUE time)
|
|||
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 @@ time_localtime(VALUE time)
|
|||
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 @@ time_gmtime(VALUE time)
|
|||
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 @@ time_to_s(VALUE time)
|
|||
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 @@ time_to_s(VALUE time)
|
|||
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 @@ time_add(struct time_object *tobj, VALUE offset, int sign)
|
|||
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 @@ time_add(struct time_object *tobj, VALUE offset, int sign)
|
|||
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 @@ time_minus(VALUE time1, VALUE time2)
|
|||
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 @@ time_succ(VALUE time)
|
|||
|
||||
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_utc_offset(VALUE time)
|
|||
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 @@ time_mdump(VALUE time)
|
|||
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 @@ time_mdump(VALUE time)
|
|||
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 @@ time_mdump(VALUE time)
|
|||
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 @@ time_dump(int argc, VALUE *argv, VALUE time)
|
|||
|
||||
rb_scan_args(argc, argv, "01", 0);
|
||||
str = time_mdump(time);
|
||||
rb_copy_generic_ivar(str, time);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
@ -1993,12 +2132,22 @@ time_mload(VALUE time, VALUE str)
|
|||
{
|
||||
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 @@ time_mload(VALUE time, VALUE str)
|
|||
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 @@ time_mload(VALUE time, VALUE str)
|
|||
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 @@ time_load(VALUE klass, VALUE str)
|
|||
{
|
||||
VALUE time = time_s_alloc(klass);
|
||||
|
||||
rb_copy_generic_ivar(time, str);
|
||||
time_mload(time, str);
|
||||
return time;
|
||||
}
|
||||
|
@ -2062,7 +2228,7 @@ time_load(VALUE klass, VALUE str)
|
|||
/*
|
||||
* <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 @@ time_load(VALUE klass, VALUE str)
|
|||
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 @@ Init_Time(void)
|
|||
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);
|
||||
|
||||
|
|
12
variable.c
12
variable.c
|
@ -907,10 +907,20 @@ rb_copy_generic_ivar(VALUE clone, VALUE obj)
|
|||
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));
|
||||
|
|
Loading…
Reference in a new issue