12708Swollman/* 22708Swollman * Copyright (c) 1989 The Regents of the University of California. 32708Swollman * All rights reserved. 42708Swollman * 5227753Stheraven * Copyright (c) 2011 The FreeBSD Foundation 6227753Stheraven * All rights reserved. 7227753Stheraven * Portions of this software were developed by David Chisnall 8227753Stheraven * under sponsorship from the FreeBSD Foundation. 9227753Stheraven * 102708Swollman * Redistribution and use in source and binary forms are permitted 112708Swollman * provided that the above copyright notice and this paragraph are 122708Swollman * duplicated in all such forms and that any documentation, 132708Swollman * advertising materials, and other materials related to such 142708Swollman * distribution and use acknowledge that the software was developed 15192625Sedwin * by the University of California, Berkeley. The name of the 162708Swollman * University may not be used to endorse or promote products derived 172708Swollman * from this software without specific prior written permission. 182708Swollman * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 192708Swollman * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 202708Swollman * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 212708Swollman */ 222708Swollman 239912Swollman#ifndef lint 249912Swollman#ifndef NOID 25192625Sedwinstatic const char elsieid[] = "@(#)strftime.3 8.3"; 269912Swollman/* 27267798Spfg * Based on the UCB version with the ID appearing below. 28267798Spfg * This is ANSIish only when "multibyte character == plain character". 29267798Spfg */ 309912Swollman#endif /* !defined NOID */ 319912Swollman#endif /* !defined lint */ 329912Swollman 3371579Sdeischen#include "namespace.h" 349912Swollman#include "private.h" 359912Swollman 3692986Sobrien#if defined(LIBC_SCCS) && !defined(lint) 379912Swollmanstatic const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; 3892986Sobrien#endif /* LIBC_SCCS and not lint */ 3992986Sobrien#include <sys/cdefs.h> 4092986Sobrien__FBSDID("$FreeBSD$"); 412708Swollman 422708Swollman#include "tzfile.h" 439912Swollman#include <fcntl.h> 449912Swollman#include <sys/stat.h> 4571579Sdeischen#include "un-namespace.h" 4628021Sjoerg#include "timelocal.h" 472708Swollman 4897423Salfredstatic char * _add(const char *, char *, const char *); 49269483Spfgstatic char * _conv(int, const char *, char *, const char *, locale_t); 50192625Sedwinstatic char * _fmt(const char *, const struct tm *, char *, const char *, 51227753Stheraven int *, locale_t); 52269483Spfgstatic char * _yconv(int, int, int, int, char *, const char *, locale_t); 532708Swollman 549912Swollmanextern char * tzname[]; 552708Swollman 56130461Sstefanf#ifndef YEAR_2000_NAME 57130461Sstefanf#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" 58130461Sstefanf#endif /* !defined YEAR_2000_NAME */ 59130461Sstefanf 60267798Spfg#define IN_NONE 0 61267798Spfg#define IN_SOME 1 62267798Spfg#define IN_THIS 2 63267798Spfg#define IN_ALL 3 64130461Sstefanf 65267798Spfg#define PAD_DEFAULT 0 66267798Spfg#define PAD_LESS 1 67267798Spfg#define PAD_SPACE 2 68267798Spfg#define PAD_ZERO 3 69137190Sdelphij 70237211Sjillesstatic const char fmt_padding[][4][5] = { 71137190Sdelphij /* DEFAULT, LESS, SPACE, ZERO */ 72267798Spfg#define PAD_FMT_MONTHDAY 0 73267798Spfg#define PAD_FMT_HMS 0 74267798Spfg#define PAD_FMT_CENTURY 0 75267798Spfg#define PAD_FMT_SHORTYEAR 0 76267798Spfg#define PAD_FMT_MONTH 0 77267798Spfg#define PAD_FMT_WEEKOFYEAR 0 78267798Spfg#define PAD_FMT_DAYOFMONTH 0 79137190Sdelphij { "%02d", "%d", "%2d", "%02d" }, 80267798Spfg#define PAD_FMT_SDAYOFMONTH 1 81267798Spfg#define PAD_FMT_SHMS 1 82137190Sdelphij { "%2d", "%d", "%2d", "%02d" }, 83137190Sdelphij#define PAD_FMT_DAYOFYEAR 2 84137190Sdelphij { "%03d", "%d", "%3d", "%03d" }, 85267798Spfg#define PAD_FMT_YEAR 3 86137190Sdelphij { "%04d", "%d", "%4d", "%04d" } 87137190Sdelphij}; 88137190Sdelphij 892708Swollmansize_t 90227753Stheravenstrftime_l(char * __restrict s, size_t maxsize, const char * __restrict format, 91227753Stheraven const struct tm * __restrict t, locale_t loc) 922708Swollman{ 93130461Sstefanf char * p; 94130461Sstefanf int warn; 95227753Stheraven FIX_LOCALE(loc); 962708Swollman 979912Swollman tzset(); 98130461Sstefanf warn = IN_NONE; 99227753Stheraven p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, loc); 100130461Sstefanf#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU 101130461Sstefanf if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) { 102227753Stheraven (void) fprintf_l(stderr, loc, "\n"); 103130461Sstefanf if (format == NULL) 104269483Spfg (void) fputs("NULL strftime format ", stderr); 105227753Stheraven else (void) fprintf_l(stderr, loc, "strftime format \"%s\" ", 106130461Sstefanf format); 107269483Spfg (void) fputs("yields only two digits of years in ", stderr); 108130461Sstefanf if (warn == IN_SOME) 109269483Spfg (void) fputs("some locales", stderr); 110130461Sstefanf else if (warn == IN_THIS) 111269483Spfg (void) fputs("the current locale", stderr); 112269483Spfg else (void) fputs("all locales", stderr); 113269483Spfg (void) fputs("\n", stderr); 114130461Sstefanf } 115130461Sstefanf#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */ 1162708Swollman if (p == s + maxsize) 117267798Spfg return (0); 1182708Swollman *p = '\0'; 1192708Swollman return p - s; 1202708Swollman} 1212708Swollman 122227753Stheravensize_t 123227753Stheravenstrftime(char * __restrict s, size_t maxsize, const char * __restrict format, 124227753Stheraven const struct tm * __restrict t) 125227753Stheraven{ 126227753Stheraven return strftime_l(s, maxsize, format, t, __get_locale()); 127227753Stheraven} 128227753Stheraven 1292708Swollmanstatic char * 130227753Stheraven_fmt(format, t, pt, ptlim, warnp, loc) 131130461Sstefanfconst char * format; 132130461Sstefanfconst struct tm * const t; 133130461Sstefanfchar * pt; 134130461Sstefanfconst char * const ptlim; 135130461Sstefanfint * warnp; 136227753Stheravenlocale_t loc; 1372708Swollman{ 138137190Sdelphij int Ealternative, Oalternative, PadIndex; 139227753Stheraven struct lc_time_T *tptr = __get_current_time_locale(loc); 14053940Sache 1419912Swollman for ( ; *format; ++format) { 1422708Swollman if (*format == '%') { 14353940Sache Ealternative = 0; 14453940Sache Oalternative = 0; 145137190Sdelphij PadIndex = PAD_DEFAULT; 1462708Swollmanlabel: 1479912Swollman switch (*++format) { 1482708Swollman case '\0': 1492708Swollman --format; 1502708Swollman break; 1512708Swollman case 'A': 152130461Sstefanf pt = _add((t->tm_wday < 0 || 153130461Sstefanf t->tm_wday >= DAYSPERWEEK) ? 15472168Sphantom "?" : tptr->weekday[t->tm_wday], 1559912Swollman pt, ptlim); 1562708Swollman continue; 1572708Swollman case 'a': 158130461Sstefanf pt = _add((t->tm_wday < 0 || 159130461Sstefanf t->tm_wday >= DAYSPERWEEK) ? 16072168Sphantom "?" : tptr->wday[t->tm_wday], 1619912Swollman pt, ptlim); 1622708Swollman continue; 1632708Swollman case 'B': 164130461Sstefanf pt = _add((t->tm_mon < 0 || 165130461Sstefanf t->tm_mon >= MONSPERYEAR) ? 16672168Sphantom "?" : (Oalternative ? tptr->alt_month : 16772168Sphantom tptr->month)[t->tm_mon], 1689912Swollman pt, ptlim); 1692708Swollman continue; 1702708Swollman case 'b': 1712708Swollman case 'h': 172130461Sstefanf pt = _add((t->tm_mon < 0 || 173130461Sstefanf t->tm_mon >= MONSPERYEAR) ? 17472168Sphantom "?" : tptr->mon[t->tm_mon], 1759912Swollman pt, ptlim); 1762708Swollman continue; 1772708Swollman case 'C': 1782708Swollman /* 179267798Spfg * %C used to do a... 180267798Spfg * _fmt("%a %b %e %X %Y", t); 181267798Spfg * ...whereas now POSIX 1003.2 calls for 182267798Spfg * something completely different. 183267798Spfg * (ado, 1993-05-24) 184267798Spfg */ 185192625Sedwin pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0, 186269483Spfg pt, ptlim, loc); 1872708Swollman continue; 1889912Swollman case 'c': 189130461Sstefanf { 190130461Sstefanf int warn2 = IN_SOME; 191130461Sstefanf 192227753Stheraven pt = _fmt(tptr->c_fmt, t, pt, ptlim, &warn2, loc); 193130461Sstefanf if (warn2 == IN_ALL) 194130461Sstefanf warn2 = IN_THIS; 195130461Sstefanf if (warn2 > *warnp) 196130461Sstefanf *warnp = warn2; 197130461Sstefanf } 1989912Swollman continue; 1992708Swollman case 'D': 200227753Stheraven pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, loc); 2012708Swollman continue; 2022708Swollman case 'd': 203269483Spfg pt = _conv(t->tm_mday, 204269483Spfg fmt_padding[PAD_FMT_DAYOFMONTH][PadIndex], 205269483Spfg pt, ptlim, loc); 2062708Swollman continue; 2072708Swollman case 'E': 20853960Sache if (Ealternative || Oalternative) 20953960Sache break; 21053940Sache Ealternative++; 21153940Sache goto label; 2122708Swollman case 'O': 2132708Swollman /* 214267798Spfg * C99 locale modifiers. 215267798Spfg * The sequences 216267798Spfg * %Ec %EC %Ex %EX %Ey %EY 217267798Spfg * %Od %oe %OH %OI %Om %OM 218267798Spfg * %OS %Ou %OU %OV %Ow %OW %Oy 219267798Spfg * are supposed to provide alternate 220267798Spfg * representations. 221267798Spfg * 222267798Spfg * FreeBSD extension 223267798Spfg * %OB 224267798Spfg */ 22553960Sache if (Ealternative || Oalternative) 22653960Sache break; 22753940Sache Oalternative++; 2282708Swollman goto label; 2292708Swollman case 'e': 230137190Sdelphij pt = _conv(t->tm_mday, 231269483Spfg fmt_padding[PAD_FMT_SDAYOFMONTH][PadIndex], 232269483Spfg pt, ptlim, loc); 2332708Swollman continue; 23453960Sache case 'F': 235227753Stheraven pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, loc); 23653960Sache continue; 2372708Swollman case 'H': 238137190Sdelphij pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_HMS][PadIndex], 239269483Spfg pt, ptlim, loc); 2402708Swollman continue; 2412708Swollman case 'I': 2422708Swollman pt = _conv((t->tm_hour % 12) ? 2432708Swollman (t->tm_hour % 12) : 12, 244267798Spfg fmt_padding[PAD_FMT_HMS][PadIndex], 245269483Spfg pt, ptlim, loc); 2462708Swollman continue; 2472708Swollman case 'j': 248137190Sdelphij pt = _conv(t->tm_yday + 1, 249269483Spfg fmt_padding[PAD_FMT_DAYOFYEAR][PadIndex], 250269483Spfg pt, ptlim, loc); 2512708Swollman continue; 2522708Swollman case 'k': 2532708Swollman /* 254267798Spfg * This used to be... 255267798Spfg * _conv(t->tm_hour % 12 ? 256267798Spfg * t->tm_hour % 12 : 12, 2, ' '); 257267798Spfg * ...and has been changed to the below to 258267798Spfg * match SunOS 4.1.1 and Arnold Robbins' 259267798Spfg * strftime version 3.0. That is, "%k" and 260267798Spfg * "%l" have been swapped. 261267798Spfg * (ado, 1993-05-24) 262267798Spfg */ 263137190Sdelphij pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_SHMS][PadIndex], 264269483Spfg pt, ptlim, loc); 2652708Swollman continue; 2662708Swollman#ifdef KITCHEN_SINK 2672708Swollman case 'K': 2682708Swollman /* 2692708Swollman ** After all this time, still unclaimed! 2702708Swollman */ 2712708Swollman pt = _add("kitchen sink", pt, ptlim); 2722708Swollman continue; 2732708Swollman#endif /* defined KITCHEN_SINK */ 2742708Swollman case 'l': 2752708Swollman /* 276267798Spfg * This used to be... 277267798Spfg * _conv(t->tm_hour, 2, ' '); 278267798Spfg * ...and has been changed to the below to 279267798Spfg * match SunOS 4.1.1 and Arnold Robbin's 280267798Spfg * strftime version 3.0. That is, "%k" and 281267798Spfg * "%l" have been swapped. 282267798Spfg * (ado, 1993-05-24) 283267798Spfg */ 2842708Swollman pt = _conv((t->tm_hour % 12) ? 2852708Swollman (t->tm_hour % 12) : 12, 286267798Spfg fmt_padding[PAD_FMT_SHMS][PadIndex], 287269483Spfg pt, ptlim, loc); 2882708Swollman continue; 2892708Swollman case 'M': 290137190Sdelphij pt = _conv(t->tm_min, fmt_padding[PAD_FMT_HMS][PadIndex], 291269483Spfg pt, ptlim, loc); 2922708Swollman continue; 2932708Swollman case 'm': 294137190Sdelphij pt = _conv(t->tm_mon + 1, 295267798Spfg fmt_padding[PAD_FMT_MONTH][PadIndex], 296269483Spfg pt, ptlim, loc); 2972708Swollman continue; 2982708Swollman case 'n': 2992708Swollman pt = _add("\n", pt, ptlim); 3002708Swollman continue; 3012708Swollman case 'p': 302130461Sstefanf pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? 303267798Spfg tptr->pm : tptr->am, 3042708Swollman pt, ptlim); 3052708Swollman continue; 3062708Swollman case 'R': 307227753Stheraven pt = _fmt("%H:%M", t, pt, ptlim, warnp, loc); 3082708Swollman continue; 3092708Swollman case 'r': 310130461Sstefanf pt = _fmt(tptr->ampm_fmt, t, pt, ptlim, 311227753Stheraven warnp, loc); 3122708Swollman continue; 3132708Swollman case 'S': 314137190Sdelphij pt = _conv(t->tm_sec, fmt_padding[PAD_FMT_HMS][PadIndex], 315269483Spfg pt, ptlim, loc); 3162708Swollman continue; 3179988Sache case 's': 31837299Sbde { 31937299Sbde struct tm tm; 32037299Sbde char buf[INT_STRLEN_MAXIMUM( 32137299Sbde time_t) + 1]; 32237299Sbde time_t mkt; 32337299Sbde 32437299Sbde tm = *t; 32537299Sbde mkt = mktime(&tm); 32637299Sbde if (TYPE_SIGNED(time_t)) 327269483Spfg (void) sprintf_l(buf, loc, "%ld", 32837299Sbde (long) mkt); 329269483Spfg else (void) sprintf_l(buf, loc, "%lu", 33037299Sbde (unsigned long) mkt); 33137299Sbde pt = _add(buf, pt, ptlim); 33237299Sbde } 3339988Sache continue; 3342708Swollman case 'T': 335227753Stheraven pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, loc); 3362708Swollman continue; 3372708Swollman case 't': 3382708Swollman pt = _add("\t", pt, ptlim); 3392708Swollman continue; 3402708Swollman case 'U': 341130461Sstefanf pt = _conv((t->tm_yday + DAYSPERWEEK - 342130461Sstefanf t->tm_wday) / DAYSPERWEEK, 343269483Spfg fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], 344269483Spfg pt, ptlim, loc); 3452708Swollman continue; 3462708Swollman case 'u': 3472708Swollman /* 348267798Spfg * From Arnold Robbins' strftime version 3.0: 349267798Spfg * "ISO 8601: Weekday as a decimal number 350267798Spfg * [1 (Monday) - 7]" 351267798Spfg * (ado, 1993-05-24) 352267798Spfg */ 353130461Sstefanf pt = _conv((t->tm_wday == 0) ? 354130461Sstefanf DAYSPERWEEK : t->tm_wday, 355269483Spfg "%d", pt, ptlim, loc); 3562708Swollman continue; 35730089Shelbig case 'V': /* ISO 8601 week number */ 35830089Shelbig case 'G': /* ISO 8601 year (four digits) */ 35930089Shelbig case 'g': /* ISO 8601 year (two digits) */ 36030089Shelbig/* 361267798Spfg * From Arnold Robbins' strftime version 3.0: "the week number of the 362267798Spfg * year (the first Monday as the first day of week 1) as a decimal number 363267798Spfg * (01-53)." 364267798Spfg * (ado, 1993-05-24) 365267798Spfg * 366267798Spfg * From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: 367267798Spfg * "Week 01 of a year is per definition the first week which has the 368267798Spfg * Thursday in this year, which is equivalent to the week which contains 369267798Spfg * the fourth day of January. In other words, the first week of a new year 370267798Spfg * is the week which has the majority of its days in the new year. Week 01 371267798Spfg * might also contain days from the previous year and the week before week 372267798Spfg * 01 of a year is the last week (52 or 53) of the previous year even if 373267798Spfg * it contains days from the new year. A week starts with Monday (day 1) 374267798Spfg * and ends with Sunday (day 7). For example, the first week of the year 375267798Spfg * 1997 lasts from 1996-12-30 to 1997-01-05..." 376267798Spfg * (ado, 1996-01-02) 377267798Spfg */ 3782708Swollman { 37930089Shelbig int year; 380192625Sedwin int base; 38130089Shelbig int yday; 38230089Shelbig int wday; 38330089Shelbig int w; 3842708Swollman 385192625Sedwin year = t->tm_year; 386192625Sedwin base = TM_YEAR_BASE; 38730089Shelbig yday = t->tm_yday; 38830089Shelbig wday = t->tm_wday; 38930089Shelbig for ( ; ; ) { 39030089Shelbig int len; 39130089Shelbig int bot; 39230089Shelbig int top; 39330089Shelbig 394192625Sedwin len = isleap_sum(year, base) ? 39530089Shelbig DAYSPERLYEAR : 39630089Shelbig DAYSPERNYEAR; 3972708Swollman /* 398267798Spfg * What yday (-3 ... 3) does 399267798Spfg * the ISO year begin on? 400267798Spfg */ 40130089Shelbig bot = ((yday + 11 - wday) % 40230089Shelbig DAYSPERWEEK) - 3; 4032708Swollman /* 404267798Spfg * What yday does the NEXT 405267798Spfg * ISO year begin on? 406267798Spfg */ 40730089Shelbig top = bot - 40830089Shelbig (len % DAYSPERWEEK); 40930089Shelbig if (top < -3) 41030089Shelbig top += DAYSPERWEEK; 41130089Shelbig top += len; 41230089Shelbig if (yday >= top) { 413192625Sedwin ++base; 41430089Shelbig w = 1; 41530089Shelbig break; 41630089Shelbig } 41730089Shelbig if (yday >= bot) { 41830089Shelbig w = 1 + ((yday - bot) / 41930089Shelbig DAYSPERWEEK); 42030089Shelbig break; 42130089Shelbig } 422192625Sedwin --base; 423192625Sedwin yday += isleap_sum(year, base) ? 42430089Shelbig DAYSPERLYEAR : 42530089Shelbig DAYSPERNYEAR; 42630089Shelbig } 4272708Swollman#ifdef XPG4_1994_04_09 428192625Sedwin if ((w == 52 && 429192625Sedwin t->tm_mon == TM_JANUARY) || 430192625Sedwin (w == 1 && 431192625Sedwin t->tm_mon == TM_DECEMBER)) 432192625Sedwin w = 53; 4332708Swollman#endif /* defined XPG4_1994_04_09 */ 43430089Shelbig if (*format == 'V') 435137190Sdelphij pt = _conv(w, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], 436269483Spfg pt, ptlim, loc); 43730089Shelbig else if (*format == 'g') { 438130461Sstefanf *warnp = IN_ALL; 439192625Sedwin pt = _yconv(year, base, 0, 1, 440269483Spfg pt, ptlim, loc); 441192625Sedwin } else pt = _yconv(year, base, 1, 1, 442269483Spfg pt, ptlim, loc); 4432708Swollman } 4442708Swollman continue; 4452708Swollman case 'v': 4462708Swollman /* 447267798Spfg * From Arnold Robbins' strftime version 3.0: 448267798Spfg * "date as dd-bbb-YYYY" 449267798Spfg * (ado, 1993-05-24) 450267798Spfg */ 451227753Stheraven pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, loc); 4522708Swollman continue; 4532708Swollman case 'W': 454130461Sstefanf pt = _conv((t->tm_yday + DAYSPERWEEK - 4552708Swollman (t->tm_wday ? 456130461Sstefanf (t->tm_wday - 1) : 457130461Sstefanf (DAYSPERWEEK - 1))) / DAYSPERWEEK, 458269483Spfg fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], 459269483Spfg pt, ptlim, loc); 4602708Swollman continue; 4612708Swollman case 'w': 462269483Spfg pt = _conv(t->tm_wday, "%d", pt, ptlim, loc); 4632708Swollman continue; 4649912Swollman case 'X': 465227753Stheraven pt = _fmt(tptr->X_fmt, t, pt, ptlim, warnp, loc); 4669912Swollman continue; 4679912Swollman case 'x': 468130461Sstefanf { 469130461Sstefanf int warn2 = IN_SOME; 470130461Sstefanf 471227753Stheraven pt = _fmt(tptr->x_fmt, t, pt, ptlim, &warn2, loc); 472130461Sstefanf if (warn2 == IN_ALL) 473130461Sstefanf warn2 = IN_THIS; 474130461Sstefanf if (warn2 > *warnp) 475130461Sstefanf *warnp = warn2; 476130461Sstefanf } 4779912Swollman continue; 4782708Swollman case 'y': 479130461Sstefanf *warnp = IN_ALL; 480192625Sedwin pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, 481269483Spfg pt, ptlim, loc); 4822708Swollman continue; 4832708Swollman case 'Y': 484192625Sedwin pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, 485269483Spfg pt, ptlim, loc); 4862708Swollman continue; 4872708Swollman case 'Z': 488130461Sstefanf#ifdef TM_ZONE 489130461Sstefanf if (t->TM_ZONE != NULL) 490130461Sstefanf pt = _add(t->TM_ZONE, pt, ptlim); 4912708Swollman else 492130461Sstefanf#endif /* defined TM_ZONE */ 493130461Sstefanf if (t->tm_isdst >= 0) 494130461Sstefanf pt = _add(tzname[t->tm_isdst != 0], 4952708Swollman pt, ptlim); 496130461Sstefanf /* 497267798Spfg * C99 says that %Z must be replaced by the 498267798Spfg * empty string if the time zone is not 499267798Spfg * determinable. 500267798Spfg */ 5012708Swollman continue; 50256756Sjoerg case 'z': 50356756Sjoerg { 504130461Sstefanf int diff; 505130461Sstefanf char const * sign; 506130461Sstefanf 507130461Sstefanf if (t->tm_isdst < 0) 508130461Sstefanf continue; 509130461Sstefanf#ifdef TM_GMTOFF 510130461Sstefanf diff = t->TM_GMTOFF; 511130461Sstefanf#else /* !defined TM_GMTOFF */ 512130461Sstefanf /* 513267798Spfg * C99 says that the UTC offset must 514267798Spfg * be computed by looking only at 515267798Spfg * tm_isdst. This requirement is 516267798Spfg * incorrect, since it means the code 517267798Spfg * must rely on magic (in this case 518267798Spfg * altzone and timezone), and the 519267798Spfg * magic might not have the correct 520267798Spfg * offset. Doing things correctly is 521267798Spfg * tricky and requires disobeying C99; 522267798Spfg * see GNU C strftime for details. 523267798Spfg * For now, punt and conform to the 524267798Spfg * standard, even though it's incorrect. 525267798Spfg * 526267798Spfg * C99 says that %z must be replaced by the 527267798Spfg * empty string if the time zone is not 528267798Spfg * determinable, so output nothing if the 529267798Spfg * appropriate variables are not available. 530267798Spfg */ 531130461Sstefanf if (t->tm_isdst == 0) 532130461Sstefanf#ifdef USG_COMPAT 533130461Sstefanf diff = -timezone; 534130461Sstefanf#else /* !defined USG_COMPAT */ 535130461Sstefanf continue; 536130461Sstefanf#endif /* !defined USG_COMPAT */ 537130461Sstefanf else 538130461Sstefanf#ifdef ALTZONE 539130461Sstefanf diff = -altzone; 540130461Sstefanf#else /* !defined ALTZONE */ 541130461Sstefanf continue; 542130461Sstefanf#endif /* !defined ALTZONE */ 543130461Sstefanf#endif /* !defined TM_GMTOFF */ 544130461Sstefanf if (diff < 0) { 545130461Sstefanf sign = "-"; 546130461Sstefanf diff = -diff; 547267798Spfg } else 548267798Spfg sign = "+"; 549130461Sstefanf pt = _add(sign, pt, ptlim); 550192625Sedwin diff /= SECSPERMIN; 551192625Sedwin diff = (diff / MINSPERHOUR) * 100 + 552192625Sedwin (diff % MINSPERHOUR); 553192625Sedwin pt = _conv(diff, 554269483Spfg fmt_padding[PAD_FMT_YEAR][PadIndex], 555269483Spfg pt, ptlim, loc); 556130461Sstefanf } 55756756Sjoerg continue; 5589912Swollman case '+': 559130461Sstefanf pt = _fmt(tptr->date_fmt, t, pt, ptlim, 560227753Stheraven warnp, loc); 5619912Swollman continue; 562137190Sdelphij case '-': 563137190Sdelphij if (PadIndex != PAD_DEFAULT) 564137190Sdelphij break; 565137190Sdelphij PadIndex = PAD_LESS; 566137190Sdelphij goto label; 567137190Sdelphij case '_': 568137190Sdelphij if (PadIndex != PAD_DEFAULT) 569137190Sdelphij break; 570137190Sdelphij PadIndex = PAD_SPACE; 571137190Sdelphij goto label; 572137190Sdelphij case '0': 573137190Sdelphij if (PadIndex != PAD_DEFAULT) 574137190Sdelphij break; 575137190Sdelphij PadIndex = PAD_ZERO; 576137190Sdelphij goto label; 5772708Swollman case '%': 5782708Swollman /* 579267798Spfg * X311J/88-090 (4.12.3.5): if conversion char is 580267798Spfg * undefined, behavior is undefined. Print out the 581267798Spfg * character itself as printf(3) also does. 582267798Spfg */ 5832708Swollman default: 5842708Swollman break; 5852708Swollman } 5862708Swollman } 5872708Swollman if (pt == ptlim) 5882708Swollman break; 5892708Swollman *pt++ = *format; 5902708Swollman } 591267798Spfg return (pt); 5922708Swollman} 5932708Swollman 5942708Swollmanstatic char * 595269483Spfg_conv(n, format, pt, ptlim, loc) 596130461Sstefanfconst int n; 597130461Sstefanfconst char * const format; 598130461Sstefanfchar * const pt; 599130461Sstefanfconst char * const ptlim; 600269483Spfglocale_t loc; 6012708Swollman{ 6029912Swollman char buf[INT_STRLEN_MAXIMUM(int) + 1]; 6032708Swollman 604269483Spfg (void) sprintf_l(buf, loc, format, n); 6052708Swollman return _add(buf, pt, ptlim); 6062708Swollman} 6072708Swollman 6082708Swollmanstatic char * 6092708Swollman_add(str, pt, ptlim) 610130461Sstefanfconst char * str; 611130461Sstefanfchar * pt; 612130461Sstefanfconst char * const ptlim; 6132708Swollman{ 6142708Swollman while (pt < ptlim && (*pt = *str++) != '\0') 6152708Swollman ++pt; 616267798Spfg return (pt); 6172708Swollman} 618192625Sedwin 619192625Sedwin/* 620267798Spfg * POSIX and the C Standard are unclear or inconsistent about 621267798Spfg * what %C and %y do if the year is negative or exceeds 9999. 622267798Spfg * Use the convention that %C concatenated with %y yields the 623267798Spfg * same output as %Y, and that %Y contains at least 4 bytes, 624267798Spfg * with more only if necessary. 625267798Spfg */ 626192625Sedwin 627192625Sedwinstatic char * 628269483Spfg_yconv(a, b, convert_top, convert_yy, pt, ptlim, loc) 629192625Sedwinconst int a; 630192625Sedwinconst int b; 631192625Sedwinconst int convert_top; 632192625Sedwinconst int convert_yy; 633192625Sedwinchar * pt; 634192625Sedwinconst char * const ptlim; 635269483Spfglocale_t loc; 636192625Sedwin{ 637192625Sedwin register int lead; 638192625Sedwin register int trail; 639192625Sedwin 640267798Spfg#define DIVISOR 100 641192625Sedwin trail = a % DIVISOR + b % DIVISOR; 642192625Sedwin lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; 643192625Sedwin trail %= DIVISOR; 644192625Sedwin if (trail < 0 && lead > 0) { 645192625Sedwin trail += DIVISOR; 646192625Sedwin --lead; 647192625Sedwin } else if (lead < 0 && trail > 0) { 648192625Sedwin trail -= DIVISOR; 649192625Sedwin ++lead; 650192625Sedwin } 651192625Sedwin if (convert_top) { 652192625Sedwin if (lead == 0 && trail < 0) 653192625Sedwin pt = _add("-0", pt, ptlim); 654269483Spfg else pt = _conv(lead, "%02d", pt, ptlim, loc); 655192625Sedwin } 656192625Sedwin if (convert_yy) 657269483Spfg pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, 658269483Spfg ptlim, loc); 659267798Spfg return (pt); 660192625Sedwin} 661