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