Ruby
2.0.0p247(2013-06-27revision41674)
|
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 "ruby/encoding.h" 00052 #include "timev.h" 00053 00054 #ifndef GAWK 00055 #include <stdio.h> 00056 #include <ctype.h> 00057 #include <string.h> 00058 #include <time.h> 00059 #include <sys/types.h> 00060 #include <errno.h> 00061 #endif 00062 #if defined(TM_IN_SYS_TIME) || !defined(GAWK) 00063 #include <sys/types.h> 00064 #if HAVE_SYS_TIME_H 00065 #include <sys/time.h> 00066 #endif 00067 #endif 00068 #include <math.h> 00069 00070 /* defaults: season to taste */ 00071 #define SYSV_EXT 1 /* stuff in System V ascftime routine */ 00072 #define SUNOS_EXT 1 /* stuff in SunOS strftime routine */ 00073 #define POSIX2_DATE 1 /* stuff in Posix 1003.2 date command */ 00074 #define VMS_EXT 1 /* include %v for VMS date format */ 00075 #define MAILHEADER_EXT 1 /* add %z for HHMM format */ 00076 #define ISO_DATE_EXT 1 /* %G and %g for year of ISO week */ 00077 00078 #if defined(ISO_DATE_EXT) 00079 #if ! defined(POSIX2_DATE) 00080 #define POSIX2_DATE 1 00081 #endif 00082 #endif 00083 00084 #if defined(POSIX2_DATE) 00085 #if ! defined(SYSV_EXT) 00086 #define SYSV_EXT 1 00087 #endif 00088 #if ! defined(SUNOS_EXT) 00089 #define SUNOS_EXT 1 00090 #endif 00091 #endif 00092 00093 #if defined(POSIX2_DATE) 00094 #define adddecl(stuff) stuff 00095 #else 00096 #define adddecl(stuff) 00097 #endif 00098 00099 #undef strchr /* avoid AIX weirdness */ 00100 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 vtm *vtm, int firstweekday); 00111 adddecl(static int iso8601wknum_v(const struct vtm *vtm);) 00112 #endif 00113 00114 #ifdef STDC_HEADERS 00115 #include <stdlib.h> 00116 #include <string.h> 00117 #else 00118 extern void *malloc(); 00119 extern void *realloc(); 00120 extern char *getenv(); 00121 extern char *strchr(); 00122 #endif 00123 00124 #define range(low, item, hi) max((low), min((item), (hi))) 00125 00126 #undef min /* just in case */ 00127 00128 /* min --- return minimum of two numbers */ 00129 00130 static inline int 00131 min(int a, int b) 00132 { 00133 return (a < b ? a : b); 00134 } 00135 00136 #undef max /* also, just in case */ 00137 00138 /* max --- return maximum of two numbers */ 00139 00140 static inline int 00141 max(int a, int b) 00142 { 00143 return (a > b ? a : b); 00144 } 00145 00146 #ifdef NO_STRING_LITERAL_CONCATENATION 00147 #error No string literal concatenation 00148 #endif 00149 00150 #define add(x,y) (rb_funcall((x), '+', 1, (y))) 00151 #define sub(x,y) (rb_funcall((x), '-', 1, (y))) 00152 #define mul(x,y) (rb_funcall((x), '*', 1, (y))) 00153 #define quo(x,y) (rb_funcall((x), rb_intern("quo"), 1, (y))) 00154 #define div(x,y) (rb_funcall((x), rb_intern("div"), 1, (y))) 00155 #define mod(x,y) (rb_funcall((x), '%', 1, (y))) 00156 00157 /* strftime --- produce formatted time */ 00158 00159 /* 00160 * enc is the encoding of the format. It is used as the encoding of resulted 00161 * string, but the name of the month and weekday are always US-ASCII. So it 00162 * is only used for the timezone name on Windows. 00163 */ 00164 static size_t 00165 rb_strftime_with_timespec(char *s, size_t maxsize, const char *format, rb_encoding *enc, const struct vtm *vtm, VALUE timev, struct timespec *ts, int gmt) 00166 { 00167 const char *const endp = s + maxsize; 00168 const char *const start = s; 00169 const char *sp, *tp; 00170 #define TBUFSIZE 100 00171 auto char tbuf[TBUFSIZE]; 00172 long off; 00173 ptrdiff_t i; 00174 int w; 00175 long y; 00176 int precision, flags, colons; 00177 char padding; 00178 enum {LEFT, CHCASE, LOWER, UPPER}; 00179 #define BIT_OF(n) (1U<<(n)) 00180 #ifdef MAILHEADER_EXT 00181 int sign; 00182 #endif 00183 00184 /* various tables, useful in North America */ 00185 static const char days_l[][10] = { 00186 "Sunday", "Monday", "Tuesday", "Wednesday", 00187 "Thursday", "Friday", "Saturday", 00188 }; 00189 static const char months_l[][10] = { 00190 "January", "February", "March", "April", 00191 "May", "June", "July", "August", "September", 00192 "October", "November", "December", 00193 }; 00194 static const char ampm[][3] = { "AM", "PM", }; 00195 00196 if (s == NULL || format == NULL || vtm == NULL || maxsize == 0) 00197 return 0; 00198 00199 /* quick check if we even need to bother */ 00200 if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) { 00201 err: 00202 errno = ERANGE; 00203 return 0; 00204 } 00205 00206 if (enc && (enc == rb_usascii_encoding() || 00207 enc == rb_ascii8bit_encoding() || enc == rb_locale_encoding())) { 00208 enc = NULL; 00209 } 00210 00211 for (; *format && s < endp - 1; format++) { 00212 #define FLAG_FOUND() do { \ 00213 if (precision > 0) \ 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), enc, 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 if (gmt) { 00452 off = 0; 00453 } 00454 else { 00455 off = NUM2LONG(rb_funcall(vtm->utc_offset, rb_intern("round"), 0)); 00456 } 00457 if (off < 0) { 00458 off = -off; 00459 sign = -1; 00460 } else { 00461 sign = +1; 00462 } 00463 switch (colons) { 00464 case 0: /* %z -> +hhmm */ 00465 precision = precision <= 5 ? 2 : precision-3; 00466 NEEDS(precision + 3); 00467 break; 00468 00469 case 1: /* %:z -> +hh:mm */ 00470 precision = precision <= 6 ? 2 : precision-4; 00471 NEEDS(precision + 4); 00472 break; 00473 00474 case 2: /* %::z -> +hh:mm:ss */ 00475 precision = precision <= 9 ? 2 : precision-7; 00476 NEEDS(precision + 7); 00477 break; 00478 00479 case 3: /* %:::z -> +hh[:mm[:ss]] */ 00480 if (off % 3600 == 0) { 00481 precision = precision <= 3 ? 2 : precision-1; 00482 NEEDS(precision + 3); 00483 } 00484 else if (off % 60 == 0) { 00485 precision = precision <= 6 ? 2 : precision-4; 00486 NEEDS(precision + 4); 00487 } 00488 else { 00489 precision = precision <= 9 ? 2 : precision-7; 00490 NEEDS(precision + 9); 00491 } 00492 break; 00493 00494 default: 00495 format--; 00496 goto unknown; 00497 } 00498 i = snprintf(s, endp - s, (padding == ' ' ? "%+*ld" : "%+.*ld"), 00499 precision + 1, sign * (off / 3600)); 00500 if (i < 0) goto err; 00501 if (sign < 0 && off < 3600) { 00502 *(padding == ' ' ? s + i - 2 : s) = '-'; 00503 } 00504 s += i; 00505 off = off % 3600; 00506 if (colons == 3 && off == 0) 00507 continue; 00508 if (1 <= colons) 00509 *s++ = ':'; 00510 i = snprintf(s, endp - s, "%02d", (int)(off / 60)); 00511 if (i < 0) goto err; 00512 s += i; 00513 off = off % 60; 00514 if (colons == 3 && off == 0) 00515 continue; 00516 if (2 <= colons) { 00517 *s++ = ':'; 00518 i = snprintf(s, endp - s, "%02d", (int)off); 00519 if (i < 0) goto err; 00520 s += i; 00521 } 00522 continue; 00523 #endif /* MAILHEADER_EXT */ 00524 00525 case 'Z': /* time zone name or abbreviation */ 00526 if (flags & BIT_OF(CHCASE)) { 00527 flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE)); 00528 flags |= BIT_OF(LOWER); 00529 } 00530 if (gmt) { 00531 i = 3; 00532 tp = "UTC"; 00533 break; 00534 } 00535 if (vtm->zone == NULL) { 00536 i = 0; 00537 } 00538 else { 00539 tp = vtm->zone; 00540 if (enc) { 00541 for (i = 0; i < TBUFSIZE && tp[i]; i++) { 00542 if ((unsigned char)tp[i] > 0x7F) { 00543 VALUE str = rb_str_conv_enc_opts(rb_str_new_cstr(tp), rb_locale_encoding(), enc, ECONV_UNDEF_REPLACE|ECONV_INVALID_REPLACE, Qnil); 00544 i = strlcpy(tbuf, RSTRING_PTR(str), TBUFSIZE); 00545 tp = tbuf; 00546 break; 00547 } 00548 } 00549 } 00550 else 00551 i = strlen(tp); 00552 } 00553 break; 00554 00555 #ifdef SYSV_EXT 00556 case 'n': /* same as \n */ 00557 FILL_PADDING(1); 00558 *s++ = '\n'; 00559 continue; 00560 00561 case 't': /* same as \t */ 00562 FILL_PADDING(1); 00563 *s++ = '\t'; 00564 continue; 00565 00566 case 'D': /* date as %m/%d/%y */ 00567 STRFTIME("%m/%d/%y"); 00568 continue; 00569 00570 case 'e': /* day of month, blank padded */ 00571 FMT(' ', 2, "d", range(1, vtm->mday, 31)); 00572 continue; 00573 00574 case 'r': /* time as %I:%M:%S %p */ 00575 STRFTIME("%I:%M:%S %p"); 00576 continue; 00577 00578 case 'R': /* time as %H:%M */ 00579 STRFTIME("%H:%M"); 00580 continue; 00581 00582 case 'T': /* time as %H:%M:%S */ 00583 STRFTIME("%H:%M:%S"); 00584 continue; 00585 #endif 00586 00587 #ifdef SUNOS_EXT 00588 case 'k': /* hour, 24-hour clock, blank pad */ 00589 i = range(0, vtm->hour, 23); 00590 FMT(' ', 2, "d", (int)i); 00591 continue; 00592 00593 case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */ 00594 i = range(0, vtm->hour, 23); 00595 if (i == 0) 00596 i = 12; 00597 else if (i > 12) 00598 i -= 12; 00599 FMT(' ', 2, "d", (int)i); 00600 continue; 00601 #endif 00602 00603 00604 #ifdef VMS_EXT 00605 case 'v': /* date as dd-bbb-YYYY */ 00606 STRFTIME("%e-%^b-%4Y"); 00607 continue; 00608 #endif 00609 00610 00611 #ifdef POSIX2_DATE 00612 case 'C': 00613 FMTV('0', 2, "d", div(vtm->year, INT2FIX(100))); 00614 continue; 00615 00616 case 'E': 00617 /* POSIX locale extensions, ignored for now */ 00618 if (!format[1] || !strchr("cCxXyY", format[1])) 00619 goto unknown; 00620 goto again; 00621 case 'O': 00622 /* POSIX locale extensions, ignored for now */ 00623 if (!format[1] || !strchr("deHkIlmMSuUVwWy", format[1])) 00624 goto unknown; 00625 goto again; 00626 00627 case 'V': /* week of year according ISO 8601 */ 00628 FMT('0', 2, "d", iso8601wknum_v(vtm)); 00629 continue; 00630 00631 case 'u': 00632 /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */ 00633 FMT('0', 1, "d", vtm->wday == 0 ? 7 : vtm->wday); 00634 continue; 00635 #endif /* POSIX2_DATE */ 00636 00637 #ifdef ISO_DATE_EXT 00638 case 'G': 00639 case 'g': 00640 /* 00641 * Year of ISO week. 00642 * 00643 * If it's December but the ISO week number is one, 00644 * that week is in next year. 00645 * If it's January but the ISO week number is 52 or 00646 * 53, that week is in last year. 00647 * Otherwise, it's this year. 00648 */ 00649 { 00650 VALUE yv = vtm->year; 00651 w = iso8601wknum_v(vtm); 00652 if (vtm->mon == 12 && w == 1) 00653 yv = add(yv, INT2FIX(1)); 00654 else if (vtm->mon == 1 && w >= 52) 00655 yv = sub(yv, INT2FIX(1)); 00656 00657 if (*format == 'G') { 00658 if (FIXNUM_P(yv)) { 00659 const long y = FIX2LONG(yv); 00660 FMT('0', 0 <= y ? 4 : 5, "ld", y); 00661 } 00662 else { 00663 FMTV('0', 4, "d", yv); 00664 } 00665 } 00666 else { 00667 yv = mod(yv, INT2FIX(100)); 00668 y = FIX2LONG(yv); 00669 FMT('0', 2, "ld", y); 00670 } 00671 continue; 00672 } 00673 00674 #endif /* ISO_DATE_EXT */ 00675 00676 00677 case 'L': 00678 w = 3; 00679 goto subsec; 00680 00681 case 'N': 00682 /* 00683 * fractional second digits. default is 9 digits 00684 * (nanosecond). 00685 * 00686 * %3N millisecond (3 digits) 00687 * %6N microsecond (6 digits) 00688 * %9N nanosecond (9 digits) 00689 */ 00690 w = 9; 00691 subsec: 00692 if (precision <= 0) { 00693 precision = w; 00694 } 00695 NEEDS(precision); 00696 00697 if (ts) { 00698 long subsec = ts->tv_nsec; 00699 if (9 < precision) { 00700 snprintf(s, endp - s, "%09ld", subsec); 00701 memset(s+9, '0', precision-9); 00702 s += precision; 00703 } 00704 else { 00705 int i; 00706 for (i = 0; i < 9-precision; i++) 00707 subsec /= 10; 00708 snprintf(s, endp - s, "%0*ld", precision, subsec); 00709 s += precision; 00710 } 00711 } 00712 else { 00713 VALUE subsec = mod(timev, INT2FIX(1)); 00714 int ww; 00715 long n; 00716 00717 ww = precision; 00718 while (9 <= ww) { 00719 subsec = mul(subsec, INT2FIX(1000000000)); 00720 ww -= 9; 00721 } 00722 n = 1; 00723 for (; 0 < ww; ww--) 00724 n *= 10; 00725 if (n != 1) 00726 subsec = mul(subsec, INT2FIX(n)); 00727 subsec = div(subsec, INT2FIX(1)); 00728 00729 if (FIXNUM_P(subsec)) { 00730 (void)snprintf(s, endp - s, "%0*ld", precision, FIX2LONG(subsec)); 00731 s += precision; 00732 } 00733 else { 00734 VALUE args[2], result; 00735 args[0] = INT2FIX(precision); 00736 args[1] = subsec; 00737 result = rb_str_format(2, args, rb_str_new2("%0*d")); 00738 (void)strlcpy(s, StringValueCStr(result), endp-s); 00739 s += precision; 00740 } 00741 } 00742 continue; 00743 00744 case 'F': /* Equivalent to %Y-%m-%d */ 00745 STRFTIME("%Y-%m-%d"); 00746 continue; 00747 00748 case '-': 00749 FLAG_FOUND(); 00750 flags |= BIT_OF(LEFT); 00751 padding = precision = 0; 00752 goto again; 00753 00754 case '^': 00755 FLAG_FOUND(); 00756 flags |= BIT_OF(UPPER); 00757 goto again; 00758 00759 case '#': 00760 FLAG_FOUND(); 00761 flags |= BIT_OF(CHCASE); 00762 goto again; 00763 00764 case '_': 00765 FLAG_FOUND(); 00766 padding = ' '; 00767 goto again; 00768 00769 case ':': 00770 { 00771 size_t l = strspn(format, ":"); 00772 if (l > 3 || format[l] != 'z') goto unknown; 00773 colons = (int)l; 00774 format += l - 1; 00775 } 00776 goto again; 00777 00778 case '0': 00779 padding = '0'; 00780 case '1': case '2': case '3': case '4': 00781 case '5': case '6': case '7': case '8': case '9': 00782 { 00783 char *e; 00784 precision = (int)strtoul(format, &e, 10); 00785 format = e - 1; 00786 goto again; 00787 } 00788 00789 default: 00790 unknown: 00791 i = format - sp + 1; 00792 tp = sp; 00793 precision = -1; 00794 flags = 0; 00795 padding = 0; 00796 colons = 0; 00797 break; 00798 } 00799 if (i) { 00800 FILL_PADDING(i); 00801 memcpy(s, tp, i); 00802 switch (flags & (BIT_OF(UPPER)|BIT_OF(LOWER))) { 00803 case BIT_OF(UPPER): 00804 do { 00805 if (ISLOWER(*s)) *s = TOUPPER(*s); 00806 } while (s++, --i); 00807 break; 00808 case BIT_OF(LOWER): 00809 do { 00810 if (ISUPPER(*s)) *s = TOLOWER(*s); 00811 } while (s++, --i); 00812 break; 00813 default: 00814 s += i; 00815 break; 00816 } 00817 } 00818 } 00819 if (s >= endp) { 00820 goto err; 00821 } 00822 if (*format == '\0') { 00823 *s = '\0'; 00824 return (s - start); 00825 } else 00826 return 0; 00827 } 00828 00829 size_t 00830 rb_strftime(char *s, size_t maxsize, const char *format, rb_encoding *enc, const struct vtm *vtm, VALUE timev, int gmt) 00831 { 00832 return rb_strftime_with_timespec(s, maxsize, format, enc, vtm, timev, NULL, gmt); 00833 } 00834 00835 size_t 00836 rb_strftime_timespec(char *s, size_t maxsize, const char *format, rb_encoding *enc, const struct vtm *vtm, struct timespec *ts, int gmt) 00837 { 00838 return rb_strftime_with_timespec(s, maxsize, format, enc, vtm, Qnil, ts, gmt); 00839 } 00840 00841 /* isleap --- is a year a leap year? */ 00842 00843 static int 00844 isleap(long year) 00845 { 00846 return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); 00847 } 00848 00849 00850 static void 00851 vtm2tm_noyear(const struct vtm *vtm, struct tm *result) 00852 { 00853 struct tm tm; 00854 00855 /* for isleap() in iso8601wknum. +100 is -1900 (mod 400). */ 00856 tm.tm_year = FIX2INT(mod(vtm->year, INT2FIX(400))) + 100; 00857 00858 tm.tm_mon = vtm->mon-1; 00859 tm.tm_mday = vtm->mday; 00860 tm.tm_hour = vtm->hour; 00861 tm.tm_min = vtm->min; 00862 tm.tm_sec = vtm->sec; 00863 tm.tm_wday = vtm->wday; 00864 tm.tm_yday = vtm->yday-1; 00865 tm.tm_isdst = vtm->isdst; 00866 #if defined(HAVE_STRUCT_TM_TM_GMTOFF) 00867 tm.tm_gmtoff = NUM2LONG(vtm->utc_offset); 00868 #endif 00869 #if defined(HAVE_TM_ZONE) 00870 tm.tm_zone = (char *)vtm->zone; 00871 #endif 00872 *result = tm; 00873 } 00874 00875 #ifdef POSIX2_DATE 00876 /* iso8601wknum --- compute week number according to ISO 8601 */ 00877 00878 static int 00879 iso8601wknum(const struct tm *timeptr) 00880 { 00881 /* 00882 * From 1003.2: 00883 * If the week (Monday to Sunday) containing January 1 00884 * has four or more days in the new year, then it is week 1; 00885 * otherwise it is the highest numbered week of the previous 00886 * year (52 or 53), and the next week is week 1. 00887 * 00888 * ADR: This means if Jan 1 was Monday through Thursday, 00889 * it was week 1, otherwise week 52 or 53. 00890 * 00891 * XPG4 erroneously included POSIX.2 rationale text in the 00892 * main body of the standard. Thus it requires week 53. 00893 */ 00894 00895 int weeknum, jan1day; 00896 00897 /* get week number, Monday as first day of the week */ 00898 weeknum = weeknumber(timeptr, 1); 00899 00900 /* 00901 * With thanks and tip of the hatlo to tml@tik.vtt.fi 00902 * 00903 * What day of the week does January 1 fall on? 00904 * We know that 00905 * (timeptr->tm_yday - jan1.tm_yday) MOD 7 == 00906 * (timeptr->tm_wday - jan1.tm_wday) MOD 7 00907 * and that 00908 * jan1.tm_yday == 0 00909 * and that 00910 * timeptr->tm_wday MOD 7 == timeptr->tm_wday 00911 * from which it follows that. . . 00912 */ 00913 jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7); 00914 if (jan1day < 0) 00915 jan1day += 7; 00916 00917 /* 00918 * If Jan 1 was a Monday through Thursday, it was in 00919 * week 1. Otherwise it was last year's highest week, which is 00920 * this year's week 0. 00921 * 00922 * What does that mean? 00923 * If Jan 1 was Monday, the week number is exactly right, it can 00924 * never be 0. 00925 * If it was Tuesday through Thursday, the weeknumber is one 00926 * less than it should be, so we add one. 00927 * Otherwise, Friday, Saturday or Sunday, the week number is 00928 * OK, but if it is 0, it needs to be 52 or 53. 00929 */ 00930 switch (jan1day) { 00931 case 1: /* Monday */ 00932 break; 00933 case 2: /* Tuesday */ 00934 case 3: /* Wednesday */ 00935 case 4: /* Thursday */ 00936 weeknum++; 00937 break; 00938 case 5: /* Friday */ 00939 case 6: /* Saturday */ 00940 case 0: /* Sunday */ 00941 if (weeknum == 0) { 00942 #ifdef USE_BROKEN_XPG4 00943 /* XPG4 (as of March 1994) says 53 unconditionally */ 00944 weeknum = 53; 00945 #else 00946 /* get week number of last week of last year */ 00947 struct tm dec31ly; /* 12/31 last year */ 00948 dec31ly = *timeptr; 00949 dec31ly.tm_year--; 00950 dec31ly.tm_mon = 11; 00951 dec31ly.tm_mday = 31; 00952 dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1; 00953 dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900L); 00954 weeknum = iso8601wknum(& dec31ly); 00955 #endif 00956 } 00957 break; 00958 } 00959 00960 if (timeptr->tm_mon == 11) { 00961 /* 00962 * The last week of the year 00963 * can be in week 1 of next year. 00964 * Sigh. 00965 * 00966 * This can only happen if 00967 * M T W 00968 * 29 30 31 00969 * 30 31 00970 * 31 00971 */ 00972 int wday, mday; 00973 00974 wday = timeptr->tm_wday; 00975 mday = timeptr->tm_mday; 00976 if ( (wday == 1 && (mday >= 29 && mday <= 31)) 00977 || (wday == 2 && (mday == 30 || mday == 31)) 00978 || (wday == 3 && mday == 31)) 00979 weeknum = 1; 00980 } 00981 00982 return weeknum; 00983 } 00984 00985 static int 00986 iso8601wknum_v(const struct vtm *vtm) 00987 { 00988 struct tm tm; 00989 vtm2tm_noyear(vtm, &tm); 00990 return iso8601wknum(&tm); 00991 } 00992 00993 #endif 00994 00995 /* weeknumber --- figure how many weeks into the year */ 00996 00997 /* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */ 00998 00999 static int 01000 weeknumber(const struct tm *timeptr, int firstweekday) 01001 { 01002 int wday = timeptr->tm_wday; 01003 int ret; 01004 01005 if (firstweekday == 1) { 01006 if (wday == 0) /* sunday */ 01007 wday = 6; 01008 else 01009 wday--; 01010 } 01011 ret = ((timeptr->tm_yday + 7 - wday) / 7); 01012 if (ret < 0) 01013 ret = 0; 01014 return ret; 01015 } 01016 01017 static int 01018 weeknumber_v(const struct vtm *vtm, int firstweekday) 01019 { 01020 struct tm tm; 01021 vtm2tm_noyear(vtm, &tm); 01022 return weeknumber(&tm, firstweekday); 01023 } 01024 01025 #if 0 01026 /* ADR --- I'm loathe to mess with ado's code ... */ 01027 01028 Date: Wed, 24 Apr 91 20:54:08 MDT 01029 From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK> 01030 To: arnold@audiofax.com 01031 01032 Hi Arnold, 01033 in a process of fixing of strftime() in libraries on Atari ST I grabbed 01034 some pieces of code from your own strftime. When doing that it came 01035 to mind that your weeknumber() function compiles a little bit nicer 01036 in the following form: 01037 /* 01038 * firstweekday is 0 if starting in Sunday, non-zero if in Monday 01039 */ 01040 { 01041 return (timeptr->tm_yday - timeptr->tm_wday + 01042 (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7; 01043 } 01044 How nicer it depends on a compiler, of course, but always a tiny bit. 01045 01046 Cheers, 01047 Michal 01048 ntomczak@vm.ucs.ualberta.ca 01049 #endif 01050 01051 #ifdef TEST_STRFTIME 01052 01053 /* 01054 * NAME: 01055 * tst 01056 * 01057 * SYNOPSIS: 01058 * tst 01059 * 01060 * DESCRIPTION: 01061 * "tst" is a test driver for the function "strftime". 01062 * 01063 * OPTIONS: 01064 * None. 01065 * 01066 * AUTHOR: 01067 * Karl Vogel 01068 * Control Data Systems, Inc. 01069 * vogelke@c-17igp.wpafb.af.mil 01070 * 01071 * BUGS: 01072 * None noticed yet. 01073 * 01074 * COMPILE: 01075 * cc -o tst -DTEST_STRFTIME strftime.c 01076 */ 01077 01078 /* ADR: I reformatted this to my liking, and deleted some unneeded code. */ 01079 01080 #ifndef NULL 01081 #include <stdio.h> 01082 #endif 01083 #include <sys/time.h> 01084 #include <string.h> 01085 01086 #define MAXTIME 132 01087 01088 /* 01089 * Array of time formats. 01090 */ 01091 01092 static char *array[] = 01093 { 01094 "(%%A) full weekday name, var length (Sunday..Saturday) %A", 01095 "(%%B) full month name, var length (January..December) %B", 01096 "(%%C) Century %C", 01097 "(%%D) date (%%m/%%d/%%y) %D", 01098 "(%%E) Locale extensions (ignored) %E", 01099 "(%%H) hour (24-hour clock, 00..23) %H", 01100 "(%%I) hour (12-hour clock, 01..12) %I", 01101 "(%%M) minute (00..59) %M", 01102 "(%%O) Locale extensions (ignored) %O", 01103 "(%%R) time, 24-hour (%%H:%%M) %R", 01104 "(%%S) second (00..60) %S", 01105 "(%%T) time, 24-hour (%%H:%%M:%%S) %T", 01106 "(%%U) week of year, Sunday as first day of week (00..53) %U", 01107 "(%%V) week of year according to ISO 8601 %V", 01108 "(%%W) week of year, Monday as first day of week (00..53) %W", 01109 "(%%X) appropriate locale time representation (%H:%M:%S) %X", 01110 "(%%Y) year with century (1970...) %Y", 01111 "(%%Z) timezone (EDT), or blank if timezone not determinable %Z", 01112 "(%%a) locale's abbreviated weekday name (Sun..Sat) %a", 01113 "(%%b) locale's abbreviated month name (Jan..Dec) %b", 01114 "(%%c) full date (Sat Nov 4 12:02:33 1989)%n%t%t%t %c", 01115 "(%%d) day of the month (01..31) %d", 01116 "(%%e) day of the month, blank-padded ( 1..31) %e", 01117 "(%%h) should be same as (%%b) %h", 01118 "(%%j) day of the year (001..366) %j", 01119 "(%%k) hour, 24-hour clock, blank pad ( 0..23) %k", 01120 "(%%l) hour, 12-hour clock, blank pad ( 1..12) %l", 01121 "(%%m) month (01..12) %m", 01122 "(%%p) locale's AM or PM based on 12-hour clock %p", 01123 "(%%r) time, 12-hour (same as %%I:%%M:%%S %%p) %r", 01124 "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7] %u", 01125 "(%%v) VMS date (dd-bbb-YYYY) %v", 01126 "(%%w) day of week (0..6, Sunday == 0) %w", 01127 "(%%x) appropriate locale date representation %x", 01128 "(%%y) last two digits of year (00..99) %y", 01129 "(%%z) timezone offset east of GMT as HHMM (e.g. -0500) %z", 01130 (char *) NULL 01131 }; 01132 01133 /* main routine. */ 01134 01135 int 01136 main(int argc, char **argv) 01137 { 01138 long time(); 01139 01140 char *next; 01141 char string[MAXTIME]; 01142 01143 int k; 01144 int length; 01145 01146 struct tm *tm; 01147 01148 long clock; 01149 01150 /* Call the function. */ 01151 01152 clock = time((long *) 0); 01153 tm = localtime(&clock); 01154 01155 for (k = 0; next = array[k]; k++) { 01156 length = strftime(string, MAXTIME, next, tm); 01157 printf("%s\n", string); 01158 } 01159 01160 exit(0); 01161 } 01162 #endif /* TEST_STRFTIME */ 01163