strftime.c revision 56756
12708Swollman/* 22708Swollman * Copyright (c) 1989 The Regents of the University of California. 32708Swollman * All rights reserved. 42708Swollman * 52708Swollman * Redistribution and use in source and binary forms are permitted 62708Swollman * provided that the above copyright notice and this paragraph are 72708Swollman * duplicated in all such forms and that any documentation, 82708Swollman * advertising materials, and other materials related to such 92708Swollman * distribution and use acknowledge that the software was developed 102708Swollman * by the University of California, Berkeley. The name of the 112708Swollman * University may not be used to endorse or promote products derived 122708Swollman * from this software without specific prior written permission. 132708Swollman * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 142708Swollman * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 152708Swollman * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 162708Swollman */ 172708Swollman 189912Swollman#ifdef LIBC_RCS 199912Swollmanstatic const char rcsid[] = 2050476Speter "$FreeBSD: head/lib/libc/stdtime/strftime.c 56756 2000-01-28 17:40:42Z joerg $"; 219912Swollman#endif 229912Swollman 239912Swollman#ifndef lint 249912Swollman#ifndef NOID 2517224Swollmanstatic const char elsieid[] = "@(#)strftime.c 7.38"; 269912Swollman/* 279912Swollman** Based on the UCB version with the ID appearing below. 289912Swollman** This is ANSIish only when "multibyte character == plain character". 299912Swollman*/ 309912Swollman#endif /* !defined NOID */ 319912Swollman#endif /* !defined lint */ 329912Swollman 339912Swollman#include "private.h" 349912Swollman 352708Swollman#ifndef LIBC_SCCS 362708Swollman#ifndef lint 379912Swollmanstatic const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; 382708Swollman#endif /* !defined lint */ 392708Swollman#endif /* !defined LIBC_SCCS */ 402708Swollman 412708Swollman#include "tzfile.h" 429912Swollman#include <fcntl.h> 439912Swollman#include <sys/stat.h> 4428021Sjoerg#include "timelocal.h" 452708Swollman 469912Swollmanstatic char * _add P((const char *, char *, const char *)); 479912Swollmanstatic char * _conv P((int, const char *, char *, const char *)); 489912Swollmanstatic char * _fmt P((const char *, const struct tm *, char *, const char *)); 492708Swollman 502708Swollmansize_t strftime P((char *, size_t, const char *, const struct tm *)); 512708Swollman 529912Swollmanextern char * tzname[]; 532708Swollman 542708Swollmansize_t 552708Swollmanstrftime(s, maxsize, format, t) 5617224Swollman char *const s; 5717224Swollman const size_t maxsize; 5817224Swollman const char *const format; 5917224Swollman const struct tm *const t; 602708Swollman{ 6117224Swollman char *p; 622708Swollman 639912Swollman tzset(); 649912Swollman p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize); 652708Swollman if (p == s + maxsize) 662708Swollman return 0; 672708Swollman *p = '\0'; 682708Swollman return p - s; 692708Swollman} 702708Swollman 712708Swollmanstatic char * 722708Swollman_fmt(format, t, pt, ptlim) 7317224Swollman const char *format; 7417224Swollman const struct tm *const t; 7517224Swollman char *pt; 7617224Swollman const char *const ptlim; 772708Swollman{ 7853940Sache int Ealternative, Oalternative; 7953940Sache 809912Swollman for ( ; *format; ++format) { 812708Swollman if (*format == '%') { 8253940Sache Ealternative = 0; 8353940Sache Oalternative = 0; 842708Swollmanlabel: 859912Swollman switch (*++format) { 862708Swollman case '\0': 872708Swollman --format; 882708Swollman break; 892708Swollman case 'A': 902708Swollman pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? 919912Swollman "?" : Locale->weekday[t->tm_wday], 929912Swollman pt, ptlim); 932708Swollman continue; 942708Swollman case 'a': 952708Swollman pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? 969912Swollman "?" : Locale->wday[t->tm_wday], 979912Swollman pt, ptlim); 982708Swollman continue; 992708Swollman case 'B': 10051186Sdt pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? 10153940Sache "?" : (Oalternative ? Locale->alt_month : 10251186Sdt Locale->month)[t->tm_mon], 1039912Swollman pt, ptlim); 1042708Swollman continue; 1052708Swollman case 'b': 1062708Swollman case 'h': 1072708Swollman pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? 1089912Swollman "?" : Locale->mon[t->tm_mon], 1099912Swollman pt, ptlim); 1102708Swollman continue; 1112708Swollman case 'C': 1122708Swollman /* 1132708Swollman ** %C used to do a... 1142708Swollman ** _fmt("%a %b %e %X %Y", t); 1152708Swollman ** ...whereas now POSIX 1003.2 calls for 1162708Swollman ** something completely different. 1172708Swollman ** (ado, 5/24/93) 1182708Swollman */ 1192708Swollman pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, 1202708Swollman "%02d", pt, ptlim); 1212708Swollman continue; 1229912Swollman case 'c': 1239912Swollman pt = _fmt(Locale->c_fmt, t, pt, ptlim); 1249912Swollman continue; 1252708Swollman case 'D': 1262708Swollman pt = _fmt("%m/%d/%y", t, pt, ptlim); 1272708Swollman continue; 1282708Swollman case 'd': 1292708Swollman pt = _conv(t->tm_mday, "%02d", pt, ptlim); 1302708Swollman continue; 1312708Swollman case 'E': 13253960Sache if (Ealternative || Oalternative) 13353960Sache break; 13453940Sache Ealternative++; 13553940Sache goto label; 1362708Swollman case 'O': 1372708Swollman /* 1382708Swollman ** POSIX locale extensions, a la 1392708Swollman ** Arnold Robbins' strftime version 3.0. 1402708Swollman ** The sequences 14153960Sache ** %Ec %EC %Ex %EX %Ey %EY 1422708Swollman ** %Od %oe %OH %OI %Om %OM 1432708Swollman ** %OS %Ou %OU %OV %Ow %OW %Oy 1442708Swollman ** are supposed to provide alternate 1452708Swollman ** representations. 1462708Swollman ** (ado, 5/24/93) 14753960Sache ** 14853960Sache ** FreeBSD extensions 14953960Sache ** %OB %Ef %EF 1502708Swollman */ 15153960Sache if (Ealternative || Oalternative) 15253960Sache break; 15353940Sache Oalternative++; 1542708Swollman goto label; 1552708Swollman case 'e': 1562708Swollman pt = _conv(t->tm_mday, "%2d", pt, ptlim); 1572708Swollman continue; 15853960Sache case 'f': 15953960Sache if (!Ealternative) 16053960Sache break; 16153960Sache pt = _fmt(Locale->Ef_fmt, t, pt, ptlim); 16253960Sache continue; 16353960Sache case 'F': 16453960Sache if (!Ealternative) 16553960Sache break; 16653960Sache pt = _fmt(Locale->EF_fmt, t, pt, ptlim); 16753960Sache continue; 1682708Swollman case 'H': 1692708Swollman pt = _conv(t->tm_hour, "%02d", pt, ptlim); 1702708Swollman continue; 1712708Swollman case 'I': 1722708Swollman pt = _conv((t->tm_hour % 12) ? 1732708Swollman (t->tm_hour % 12) : 12, 1742708Swollman "%02d", pt, ptlim); 1752708Swollman continue; 1762708Swollman case 'j': 1772708Swollman pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); 1782708Swollman continue; 1792708Swollman case 'k': 1802708Swollman /* 1812708Swollman ** This used to be... 1822708Swollman ** _conv(t->tm_hour % 12 ? 1832708Swollman ** t->tm_hour % 12 : 12, 2, ' '); 1842708Swollman ** ...and has been changed to the below to 1852708Swollman ** match SunOS 4.1.1 and Arnold Robbins' 1862708Swollman ** strftime version 3.0. That is, "%k" and 1872708Swollman ** "%l" have been swapped. 1882708Swollman ** (ado, 5/24/93) 1892708Swollman */ 1902708Swollman pt = _conv(t->tm_hour, "%2d", pt, ptlim); 1912708Swollman continue; 1922708Swollman#ifdef KITCHEN_SINK 1932708Swollman case 'K': 1942708Swollman /* 1952708Swollman ** After all this time, still unclaimed! 1962708Swollman */ 1972708Swollman pt = _add("kitchen sink", pt, ptlim); 1982708Swollman continue; 1992708Swollman#endif /* defined KITCHEN_SINK */ 2002708Swollman case 'l': 2012708Swollman /* 2022708Swollman ** This used to be... 2032708Swollman ** _conv(t->tm_hour, 2, ' '); 2042708Swollman ** ...and has been changed to the below to 2052708Swollman ** match SunOS 4.1.1 and Arnold Robbin's 2062708Swollman ** strftime version 3.0. That is, "%k" and 2072708Swollman ** "%l" have been swapped. 2082708Swollman ** (ado, 5/24/93) 2092708Swollman */ 2102708Swollman pt = _conv((t->tm_hour % 12) ? 2112708Swollman (t->tm_hour % 12) : 12, 2122708Swollman "%2d", pt, ptlim); 2132708Swollman continue; 2142708Swollman case 'M': 2152708Swollman pt = _conv(t->tm_min, "%02d", pt, ptlim); 2162708Swollman continue; 2172708Swollman case 'm': 2182708Swollman pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); 2192708Swollman continue; 2202708Swollman case 'n': 2212708Swollman pt = _add("\n", pt, ptlim); 2222708Swollman continue; 2232708Swollman case 'p': 2249912Swollman pt = _add((t->tm_hour >= 12) ? 2259912Swollman Locale->pm : 2269912Swollman Locale->am, 2272708Swollman pt, ptlim); 2282708Swollman continue; 2292708Swollman case 'R': 2302708Swollman pt = _fmt("%H:%M", t, pt, ptlim); 2312708Swollman continue; 2322708Swollman case 'r': 2332708Swollman pt = _fmt("%I:%M:%S %p", t, pt, ptlim); 2342708Swollman continue; 2352708Swollman case 'S': 2362708Swollman pt = _conv(t->tm_sec, "%02d", pt, ptlim); 2372708Swollman continue; 2389988Sache case 's': 23937299Sbde { 24037299Sbde struct tm tm; 24137299Sbde char buf[INT_STRLEN_MAXIMUM( 24237299Sbde time_t) + 1]; 24337299Sbde time_t mkt; 24437299Sbde 24537299Sbde tm = *t; 24637299Sbde mkt = mktime(&tm); 24737299Sbde if (TYPE_SIGNED(time_t)) 24837299Sbde (void) sprintf(buf, "%ld", 24937299Sbde (long) mkt); 25037299Sbde else (void) sprintf(buf, "%lu", 25137299Sbde (unsigned long) mkt); 25237299Sbde pt = _add(buf, pt, ptlim); 25337299Sbde } 2549988Sache continue; 2552708Swollman case 'T': 2562708Swollman pt = _fmt("%H:%M:%S", t, pt, ptlim); 2572708Swollman continue; 2582708Swollman case 't': 2592708Swollman pt = _add("\t", pt, ptlim); 2602708Swollman continue; 2612708Swollman case 'U': 2622708Swollman pt = _conv((t->tm_yday + 7 - t->tm_wday) / 7, 2632708Swollman "%02d", pt, ptlim); 2642708Swollman continue; 2652708Swollman case 'u': 2662708Swollman /* 2672708Swollman ** From Arnold Robbins' strftime version 3.0: 2682708Swollman ** "ISO 8601: Weekday as a decimal number 2692708Swollman ** [1 (Monday) - 7]" 2702708Swollman ** (ado, 5/24/93) 2712708Swollman */ 2722708Swollman pt = _conv((t->tm_wday == 0) ? 7 : t->tm_wday, 2732708Swollman "%d", pt, ptlim); 2742708Swollman continue; 27530089Shelbig case 'V': /* ISO 8601 week number */ 27630089Shelbig case 'G': /* ISO 8601 year (four digits) */ 27730089Shelbig case 'g': /* ISO 8601 year (two digits) */ 27830089Shelbig/* 27930089Shelbig** From Arnold Robbins' strftime version 3.0: "the week number of the 28030089Shelbig** year (the first Monday as the first day of week 1) as a decimal number 28130089Shelbig** (01-53)." 28230089Shelbig** (ado, 1993-05-24) 28330089Shelbig** 28430089Shelbig** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: 28530089Shelbig** "Week 01 of a year is per definition the first week which has the 28630089Shelbig** Thursday in this year, which is equivalent to the week which contains 28730089Shelbig** the fourth day of January. In other words, the first week of a new year 28830089Shelbig** is the week which has the majority of its days in the new year. Week 01 28930089Shelbig** might also contain days from the previous year and the week before week 29030089Shelbig** 01 of a year is the last week (52 or 53) of the previous year even if 29130089Shelbig** it contains days from the new year. A week starts with Monday (day 1) 29230089Shelbig** and ends with Sunday (day 7). For example, the first week of the year 29330089Shelbig** 1997 lasts from 1996-12-30 to 1997-01-05..." 29430089Shelbig** (ado, 1996-01-02) 29530089Shelbig*/ 2962708Swollman { 29730089Shelbig int year; 29830089Shelbig int yday; 29930089Shelbig int wday; 30030089Shelbig int w; 3012708Swollman 30230089Shelbig year = t->tm_year + TM_YEAR_BASE; 30330089Shelbig yday = t->tm_yday; 30430089Shelbig wday = t->tm_wday; 30530089Shelbig for ( ; ; ) { 30630089Shelbig int len; 30730089Shelbig int bot; 30830089Shelbig int top; 30930089Shelbig 31030089Shelbig len = isleap(year) ? 31130089Shelbig DAYSPERLYEAR : 31230089Shelbig DAYSPERNYEAR; 3132708Swollman /* 31430089Shelbig ** What yday (-3 ... 3) does 31530089Shelbig ** the ISO year begin on? 3162708Swollman */ 31730089Shelbig bot = ((yday + 11 - wday) % 31830089Shelbig DAYSPERWEEK) - 3; 3192708Swollman /* 32030089Shelbig ** What yday does the NEXT 32130089Shelbig ** ISO year begin on? 3222708Swollman */ 32330089Shelbig top = bot - 32430089Shelbig (len % DAYSPERWEEK); 32530089Shelbig if (top < -3) 32630089Shelbig top += DAYSPERWEEK; 32730089Shelbig top += len; 32830089Shelbig if (yday >= top) { 32930089Shelbig ++year; 33030089Shelbig w = 1; 33130089Shelbig break; 33230089Shelbig } 33330089Shelbig if (yday >= bot) { 33430089Shelbig w = 1 + ((yday - bot) / 33530089Shelbig DAYSPERWEEK); 33630089Shelbig break; 33730089Shelbig } 33830089Shelbig --year; 33930089Shelbig yday += isleap(year) ? 34030089Shelbig DAYSPERLYEAR : 34130089Shelbig DAYSPERNYEAR; 34230089Shelbig } 3432708Swollman#ifdef XPG4_1994_04_09 34430089Shelbig if ((w == 52 34530089Shelbig && t->tm_mon == TM_JANUARY) 34630089Shelbig || (w == 1 34730089Shelbig && t->tm_mon == TM_DECEMBER)) 34830089Shelbig w = 53; 3492708Swollman#endif /* defined XPG4_1994_04_09 */ 35030089Shelbig if (*format == 'V') 35130089Shelbig pt = _conv(w, "%02d", 35230089Shelbig pt, ptlim); 35330089Shelbig else if (*format == 'g') { 35430089Shelbig pt = _conv(year % 100, "%02d", 35530089Shelbig pt, ptlim); 35630089Shelbig } else pt = _conv(year, "%04d", 35730089Shelbig pt, ptlim); 3582708Swollman } 3592708Swollman continue; 3602708Swollman case 'v': 3612708Swollman /* 3622708Swollman ** From Arnold Robbins' strftime version 3.0: 3632708Swollman ** "date as dd-bbb-YYYY" 3642708Swollman ** (ado, 5/24/93) 3652708Swollman */ 3662708Swollman pt = _fmt("%e-%b-%Y", t, pt, ptlim); 3672708Swollman continue; 3682708Swollman case 'W': 3692708Swollman pt = _conv((t->tm_yday + 7 - 3702708Swollman (t->tm_wday ? 3712708Swollman (t->tm_wday - 1) : 6)) / 7, 3722708Swollman "%02d", pt, ptlim); 3732708Swollman continue; 3742708Swollman case 'w': 3752708Swollman pt = _conv(t->tm_wday, "%d", pt, ptlim); 3762708Swollman continue; 3779912Swollman case 'X': 3789912Swollman pt = _fmt(Locale->X_fmt, t, pt, ptlim); 3799912Swollman continue; 3809912Swollman case 'x': 38153960Sache pt = _fmt(Locale->x_fmt, t, pt, ptlim); 3829912Swollman continue; 3832708Swollman case 'y': 3842708Swollman pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, 3852708Swollman "%02d", pt, ptlim); 3862708Swollman continue; 3872708Swollman case 'Y': 3882708Swollman pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d", 3892708Swollman pt, ptlim); 3902708Swollman continue; 3912708Swollman case 'Z': 3929912Swollman if (t->tm_zone != NULL) 3939912Swollman pt = _add(t->tm_zone, pt, ptlim); 3942708Swollman else 3952708Swollman if (t->tm_isdst == 0 || t->tm_isdst == 1) { 3962708Swollman pt = _add(tzname[t->tm_isdst], 3972708Swollman pt, ptlim); 3982708Swollman } else pt = _add("?", pt, ptlim); 3992708Swollman continue; 40056756Sjoerg case 'z': 40156756Sjoerg { 40256756Sjoerg long absoff; 40356756Sjoerg if (t->tm_gmtoff >= 0) { 40456756Sjoerg absoff = t->tm_gmtoff; 40556756Sjoerg pt = _add("+", pt, ptlim); 40656756Sjoerg } else { 40756756Sjoerg absoff = -t->tm_gmtoff; 40856756Sjoerg pt = _add("-", pt, ptlim); 40956756Sjoerg } 41056756Sjoerg pt = _conv(absoff / 3600, "%02d", 41156756Sjoerg pt, ptlim); 41256756Sjoerg pt = _conv((absoff % 3600) / 60, "%02d", 41356756Sjoerg pt, ptlim); 41456756Sjoerg }; 41556756Sjoerg continue; 4169912Swollman case '+': 4179912Swollman pt = _fmt(Locale->date_fmt, t, pt, ptlim); 4189912Swollman continue; 4192708Swollman case '%': 4202708Swollman /* 4212708Swollman * X311J/88-090 (4.12.3.5): if conversion char is 4222708Swollman * undefined, behavior is undefined. Print out the 4232708Swollman * character itself as printf(3) also does. 4242708Swollman */ 4252708Swollman default: 4262708Swollman break; 4272708Swollman } 4282708Swollman } 4292708Swollman if (pt == ptlim) 4302708Swollman break; 4312708Swollman *pt++ = *format; 4322708Swollman } 4332708Swollman return pt; 4342708Swollman} 4352708Swollman 4362708Swollmanstatic char * 4372708Swollman_conv(n, format, pt, ptlim) 43817224Swollman const int n; 43917224Swollman const char *const format; 44017224Swollman char *const pt; 44117224Swollman const char *const ptlim; 4422708Swollman{ 4439912Swollman char buf[INT_STRLEN_MAXIMUM(int) + 1]; 4442708Swollman 4452708Swollman (void) sprintf(buf, format, n); 4462708Swollman return _add(buf, pt, ptlim); 4472708Swollman} 4482708Swollman 4492708Swollmanstatic char * 4502708Swollman_add(str, pt, ptlim) 45117224Swollman const char *str; 45217224Swollman char *pt; 45317224Swollman const char *const ptlim; 4542708Swollman{ 4552708Swollman while (pt < ptlim && (*pt = *str++) != '\0') 4562708Swollman ++pt; 4572708Swollman return pt; 4582708Swollman} 459