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