strftime.c revision 267798
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: stable/10/lib/libc/stdtime/strftime.c 267798 2014-06-23 15:03:51Z pfg $"); 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 *); 4997423Salfredstatic char * _conv(int, const char *, char *, const char *); 50192625Sedwinstatic char * _fmt(const char *, const struct tm *, char *, const char *, 51227753Stheraven int *, locale_t); 52192625Sedwinstatic char * _yconv(int, int, int, int, char *, const char *); 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) 104227753Stheraven (void) fprintf_l(stderr, loc, "NULL strftime format "); 105227753Stheraven else (void) fprintf_l(stderr, loc, "strftime format \"%s\" ", 106130461Sstefanf format); 107227753Stheraven (void) fprintf_l(stderr, loc, "yields only two digits of years in "); 108130461Sstefanf if (warn == IN_SOME) 109227753Stheraven (void) fprintf_l(stderr, loc, "some locales"); 110130461Sstefanf else if (warn == IN_THIS) 111227753Stheraven (void) fprintf_l(stderr, loc, "the current locale"); 112227753Stheraven else (void) fprintf_l(stderr, loc, "all locales"); 113227753Stheraven (void) fprintf_l(stderr, loc, "\n"); 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, 186192625Sedwin pt, ptlim); 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': 203137190Sdelphij pt = _conv(t->tm_mday, fmt_padding[PAD_FMT_DAYOFMONTH][PadIndex], 204137190Sdelphij pt, ptlim); 2052708Swollman continue; 2062708Swollman case 'E': 20753960Sache if (Ealternative || Oalternative) 20853960Sache break; 20953940Sache Ealternative++; 21053940Sache goto label; 2112708Swollman case 'O': 2122708Swollman /* 213267798Spfg * C99 locale modifiers. 214267798Spfg * The sequences 215267798Spfg * %Ec %EC %Ex %EX %Ey %EY 216267798Spfg * %Od %oe %OH %OI %Om %OM 217267798Spfg * %OS %Ou %OU %OV %Ow %OW %Oy 218267798Spfg * are supposed to provide alternate 219267798Spfg * representations. 220267798Spfg * 221267798Spfg * FreeBSD extension 222267798Spfg * %OB 223267798Spfg */ 22453960Sache if (Ealternative || Oalternative) 22553960Sache break; 22653940Sache Oalternative++; 2272708Swollman goto label; 2282708Swollman case 'e': 229137190Sdelphij pt = _conv(t->tm_mday, 230137190Sdelphij fmt_padding[PAD_FMT_SDAYOFMONTH][PadIndex], pt, ptlim); 2312708Swollman continue; 23253960Sache case 'F': 233227753Stheraven pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, loc); 23453960Sache continue; 2352708Swollman case 'H': 236137190Sdelphij pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_HMS][PadIndex], 237137190Sdelphij pt, ptlim); 2382708Swollman continue; 2392708Swollman case 'I': 2402708Swollman pt = _conv((t->tm_hour % 12) ? 2412708Swollman (t->tm_hour % 12) : 12, 242267798Spfg fmt_padding[PAD_FMT_HMS][PadIndex], 243267798Spfg pt, ptlim); 2442708Swollman continue; 2452708Swollman case 'j': 246137190Sdelphij pt = _conv(t->tm_yday + 1, 247137190Sdelphij fmt_padding[PAD_FMT_DAYOFYEAR][PadIndex], pt, ptlim); 2482708Swollman continue; 2492708Swollman case 'k': 2502708Swollman /* 251267798Spfg * This used to be... 252267798Spfg * _conv(t->tm_hour % 12 ? 253267798Spfg * t->tm_hour % 12 : 12, 2, ' '); 254267798Spfg * ...and has been changed to the below to 255267798Spfg * match SunOS 4.1.1 and Arnold Robbins' 256267798Spfg * strftime version 3.0. That is, "%k" and 257267798Spfg * "%l" have been swapped. 258267798Spfg * (ado, 1993-05-24) 259267798Spfg */ 260137190Sdelphij pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_SHMS][PadIndex], 261137190Sdelphij pt, ptlim); 2622708Swollman continue; 2632708Swollman#ifdef KITCHEN_SINK 2642708Swollman case 'K': 2652708Swollman /* 2662708Swollman ** After all this time, still unclaimed! 2672708Swollman */ 2682708Swollman pt = _add("kitchen sink", pt, ptlim); 2692708Swollman continue; 2702708Swollman#endif /* defined KITCHEN_SINK */ 2712708Swollman case 'l': 2722708Swollman /* 273267798Spfg * This used to be... 274267798Spfg * _conv(t->tm_hour, 2, ' '); 275267798Spfg * ...and has been changed to the below to 276267798Spfg * match SunOS 4.1.1 and Arnold Robbin's 277267798Spfg * strftime version 3.0. That is, "%k" and 278267798Spfg * "%l" have been swapped. 279267798Spfg * (ado, 1993-05-24) 280267798Spfg */ 2812708Swollman pt = _conv((t->tm_hour % 12) ? 2822708Swollman (t->tm_hour % 12) : 12, 283267798Spfg fmt_padding[PAD_FMT_SHMS][PadIndex], 284267798Spfg pt, ptlim); 2852708Swollman continue; 2862708Swollman case 'M': 287137190Sdelphij pt = _conv(t->tm_min, fmt_padding[PAD_FMT_HMS][PadIndex], 288137190Sdelphij pt, ptlim); 2892708Swollman continue; 2902708Swollman case 'm': 291137190Sdelphij pt = _conv(t->tm_mon + 1, 292267798Spfg fmt_padding[PAD_FMT_MONTH][PadIndex], 293267798Spfg pt, ptlim); 2942708Swollman continue; 2952708Swollman case 'n': 2962708Swollman pt = _add("\n", pt, ptlim); 2972708Swollman continue; 2982708Swollman case 'p': 299130461Sstefanf pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? 300267798Spfg tptr->pm : tptr->am, 3012708Swollman pt, ptlim); 3022708Swollman continue; 3032708Swollman case 'R': 304227753Stheraven pt = _fmt("%H:%M", t, pt, ptlim, warnp, loc); 3052708Swollman continue; 3062708Swollman case 'r': 307130461Sstefanf pt = _fmt(tptr->ampm_fmt, t, pt, ptlim, 308227753Stheraven warnp, loc); 3092708Swollman continue; 3102708Swollman case 'S': 311137190Sdelphij pt = _conv(t->tm_sec, fmt_padding[PAD_FMT_HMS][PadIndex], 312137190Sdelphij pt, ptlim); 3132708Swollman continue; 3149988Sache case 's': 31537299Sbde { 31637299Sbde struct tm tm; 31737299Sbde char buf[INT_STRLEN_MAXIMUM( 31837299Sbde time_t) + 1]; 31937299Sbde time_t mkt; 32037299Sbde 32137299Sbde tm = *t; 32237299Sbde mkt = mktime(&tm); 32337299Sbde if (TYPE_SIGNED(time_t)) 32437299Sbde (void) sprintf(buf, "%ld", 32537299Sbde (long) mkt); 32637299Sbde else (void) sprintf(buf, "%lu", 32737299Sbde (unsigned long) mkt); 32837299Sbde pt = _add(buf, pt, ptlim); 32937299Sbde } 3309988Sache continue; 3312708Swollman case 'T': 332227753Stheraven pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, loc); 3332708Swollman continue; 3342708Swollman case 't': 3352708Swollman pt = _add("\t", pt, ptlim); 3362708Swollman continue; 3372708Swollman case 'U': 338130461Sstefanf pt = _conv((t->tm_yday + DAYSPERWEEK - 339130461Sstefanf t->tm_wday) / DAYSPERWEEK, 340137190Sdelphij fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim); 3412708Swollman continue; 3422708Swollman case 'u': 3432708Swollman /* 344267798Spfg * From Arnold Robbins' strftime version 3.0: 345267798Spfg * "ISO 8601: Weekday as a decimal number 346267798Spfg * [1 (Monday) - 7]" 347267798Spfg * (ado, 1993-05-24) 348267798Spfg */ 349130461Sstefanf pt = _conv((t->tm_wday == 0) ? 350130461Sstefanf DAYSPERWEEK : t->tm_wday, 3512708Swollman "%d", pt, ptlim); 3522708Swollman continue; 35330089Shelbig case 'V': /* ISO 8601 week number */ 35430089Shelbig case 'G': /* ISO 8601 year (four digits) */ 35530089Shelbig case 'g': /* ISO 8601 year (two digits) */ 35630089Shelbig/* 357267798Spfg * From Arnold Robbins' strftime version 3.0: "the week number of the 358267798Spfg * year (the first Monday as the first day of week 1) as a decimal number 359267798Spfg * (01-53)." 360267798Spfg * (ado, 1993-05-24) 361267798Spfg * 362267798Spfg * From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: 363267798Spfg * "Week 01 of a year is per definition the first week which has the 364267798Spfg * Thursday in this year, which is equivalent to the week which contains 365267798Spfg * the fourth day of January. In other words, the first week of a new year 366267798Spfg * is the week which has the majority of its days in the new year. Week 01 367267798Spfg * might also contain days from the previous year and the week before week 368267798Spfg * 01 of a year is the last week (52 or 53) of the previous year even if 369267798Spfg * it contains days from the new year. A week starts with Monday (day 1) 370267798Spfg * and ends with Sunday (day 7). For example, the first week of the year 371267798Spfg * 1997 lasts from 1996-12-30 to 1997-01-05..." 372267798Spfg * (ado, 1996-01-02) 373267798Spfg */ 3742708Swollman { 37530089Shelbig int year; 376192625Sedwin int base; 37730089Shelbig int yday; 37830089Shelbig int wday; 37930089Shelbig int w; 3802708Swollman 381192625Sedwin year = t->tm_year; 382192625Sedwin base = TM_YEAR_BASE; 38330089Shelbig yday = t->tm_yday; 38430089Shelbig wday = t->tm_wday; 38530089Shelbig for ( ; ; ) { 38630089Shelbig int len; 38730089Shelbig int bot; 38830089Shelbig int top; 38930089Shelbig 390192625Sedwin len = isleap_sum(year, base) ? 39130089Shelbig DAYSPERLYEAR : 39230089Shelbig DAYSPERNYEAR; 3932708Swollman /* 394267798Spfg * What yday (-3 ... 3) does 395267798Spfg * the ISO year begin on? 396267798Spfg */ 39730089Shelbig bot = ((yday + 11 - wday) % 39830089Shelbig DAYSPERWEEK) - 3; 3992708Swollman /* 400267798Spfg * What yday does the NEXT 401267798Spfg * ISO year begin on? 402267798Spfg */ 40330089Shelbig top = bot - 40430089Shelbig (len % DAYSPERWEEK); 40530089Shelbig if (top < -3) 40630089Shelbig top += DAYSPERWEEK; 40730089Shelbig top += len; 40830089Shelbig if (yday >= top) { 409192625Sedwin ++base; 41030089Shelbig w = 1; 41130089Shelbig break; 41230089Shelbig } 41330089Shelbig if (yday >= bot) { 41430089Shelbig w = 1 + ((yday - bot) / 41530089Shelbig DAYSPERWEEK); 41630089Shelbig break; 41730089Shelbig } 418192625Sedwin --base; 419192625Sedwin yday += isleap_sum(year, base) ? 42030089Shelbig DAYSPERLYEAR : 42130089Shelbig DAYSPERNYEAR; 42230089Shelbig } 4232708Swollman#ifdef XPG4_1994_04_09 424192625Sedwin if ((w == 52 && 425192625Sedwin t->tm_mon == TM_JANUARY) || 426192625Sedwin (w == 1 && 427192625Sedwin t->tm_mon == TM_DECEMBER)) 428192625Sedwin w = 53; 4292708Swollman#endif /* defined XPG4_1994_04_09 */ 43030089Shelbig if (*format == 'V') 431137190Sdelphij pt = _conv(w, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], 43230089Shelbig pt, ptlim); 43330089Shelbig else if (*format == 'g') { 434130461Sstefanf *warnp = IN_ALL; 435192625Sedwin pt = _yconv(year, base, 0, 1, 43630089Shelbig pt, ptlim); 437192625Sedwin } else pt = _yconv(year, base, 1, 1, 43830089Shelbig pt, ptlim); 4392708Swollman } 4402708Swollman continue; 4412708Swollman case 'v': 4422708Swollman /* 443267798Spfg * From Arnold Robbins' strftime version 3.0: 444267798Spfg * "date as dd-bbb-YYYY" 445267798Spfg * (ado, 1993-05-24) 446267798Spfg */ 447227753Stheraven pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, loc); 4482708Swollman continue; 4492708Swollman case 'W': 450130461Sstefanf pt = _conv((t->tm_yday + DAYSPERWEEK - 4512708Swollman (t->tm_wday ? 452130461Sstefanf (t->tm_wday - 1) : 453130461Sstefanf (DAYSPERWEEK - 1))) / DAYSPERWEEK, 454137190Sdelphij fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim); 4552708Swollman continue; 4562708Swollman case 'w': 4572708Swollman pt = _conv(t->tm_wday, "%d", pt, ptlim); 4582708Swollman continue; 4599912Swollman case 'X': 460227753Stheraven pt = _fmt(tptr->X_fmt, t, pt, ptlim, warnp, loc); 4619912Swollman continue; 4629912Swollman case 'x': 463130461Sstefanf { 464130461Sstefanf int warn2 = IN_SOME; 465130461Sstefanf 466227753Stheraven pt = _fmt(tptr->x_fmt, t, pt, ptlim, &warn2, loc); 467130461Sstefanf if (warn2 == IN_ALL) 468130461Sstefanf warn2 = IN_THIS; 469130461Sstefanf if (warn2 > *warnp) 470130461Sstefanf *warnp = warn2; 471130461Sstefanf } 4729912Swollman continue; 4732708Swollman case 'y': 474130461Sstefanf *warnp = IN_ALL; 475192625Sedwin pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1, 476192625Sedwin pt, ptlim); 4772708Swollman continue; 4782708Swollman case 'Y': 479192625Sedwin pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1, 4802708Swollman pt, ptlim); 4812708Swollman continue; 4822708Swollman case 'Z': 483130461Sstefanf#ifdef TM_ZONE 484130461Sstefanf if (t->TM_ZONE != NULL) 485130461Sstefanf pt = _add(t->TM_ZONE, pt, ptlim); 4862708Swollman else 487130461Sstefanf#endif /* defined TM_ZONE */ 488130461Sstefanf if (t->tm_isdst >= 0) 489130461Sstefanf pt = _add(tzname[t->tm_isdst != 0], 4902708Swollman pt, ptlim); 491130461Sstefanf /* 492267798Spfg * C99 says that %Z must be replaced by the 493267798Spfg * empty string if the time zone is not 494267798Spfg * determinable. 495267798Spfg */ 4962708Swollman continue; 49756756Sjoerg case 'z': 49856756Sjoerg { 499130461Sstefanf int diff; 500130461Sstefanf char const * sign; 501130461Sstefanf 502130461Sstefanf if (t->tm_isdst < 0) 503130461Sstefanf continue; 504130461Sstefanf#ifdef TM_GMTOFF 505130461Sstefanf diff = t->TM_GMTOFF; 506130461Sstefanf#else /* !defined TM_GMTOFF */ 507130461Sstefanf /* 508267798Spfg * C99 says that the UTC offset must 509267798Spfg * be computed by looking only at 510267798Spfg * tm_isdst. This requirement is 511267798Spfg * incorrect, since it means the code 512267798Spfg * must rely on magic (in this case 513267798Spfg * altzone and timezone), and the 514267798Spfg * magic might not have the correct 515267798Spfg * offset. Doing things correctly is 516267798Spfg * tricky and requires disobeying C99; 517267798Spfg * see GNU C strftime for details. 518267798Spfg * For now, punt and conform to the 519267798Spfg * standard, even though it's incorrect. 520267798Spfg * 521267798Spfg * C99 says that %z must be replaced by the 522267798Spfg * empty string if the time zone is not 523267798Spfg * determinable, so output nothing if the 524267798Spfg * appropriate variables are not available. 525267798Spfg */ 526130461Sstefanf if (t->tm_isdst == 0) 527130461Sstefanf#ifdef USG_COMPAT 528130461Sstefanf diff = -timezone; 529130461Sstefanf#else /* !defined USG_COMPAT */ 530130461Sstefanf continue; 531130461Sstefanf#endif /* !defined USG_COMPAT */ 532130461Sstefanf else 533130461Sstefanf#ifdef ALTZONE 534130461Sstefanf diff = -altzone; 535130461Sstefanf#else /* !defined ALTZONE */ 536130461Sstefanf continue; 537130461Sstefanf#endif /* !defined ALTZONE */ 538130461Sstefanf#endif /* !defined TM_GMTOFF */ 539130461Sstefanf if (diff < 0) { 540130461Sstefanf sign = "-"; 541130461Sstefanf diff = -diff; 542267798Spfg } else 543267798Spfg sign = "+"; 544130461Sstefanf pt = _add(sign, pt, ptlim); 545192625Sedwin diff /= SECSPERMIN; 546192625Sedwin diff = (diff / MINSPERHOUR) * 100 + 547192625Sedwin (diff % MINSPERHOUR); 548192625Sedwin pt = _conv(diff, 549137190Sdelphij fmt_padding[PAD_FMT_YEAR][PadIndex], pt, ptlim); 550130461Sstefanf } 55156756Sjoerg continue; 5529912Swollman case '+': 553130461Sstefanf pt = _fmt(tptr->date_fmt, t, pt, ptlim, 554227753Stheraven warnp, loc); 5559912Swollman continue; 556137190Sdelphij case '-': 557137190Sdelphij if (PadIndex != PAD_DEFAULT) 558137190Sdelphij break; 559137190Sdelphij PadIndex = PAD_LESS; 560137190Sdelphij goto label; 561137190Sdelphij case '_': 562137190Sdelphij if (PadIndex != PAD_DEFAULT) 563137190Sdelphij break; 564137190Sdelphij PadIndex = PAD_SPACE; 565137190Sdelphij goto label; 566137190Sdelphij case '0': 567137190Sdelphij if (PadIndex != PAD_DEFAULT) 568137190Sdelphij break; 569137190Sdelphij PadIndex = PAD_ZERO; 570137190Sdelphij goto label; 5712708Swollman case '%': 5722708Swollman /* 573267798Spfg * X311J/88-090 (4.12.3.5): if conversion char is 574267798Spfg * undefined, behavior is undefined. Print out the 575267798Spfg * character itself as printf(3) also does. 576267798Spfg */ 5772708Swollman default: 5782708Swollman break; 5792708Swollman } 5802708Swollman } 5812708Swollman if (pt == ptlim) 5822708Swollman break; 5832708Swollman *pt++ = *format; 5842708Swollman } 585267798Spfg return (pt); 5862708Swollman} 5872708Swollman 5882708Swollmanstatic char * 5892708Swollman_conv(n, format, pt, ptlim) 590130461Sstefanfconst int n; 591130461Sstefanfconst char * const format; 592130461Sstefanfchar * const pt; 593130461Sstefanfconst char * const ptlim; 5942708Swollman{ 5959912Swollman char buf[INT_STRLEN_MAXIMUM(int) + 1]; 5962708Swollman 59772619Skris (void) sprintf(buf, format, n); 5982708Swollman return _add(buf, pt, ptlim); 5992708Swollman} 6002708Swollman 6012708Swollmanstatic char * 6022708Swollman_add(str, pt, ptlim) 603130461Sstefanfconst char * str; 604130461Sstefanfchar * pt; 605130461Sstefanfconst char * const ptlim; 6062708Swollman{ 6072708Swollman while (pt < ptlim && (*pt = *str++) != '\0') 6082708Swollman ++pt; 609267798Spfg return (pt); 6102708Swollman} 611192625Sedwin 612192625Sedwin/* 613267798Spfg * POSIX and the C Standard are unclear or inconsistent about 614267798Spfg * what %C and %y do if the year is negative or exceeds 9999. 615267798Spfg * Use the convention that %C concatenated with %y yields the 616267798Spfg * same output as %Y, and that %Y contains at least 4 bytes, 617267798Spfg * with more only if necessary. 618267798Spfg */ 619192625Sedwin 620192625Sedwinstatic char * 621192625Sedwin_yconv(a, b, convert_top, convert_yy, pt, ptlim) 622192625Sedwinconst int a; 623192625Sedwinconst int b; 624192625Sedwinconst int convert_top; 625192625Sedwinconst int convert_yy; 626192625Sedwinchar * pt; 627192625Sedwinconst char * const ptlim; 628192625Sedwin{ 629192625Sedwin register int lead; 630192625Sedwin register int trail; 631192625Sedwin 632267798Spfg#define DIVISOR 100 633192625Sedwin trail = a % DIVISOR + b % DIVISOR; 634192625Sedwin lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; 635192625Sedwin trail %= DIVISOR; 636192625Sedwin if (trail < 0 && lead > 0) { 637192625Sedwin trail += DIVISOR; 638192625Sedwin --lead; 639192625Sedwin } else if (lead < 0 && trail > 0) { 640192625Sedwin trail -= DIVISOR; 641192625Sedwin ++lead; 642192625Sedwin } 643192625Sedwin if (convert_top) { 644192625Sedwin if (lead == 0 && trail < 0) 645192625Sedwin pt = _add("-0", pt, ptlim); 646192625Sedwin else pt = _conv(lead, "%02d", pt, ptlim); 647192625Sedwin } 648192625Sedwin if (convert_yy) 649192625Sedwin pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); 650267798Spfg return (pt); 651192625Sedwin} 652