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 FMT('0', 3, "d", vtm->yday);
00367 continue;
00368
00369 case 'm':
00370 i = range(1, vtm->mon, 12);
00371 FMT('0', 2, "d", (int)i);
00372 continue;
00373
00374 case 'M':
00375 i = range(0, vtm->min, 59);
00376 FMT('0', 2, "d", (int)i);
00377 continue;
00378
00379 case 'p':
00380 case 'P':
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':
00409 i = range(0, vtm->sec, 60);
00410 FMT('0', 2, "d", (int)i);
00411 continue;
00412
00413 case 'U':
00414 FMT('0', 2, "d", weeknumber_v(vtm, 0));
00415 continue;
00416
00417 case 'w':
00418 i = range(0, vtm->wday, 6);
00419 FMT('0', 1, "d", (int)i);
00420 continue;
00421
00422 case 'W':
00423 FMT('0', 2, "d", weeknumber_v(vtm, 1));
00424 continue;
00425
00426 case 'x':
00427 STRFTIME("%m/%d/%y");
00428 continue;
00429
00430 case 'X':
00431 STRFTIME("%H:%M:%S");
00432 continue;
00433
00434 case 'y':
00435 i = NUM2INT(mod(vtm->year, INT2FIX(100)));
00436 FMT('0', 2, "d", (int)i);
00437 continue;
00438
00439 case 'Y':
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':
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:
00465 precision = precision <= 5 ? 2 : precision-3;
00466 NEEDS(precision + 3);
00467 break;
00468
00469 case 1:
00470 precision = precision <= 6 ? 2 : precision-4;
00471 NEEDS(precision + 4);
00472 break;
00473
00474 case 2:
00475 precision = precision <= 9 ? 2 : precision-7;
00476 NEEDS(precision + 7);
00477 break;
00478
00479 case 3:
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
00524
00525 case 'Z':
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':
00557 FILL_PADDING(1);
00558 *s++ = '\n';
00559 continue;
00560
00561 case 't':
00562 FILL_PADDING(1);
00563 *s++ = '\t';
00564 continue;
00565
00566 case 'D':
00567 STRFTIME("%m/%d/%y");
00568 continue;
00569
00570 case 'e':
00571 FMT(' ', 2, "d", range(1, vtm->mday, 31));
00572 continue;
00573
00574 case 'r':
00575 STRFTIME("%I:%M:%S %p");
00576 continue;
00577
00578 case 'R':
00579 STRFTIME("%H:%M");
00580 continue;
00581
00582 case 'T':
00583 STRFTIME("%H:%M:%S");
00584 continue;
00585 #endif
00586
00587 #ifdef SUNOS_EXT
00588 case 'k':
00589 i = range(0, vtm->hour, 23);
00590 FMT(' ', 2, "d", (int)i);
00591 continue;
00592
00593 case 'l':
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':
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
00618 if (!format[1] || !strchr("cCxXyY", format[1]))
00619 goto unknown;
00620 goto again;
00621 case 'O':
00622
00623 if (!format[1] || !strchr("deHkIlmMSuUVwWy", format[1]))
00624 goto unknown;
00625 goto again;
00626
00627 case 'V':
00628 FMT('0', 2, "d", iso8601wknum_v(vtm));
00629 continue;
00630
00631 case 'u':
00632
00633 FMT('0', 1, "d", vtm->wday == 0 ? 7 : vtm->wday);
00634 continue;
00635 #endif
00636
00637 #ifdef ISO_DATE_EXT
00638 case 'G':
00639 case 'g':
00640
00641
00642
00643
00644
00645
00646
00647
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
00675
00676
00677 case 'L':
00678 w = 3;
00679 goto subsec;
00680
00681 case 'N':
00682
00683
00684
00685
00686
00687
00688
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':
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
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
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
00877
00878 static int
00879 iso8601wknum(const struct tm *timeptr)
00880 {
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895 int weeknum, jan1day;
00896
00897
00898 weeknum = weeknumber(timeptr, 1);
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913 jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
00914 if (jan1day < 0)
00915 jan1day += 7;
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930 switch (jan1day) {
00931 case 1:
00932 break;
00933 case 2:
00934 case 3:
00935 case 4:
00936 weeknum++;
00937 break;
00938 case 5:
00939 case 6:
00940 case 0:
00941 if (weeknum == 0) {
00942 #ifdef USE_BROKEN_XPG4
00943
00944 weeknum = 53;
00945 #else
00946
00947 struct tm dec31ly;
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
00963
00964
00965
00966
00967
00968
00969
00970
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
00996
00997
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)
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
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
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
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 #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
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
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
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
01163