[BACK]Return to strftime.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libc / time

Annotation of src/lib/libc/time/strftime.c, Revision 1.37

1.37    ! christos    1: /*     $NetBSD: strftime.c,v 1.36 2016/03/15 15:16:01 christos Exp $   */
1.36      christos    2:
                      3: /* Convert a broken-down time stamp to a string.  */
                      4:
                      5: /* Copyright 1989 The Regents of the University of California.
                      6:    All rights reserved.
                      7:
                      8:    Redistribution and use in source and binary forms, with or without
                      9:    modification, are permitted provided that the following conditions
                     10:    are met:
                     11:    1. Redistributions of source code must retain the above copyright
                     12:       notice, this list of conditions and the following disclaimer.
                     13:    2. Redistributions in binary form must reproduce the above copyright
                     14:       notice, this list of conditions and the following disclaimer in the
                     15:       documentation and/or other materials provided with the distribution.
                     16:    3. Neither the name of the University nor the names of its contributors
                     17:       may be used to endorse or promote products derived from this software
                     18:       without specific prior written permission.
                     19:
                     20:    THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
                     21:    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     22:    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     23:    ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     24:    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     25:    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     26:    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     27:    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     28:    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     29:    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     30:    SUCH DAMAGE.  */
1.1       mrg        31:
1.3       christos   32: #include <sys/cdefs.h>
1.1       mrg        33: #if defined(LIBC_SCCS) && !defined(lint)
1.3       christos   34: #if 0
1.13      kleink     35: static char    elsieid[] = "@(#)strftime.c     7.64";
1.20      mlelstv    36: static char    elsieid[] = "@(#)strftime.c     8.3";
1.3       christos   37: #else
1.37    ! christos   38: __RCSID("$NetBSD: strftime.c,v 1.36 2016/03/15 15:16:01 christos Exp $");
1.3       christos   39: #endif
1.1       mrg        40: #endif /* LIBC_SCCS and not lint */
                     41:
1.4       jtc        42: #include "namespace.h"
1.12      kleink     43:
1.25      joerg      44: #include <stddef.h>
1.30      christos   45: #include <assert.h>
1.25      joerg      46: #include <locale.h>
                     47: #include "setlocale_local.h"
                     48:
1.12      kleink     49: /*
1.36      christos   50: ** Based on the UCB version with the copyright notice appearing above.
1.24      christos   51: **
1.12      kleink     52: ** This is ANSIish only when "multibyte character == plain character".
                     53: */
                     54:
1.11      taca       55: #include "private.h"
1.12      kleink     56:
1.16      kleink     57: /*
                     58: ** We don't use these extensions in strftime operation even when
                     59: ** supported by the local tzcode configuration.  A strictly
                     60: ** conforming C application may leave them in undefined state.
                     61: */
                     62:
1.15      kleink     63: #ifdef _LIBC
                     64: #undef TM_ZONE
1.16      kleink     65: #undef TM_GMTOFF
1.15      kleink     66: #endif
                     67:
1.12      kleink     68: #include "tzfile.h"
                     69: #include "fcntl.h"
                     70: #include "locale.h"
                     71:
1.21      christos   72: #ifdef __weak_alias
1.25      joerg      73: __weak_alias(strftime_l, _strftime_l)
                     74: __weak_alias(strftime_lz, _strftime_lz)
1.21      christos   75: __weak_alias(strftime_z, _strftime_z)
                     76: #endif
                     77:
1.12      kleink     78: #include "sys/localedef.h"
1.25      joerg      79: #define _TIME_LOCALE(loc) \
                     80:     ((_TimeLocale *)((loc)->part_impl[(size_t)LC_TIME]))
1.20      mlelstv    81: #define c_fmt   d_t_fmt
1.12      kleink     82:
1.20      mlelstv    83: static char *  _add(const char *, char *, const char *);
                     84: static char *  _conv(int, const char *, char *, const char *);
1.21      christos   85: static char *  _fmt(const timezone_t, const char *, const struct tm *, char *,
1.25      joerg      86:                        const char *, int *, locale_t);
1.33      christos   87: static char *  _yconv(int, int, bool, bool, char *, const char *);
1.12      kleink     88:
1.36      christos   89: #if !HAVE_POSIX_DECLS
1.20      mlelstv    90: extern char *  tzname[];
1.36      christos   91: #endif
1.12      kleink     92:
                     93: #ifndef YEAR_2000_NAME
                     94: #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
                     95: #endif /* !defined YEAR_2000_NAME */
                     96:
                     97: #define IN_NONE        0
                     98: #define IN_SOME        1
                     99: #define IN_THIS        2
                    100: #define IN_ALL 3
1.1       mrg       101:
                    102: size_t
1.25      joerg     103: strftime_z(const timezone_t sp, char * __restrict s, size_t maxsize,
                    104:     const char * __restrict format, const struct tm * __restrict t)
                    105: {
1.26      joerg     106:        return strftime_lz(sp, s, maxsize, format, t, _current_locale());
1.25      joerg     107: }
                    108:
1.33      christos  109: #if HAVE_STRFTIME_L
                    110: size_t
                    111: strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
                    112:           locale_t locale)
                    113: {
                    114:   /* Just call strftime, as only the C locale is supported.  */
                    115:   return strftime(s, maxsize, format, t);
                    116: }
                    117: #endif
                    118:
1.25      joerg     119: size_t
                    120: strftime_lz(const timezone_t sp, char *const s, const size_t maxsize,
                    121:     const char *const format, const struct tm *const t, locale_t loc)
1.1       mrg       122: {
1.12      kleink    123:        char *  p;
                    124:        int     warn;
1.5       kleink    125:
1.12      kleink    126:        warn = IN_NONE;
1.37    ! christos  127:        p = _fmt(sp, format, t, s, s + maxsize, &warn, loc);
1.12      kleink    128: #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
                    129:        if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
                    130:                (void) fprintf(stderr, "\n");
1.37    ! christos  131:                (void) fprintf(stderr, "strftime format \"%s\" ", format);
1.12      kleink    132:                (void) fprintf(stderr, "yields only two digits of years in ");
                    133:                if (warn == IN_SOME)
                    134:                        (void) fprintf(stderr, "some locales");
                    135:                else if (warn == IN_THIS)
                    136:                        (void) fprintf(stderr, "the current locale");
                    137:                else    (void) fprintf(stderr, "all locales");
                    138:                (void) fprintf(stderr, "\n");
                    139:        }
                    140: #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
                    141:        if (p == s + maxsize)
                    142:                return 0;
                    143:        *p = '\0';
                    144:        return p - s;
1.1       mrg       145: }
                    146:
1.12      kleink    147: static char *
1.34      christos  148: _fmt(const timezone_t sp, const char *format, const struct tm *t, char *pt,
                    149:      const char *ptlim, int *warnp, locale_t loc)
1.1       mrg       150: {
1.12      kleink    151:        for ( ; *format; ++format) {
1.1       mrg       152:                if (*format == '%') {
1.12      kleink    153: label:
                    154:                        switch (*++format) {
1.1       mrg       155:                        case '\0':
                    156:                                --format;
                    157:                                break;
                    158:                        case 'A':
1.12      kleink    159:                                pt = _add((t->tm_wday < 0 ||
                    160:                                        t->tm_wday >= DAYSPERWEEK) ?
1.25      joerg     161:                                        "?" : _TIME_LOCALE(loc)->day[t->tm_wday],
1.12      kleink    162:                                        pt, ptlim);
1.1       mrg       163:                                continue;
                    164:                        case 'a':
1.12      kleink    165:                                pt = _add((t->tm_wday < 0 ||
                    166:                                        t->tm_wday >= DAYSPERWEEK) ?
1.25      joerg     167:                                        "?" : _TIME_LOCALE(loc)->abday[t->tm_wday],
1.12      kleink    168:                                        pt, ptlim);
1.1       mrg       169:                                continue;
                    170:                        case 'B':
1.12      kleink    171:                                pt = _add((t->tm_mon < 0 ||
                    172:                                        t->tm_mon >= MONSPERYEAR) ?
1.25      joerg     173:                                        "?" : _TIME_LOCALE(loc)->mon[t->tm_mon],
1.12      kleink    174:                                        pt, ptlim);
1.1       mrg       175:                                continue;
                    176:                        case 'b':
                    177:                        case 'h':
1.12      kleink    178:                                pt = _add((t->tm_mon < 0 ||
                    179:                                        t->tm_mon >= MONSPERYEAR) ?
1.25      joerg     180:                                        "?" : _TIME_LOCALE(loc)->abmon[t->tm_mon],
1.12      kleink    181:                                        pt, ptlim);
1.1       mrg       182:                                continue;
                    183:                        case 'C':
1.12      kleink    184:                                /*
                    185:                                ** %C used to do a...
                    186:                                **      _fmt("%a %b %e %X %Y", t);
                    187:                                ** ...whereas now POSIX 1003.2 calls for
                    188:                                ** something completely different.
                    189:                                ** (ado, 1993-05-24)
                    190:                                */
1.33      christos  191:                                pt = _yconv(t->tm_year, TM_YEAR_BASE,
                    192:                                            true, false, pt, ptlim);
1.1       mrg       193:                                continue;
                    194:                        case 'c':
1.12      kleink    195:                                {
                    196:                                int warn2 = IN_SOME;
                    197:
1.25      joerg     198:                                pt = _fmt(sp, _TIME_LOCALE(loc)->c_fmt, t, pt,
                    199:                                    ptlim, &warn2, loc);
1.12      kleink    200:                                if (warn2 == IN_ALL)
                    201:                                        warn2 = IN_THIS;
                    202:                                if (warn2 > *warnp)
                    203:                                        *warnp = warn2;
                    204:                                }
1.1       mrg       205:                                continue;
                    206:                        case 'D':
1.25      joerg     207:                                pt = _fmt(sp, "%m/%d/%y", t, pt, ptlim, warnp,
                    208:                                    loc);
1.1       mrg       209:                                continue;
                    210:                        case 'd':
1.12      kleink    211:                                pt = _conv(t->tm_mday, "%02d", pt, ptlim);
1.1       mrg       212:                                continue;
1.12      kleink    213:                        case 'E':
                    214:                        case 'O':
                    215:                                /*
                    216:                                ** C99 locale modifiers.
                    217:                                ** The sequences
                    218:                                **      %Ec %EC %Ex %EX %Ey %EY
                    219:                                **      %Od %oe %OH %OI %Om %OM
                    220:                                **      %OS %Ou %OU %OV %Ow %OW %Oy
                    221:                                ** are supposed to provide alternate
                    222:                                ** representations.
                    223:                                */
                    224:                                goto label;
1.1       mrg       225:                        case 'e':
1.12      kleink    226:                                pt = _conv(t->tm_mday, "%2d", pt, ptlim);
1.10      kleink    227:                                continue;
                    228:                        case 'F':
1.25      joerg     229:                                pt = _fmt(sp, "%Y-%m-%d", t, pt, ptlim, warnp,
                    230:                                    loc);
1.1       mrg       231:                                continue;
                    232:                        case 'H':
1.12      kleink    233:                                pt = _conv(t->tm_hour, "%02d", pt, ptlim);
1.1       mrg       234:                                continue;
                    235:                        case 'I':
1.12      kleink    236:                                pt = _conv((t->tm_hour % 12) ?
                    237:                                        (t->tm_hour % 12) : 12,
                    238:                                        "%02d", pt, ptlim);
1.1       mrg       239:                                continue;
                    240:                        case 'j':
1.12      kleink    241:                                pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
1.1       mrg       242:                                continue;
                    243:                        case 'k':
1.12      kleink    244:                                /*
                    245:                                ** This used to be...
                    246:                                **      _conv(t->tm_hour % 12 ?
                    247:                                **              t->tm_hour % 12 : 12, 2, ' ');
                    248:                                ** ...and has been changed to the below to
                    249:                                ** match SunOS 4.1.1 and Arnold Robbins'
1.20      mlelstv   250:                                ** strftime version 3.0. That is, "%k" and
1.12      kleink    251:                                ** "%l" have been swapped.
                    252:                                ** (ado, 1993-05-24)
                    253:                                */
                    254:                                pt = _conv(t->tm_hour, "%2d", pt, ptlim);
                    255:                                continue;
                    256: #ifdef KITCHEN_SINK
                    257:                        case 'K':
                    258:                                /*
                    259:                                ** After all this time, still unclaimed!
                    260:                                */
                    261:                                pt = _add("kitchen sink", pt, ptlim);
1.1       mrg       262:                                continue;
1.12      kleink    263: #endif /* defined KITCHEN_SINK */
1.1       mrg       264:                        case 'l':
1.12      kleink    265:                                /*
                    266:                                ** This used to be...
                    267:                                **      _conv(t->tm_hour, 2, ' ');
                    268:                                ** ...and has been changed to the below to
                    269:                                ** match SunOS 4.1.1 and Arnold Robbin's
1.20      mlelstv   270:                                ** strftime version 3.0. That is, "%k" and
1.12      kleink    271:                                ** "%l" have been swapped.
                    272:                                ** (ado, 1993-05-24)
                    273:                                */
                    274:                                pt = _conv((t->tm_hour % 12) ?
                    275:                                        (t->tm_hour % 12) : 12,
                    276:                                        "%2d", pt, ptlim);
1.1       mrg       277:                                continue;
                    278:                        case 'M':
1.12      kleink    279:                                pt = _conv(t->tm_min, "%02d", pt, ptlim);
1.1       mrg       280:                                continue;
                    281:                        case 'm':
1.12      kleink    282:                                pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
1.1       mrg       283:                                continue;
                    284:                        case 'n':
1.12      kleink    285:                                pt = _add("\n", pt, ptlim);
1.1       mrg       286:                                continue;
                    287:                        case 'p':
1.12      kleink    288:                                pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
1.25      joerg     289:                                        _TIME_LOCALE(loc)->am_pm[1] :
                    290:                                        _TIME_LOCALE(loc)->am_pm[0],
1.12      kleink    291:                                        pt, ptlim);
1.1       mrg       292:                                continue;
                    293:                        case 'R':
1.25      joerg     294:                                pt = _fmt(sp, "%H:%M", t, pt, ptlim, warnp,
                    295:                                    loc);
1.1       mrg       296:                                continue;
                    297:                        case 'r':
1.25      joerg     298:                                pt = _fmt(sp, _TIME_LOCALE(loc)->t_fmt_ampm, t,
                    299:                                    pt, ptlim, warnp, loc);
1.1       mrg       300:                                continue;
                    301:                        case 'S':
1.12      kleink    302:                                pt = _conv(t->tm_sec, "%02d", pt, ptlim);
1.1       mrg       303:                                continue;
                    304:                        case 's':
1.12      kleink    305:                                {
                    306:                                        struct tm       tm;
                    307:                                        char            buf[INT_STRLEN_MAXIMUM(
                    308:                                                                time_t) + 1];
                    309:                                        time_t          mkt;
                    310:
                    311:                                        tm = *t;
                    312:                                        mkt = mktime(&tm);
                    313:                                        /* CONSTCOND */
                    314:                                        if (TYPE_SIGNED(time_t))
1.27      christos  315:                                                (void)snprintf(buf, sizeof(buf),
                    316:                                                    "%jd", (intmax_t) mkt);
                    317:                                        else    (void)snprintf(buf, sizeof(buf),
                    318:                                                    "%ju", (uintmax_t) mkt);
1.12      kleink    319:                                        pt = _add(buf, pt, ptlim);
                    320:                                }
1.1       mrg       321:                                continue;
                    322:                        case 'T':
1.25      joerg     323:                                pt = _fmt(sp, "%H:%M:%S", t, pt, ptlim, warnp,
                    324:                                    loc);
1.1       mrg       325:                                continue;
                    326:                        case 't':
1.12      kleink    327:                                pt = _add("\t", pt, ptlim);
1.1       mrg       328:                                continue;
                    329:                        case 'U':
1.12      kleink    330:                                pt = _conv((t->tm_yday + DAYSPERWEEK -
                    331:                                        t->tm_wday) / DAYSPERWEEK,
                    332:                                        "%02d", pt, ptlim);
1.1       mrg       333:                                continue;
                    334:                        case 'u':
1.12      kleink    335:                                /*
                    336:                                ** From Arnold Robbins' strftime version 3.0:
                    337:                                ** "ISO 8601: Weekday as a decimal number
                    338:                                ** [1 (Monday) - 7]"
                    339:                                ** (ado, 1993-05-24)
                    340:                                */
                    341:                                pt = _conv((t->tm_wday == 0) ?
                    342:                                        DAYSPERWEEK : t->tm_wday,
                    343:                                        "%d", pt, ptlim);
1.1       mrg       344:                                continue;
1.8       augustss  345:                        case 'V':       /* ISO 8601 week number */
                    346:                        case 'G':       /* ISO 8601 year (four digits) */
                    347:                        case 'g':       /* ISO 8601 year (two digits) */
                    348: /*
1.20      mlelstv   349: ** From Arnold Robbins' strftime version 3.0: "the week number of the
1.8       augustss  350: ** year (the first Monday as the first day of week 1) as a decimal number
                    351: ** (01-53)."
                    352: ** (ado, 1993-05-24)
                    353: **
1.31      christos  354: ** From <http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html> by Markus Kuhn:
1.8       augustss  355: ** "Week 01 of a year is per definition the first week which has the
                    356: ** Thursday in this year, which is equivalent to the week which contains
                    357: ** the fourth day of January. In other words, the first week of a new year
                    358: ** is the week which has the majority of its days in the new year. Week 01
                    359: ** might also contain days from the previous year and the week before week
                    360: ** 01 of a year is the last week (52 or 53) of the previous year even if
                    361: ** it contains days from the new year. A week starts with Monday (day 1)
1.20      mlelstv   362: ** and ends with Sunday (day 7). For example, the first week of the year
1.8       augustss  363: ** 1997 lasts from 1996-12-30 to 1997-01-05..."
                    364: ** (ado, 1996-01-02)
                    365: */
1.1       mrg       366:                                {
1.8       augustss  367:                                        int     year;
1.20      mlelstv   368:                                        int     base;
1.8       augustss  369:                                        int     yday;
                    370:                                        int     wday;
                    371:                                        int     w;
                    372:
1.20      mlelstv   373:                                        year = t->tm_year;
                    374:                                        base = TM_YEAR_BASE;
1.8       augustss  375:                                        yday = t->tm_yday;
                    376:                                        wday = t->tm_wday;
                    377:                                        for ( ; ; ) {
                    378:                                                int     len;
                    379:                                                int     bot;
                    380:                                                int     top;
                    381:
1.20      mlelstv   382:                                                len = isleap_sum(year, base) ?
1.8       augustss  383:                                                        DAYSPERLYEAR :
                    384:                                                        DAYSPERNYEAR;
                    385:                                                /*
                    386:                                                ** What yday (-3 ... 3) does
                    387:                                                ** the ISO year begin on?
                    388:                                                */
                    389:                                                bot = ((yday + 11 - wday) %
                    390:                                                        DAYSPERWEEK) - 3;
                    391:                                                /*
                    392:                                                ** What yday does the NEXT
                    393:                                                ** ISO year begin on?
                    394:                                                */
                    395:                                                top = bot -
                    396:                                                        (len % DAYSPERWEEK);
                    397:                                                if (top < -3)
                    398:                                                        top += DAYSPERWEEK;
                    399:                                                top += len;
                    400:                                                if (yday >= top) {
1.20      mlelstv   401:                                                        ++base;
1.8       augustss  402:                                                        w = 1;
                    403:                                                        break;
                    404:                                                }
                    405:                                                if (yday >= bot) {
                    406:                                                        w = 1 + ((yday - bot) /
                    407:                                                                DAYSPERWEEK);
                    408:                                                        break;
                    409:                                                }
1.20      mlelstv   410:                                                --base;
                    411:                                                yday += isleap_sum(year, base) ?
1.8       augustss  412:                                                        DAYSPERLYEAR :
                    413:                                                        DAYSPERNYEAR;
                    414:                                        }
                    415: #ifdef XPG4_1994_04_09
1.20      mlelstv   416:                                        if ((w == 52 &&
                    417:                                                t->tm_mon == TM_JANUARY) ||
                    418:                                                (w == 1 &&
                    419:                                                t->tm_mon == TM_DECEMBER))
                    420:                                                        w = 53;
1.8       augustss  421: #endif /* defined XPG4_1994_04_09 */
1.12      kleink    422:                                        if (*format == 'V')
                    423:                                                pt = _conv(w, "%02d",
                    424:                                                        pt, ptlim);
                    425:                                        else if (*format == 'g') {
                    426:                                                *warnp = IN_ALL;
1.33      christos  427:                                                pt = _yconv(year, base,
                    428:                                                        false, true,
1.12      kleink    429:                                                        pt, ptlim);
1.33      christos  430:                                        } else  pt = _yconv(year, base,
                    431:                                                        true, true,
1.12      kleink    432:                                                        pt, ptlim);
1.1       mrg       433:                                }
                    434:                                continue;
1.12      kleink    435:                        case 'v':
                    436:                                /*
                    437:                                ** From Arnold Robbins' strftime version 3.0:
                    438:                                ** "date as dd-bbb-YYYY"
                    439:                                ** (ado, 1993-05-24)
                    440:                                */
1.25      joerg     441:                                pt = _fmt(sp, "%e-%b-%Y", t, pt, ptlim, warnp,
                    442:                                    loc);
1.12      kleink    443:                                continue;
1.1       mrg       444:                        case 'W':
1.12      kleink    445:                                pt = _conv((t->tm_yday + DAYSPERWEEK -
                    446:                                        (t->tm_wday ?
                    447:                                        (t->tm_wday - 1) :
                    448:                                        (DAYSPERWEEK - 1))) / DAYSPERWEEK,
                    449:                                        "%02d", pt, ptlim);
1.1       mrg       450:                                continue;
                    451:                        case 'w':
1.12      kleink    452:                                pt = _conv(t->tm_wday, "%d", pt, ptlim);
                    453:                                continue;
                    454:                        case 'X':
1.25      joerg     455:                                pt = _fmt(sp, _TIME_LOCALE(loc)->t_fmt, t, pt,
                    456:                                    ptlim, warnp, loc);
1.1       mrg       457:                                continue;
                    458:                        case 'x':
1.12      kleink    459:                                {
                    460:                                int     warn2 = IN_SOME;
                    461:
1.25      joerg     462:                                pt = _fmt(sp, _TIME_LOCALE(loc)->d_fmt, t, pt,
                    463:                                    ptlim, &warn2, loc);
1.12      kleink    464:                                if (warn2 == IN_ALL)
                    465:                                        warn2 = IN_THIS;
                    466:                                if (warn2 > *warnp)
                    467:                                        *warnp = warn2;
                    468:                                }
1.1       mrg       469:                                continue;
                    470:                        case 'y':
1.12      kleink    471:                                *warnp = IN_ALL;
1.33      christos  472:                                pt = _yconv(t->tm_year, TM_YEAR_BASE,
                    473:                                        false, true,
1.20      mlelstv   474:                                        pt, ptlim);
1.1       mrg       475:                                continue;
                    476:                        case 'Y':
1.33      christos  477:                                pt = _yconv(t->tm_year, TM_YEAR_BASE,
                    478:                                        true, true,
1.12      kleink    479:                                        pt, ptlim);
1.1       mrg       480:                                continue;
                    481:                        case 'Z':
1.11      taca      482: #ifdef TM_ZONE
1.35      christos  483:                                pt = _add(t->TM_ZONE, pt, ptlim);
1.12      kleink    484: #endif /* defined TM_ZONE */
                    485:                                if (t->tm_isdst >= 0)
1.21      christos  486:                                        pt = _add((sp ?
                    487:                                            tzgetname(sp, t->tm_isdst) :
                    488:                                            tzname[t->tm_isdst != 0]),
                    489:                                            pt, ptlim);
1.12      kleink    490:                                /*
                    491:                                ** C99 says that %Z must be replaced by the
                    492:                                ** empty string if the time zone is not
                    493:                                ** determinable.
                    494:                                */
                    495:                                continue;
                    496:                        case 'z':
                    497:                                {
1.30      christos  498:                                long            diff;
1.12      kleink    499:                                char const *    sign;
                    500:
                    501:                                if (t->tm_isdst < 0)
                    502:                                        continue;
                    503: #ifdef TM_GMTOFF
                    504:                                diff = (int)t->TM_GMTOFF;
                    505: #else /* !defined TM_GMTOFF */
                    506:                                /*
1.29      christos  507:                                ** C99 says that the UT offset must
1.12      kleink    508:                                ** be computed by looking only at
1.20      mlelstv   509:                                ** tm_isdst. This requirement is
1.12      kleink    510:                                ** incorrect, since it means the code
                    511:                                ** must rely on magic (in this case
                    512:                                ** altzone and timezone), and the
                    513:                                ** magic might not have the correct
1.20      mlelstv   514:                                ** offset. Doing things correctly is
1.12      kleink    515:                                ** tricky and requires disobeying C99;
                    516:                                ** see GNU C strftime for details.
                    517:                                ** For now, punt and conform to the
                    518:                                ** standard, even though it's incorrect.
                    519:                                **
                    520:                                ** C99 says that %z must be replaced by the
                    521:                                ** empty string if the time zone is not
                    522:                                ** determinable, so output nothing if the
                    523:                                ** appropriate variables are not available.
                    524:                                */
1.16      kleink    525: #ifndef STD_INSPIRED
1.12      kleink    526:                                if (t->tm_isdst == 0)
                    527: #ifdef USG_COMPAT
                    528:                                        diff = -timezone;
1.13      kleink    529: #else /* !defined USG_COMPAT */
1.12      kleink    530:                                        continue;
                    531: #endif /* !defined USG_COMPAT */
                    532:                                else
                    533: #ifdef ALTZONE
                    534:                                        diff = -altzone;
                    535: #else /* !defined ALTZONE */
                    536:                                        continue;
                    537: #endif /* !defined ALTZONE */
1.16      kleink    538: #else /* defined STD_INSPIRED */
                    539:                                {
                    540:                                        struct tm tmp;
                    541:                                        time_t lct, gct;
                    542:
                    543:                                        /*
                    544:                                        ** Get calendar time from t
                    545:                                        ** being treated as local.
                    546:                                        */
                    547:                                        tmp = *t; /* mktime discards const */
                    548:                                        lct = mktime(&tmp);
                    549:
                    550:                                        if (lct == (time_t)-1)
                    551:                                                continue;
                    552:
                    553:                                        /*
                    554:                                        ** Get calendar time from t
                    555:                                        ** being treated as GMT.
                    556:                                        **/
                    557:                                        tmp = *t; /* mktime discards const */
                    558:                                        gct = timegm(&tmp);
                    559:
                    560:                                        if (gct == (time_t)-1)
                    561:                                                continue;
                    562:
                    563:                                        /* LINTED difference will fit int */
                    564:                                        diff = (intmax_t)gct - (intmax_t)lct;
                    565:                                }
                    566: #endif /* defined STD_INSPIRED */
1.12      kleink    567: #endif /* !defined TM_GMTOFF */
                    568:                                if (diff < 0) {
                    569:                                        sign = "-";
                    570:                                        diff = -diff;
                    571:                                } else  sign = "+";
                    572:                                pt = _add(sign, pt, ptlim);
1.20      mlelstv   573:                                diff /= SECSPERMIN;
                    574:                                diff = (diff / MINSPERHOUR) * 100 +
                    575:                                        (diff % MINSPERHOUR);
1.30      christos  576:                                _DIAGASSERT(__type_fit(int, diff));
                    577:                                pt = _conv((int)diff, "%04d", pt, ptlim);
1.12      kleink    578:                                }
1.1       mrg       579:                                continue;
1.12      kleink    580: #if 0
                    581:                        case '+':
1.25      joerg     582:                                pt = _fmt(sp, _TIME_LOCALE(loc)->date_fmt, t,
                    583:                                    pt, ptlim, warnp, loc);
1.12      kleink    584:                                continue;
                    585: #endif
1.1       mrg       586:                        case '%':
                    587:                        /*
1.12      kleink    588:                        ** X311J/88-090 (4.12.3.5): if conversion char is
1.20      mlelstv   589:                        ** undefined, behavior is undefined. Print out the
1.12      kleink    590:                        ** character itself as printf(3) also does.
                    591:                        */
1.1       mrg       592:                        default:
                    593:                                break;
                    594:                        }
                    595:                }
1.12      kleink    596:                if (pt == ptlim)
                    597:                        break;
                    598:                *pt++ = *format;
1.1       mrg       599:        }
1.12      kleink    600:        return pt;
1.1       mrg       601: }
                    602:
1.21      christos  603: size_t
1.34      christos  604: strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
1.21      christos  605: {
                    606:        tzset();
                    607:        return strftime_z(NULL, s, maxsize, format, t);
                    608: }
                    609:
1.25      joerg     610: size_t
                    611: strftime_l(char * __restrict s, size_t maxsize, const char * __restrict format,
                    612:     const struct tm * __restrict t, locale_t loc)
                    613: {
                    614:        tzset();
                    615:        return strftime_lz(NULL, s, maxsize, format, t, loc);
                    616: }
                    617:
1.12      kleink    618: static char *
1.34      christos  619: _conv(int n, const char *format, char *pt, const char *ptlim)
1.1       mrg       620: {
1.12      kleink    621:        char    buf[INT_STRLEN_MAXIMUM(int) + 1];
1.1       mrg       622:
1.19      christos  623:        (void) snprintf(buf, sizeof(buf), format, n);
1.12      kleink    624:        return _add(buf, pt, ptlim);
1.1       mrg       625: }
                    626:
1.12      kleink    627: static char *
1.34      christos  628: _add(const char *str, char *pt, const char *ptlim)
1.1       mrg       629: {
1.12      kleink    630:        while (pt < ptlim && (*pt = *str++) != '\0')
                    631:                ++pt;
                    632:        return pt;
1.1       mrg       633: }
1.20      mlelstv   634:
                    635: /*
                    636: ** POSIX and the C Standard are unclear or inconsistent about
                    637: ** what %C and %y do if the year is negative or exceeds 9999.
                    638: ** Use the convention that %C concatenated with %y yields the
                    639: ** same output as %Y, and that %Y contains at least 4 bytes,
                    640: ** with more only if necessary.
                    641: */
                    642:
                    643: static char *
1.33      christos  644: _yconv(int a, int b, bool convert_top, bool convert_yy,
1.34      christos  645:     char *pt, const char * ptlim)
1.20      mlelstv   646: {
1.28      christos  647:        int     lead;
                    648:        int     trail;
1.20      mlelstv   649:
                    650: #define DIVISOR        100
                    651:        trail = a % DIVISOR + b % DIVISOR;
                    652:        lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
                    653:        trail %= DIVISOR;
                    654:        if (trail < 0 && lead > 0) {
                    655:                trail += DIVISOR;
                    656:                --lead;
                    657:        } else if (lead < 0 && trail > 0) {
                    658:                trail -= DIVISOR;
                    659:                ++lead;
                    660:        }
                    661:        if (convert_top) {
                    662:                if (lead == 0 && trail < 0)
                    663:                        pt = _add("-0", pt, ptlim);
                    664:                else    pt = _conv(lead, "%02d", pt, ptlim);
                    665:        }
                    666:        if (convert_yy)
                    667:                pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
                    668:        return pt;
                    669: }

CVSweb <webmaster@jp.NetBSD.org>