50#include "ruby/internal/config.h"
60#if defined(TM_IN_SYS_TIME) || !defined(GAWK)
69#include "internal/string.h"
70#include "internal/vm.h"
81#define MAILHEADER_EXT 1
84#if defined(ISO_DATE_EXT)
85#if ! defined(POSIX2_DATE)
90#if defined(POSIX2_DATE)
91#if ! defined(SYSV_EXT)
94#if ! defined(SUNOS_EXT)
99#if defined(POSIX2_DATE)
100#define adddecl(stuff) stuff
102#define adddecl(stuff)
107#if !defined __STDC__ && !defined _WIN32
109static int weeknumber();
110adddecl(
static int iso8601wknum();)
111static int weeknumber_v();
112adddecl(
static int iso8601wknum_v();)
114static int weeknumber(
const struct tm *timeptr,
int firstweekday);
115adddecl(
static int iso8601wknum(
const struct tm *timeptr);)
116static int weeknumber_v(
const struct vtm *
vtm,
int firstweekday);
117adddecl(
static int iso8601wknum_v(
const struct vtm *
vtm);)
124extern void *malloc();
125extern void *realloc();
126extern char *getenv();
127extern char *strchr();
130#define range(low, item, hi) max((low), min((item), (hi)))
139 return (a < b ? a : b);
149 return (a > b ? a : b);
152#ifdef NO_STRING_LITERAL_CONCATENATION
153#error No string literal concatenation
156#define add(x,y) (rb_funcall((x), '+', 1, (y)))
157#define sub(x,y) (rb_funcall((x), '-', 1, (y)))
158#define mul(x,y) (rb_funcall((x), '*', 1, (y)))
159#define quo(x,y) (rb_funcall((x), rb_intern("quo"), 1, (y)))
160#define div(x,y) (rb_funcall((x), rb_intern("div"), 1, (y)))
161#define mod(x,y) (rb_funcall((x), '%', 1, (y)))
165enum {LEFT, CHCASE, LOWER, UPPER};
166#define BIT_OF(n) (1U<<(n))
169resize_buffer(VALUE ftime,
char *s,
const char **start,
const char **endp,
170 ptrdiff_t n,
size_t maxsize)
172 size_t len = s - *start;
173 size_t nlen = len + n * 2;
175 if (nlen < len || nlen > maxsize) {
187buffer_size_check(
const char *s,
188 const char *format_end,
size_t format_len,
192 const char *format = format_end-format_len;
199case_conv(
char *s, ptrdiff_t i,
int flags)
201 switch (flags & (BIT_OF(UPPER)|BIT_OF(LOWER))) {
220format_value(VALUE val,
int base)
222 if (!RB_BIGNUM_TYPE_P(val))
223 val = rb_Integer(val);
233rb_strftime_with_timespec(VALUE ftime,
const char *format,
size_t format_len,
235 VALUE timev,
struct timespec *ts,
int gmt,
size_t maxsize)
239 const char *start = s;
241 const char *
const format_end = format + format_len;
244 auto char tbuf[TBUFSIZE];
249 int precision, flags, colons;
257 static const char days_l[][10] = {
258 "Sunday",
"Monday",
"Tuesday",
"Wednesday",
259 "Thursday",
"Friday",
"Saturday",
261 static const char months_l[][10] = {
262 "January",
"February",
"March",
"April",
263 "May",
"June",
"July",
"August",
"September",
264 "October",
"November",
"December",
266 static const char ampm[][3] = {
"AM",
"PM", };
268 if (format == NULL || format_len == 0 ||
vtm == NULL) {
280 for (; format < format_end; format++) {
281#define FLAG_FOUND() do { \
285#define NEEDS(n) do { \
286 if (s >= endp || (n) >= endp - s - 1) { \
287 s = resize_buffer(ftime, s, &start, &endp, (n), maxsize); \
288 buffer_size_check(s, format_end, format_len, enc); \
291#define FILL_PADDING(i) do { \
292 if (!(flags & BIT_OF(LEFT)) && precision > (i)) { \
294 memset(s, padding ? padding : ' ', precision - (i)); \
295 s += precision - (i); \
301#define FMT_PADDING(fmt, def_pad) \
302 (&"%*"fmt"\0""%0*"fmt[\
303 (padding == '0' || (!padding && (def_pad) == '0')) ? \
304 rb_strlen_lit("%*"fmt)+1 : 0])
305#define FMT_PRECISION(def_prec) \
306 ((flags & BIT_OF(LEFT)) ? (1) : \
307 (precision <= 0) ? (def_prec) : (precision))
308#define FMT(def_pad, def_prec, fmt, val) \
310 precision = FMT_PRECISION(def_prec); \
313 rb_str_set_len(ftime, len); \
314 rb_str_catf(ftime, FMT_PADDING(fmt, def_pad), \
316 RSTRING_GETMEM(ftime, s, len); \
317 endp = (start = s) + rb_str_capacity(ftime); \
320#define STRFTIME(fmt) \
323 rb_str_set_len(ftime, len); \
324 if (!rb_strftime_with_timespec(ftime, (fmt), rb_strlen_lit(fmt), \
325 enc, time, vtm, timev, ts, gmt, maxsize)) \
327 s = RSTRING_PTR(ftime); \
328 i = RSTRING_LEN(ftime) - len; \
329 endp = (start = s) + rb_str_capacity(ftime); \
331 if (i > 0) case_conv(s, i, flags); \
332 if (precision > i) {\
336 memmove(s + precision - i, s, i);\
337 memset(s, padding ? padding : ' ', precision - i); \
342#define FMTV(def_pad, def_prec, fmt, val) \
345 if (FIXNUM_P(tmp)) { \
346 FMT((def_pad), (def_prec), "l"fmt, FIX2LONG(tmp)); \
349 const int base = ((fmt[0] == 'x') ? 16 : \
350 (fmt[0] == 'o') ? 8 : \
352 precision = FMT_PRECISION(def_prec); \
353 if (!padding) padding = (def_pad); \
354 tmp = format_value(tmp, base); \
355 i = RSTRING_LEN(tmp); \
357 rb_str_set_len(ftime, s-start); \
358 rb_str_append(ftime, tmp); \
359 RSTRING_GETMEM(ftime, s, len); \
360 endp = (start = s) + rb_str_capacity(ftime); \
365 tp = memchr(format,
'%', format_end - format);
366 if (!tp) tp = format_end;
368 memcpy(s, format, tp - format);
371 if (format == format_end)
break;
380 if (++format >= format_end)
goto unknown;
388 if (flags & BIT_OF(CHCASE)) {
389 flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE));
390 flags |= BIT_OF(UPPER);
395 i = 3, tp = days_l[
vtm->wday];
399 if (flags & BIT_OF(CHCASE)) {
400 flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE));
401 flags |= BIT_OF(UPPER);
406 i = strlen(tp = days_l[
vtm->wday]);
413 if (flags & BIT_OF(CHCASE)) {
414 flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE));
415 flags |= BIT_OF(UPPER);
417 if (
vtm->mon < 1 ||
vtm->mon > 12)
420 i = 3, tp = months_l[
vtm->mon-1];
424 if (flags & BIT_OF(CHCASE)) {
425 flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE));
426 flags |= BIT_OF(UPPER);
428 if (
vtm->mon < 1 ||
vtm->mon > 12)
431 i = strlen(tp = months_l[
vtm->mon-1]);
435 STRFTIME(
"%a %b %e %H:%M:%S %Y");
439 i = range(1,
vtm->mday, 31);
440 FMT(
'0', 2,
"d", (
int)i);
444 i = range(0,
vtm->hour, 23);
445 FMT(
'0', 2,
"d", (
int)i);
449 i = range(0,
vtm->hour, 23);
454 FMT(
'0', 2,
"d", (
int)i);
458 i = range(1,
vtm->yday, 366);
459 FMT(
'0', 3,
"d", (
int)i);
463 i = range(1,
vtm->mon, 12);
464 FMT(
'0', 2,
"d", (
int)i);
468 i = range(0,
vtm->min, 59);
469 FMT(
'0', 2,
"d", (
int)i);
474 if ((*format ==
'p' && (flags & BIT_OF(CHCASE))) ||
475 (*format ==
'P' && !(flags & (BIT_OF(CHCASE)|BIT_OF(UPPER))))) {
476 flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE));
477 flags |= BIT_OF(LOWER);
479 i = range(0,
vtm->hour, 23);
489 time_t sec = ts->tv_sec;
491 FMT(
'0', 1, PRI_TIMET_PREFIX
"d", sec);
493 FMT(
'0', 1, PRI_TIMET_PREFIX
"u", sec);
496 VALUE sec = div(timev,
INT2FIX(1));
497 FMTV(
'0', 1,
"d", sec);
502 i = range(0,
vtm->sec, 60);
503 FMT(
'0', 2,
"d", (
int)i);
507 FMT(
'0', 2,
"d", weeknumber_v(
vtm, 0));
511 i = range(0,
vtm->wday, 6);
512 FMT(
'0', 1,
"d", (
int)i);
516 FMT(
'0', 2,
"d", weeknumber_v(
vtm, 1));
520 STRFTIME(
"%m/%d/%y");
524 STRFTIME(
"%H:%M:%S");
529 FMT(
'0', 2,
"d", (
int)i);
535 FMT(
'0', 0 <= y ? 4 : 5,
"ld", y);
538 FMTV(
'0', 4,
"d",
vtm->year);
550 if (off < 0 || (gmt && (flags & BIT_OF(LEFT)))) {
559 precision = precision <= 5 ? 2 : precision-3;
560 NEEDS(precision + 3);
564 precision = precision <= 6 ? 2 : precision-4;
565 NEEDS(precision + 4);
569 precision = precision <= 9 ? 2 : precision-7;
570 NEEDS(precision + 7);
574 if (off % 3600 == 0) {
575 precision = precision <= 3 ? 2 : precision-1;
576 NEEDS(precision + 3);
578 else if (off % 60 == 0) {
579 precision = precision <= 6 ? 2 : precision-4;
580 NEEDS(precision + 4);
583 precision = precision <= 9 ? 2 : precision-7;
584 NEEDS(precision + 9);
592 i = snprintf(s, endp - s, (padding ==
' ' ?
"%+*ld" :
"%+.*ld"),
593 precision + (padding ==
' '), sign * (off / 3600));
595 if (sign < 0 && off < 3600) {
596 *(padding ==
' ' ? s + i - 2 : s) =
'-';
600 if (colons == 3 && off == 0)
604 i = snprintf(s, endp - s,
"%02d", (
int)(off / 60));
608 if (colons == 3 && off == 0)
612 i = snprintf(s, endp - s,
"%02d", (
int)off);
620 if (flags & BIT_OF(CHCASE)) {
621 flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE));
622 flags |= BIT_OF(LOWER);
634 zone = rb_time_zone_abbreviation(
vtm->zone, time);
638 for (i = 0; i < TBUFSIZE && tp[i]; i++) {
639 if ((
unsigned char)tp[i] > 0x7F) {
664 STRFTIME(
"%m/%d/%y");
668 FMT(
' ', 2,
"d", range(1,
vtm->mday, 31));
672 STRFTIME(
"%I:%M:%S %p");
680 STRFTIME(
"%H:%M:%S");
686 i = range(0,
vtm->hour, 23);
687 FMT(
' ', 2,
"d", (
int)i);
691 i = range(0,
vtm->hour, 23);
696 FMT(
' ', 2,
"d", (
int)i);
703 STRFTIME(
"%e-%^b-%4Y");
710 FMTV(
'0', 2,
"d", div(
vtm->year,
INT2FIX(100)));
715 if (!format[1] || !strchr(
"cCxXyY", format[1]))
720 if (!format[1] || !strchr(
"deHkIlmMSuUVwWy", format[1]))
725 FMT(
'0', 2,
"d", iso8601wknum_v(
vtm));
730 FMT(
'0', 1,
"d",
vtm->wday == 0 ? 7 :
vtm->wday);
747 VALUE yv =
vtm->year;
748 w = iso8601wknum_v(
vtm);
749 if (
vtm->mon == 12 && w == 1)
751 else if (
vtm->mon == 1 && w >= 52)
754 if (*format ==
'G') {
757 FMT(
'0', 0 <= y ? 4 : 5,
"ld", y);
760 FMTV(
'0', 4,
"d", yv);
766 FMT(
'0', 2,
"ld", y);
789 if (precision <= 0) {
795 long subsec = ts->tv_nsec;
797 snprintf(s, endp - s,
"%09ld", subsec);
798 memset(s+9,
'0', precision-9);
803 for (i = 0; i < 9-precision; i++)
805 snprintf(s, endp - s,
"%0*ld", precision, subsec);
810 VALUE subsec = mod(timev,
INT2FIX(1));
816 subsec = mul(subsec,
INT2FIX(1000000000));
823 subsec = mul(subsec,
INT2FIX(n));
824 subsec = div(subsec,
INT2FIX(1));
827 (void)snprintf(s, endp - s,
"%0*ld", precision,
FIX2LONG(subsec));
831 VALUE args[2], result;
835 rb_fstring_lit(
"%0*d"));
843 STRFTIME(
"%Y-%m-%d");
848 flags |= BIT_OF(LEFT);
849 padding = precision = 0;
854 flags |= BIT_OF(UPPER);
859 flags |= BIT_OF(CHCASE);
868 for (colons = 1; colons <= 3; ++colons) {
869 if (format+colons >= format_end)
goto unknown;
870 if (format[colons] ==
'z')
break;
871 if (format[colons] !=
':')
goto unknown;
873 format += colons - 1;
878 case '1':
case '2':
case '3':
case '4':
879 case '5':
case '6':
case '7':
case '8':
case '9':
884 if (ov || u > INT_MAX)
goto unknown;
903 s = case_conv(s, i, flags);
906 if (format != format_end) {
919strftime_size_limit(
size_t format_len)
921 size_t limit = format_len * (1*1024*1024);
922 if (limit < format_len) limit = format_len;
923 else if (limit < 1024) limit = 1024;
928rb_strftime(
const char *format,
size_t format_len,
rb_encoding *enc,
929 VALUE time,
const struct vtm *
vtm, VALUE timev,
int gmt)
932 return rb_strftime_with_timespec(result, format, format_len, enc,
933 time,
vtm, timev, NULL, gmt,
934 strftime_size_limit(format_len));
938rb_strftime_timespec(
const char *format,
size_t format_len,
rb_encoding *enc,
942 return rb_strftime_with_timespec(result, format, format_len, enc,
944 strftime_size_limit(format_len));
949rb_strftime_limit(
const char *format,
size_t format_len,
rb_encoding *enc,
951 int gmt,
size_t maxsize)
954 return rb_strftime_with_timespec(result, format, format_len, enc,
955 time,
vtm,
Qnil, ts, gmt, maxsize);
964 return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
969vtm2tm_noyear(
const struct vtm *
vtm,
struct tm *result)
976 tm.tm_mon =
vtm->mon-1;
977 tm.tm_mday =
vtm->mday;
978 tm.tm_hour =
vtm->hour;
979 tm.tm_min =
vtm->min;
980 tm.tm_sec =
vtm->sec;
981 tm.tm_wday =
vtm->wday;
982 tm.tm_yday =
vtm->yday-1;
983 tm.tm_isdst =
vtm->isdst;
984#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
987#if defined(HAVE_TM_ZONE)
988 tm.tm_zone = (
char *)
vtm->zone;
997iso8601wknum(
const struct tm *timeptr)
1013 int weeknum, jan1day;
1016 weeknum = weeknumber(timeptr, 1);
1031 jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
1060#ifdef USE_BROKEN_XPG4
1068 dec31ly.tm_mon = 11;
1069 dec31ly.tm_mday = 31;
1070 dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
1071 dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900L);
1072 weeknum = iso8601wknum(& dec31ly);
1078 if (timeptr->tm_mon == 11) {
1092 wday = timeptr->tm_wday;
1093 mday = timeptr->tm_mday;
1094 if ( (wday == 1 && (mday >= 29 && mday <= 31))
1095 || (wday == 2 && (mday == 30 || mday == 31))
1096 || (wday == 3 && mday == 31))
1104iso8601wknum_v(
const struct vtm *
vtm)
1107 vtm2tm_noyear(
vtm, &
tm);
1108 return iso8601wknum(&
tm);
1118weeknumber(
const struct tm *timeptr,
int firstweekday)
1120 int wday = timeptr->tm_wday;
1123 if (firstweekday == 1) {
1129 ret = ((timeptr->tm_yday + 7 - wday) / 7);
1136weeknumber_v(
const struct vtm *
vtm,
int firstweekday)
1139 vtm2tm_noyear(
vtm, &
tm);
1140 return weeknumber(&
tm, firstweekday);
1146Date: Wed, 24 Apr 91 20:54:08 MDT
1147From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK>
1148To: arnold@audiofax.com
1151in a process of fixing of strftime() in libraries on Atari ST I grabbed
1152some pieces of code from your own strftime. When doing that it came
1153to mind that your weeknumber() function compiles a little bit nicer
1154in the following form:
1159 return (timeptr->tm_yday - timeptr->tm_wday +
1160 (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
1162How nicer it depends on a compiler, of course, but always a tiny bit.
1166 ntomczak@vm.ucs.ualberta.ca
1201#include <sys/time.h>
1210static char *array[] =
1212 "(%%A) full weekday name, var length (Sunday..Saturday) %A",
1213 "(%%B) full month name, var length (January..December) %B",
1215 "(%%D) date (%%m/%%d/%%y) %D",
1216 "(%%E) Locale extensions (ignored) %E",
1217 "(%%H) hour (24-hour clock, 00..23) %H",
1218 "(%%I) hour (12-hour clock, 01..12) %I",
1219 "(%%M) minute (00..59) %M",
1220 "(%%O) Locale extensions (ignored) %O",
1221 "(%%R) time, 24-hour (%%H:%%M) %R",
1222 "(%%S) second (00..60) %S",
1223 "(%%T) time, 24-hour (%%H:%%M:%%S) %T",
1224 "(%%U) week of year, Sunday as first day of week (00..53) %U",
1225 "(%%V) week of year according to ISO 8601 %V",
1226 "(%%W) week of year, Monday as first day of week (00..53) %W",
1227 "(%%X) appropriate locale time representation (%H:%M:%S) %X",
1228 "(%%Y) year with century (1970...) %Y",
1229 "(%%Z) timezone (EDT), or blank if timezone not determinable %Z",
1230 "(%%a) locale's abbreviated weekday name (Sun..Sat) %a",
1231 "(%%b) locale's abbreviated month name (Jan..Dec) %b",
1232 "(%%c) full date (Sat Nov 4 12:02:33 1989)%n%t%t%t %c",
1233 "(%%d) day of the month (01..31) %d",
1234 "(%%e) day of the month, blank-padded ( 1..31) %e",
1235 "(%%h) should be same as (%%b) %h",
1236 "(%%j) day of the year (001..366) %j",
1237 "(%%k) hour, 24-hour clock, blank pad ( 0..23) %k",
1238 "(%%l) hour, 12-hour clock, blank pad ( 1..12) %l",
1239 "(%%m) month (01..12) %m",
1240 "(%%p) locale's AM or PM based on 12-hour clock %p",
1241 "(%%r) time, 12-hour (same as %%I:%%M:%%S %%p) %r",
1242 "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7] %u",
1243 "(%%v) VMS date (dd-bbb-YYYY) %v",
1244 "(%%w) day of week (0..6, Sunday == 0) %w",
1245 "(%%x) appropriate locale date representation %x",
1246 "(%%y) last two digits of year (00..99) %y",
1247 "(%%z) timezone offset east of GMT as HHMM (e.g. -0500) %z",
1254main(
int argc,
char **argv)
1259 char string[MAXTIME];
1270 clock = time((
long *) 0);
1271 tm = localtime(&clock);
1273 for (k = 0; next = array[k]; k++) {
1274 length = strftime(
string, MAXTIME, next,
tm);
1275 printf(
"%s\n",
string);
#define INT2FIX
Old name of RB_INT2FIX.
#define ISUPPER
Old name of rb_isupper.
#define ECONV_UNDEF_REPLACE
Old name of RUBY_ECONV_UNDEF_REPLACE.
#define FIX2INT
Old name of RB_FIX2INT.
#define ECONV_INVALID_REPLACE
Old name of RUBY_ECONV_INVALID_REPLACE.
#define TOUPPER
Old name of rb_toupper.
#define ISLOWER
Old name of rb_islower.
#define TOLOWER
Old name of rb_tolower.
#define NUM2INT
Old name of RB_NUM2INT.
#define Qnil
Old name of RUBY_Qnil.
#define FIX2LONG
Old name of RB_FIX2LONG.
#define NIL_P
Old name of RB_NIL_P.
#define NUM2LONG
Old name of RB_NUM2LONG.
#define FIXNUM_P
Old name of RB_FIXNUM_P.
void rb_syserr_fail_str(int e, VALUE mesg)
Identical to rb_syserr_fail(), except it takes the message in Ruby's String instead of C's.
rb_encoding * rb_ascii8bit_encoding(void)
Queries the encoding that represents ASCII-8BIT a.k.a.
rb_encoding * rb_locale_encoding(void)
Queries the encoding that represents the current locale.
rb_encoding * rb_usascii_encoding(void)
Queries the encoding that represents US-ASCII.
VALUE rb_str_conv_enc_opts(VALUE str, rb_encoding *from, rb_encoding *to, int ecflags, VALUE ecopts)
Identical to rb_str_conv_enc(), except it additionally takes IO encoder options.
VALUE rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)
Identical to rb_enc_str_new(), except it additionally takes an encoding.
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
VALUE rb_big2str(VALUE x, int base)
Generates a place-value representation of the passed integer.
size_t rb_str_capacity(VALUE str)
Queries the capacity of the given string.
void rb_str_set_len(VALUE str, long len)
Overwrites the length of the string.
VALUE rb_str_new_cstr(const char *ptr)
Identical to rb_str_new(), except it assumes the passed pointer is a pointer to a C string.
VALUE rb_str_resize(VALUE str, long len)
Overwrites the length of the string.
void rb_str_modify_expand(VALUE str, long capa)
Identical to rb_str_modify(), except it additionally expands the capacity of the receiver.
ID rb_intern(const char *name)
Finds or creates a symbol of the given name.
unsigned long ruby_scan_digits(const char *str, ssize_t len, int base, size_t *retlen, int *overflow)
Scans the passed string, assuming the string is a textual representation of an integer.
VALUE rb_str_format(int argc, const VALUE *argv, VALUE fmt)
Formats a string.
static long RSTRING_LEN(VALUE str)
Queries the length of the string.
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...