strftime.c revision 6815
1#ifndef lint 2#ifndef NOID 3static char elsieid[] = "@(#)strftime.c 7.19"; 4/* 5** Based on the UCB version with the ID appearing below. 6** This is ANSIish only when time is treated identically in all locales and 7** when "multibyte character == plain character". 8*/ 9#endif /* !defined NOID */ 10#endif /* !defined lint */ 11 12#include "private.h" 13 14/* 15 * Copyright (c) 1989 The Regents of the University of California. 16 * All rights reserved. 17 * 18 * Redistribution and use in source and binary forms are permitted 19 * provided that the above copyright notice and this paragraph are 20 * duplicated in all such forms and that any documentation, 21 * advertising materials, and other materials related to such 22 * distribution and use acknowledge that the software was developed 23 * by the University of California, Berkeley. The name of the 24 * University may not be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 27 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 28 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 29 */ 30 31#ifndef LIBC_SCCS 32#ifndef lint 33static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89"; 34#endif /* !defined lint */ 35#endif /* !defined LIBC_SCCS */ 36 37#include "tzfile.h" 38 39static const char afmt[][4] = { 40 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 41}; 42static const char Afmt[][10] = { 43 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", 44 "Saturday" 45}; 46static const char bfmt[][4] = { 47 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", 48 "Oct", "Nov", "Dec" 49}; 50static const char Bfmt[][10] = { 51 "January", "February", "March", "April", "May", "June", "July", 52 "August", "September", "October", "November", "December" 53}; 54 55static char *_add P((const char *, char *, const char *)); 56static char *_conv P((int, const char *, char *, const char *)); 57static char *_fmt P((const char *, const struct tm *, char *, const char *)); 58static char *_secs P((const struct tm *, char *, const char *)); 59 60size_t strftime P((char *, size_t, const char *, const struct tm *)); 61 62extern char *tzname[]; 63 64size_t 65strftime(s, maxsize, format, t) 66 char *s; 67 size_t maxsize; 68 const char *format; 69 const struct tm *t; 70{ 71 char *p; 72 73 p = _fmt(format, t, s, s + maxsize); 74 if (p == s + maxsize) 75 return 0; 76 *p = '\0'; 77 return p - s; 78} 79 80static char * 81_fmt(format, t, pt, ptlim) 82 const char *format; 83 const struct tm *t; 84 char *pt; 85 const char *ptlim; 86{ 87 for (; *format; ++format) { 88 if (*format == '%') { 89label: 90 switch(*++format) { 91 case '\0': 92 --format; 93 break; 94 case 'A': 95 pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? 96 "?" : Afmt[t->tm_wday], pt, ptlim); 97 continue; 98 case 'a': 99 pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ? 100 "?" : afmt[t->tm_wday], pt, ptlim); 101 continue; 102 case 'B': 103 pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? 104 "?" : Bfmt[t->tm_mon], pt, ptlim); 105 continue; 106 case 'b': 107 case 'h': 108 pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ? 109 "?" : bfmt[t->tm_mon], pt, ptlim); 110 continue; 111 case 'c': 112 pt = _fmt("%D %X", t, pt, ptlim); 113 continue; 114 case 'C': 115 /* 116 ** %C used to do a... 117 ** _fmt("%a %b %e %X %Y", t); 118 ** ...whereas now POSIX 1003.2 calls for 119 ** something completely different. 120 ** (ado, 5/24/93) 121 */ 122 pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, 123 "%02d", pt, ptlim); 124 continue; 125 case 'D': 126 pt = _fmt("%m/%d/%y", t, pt, ptlim); 127 continue; 128 case 'x': 129 /* 130 ** Version 3.0 of strftime from Arnold Robbins 131 ** (arnold@skeeve.atl.ga.us) does the 132 ** equivalent of... 133 ** _fmt("%a %b %e %Y"); 134 ** ...for %x; since the X3J11 C language 135 ** standard calls for "date, using locale's 136 ** date format," anything goes. Using just 137 ** numbers (as here) makes Quakers happier. 138 ** Word from Paul Eggert (eggert@twinsun.com) 139 ** is that %Y-%m-%d is the ISO standard date 140 ** format, specified in ISO 2014 and later 141 ** ISO 8601:1988, with a summary available in 142 ** pub/doc/ISO/english/ISO8601.ps.Z on 143 ** ftp.uni-erlangen.de. 144 ** (ado, 5/30/93) 145 */ 146 pt = _fmt("%m/%d/%y", t, pt, ptlim); 147 continue; 148 case 'd': 149 pt = _conv(t->tm_mday, "%02d", pt, ptlim); 150 continue; 151 case 'E': 152 case 'O': 153 /* 154 ** POSIX locale extensions, a la 155 ** Arnold Robbins' strftime version 3.0. 156 ** The sequences 157 ** %Ec %EC %Ex %Ey %EY 158 ** %Od %oe %OH %OI %Om %OM 159 ** %OS %Ou %OU %OV %Ow %OW %Oy 160 ** are supposed to provide alternate 161 ** representations. 162 ** (ado, 5/24/93) 163 */ 164 goto label; 165 case 'e': 166 pt = _conv(t->tm_mday, "%2d", pt, ptlim); 167 continue; 168 case 'H': 169 pt = _conv(t->tm_hour, "%02d", pt, ptlim); 170 continue; 171 case 'I': 172 pt = _conv((t->tm_hour % 12) ? 173 (t->tm_hour % 12) : 12, 174 "%02d", pt, ptlim); 175 continue; 176 case 'j': 177 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); 178 continue; 179 case 'k': 180 /* 181 ** This used to be... 182 ** _conv(t->tm_hour % 12 ? 183 ** t->tm_hour % 12 : 12, 2, ' '); 184 ** ...and has been changed to the below to 185 ** match SunOS 4.1.1 and Arnold Robbins' 186 ** strftime version 3.0. That is, "%k" and 187 ** "%l" have been swapped. 188 ** (ado, 5/24/93) 189 */ 190 pt = _conv(t->tm_hour, "%2d", pt, ptlim); 191 continue; 192#ifdef KITCHEN_SINK 193 case 'K': 194 /* 195 ** After all this time, still unclaimed! 196 */ 197 pt = _add("kitchen sink", pt, ptlim); 198 continue; 199#endif /* defined KITCHEN_SINK */ 200 case 'l': 201 /* 202 ** This used to be... 203 ** _conv(t->tm_hour, 2, ' '); 204 ** ...and has been changed to the below to 205 ** match SunOS 4.1.1 and Arnold Robbin's 206 ** strftime version 3.0. That is, "%k" and 207 ** "%l" have been swapped. 208 ** (ado, 5/24/93) 209 */ 210 pt = _conv((t->tm_hour % 12) ? 211 (t->tm_hour % 12) : 12, 212 "%2d", pt, ptlim); 213 continue; 214 case 'M': 215 pt = _conv(t->tm_min, "%02d", pt, ptlim); 216 continue; 217 case 'm': 218 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); 219 continue; 220 case 'n': 221 pt = _add("\n", pt, ptlim); 222 continue; 223 case 'p': 224 pt = _add(t->tm_hour >= 12 ? "PM" : "AM", 225 pt, ptlim); 226 continue; 227 case 'R': 228 pt = _fmt("%H:%M", t, pt, ptlim); 229 continue; 230 case 'r': 231 pt = _fmt("%I:%M:%S %p", t, pt, ptlim); 232 continue; 233 case 'S': 234 pt = _conv(t->tm_sec, "%02d", pt, ptlim); 235 continue; 236 case 's': 237 pt = _secs(t, pt, ptlim); 238 continue; 239 case 'T': 240 case 'X': 241 pt = _fmt("%H:%M:%S", t, pt, ptlim); 242 continue; 243 case 't': 244 pt = _add("\t", pt, ptlim); 245 continue; 246 case 'U': 247 pt = _conv((t->tm_yday + 7 - t->tm_wday) / 7, 248 "%02d", pt, ptlim); 249 continue; 250 case 'u': 251 /* 252 ** From Arnold Robbins' strftime version 3.0: 253 ** "ISO 8601: Weekday as a decimal number 254 ** [1 (Monday) - 7]" 255 ** (ado, 5/24/93) 256 */ 257 pt = _conv((t->tm_wday == 0) ? 7 : t->tm_wday, 258 "%d", pt, ptlim); 259 continue; 260 case 'V': 261 /* 262 ** From Arnold Robbins' strftime version 3.0: 263 ** "the week number of the year (the first 264 ** Monday as the first day of week 1) as a 265 ** decimal number (01-53). The method for 266 ** determining the week number is as specified 267 ** by ISO 8601 (to wit: if the week containing 268 ** January 1 has four or more days in the new 269 ** year, then it is week 1, otherwise it is 270 ** week 53 of the previous year and the next 271 ** week is week 1)." 272 ** (ado, 5/24/93) 273 */ 274 /* 275 ** XXX--If January 1 falls on a Friday, 276 ** January 1-3 are part of week 53 of the 277 ** previous year. By analogy, if January 278 ** 1 falls on a Thursday, are December 29-31 279 ** of the PREVIOUS year part of week 1??? 280 ** (ado 5/24/93) 281 ** 282 ** You are understood not to expect this. 283 */ 284 { 285 int i; 286 287 i = (t->tm_yday + 10 - (t->tm_wday ? 288 (t->tm_wday - 1) : 6)) / 7; 289 if (i == 0) { 290 /* 291 ** What day of the week does 292 ** January 1 fall on? 293 */ 294 i = t->tm_wday - 295 (t->tm_yday - 1); 296 /* 297 ** Fri Jan 1: 53 298 ** Sun Jan 1: 52 299 ** Sat Jan 1: 53 if previous 300 ** year a leap 301 ** year, else 52 302 */ 303 if (i == TM_FRIDAY) 304 i = 53; 305 else if (i == TM_SUNDAY) 306 i = 52; 307 else i = isleap(t->tm_year + 308 TM_YEAR_BASE) ? 309 53 : 52; 310#ifdef XPG4_1994_04_09 311 /* 312 ** As of 4/9/94, though, 313 ** XPG4 calls for 53 314 ** unconditionally. 315 */ 316 i = 53; 317#endif /* defined XPG4_1994_04_09 */ 318 } 319 pt = _conv(i, "%02d", pt, ptlim); 320 } 321 continue; 322 case 'v': 323 /* 324 ** From Arnold Robbins' strftime version 3.0: 325 ** "date as dd-bbb-YYYY" 326 ** (ado, 5/24/93) 327 */ 328 pt = _fmt("%e-%b-%Y", t, pt, ptlim); 329 continue; 330 case 'W': 331 pt = _conv((t->tm_yday + 7 - 332 (t->tm_wday ? 333 (t->tm_wday - 1) : 6)) / 7, 334 "%02d", pt, ptlim); 335 continue; 336 case 'w': 337 pt = _conv(t->tm_wday, "%d", pt, ptlim); 338 continue; 339 case 'y': 340 pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, 341 "%02d", pt, ptlim); 342 continue; 343 case 'Y': 344 pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d", 345 pt, ptlim); 346 continue; 347 case 'Z': 348#ifdef TM_ZONE 349 if (t->TM_ZONE) 350 pt = _add(t->TM_ZONE, pt, ptlim); 351 else 352#endif /* defined TM_ZONE */ 353 if (t->tm_isdst == 0 || t->tm_isdst == 1) { 354 pt = _add(tzname[t->tm_isdst], 355 pt, ptlim); 356 } else pt = _add("?", pt, ptlim); 357 continue; 358 case '%': 359 /* 360 * X311J/88-090 (4.12.3.5): if conversion char is 361 * undefined, behavior is undefined. Print out the 362 * character itself as printf(3) also does. 363 */ 364 default: 365 break; 366 } 367 } 368 if (pt == ptlim) 369 break; 370 *pt++ = *format; 371 } 372 return pt; 373} 374 375static char * 376_conv(n, format, pt, ptlim) 377 int n; 378 const char *format; 379 char *pt; 380 const char *ptlim; 381{ 382 char buf[INT_STRLEN_MAXIMUM(int) + 1]; 383 384 (void) sprintf(buf, format, n); 385 return _add(buf, pt, ptlim); 386} 387 388static char * 389_secs(t, pt, ptlim) 390 const struct tm *t; 391 char *pt; 392 const char *ptlim; 393{ 394 static char buf[INT_STRLEN_MAXIMUM(int) + 1]; 395 register time_t s; 396 register char *p; 397 struct tm tmp; 398 399 /* Make a copy, mktime(3) modifies the tm struct. */ 400 tmp = *t; 401 s = mktime(&tmp); 402 (void) sprintf(buf, "%d", s); 403 return(_add(buf, pt, ptlim)); 404} 405 406static char * 407_add(str, pt, ptlim) 408 const char *str; 409 char *pt; 410 const char *ptlim; 411{ 412 while (pt < ptlim && (*pt = *str++) != '\0') 413 ++pt; 414 return pt; 415} 416