strftime.c revision 2708
12708Swollman#ifndef lint 22708Swollman#ifndef NOID 32708Swollmanstatic char elsieid[] = "@(#)strftime.c 7.19"; 42708Swollman/* 52708Swollman** Based on the UCB version with the ID appearing below. 62708Swollman** This is ANSIish only when time is treated identically in all locales and 72708Swollman** when "multibyte character == plain character". 82708Swollman*/ 92708Swollman#endif /* !defined NOID */ 102708Swollman#endif /* !defined lint */ 112708Swollman 122708Swollman#include "private.h" 132708Swollman 142708Swollman/* 152708Swollman * Copyright (c) 1989 The Regents of the University of California. 162708Swollman * All rights reserved. 172708Swollman * 182708Swollman * Redistribution and use in source and binary forms are permitted 192708Swollman * provided that the above copyright notice and this paragraph are 202708Swollman * duplicated in all such forms and that any documentation, 212708Swollman * advertising materials, and other materials related to such 222708Swollman * distribution and use acknowledge that the software was developed 232708Swollman * by the University of California, Berkeley. The name of the 242708Swollman * University may not be used to endorse or promote products derived 252708Swollman * from this software without specific prior written permission. 262708Swollman * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 272708Swollman * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 282708Swollman * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 292708Swollman */ 302708Swollman 312708Swollman#ifndef LIBC_SCCS 322708Swollman#ifndef lint 332708Swollmanstatic const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; 342708Swollman#endif /* !defined lint */ 352708Swollman#endif /* !defined LIBC_SCCS */ 362708Swollman 372708Swollman#include "tzfile.h" 382708Swollman 392708Swollmanstatic const char afmt[][4] = { 402708Swollman "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 412708Swollman}; 422708Swollmanstatic const char Afmt[][10] = { 432708Swollman "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", 442708Swollman "Saturday" 452708Swollman}; 462708Swollmanstatic const char bfmt[][4] = { 472708Swollman "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", 482708Swollman "Oct", "Nov", "Dec" 492708Swollman}; 502708Swollmanstatic const char Bfmt[][10] = { 512708Swollman "January", "February", "March", "April", "May", "June", "July", 522708Swollman "August", "September", "October", "November", "December" 532708Swollman}; 542708Swollman 552708Swollmanstatic char *_add P((const char *, char *, const char *)); 562708Swollmanstatic char *_conv P((int, const char *, char *, const char *)); 572708Swollmanstatic char *_fmt P((const char *, const struct tm *, char *, const char *)); 582708Swollman 592708Swollmansize_t strftime P((char *, size_t, const char *, const struct tm *)); 602708Swollman 612708Swollmanextern char *tzname[]; 622708Swollman 632708Swollmansize_t 642708Swollmanstrftime(s, maxsize, format, t) 652708Swollman char *s; 662708Swollman size_t maxsize; 672708Swollman const char *format; 682708Swollman const struct tm *t; 692708Swollman{ 702708Swollman char *p; 712708Swollman 722708Swollman p = _fmt(format, t, s, s + maxsize); 732708Swollman if (p == s + maxsize) 742708Swollman return 0; 752708Swollman *p = '\0'; 762708Swollman return p - s; 772708Swollman} 782708Swollman 792708Swollmanstatic char * 802708Swollman_fmt(format, t, pt, ptlim) 812708Swollman const char *format; 822708Swollman const struct tm *t; 832708Swollman char *pt; 842708Swollman const char *ptlim; 852708Swollman{ 862708Swollman for (; *format; ++format) { 872708Swollman if (*format == '%') { 882708Swollmanlabel: 892708Swollman switch(*++format) { 902708Swollman case '\0': 912708Swollman --format; 922708Swollman break; 932708Swollman case 'A': 942708Swollman pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? 952708Swollman "?" : Afmt[t->tm_wday], pt, ptlim); 962708Swollman continue; 972708Swollman case 'a': 982708Swollman pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? 992708Swollman "?" : afmt[t->tm_wday], pt, ptlim); 1002708Swollman continue; 1012708Swollman case 'B': 1022708Swollman pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? 1032708Swollman "?" : Bfmt[t->tm_mon], pt, ptlim); 1042708Swollman continue; 1052708Swollman case 'b': 1062708Swollman case 'h': 1072708Swollman pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? 1082708Swollman "?" : bfmt[t->tm_mon], pt, ptlim); 1092708Swollman continue; 1102708Swollman case 'c': 1112708Swollman pt = _fmt("%D %X", t, pt, ptlim); 1122708Swollman continue; 1132708Swollman case 'C': 1142708Swollman /* 1152708Swollman ** %C used to do a... 1162708Swollman ** _fmt("%a %b %e %X %Y", t); 1172708Swollman ** ...whereas now POSIX 1003.2 calls for 1182708Swollman ** something completely different. 1192708Swollman ** (ado, 5/24/93) 1202708Swollman */ 1212708Swollman pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, 1222708Swollman "%02d", pt, ptlim); 1232708Swollman continue; 1242708Swollman case 'D': 1252708Swollman pt = _fmt("%m/%d/%y", t, pt, ptlim); 1262708Swollman continue; 1272708Swollman case 'x': 1282708Swollman /* 1292708Swollman ** Version 3.0 of strftime from Arnold Robbins 1302708Swollman ** (arnold@skeeve.atl.ga.us) does the 1312708Swollman ** equivalent of... 1322708Swollman ** _fmt("%a %b %e %Y"); 1332708Swollman ** ...for %x; since the X3J11 C language 1342708Swollman ** standard calls for "date, using locale's 1352708Swollman ** date format," anything goes. Using just 1362708Swollman ** numbers (as here) makes Quakers happier. 1372708Swollman ** Word from Paul Eggert (eggert@twinsun.com) 1382708Swollman ** is that %Y-%m-%d is the ISO standard date 1392708Swollman ** format, specified in ISO 2014 and later 1402708Swollman ** ISO 8601:1988, with a summary available in 1412708Swollman ** pub/doc/ISO/english/ISO8601.ps.Z on 1422708Swollman ** ftp.uni-erlangen.de. 1432708Swollman ** (ado, 5/30/93) 1442708Swollman */ 1452708Swollman pt = _fmt("%m/%d/%y", t, pt, ptlim); 1462708Swollman continue; 1472708Swollman case 'd': 1482708Swollman pt = _conv(t->tm_mday, "%02d", pt, ptlim); 1492708Swollman continue; 1502708Swollman case 'E': 1512708Swollman case 'O': 1522708Swollman /* 1532708Swollman ** POSIX locale extensions, a la 1542708Swollman ** Arnold Robbins' strftime version 3.0. 1552708Swollman ** The sequences 1562708Swollman ** %Ec %EC %Ex %Ey %EY 1572708Swollman ** %Od %oe %OH %OI %Om %OM 1582708Swollman ** %OS %Ou %OU %OV %Ow %OW %Oy 1592708Swollman ** are supposed to provide alternate 1602708Swollman ** representations. 1612708Swollman ** (ado, 5/24/93) 1622708Swollman */ 1632708Swollman goto label; 1642708Swollman case 'e': 1652708Swollman pt = _conv(t->tm_mday, "%2d", pt, ptlim); 1662708Swollman continue; 1672708Swollman case 'H': 1682708Swollman pt = _conv(t->tm_hour, "%02d", pt, ptlim); 1692708Swollman continue; 1702708Swollman case 'I': 1712708Swollman pt = _conv((t->tm_hour % 12) ? 1722708Swollman (t->tm_hour % 12) : 12, 1732708Swollman "%02d", pt, ptlim); 1742708Swollman continue; 1752708Swollman case 'j': 1762708Swollman pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); 1772708Swollman continue; 1782708Swollman case 'k': 1792708Swollman /* 1802708Swollman ** This used to be... 1812708Swollman ** _conv(t->tm_hour % 12 ? 1822708Swollman ** t->tm_hour % 12 : 12, 2, ' '); 1832708Swollman ** ...and has been changed to the below to 1842708Swollman ** match SunOS 4.1.1 and Arnold Robbins' 1852708Swollman ** strftime version 3.0. That is, "%k" and 1862708Swollman ** "%l" have been swapped. 1872708Swollman ** (ado, 5/24/93) 1882708Swollman */ 1892708Swollman pt = _conv(t->tm_hour, "%2d", pt, ptlim); 1902708Swollman continue; 1912708Swollman#ifdef KITCHEN_SINK 1922708Swollman case 'K': 1932708Swollman /* 1942708Swollman ** After all this time, still unclaimed! 1952708Swollman */ 1962708Swollman pt = _add("kitchen sink", pt, ptlim); 1972708Swollman continue; 1982708Swollman#endif /* defined KITCHEN_SINK */ 1992708Swollman case 'l': 2002708Swollman /* 2012708Swollman ** This used to be... 2022708Swollman ** _conv(t->tm_hour, 2, ' '); 2032708Swollman ** ...and has been changed to the below to 2042708Swollman ** match SunOS 4.1.1 and Arnold Robbin's 2052708Swollman ** strftime version 3.0. That is, "%k" and 2062708Swollman ** "%l" have been swapped. 2072708Swollman ** (ado, 5/24/93) 2082708Swollman */ 2092708Swollman pt = _conv((t->tm_hour % 12) ? 2102708Swollman (t->tm_hour % 12) : 12, 2112708Swollman "%2d", pt, ptlim); 2122708Swollman continue; 2132708Swollman case 'M': 2142708Swollman pt = _conv(t->tm_min, "%02d", pt, ptlim); 2152708Swollman continue; 2162708Swollman case 'm': 2172708Swollman pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); 2182708Swollman continue; 2192708Swollman case 'n': 2202708Swollman pt = _add("\n", pt, ptlim); 2212708Swollman continue; 2222708Swollman case 'p': 2232708Swollman pt = _add(t->tm_hour >= 12 ? "PM" : "AM", 2242708Swollman pt, ptlim); 2252708Swollman continue; 2262708Swollman case 'R': 2272708Swollman pt = _fmt("%H:%M", t, pt, ptlim); 2282708Swollman continue; 2292708Swollman case 'r': 2302708Swollman pt = _fmt("%I:%M:%S %p", t, pt, ptlim); 2312708Swollman continue; 2322708Swollman case 'S': 2332708Swollman pt = _conv(t->tm_sec, "%02d", pt, ptlim); 2342708Swollman continue; 2352708Swollman case 'T': 2362708Swollman case 'X': 2372708Swollman pt = _fmt("%H:%M:%S", t, pt, ptlim); 2382708Swollman continue; 2392708Swollman case 't': 2402708Swollman pt = _add("\t", pt, ptlim); 2412708Swollman continue; 2422708Swollman case 'U': 2432708Swollman pt = _conv((t->tm_yday + 7 - t->tm_wday) / 7, 2442708Swollman "%02d", pt, ptlim); 2452708Swollman continue; 2462708Swollman case 'u': 2472708Swollman /* 2482708Swollman ** From Arnold Robbins' strftime version 3.0: 2492708Swollman ** "ISO 8601: Weekday as a decimal number 2502708Swollman ** [1 (Monday) - 7]" 2512708Swollman ** (ado, 5/24/93) 2522708Swollman */ 2532708Swollman pt = _conv((t->tm_wday == 0) ? 7 : t->tm_wday, 2542708Swollman "%d", pt, ptlim); 2552708Swollman continue; 2562708Swollman case 'V': 2572708Swollman /* 2582708Swollman ** From Arnold Robbins' strftime version 3.0: 2592708Swollman ** "the week number of the year (the first 2602708Swollman ** Monday as the first day of week 1) as a 2612708Swollman ** decimal number (01-53). The method for 2622708Swollman ** determining the week number is as specified 2632708Swollman ** by ISO 8601 (to wit: if the week containing 2642708Swollman ** January 1 has four or more days in the new 2652708Swollman ** year, then it is week 1, otherwise it is 2662708Swollman ** week 53 of the previous year and the next 2672708Swollman ** week is week 1)." 2682708Swollman ** (ado, 5/24/93) 2692708Swollman */ 2702708Swollman /* 2712708Swollman ** XXX--If January 1 falls on a Friday, 2722708Swollman ** January 1-3 are part of week 53 of the 2732708Swollman ** previous year. By analogy, if January 2742708Swollman ** 1 falls on a Thursday, are December 29-31 2752708Swollman ** of the PREVIOUS year part of week 1??? 2762708Swollman ** (ado 5/24/93) 2772708Swollman ** 2782708Swollman ** You are understood not to expect this. 2792708Swollman */ 2802708Swollman { 2812708Swollman int i; 2822708Swollman 2832708Swollman i = (t->tm_yday + 10 - (t->tm_wday ? 2842708Swollman (t->tm_wday - 1) : 6)) / 7; 2852708Swollman if (i == 0) { 2862708Swollman /* 2872708Swollman ** What day of the week does 2882708Swollman ** January 1 fall on? 2892708Swollman */ 2902708Swollman i = t->tm_wday - 2912708Swollman (t->tm_yday - 1); 2922708Swollman /* 2932708Swollman ** Fri Jan 1: 53 2942708Swollman ** Sun Jan 1: 52 2952708Swollman ** Sat Jan 1: 53 if previous 2962708Swollman ** year a leap 2972708Swollman ** year, else 52 2982708Swollman */ 2992708Swollman if (i == TM_FRIDAY) 3002708Swollman i = 53; 3012708Swollman else if (i == TM_SUNDAY) 3022708Swollman i = 52; 3032708Swollman else i = isleap(t->tm_year + 3042708Swollman TM_YEAR_BASE) ? 3052708Swollman 53 : 52; 3062708Swollman#ifdef XPG4_1994_04_09 3072708Swollman /* 3082708Swollman ** As of 4/9/94, though, 3092708Swollman ** XPG4 calls for 53 3102708Swollman ** unconditionally. 3112708Swollman */ 3122708Swollman i = 53; 3132708Swollman#endif /* defined XPG4_1994_04_09 */ 3142708Swollman } 3152708Swollman pt = _conv(i, "%02d", pt, ptlim); 3162708Swollman } 3172708Swollman continue; 3182708Swollman case 'v': 3192708Swollman /* 3202708Swollman ** From Arnold Robbins' strftime version 3.0: 3212708Swollman ** "date as dd-bbb-YYYY" 3222708Swollman ** (ado, 5/24/93) 3232708Swollman */ 3242708Swollman pt = _fmt("%e-%b-%Y", t, pt, ptlim); 3252708Swollman continue; 3262708Swollman case 'W': 3272708Swollman pt = _conv((t->tm_yday + 7 - 3282708Swollman (t->tm_wday ? 3292708Swollman (t->tm_wday - 1) : 6)) / 7, 3302708Swollman "%02d", pt, ptlim); 3312708Swollman continue; 3322708Swollman case 'w': 3332708Swollman pt = _conv(t->tm_wday, "%d", pt, ptlim); 3342708Swollman continue; 3352708Swollman case 'y': 3362708Swollman pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, 3372708Swollman "%02d", pt, ptlim); 3382708Swollman continue; 3392708Swollman case 'Y': 3402708Swollman pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d", 3412708Swollman pt, ptlim); 3422708Swollman continue; 3432708Swollman case 'Z': 3442708Swollman#ifdef TM_ZONE 3452708Swollman if (t->TM_ZONE) 3462708Swollman pt = _add(t->TM_ZONE, pt, ptlim); 3472708Swollman else 3482708Swollman#endif /* defined TM_ZONE */ 3492708Swollman if (t->tm_isdst == 0 || t->tm_isdst == 1) { 3502708Swollman pt = _add(tzname[t->tm_isdst], 3512708Swollman pt, ptlim); 3522708Swollman } else pt = _add("?", pt, ptlim); 3532708Swollman continue; 3542708Swollman case '%': 3552708Swollman /* 3562708Swollman * X311J/88-090 (4.12.3.5): if conversion char is 3572708Swollman * undefined, behavior is undefined. Print out the 3582708Swollman * character itself as printf(3) also does. 3592708Swollman */ 3602708Swollman default: 3612708Swollman break; 3622708Swollman } 3632708Swollman } 3642708Swollman if (pt == ptlim) 3652708Swollman break; 3662708Swollman *pt++ = *format; 3672708Swollman } 3682708Swollman return pt; 3692708Swollman} 3702708Swollman 3712708Swollmanstatic char * 3722708Swollman_conv(n, format, pt, ptlim) 3732708Swollman int n; 3742708Swollman const char *format; 3752708Swollman char *pt; 3762708Swollman const char *ptlim; 3772708Swollman{ 3782708Swollman char buf[INT_STRLEN_MAXIMUM(int) + 1]; 3792708Swollman 3802708Swollman (void) sprintf(buf, format, n); 3812708Swollman return _add(buf, pt, ptlim); 3822708Swollman} 3832708Swollman 3842708Swollmanstatic char * 3852708Swollman_add(str, pt, ptlim) 3862708Swollman const char *str; 3872708Swollman char *pt; 3882708Swollman const char *ptlim; 3892708Swollman{ 3902708Swollman while (pt < ptlim && (*pt = *str++) != '\0') 3912708Swollman ++pt; 3922708Swollman return pt; 3932708Swollman} 394