Ruby 1.9.3p327(2012-11-10revision37606)
|
00001 /* -*- c-file-style: "linux" -*- */ 00002 00003 /* 00004 * strftime.c 00005 * 00006 * Public-domain implementation of ANSI C library routine. 00007 * 00008 * It's written in old-style C for maximal portability. 00009 * However, since I'm used to prototypes, I've included them too. 00010 * 00011 * If you want stuff in the System V ascftime routine, add the SYSV_EXT define. 00012 * For extensions from SunOS, add SUNOS_EXT. 00013 * For stuff needed to implement the P1003.2 date command, add POSIX2_DATE. 00014 * For VMS dates, add VMS_EXT. 00015 * For a an RFC822 time format, add MAILHEADER_EXT. 00016 * For ISO week years, add ISO_DATE_EXT. 00017 * For complete POSIX semantics, add POSIX_SEMANTICS. 00018 * 00019 * The code for %c, %x, and %X now follows the 1003.2 specification for 00020 * the POSIX locale. 00021 * This version ignores LOCALE information. 00022 * It also doesn't worry about multi-byte characters. 00023 * So there. 00024 * 00025 * This file is also shipped with GAWK (GNU Awk), gawk specific bits of 00026 * code are included if GAWK is defined. 00027 * 00028 * Arnold Robbins 00029 * January, February, March, 1991 00030 * Updated March, April 1992 00031 * Updated April, 1993 00032 * Updated February, 1994 00033 * Updated May, 1994 00034 * Updated January, 1995 00035 * Updated September, 1995 00036 * Updated January, 1996 00037 * 00038 * Fixes from ado@elsie.nci.nih.gov 00039 * February 1991, May 1992 00040 * Fixes from Tor Lillqvist tml@tik.vtt.fi 00041 * May, 1993 00042 * Further fixes from ado@elsie.nci.nih.gov 00043 * February 1994 00044 * %z code from chip@chinacat.unicom.com 00045 * Applied September 1995 00046 * %V code fixed (again) and %G, %g added, 00047 * January 1996 00048 */ 00049 00050 #include "ruby/ruby.h" 00051 #include "timev.h" 00052 00053 #ifndef GAWK 00054 #include <stdio.h> 00055 #include <ctype.h> 00056 #include <string.h> 00057 #include <time.h> 00058 #include <sys/types.h> 00059 #include <errno.h> 00060 #endif 00061 #if defined(TM_IN_SYS_TIME) || !defined(GAWK) 00062 #include <sys/types.h> 00063 #if HAVE_SYS_TIME_H 00064 #include <sys/time.h> 00065 #endif 00066 #endif 00067 #include <math.h> 00068 00069 /* defaults: season to taste */ 00070 #define SYSV_EXT 1 /* stuff in System V ascftime routine */ 00071 #define SUNOS_EXT 1 /* stuff in SunOS strftime routine */ 00072 #define POSIX2_DATE 1 /* stuff in Posix 1003.2 date command */ 00073 #define VMS_EXT 1 /* include %v for VMS date format */ 00074 #define MAILHEADER_EXT 1 /* add %z for HHMM format */ 00075 #define ISO_DATE_EXT 1 /* %G and %g for year of ISO week */ 00076 00077 #if defined(ISO_DATE_EXT) 00078 #if ! defined(POSIX2_DATE) 00079 #define POSIX2_DATE 1 00080 #endif 00081 #endif 00082 00083 #if defined(POSIX2_DATE) 00084 #if ! defined(SYSV_EXT) 00085 #define SYSV_EXT 1 00086 #endif 00087 #if ! defined(SUNOS_EXT) 00088 #define SUNOS_EXT 1 00089 #endif 00090 #endif 00091 00092 #if defined(POSIX2_DATE) 00093 #define adddecl(stuff) stuff 00094 #else 00095 #define adddecl(stuff) 00096 #endif 00097 00098 #undef strchr /* avoid AIX weirdness */ 00099 00100 #if !defined __STDC__ && !defined _WIN32 00101 #define const 00102 static int weeknumber(); 00103 adddecl(static int iso8601wknum();) 00104 static int weeknumber_v(); 00105 adddecl(static int iso8601wknum_v();) 00106 #else 00107 static int weeknumber(const struct tm *timeptr, int firstweekday); 00108 adddecl(static int iso8601wknum(const struct tm *timeptr);) 00109 static int weeknumber_v(const struct vtm *vtm, int firstweekday); 00110 adddecl(static int iso8601wknum_v(const struct vtm *vtm);) 00111 #endif 00112 00113 #ifdef STDC_HEADERS 00114 #include <stdlib.h> 00115 #include <string.h> 00116 #else 00117 extern void *malloc(); 00118 extern void *realloc(); 00119 extern char *getenv(); 00120 extern char *strchr(); 00121 #endif 00122 00123 #define range(low, item, hi) max((low), min((item), (hi))) 00124 00125 #undef min /* just in case */ 00126 00127 /* min --- return minimum of two numbers */ 00128 00129 #ifndef __STDC__ 00130 static inline int 00131 min(a, b) 00132 int a, b; 00133 #else 00134 static inline int 00135 min(int a, int b) 00136 #endif 00137 { 00138 return (a < b ? a : b); 00139 } 00140 00141 #undef max /* also, just in case */ 00142 00143 /* max --- return maximum of two numbers */ 00144 00145 #ifndef __STDC__ 00146 static inline int 00147 max(a, b) 00148 int a, b; 00149 #else 00150 static inline int 00151 max(int a, int b) 00152 #endif 00153 { 00154 return (a > b ? a : b); 00155 } 00156 00157 #ifdef NO_STRING_LITERAL_CONCATENATION 00158 #error No string literal concatenation 00159 #endif 00160 00161 #define add(x,y) (rb_funcall((x), '+', 1, (y))) 00162 #define sub(x,y) (rb_funcall((x), '-', 1, (y))) 00163 #define mul(x,y) (rb_funcall((x), '*', 1, (y))) 00164 #define quo(x,y) (rb_funcall((x), rb_intern("quo"), 1, (y))) 00165 #define div(x,y) (rb_funcall((x), rb_intern("div"), 1, (y))) 00166 #define mod(x,y) (rb_funcall((x), '%', 1, (y))) 00167 00168 /* strftime --- produce formatted time */ 00169 00170 static size_t 00171 rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, const struct vtm *vtm, VALUE timev, struct timespec *ts, int gmt) 00172 { 00173 const char *const endp = s + maxsize; 00174 const char *const start = s; 00175 const char *sp, *tp; 00176 auto char tbuf[100]; 00177 long off; 00178 ptrdiff_t i; 00179 int w; 00180 long y; 00181 int precision, flags, colons; 00182 char padding; 00183 enum {LEFT, CHCASE, LOWER, UPPER, LOCALE_O, LOCALE_E}; 00184 #define BIT_OF(n) (1U<<(n)) 00185 #ifdef MAILHEADER_EXT 00186 int sign; 00187 #endif 00188 00189 /* various tables, useful in North America */ 00190 static const char days_l[][10] = { 00191 "Sunday", "Monday", "Tuesday", "Wednesday", 00192 "Thursday", "Friday", "Saturday", 00193 }; 00194 static const char months_l[][10] = { 00195 "January", "February", "March", "April", 00196 "May", "June", "July", "August", "September", 00197 "October", "November", "December", 00198 }; 00199 static const char ampm[][3] = { "AM", "PM", }; 00200 00201 if (s == NULL || format == NULL || vtm == NULL || maxsize == 0) 00202 return 0; 00203 00204 /* quick check if we even need to bother */ 00205 if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) { 00206 err: 00207 errno = ERANGE; 00208 return 0; 00209 } 00210 00211 for (; *format && s < endp - 1; format++) { 00212 #define FLAG_FOUND() do { \ 00213 if (precision > 0 || flags & (BIT_OF(LOCALE_E)|BIT_OF(LOCALE_O))) \ 00214 goto unknown; \ 00215 } while (0) 00216 #define NEEDS(n) do if (s >= endp || (n) >= endp - s - 1) goto err; while (0) 00217 #define FILL_PADDING(i) do { \ 00218 if (!(flags & BIT_OF(LEFT)) && precision > (i)) { \ 00219 NEEDS(precision); \ 00220 memset(s, padding ? padding : ' ', precision - (i)); \ 00221 s += precision - (i); \ 00222 } \ 00223 else { \ 00224 NEEDS(i); \ 00225 } \ 00226 } while (0); 00227 #define FMT(def_pad, def_prec, fmt, val) \ 00228 do { \ 00229 int l; \ 00230 if (precision <= 0) precision = (def_prec); \ 00231 if (flags & BIT_OF(LEFT)) precision = 1; \ 00232 l = snprintf(s, endp - s, \ 00233 ((padding == '0' || (!padding && (def_pad) == '0')) ? "%0*"fmt : "%*"fmt), \ 00234 precision, (val)); \ 00235 if (l < 0) goto err; \ 00236 s += l; \ 00237 } while (0) 00238 #define STRFTIME(fmt) \ 00239 do { \ 00240 i = rb_strftime_with_timespec(s, endp - s, (fmt), vtm, timev, ts, gmt); \ 00241 if (!i) return 0; \ 00242 if (precision > i) {\ 00243 NEEDS(precision); \ 00244 memmove(s + precision - i, s, i);\ 00245 memset(s, padding ? padding : ' ', precision - i); \ 00246 s += precision; \ 00247 }\ 00248 else s += i; \ 00249 } while (0) 00250 #define FMTV(def_pad, def_prec, fmt, val) \ 00251 do { \ 00252 VALUE tmp = (val); \ 00253 if (FIXNUM_P(tmp)) { \ 00254 FMT((def_pad), (def_prec), "l"fmt, FIX2LONG(tmp)); \ 00255 } \ 00256 else { \ 00257 VALUE args[2], result; \ 00258 size_t l; \ 00259 if (precision <= 0) precision = (def_prec); \ 00260 if (flags & BIT_OF(LEFT)) precision = 1; \ 00261 args[0] = INT2FIX(precision); \ 00262 args[1] = (val); \ 00263 if (padding == '0' || (!padding && (def_pad) == '0')) \ 00264 result = rb_str_format(2, args, rb_str_new2("%0*"fmt)); \ 00265 else \ 00266 result = rb_str_format(2, args, rb_str_new2("%*"fmt)); \ 00267 l = strlcpy(s, StringValueCStr(result), endp-s); \ 00268 if ((size_t)(endp-s) <= l) \ 00269 goto err; \ 00270 s += l; \ 00271 } \ 00272 } while (0) 00273 00274 if (*format != '%') { 00275 *s++ = *format; 00276 continue; 00277 } 00278 tp = tbuf; 00279 sp = format; 00280 precision = -1; 00281 flags = 0; 00282 padding = 0; 00283 colons = 0; 00284 again: 00285 switch (*++format) { 00286 case '\0': 00287 format--; 00288 goto unknown; 00289 00290 case '%': 00291 FILL_PADDING(1); 00292 *s++ = '%'; 00293 continue; 00294 00295 case 'a': /* abbreviated weekday name */ 00296 if (flags & BIT_OF(CHCASE)) { 00297 flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE)); 00298 flags |= BIT_OF(UPPER); 00299 } 00300 if (vtm->wday < 0 || vtm->wday > 6) 00301 i = 1, tp = "?"; 00302 else 00303 i = 3, tp = days_l[vtm->wday]; 00304 break; 00305 00306 case 'A': /* full weekday name */ 00307 if (flags & BIT_OF(CHCASE)) { 00308 flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE)); 00309 flags |= BIT_OF(UPPER); 00310 } 00311 if (vtm->wday < 0 || vtm->wday > 6) 00312 i = 1, tp = "?"; 00313 else 00314 i = strlen(tp = days_l[vtm->wday]); 00315 break; 00316 00317 #ifdef SYSV_EXT 00318 case 'h': /* abbreviated month name */ 00319 #endif 00320 case 'b': /* abbreviated month name */ 00321 if (flags & BIT_OF(CHCASE)) { 00322 flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE)); 00323 flags |= BIT_OF(UPPER); 00324 } 00325 if (vtm->mon < 1 || vtm->mon > 12) 00326 i = 1, tp = "?"; 00327 else 00328 i = 3, tp = months_l[vtm->mon-1]; 00329 break; 00330 00331 case 'B': /* full month name */ 00332 if (flags & BIT_OF(CHCASE)) { 00333 flags &= ~(BIT_OF(LOWER)|BIT_OF(CHCASE)); 00334 flags |= BIT_OF(UPPER); 00335 } 00336 if (vtm->mon < 1 || vtm->mon > 12) 00337 i = 1, tp = "?"; 00338 else 00339 i = strlen(tp = months_l[vtm->mon-1]); 00340 break; 00341 00342 case 'c': /* appropriate date and time representation */ 00343 STRFTIME("%a %b %e %H:%M:%S %Y"); 00344 continue; 00345 00346 case 'd': /* day of the month, 01 - 31 */ 00347 i = range(1, vtm->mday, 31); 00348 FMT('0', 2, "d", (int)i); 00349 continue; 00350 00351 case 'H': /* hour, 24-hour clock, 00 - 23 */ 00352 i = range(0, vtm->hour, 23); 00353 FMT('0', 2, "d", (int)i); 00354 continue; 00355 00356 case 'I': /* hour, 12-hour clock, 01 - 12 */ 00357 i = range(0, vtm->hour, 23); 00358 if (i == 0) 00359 i = 12; 00360 else if (i > 12) 00361 i -= 12; 00362 FMT('0', 2, "d", (int)i); 00363 continue; 00364 00365 case 'j': /* day of the year, 001 - 366 */ 00366 FMT('0', 3, "d", vtm->yday); 00367 continue; 00368 00369 case 'm': /* month, 01 - 12 */ 00370 i = range(1, vtm->mon, 12); 00371 FMT('0', 2, "d", (int)i); 00372 continue; 00373 00374 case 'M': /* minute, 00 - 59 */ 00375 i = range(0, vtm->min, 59); 00376 FMT('0', 2, "d", (int)i); 00377 continue; 00378 00379 case 'p': /* AM or PM based on 12-hour clock */ 00380 case 'P': /* am or pm based on 12-hour clock */ 00381 if ((*format == 'p' && (flags & BIT_OF(CHCASE))) || 00382 (*format == 'P' && !(flags & (BIT_OF(CHCASE)|BIT_OF(UPPER))))) { 00383 flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE)); 00384 flags |= BIT_OF(LOWER); 00385 } 00386 i = range(0, vtm->hour, 23); 00387 if (i < 12) 00388 tp = ampm[0]; 00389 else 00390 tp = ampm[1]; 00391 i = 2; 00392 break; 00393 00394 case 's': 00395 if (ts) { 00396 time_t sec = ts->tv_sec; 00397 if (~(time_t)0 <= 0) 00398 FMT('0', 1, PRI_TIMET_PREFIX"d", sec); 00399 else 00400 FMT('0', 1, PRI_TIMET_PREFIX"u", sec); 00401 } 00402 else { 00403 VALUE sec = div(timev, INT2FIX(1)); 00404 FMTV('0', 1, "d", sec); 00405 } 00406 continue; 00407 00408 case 'S': /* second, 00 - 60 */ 00409 i = range(0, vtm->sec, 60); 00410 FMT('0', 2, "d", (int)i); 00411 continue; 00412 00413 case 'U': /* week of year, Sunday is first day of week */ 00414 FMT('0', 2, "d", weeknumber_v(vtm, 0)); 00415 continue; 00416 00417 case 'w': /* weekday, Sunday == 0, 0 - 6 */ 00418 i = range(0, vtm->wday, 6); 00419 FMT('0', 1, "d", (int)i); 00420 continue; 00421 00422 case 'W': /* week of year, Monday is first day of week */ 00423 FMT('0', 2, "d", weeknumber_v(vtm, 1)); 00424 continue; 00425 00426 case 'x': /* appropriate date representation */ 00427 STRFTIME("%m/%d/%y"); 00428 continue; 00429 00430 case 'X': /* appropriate time representation */ 00431 STRFTIME("%H:%M:%S"); 00432 continue; 00433 00434 case 'y': /* year without a century, 00 - 99 */ 00435 i = NUM2INT(mod(vtm->year, INT2FIX(100))); 00436 FMT('0', 2, "d", (int)i); 00437 continue; 00438 00439 case 'Y': /* year with century */ 00440 if (FIXNUM_P(vtm->year)) { 00441 long y = FIX2LONG(vtm->year); 00442 FMT('0', 0 <= y ? 4 : 5, "ld", y); 00443 } 00444 else { 00445 FMTV('0', 4, "d", vtm->year); 00446 } 00447 continue; 00448 00449 #ifdef MAILHEADER_EXT 00450 case 'z': /* time zone offset east of GMT e.g. -0600 */ 00451 switch (colons) { 00452 case 0: /* %z -> +hhmm */ 00453 precision = precision <= 5 ? 2 : precision-3; 00454 NEEDS(precision + 3); 00455 break; 00456 00457 case 1: /* %:z -> +hh:mm */ 00458 precision = precision <= 6 ? 2 : precision-4; 00459 NEEDS(precision + 4); 00460 break; 00461 00462 case 2: /* %::z -> +hh:mm:ss */ 00463 precision = precision <= 9 ? 2 : precision-7; 00464 NEEDS(precision + 7); 00465 break; 00466 00467 default: 00468 format--; 00469 goto unknown; 00470 } 00471 if (gmt) { 00472 off = 0; 00473 } 00474 else { 00475 off = NUM2LONG(rb_funcall(vtm->utc_offset, rb_intern("round"), 0)); 00476 } 00477 if (off < 0) { 00478 off = -off; 00479 sign = -1; 00480 } else { 00481 sign = +1; 00482 } 00483 i = snprintf(s, endp - s, (padding == ' ' ? "%+*ld" : "%+.*ld"), 00484 precision + 1, sign * (off / 3600)); 00485 if (i < 0) goto err; 00486 if (sign < 0 && off < 3600) { 00487 *(padding == ' ' ? s + i - 2 : s) = '-'; 00488 } 00489 s += i; 00490 off = off % 3600; 00491 if (1 <= colons) 00492 *s++ = ':'; 00493 i = snprintf(s, endp - s, "%02d", (int)(off / 60)); 00494 if (i < 0) goto err; 00495 s += i; 00496 off = off % 60; 00497 if (2 <= colons) { 00498 *s++ = ':'; 00499 i = snprintf(s, endp - s, "%02d", (int)off); 00500 if (i < 0) goto err; 00501 s += i; 00502 } 00503 continue; 00504 #endif /* MAILHEADER_EXT */ 00505 00506 case 'Z': /* time zone name or abbreviation */ 00507 if (flags & BIT_OF(CHCASE)) { 00508 flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE)); 00509 flags |= BIT_OF(LOWER); 00510 } 00511 if (gmt) { 00512 i = 3; 00513 tp = "UTC"; 00514 break; 00515 } 00516 if (vtm->zone == NULL) 00517 tp = ""; 00518 else 00519 tp = vtm->zone; 00520 i = strlen(tp); 00521 break; 00522 00523 #ifdef SYSV_EXT 00524 case 'n': /* same as \n */ 00525 FILL_PADDING(1); 00526 *s++ = '\n'; 00527 continue; 00528 00529 case 't': /* same as \t */ 00530 FILL_PADDING(1); 00531 *s++ = '\t'; 00532 continue; 00533 00534 case 'D': /* date as %m/%d/%y */ 00535 STRFTIME("%m/%d/%y"); 00536 continue; 00537 00538 case 'e': /* day of month, blank padded */ 00539 FMT(' ', 2, "d", range(1, vtm->mday, 31)); 00540 continue; 00541 00542 case 'r': /* time as %I:%M:%S %p */ 00543 STRFTIME("%I:%M:%S %p"); 00544 continue; 00545 00546 case 'R': /* time as %H:%M */ 00547 STRFTIME("%H:%M"); 00548 continue; 00549 00550 case 'T': /* time as %H:%M:%S */ 00551 STRFTIME("%H:%M:%S"); 00552 continue; 00553 #endif 00554 00555 #ifdef SUNOS_EXT 00556 case 'k': /* hour, 24-hour clock, blank pad */ 00557 i = range(0, vtm->hour, 23); 00558 FMT(' ', 2, "d", (int)i); 00559 continue; 00560 00561 case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */ 00562 i = range(0, vtm->hour, 23); 00563 if (i == 0) 00564 i = 12; 00565 else if (i > 12) 00566 i -= 12; 00567 FMT(' ', 2, "d", (int)i); 00568 continue; 00569 #endif 00570 00571 00572 #ifdef VMS_EXT 00573 case 'v': /* date as dd-bbb-YYYY */ 00574 STRFTIME("%e-%^b-%4Y"); 00575 continue; 00576 #endif 00577 00578 00579 #ifdef POSIX2_DATE 00580 case 'C': 00581 FMTV('0', 2, "d", div(vtm->year, INT2FIX(100))); 00582 continue; 00583 00584 case 'E': 00585 /* POSIX locale extensions, ignored for now */ 00586 flags |= BIT_OF(LOCALE_E); 00587 goto again; 00588 case 'O': 00589 /* POSIX locale extensions, ignored for now */ 00590 flags |= BIT_OF(LOCALE_O); 00591 goto again; 00592 00593 case 'V': /* week of year according ISO 8601 */ 00594 FMT('0', 2, "d", iso8601wknum_v(vtm)); 00595 continue; 00596 00597 case 'u': 00598 /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */ 00599 FMT('0', 1, "d", vtm->wday == 0 ? 7 : vtm->wday); 00600 continue; 00601 #endif /* POSIX2_DATE */ 00602 00603 #ifdef ISO_DATE_EXT 00604 case 'G': 00605 case 'g': 00606 /* 00607 * Year of ISO week. 00608 * 00609 * If it's December but the ISO week number is one, 00610 * that week is in next year. 00611 * If it's January but the ISO week number is 52 or 00612 * 53, that week is in last year. 00613 * Otherwise, it's this year. 00614 */ 00615 { 00616 VALUE yv = vtm->year; 00617 w = iso8601wknum_v(vtm); 00618 if (vtm->mon == 12 && w == 1) 00619 yv = add(yv, INT2FIX(1)); 00620 else if (vtm->mon == 1 && w >= 52) 00621 yv = sub(yv, INT2FIX(1)); 00622 00623 if (*format == 'G') { 00624 if (FIXNUM_P(yv)) { 00625 const long y = FIX2LONG(yv); 00626 FMT('0', 0 <= y ? 4 : 5, "ld", y); 00627 } 00628 else { 00629 FMTV('0', 4, "d", yv); 00630 } 00631 } 00632 else { 00633 yv = mod(yv, INT2FIX(100)); 00634 y = FIX2LONG(yv); 00635 FMT('0', 2, "ld", y); 00636 } 00637 continue; 00638 } 00639 00640 #endif /* ISO_DATE_EXT */ 00641 00642 00643 case 'L': 00644 w = 3; 00645 goto subsec; 00646 00647 case 'N': 00648 /* 00649 * fractional second digits. default is 9 digits 00650 * (nanosecond). 00651 * 00652 * %3N millisecond (3 digits) 00653 * %6N microsecond (6 digits) 00654 * %9N nanosecond (9 digits) 00655 */ 00656 w = 9; 00657 subsec: 00658 if (precision <= 0) { 00659 precision = w; 00660 } 00661 NEEDS(precision); 00662 00663 if (ts) { 00664 long subsec = ts->tv_nsec; 00665 if (9 < precision) { 00666 snprintf(s, endp - s, "%09ld", subsec); 00667 memset(s+9, '0', precision-9); 00668 s += precision; 00669 } 00670 else { 00671 int i; 00672 for (i = 0; i < 9-precision; i++) 00673 subsec /= 10; 00674 snprintf(s, endp - s, "%0*ld", precision, subsec); 00675 s += precision; 00676 } 00677 } 00678 else { 00679 VALUE subsec = mod(timev, INT2FIX(1)); 00680 int ww; 00681 long n; 00682 00683 ww = precision; 00684 while (9 <= ww) { 00685 subsec = mul(subsec, INT2FIX(1000000000)); 00686 ww -= 9; 00687 } 00688 n = 1; 00689 for (; 0 < ww; ww--) 00690 n *= 10; 00691 if (n != 1) 00692 subsec = mul(subsec, INT2FIX(n)); 00693 subsec = div(subsec, INT2FIX(1)); 00694 00695 if (FIXNUM_P(subsec)) { 00696 (void)snprintf(s, endp - s, "%0*ld", precision, FIX2LONG(subsec)); 00697 s += precision; 00698 } 00699 else { 00700 VALUE args[2], result; 00701 args[0] = INT2FIX(precision); 00702 args[1] = subsec; 00703 result = rb_str_format(2, args, rb_str_new2("%0*d")); 00704 (void)strlcpy(s, StringValueCStr(result), endp-s); 00705 s += precision; 00706 } 00707 } 00708 continue; 00709 00710 case 'F': /* Equivalent to %Y-%m-%d */ 00711 STRFTIME("%Y-%m-%d"); 00712 continue; 00713 00714 case '-': 00715 FLAG_FOUND(); 00716 flags |= BIT_OF(LEFT); 00717 padding = precision = 0; 00718 goto again; 00719 00720 case '^': 00721 FLAG_FOUND(); 00722 flags |= BIT_OF(UPPER); 00723 goto again; 00724 00725 case '#': 00726 FLAG_FOUND(); 00727 flags |= BIT_OF(CHCASE); 00728 goto again; 00729 00730 case '_': 00731 FLAG_FOUND(); 00732 padding = ' '; 00733 goto again; 00734 00735 case ':': 00736 FLAG_FOUND(); 00737 colons++; 00738 goto again; 00739 00740 case '0': 00741 padding = '0'; 00742 case '1': case '2': case '3': case '4': 00743 case '5': case '6': case '7': case '8': case '9': 00744 { 00745 char *e; 00746 precision = (int)strtoul(format, &e, 10); 00747 format = e - 1; 00748 goto again; 00749 } 00750 00751 default: 00752 unknown: 00753 i = format - sp + 1; 00754 tp = sp; 00755 precision = -1; 00756 flags = 0; 00757 padding = 0; 00758 colons = 0; 00759 break; 00760 } 00761 if (i) { 00762 FILL_PADDING(i); 00763 memcpy(s, tp, i); 00764 switch (flags & (BIT_OF(UPPER)|BIT_OF(LOWER))) { 00765 case BIT_OF(UPPER): 00766 do { 00767 if (ISLOWER(*s)) *s = TOUPPER(*s); 00768 } while (s++, --i); 00769 break; 00770 case BIT_OF(LOWER): 00771 do { 00772 if (ISUPPER(*s)) *s = TOLOWER(*s); 00773 } while (s++, --i); 00774 break; 00775 default: 00776 s += i; 00777 break; 00778 } 00779 } 00780 } 00781 if (s >= endp) { 00782 goto err; 00783 } 00784 if (*format == '\0') { 00785 *s = '\0'; 00786 return (s - start); 00787 } else 00788 return 0; 00789 } 00790 00791 size_t 00792 rb_strftime(char *s, size_t maxsize, const char *format, const struct vtm *vtm, VALUE timev, int gmt) 00793 { 00794 return rb_strftime_with_timespec(s, maxsize, format, vtm, timev, NULL, gmt); 00795 } 00796 00797 size_t 00798 rb_strftime_timespec(char *s, size_t maxsize, const char *format, const struct vtm *vtm, struct timespec *ts, int gmt) 00799 { 00800 return rb_strftime_with_timespec(s, maxsize, format, vtm, Qnil, ts, gmt); 00801 } 00802 00803 /* isleap --- is a year a leap year? */ 00804 00805 #ifndef __STDC__ 00806 static int 00807 isleap(year) 00808 long year; 00809 #else 00810 static int 00811 isleap(long year) 00812 #endif 00813 { 00814 return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); 00815 } 00816 00817 00818 static void 00819 vtm2tm_noyear(const struct vtm *vtm, struct tm *result) 00820 { 00821 struct tm tm; 00822 00823 /* for isleap() in iso8601wknum. +100 is -1900 (mod 400). */ 00824 tm.tm_year = FIX2INT(mod(vtm->year, INT2FIX(400))) + 100; 00825 00826 tm.tm_mon = vtm->mon-1; 00827 tm.tm_mday = vtm->mday; 00828 tm.tm_hour = vtm->hour; 00829 tm.tm_min = vtm->min; 00830 tm.tm_sec = vtm->sec; 00831 tm.tm_wday = vtm->wday; 00832 tm.tm_yday = vtm->yday-1; 00833 tm.tm_isdst = vtm->isdst; 00834 #if defined(HAVE_STRUCT_TM_TM_GMTOFF) 00835 tm.tm_gmtoff = NUM2LONG(vtm->utc_offset); 00836 #endif 00837 #if defined(HAVE_TM_ZONE) 00838 tm.tm_zone = (char *)vtm->zone; 00839 #endif 00840 *result = tm; 00841 } 00842 00843 #ifdef POSIX2_DATE 00844 /* iso8601wknum --- compute week number according to ISO 8601 */ 00845 00846 #ifndef __STDC__ 00847 static int 00848 iso8601wknum(timeptr) 00849 const struct tm *timeptr; 00850 #else 00851 static int 00852 iso8601wknum(const struct tm *timeptr) 00853 #endif 00854 { 00855 /* 00856 * From 1003.2: 00857 * If the week (Monday to Sunday) containing January 1 00858 * has four or more days in the new year, then it is week 1; 00859 * otherwise it is the highest numbered week of the previous 00860 * year (52 or 53), and the next week is week 1. 00861 * 00862 * ADR: This means if Jan 1 was Monday through Thursday, 00863 * it was week 1, otherwise week 52 or 53. 00864 * 00865 * XPG4 erroneously included POSIX.2 rationale text in the 00866 * main body of the standard. Thus it requires week 53. 00867 */ 00868 00869 int weeknum, jan1day; 00870 00871 /* get week number, Monday as first day of the week */ 00872 weeknum = weeknumber(timeptr, 1); 00873 00874 /* 00875 * With thanks and tip of the hatlo to tml@tik.vtt.fi 00876 * 00877 * What day of the week does January 1 fall on? 00878 * We know that 00879 * (timeptr->tm_yday - jan1.tm_yday) MOD 7 == 00880 * (timeptr->tm_wday - jan1.tm_wday) MOD 7 00881 * and that 00882 * jan1.tm_yday == 0 00883 * and that 00884 * timeptr->tm_wday MOD 7 == timeptr->tm_wday 00885 * from which it follows that. . . 00886 */ 00887 jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7); 00888 if (jan1day < 0) 00889 jan1day += 7; 00890 00891 /* 00892 * If Jan 1 was a Monday through Thursday, it was in 00893 * week 1. Otherwise it was last year's highest week, which is 00894 * this year's week 0. 00895 * 00896 * What does that mean? 00897 * If Jan 1 was Monday, the week number is exactly right, it can 00898 * never be 0. 00899 * If it was Tuesday through Thursday, the weeknumber is one 00900 * less than it should be, so we add one. 00901 * Otherwise, Friday, Saturday or Sunday, the week number is 00902 * OK, but if it is 0, it needs to be 52 or 53. 00903 */ 00904 switch (jan1day) { 00905 case 1: /* Monday */ 00906 break; 00907 case 2: /* Tuesday */ 00908 case 3: /* Wednesday */ 00909 case 4: /* Thursday */ 00910 weeknum++; 00911 break; 00912 case 5: /* Friday */ 00913 case 6: /* Saturday */ 00914 case 0: /* Sunday */ 00915 if (weeknum == 0) { 00916 #ifdef USE_BROKEN_XPG4 00917 /* XPG4 (as of March 1994) says 53 unconditionally */ 00918 weeknum = 53; 00919 #else 00920 /* get week number of last week of last year */ 00921 struct tm dec31ly; /* 12/31 last year */ 00922 dec31ly = *timeptr; 00923 dec31ly.tm_year--; 00924 dec31ly.tm_mon = 11; 00925 dec31ly.tm_mday = 31; 00926 dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1; 00927 dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900L); 00928 weeknum = iso8601wknum(& dec31ly); 00929 #endif 00930 } 00931 break; 00932 } 00933 00934 if (timeptr->tm_mon == 11) { 00935 /* 00936 * The last week of the year 00937 * can be in week 1 of next year. 00938 * Sigh. 00939 * 00940 * This can only happen if 00941 * M T W 00942 * 29 30 31 00943 * 30 31 00944 * 31 00945 */ 00946 int wday, mday; 00947 00948 wday = timeptr->tm_wday; 00949 mday = timeptr->tm_mday; 00950 if ( (wday == 1 && (mday >= 29 && mday <= 31)) 00951 || (wday == 2 && (mday == 30 || mday == 31)) 00952 || (wday == 3 && mday == 31)) 00953 weeknum = 1; 00954 } 00955 00956 return weeknum; 00957 } 00958 00959 static int 00960 iso8601wknum_v(const struct vtm *vtm) 00961 { 00962 struct tm tm; 00963 vtm2tm_noyear(vtm, &tm); 00964 return iso8601wknum(&tm); 00965 } 00966 00967 #endif 00968 00969 /* weeknumber --- figure how many weeks into the year */ 00970 00971 /* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */ 00972 00973 #ifndef __STDC__ 00974 static int 00975 weeknumber(timeptr, firstweekday) 00976 const struct tm *timeptr; 00977 int firstweekday; 00978 #else 00979 static int 00980 weeknumber(const struct tm *timeptr, int firstweekday) 00981 #endif 00982 { 00983 int wday = timeptr->tm_wday; 00984 int ret; 00985 00986 if (firstweekday == 1) { 00987 if (wday == 0) /* sunday */ 00988 wday = 6; 00989 else 00990 wday--; 00991 } 00992 ret = ((timeptr->tm_yday + 7 - wday) / 7); 00993 if (ret < 0) 00994 ret = 0; 00995 return ret; 00996 } 00997 00998 static int 00999 weeknumber_v(const struct vtm *vtm, int firstweekday) 01000 { 01001 struct tm tm; 01002 vtm2tm_noyear(vtm, &tm); 01003 return weeknumber(&tm, firstweekday); 01004 } 01005 01006 #if 0 01007 /* ADR --- I'm loathe to mess with ado's code ... */ 01008 01009 Date: Wed, 24 Apr 91 20:54:08 MDT 01010 From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK> 01011 To: arnold@audiofax.com 01012 01013 Hi Arnold, 01014 in a process of fixing of strftime() in libraries on Atari ST I grabbed 01015 some pieces of code from your own strftime. When doing that it came 01016 to mind that your weeknumber() function compiles a little bit nicer 01017 in the following form: 01018 /* 01019 * firstweekday is 0 if starting in Sunday, non-zero if in Monday 01020 */ 01021 { 01022 return (timeptr->tm_yday - timeptr->tm_wday + 01023 (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7; 01024 } 01025 How nicer it depends on a compiler, of course, but always a tiny bit. 01026 01027 Cheers, 01028 Michal 01029 ntomczak@vm.ucs.ualberta.ca 01030 #endif 01031 01032 #ifdef TEST_STRFTIME 01033 01034 /* 01035 * NAME: 01036 * tst 01037 * 01038 * SYNOPSIS: 01039 * tst 01040 * 01041 * DESCRIPTION: 01042 * "tst" is a test driver for the function "strftime". 01043 * 01044 * OPTIONS: 01045 * None. 01046 * 01047 * AUTHOR: 01048 * Karl Vogel 01049 * Control Data Systems, Inc. 01050 * vogelke@c-17igp.wpafb.af.mil 01051 * 01052 * BUGS: 01053 * None noticed yet. 01054 * 01055 * COMPILE: 01056 * cc -o tst -DTEST_STRFTIME strftime.c 01057 */ 01058 01059 /* ADR: I reformatted this to my liking, and deleted some unneeded code. */ 01060 01061 #ifndef NULL 01062 #include <stdio.h> 01063 #endif 01064 #include <sys/time.h> 01065 #include <string.h> 01066 01067 #define MAXTIME 132 01068 01069 /* 01070 * Array of time formats. 01071 */ 01072 01073 static char *array[] = 01074 { 01075 "(%%A) full weekday name, var length (Sunday..Saturday) %A", 01076 "(%%B) full month name, var length (January..December) %B", 01077 "(%%C) Century %C", 01078 "(%%D) date (%%m/%%d/%%y) %D", 01079 "(%%E) Locale extensions (ignored) %E", 01080 "(%%H) hour (24-hour clock, 00..23) %H", 01081 "(%%I) hour (12-hour clock, 01..12) %I", 01082 "(%%M) minute (00..59) %M", 01083 "(%%O) Locale extensions (ignored) %O", 01084 "(%%R) time, 24-hour (%%H:%%M) %R", 01085 "(%%S) second (00..60) %S", 01086 "(%%T) time, 24-hour (%%H:%%M:%%S) %T", 01087 "(%%U) week of year, Sunday as first day of week (00..53) %U", 01088 "(%%V) week of year according to ISO 8601 %V", 01089 "(%%W) week of year, Monday as first day of week (00..53) %W", 01090 "(%%X) appropriate locale time representation (%H:%M:%S) %X", 01091 "(%%Y) year with century (1970...) %Y", 01092 "(%%Z) timezone (EDT), or blank if timezone not determinable %Z", 01093 "(%%a) locale's abbreviated weekday name (Sun..Sat) %a", 01094 "(%%b) locale's abbreviated month name (Jan..Dec) %b", 01095 "(%%c) full date (Sat Nov 4 12:02:33 1989)%n%t%t%t %c", 01096 "(%%d) day of the month (01..31) %d", 01097 "(%%e) day of the month, blank-padded ( 1..31) %e", 01098 "(%%h) should be same as (%%b) %h", 01099 "(%%j) day of the year (001..366) %j", 01100 "(%%k) hour, 24-hour clock, blank pad ( 0..23) %k", 01101 "(%%l) hour, 12-hour clock, blank pad ( 1..12) %l", 01102 "(%%m) month (01..12) %m", 01103 "(%%p) locale's AM or PM based on 12-hour clock %p", 01104 "(%%r) time, 12-hour (same as %%I:%%M:%%S %%p) %r", 01105 "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7] %u", 01106 "(%%v) VMS date (dd-bbb-YYYY) %v", 01107 "(%%w) day of week (0..6, Sunday == 0) %w", 01108 "(%%x) appropriate locale date representation %x", 01109 "(%%y) last two digits of year (00..99) %y", 01110 "(%%z) timezone offset east of GMT as HHMM (e.g. -0500) %z", 01111 (char *) NULL 01112 }; 01113 01114 /* main routine. */ 01115 01116 int 01117 main(argc, argv) 01118 int argc; 01119 char **argv; 01120 { 01121 long time(); 01122 01123 char *next; 01124 char string[MAXTIME]; 01125 01126 int k; 01127 int length; 01128 01129 struct tm *tm; 01130 01131 long clock; 01132 01133 /* Call the function. */ 01134 01135 clock = time((long *) 0); 01136 tm = localtime(&clock); 01137 01138 for (k = 0; next = array[k]; k++) { 01139 length = strftime(string, MAXTIME, next, tm); 01140 printf("%s\n", string); 01141 } 01142 01143 exit(0); 01144 } 01145 #endif /* TEST_STRFTIME */ 01146