diff options
Diffstat (limited to 'time.c')
-rw-r--r-- | time.c | 490 |
1 files changed, 309 insertions, 181 deletions
@@ -6,26 +6,22 @@ $Date$ created at: Tue Dec 28 14:31:59 JST 1993 - Copyright (C) 1993-1998 Yukihiro Matsumoto + Copyright (C) 1993-1999 Yukihiro Matsumoto ************************************************/ #include "ruby.h" #include <sys/types.h> -#ifdef USE_CWGUSI -int gettimeofday(struct timeval*, struct timezone*); -int strcasecmp(char*, char*); -#endif - #include <time.h> #ifndef NT #ifdef HAVE_SYS_TIME_H # include <sys/time.h> #else +#define time_t long struct timeval { - long tv_sec; /* seconds */ - long tv_usec; /* and microseconds */ + time_t tv_sec; /* seconds */ + time_t tv_usec; /* and microseconds */ }; #endif #endif /* NT */ @@ -33,7 +29,16 @@ struct timeval { #ifdef HAVE_SYS_TIMES_H #include <sys/times.h> #endif -#include <math.h> + +#ifdef USE_CWGUSI +#define time_t long +int gettimeofday(struct timeval*, struct timezone*); +int strcasecmp(char*, char*); +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif VALUE rb_cTime; #if defined(HAVE_TIMES) || defined(NT) @@ -43,9 +48,7 @@ static VALUE S_Tms; struct time_object { struct timeval tv; struct tm tm; -#ifndef HAVE_TM_ZONE int gmt; -#endif int tm_got; }; @@ -63,10 +66,9 @@ time_s_now(klass) obj = Data_Make_Struct(klass, struct time_object, 0, free, tobj); tobj->tm_got=0; - if (gettimeofday(&(tobj->tv), 0) == -1) { + if (gettimeofday(&tobj->tv, 0) < 0) { rb_sys_fail("gettimeofday"); } - rb_obj_call_init(obj); return obj; } @@ -74,13 +76,15 @@ time_s_now(klass) static VALUE time_new_internal(klass, sec, usec) VALUE klass; - int sec, usec; + time_t sec, usec; { VALUE obj; struct time_object *tobj; +#ifndef USE_CWGUSI if (sec < 0 || (sec == 0 && usec < 0)) rb_raise(rb_eArgError, "time must be positive"); +#endif obj = Data_Make_Struct(klass, struct time_object, 0, free, tobj); tobj->tm_got = 0; tobj->tv.tv_sec = sec; @@ -91,21 +95,20 @@ time_new_internal(klass, sec, usec) VALUE rb_time_new(sec, usec) - int sec, usec; + time_t sec, usec; { return time_new_internal(rb_cTime, sec, usec); } struct timeval -rb_time_timeval(time) +rb_time_interval(time) VALUE time; { - struct time_object *tobj; struct timeval t; switch (TYPE(time)) { case T_FIXNUM: - t.tv_sec = FIX2INT(time); + t.tv_sec = FIX2LONG(time); if (t.tv_sec < 0) rb_raise(rb_eArgError, "time must be positive"); t.tv_usec = 0; @@ -114,37 +117,57 @@ rb_time_timeval(time) case T_FLOAT: if (RFLOAT(time)->value < 0.0) rb_raise(rb_eArgError, "time must be positive"); - t.tv_sec = (long)floor(RFLOAT(time)->value); - t.tv_usec = (long)((RFLOAT(time)->value - t.tv_sec) * 1000000.0); + t.tv_sec = (time_t)RFLOAT(time)->value; + t.tv_usec = (time_t)((RFLOAT(time)->value - (double)t.tv_sec)*1e6); break; case T_BIGNUM: - t.tv_sec = NUM2INT(time); + t.tv_sec = NUM2LONG(time); if (t.tv_sec < 0) rb_raise(rb_eArgError, "time must be positive"); t.tv_usec = 0; break; default: - if (!rb_obj_is_kind_of(time, rb_cTime)) { - rb_raise(rb_eTypeError, "Can't convert %s into Time", - rb_class2name(CLASS_OF(time))); - } - GetTimeval(time, tobj); - t = tobj->tv; + rb_raise(rb_eTypeError, "can't convert %s into Time interval", + rb_class2name(CLASS_OF(time))); break; } return t; } +struct timeval +rb_time_timeval(time) + VALUE time; +{ + struct time_object *tobj; + struct timeval t; + + if (rb_obj_is_kind_of(time, rb_cTime)) { + GetTimeval(time, tobj); + t = tobj->tv; + return t; + } + return rb_time_interval(time); +} + static VALUE time_s_at(klass, time) VALUE klass, time; { struct timeval tv; + VALUE t; tv = rb_time_timeval(time); - return time_new_internal(klass, tv.tv_sec, tv.tv_usec); + t = time_new_internal(klass, tv.tv_sec, tv.tv_usec); + if (TYPE(time) == T_DATA) { + struct time_object *tobj, *tobj2; + + GetTimeval(time, tobj); + GetTimeval(t, tobj2); + tobj2->gmt = tobj->gmt; + } + return t; } static char *months [12] = { @@ -152,22 +175,22 @@ static char *months [12] = { "jul", "aug", "sep", "oct", "nov", "dec", }; -static int -obj2int(obj) +static long +obj2long(obj) VALUE obj; { if (TYPE(obj) == T_STRING) { obj = rb_str2inum(RSTRING(obj)->ptr, 10); } - return NUM2INT(obj); + return NUM2LONG(obj); } static void -time_arg(argc, argv, args) +time_arg(argc, argv, tm) int argc; VALUE *argv; - int *args; + struct tm *tm; { VALUE v[6]; int i; @@ -184,102 +207,98 @@ time_arg(argc, argv, args) rb_scan_args(argc, argv, "15", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5]); } - args[0] = obj2int(v[0]); - if (args[0] < 70) args[0] += 100; - if (args[0] > 1900) args[0] -= 1900; + tm->tm_year = obj2long(v[0]); + if (tm->tm_year < 69) tm->tm_year += 100; + if (tm->tm_year >= 1000) tm->tm_year -= 1900; if (NIL_P(v[1])) { - args[1] = 0; + tm->tm_mon = 0; } else if (TYPE(v[1]) == T_STRING) { - args[1] = -1; + tm->tm_mon = -1; for (i=0; i<12; i++) { - if (strcasecmp(months[i], RSTRING(v[1])->ptr) == 0) { - args[1] = i; + if (RSTRING(v[1])->len == 3 && + strcasecmp(months[i], RSTRING(v[1])->ptr) == 0) { + tm->tm_mon = i; break; } } - if (args[1] == -1) { + if (tm->tm_mon == -1) { char c = RSTRING(v[1])->ptr[0]; if ('0' <= c && c <= '9') { - args[1] = obj2int(v[1])-1; + tm->tm_mon = obj2long(v[1])-1; } } } else { - args[1] = obj2int(v[1]) - 1; + tm->tm_mon = obj2long(v[1]) - 1; } if (NIL_P(v[2])) { - args[2] = 1; + tm->tm_mday = 1; } else { - args[2] = obj2int(v[2]); - } - for (i=3;i<6;i++) { - if (NIL_P(v[i])) { - args[i] = 0; - } - else { - args[i] = obj2int(v[i]); - } + tm->tm_mday = obj2long(v[2]); } + 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]); /* value validation */ - if ( args[0] < 70|| args[0] > 137 - || args[1] < 0 || args[1] > 11 - || args[2] < 1 || args[2] > 31 - || args[3] < 0 || args[3] > 23 - || args[4] < 0 || args[4] > 59 - || args[5] < 0 || args[5] > 60) + if ( tm->tm_year < 69 + || tm->tm_mon < 0 || tm->tm_mon > 11 + || tm->tm_mday < 1 || tm->tm_mday > 31 + || tm->tm_hour < 0 || tm->tm_hour > 23 + || tm->tm_min < 0 || tm->tm_min > 59 + || tm->tm_sec < 0 || tm->tm_sec > 60) rb_raise(rb_eArgError, "argument out of range"); } static VALUE time_gmtime _((VALUE)); static VALUE time_localtime _((VALUE)); -static VALUE -time_gm_or_local(argc, argv, gm_or_local, klass) - int argc; - VALUE *argv; - int gm_or_local; - VALUE klass; +static VALUE time_get_tm _((VALUE, int)); + +static time_t +make_time_t(tptr, fn) + struct tm *tptr; + struct tm *(*fn)(); { - int args[6]; struct timeval tv; - struct tm *tm; time_t guess, t; - int diff; - struct tm *(*fn)(); - VALUE time; - - fn = (gm_or_local) ? gmtime : localtime; - time_arg(argc, argv, args); + struct tm *tm; + long diff; - gettimeofday(&tv, 0); + if (gettimeofday(&tv, 0) < 0) { + rb_sys_fail("gettimeofday"); + } guess = tv.tv_sec; tm = (*fn)(&guess); if (!tm) goto error; - t = args[0]; + t = tptr->tm_year; + if (t < 69) goto out_of_range; while (diff = t - (tm->tm_year)) { guess += diff * 364 * 24 * 3600; - if (guess < 0) rb_raise(rb_eArgError, "too far future"); + if (diff > 0 && guess < 0) goto out_of_range; tm = (*fn)(&guess); if (!tm) goto error; } - t = args[1]; + t = tptr->tm_mon; while (diff = t - tm->tm_mon) { guess += diff * 27 * 24 * 3600; tm = (*fn)(&guess); if (!tm) goto error; + if (tptr->tm_year != tm->tm_year) goto out_of_range; } - guess += (args[2] - tm->tm_mday) * 3600 * 24; - guess += (args[3] - tm->tm_hour) * 3600; - guess += (args[4] - tm->tm_min) * 60; - guess += args[5] - tm->tm_sec; + guess += (tptr->tm_mday - tm->tm_mday) * 3600 * 24; + guess += (tptr->tm_hour - tm->tm_hour) * 3600; + guess += (tptr->tm_min - tm->tm_min) * 60; + guess += tptr->tm_sec - tm->tm_sec; + if (guess < 0) goto out_of_range; - time = time_new_internal(klass, guess, 0); - if (gm_or_local) return time_gmtime(time); - return time_localtime(time); + return guess; + + out_of_range: + rb_raise(rb_eArgError, "time out of range"); error: rb_raise(rb_eArgError, "gmtime/localtime error"); @@ -287,6 +306,25 @@ time_gm_or_local(argc, argv, gm_or_local, klass) } static VALUE +time_gm_or_local(argc, argv, gm_or_local, klass) + int argc; + VALUE *argv; + int gm_or_local; + VALUE klass; +{ + struct tm tm; + struct tm *(*fn)(); + VALUE time; + + fn = (gm_or_local) ? gmtime : localtime; + time_arg(argc, argv, &tm); + + time = time_new_internal(klass, make_time_t(&tm, fn), 0); + if (gm_or_local) return time_gmtime(time); + return time_localtime(time); +} + +static VALUE time_s_timegm(argc, argv, klass) int argc; VALUE *argv; @@ -311,7 +349,7 @@ time_to_i(time) struct time_object *tobj; GetTimeval(time, tobj); - return rb_int2inum(tobj->tv.tv_sec); + return INT2NUM(tobj->tv.tv_sec); } static VALUE @@ -331,7 +369,7 @@ time_usec(time) struct time_object *tobj; GetTimeval(time, tobj); - return INT2FIX(tobj->tv.tv_usec); + return INT2NUM(tobj->tv.tv_usec); } static VALUE @@ -339,12 +377,12 @@ time_cmp(time1, time2) VALUE time1, time2; { struct time_object *tobj1, *tobj2; - int i; + long i; GetTimeval(time1, tobj1); switch (TYPE(time2)) { case T_FIXNUM: - i = FIX2INT(time2); + i = FIX2LONG(time2); if (tobj1->tv.tv_sec == i) return INT2FIX(0); if (tobj1->tv.tv_sec > i) return INT2FIX(1); return FIX2INT(-1); @@ -353,10 +391,13 @@ time_cmp(time1, time2) { double t; - if (tobj1->tv.tv_sec == (int)RFLOAT(time2)->value) return INT2FIX(0); + if (tobj1->tv.tv_sec == (time_t)RFLOAT(time2)->value) + return INT2FIX(0); t = (double)tobj1->tv.tv_sec + (double)tobj1->tv.tv_usec*1e-6; - if (tobj1->tv.tv_sec == RFLOAT(time2)->value) return INT2FIX(0); - if (tobj1->tv.tv_sec > RFLOAT(time2)->value) return INT2FIX(1); + if (tobj1->tv.tv_sec == (time_t)RFLOAT(time2)->value) + return INT2FIX(0); + if (tobj1->tv.tv_sec > (time_t)RFLOAT(time2)->value) + return INT2FIX(1); return FIX2INT(-1); } } @@ -371,7 +412,7 @@ time_cmp(time1, time2) if (tobj1->tv.tv_sec > tobj2->tv.tv_sec) return INT2FIX(1); return FIX2INT(-1); } - i = NUM2INT(time2); + i = NUM2LONG(time2); if (tobj1->tv.tv_sec == i) return INT2FIX(0); if (tobj1->tv.tv_sec > i) return INT2FIX(1); return FIX2INT(-1); @@ -394,11 +435,22 @@ time_eql(time1, time2) } static VALUE +time_gmt_p(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->gmt) return Qtrue; + return Qfalse; +} + +static VALUE time_hash(time) VALUE time; { struct time_object *tobj; - int hash; + long hash; GetTimeval(time, tobj); hash = tobj->tv.tv_sec ^ tobj->tv.tv_usec; @@ -406,19 +458,34 @@ time_hash(time) } static VALUE +time_clone(time) + VALUE time; +{ + VALUE obj; + struct time_object *tobj, *newtobj; + + GetTimeval(time, tobj); + obj = Data_Make_Struct(0, struct time_object, 0, free, newtobj); + CLONESETUP(obj, time); + MEMCPY(newtobj, tobj, struct time_object, 1); + + return obj; +} + +static VALUE time_localtime(time) VALUE time; { struct time_object *tobj; struct tm *tm_tmp; + time_t t; GetTimeval(time, tobj); - tm_tmp = localtime((const time_t*)&tobj->tv.tv_sec); + t = tobj->tv.tv_sec; + tm_tmp = localtime(&t); tobj->tm = *tm_tmp; tobj->tm_got = 1; -#ifndef HAVE_TM_ZONE tobj->gmt = 0; -#endif return time; } @@ -428,18 +495,27 @@ time_gmtime(time) { struct time_object *tobj; struct tm *tm_tmp; + time_t t; GetTimeval(time, tobj); - tm_tmp = gmtime((const time_t*)&tobj->tv.tv_sec); + t = tobj->tv.tv_sec; + tm_tmp = gmtime(&t); tobj->tm = *tm_tmp; tobj->tm_got = 1; -#ifndef HAVE_TM_ZONE tobj->gmt = 1; -#endif return time; } static VALUE +time_get_tm(time, gmt) + VALUE time; + int gmt; +{ + if (gmt) return time_gmtime(time); + return time_localtime(time); +} + +static VALUE time_asctime(time) VALUE time; { @@ -448,9 +524,9 @@ time_asctime(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } - s = asctime(&(tobj->tm)); + s = asctime(&tobj->tm); if (s[24] == '\n') s[24] = '\0'; return rb_str_new2(s); @@ -461,21 +537,21 @@ time_to_s(time) VALUE time; { struct time_object *tobj; - char buf[64]; + char buf[128]; int len; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } #ifndef HAVE_TM_ZONE if (tobj->gmt == 1) { - len = strftime(buf, 64, "%a %b %d %H:%M:%S GMT %Y", &(tobj->tm)); + len = strftime(buf, 128, "%a %b %d %H:%M:%S GMT %Y", &tobj->tm); } else #endif { - len = strftime(buf, 64, "%a %b %d %H:%M:%S %Z %Y", &(tobj->tm)); + len = strftime(buf, 128, "%a %b %d %H:%M:%S %Z %Y", &tobj->tm); } return rb_str_new(buf, len); } @@ -484,64 +560,67 @@ static VALUE time_plus(time1, time2) VALUE time1, time2; { - struct time_object *tobj1, *tobj2; - long sec, usec; + struct time_object *tobj; + time_t sec, usec; + double f; - GetTimeval(time1, tobj1); - if (TYPE(time2) == T_FLOAT) { - unsigned int nsec = (unsigned int)RFLOAT(time2)->value; - sec = tobj1->tv.tv_sec + nsec; - usec = tobj1->tv.tv_usec + (long)(RFLOAT(time2)->value-(double)nsec)*1e6; - } - else if (rb_obj_is_instance_of(time2, rb_cTime)) { - GetTimeval(time2, tobj2); - sec = tobj1->tv.tv_sec + tobj2->tv.tv_sec; - usec = tobj1->tv.tv_usec + tobj2->tv.tv_usec; - } - else { - sec = tobj1->tv.tv_sec + NUM2INT(time2); - usec = tobj1->tv.tv_usec; + GetTimeval(time1, tobj); + + if (rb_obj_is_kind_of(time2, rb_cTime)) { + rb_raise(rb_eTypeError, "time + time?"); } + f = NUM2DBL(time2); + sec = (time_t)f; + usec = tobj->tv.tv_usec + (time_t)((f - (double)sec)*1e6); + sec = tobj->tv.tv_sec + sec; if (usec >= 1000000) { /* usec overflow */ sec++; usec -= 1000000; } - return rb_time_new(sec, usec); + time2 = rb_time_new(sec, usec); + if (tobj->gmt) { + GetTimeval(time2, tobj); + tobj->gmt = 1; + } + return time2; } static VALUE time_minus(time1, time2) VALUE time1, time2; { - struct time_object *tobj1, *tobj2; - int sec, usec; + struct time_object *tobj; + time_t sec, usec; + double f; - GetTimeval(time1, tobj1); + GetTimeval(time1, tobj); if (rb_obj_is_instance_of(time2, rb_cTime)) { - double f; + struct time_object *tobj2; GetTimeval(time2, tobj2); - f = tobj1->tv.tv_sec - tobj2->tv.tv_sec; - - f += (tobj1->tv.tv_usec - tobj2->tv.tv_usec)*1e-6; + f = tobj->tv.tv_sec - tobj2->tv.tv_sec; + f += (tobj->tv.tv_usec - tobj2->tv.tv_usec)*1e-6; return rb_float_new(f); } - else if (TYPE(time2) == T_FLOAT) { - sec = tobj1->tv.tv_sec - (int)RFLOAT(time2)->value; - usec = tobj1->tv.tv_usec - (RFLOAT(time2)->value - (double)sec)*1e6; - } else { - sec = tobj1->tv.tv_sec - NUM2INT(time2); - usec = tobj1->tv.tv_usec; + f = NUM2DBL(time2); + sec = (time_t)f; + usec = tobj->tv.tv_usec - (time_t)((f - (double)sec)*1e6); + sec = tobj->tv.tv_sec - sec; } if (usec < 0) { /* usec underflow */ sec--; usec += 1000000; } - return rb_time_new(sec, usec); + time2 = rb_time_new(sec, usec); + if (tobj->gmt) { + GetTimeval(time2, tobj); + tobj->gmt = 1; + } + return time2; } static VALUE @@ -552,7 +631,7 @@ time_sec(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } return INT2FIX(tobj->tm.tm_sec); } @@ -565,7 +644,7 @@ time_min(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } return INT2FIX(tobj->tm.tm_min); } @@ -578,7 +657,7 @@ time_hour(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } return INT2FIX(tobj->tm.tm_hour); } @@ -591,7 +670,7 @@ time_mday(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } return INT2FIX(tobj->tm.tm_mday); } @@ -604,7 +683,7 @@ time_mon(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } return INT2FIX(tobj->tm.tm_mon+1); } @@ -617,7 +696,7 @@ time_year(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } return INT2FIX(tobj->tm.tm_year+1900); } @@ -630,7 +709,7 @@ time_wday(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } return INT2FIX(tobj->tm.tm_wday); } @@ -643,9 +722,9 @@ time_yday(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } - return INT2FIX(tobj->tm.tm_yday); + return INT2FIX(tobj->tm.tm_yday+1); } static VALUE @@ -656,7 +735,7 @@ time_isdst(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } return tobj->tm.tm_isdst?Qtrue:Qfalse; } @@ -666,15 +745,15 @@ time_zone(time) VALUE time; { struct time_object *tobj; - char buf[10]; + char buf[64]; int len; GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } - len = strftime(buf, 10, "%Z", &(tobj->tm)); + len = strftime(buf, 64, "%Z", &tobj->tm); return rb_str_new(buf, len); } @@ -686,7 +765,7 @@ time_to_a(time) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } return rb_ary_new3(10, INT2FIX(tobj->tm.tm_sec), @@ -696,7 +775,7 @@ time_to_a(time) INT2FIX(tobj->tm.tm_mon+1), INT2FIX(tobj->tm.tm_year+1900), INT2FIX(tobj->tm.tm_wday), - INT2FIX(tobj->tm.tm_yday), + INT2FIX(tobj->tm.tm_yday+1), tobj->tm.tm_isdst?Qtrue:Qfalse, time_zone(time)); } @@ -708,23 +787,31 @@ rb_strftime(buf, format, time) char * volatile format; struct tm * volatile time; { - volatile int i; - int len; + volatile int size; + int len, flen; + (*buf)[0] = '\0'; + flen = strlen(format); + if (flen == 0) { + return 0; + } len = strftime(*buf, SMALLBUF, format, time); if (len != 0) return len; - for (i=1024; i<8192; i+=1024) { - *buf = xmalloc(i); - len = strftime(*buf, i-1, format, time); - if (len == 0) { - free(*buf); - continue; - } - return len; + for (size=1024; ; size*=2) { + *buf = xmalloc(size); + (*buf)[0] = '\0'; + len = strftime(*buf, size, format, time); + /* + * buflen can be zero EITHER because there's not enough + * room in the string, or because the control command + * goes to the empty string. Make a reasonable guess that + * if the buffer is 1024 times bigger than the length of the + * format string, it's not failing for lack of room. + */ + if (len > 0 || len >= 1024 * flen) return len; + free(*buf); } - - rb_raise(rb_eArgError, "bad strftime format or result too long"); - return Qnil; /* not reached */ + /* not reached */ } static VALUE @@ -740,23 +827,28 @@ time_strftime(time, format) GetTimeval(time, tobj); if (tobj->tm_got == 0) { - time_localtime(time); + time_get_tm(time, tobj->gmt); } fmt = str2cstr(format, &len); + if (len == 0) { + rb_warning("strftime called with empty format string"); + } if (strlen(fmt) < len) { /* Ruby string may contain \0's. */ char *p = fmt, *pe = fmt + len; str = rb_str_new(0, 0); while (p < pe) { - len = rb_strftime(&buf, p, &(tobj->tm)); + len = rb_strftime(&buf, p, &tobj->tm); rb_str_cat(str, buf, len); p += strlen(p) + 1; + if (p <= pe) + rb_str_cat(str, "\0", 1); if (len > SMALLBUF) free(buf); } return str; } - len = rb_strftime(&buf, RSTRING(format)->ptr, &(tobj->tm)); + len = rb_strftime(&buf, RSTRING(format)->ptr, &tobj->tm); str = rb_str_new(buf, len); if (buf != buffer) free(buf); return str; @@ -797,26 +889,43 @@ time_s_times(obj) } static VALUE -time_dump(time, limit) - VALUE time, limit; +time_dump(argc, argv, time) + int argc; + VALUE *argv; + VALUE time; { + VALUE dummy; struct time_object *tobj; - int sec, usec; + struct tm *tm; + unsigned long p, s; unsigned char buf[8]; + time_t t; int i; + rb_scan_args(argc, argv, "01", &dummy); GetTimeval(time, tobj); - sec = tobj->tv.tv_sec; - usec = tobj->tv.tv_usec; + + t = tobj->tv.tv_sec; + tm = gmtime(&t); + + p = 0x1 << 31 | /* 1 */ + tm->tm_year << 14 | /* 17 */ + tm->tm_mon << 10 | /* 4 */ + tm->tm_mday << 5 | /* 5 */ + tm->tm_hour; /* 5 */ + s = tm->tm_min << 26 | /* 6 */ + tm->tm_sec << 20 | /* 6 */ + tobj->tv.tv_usec; /* 20 */ for (i=0; i<4; i++) { - buf[i] = sec & 0xff; - sec = RSHIFT(sec, 8); + buf[i] = p & 0xff; + p = RSHIFT(p, 8); } for (i=4; i<8; i++) { - buf[i] = usec & 0xff; - usec = RSHIFT(usec, 8); + buf[i] = s & 0xff; + s = RSHIFT(s, 8); } + return rb_str_new(buf, 8); } @@ -824,8 +933,10 @@ static VALUE time_load(klass, str) VALUE klass, str; { - int sec, usec; + unsigned long p, s; + time_t sec, usec; unsigned char *buf; + struct tm tm; int i; buf = str2cstr(str, &i); @@ -833,14 +944,28 @@ time_load(klass, str) rb_raise(rb_eTypeError, "marshaled time format differ"); } - sec = usec = 0; + p = s = 0; for (i=0; i<4; i++) { - sec |= buf[i]<<(8*i); + p |= buf[i]<<(8*i); } for (i=4; i<8; i++) { - usec |= buf[i]<<(8*(i-4)); + s |= buf[i]<<(8*(i-4)); } + if ((p & (1<<31)) == 0) { + return time_new_internal(klass, p, s); + } + p &= ~(1<<31); + tm.tm_year = (p >> 14) & 0x1ffff; + tm.tm_mon = (p >> 10) & 0xf; + tm.tm_mday = (p >> 5) & 0x1f; + tm.tm_hour = p & 0x1f; + tm.tm_min = (s >> 26) & 0x3f; + tm.tm_sec = (s >> 20) & 0x3f; + + sec = make_time_t(&tm, gmtime); + usec = (time_t) s & 0xfffff; + return time_new_internal(klass, sec, usec); } @@ -864,6 +989,7 @@ Init_Time() rb_define_method(rb_cTime, "<=>", time_cmp, 1); rb_define_method(rb_cTime, "eql?", time_eql, 1); rb_define_method(rb_cTime, "hash", time_hash, 0); + rb_define_method(rb_cTime, "clone", time_clone, 0); rb_define_method(rb_cTime, "localtime", time_localtime, 0); rb_define_method(rb_cTime, "gmtime", time_gmtime, 0); @@ -889,6 +1015,8 @@ Init_Time() rb_define_method(rb_cTime, "isdst", time_isdst, 0); rb_define_method(rb_cTime, "zone", time_zone, 0); + rb_define_method(rb_cTime, "gmt?", time_gmt_p, 0); + 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); @@ -900,6 +1028,6 @@ Init_Time() #endif /* methods for marshaling */ + rb_define_method(rb_cTime, "_dump", time_dump, -1); rb_define_singleton_method(rb_cTime, "_load", time_load, 1); - rb_define_method(rb_cTime, "_dump", time_dump, 1); } |