strftime.c revision 30089
133965Sjdp/* 2218822Sdim * Copyright (c) 1989 The Regents of the University of California. 392828Sobrien * All rights reserved. 4218822Sdim * 5218822Sdim * Redistribution and use in source and binary forms are permitted 6218822Sdim * provided that the above copyright notice and this paragraph are 7218822Sdim * duplicated in all such forms and that any documentation, 8218822Sdim * advertising materials, and other materials related to such 9218822Sdim * distribution and use acknowledge that the software was developed 10218822Sdim * by the University of California, Berkeley. The name of the 11218822Sdim * University may not be used to endorse or promote products derived 12218822Sdim * from this software without specific prior written permission. 13218822Sdim * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14218822Sdim * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15218822Sdim * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16218822Sdim */ 17218822Sdim 18218822Sdim#ifdef LIBC_RCS 19218822Sdimstatic const char rcsid[] = 20218822Sdim "$Id: strftime.c,v 1.18 1997/08/09 15:43:53 joerg Exp $"; 21218822Sdim#endif 22218822Sdim 23218822Sdim#ifndef lint 24218822Sdim#ifndef NOID 25218822Sdimstatic const char elsieid[] = "@(#)strftime.c 7.38"; 26218822Sdim/* 27218822Sdim** Based on the UCB version with the ID appearing below. 28218822Sdim** This is ANSIish only when "multibyte character == plain character". 29218822Sdim*/ 30218822Sdim#endif /* !defined NOID */ 31218822Sdim#endif /* !defined lint */ 32218822Sdim 33218822Sdim#include "private.h" 34218822Sdim 35218822Sdim#ifndef LIBC_SCCS 36218822Sdim#ifndef lint 37218822Sdimstatic const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; 38218822Sdim#endif /* !defined lint */ 39218822Sdim#endif /* !defined LIBC_SCCS */ 40218822Sdim 41218822Sdim#include "tzfile.h" 42218822Sdim#include <fcntl.h> 43218822Sdim#include <sys/stat.h> 44218822Sdim#include "timelocal.h" 45218822Sdim 46218822Sdimstatic char * _add P((const char *, char *, const char *)); 47218822Sdimstatic char * _conv P((int, const char *, char *, const char *)); 48218822Sdimstatic char * _fmt P((const char *, const struct tm *, char *, const char *)); 49218822Sdimstatic char * _secs P((const struct tm *, char *, const char *)); 50218822Sdim 51218822Sdimsize_t strftime P((char *, size_t, const char *, const struct tm *)); 52218822Sdim 53218822Sdimextern char * tzname[]; 54218822Sdim 55218822Sdimsize_t 56218822Sdimstrftime(s, maxsize, format, t) 57218822Sdim char *const s; 58218822Sdim const size_t maxsize; 59218822Sdim const char *const format; 60218822Sdim const struct tm *const t; 61218822Sdim{ 62218822Sdim char *p; 63218822Sdim 64218822Sdim tzset(); 65218822Sdim p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize); 66218822Sdim if (p == s + maxsize) 67218822Sdim return 0; 68218822Sdim *p = '\0'; 69218822Sdim return p - s; 70218822Sdim} 71218822Sdim 72218822Sdimstatic char * 73218822Sdim_fmt(format, t, pt, ptlim) 74218822Sdim const char *format; 75218822Sdim const struct tm *const t; 76218822Sdim char *pt; 77218822Sdim const char *const ptlim; 78218822Sdim{ 79218822Sdim for ( ; *format; ++format) { 80218822Sdim if (*format == '%') { 81130561Sobrienlabel: 82130561Sobrien switch (*++format) { 83130561Sobrien case '\0': 84130561Sobrien --format; 85130561Sobrien break; 86130561Sobrien case 'A': 87130561Sobrien pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? 88130561Sobrien "?" : Locale->weekday[t->tm_wday], 89130561Sobrien pt, ptlim); 90130561Sobrien continue; 91130561Sobrien case 'a': 92130561Sobrien pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? 93130561Sobrien "?" : Locale->wday[t->tm_wday], 94130561Sobrien pt, ptlim); 95130561Sobrien continue; 96130561Sobrien case 'B': 97130561Sobrien pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? 98130561Sobrien "?" : Locale->month[t->tm_mon], 99130561Sobrien pt, ptlim); 100130561Sobrien continue; 101130561Sobrien case 'b': 102130561Sobrien case 'h': 103130561Sobrien pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? 104130561Sobrien "?" : Locale->mon[t->tm_mon], 105130561Sobrien pt, ptlim); 106130561Sobrien continue; 107130561Sobrien case 'C': 108130561Sobrien /* 109130561Sobrien ** %C used to do a... 110130561Sobrien ** _fmt("%a %b %e %X %Y", t); 111130561Sobrien ** ...whereas now POSIX 1003.2 calls for 112130561Sobrien ** something completely different. 113130561Sobrien ** (ado, 5/24/93) 114130561Sobrien */ 115130561Sobrien pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, 116130561Sobrien "%02d", pt, ptlim); 117130561Sobrien continue; 118130561Sobrien case 'c': 119130561Sobrien pt = _fmt(Locale->c_fmt, t, pt, ptlim); 120130561Sobrien continue; 121130561Sobrien case 'D': 122130561Sobrien pt = _fmt("%m/%d/%y", t, pt, ptlim); 123130561Sobrien continue; 124130561Sobrien case 'd': 125130561Sobrien pt = _conv(t->tm_mday, "%02d", pt, ptlim); 126130561Sobrien continue; 127130561Sobrien case 'E': 128130561Sobrien case 'O': 129130561Sobrien /* 130130561Sobrien ** POSIX locale extensions, a la 131130561Sobrien ** Arnold Robbins' strftime version 3.0. 132130561Sobrien ** The sequences 133130561Sobrien ** %Ec %EC %Ex %Ey %EY 134130561Sobrien ** %Od %oe %OH %OI %Om %OM 135104834Sobrien ** %OS %Ou %OU %OV %Ow %OW %Oy 136104834Sobrien ** are supposed to provide alternate 137130561Sobrien ** representations. 138130561Sobrien ** (ado, 5/24/93) 139104834Sobrien */ 140130561Sobrien goto label; 141104834Sobrien case 'e': 142130561Sobrien pt = _conv(t->tm_mday, "%2d", pt, ptlim); 143130561Sobrien continue; 144104834Sobrien case 'H': 145130561Sobrien pt = _conv(t->tm_hour, "%02d", pt, ptlim); 146130561Sobrien continue; 147130561Sobrien case 'I': 148104834Sobrien pt = _conv((t->tm_hour % 12) ? 14992828Sobrien (t->tm_hour % 12) : 12, 15092828Sobrien "%02d", pt, ptlim); 151130561Sobrien continue; 15291041Sobrien case 'j': 153130561Sobrien pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); 15491041Sobrien continue; 155130561Sobrien case 'k': 156130561Sobrien /* 157130561Sobrien ** This used to be... 158130561Sobrien ** _conv(t->tm_hour % 12 ? 15933965Sjdp ** t->tm_hour % 12 : 12, 2, ' '); 160130561Sobrien ** ...and has been changed to the below to 161130561Sobrien ** match SunOS 4.1.1 and Arnold Robbins' 16289857Sobrien ** strftime version 3.0. That is, "%k" and 163130561Sobrien ** "%l" have been swapped. 164130561Sobrien ** (ado, 5/24/93) 16589857Sobrien */ 166130561Sobrien pt = _conv(t->tm_hour, "%2d", pt, ptlim); 167130561Sobrien continue; 16889857Sobrien#ifdef KITCHEN_SINK 169130561Sobrien case 'K': 170130561Sobrien /* 171130561Sobrien ** After all this time, still unclaimed! 17289857Sobrien */ 17377298Sobrien pt = _add("kitchen sink", pt, ptlim); 17477298Sobrien continue; 175130561Sobrien#endif /* defined KITCHEN_SINK */ 17677298Sobrien case 'l': 177130561Sobrien /* 17877298Sobrien ** This used to be... 179130561Sobrien ** _conv(t->tm_hour, 2, ' '); 18077298Sobrien ** ...and has been changed to the below to 181130561Sobrien ** match SunOS 4.1.1 and Arnold Robbin's 18277298Sobrien ** strftime version 3.0. That is, "%k" and 183130561Sobrien ** "%l" have been swapped. 18477298Sobrien ** (ado, 5/24/93) 185130561Sobrien */ 18677298Sobrien pt = _conv((t->tm_hour % 12) ? 187130561Sobrien (t->tm_hour % 12) : 12, 18877298Sobrien "%2d", pt, ptlim); 189130561Sobrien continue; 19077298Sobrien case 'M': 191130561Sobrien pt = _conv(t->tm_min, "%02d", pt, ptlim); 19277298Sobrien continue; 193130561Sobrien case 'm': 194130561Sobrien pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); 195130561Sobrien continue; 196130561Sobrien case 'n': 19760484Sobrien pt = _add("\n", pt, ptlim); 19860484Sobrien continue; 199130561Sobrien case 'p': 200130561Sobrien pt = _add((t->tm_hour >= 12) ? 20161843Sobrien Locale->pm : 202130561Sobrien Locale->am, 20360484Sobrien pt, ptlim); 204130561Sobrien continue; 20560484Sobrien case 'R': 206130561Sobrien pt = _fmt("%H:%M", t, pt, ptlim); 20760484Sobrien continue; 208130561Sobrien case 'r': 20960484Sobrien pt = _fmt("%I:%M:%S %p", t, pt, ptlim); 210130561Sobrien continue; 21160484Sobrien case 'S': 212130561Sobrien pt = _conv(t->tm_sec, "%02d", pt, ptlim); 21360484Sobrien continue; 214130561Sobrien case 's': 21560484Sobrien pt = _secs(t, pt, ptlim); 216130561Sobrien continue; 21760484Sobrien case 'T': 218130561Sobrien pt = _fmt("%H:%M:%S", t, pt, ptlim); 21960484Sobrien continue; 220130561Sobrien case 't': 221130561Sobrien pt = _add("\t", pt, ptlim); 22260484Sobrien continue; 223130561Sobrien case 'U': 22460484Sobrien pt = _conv((t->tm_yday + 7 - t->tm_wday) / 7, 225130561Sobrien "%02d", pt, ptlim); 22660484Sobrien continue; 227130561Sobrien case 'u': 22860484Sobrien /* 229130561Sobrien ** From Arnold Robbins' strftime version 3.0: 230130561Sobrien ** "ISO 8601: Weekday as a decimal number 231130561Sobrien ** [1 (Monday) - 7]" 232130561Sobrien ** (ado, 5/24/93) 23360484Sobrien */ 234130561Sobrien pt = _conv((t->tm_wday == 0) ? 7 : t->tm_wday, 23560484Sobrien "%d", pt, ptlim); 236130561Sobrien continue; 23760484Sobrien case 'V': /* ISO 8601 week number */ 238130561Sobrien case 'G': /* ISO 8601 year (four digits) */ 23960484Sobrien case 'g': /* ISO 8601 year (two digits) */ 240130561Sobrien/* 24160484Sobrien** From Arnold Robbins' strftime version 3.0: "the week number of the 242130561Sobrien** year (the first Monday as the first day of week 1) as a decimal number 24361843Sobrien** (01-53)." 24438889Sjdp** (ado, 1993-05-24) 24538889Sjdp** 246130561Sobrien** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: 24738889Sjdp** "Week 01 of a year is per definition the first week which has the 248130561Sobrien** Thursday in this year, which is equivalent to the week which contains 249130561Sobrien** the fourth day of January. In other words, the first week of a new year 25038889Sjdp** is the week which has the majority of its days in the new year. Week 01 251130561Sobrien** might also contain days from the previous year and the week before week 25238889Sjdp** 01 of a year is the last week (52 or 53) of the previous year even if 253130561Sobrien** it contains days from the new year. A week starts with Monday (day 1) 254130561Sobrien** and ends with Sunday (day 7). For example, the first week of the year 25538889Sjdp** 1997 lasts from 1996-12-30 to 1997-01-05..." 256130561Sobrien** (ado, 1996-01-02) 25738889Sjdp*/ 25833965Sjdp { 25933965Sjdp int year; 260130561Sobrien int yday; 26133965Sjdp int wday; 262130561Sobrien int w; 26333965Sjdp 264130561Sobrien year = t->tm_year + TM_YEAR_BASE; 26533965Sjdp yday = t->tm_yday; 266130561Sobrien wday = t->tm_wday; 26733965Sjdp for ( ; ; ) { 268130561Sobrien int len; 269130561Sobrien int bot; 27033965Sjdp int top; 271130561Sobrien 272130561Sobrien len = isleap(year) ? 273130561Sobrien DAYSPERLYEAR : 274130561Sobrien DAYSPERNYEAR; 27533965Sjdp /* 276130561Sobrien ** What yday (-3 ... 3) does 27733965Sjdp ** the ISO year begin on? 278130561Sobrien */ 279130561Sobrien bot = ((yday + 11 - wday) % 28033965Sjdp DAYSPERWEEK) - 3; 281130561Sobrien /* 282130561Sobrien ** What yday does the NEXT 28333965Sjdp ** ISO year begin on? 28433965Sjdp */ 28533965Sjdp top = bot - 286130561Sobrien (len % DAYSPERWEEK); 287130561Sobrien if (top < -3) 288130561Sobrien top += DAYSPERWEEK; 289130561Sobrien top += len; 29033965Sjdp if (yday >= top) { 291130561Sobrien ++year; 29233965Sjdp w = 1; 293130561Sobrien break; 29433965Sjdp } 295130561Sobrien if (yday >= bot) { 29633965Sjdp w = 1 + ((yday - bot) / 297130561Sobrien DAYSPERWEEK); 29833965Sjdp break; 299130561Sobrien } 30033965Sjdp --year; 301130561Sobrien yday += isleap(year) ? 302130561Sobrien DAYSPERLYEAR : 303130561Sobrien DAYSPERNYEAR; 304130561Sobrien } 30533965Sjdp#ifdef XPG4_1994_04_09 306130561Sobrien if ((w == 52 30733965Sjdp && t->tm_mon == TM_JANUARY) 30833965Sjdp || (w == 1 30933965Sjdp && t->tm_mon == TM_DECEMBER)) 310130561Sobrien w = 53; 31133965Sjdp#endif /* defined XPG4_1994_04_09 */ 312130561Sobrien if (*format == 'V') 313130561Sobrien pt = _conv(w, "%02d", 314130561Sobrien pt, ptlim); 31533965Sjdp else if (*format == 'g') { 316130561Sobrien pt = _conv(year % 100, "%02d", 31733965Sjdp pt, ptlim); 318130561Sobrien } else pt = _conv(year, "%04d", 31933965Sjdp pt, ptlim); 320130561Sobrien } 32133965Sjdp continue; 32233965Sjdp case 'v': 32333965Sjdp /* 324130561Sobrien ** From Arnold Robbins' strftime version 3.0: 32533965Sjdp ** "date as dd-bbb-YYYY" 326130561Sobrien ** (ado, 5/24/93) 32733965Sjdp */ 328130561Sobrien pt = _fmt("%e-%b-%Y", t, pt, ptlim); 329130561Sobrien continue; 33033965Sjdp case 'W': 331130561Sobrien pt = _conv((t->tm_yday + 7 - 33233965Sjdp (t->tm_wday ? 333130561Sobrien (t->tm_wday - 1) : 6)) / 7, 334130561Sobrien "%02d", pt, ptlim); 335130561Sobrien continue; 336130561Sobrien case 'w': 33733965Sjdp pt = _conv(t->tm_wday, "%d", pt, ptlim); 338130561Sobrien continue; 33933965Sjdp case 'X': 340130561Sobrien pt = _fmt(Locale->X_fmt, t, pt, ptlim); 341130561Sobrien continue; 34233965Sjdp case 'x': 343130561Sobrien pt = _fmt(Locale->x_fmt, t, pt, ptlim); 34433965Sjdp continue; 345130561Sobrien case 'y': 346130561Sobrien pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, 34733965Sjdp "%02d", pt, ptlim); 348130561Sobrien continue; 349130561Sobrien case 'Y': 35033965Sjdp pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d", 351130561Sobrien pt, ptlim); 352130561Sobrien continue; 35333965Sjdp case 'Z': 354130561Sobrien if (t->tm_zone != NULL) 35533965Sjdp pt = _add(t->tm_zone, pt, ptlim); 35633965Sjdp else 35733965Sjdp if (t->tm_isdst == 0 || t->tm_isdst == 1) { 358130561Sobrien pt = _add(tzname[t->tm_isdst], 35933965Sjdp pt, ptlim); 360130561Sobrien } else pt = _add("?", pt, ptlim); 36133965Sjdp continue; 362130561Sobrien case '+': 363130561Sobrien pt = _fmt(Locale->date_fmt, t, pt, ptlim); 364130561Sobrien continue; 36533965Sjdp case '%': 366130561Sobrien /* 367130561Sobrien * X311J/88-090 (4.12.3.5): if conversion char is 368130561Sobrien * undefined, behavior is undefined. Print out the 369130561Sobrien * character itself as printf(3) also does. 370130561Sobrien */ 37133965Sjdp default: 372130561Sobrien break; 373130561Sobrien } 374130561Sobrien } 37533965Sjdp if (pt == ptlim) 376130561Sobrien break; 377130561Sobrien *pt++ = *format; 378130561Sobrien } 379130561Sobrien return pt; 38033965Sjdp} 381130561Sobrien 38233965Sjdpstatic char * 383130561Sobrien_conv(n, format, pt, ptlim) 384130561Sobrien const int n; 38533965Sjdp const char *const format; 386130561Sobrien char *const pt; 387130561Sobrien const char *const ptlim; 388130561Sobrien{ 389130561Sobrien char buf[INT_STRLEN_MAXIMUM(int) + 1]; 39033965Sjdp 391130561Sobrien (void) sprintf(buf, format, n); 392130561Sobrien return _add(buf, pt, ptlim); 393130561Sobrien} 394130561Sobrien 39533965Sjdpstatic char * 39633965Sjdp_secs(t, pt, ptlim) 39733965Sjdp const struct tm *t; 398130561Sobrien char *pt; 39933965Sjdp const char *ptlim; 400130561Sobrien{ 401130561Sobrien char buf[INT_STRLEN_MAXIMUM(int) + 1]; 402130561Sobrien register time_t s; 403130561Sobrien struct tm tmp; 404130561Sobrien 40533965Sjdp /* Make a copy, mktime(3) modifies the tm struct. */ 406130561Sobrien tmp = *t; 407130561Sobrien s = mktime(&tmp); 408130561Sobrien (void) sprintf(buf, "%ld", s); 40933965Sjdp return _add(buf, pt, ptlim); 410130561Sobrien} 411130561Sobrien 41233965Sjdpstatic char * 413130561Sobrien_add(str, pt, ptlim) 414130561Sobrien const char *str; 415130561Sobrien char *pt; 416130561Sobrien const char *const ptlim; 41733965Sjdp{ 418130561Sobrien while (pt < ptlim && (*pt = *str++) != '\0') 419130561Sobrien ++pt; 420130561Sobrien return pt; 421130561Sobrien} 422130561Sobrien