aboutsummaryrefslogtreecommitdiffstats
path: root/strftime.c
diff options
context:
space:
mode:
authorakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-04-21 14:56:59 +0000
committerakr <akr@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2009-04-21 14:56:59 +0000
commit3bc16f62c1fb6225b3f6cb96b62f78e44636aae4 (patch)
tree7861dbce5046671694894b080ca6d0e0d15c6175 /strftime.c
parent143846ad369eba2324e13743ff03bcf50d56c942 (diff)
downloadruby-3bc16f62c1fb6225b3f6cb96b62f78e44636aae4.tar.gz
* 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
Diffstat (limited to 'strftime.c')
-rw-r--r--strftime.c244
1 files changed, 169 insertions, 75 deletions
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 <stdio.h>
@@ -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 ... */