strftime.c revision 74412
1254721Semaste/* 2254721Semaste * Copyright (c) 1989 The Regents of the University of California. 3353358Sdim * All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms are permitted 6254721Semaste * provided that the above copyright notice and this paragraph are 7254721Semaste * duplicated in all such forms and that any documentation, 8254721Semaste * advertising materials, and other materials related to such 9254721Semaste * distribution and use acknowledge that the software was developed 10353358Sdim * by the University of California, Berkeley. The name of the 11353358Sdim * University may not be used to endorse or promote products derived 12254721Semaste * from this software without specific prior written permission. 13360784Sdim * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14254721Semaste * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15254721Semaste * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16321369Sdim */ 17321369Sdim 18254721Semaste#ifdef LIBC_RCS 19254721Semastestatic const char rcsid[] = 20254721Semaste "$FreeBSD: head/lib/libc/stdtime/strftime.c 74412 2001-03-18 11:58:15Z ache $"; 21254721Semaste#endif 22360784Sdim 23360784Sdim#ifndef lint 24360784Sdim#ifndef NOID 25360784Sdimstatic const char elsieid[] = "@(#)strftime.c 7.38"; 26360784Sdim/* 27360784Sdim** Based on the UCB version with the ID appearing below. 28360784Sdim** This is ANSIish only when "multibyte character == plain character". 29360784Sdim*/ 30360784Sdim#endif /* !defined NOID */ 31360784Sdim#endif /* !defined lint */ 32360784Sdim 33360784Sdim#include "namespace.h" 34360784Sdim#include "private.h" 35360784Sdim 36360784Sdim#ifndef LIBC_SCCS 37360784Sdim#ifndef lint 38360784Sdimstatic const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; 39360784Sdim#endif /* !defined lint */ 40360784Sdim#endif /* !defined LIBC_SCCS */ 41360784Sdim 42360784Sdim#include "tzfile.h" 43360784Sdim#include <fcntl.h> 44360784Sdim#include <sys/stat.h> 45360784Sdim#include "un-namespace.h" 46360784Sdim#include "timelocal.h" 47360784Sdim 48314564Sdimstatic char * _add P((const char *, char *, const char *)); 49360784Sdimstatic char * _conv P((int, const char *, char *, const char *)); 50353358Sdimstatic char * _fmt P((const char *, const struct tm *, char *, const char *)); 51353358Sdim 52254721Semastesize_t strftime P((char *, size_t, const char *, const struct tm *)); 53360784Sdim 54360784Sdimextern char * tzname[]; 55360784Sdim 56360784Sdimsize_t 57360784Sdimstrftime(s, maxsize, format, t) 58360784Sdim char *const s; 59314564Sdim const size_t maxsize; 60353358Sdim const char *const format; 61353358Sdim const struct tm *const t; 62353358Sdim{ 63353358Sdim char *p; 64353358Sdim 65254721Semaste tzset(); 66254721Semaste p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize); 67360784Sdim if (p == s + maxsize) 68314564Sdim return 0; 69353358Sdim *p = '\0'; 70360784Sdim return p - s; 71353358Sdim} 72353358Sdim 73353358Sdimstatic char * 74353358Sdim_fmt(format, t, pt, ptlim) 75353358Sdim const char *format; 76353358Sdim const struct tm *const t; 77254721Semaste char *pt; 78254721Semaste const char *const ptlim; 79360784Sdim{ 80360784Sdim int Ealternative, Oalternative; 81353358Sdim struct lc_time_T *tptr = __get_current_time_locale(); 82353358Sdim 83353358Sdim for ( ; *format; ++format) { 84353358Sdim if (*format == '%') { 85353358Sdim Ealternative = 0; 86353358Sdim Oalternative = 0; 87254721Semastelabel: 88360784Sdim switch (*++format) { 89360784Sdim case '\0': 90353358Sdim --format; 91353358Sdim break; 92314564Sdim case 'A': 93353358Sdim pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? 94254721Semaste "?" : tptr->weekday[t->tm_wday], 95360784Sdim pt, ptlim); 96360784Sdim continue; 97314564Sdim case 'a': 98254721Semaste pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? 99314564Sdim "?" : tptr->wday[t->tm_wday], 100353358Sdim pt, ptlim); 101276479Sdim continue; 102360784Sdim case 'B': 103360784Sdim pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? 104254721Semaste "?" : (Oalternative ? tptr->alt_month : 105254721Semaste tptr->month)[t->tm_mon], 106314564Sdim pt, ptlim); 107353358Sdim continue; 108353358Sdim case 'b': 109360784Sdim case 'h': 110254721Semaste pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? 111254721Semaste "?" : tptr->mon[t->tm_mon], 112314564Sdim pt, ptlim); 113353358Sdim continue; 114353358Sdim case 'C': 115360784Sdim /* 116254721Semaste ** %C used to do a... 117254721Semaste ** _fmt("%a %b %e %X %Y", t); 118314564Sdim ** ...whereas now POSIX 1003.2 calls for 119360784Sdim ** something completely different. 120314564Sdim ** (ado, 5/24/93) 121314564Sdim */ 122314564Sdim pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, 123314564Sdim "%02d", pt, ptlim); 124314564Sdim continue; 125314564Sdim case 'c': 126254721Semaste pt = _fmt(tptr->c_fmt, t, pt, ptlim); 127254721Semaste continue; 128360784Sdim case 'D': 129360784Sdim pt = _fmt("%m/%d/%y", t, pt, ptlim); 130360784Sdim continue; 131360784Sdim case 'd': 132360784Sdim pt = _conv(t->tm_mday, "%02d", pt, ptlim); 133360784Sdim continue; 134360784Sdim case 'E': 135360784Sdim if (Ealternative || Oalternative) 136360784Sdim break; 137360784Sdim Ealternative++; 138360784Sdim goto label; 139360784Sdim case 'O': 140360784Sdim /* 141360784Sdim ** POSIX locale extensions, a la 142360784Sdim ** Arnold Robbins' strftime version 3.0. 143314564Sdim ** The sequences 144360784Sdim ** %Ec %EC %Ex %EX %Ey %EY 145314564Sdim ** %Od %oe %OH %OI %Om %OM 146314564Sdim ** %OS %Ou %OU %OV %Ow %OW %Oy 147314564Sdim ** are supposed to provide alternate 148314564Sdim ** representations. 149314564Sdim ** (ado, 5/24/93) 150314564Sdim ** 151254721Semaste ** FreeBSD extensions 152254721Semaste ** %OB %Ef %EF 153360784Sdim */ 154360784Sdim if (Ealternative || Oalternative) 155360784Sdim break; 156360784Sdim Oalternative++; 157360784Sdim goto label; 158360784Sdim case 'e': 159360784Sdim pt = _conv(t->tm_mday, "%2d", pt, ptlim); 160360784Sdim continue; 161360784Sdim case 'f': 162360784Sdim if (!Ealternative) 163360784Sdim break; 164360784Sdim pt = _fmt(*(tptr->md_order) == 'd' ? 165360784Sdim "%e %b" : "%b %e", 166360784Sdim t, pt, ptlim); 167360784Sdim continue; 168314564Sdim case 'F': 169353358Sdim if (!Ealternative) 170353358Sdim pt = _fmt("%Y-%m-%d", t, pt, ptlim); 171360784Sdim else 172254721Semaste pt = _fmt(*(tptr->md_order) == 'd' ? 173254721Semaste "%e %B" : "%B %e", 174314564Sdim t, pt, ptlim); 175353358Sdim continue; 176353358Sdim case 'H': 177353358Sdim pt = _conv(t->tm_hour, "%02d", pt, ptlim); 178360784Sdim continue; 179254721Semaste case 'I': 180254721Semaste pt = _conv((t->tm_hour % 12) ? 181314564Sdim (t->tm_hour % 12) : 12, 182353358Sdim "%02d", pt, ptlim); 183353358Sdim continue; 184353358Sdim case 'j': 185360784Sdim pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); 186254721Semaste continue; 187254721Semaste case 'k': 188314564Sdim /* 189353358Sdim ** This used to be... 190353358Sdim ** _conv(t->tm_hour % 12 ? 191360784Sdim ** t->tm_hour % 12 : 12, 2, ' '); 192254721Semaste ** ...and has been changed to the below to 193254721Semaste ** match SunOS 4.1.1 and Arnold Robbins' 194314564Sdim ** strftime version 3.0. That is, "%k" and 195353358Sdim ** "%l" have been swapped. 196353358Sdim ** (ado, 5/24/93) 197360784Sdim */ 198254721Semaste pt = _conv(t->tm_hour, "%2d", pt, ptlim); 199254721Semaste continue; 200314564Sdim#ifdef KITCHEN_SINK 201353358Sdim case 'K': 202353358Sdim /* 203353358Sdim ** After all this time, still unclaimed! 204360784Sdim */ 205254721Semaste pt = _add("kitchen sink", pt, ptlim); 206254721Semaste continue; 207314564Sdim#endif /* defined KITCHEN_SINK */ 208353358Sdim case 'l': 209353358Sdim /* 210353358Sdim ** This used to be... 211360784Sdim ** _conv(t->tm_hour, 2, ' '); 212254721Semaste ** ...and has been changed to the below to 213254721Semaste ** match SunOS 4.1.1 and Arnold Robbin's 214314564Sdim ** strftime version 3.0. That is, "%k" and 215360784Sdim ** "%l" have been swapped. 216254721Semaste ** (ado, 5/24/93) 217254721Semaste */ 218314564Sdim pt = _conv((t->tm_hour % 12) ? 219360784Sdim (t->tm_hour % 12) : 12, 220254721Semaste "%2d", pt, ptlim); 221254721Semaste continue; 222314564Sdim case 'M': 223360784Sdim pt = _conv(t->tm_min, "%02d", pt, ptlim); 224254721Semaste continue; 225254721Semaste case 'm': 226314564Sdim pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); 227360784Sdim continue; 228254721Semaste case 'n': 229254721Semaste pt = _add("\n", pt, ptlim); 230314564Sdim continue; 231353358Sdim case 'p': 232353358Sdim pt = _add((t->tm_hour >= 12) ? 233353358Sdim tptr->pm : 234314564Sdim tptr->am, 235254721Semaste pt, ptlim); 236360784Sdim continue; 237360784Sdim case 'R': 238360784Sdim pt = _fmt("%H:%M", t, pt, ptlim); 239360784Sdim continue; 240360784Sdim case 'r': 241360784Sdim pt = _fmt(tptr->ampm_fmt, t, pt, ptlim); 242360784Sdim continue; 243360784Sdim case 'S': 244360784Sdim pt = _conv(t->tm_sec, "%02d", pt, ptlim); 245360784Sdim continue; 246254721Semaste case 's': 247360784Sdim { 248360784Sdim struct tm tm; 249254721Semaste char buf[INT_STRLEN_MAXIMUM( 250360784Sdim time_t) + 1]; 251360784Sdim time_t mkt; 252254721Semaste 253314564Sdim tm = *t; 254254721Semaste mkt = mktime(&tm); 255254721Semaste if (TYPE_SIGNED(time_t)) 256314564Sdim (void) sprintf(buf, "%ld", 257360784Sdim (long) mkt); 258360784Sdim else (void) sprintf(buf, "%lu", 259353358Sdim (unsigned long) mkt); 260314564Sdim pt = _add(buf, pt, ptlim); 261254721Semaste } 262254721Semaste continue; 263314564Sdim case 'T': 264360784Sdim pt = _fmt("%H:%M:%S", t, pt, ptlim); 265360784Sdim continue; 266353358Sdim case 't': 267314564Sdim pt = _add("\t", pt, ptlim); 268309124Sdim continue; 269309124Sdim case 'U': 270314564Sdim pt = _conv((t->tm_yday + 7 - t->tm_wday) / 7, 271314564Sdim "%02d", pt, ptlim); 272360784Sdim continue; 273360784Sdim case 'u': 274360784Sdim /* 275360784Sdim ** From Arnold Robbins' strftime version 3.0: 276254721Semaste ** "ISO 8601: Weekday as a decimal number 277254721Semaste ** [1 (Monday) - 7]" 278314564Sdim ** (ado, 5/24/93) 279314564Sdim */ 280360784Sdim pt = _conv((t->tm_wday == 0) ? 7 : t->tm_wday, 281360784Sdim "%d", pt, ptlim); 282360784Sdim continue; 283360784Sdim case 'V': /* ISO 8601 week number */ 284360784Sdim case 'G': /* ISO 8601 year (four digits) */ 285360784Sdim case 'g': /* ISO 8601 year (two digits) */ 286360784Sdim/* 287360784Sdim** From Arnold Robbins' strftime version 3.0: "the week number of the 288360784Sdim** year (the first Monday as the first day of week 1) as a decimal number 289360784Sdim** (01-53)." 290360784Sdim** (ado, 1993-05-24) 291360784Sdim** 292360784Sdim** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn: 293353358Sdim** "Week 01 of a year is per definition the first week which has the 294360784Sdim** Thursday in this year, which is equivalent to the week which contains 295360784Sdim** the fourth day of January. In other words, the first week of a new year 296360784Sdim** is the week which has the majority of its days in the new year. Week 01 297353358Sdim** might also contain days from the previous year and the week before week 298360784Sdim** 01 of a year is the last week (52 or 53) of the previous year even if 299360784Sdim** it contains days from the new year. A week starts with Monday (day 1) 300360784Sdim** and ends with Sunday (day 7). For example, the first week of the year 301360784Sdim** 1997 lasts from 1996-12-30 to 1997-01-05..." 302309124Sdim** (ado, 1996-01-02) 303309124Sdim*/ 304360784Sdim { 305360784Sdim int year; 306360784Sdim int yday; 307360784Sdim int wday; 308360784Sdim int w; 309360784Sdim 310314564Sdim year = t->tm_year + TM_YEAR_BASE; 311353358Sdim yday = t->tm_yday; 312353358Sdim wday = t->tm_wday; 313353358Sdim for ( ; ; ) { 314360784Sdim int len; 315360784Sdim int bot; 316360784Sdim int top; 317360784Sdim 318360784Sdim len = isleap(year) ? 319360784Sdim DAYSPERLYEAR : 320360784Sdim DAYSPERNYEAR; 321254721Semaste /* 322254721Semaste ** What yday (-3 ... 3) does 323314564Sdim ** the ISO year begin on? 324353358Sdim */ 325353358Sdim bot = ((yday + 11 - wday) % 326353358Sdim DAYSPERWEEK) - 3; 327314564Sdim /* 328360784Sdim ** What yday does the NEXT 329314564Sdim ** ISO year begin on? 330314564Sdim */ 331254721Semaste top = bot - 332254721Semaste (len % DAYSPERWEEK); 333314564Sdim if (top < -3) 334353358Sdim top += DAYSPERWEEK; 335353358Sdim top += len; 336353358Sdim if (yday >= top) { 337360784Sdim ++year; 338314564Sdim w = 1; 339314564Sdim break; 340254721Semaste } 341254721Semaste if (yday >= bot) { 342314564Sdim w = 1 + ((yday - bot) / 343360784Sdim DAYSPERWEEK); 344360784Sdim break; 345360784Sdim } 346360784Sdim --year; 347360784Sdim yday += isleap(year) ? 348254721Semaste DAYSPERLYEAR : 349254721Semaste DAYSPERNYEAR; 350314564Sdim } 351314564Sdim#ifdef XPG4_1994_04_09 352353358Sdim if ((w == 52 353353358Sdim && t->tm_mon == TM_JANUARY) 354353358Sdim || (w == 1 355353358Sdim && t->tm_mon == TM_DECEMBER)) 356360784Sdim w = 53; 357360784Sdim#endif /* defined XPG4_1994_04_09 */ 358360784Sdim if (*format == 'V') 359360784Sdim pt = _conv(w, "%02d", 360254721Semaste pt, ptlim); 361254721Semaste else if (*format == 'g') { 362314564Sdim pt = _conv(year % 100, "%02d", 363353358Sdim pt, ptlim); 364353358Sdim } else pt = _conv(year, "%04d", 365353358Sdim pt, ptlim); 366360784Sdim } 367360784Sdim continue; 368254721Semaste case 'v': 369353358Sdim /* 370353358Sdim ** From Arnold Robbins' strftime version 3.0: 371353358Sdim ** "date as dd-bbb-YYYY" 372353358Sdim ** (ado, 5/24/93) 373353358Sdim */ 374353358Sdim pt = _fmt("%e-%b-%Y", t, pt, ptlim); 375353358Sdim continue; 376353358Sdim case 'W': 377360784Sdim pt = _conv((t->tm_yday + 7 - 378360784Sdim (t->tm_wday ? 379353358Sdim (t->tm_wday - 1) : 6)) / 7, 380353358Sdim "%02d", pt, ptlim); 381360784Sdim continue; 382353358Sdim case 'w': 383353358Sdim pt = _conv(t->tm_wday, "%d", pt, ptlim); 384353358Sdim continue; 385353358Sdim case 'X': 386353358Sdim pt = _fmt(tptr->X_fmt, t, pt, ptlim); 387353358Sdim continue; 388353358Sdim case 'x': 389353358Sdim pt = _fmt(tptr->x_fmt, t, pt, ptlim); 390353358Sdim continue; 391360784Sdim case 'y': 392360784Sdim pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, 393360784Sdim "%02d", pt, ptlim); 394360784Sdim continue; 395353358Sdim case 'Y': 396353358Sdim pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d", 397353358Sdim pt, ptlim); 398353358Sdim continue; 399353358Sdim case 'Z': 400353358Sdim if (t->tm_zone != NULL) 401353358Sdim pt = _add(t->tm_zone, pt, ptlim); 402353358Sdim else 403353358Sdim if (t->tm_isdst == 0 || t->tm_isdst == 1) { 404353358Sdim pt = _add(tzname[t->tm_isdst], 405353358Sdim pt, ptlim); 406353358Sdim } else pt = _add("?", pt, ptlim); 407353358Sdim continue; 408353358Sdim case 'z': 409353358Sdim { 410353358Sdim long absoff; 411353358Sdim if (t->tm_gmtoff >= 0) { 412353358Sdim absoff = t->tm_gmtoff; 413360784Sdim pt = _add("+", pt, ptlim); 414360784Sdim } else { 415360784Sdim absoff = -t->tm_gmtoff; 416360784Sdim pt = _add("-", pt, ptlim); 417360784Sdim } 418360784Sdim pt = _conv(absoff / 3600, "%02d", 419360784Sdim pt, ptlim); 420360784Sdim pt = _conv((absoff % 3600) / 60, "%02d", 421353358Sdim pt, ptlim); 422353358Sdim }; 423353358Sdim continue; 424353358Sdim case '+': 425353358Sdim pt = _fmt(tptr->date_fmt, t, pt, ptlim); 426353358Sdim continue; 427353358Sdim case '%': 428353358Sdim /* 429353358Sdim * X311J/88-090 (4.12.3.5): if conversion char is 430353358Sdim * undefined, behavior is undefined. Print out the 431353358Sdim * character itself as printf(3) also does. 432353358Sdim */ 433353358Sdim default: 434353358Sdim break; 435353358Sdim } 436 } 437 if (pt == ptlim) 438 break; 439 *pt++ = *format; 440 } 441 return pt; 442} 443 444static char * 445_conv(n, format, pt, ptlim) 446 const int n; 447 const char *const format; 448 char *const pt; 449 const char *const ptlim; 450{ 451 char buf[INT_STRLEN_MAXIMUM(int) + 1]; 452 453 (void) sprintf(buf, format, n); 454 return _add(buf, pt, ptlim); 455} 456 457static char * 458_add(str, pt, ptlim) 459 const char *str; 460 char *pt; 461 const char *const ptlim; 462{ 463 while (pt < ptlim && (*pt = *str++) != '\0') 464 ++pt; 465 return pt; 466} 467