diff options
-rw-r--r-- | ChangeLog | 27 | ||||
-rw-r--r-- | configure.in | 13 | ||||
-rw-r--r-- | missing/strftime.c | 27 | ||||
-rw-r--r-- | time.c | 140 |
4 files changed, 180 insertions, 27 deletions
@@ -1,3 +1,30 @@ +Mon Dec 17 15:19:32 2001 Tanaka Akira <akr@m17n.org> + + * time.c: new method `gmtoff', `gmt_offset' and `utc_offset'. + (time_utc_offset): new function. + (Init_Time): bind above methods to `time_utc_offset'. + + * time.c: 64bit time_t support. + (time_s_at): use NUM2LONG instead of NUM2INT for tv_sec. + (time_arg): initialize tm_isdst correctly. + use long to initialize tm_year. + (search_time_t): renamed from `make_time_t'. + (make_time_t): call `timegm' and `mktime' instead of `search_time_t' + if availabe. + (time_to_i): use LONG2NUM instead of INT2NUM. + (time_localtime): check localtime failure. + (time_gmtime): check gmtime failure. + (time_year): use LONG2NUM instead of INT2FIX. + (time_to_a): use long for tm_year. + (time_dump): check tm_year which is not representable with 17bit. + (time_load): initialize tm_isdst. + + * configure.in: check existence of `mktime' and `timegm'. + check existence of tm_gmtoff field of struct tm. + fix negative time_t for 64bit time_t. + + * missing/strftime.c: fix overflow by tm_year + 1900. + Fri Dec 14 04:23:36 2001 Minero Aoki <aamine@loveruby.net> * lib/net/pop.rb: new method Net::POP3.APOP diff --git a/configure.in b/configure.in index 4bba1828c6..b70f95d722 100644 --- a/configure.in +++ b/configure.in @@ -300,8 +300,17 @@ AC_CHECK_FUNCS(fmod killpg drand48 random wait4 waitpid syscall chroot\ setitimer setruid seteuid setreuid setresuid setproctitle\ setrgid setegid setregid setresgid pause lchown lchmod\ getpgrp setpgrp getpgid setpgid getgroups getpriority getrlimit\ - dlopen sigprocmask sigaction _setjmp setsid telldir seekdir fchmod) + dlopen sigprocmask sigaction _setjmp setsid telldir seekdir fchmod\ + mktime timegm) AC_STRUCT_TIMEZONE +AC_CACHE_CHECK(for struct tm.tm_gmtoff, rb_cv_member_struct_tm_tm_gmtoff, + [AC_TRY_COMPILE([#include <time.h>], + [struct tm t; t.tm_gmtoff = 3600;], + [rb_cv_member_struct_tm_tm_gmtoff=yes], + [rb_cv_member_struct_tm_tm_gmtoff=no])]) +if test "$rb_cv_member_struct_tm_tm_gmtoff" = yes; then + AC_DEFINE(HAVE_STRUCT_TM_TM_GMTOFF) +fi AC_CACHE_CHECK(for external int daylight, rb_cv_have_daylight, [AC_TRY_LINK([#include <time.h> int i;], @@ -337,7 +346,7 @@ main() struct tm *tm; check(gmtime(&t), 69, 12, 31, 23, 59); - t = -0x80000000; + t = ~(time_t)0 << 31; check(gmtime(&t), 1, 12, 13, 20, 52); return 0; } diff --git a/missing/strftime.c b/missing/strftime.c index 3042649aeb..a9e0bc0757 100644 --- a/missing/strftime.c +++ b/missing/strftime.c @@ -175,7 +175,8 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr) char *start = s; auto char tbuf[100]; long off; - int i, w, y; + int i, w; + long y; static short first = 1; #ifdef POSIX_SEMANTICS static char *savetz = NULL; @@ -378,7 +379,7 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr) break; case 'Y': /* year with century */ - sprintf(tbuf, "%d", 1900 + timeptr->tm_year); + sprintf(tbuf, "%ld", 1900L + timeptr->tm_year); break; #ifdef MAILHEADER_EXT @@ -503,10 +504,10 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr) #ifdef VMS_EXT case 'v': /* date as dd-bbb-YYYY */ - sprintf(tbuf, "%2d-%3.3s-%4d", + sprintf(tbuf, "%2d-%3.3s-%4ld", range(1, timeptr->tm_mday, 31), months_a[range(0, timeptr->tm_mon, 11)], - timeptr->tm_year + 1900); + timeptr->tm_year + 1900L); for (i = 3; i < 6; i++) if (islower(tbuf[i])) tbuf[i] = toupper(tbuf[i]); @@ -516,7 +517,7 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr) #ifdef POSIX2_DATE case 'C': - sprintf(tbuf, "%02d", (timeptr->tm_year + 1900) / 100); + sprintf(tbuf, "%02ld", (timeptr->tm_year + 1900L) / 100); break; @@ -550,16 +551,16 @@ strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr) */ w = iso8601wknum(timeptr); if (timeptr->tm_mon == 11 && w == 1) - y = 1900 + timeptr->tm_year + 1; + y = 1900L + timeptr->tm_year + 1; else if (timeptr->tm_mon == 0 && w >= 52) - y = 1900 + timeptr->tm_year - 1; + y = 1900L + timeptr->tm_year - 1; else - y = 1900 + timeptr->tm_year; + y = 1900L + timeptr->tm_year; if (*format == 'G') - sprintf(tbuf, "%d", y); + sprintf(tbuf, "%ld", y); else - sprintf(tbuf, "%02d", y % 100); + sprintf(tbuf, "%02ld", y % 100); break; #endif /* ISO_DATE_EXT */ default: @@ -590,10 +591,10 @@ out: #ifndef __STDC__ static int isleap(year) -int year; +long year; #else static int -isleap(int year) +isleap(long year) #endif { return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); @@ -684,7 +685,7 @@ iso8601wknum(const struct tm *timeptr) dec31ly.tm_mon = 11; dec31ly.tm_mday = 31; dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1; - dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900); + dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900L); weeknum = iso8601wknum(& dec31ly); #endif } @@ -173,7 +173,7 @@ time_s_at(argc, argv, klass) VALUE time, t; if (rb_scan_args(argc, argv, "11", &time, &t) == 2) { - tv.tv_sec = NUM2INT(time); + tv.tv_sec = NUM2LONG(time); tv.tv_usec = NUM2INT(t); } else { @@ -215,6 +215,7 @@ time_arg(argc, argv, tm, usec) { VALUE v[7]; int i; + long year; MEMZERO(tm, struct tm, 1); if (argc == 10) { @@ -225,16 +226,28 @@ time_arg(argc, argv, tm, usec) v[4] = argv[1]; v[5] = argv[0]; *usec = 0; - tm->tm_isdst = RTEST(argv[9]) ? 1 : 0; + tm->tm_isdst = RTEST(argv[8]) ? 1 : 0; } else { rb_scan_args(argc, argv, "16", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5],&v[6]); *usec = (argc == 7) ? NUM2INT(v[6]) : 0; + tm->tm_isdst = -1; } - tm->tm_year = obj2long(v[0]); - if (0 <= tm->tm_year && tm->tm_year < 69) tm->tm_year += 100; - if (tm->tm_year >= 1900) tm->tm_year -= 1900; + year = obj2long(v[0]); + + if (0 <= year && year < 39) { + year += 2000; + rb_warning("2 digits year is used"); + } + else if (69 <= year && year < 139) { + year += 1900; + rb_warning("2 or 3 digits year is used"); + } + + year -= 1900; + tm->tm_year = year; + if (NIL_P(v[1])) { tm->tm_mon = 0; } @@ -270,6 +283,7 @@ time_arg(argc, argv, tm, usec) /* value validation */ if ( + tm->tm_year != year || #ifndef NEGATIVE_TIME_T tm->tm_year < 69 || #endif @@ -307,7 +321,7 @@ tmcmp(a, b) } static time_t -make_time_t(tptr, utc_p) +search_time_t(tptr, utc_p) struct tm *tptr; int utc_p; { @@ -316,10 +330,10 @@ make_time_t(tptr, utc_p) int d, have_guess; int find_dst; - find_dst = 1; + find_dst = 0 < tptr->tm_isdst; #ifdef NEGATIVE_TIME_T - guess_lo = 1 << (8 * sizeof(time_t) - 1); + guess_lo = 1L << (8 * sizeof(time_t) - 1); #else guess_lo = 0; #endif @@ -536,6 +550,55 @@ make_time_t(tptr, utc_p) return 0; /* not reached */ } +static time_t +make_time_t(tptr, utc_p) + struct tm *tptr; + int utc_p; +{ + time_t t; + struct tm *tmp, buf; + buf = *tptr; + if (utc_p) { +#if defined(HAVE_TIMEGM) + t = timegm(&buf); + if (t == -1) { +#ifdef NEGATIVE_TIME_T + if (!(tmp = gmtime(&t)) || + tptr->tm_year != tmp->tm_year || + tptr->tm_mon != tmp->tm_mon || + tptr->tm_mday != tmp->tm_mday || + tptr->tm_hour != tmp->tm_hour || + tptr->tm_min != tmp->tm_min || + tptr->tm_sec != tmp->tm_sec) +#endif + rb_raise(rb_eArgError, "gmtime error"); + } +#else + t = search_time_t(&buf, utc_p); +#endif + } + else { +#if defined(HAVE_MKTIME) + t = mktime(&buf); + if (t == -1) { +#ifdef NEGATIVE_TIME_T + if (!(tmp = localtime(&t)) || + tptr->tm_year != tmp->tm_year || + tptr->tm_mon != tmp->tm_mon || + tptr->tm_mday != tmp->tm_mday || + tptr->tm_hour != tmp->tm_hour || + tptr->tm_min != tmp->tm_min || + tptr->tm_sec != tmp->tm_sec) +#endif + rb_raise(rb_eArgError, "localtime error"); + } +#else + t = search_time_t(&buf, utc_p); +#endif + } + return t; +} + static VALUE time_utc_or_local(argc, argv, utc_p, klass) int argc; @@ -578,7 +641,7 @@ time_to_i(time) struct time_object *tobj; GetTimeval(time, tobj); - return INT2NUM(tobj->tv.tv_sec); + return LONG2NUM(tobj->tv.tv_sec); } static VALUE @@ -746,6 +809,8 @@ time_localtime(time) } t = tobj->tv.tv_sec; tm_tmp = localtime(&t); + if (!tm_tmp) + rb_raise(rb_eArgError, "localtime error"); tobj->tm = *tm_tmp; tobj->tm_got = 1; tobj->gmt = 0; @@ -770,6 +835,8 @@ time_gmtime(time) } t = tobj->tv.tv_sec; tm_tmp = gmtime(&t); + if (!tm_tmp) + rb_raise(rb_eArgError, "gmtime error"); tobj->tm = *tm_tmp; tobj->tm_got = 1; tobj->gmt = 1; @@ -1002,7 +1069,7 @@ time_year(time) if (tobj->tm_got == 0) { time_get_tm(time, tobj->gmt); } - return INT2FIX(tobj->tm.tm_year+1900); + return LONG2NUM((long)tobj->tm.tm_year+1900); } static VALUE @@ -1071,6 +1138,48 @@ time_zone(time) } static VALUE +time_utc_offset(time) + VALUE time; +{ + struct time_object *tobj; + + GetTimeval(time, tobj); + if (tobj->tm_got == 0) { + time_get_tm(time, tobj->gmt); + } + + if (tobj->gmt == 1) { + return INT2FIX(0); + } + else { +#if defined(HAVE_STRUCT_TM_TM_GMTOFF) + return INT2NUM(tobj->tm.tm_gmtoff); +#else + struct tm *u, *l; + time_t t; + int off; + l = &tobj->tm; + t = tobj->tv.tv_sec; + u = gmtime(&t); + if (!u) + rb_raise(rb_eArgError, "gmtime error"); + if (l->tm_year != u->tm_year) + off = l->tm_year < u->tm_year ? -1 : 1; + else if (l->tm_mon != u->tm_mon) + off = l->tm_mon < u->tm_mon ? -1 : 1; + else if (l->tm_mday != u->tm_mday) + off = l->tm_mday < u->tm_mday ? -1 : 1; + else + off = 0; + off = off * 24 + l->tm_hour - u->tm_hour; + off = off * 60 + l->tm_min - u->tm_min; + off = off * 60 + l->tm_sec - u->tm_sec; + return INT2FIX(off); +#endif + } +} + +static VALUE time_to_a(time) VALUE time; { @@ -1086,7 +1195,7 @@ time_to_a(time) INT2FIX(tobj->tm.tm_hour), INT2FIX(tobj->tm.tm_mday), INT2FIX(tobj->tm.tm_mon+1), - INT2FIX(tobj->tm.tm_year+1900), + LONG2NUM((long)tobj->tm.tm_year+1900), INT2FIX(tobj->tm.tm_wday), INT2FIX(tobj->tm.tm_yday+1), tobj->tm.tm_isdst?Qtrue:Qfalse, @@ -1200,6 +1309,9 @@ time_dump(argc, argv, time) t = tobj->tv.tv_sec; tm = gmtime(&t); + if ((tm->tm_year & 0x1ffff) != tm->tm_year) + rb_raise(rb_eArgError, "too big year to marshal"); + p = 0x1 << 31 | /* 1 */ tm->tm_year << 14 | /* 17 */ tm->tm_mon << 10 | /* 4 */ @@ -1255,8 +1367,9 @@ time_load(klass, str) tm.tm_hour = p & 0x1f; tm.tm_min = (s >> 26) & 0x3f; tm.tm_sec = (s >> 20) & 0x3f; + tm.tm_isdst = 0; - sec = make_time_t(&tm, gmtime); + sec = make_time_t(&tm, Qtrue); usec = (time_t) s & 0xfffff; return time_new_internal(klass, sec, usec); @@ -1315,6 +1428,9 @@ Init_Time() rb_define_method(rb_cTime, "isdst", time_isdst, 0); rb_define_method(rb_cTime, "dst?", time_isdst, 0); rb_define_method(rb_cTime, "zone", time_zone, 0); + rb_define_method(rb_cTime, "gmtoff", time_utc_offset, 0); + rb_define_method(rb_cTime, "gmt_offset", time_utc_offset, 0); + rb_define_method(rb_cTime, "utc_offset", time_utc_offset, 0); rb_define_method(rb_cTime, "utc?", time_utc_p, 0); rb_define_method(rb_cTime, "gmt?", time_utc_p, 0); |