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