From 3bc16f62c1fb6225b3f6cb96b62f78e44636aae4 Mon Sep 17 00:00:00 2001 From: akr Date: Tue, 21 Apr 2009 14:56:59 +0000 Subject: * time.c: remove time_t restriction from Time class. * timev.h: new file to define struct vtm. * strftime.c: format struct vtm instead of struct tm. * ext/syck/rubyext.c (mktime_do): don't use time_t; [ruby-dev:38191] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@23242 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- strftime.c | 244 ++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 169 insertions(+), 75 deletions(-) (limited to 'strftime.c') diff --git a/strftime.c b/strftime.c index 9c7b7421a5..69012b122c 100644 --- a/strftime.c +++ b/strftime.c @@ -48,6 +48,7 @@ */ #include "ruby/ruby.h" +#include "timev.h" #ifndef GAWK #include @@ -103,9 +104,13 @@ #define const /**/ static int weeknumber(); adddecl(static int iso8601wknum();) +static int weeknumber_v(); +adddecl(static int iso8601wknum_v();) #else static int weeknumber(const struct tm *timeptr, int firstweekday); adddecl(static int iso8601wknum(const struct tm *timeptr);) +static int weeknumber_v(const struct vtm *vtm, int firstweekday); +adddecl(static int iso8601wknum_v(const struct vtm *vtm);) #endif #ifdef STDC_HEADERS @@ -175,10 +180,17 @@ max(int a, int b) #error No string literal concatenation #endif +#define add(x,y) (rb_funcall((x), '+', 1, (y))) +#define sub(x,y) (rb_funcall((x), '-', 1, (y))) +#define mul(x,y) (rb_funcall((x), '*', 1, (y))) +#define quo(x,y) (rb_funcall((x), rb_intern("quo"), 1, (y))) +#define div(x,y) (rb_funcall((x), rb_intern("div"), 1, (y))) +#define mod(x,y) (rb_funcall((x), '%', 1, (y))) + /* strftime --- produce formatted time */ size_t -rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr, const struct timespec *ts, int gmt) +rb_strftime(char *s, size_t maxsize, const char *format, const struct vtm *vtm, VALUE timev, int gmt) { char *endp = s + maxsize; char *start = s; @@ -219,7 +231,7 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept }; static const char ampm[][3] = { "AM", "PM", }; - if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0) + if (s == NULL || format == NULL || vtm == NULL || maxsize == 0) return 0; /* quick check if we even need to bother */ @@ -293,7 +305,7 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept } while (0) #define STRFTIME(fmt) \ do { \ - i = rb_strftime(s, endp - s, fmt, timeptr, ts, gmt); \ + i = rb_strftime(s, endp - s, fmt, vtm, timev, gmt); \ if (!i) return 0; \ if (precision > i) {\ memmove(s + precision - i, s, i);\ @@ -302,6 +314,29 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept }\ else s += i; \ } while (0) +#define FMTV(def_pad, def_prec, fmt, val) \ + do { \ + VALUE tmp = (val); \ + if (FIXNUM_P(tmp)) { \ + FMT((def_pad), (def_prec), "l"fmt, FIX2LONG(tmp)); \ + } \ + else { \ + VALUE args[2], result; \ + size_t l; \ + if (precision <= 0) precision = (def_prec); \ + if (flags & BIT_OF(LEFT)) precision = 1; \ + args[0] = INT2FIX(precision); \ + args[1] = val; \ + if (padding == '0' || (!padding && def_pad == '0')) \ + result = rb_str_format(2, args, rb_str_new2("%0*"fmt)); \ + else \ + result = rb_str_format(2, args, rb_str_new2("%*"fmt)); \ + l = strlcpy(s, StringValueCStr(result), endp-s); \ + if ((size_t)(endp-s) <= l) \ + goto err; \ + s += l; \ + } \ + } while (0) if (*format != '%') { *s++ = *format; @@ -328,10 +363,10 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE)); flags |= BIT_OF(UPPER); } - if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) + if (vtm->wday < 0 || vtm->wday > 6) i = 1, tp = "?"; else - i = 3, tp = days_l[timeptr->tm_wday]; + i = 3, tp = days_l[vtm->wday]; break; case 'A': /* full weekday name */ @@ -339,10 +374,10 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE)); flags |= BIT_OF(UPPER); } - if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) + if (vtm->wday < 0 || vtm->wday > 6) i = 1, tp = "?"; else - i = strlen(tp = days_l[timeptr->tm_wday]); + i = strlen(tp = days_l[vtm->wday]); break; #ifdef SYSV_EXT @@ -353,10 +388,10 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE)); flags |= BIT_OF(UPPER); } - if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) + if (vtm->mon < 1 || vtm->mon > 12) i = 1, tp = "?"; else - i = 3, tp = months_l[timeptr->tm_mon]; + i = 3, tp = months_l[vtm->mon-1]; break; case 'B': /* full month name */ @@ -364,10 +399,10 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE)); flags |= BIT_OF(UPPER); } - if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) + if (vtm->mon < 1 || vtm->mon > 12) i = 1, tp = "?"; else - i = strlen(tp = months_l[timeptr->tm_mon]); + i = strlen(tp = months_l[vtm->mon-1]); break; case 'c': /* appropriate date and time representation */ @@ -375,17 +410,17 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept continue; case 'd': /* day of the month, 01 - 31 */ - i = range(1, timeptr->tm_mday, 31); + i = range(1, vtm->mday, 31); FMT('0', 2, "d", i); continue; case 'H': /* hour, 24-hour clock, 00 - 23 */ - i = range(0, timeptr->tm_hour, 23); + i = range(0, vtm->hour, 23); FMT('0', 2, "d", i); continue; case 'I': /* hour, 12-hour clock, 01 - 12 */ - i = range(0, timeptr->tm_hour, 23); + i = range(0, vtm->hour, 23); if (i == 0) i = 12; else if (i > 12) @@ -394,16 +429,16 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept continue; case 'j': /* day of the year, 001 - 366 */ - FMT('0', 3, "d", timeptr->tm_yday + 1); + FMT('0', 3, "d", vtm->yday); continue; case 'm': /* month, 01 - 12 */ - i = range(0, timeptr->tm_mon, 11); - FMT('0', 2, "d", i + 1); + i = range(1, vtm->mon, 12); + FMT('0', 2, "d", i); continue; case 'M': /* minute, 00 - 59 */ - i = range(0, timeptr->tm_min, 59); + i = range(0, vtm->min, 59); FMT('0', 2, "d", i); continue; @@ -414,7 +449,7 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE)); flags |= BIT_OF(LOWER); } - i = range(0, timeptr->tm_hour, 23); + i = range(0, vtm->hour, 23); if (i < 12) tp = ampm[0]; else @@ -423,25 +458,28 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept break; case 's': - FMT('0', 1, "d", (int) ts->tv_sec); - continue; + { + VALUE sec = div(timev, INT2FIX(1)); + FMTV('0', 1, "d", sec); + continue; + } case 'S': /* second, 00 - 60 */ - i = range(0, timeptr->tm_sec, 60); + i = range(0, vtm->sec, 60); FMT('0', 2, "d", i); continue; case 'U': /* week of year, Sunday is first day of week */ - FMT('0', 2, "d", weeknumber(timeptr, 0)); + FMT('0', 2, "d", weeknumber_v(vtm, 0)); continue; case 'w': /* weekday, Sunday == 0, 0 - 6 */ - i = range(0, timeptr->tm_wday, 6); + i = range(0, vtm->wday, 6); FMT('0', 1, "d", i); continue; case 'W': /* week of year, Monday is first day of week */ - FMT('0', 2, "d", weeknumber(timeptr, 1)); + FMT('0', 2, "d", weeknumber_v(vtm, 1)); continue; case 'x': /* appropriate date representation */ @@ -453,12 +491,12 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept continue; case 'y': /* year without a century, 00 - 99 */ - i = timeptr->tm_year % 100; + i = NUM2INT(mod(vtm->year, INT2FIX(100))); FMT('0', 2, "d", i); continue; case 'Y': /* year with century */ - FMT('0', 1, "ld", 1900L + timeptr->tm_year); + FMTV('0', 1, "d", vtm->year); continue; #ifdef MAILHEADER_EXT @@ -484,6 +522,8 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept off = 0; } else { + off = NUM2LONG(rb_funcall(quo(vtm->utc_offset, INT2FIX(60)), rb_intern("round"), 0)); +#if 0 #ifdef HAVE_TM_NAME /* * Systems with tm_name probably have tm_tzadj as @@ -521,6 +561,7 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept #endif /* !HAVE_VAR_TIMEZONE */ #endif /* !HAVE_TM_ZONE */ #endif /* !HAVE_TM_NAME */ +#endif /* 0 */ } if (off < 0) { off = -off; @@ -546,6 +587,7 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept tp = "UTC"; break; } +#if 0 #ifdef HAVE_TZNAME i = (daylight && timeptr->tm_isdst > 0); /* 0 or 1 */ tp = tzname[i]; @@ -567,6 +609,8 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept #endif /* HAVE_TM_NAME */ #endif /* HAVE_TM_ZONE */ #endif /* HAVE_TZNAME */ +#endif /* 0 */ + tp = vtm->zone; i = strlen(tp); break; @@ -586,7 +630,7 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept continue; case 'e': /* day of month, blank padded */ - FMT(' ', 2, "d", range(1, timeptr->tm_mday, 31)); + FMT(' ', 2, "d", range(1, vtm->mday, 31)); continue; case 'r': /* time as %I:%M:%S %p */ @@ -604,12 +648,12 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept #ifdef SUNOS_EXT case 'k': /* hour, 24-hour clock, blank pad */ - i = range(0, timeptr->tm_hour, 23); + i = range(0, vtm->hour, 23); FMT(' ', 2, "d", i); continue; case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */ - i = range(0, timeptr->tm_hour, 23); + i = range(0, vtm->hour, 23); if (i == 0) i = 12; else if (i > 12) @@ -621,25 +665,16 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept #ifdef VMS_EXT case 'v': /* date as dd-bbb-YYYY */ - w = snprintf(s, endp - s, "%2d-%3.3s-%4ld", - range(1, timeptr->tm_mday, 31), - months_l[range(0, timeptr->tm_mon, 11)], - timeptr->tm_year + 1900L); - if (w < 0) goto err; - for (i = 3; i < 6; i++) - if (ISLOWER(s[i])) - s[i] = TOUPPER(s[i]); - s += w; + STRFTIME("%e-%^b-%4Y"); continue; #endif #ifdef POSIX2_DATE case 'C': - FMT('0', 2, "ld", (timeptr->tm_year + 1900L) / 100); + FMTV('0', 2, "d", div(vtm->year, INT2FIX(100))); continue; - case 'E': /* POSIX locale extensions, ignored for now */ flags |= BIT_OF(LOCALE_E); @@ -650,12 +685,12 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept goto again; case 'V': /* week of year according ISO 8601 */ - FMT('0', 2, "d", iso8601wknum(timeptr)); + FMT('0', 2, "d", iso8601wknum_v(vtm)); continue; case 'u': /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */ - FMT('0', 1, "d", timeptr->tm_wday == 0 ? 7 : timeptr->tm_wday); + FMT('0', 1, "d", vtm->wday == 0 ? 7 : vtm->wday); continue; #endif /* POSIX2_DATE */ @@ -671,19 +706,25 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept * 53, that week is in last year. * Otherwise, it's this year. */ - w = iso8601wknum(timeptr); - if (timeptr->tm_mon == 11 && w == 1) - y = 1900L + timeptr->tm_year + 1; - else if (timeptr->tm_mon == 0 && w >= 52) - y = 1900L + timeptr->tm_year - 1; - else - y = 1900L + timeptr->tm_year; + { + VALUE yv = vtm->year; + w = iso8601wknum_v(vtm); + if (vtm->mon == 12 && w == 1) + yv = add(yv, INT2FIX(1)); + else if (vtm->mon == 1 && w >= 52) + yv = sub(yv, INT2FIX(1)); + + if (*format == 'G') { + FMTV('0', 1, "d", yv); + } + else { + yv = mod(yv, INT2FIX(100)); + y = FIX2LONG(yv); + FMT('0', 2, "ld", y); + } + continue; + } - if (*format == 'G') - FMT('0', 1, "ld", y); - else - FMT('0', 2, "ld", y % 100); - continue; #endif /* ISO_DATE_EXT */ @@ -703,35 +744,46 @@ rb_strftime(char *s, size_t maxsize, const char *format, const struct tm *timept w = 9; subsec: { - long n = ts->tv_nsec; + VALUE subsec = mod(timev, INT2FIX(1)); + int ww; + long n; if (precision <= 0) { precision = w; } NEEDS(precision); - if (precision < w) { - snprintf(tbuf, w + 1, "%.*ld", w, n); - memcpy(s, tbuf, precision); - } - else { - snprintf(s, endp - s, "%.*ld", w, n); - memset(s + w, '0', precision - w); - } - s += precision; + + ww = precision; + while (9 <= ww) { + subsec = mul(subsec, INT2FIX(1000000000)); + ww -= 9; + } + n = 1; + for (; 0 < ww; ww--) + n *= 10; + if (n != 1) + subsec = mul(subsec, INT2FIX(n)); + subsec = div(subsec, INT2FIX(1)); + + if (FIXNUM_P(subsec)) { + int l; + l = snprintf(s, endp - s, "%0*ld", precision, FIX2LONG(subsec)); + s += precision; + } + else { + VALUE args[2], result; + size_t l; + args[0] = INT2FIX(precision); + args[1] = subsec; + result = rb_str_format(2, args, rb_str_new2("%0*d")); + l = strlcpy(s, StringValueCStr(result), endp-s); + s += precision; + } } continue; case 'F': /* Equivalent to %Y-%m-%d */ - { - int mon, mday; - mon = range(0, timeptr->tm_mon, 11) + 1; - mday = range(1, timeptr->tm_mday, 31); - i = snprintf(s, endp - s, "%ld-%02d-%02d", - 1900L + timeptr->tm_year, mon, mday); - if (i < 0) - goto err; - s += i; - } + STRFTIME("%Y-%m-%d"); continue; case '-': @@ -820,6 +872,31 @@ isleap(long year) } +static void +vtm2tm_noyear(const struct vtm *vtm, struct tm *result) +{ + struct tm tm; + + /* for isleap() in iso8601wknum. +100 is -1900 (mod 400). */ + tm.tm_year = FIX2INT(mod(vtm->year, INT2FIX(400))) + 100; + + tm.tm_mon = vtm->mon-1; + tm.tm_mday = vtm->mday; + tm.tm_hour = vtm->hour; + tm.tm_min = vtm->min; + tm.tm_sec = vtm->sec; + tm.tm_wday = vtm->wday; + tm.tm_yday = vtm->yday-1; + tm.tm_isdst = vtm->isdst; +#if defined(HAVE_STRUCT_TM_TM_GMTOFF) + tm.tm_gmtoff = NUM2LONG(vtm->utc_offset); +#endif +#if defined(HAVE_TM_ZONE) + tm.tm_zone = vtm->zone; +#endif + *result = tm; +} + #ifdef POSIX2_DATE /* iso8601wknum --- compute week number according to ISO 8601 */ @@ -935,6 +1012,15 @@ iso8601wknum(const struct tm *timeptr) return weeknum; } + +static int +iso8601wknum_v(const struct vtm *vtm) +{ + struct tm tm; + vtm2tm_noyear(vtm, &tm); + return iso8601wknum(&tm); +} + #endif /* weeknumber --- figure how many weeks into the year */ @@ -966,6 +1052,14 @@ weeknumber(const struct tm *timeptr, int firstweekday) return ret; } +static int +weeknumber_v(const struct vtm *vtm, int firstweekday) +{ + struct tm tm; + vtm2tm_noyear(vtm, &tm); + return weeknumber(&tm, firstweekday); +} + #if 0 /* ADR --- I'm loathe to mess with ado's code ... */ -- cgit v1.2.3