00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
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
00071 #define SYSV_EXT 1
00072 #define SUNOS_EXT 1
00073 #define POSIX2_DATE 1
00074 #define VMS_EXT 1
00075 #define MAILHEADER_EXT 1
00076 #define ISO_DATE_EXT 1
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
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
00127
00128
00129
00130 static inline int
00131 min(int a, int b)
00132 {
00133 return (a < b ? a : b);
00134 }
00135
00136 #undef max
00137
00138
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
00158
00159
00160
00161
00162
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
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
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':
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':
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':
00319 #endif
00320 case 'b':
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':
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':
00343 STRFTIME("%a %b %e %H:%M:%S %Y");
00344 continue;
00345
00346 case 'd':
00347 i = range(1, vtm->mday, 31);
00348 FMT('0', 2, "d", (int)i);
00349 continue;
00350
00351 case 'H':
00352 i = range(0, vtm->hour, 23);
00353 FMT('0', 2, "d", (int)i);
00354 continue;
00355
00356 case 'I':
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':
00366 i = range(1, vtm->yday, 366);
00367 FMT('0', 3, "d", (int)i);
00368 continue;
00369
00370 case 'm':
00371 i = range(1, vtm->mon, 12);
00372 FMT('0', 2, "d", (int)i);
00373 continue;
00374
00375 case 'M':
00376 i = range(0, vtm->min, 59);
00377 FMT('0', 2, "d", (int)i);
00378 continue;
00379
00380 case 'p':
00381 case 'P':
00382 if ((*format == 'p' && (flags & BIT_OF(CHCASE))) ||
00383 (*format == 'P' && !(flags & (BIT_OF(CHCASE)|BIT_OF(UPPER))))) {
00384 flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE));
00385 flags |= BIT_OF(LOWER);
00386 }
00387 i = range(0, vtm->hour, 23);
00388 if (i < 12)
00389 tp = ampm[0];
00390 else
00391 tp = ampm[1];
00392 i = 2;
00393 break;
00394
00395 case 's':
00396 if (ts) {
00397 time_t sec = ts->tv_sec;
00398 if (~(time_t)0 <= 0)
00399 FMT('0', 1, PRI_TIMET_PREFIX"d", sec);
00400 else
00401 FMT('0', 1, PRI_TIMET_PREFIX"u", sec);
00402 }
00403 else {
00404 VALUE sec = div(timev, INT2FIX(1));
00405 FMTV('0', 1, "d", sec);
00406 }
00407 continue;
00408
00409 case 'S':
00410 i = range(0, vtm->sec, 60);
00411 FMT('0', 2, "d", (int)i);
00412 continue;
00413
00414 case 'U':
00415 FMT('0', 2, "d", weeknumber_v(vtm, 0));
00416 continue;
00417
00418 case 'w':
00419 i = range(0, vtm->wday, 6);
00420 FMT('0', 1, "d", (int)i);
00421 continue;
00422
00423 case 'W':
00424 FMT('0', 2, "d", weeknumber_v(vtm, 1));
00425 continue;
00426
00427 case 'x':
00428 STRFTIME("%m/%d/%y");
00429 continue;
00430
00431 case 'X':
00432 STRFTIME("%H:%M:%S");
00433 continue;
00434
00435 case 'y':
00436 i = NUM2INT(mod(vtm->year, INT2FIX(100)));
00437 FMT('0', 2, "d", (int)i);
00438 continue;
00439
00440 case 'Y':
00441 if (FIXNUM_P(vtm->year)) {
00442 long y = FIX2LONG(vtm->year);
00443 FMT('0', 0 <= y ? 4 : 5, "ld", y);
00444 }
00445 else {
00446 FMTV('0', 4, "d", vtm->year);
00447 }
00448 continue;
00449
00450 #ifdef MAILHEADER_EXT
00451 case 'z':
00452 if (gmt) {
00453 off = 0;
00454 }
00455 else {
00456 off = NUM2LONG(rb_funcall(vtm->utc_offset, rb_intern("round"), 0));
00457 }
00458 if (off < 0) {
00459 off = -off;
00460 sign = -1;
00461 } else {
00462 sign = +1;
00463 }
00464 switch (colons) {
00465 case 0:
00466 precision = precision <= 5 ? 2 : precision-3;
00467 NEEDS(precision + 3);
00468 break;
00469
00470 case 1:
00471 precision = precision <= 6 ? 2 : precision-4;
00472 NEEDS(precision + 4);
00473 break;
00474
00475 case 2:
00476 precision = precision <= 9 ? 2 : precision-7;
00477 NEEDS(precision + 7);
00478 break;
00479
00480 case 3:
00481 if (off % 3600 == 0) {
00482 precision = precision <= 3 ? 2 : precision-1;
00483 NEEDS(precision + 3);
00484 }
00485 else if (off % 60 == 0) {
00486 precision = precision <= 6 ? 2 : precision-4;
00487 NEEDS(precision + 4);
00488 }
00489 else {
00490 precision = precision <= 9 ? 2 : precision-7;
00491 NEEDS(precision + 9);
00492 }
00493 break;
00494
00495 default:
00496 format--;
00497 goto unknown;
00498 }
00499 i = snprintf(s, endp - s, (padding == ' ' ? "%+*ld" : "%+.*ld"),
00500 precision + 1, sign * (off / 3600));
00501 if (i < 0) goto err;
00502 if (sign < 0 && off < 3600) {
00503 *(padding == ' ' ? s + i - 2 : s) = '-';
00504 }
00505 s += i;
00506 off = off % 3600;
00507 if (colons == 3 && off == 0)
00508 continue;
00509 if (1 <= colons)
00510 *s++ = ':';
00511 i = snprintf(s, endp - s, "%02d", (int)(off / 60));
00512 if (i < 0) goto err;
00513 s += i;
00514 off = off % 60;
00515 if (colons == 3 && off == 0)
00516 continue;
00517 if (2 <= colons) {
00518 *s++ = ':';
00519 i = snprintf(s, endp - s, "%02d", (int)off);
00520 if (i < 0) goto err;
00521 s += i;
00522 }
00523 continue;
00524 #endif
00525
00526 case 'Z':
00527 if (flags & BIT_OF(CHCASE)) {
00528 flags &= ~(BIT_OF(UPPER)|BIT_OF(CHCASE));
00529 flags |= BIT_OF(LOWER);
00530 }
00531 if (gmt) {
00532 i = 3;
00533 tp = "UTC";
00534 break;
00535 }
00536 if (vtm->zone == NULL) {
00537 i = 0;
00538 }
00539 else {
00540 tp = vtm->zone;
00541 if (enc) {
00542 for (i = 0; i < TBUFSIZE && tp[i]; i++) {
00543 if ((unsigned char)tp[i] > 0x7F) {
00544 VALUE str = rb_str_conv_enc_opts(rb_str_new_cstr(tp), rb_locale_encoding(), enc, ECONV_UNDEF_REPLACE|ECONV_INVALID_REPLACE, Qnil);
00545 i = strlcpy(tbuf, RSTRING_PTR(str), TBUFSIZE);
00546 tp = tbuf;
00547 break;
00548 }
00549 }
00550 }
00551 else
00552 i = strlen(tp);
00553 }
00554 break;
00555
00556 #ifdef SYSV_EXT
00557 case 'n':
00558 FILL_PADDING(1);
00559 *s++ = '\n';
00560 continue;
00561
00562 case 't':
00563 FILL_PADDING(1);
00564 *s++ = '\t';
00565 continue;
00566
00567 case 'D':
00568 STRFTIME("%m/%d/%y");
00569 continue;
00570
00571 case 'e':
00572 FMT(' ', 2, "d", range(1, vtm->mday, 31));
00573 continue;
00574
00575 case 'r':
00576 STRFTIME("%I:%M:%S %p");
00577 continue;
00578
00579 case 'R':
00580 STRFTIME("%H:%M");
00581 continue;
00582
00583 case 'T':
00584 STRFTIME("%H:%M:%S");
00585 continue;
00586 #endif
00587
00588 #ifdef SUNOS_EXT
00589 case 'k':
00590 i = range(0, vtm->hour, 23);
00591 FMT(' ', 2, "d", (int)i);
00592 continue;
00593
00594 case 'l':
00595 i = range(0, vtm->hour, 23);
00596 if (i == 0)
00597 i = 12;
00598 else if (i > 12)
00599 i -= 12;
00600 FMT(' ', 2, "d", (int)i);
00601 continue;
00602 #endif
00603
00604
00605 #ifdef VMS_EXT
00606 case 'v':
00607 STRFTIME("%e-%^b-%4Y");
00608 continue;
00609 #endif
00610
00611
00612 #ifdef POSIX2_DATE
00613 case 'C':
00614 FMTV('0', 2, "d", div(vtm->year, INT2FIX(100)));
00615 continue;
00616
00617 case 'E':
00618
00619 if (!format[1] || !strchr("cCxXyY", format[1]))
00620 goto unknown;
00621 goto again;
00622 case 'O':
00623
00624 if (!format[1] || !strchr("deHkIlmMSuUVwWy", format[1]))
00625 goto unknown;
00626 goto again;
00627
00628 case 'V':
00629 FMT('0', 2, "d", iso8601wknum_v(vtm));
00630 continue;
00631
00632 case 'u':
00633
00634 FMT('0', 1, "d", vtm->wday == 0 ? 7 : vtm->wday);
00635 continue;
00636 #endif
00637
00638 #ifdef ISO_DATE_EXT
00639 case 'G':
00640 case 'g':
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650 {
00651 VALUE yv = vtm->year;
00652 w = iso8601wknum_v(vtm);
00653 if (vtm->mon == 12 && w == 1)
00654 yv = add(yv, INT2FIX(1));
00655 else if (vtm->mon == 1 && w >= 52)
00656 yv = sub(yv, INT2FIX(1));
00657
00658 if (*format == 'G') {
00659 if (FIXNUM_P(yv)) {
00660 const long y = FIX2LONG(yv);
00661 FMT('0', 0 <= y ? 4 : 5, "ld", y);
00662 }
00663 else {
00664 FMTV('0', 4, "d", yv);
00665 }
00666 }
00667 else {
00668 yv = mod(yv, INT2FIX(100));
00669 y = FIX2LONG(yv);
00670 FMT('0', 2, "ld", y);
00671 }
00672 continue;
00673 }
00674
00675 #endif
00676
00677
00678 case 'L':
00679 w = 3;
00680 goto subsec;
00681
00682 case 'N':
00683
00684
00685
00686
00687
00688
00689
00690
00691 w = 9;
00692 subsec:
00693 if (precision <= 0) {
00694 precision = w;
00695 }
00696 NEEDS(precision);
00697
00698 if (ts) {
00699 long subsec = ts->tv_nsec;
00700 if (9 < precision) {
00701 snprintf(s, endp - s, "%09ld", subsec);
00702 memset(s+9, '0', precision-9);
00703 s += precision;
00704 }
00705 else {
00706 int i;
00707 for (i = 0; i < 9-precision; i++)
00708 subsec /= 10;
00709 snprintf(s, endp - s, "%0*ld", precision, subsec);
00710 s += precision;
00711 }
00712 }
00713 else {
00714 VALUE subsec = mod(timev, INT2FIX(1));
00715 int ww;
00716 long n;
00717
00718 ww = precision;
00719 while (9 <= ww) {
00720 subsec = mul(subsec, INT2FIX(1000000000));
00721 ww -= 9;
00722 }
00723 n = 1;
00724 for (; 0 < ww; ww--)
00725 n *= 10;
00726 if (n != 1)
00727 subsec = mul(subsec, INT2FIX(n));
00728 subsec = div(subsec, INT2FIX(1));
00729
00730 if (FIXNUM_P(subsec)) {
00731 (void)snprintf(s, endp - s, "%0*ld", precision, FIX2LONG(subsec));
00732 s += precision;
00733 }
00734 else {
00735 VALUE args[2], result;
00736 args[0] = INT2FIX(precision);
00737 args[1] = subsec;
00738 result = rb_str_format(2, args, rb_str_new2("%0*d"));
00739 (void)strlcpy(s, StringValueCStr(result), endp-s);
00740 s += precision;
00741 }
00742 }
00743 continue;
00744
00745 case 'F':
00746 STRFTIME("%Y-%m-%d");
00747 continue;
00748
00749 case '-':
00750 FLAG_FOUND();
00751 flags |= BIT_OF(LEFT);
00752 padding = precision = 0;
00753 goto again;
00754
00755 case '^':
00756 FLAG_FOUND();
00757 flags |= BIT_OF(UPPER);
00758 goto again;
00759
00760 case '#':
00761 FLAG_FOUND();
00762 flags |= BIT_OF(CHCASE);
00763 goto again;
00764
00765 case '_':
00766 FLAG_FOUND();
00767 padding = ' ';
00768 goto again;
00769
00770 case ':':
00771 {
00772 size_t l = strspn(format, ":");
00773 if (l > 3 || format[l] != 'z') goto unknown;
00774 colons = (int)l;
00775 format += l - 1;
00776 }
00777 goto again;
00778
00779 case '0':
00780 padding = '0';
00781 case '1': case '2': case '3': case '4':
00782 case '5': case '6': case '7': case '8': case '9':
00783 {
00784 char *e;
00785 precision = (int)strtoul(format, &e, 10);
00786 format = e - 1;
00787 goto again;
00788 }
00789
00790 default:
00791 unknown:
00792 i = format - sp + 1;
00793 tp = sp;
00794 precision = -1;
00795 flags = 0;
00796 padding = 0;
00797 colons = 0;
00798 break;
00799 }
00800 if (i) {
00801 FILL_PADDING(i);
00802 memcpy(s, tp, i);
00803 switch (flags & (BIT_OF(UPPER)|BIT_OF(LOWER))) {
00804 case BIT_OF(UPPER):
00805 do {
00806 if (ISLOWER(*s)) *s = TOUPPER(*s);
00807 } while (s++, --i);
00808 break;
00809 case BIT_OF(LOWER):
00810 do {
00811 if (ISUPPER(*s)) *s = TOLOWER(*s);
00812 } while (s++, --i);
00813 break;
00814 default:
00815 s += i;
00816 break;
00817 }
00818 }
00819 }
00820 if (s >= endp) {
00821 goto err;
00822 }
00823 if (*format == '\0') {
00824 *s = '\0';
00825 return (s - start);
00826 } else
00827 return 0;
00828 }
00829
00830 size_t
00831 rb_strftime(char *s, size_t maxsize, const char *format, rb_encoding *enc, const struct vtm *vtm, VALUE timev, int gmt)
00832 {
00833 return rb_strftime_with_timespec(s, maxsize, format, enc, vtm, timev, NULL, gmt);
00834 }
00835
00836 size_t
00837 rb_strftime_timespec(char *s, size_t maxsize, const char *format, rb_encoding *enc, const struct vtm *vtm, struct timespec *ts, int gmt)
00838 {
00839 return rb_strftime_with_timespec(s, maxsize, format, enc, vtm, Qnil, ts, gmt);
00840 }
00841
00842
00843
00844 static int
00845 isleap(long year)
00846 {
00847 return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
00848 }
00849
00850
00851 static void
00852 vtm2tm_noyear(const struct vtm *vtm, struct tm *result)
00853 {
00854 struct tm tm;
00855
00856
00857 tm.tm_year = FIX2INT(mod(vtm->year, INT2FIX(400))) + 100;
00858
00859 tm.tm_mon = vtm->mon-1;
00860 tm.tm_mday = vtm->mday;
00861 tm.tm_hour = vtm->hour;
00862 tm.tm_min = vtm->min;
00863 tm.tm_sec = vtm->sec;
00864 tm.tm_wday = vtm->wday;
00865 tm.tm_yday = vtm->yday-1;
00866 tm.tm_isdst = vtm->isdst;
00867 #if defined(HAVE_STRUCT_TM_TM_GMTOFF)
00868 tm.tm_gmtoff = NUM2LONG(vtm->utc_offset);
00869 #endif
00870 #if defined(HAVE_TM_ZONE)
00871 tm.tm_zone = (char *)vtm->zone;
00872 #endif
00873 *result = tm;
00874 }
00875
00876 #ifdef POSIX2_DATE
00877
00878
00879 static int
00880 iso8601wknum(const struct tm *timeptr)
00881 {
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896 int weeknum, jan1day;
00897
00898
00899 weeknum = weeknumber(timeptr, 1);
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914 jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
00915 if (jan1day < 0)
00916 jan1day += 7;
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931 switch (jan1day) {
00932 case 1:
00933 break;
00934 case 2:
00935 case 3:
00936 case 4:
00937 weeknum++;
00938 break;
00939 case 5:
00940 case 6:
00941 case 0:
00942 if (weeknum == 0) {
00943 #ifdef USE_BROKEN_XPG4
00944
00945 weeknum = 53;
00946 #else
00947
00948 struct tm dec31ly;
00949 dec31ly = *timeptr;
00950 dec31ly.tm_year--;
00951 dec31ly.tm_mon = 11;
00952 dec31ly.tm_mday = 31;
00953 dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
00954 dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900L);
00955 weeknum = iso8601wknum(& dec31ly);
00956 #endif
00957 }
00958 break;
00959 }
00960
00961 if (timeptr->tm_mon == 11) {
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973 int wday, mday;
00974
00975 wday = timeptr->tm_wday;
00976 mday = timeptr->tm_mday;
00977 if ( (wday == 1 && (mday >= 29 && mday <= 31))
00978 || (wday == 2 && (mday == 30 || mday == 31))
00979 || (wday == 3 && mday == 31))
00980 weeknum = 1;
00981 }
00982
00983 return weeknum;
00984 }
00985
00986 static int
00987 iso8601wknum_v(const struct vtm *vtm)
00988 {
00989 struct tm tm;
00990 vtm2tm_noyear(vtm, &tm);
00991 return iso8601wknum(&tm);
00992 }
00993
00994 #endif
00995
00996
00997
00998
00999
01000 static int
01001 weeknumber(const struct tm *timeptr, int firstweekday)
01002 {
01003 int wday = timeptr->tm_wday;
01004 int ret;
01005
01006 if (firstweekday == 1) {
01007 if (wday == 0)
01008 wday = 6;
01009 else
01010 wday--;
01011 }
01012 ret = ((timeptr->tm_yday + 7 - wday) / 7);
01013 if (ret < 0)
01014 ret = 0;
01015 return ret;
01016 }
01017
01018 static int
01019 weeknumber_v(const struct vtm *vtm, int firstweekday)
01020 {
01021 struct tm tm;
01022 vtm2tm_noyear(vtm, &tm);
01023 return weeknumber(&tm, firstweekday);
01024 }
01025
01026 #if 0
01027
01028
01029 Date: Wed, 24 Apr 91 20:54:08 MDT
01030 From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK>
01031 To: arnold@audiofax.com
01032
01033 Hi Arnold,
01034 in a process of fixing of strftime() in libraries on Atari ST I grabbed
01035 some pieces of code from your own strftime. When doing that it came
01036 to mind that your weeknumber() function compiles a little bit nicer
01037 in the following form:
01038
01039
01040
01041 {
01042 return (timeptr->tm_yday - timeptr->tm_wday +
01043 (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
01044 }
01045 How nicer it depends on a compiler, of course, but always a tiny bit.
01046
01047 Cheers,
01048 Michal
01049 ntomczak@vm.ucs.ualberta.ca
01050 #endif
01051
01052 #ifdef TEST_STRFTIME
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081 #ifndef NULL
01082 #include <stdio.h>
01083 #endif
01084 #include <sys/time.h>
01085 #include <string.h>
01086
01087 #define MAXTIME 132
01088
01089
01090
01091
01092
01093 static char *array[] =
01094 {
01095 "(%%A) full weekday name, var length (Sunday..Saturday) %A",
01096 "(%%B) full month name, var length (January..December) %B",
01097 "(%%C) Century %C",
01098 "(%%D) date (%%m/%%d/%%y) %D",
01099 "(%%E) Locale extensions (ignored) %E",
01100 "(%%H) hour (24-hour clock, 00..23) %H",
01101 "(%%I) hour (12-hour clock, 01..12) %I",
01102 "(%%M) minute (00..59) %M",
01103 "(%%O) Locale extensions (ignored) %O",
01104 "(%%R) time, 24-hour (%%H:%%M) %R",
01105 "(%%S) second (00..60) %S",
01106 "(%%T) time, 24-hour (%%H:%%M:%%S) %T",
01107 "(%%U) week of year, Sunday as first day of week (00..53) %U",
01108 "(%%V) week of year according to ISO 8601 %V",
01109 "(%%W) week of year, Monday as first day of week (00..53) %W",
01110 "(%%X) appropriate locale time representation (%H:%M:%S) %X",
01111 "(%%Y) year with century (1970...) %Y",
01112 "(%%Z) timezone (EDT), or blank if timezone not determinable %Z",
01113 "(%%a) locale's abbreviated weekday name (Sun..Sat) %a",
01114 "(%%b) locale's abbreviated month name (Jan..Dec) %b",
01115 "(%%c) full date (Sat Nov 4 12:02:33 1989)%n%t%t%t %c",
01116 "(%%d) day of the month (01..31) %d",
01117 "(%%e) day of the month, blank-padded ( 1..31) %e",
01118 "(%%h) should be same as (%%b) %h",
01119 "(%%j) day of the year (001..366) %j",
01120 "(%%k) hour, 24-hour clock, blank pad ( 0..23) %k",
01121 "(%%l) hour, 12-hour clock, blank pad ( 1..12) %l",
01122 "(%%m) month (01..12) %m",
01123 "(%%p) locale's AM or PM based on 12-hour clock %p",
01124 "(%%r) time, 12-hour (same as %%I:%%M:%%S %%p) %r",
01125 "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7] %u",
01126 "(%%v) VMS date (dd-bbb-YYYY) %v",
01127 "(%%w) day of week (0..6, Sunday == 0) %w",
01128 "(%%x) appropriate locale date representation %x",
01129 "(%%y) last two digits of year (00..99) %y",
01130 "(%%z) timezone offset east of GMT as HHMM (e.g. -0500) %z",
01131 (char *) NULL
01132 };
01133
01134
01135
01136 int
01137 main(int argc, char **argv)
01138 {
01139 long time();
01140
01141 char *next;
01142 char string[MAXTIME];
01143
01144 int k;
01145 int length;
01146
01147 struct tm *tm;
01148
01149 long clock;
01150
01151
01152
01153 clock = time((long *) 0);
01154 tm = localtime(&clock);
01155
01156 for (k = 0; next = array[k]; k++) {
01157 length = strftime(string, MAXTIME, next, tm);
01158 printf("%s\n", string);
01159 }
01160
01161 exit(0);
01162 }
01163 #endif
01164