summaryrefslogtreecommitdiff
path: root/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'time.c')
-rw-r--r--time.c324
1 files changed, 191 insertions, 133 deletions
diff --git a/time.c b/time.c
index e58fc4cf70..e059273b72 100644
--- a/time.c
+++ b/time.c
@@ -13,11 +13,6 @@
#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
@@ -33,7 +28,16 @@ struct timeval {
#ifdef HAVE_SYS_TIMES_H
#include <sys/times.h>
#endif
+
+#ifdef USE_CWGUSI
+typedef int time_t;
+time_t gettimeofday(struct timeval*, struct timezone*);
+time_t strcasecmp(char*, char*);
+#endif
+
+#if 0
#include <math.h>
+#endif
VALUE rb_cTime;
#if defined(HAVE_TIMES) || defined(NT)
@@ -63,7 +67,7 @@ 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);
@@ -74,7 +78,7 @@ 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;
@@ -93,7 +97,7 @@ 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);
}
@@ -107,7 +111,7 @@ rb_time_timeval(time)
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;
@@ -121,7 +125,7 @@ rb_time_timeval(time)
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;
@@ -154,22 +158,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;
@@ -186,102 +190,91 @@ 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 > 1900) 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 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;
while (diff = t - (tm->tm_year)) {
guess += diff * 364 * 24 * 3600;
- if (guess < 0) rb_raise(rb_eArgError, "too far future");
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;
}
- 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) rb_raise(rb_eArgError, "too far future");
- time = time_new_internal(klass, guess, 0);
- if (gm_or_local) return time_gmtime(time);
- return time_localtime(time);
+ return guess;
error:
rb_raise(rb_eArgError, "gmtime/localtime error");
@@ -289,6 +282,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;
@@ -313,7 +325,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
@@ -333,7 +345,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
@@ -341,12 +353,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);
@@ -355,7 +367,7 @@ time_cmp(time1, time2)
{
double t;
- if (tobj1->tv.tv_sec == (int)RFLOAT(time2)->value) return INT2FIX(0);
+ if (tobj1->tv.tv_sec == (long)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);
@@ -373,7 +385,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);
@@ -400,7 +412,7 @@ 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;
@@ -463,7 +475,7 @@ time_to_s(time)
VALUE time;
{
struct time_object *tobj;
- char buf[64];
+ char buf[128];
int len;
GetTimeval(time, tobj);
@@ -472,12 +484,12 @@ time_to_s(time)
}
#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);
}
@@ -488,22 +500,14 @@ time_plus(time1, time2)
{
struct time_object *tobj1, *tobj2;
long 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;
- }
+
+ f = NUM2DBL(time2);
+ sec = (long)f;
+ usec = tobj1->tv.tv_usec + (long)((f - (double)sec)*1e6);
+ sec = tobj1->tv.tv_sec + sec;
if (usec >= 1000000) { /* usec overflow */
sec++;
@@ -517,26 +521,23 @@ time_minus(time1, time2)
VALUE time1, time2;
{
struct time_object *tobj1, *tobj2;
- int sec, usec;
+ long sec, usec;
+ double f;
GetTimeval(time1, tobj1);
if (rb_obj_is_instance_of(time2, rb_cTime)) {
- double f;
GetTimeval(time2, tobj2);
f = tobj1->tv.tv_sec - tobj2->tv.tv_sec;
-
f += (tobj1->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 = (long)f;
+ usec = tobj1->tv.tv_usec - (long)((f - (double)sec)*1e6);
+ sec = tobj1->tv.tv_sec - sec;
}
if (usec < 0) { /* usec underflow */
@@ -647,7 +648,7 @@ time_yday(time)
if (tobj->tm_got == 0) {
time_localtime(time);
}
- return INT2FIX(tobj->tm.tm_yday);
+ return INT2FIX(tobj->tm.tm_yday+1);
}
static VALUE
@@ -668,7 +669,7 @@ time_zone(time)
VALUE time;
{
struct time_object *tobj;
- char buf[10];
+ char buf[64];
int len;
GetTimeval(time, tobj);
@@ -676,7 +677,7 @@ time_zone(time)
time_localtime(time);
}
- len = strftime(buf, 10, "%Z", &(tobj->tm));
+ len = strftime(buf, 64, "%Z", &(tobj->tm));
return rb_str_new(buf, len);
}
@@ -698,7 +699,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));
}
@@ -710,23 +711,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
@@ -745,6 +754,9 @@ time_strftime(time, format)
time_localtime(time);
}
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;
@@ -754,6 +766,8 @@ time_strftime(time, format)
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;
@@ -799,48 +813,92 @@ 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;
- unsigned char buf[8];
+ time_t sec, usec;
+ struct tm *tm;
+ unsigned char buf[11];
int i;
+ rb_scan_args(argc, argv, "01", &dummy);
GetTimeval(time, tobj);
sec = tobj->tv.tv_sec;
usec = tobj->tv.tv_usec;
+ tm = gmtime(&sec);
+
+ i = tm->tm_year;
+ buf[0] = i & 0xff;
+ i = RSHIFT(i, 8);
+ buf[1] = i & 0xff;
+
+ buf[2] = tm->tm_mon;
+ buf[3] = tm->tm_mday;
+ buf[4] = tm->tm_hour;
+ buf[5] = tm->tm_min;
+ buf[6] = tm->tm_sec;
+
+ for (i=7; i<11; i++) {
+ buf[i] = usec & 0xff;
+ usec = RSHIFT(usec, 8);
+ }
+ return rb_str_new(buf, 11);
+}
+
+static VALUE
+time_old_load(klass, buf)
+ VALUE klass;
+ unsigned char *buf;
+{
+ time_t sec, usec;
+ int i;
+
+ sec = usec = 0;
for (i=0; i<4; i++) {
- buf[i] = sec & 0xff;
- sec = RSHIFT(sec, 8);
+ sec |= buf[i]<<(8*i);
}
for (i=4; i<8; i++) {
- buf[i] = usec & 0xff;
- usec = RSHIFT(usec, 8);
+ usec |= buf[i]<<(8*(i-4));
}
- return rb_str_new(buf, 8);
+
+ return time_new_internal(klass, sec, usec);
}
static VALUE
time_load(klass, str)
VALUE klass, str;
{
- int sec, usec;
+ time_t sec, usec;
unsigned char *buf;
+ struct tm tm;
int i;
buf = str2cstr(str, &i);
- if (i != 8) {
+ if (i == 8) {
+ return time_old_load(klass, buf);
+ }
+ if (i != 11) {
rb_raise(rb_eTypeError, "marshaled time format differ");
}
- sec = usec = 0;
+ tm.tm_year = buf[0];
+ tm.tm_year |= buf[1]<<8;
+ tm.tm_mon = buf[2];
+ tm.tm_mday = buf[3];
+ tm.tm_hour = buf[4];
+ tm.tm_min = buf[5];
+ tm.tm_sec = buf[6];
+
+ sec = make_time_t(&tm, gmtime);
+
+ usec = 0;
for (i=0; i<4; i++) {
- sec |= buf[i]<<(8*i);
- }
- for (i=4; i<8; i++) {
- usec |= buf[i]<<(8*(i-4));
+ usec |= buf[i+7]<<(8*i);
}
return time_new_internal(klass, sec, usec);
@@ -902,6 +960,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);
}