1 1.57 christos /* $NetBSD: strftime.c,v 1.57 2025/01/23 22:44:22 christos Exp $ */ 2 1.36 christos 3 1.39 christos /* Convert a broken-down timestamp to a string. */ 4 1.36 christos 5 1.36 christos /* Copyright 1989 The Regents of the University of California. 6 1.36 christos All rights reserved. 7 1.36 christos 8 1.36 christos Redistribution and use in source and binary forms, with or without 9 1.36 christos modification, are permitted provided that the following conditions 10 1.36 christos are met: 11 1.36 christos 1. Redistributions of source code must retain the above copyright 12 1.36 christos notice, this list of conditions and the following disclaimer. 13 1.36 christos 2. Redistributions in binary form must reproduce the above copyright 14 1.36 christos notice, this list of conditions and the following disclaimer in the 15 1.36 christos documentation and/or other materials provided with the distribution. 16 1.36 christos 3. Neither the name of the University nor the names of its contributors 17 1.36 christos may be used to endorse or promote products derived from this software 18 1.36 christos without specific prior written permission. 19 1.36 christos 20 1.36 christos THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND 21 1.36 christos ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 1.36 christos IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 1.36 christos ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 1.36 christos FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 1.36 christos DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 1.36 christos OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 1.36 christos HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 1.36 christos LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 1.36 christos OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 1.36 christos SUCH DAMAGE. */ 31 1.1 mrg 32 1.3 christos #include <sys/cdefs.h> 33 1.1 mrg #if defined(LIBC_SCCS) && !defined(lint) 34 1.3 christos #if 0 35 1.13 kleink static char elsieid[] = "@(#)strftime.c 7.64"; 36 1.20 mlelstv static char elsieid[] = "@(#)strftime.c 8.3"; 37 1.3 christos #else 38 1.57 christos __RCSID("$NetBSD: strftime.c,v 1.57 2025/01/23 22:44:22 christos Exp $"); 39 1.3 christos #endif 40 1.1 mrg #endif /* LIBC_SCCS and not lint */ 41 1.1 mrg 42 1.4 jtc #include "namespace.h" 43 1.12 kleink 44 1.25 joerg #include <stddef.h> 45 1.30 christos #include <assert.h> 46 1.25 joerg #include <locale.h> 47 1.25 joerg #include "setlocale_local.h" 48 1.25 joerg 49 1.12 kleink /* 50 1.36 christos ** Based on the UCB version with the copyright notice appearing above. 51 1.24 christos ** 52 1.12 kleink ** This is ANSIish only when "multibyte character == plain character". 53 1.12 kleink */ 54 1.12 kleink 55 1.11 taca #include "private.h" 56 1.12 kleink 57 1.16 kleink /* 58 1.16 kleink ** We don't use these extensions in strftime operation even when 59 1.16 kleink ** supported by the local tzcode configuration. A strictly 60 1.16 kleink ** conforming C application may leave them in undefined state. 61 1.16 kleink */ 62 1.16 kleink 63 1.15 kleink #ifdef _LIBC 64 1.15 kleink #undef TM_ZONE 65 1.16 kleink #undef TM_GMTOFF 66 1.15 kleink #endif 67 1.15 kleink 68 1.39 christos #include <fcntl.h> 69 1.39 christos #include <locale.h> 70 1.40 christos #include <stdio.h> 71 1.40 christos 72 1.57 christos /* If true, the value returned by an idealized unlimited-range mktime 73 1.57 christos always fits into an integer type with bounds MIN and MAX. 74 1.57 christos If false, the value might not fit. 75 1.57 christos This macro is usable in #if if its arguments are. 76 1.57 christos Add or subtract 2**31 - 1 for the maximum UT offset allowed in a TZif file, 77 1.57 christos divide by the maximum number of non-leap seconds in a year, 78 1.57 christos divide again by two just to be safe, 79 1.57 christos and account for the tm_year origin (1900) and time_t origin (1970). */ 80 1.57 christos #define MKTIME_FITS_IN(min, max) \ 81 1.57 christos ((min) < 0 \ 82 1.57 christos && ((min) + 0x7fffffff) / 366 / 24 / 60 / 60 / 2 + 1970 - 1900 < INT_MIN \ 83 1.57 christos && INT_MAX < ((max) - 0x7fffffff) / 366 / 24 / 60 / 60 / 2 + 1970 - 1900) 84 1.57 christos 85 1.57 christos /* MKTIME_MIGHT_OVERFLOW is true if mktime can fail due to time_t overflow 86 1.57 christos or if it is not known whether mktime can fail, 87 1.57 christos and is false if mktime definitely cannot fail. 88 1.57 christos This macro is usable in #if, and so does not use TIME_T_MAX or sizeof. 89 1.57 christos If the builder has not configured this macro, guess based on what 90 1.57 christos known platforms do. When in doubt, guess true. */ 91 1.57 christos #ifndef MKTIME_MIGHT_OVERFLOW 92 1.57 christos # if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ 93 1.57 christos # include <sys/param.h> 94 1.57 christos # endif 95 1.57 christos # if ((/* The following heuristics assume native time_t. */ \ 96 1.57 christos defined_time_tz) \ 97 1.57 christos || ((/* Traditional time_t is 'long', so if 'long' is not wide enough \ 98 1.57 christos assume overflow unless we're on a known-safe host. */ \ 99 1.57 christos !MKTIME_FITS_IN(LONG_MIN, LONG_MAX)) \ 100 1.57 christos && (/* GNU C Library 2.29 (2019-02-01) and later has 64-bit time_t \ 101 1.57 christos if __TIMESIZE is 64. */ \ 102 1.57 christos !defined __TIMESIZE || __TIMESIZE < 64) \ 103 1.57 christos && (/* FreeBSD 12 r320347 (__FreeBSD_version 1200036; 2017-06-26), \ 104 1.57 christos and later has 64-bit time_t on all platforms but i386 which \ 105 1.57 christos is currently scheduled for end-of-life on 2028-11-30. */ \ 106 1.57 christos !defined __FreeBSD_version || __FreeBSD_version < 1200036 \ 107 1.57 christos || defined __i386) \ 108 1.57 christos && (/* NetBSD 6.0 (2012-10-17) and later has 64-bit time_t. */ \ 109 1.57 christos !defined __NetBSD_Version__ || __NetBSD_Version__ < 600000000) \ 110 1.57 christos && (/* OpenBSD 5.5 (2014-05-01) and later has 64-bit time_t. */ \ 111 1.57 christos !defined OpenBSD || OpenBSD < 201405))) 112 1.57 christos # define MKTIME_MIGHT_OVERFLOW 1 113 1.57 christos # else 114 1.57 christos # define MKTIME_MIGHT_OVERFLOW 0 115 1.57 christos # endif 116 1.57 christos #endif 117 1.57 christos /* Check that MKTIME_MIGHT_OVERFLOW is consistent with time_t's range. */ 118 1.57 christos static_assert(MKTIME_MIGHT_OVERFLOW 119 1.57 christos || MKTIME_FITS_IN(TIME_T_MIN, TIME_T_MAX)); 120 1.57 christos 121 1.57 christos #if MKTIME_MIGHT_OVERFLOW 122 1.57 christos /* Do this after system includes as it redefines time_t, mktime, timeoff. */ 123 1.57 christos # define USE_TIMEX_T true 124 1.57 christos # include "localtime.c" 125 1.57 christos #endif 126 1.57 christos 127 1.40 christos #ifndef DEPRECATE_TWO_DIGIT_YEARS 128 1.57 christos # define DEPRECATE_TWO_DIGIT_YEARS 0 129 1.40 christos #endif 130 1.12 kleink 131 1.21 christos #ifdef __weak_alias 132 1.25 joerg __weak_alias(strftime_l, _strftime_l) 133 1.25 joerg __weak_alias(strftime_lz, _strftime_lz) 134 1.21 christos __weak_alias(strftime_z, _strftime_z) 135 1.21 christos #endif 136 1.21 christos 137 1.12 kleink #include "sys/localedef.h" 138 1.25 joerg #define _TIME_LOCALE(loc) \ 139 1.56 riastrad ((_TimeLocale *)((loc)->part_impl[LC_TIME])) 140 1.20 mlelstv #define c_fmt d_t_fmt 141 1.12 kleink 142 1.40 christos enum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL }; 143 1.40 christos 144 1.20 mlelstv static char * _add(const char *, char *, const char *); 145 1.44 christos static char * _conv(int, const char *, char *, const char *, locale_t); 146 1.21 christos static char * _fmt(const timezone_t, const char *, const struct tm *, char *, 147 1.40 christos const char *, enum warn *, locale_t); 148 1.44 christos static char * _yconv(int, int, bool, bool, char *, const char *, locale_t); 149 1.12 kleink 150 1.12 kleink #ifndef YEAR_2000_NAME 151 1.50 christos # define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" 152 1.12 kleink #endif /* !defined YEAR_2000_NAME */ 153 1.12 kleink 154 1.44 christos #define IN_NONE 0 155 1.44 christos #define IN_SOME 1 156 1.44 christos #define IN_THIS 2 157 1.44 christos #define IN_ALL 3 158 1.44 christos 159 1.44 christos #define PAD_DEFAULT 0 160 1.44 christos #define PAD_LESS 1 161 1.44 christos #define PAD_SPACE 2 162 1.44 christos #define PAD_ZERO 3 163 1.44 christos 164 1.44 christos static const char fmt_padding[][4][5] = { 165 1.44 christos /* DEFAULT, LESS, SPACE, ZERO */ 166 1.44 christos #define PAD_FMT_MONTHDAY 0 167 1.44 christos #define PAD_FMT_HMS 0 168 1.44 christos #define PAD_FMT_CENTURY 0 169 1.44 christos #define PAD_FMT_SHORTYEAR 0 170 1.44 christos #define PAD_FMT_MONTH 0 171 1.44 christos #define PAD_FMT_WEEKOFYEAR 0 172 1.44 christos #define PAD_FMT_DAYOFMONTH 0 173 1.44 christos { "%02d", "%d", "%2d", "%02d" }, 174 1.44 christos #define PAD_FMT_SDAYOFMONTH 1 175 1.44 christos #define PAD_FMT_SHMS 1 176 1.44 christos { "%2d", "%d", "%2d", "%02d" }, 177 1.44 christos #define PAD_FMT_DAYOFYEAR 2 178 1.44 christos { "%03d", "%d", "%3d", "%03d" }, 179 1.44 christos #define PAD_FMT_YEAR 3 180 1.44 christos { "%04d", "%d", "%4d", "%04d" } 181 1.44 christos }; 182 1.44 christos 183 1.1 mrg size_t 184 1.25 joerg strftime_z(const timezone_t sp, char * __restrict s, size_t maxsize, 185 1.25 joerg const char * __restrict format, const struct tm * __restrict t) 186 1.25 joerg { 187 1.26 joerg return strftime_lz(sp, s, maxsize, format, t, _current_locale()); 188 1.25 joerg } 189 1.25 joerg 190 1.33 christos #if HAVE_STRFTIME_L 191 1.33 christos size_t 192 1.52 christos strftime_l(char *restrict s, size_t maxsize, char const *restrict format, 193 1.52 christos struct tm const *restrict t, 194 1.51 christos ATTRIBUTE_MAYBE_UNUSED locale_t locale) 195 1.33 christos { 196 1.33 christos /* Just call strftime, as only the C locale is supported. */ 197 1.33 christos return strftime(s, maxsize, format, t); 198 1.33 christos } 199 1.33 christos #endif 200 1.33 christos 201 1.25 joerg size_t 202 1.25 joerg strftime_lz(const timezone_t sp, char *const s, const size_t maxsize, 203 1.25 joerg const char *const format, const struct tm *const t, locale_t loc) 204 1.1 mrg { 205 1.12 kleink char * p; 206 1.48 christos int saved_errno = errno; 207 1.40 christos enum warn warn = IN_NONE; 208 1.5 kleink 209 1.37 christos p = _fmt(sp, format, t, s, s + maxsize, &warn, loc); 210 1.40 christos if (/*CONSTCOND*/DEPRECATE_TWO_DIGIT_YEARS 211 1.40 christos && warn != IN_NONE && getenv(YEAR_2000_NAME)) { 212 1.12 kleink (void) fprintf(stderr, "\n"); 213 1.37 christos (void) fprintf(stderr, "strftime format \"%s\" ", format); 214 1.12 kleink (void) fprintf(stderr, "yields only two digits of years in "); 215 1.12 kleink if (warn == IN_SOME) 216 1.12 kleink (void) fprintf(stderr, "some locales"); 217 1.12 kleink else if (warn == IN_THIS) 218 1.12 kleink (void) fprintf(stderr, "the current locale"); 219 1.12 kleink else (void) fprintf(stderr, "all locales"); 220 1.12 kleink (void) fprintf(stderr, "\n"); 221 1.12 kleink } 222 1.48 christos if (p == s + maxsize) { 223 1.48 christos errno = ERANGE; 224 1.12 kleink return 0; 225 1.48 christos } 226 1.12 kleink *p = '\0'; 227 1.48 christos errno = saved_errno; 228 1.12 kleink return p - s; 229 1.1 mrg } 230 1.1 mrg 231 1.12 kleink static char * 232 1.34 christos _fmt(const timezone_t sp, const char *format, const struct tm *t, char *pt, 233 1.40 christos const char *ptlim, enum warn *warnp, locale_t loc) 234 1.1 mrg { 235 1.44 christos int Ealternative, Oalternative, PadIndex; 236 1.44 christos _TimeLocale *tptr = _TIME_LOCALE(loc); 237 1.44 christos 238 1.12 kleink for ( ; *format; ++format) { 239 1.1 mrg if (*format == '%') { 240 1.44 christos Ealternative = 0; 241 1.44 christos Oalternative = 0; 242 1.44 christos PadIndex = PAD_DEFAULT; 243 1.12 kleink label: 244 1.12 kleink switch (*++format) { 245 1.1 mrg case '\0': 246 1.57 christos /* Output unknown conversion specifiers as-is, 247 1.57 christos to aid debugging. This includes '%' at 248 1.57 christos format end. This conforms to C23 section 249 1.57 christos 7.29.3.5 paragraph 6, which says behavior 250 1.57 christos is undefined here. */ 251 1.57 christos default: 252 1.1 mrg --format; 253 1.1 mrg break; 254 1.1 mrg case 'A': 255 1.12 kleink pt = _add((t->tm_wday < 0 || 256 1.12 kleink t->tm_wday >= DAYSPERWEEK) ? 257 1.44 christos "?" : tptr->day[t->tm_wday], 258 1.12 kleink pt, ptlim); 259 1.1 mrg continue; 260 1.1 mrg case 'a': 261 1.12 kleink pt = _add((t->tm_wday < 0 || 262 1.12 kleink t->tm_wday >= DAYSPERWEEK) ? 263 1.44 christos "?" : tptr->abday[t->tm_wday], 264 1.12 kleink pt, ptlim); 265 1.1 mrg continue; 266 1.1 mrg case 'B': 267 1.12 kleink pt = _add((t->tm_mon < 0 || 268 1.12 kleink t->tm_mon >= MONSPERYEAR) ? 269 1.44 christos "?" : 270 1.44 christos /* no alt_month in _TimeLocale */ 271 1.44 christos (Oalternative ? tptr->mon/*alt_month*/: 272 1.44 christos tptr->mon)[t->tm_mon], 273 1.12 kleink pt, ptlim); 274 1.1 mrg continue; 275 1.1 mrg case 'b': 276 1.1 mrg case 'h': 277 1.12 kleink pt = _add((t->tm_mon < 0 || 278 1.12 kleink t->tm_mon >= MONSPERYEAR) ? 279 1.44 christos "?" : tptr->abmon[t->tm_mon], 280 1.12 kleink pt, ptlim); 281 1.1 mrg continue; 282 1.1 mrg case 'C': 283 1.12 kleink /* 284 1.12 kleink ** %C used to do a... 285 1.12 kleink ** _fmt("%a %b %e %X %Y", t); 286 1.12 kleink ** ...whereas now POSIX 1003.2 calls for 287 1.12 kleink ** something completely different. 288 1.12 kleink ** (ado, 1993-05-24) 289 1.12 kleink */ 290 1.33 christos pt = _yconv(t->tm_year, TM_YEAR_BASE, 291 1.44 christos true, false, pt, ptlim, loc); 292 1.1 mrg continue; 293 1.1 mrg case 'c': 294 1.12 kleink { 295 1.40 christos enum warn warn2 = IN_SOME; 296 1.12 kleink 297 1.44 christos pt = _fmt(sp, tptr->c_fmt, t, pt, 298 1.25 joerg ptlim, &warn2, loc); 299 1.12 kleink if (warn2 == IN_ALL) 300 1.12 kleink warn2 = IN_THIS; 301 1.12 kleink if (warn2 > *warnp) 302 1.12 kleink *warnp = warn2; 303 1.12 kleink } 304 1.1 mrg continue; 305 1.1 mrg case 'D': 306 1.25 joerg pt = _fmt(sp, "%m/%d/%y", t, pt, ptlim, warnp, 307 1.25 joerg loc); 308 1.1 mrg continue; 309 1.1 mrg case 'd': 310 1.44 christos pt = _conv(t->tm_mday, 311 1.44 christos fmt_padding[PAD_FMT_DAYOFMONTH][PadIndex], 312 1.44 christos pt, ptlim, loc); 313 1.1 mrg continue; 314 1.12 kleink case 'E': 315 1.44 christos if (Ealternative || Oalternative) 316 1.44 christos break; 317 1.44 christos Ealternative++; 318 1.44 christos goto label; 319 1.12 kleink case 'O': 320 1.12 kleink /* 321 1.40 christos ** Locale modifiers of C99 and later. 322 1.12 kleink ** The sequences 323 1.12 kleink ** %Ec %EC %Ex %EX %Ey %EY 324 1.12 kleink ** %Od %oe %OH %OI %Om %OM 325 1.12 kleink ** %OS %Ou %OU %OV %Ow %OW %Oy 326 1.41 christos ** are supposed to provide alternative 327 1.12 kleink ** representations. 328 1.12 kleink */ 329 1.44 christos if (Ealternative || Oalternative) 330 1.44 christos break; 331 1.44 christos Oalternative++; 332 1.12 kleink goto label; 333 1.1 mrg case 'e': 334 1.44 christos pt = _conv(t->tm_mday, 335 1.44 christos fmt_padding[PAD_FMT_SDAYOFMONTH][PadIndex], 336 1.44 christos pt, ptlim, loc); 337 1.10 kleink continue; 338 1.10 kleink case 'F': 339 1.25 joerg pt = _fmt(sp, "%Y-%m-%d", t, pt, ptlim, warnp, 340 1.25 joerg loc); 341 1.1 mrg continue; 342 1.1 mrg case 'H': 343 1.44 christos pt = _conv(t->tm_hour, 344 1.44 christos fmt_padding[PAD_FMT_HMS][PadIndex], 345 1.44 christos pt, ptlim, loc); 346 1.1 mrg continue; 347 1.1 mrg case 'I': 348 1.12 kleink pt = _conv((t->tm_hour % 12) ? 349 1.44 christos (t->tm_hour % 12) : 12, 350 1.44 christos fmt_padding[PAD_FMT_HMS][PadIndex], 351 1.44 christos pt, ptlim, loc); 352 1.1 mrg continue; 353 1.1 mrg case 'j': 354 1.44 christos pt = _conv(t->tm_yday + 1, 355 1.44 christos fmt_padding[PAD_FMT_DAYOFYEAR][PadIndex], 356 1.44 christos pt, ptlim, loc); 357 1.1 mrg continue; 358 1.1 mrg case 'k': 359 1.12 kleink /* 360 1.12 kleink ** This used to be... 361 1.12 kleink ** _conv(t->tm_hour % 12 ? 362 1.12 kleink ** t->tm_hour % 12 : 12, 2, ' '); 363 1.12 kleink ** ...and has been changed to the below to 364 1.12 kleink ** match SunOS 4.1.1 and Arnold Robbins' 365 1.20 mlelstv ** strftime version 3.0. That is, "%k" and 366 1.12 kleink ** "%l" have been swapped. 367 1.12 kleink ** (ado, 1993-05-24) 368 1.12 kleink */ 369 1.44 christos pt = _conv(t->tm_hour, 370 1.44 christos fmt_padding[PAD_FMT_SHMS][PadIndex], 371 1.44 christos pt, ptlim, loc); 372 1.12 kleink continue; 373 1.12 kleink #ifdef KITCHEN_SINK 374 1.12 kleink case 'K': 375 1.12 kleink /* 376 1.12 kleink ** After all this time, still unclaimed! 377 1.12 kleink */ 378 1.12 kleink pt = _add("kitchen sink", pt, ptlim); 379 1.1 mrg continue; 380 1.12 kleink #endif /* defined KITCHEN_SINK */ 381 1.1 mrg case 'l': 382 1.12 kleink /* 383 1.12 kleink ** This used to be... 384 1.12 kleink ** _conv(t->tm_hour, 2, ' '); 385 1.12 kleink ** ...and has been changed to the below to 386 1.12 kleink ** match SunOS 4.1.1 and Arnold Robbin's 387 1.20 mlelstv ** strftime version 3.0. That is, "%k" and 388 1.12 kleink ** "%l" have been swapped. 389 1.12 kleink ** (ado, 1993-05-24) 390 1.12 kleink */ 391 1.12 kleink pt = _conv((t->tm_hour % 12) ? 392 1.12 kleink (t->tm_hour % 12) : 12, 393 1.44 christos fmt_padding[PAD_FMT_SHMS][PadIndex], 394 1.44 christos pt, ptlim, loc); 395 1.1 mrg continue; 396 1.1 mrg case 'M': 397 1.44 christos pt = _conv(t->tm_min, 398 1.44 christos fmt_padding[PAD_FMT_HMS][PadIndex], 399 1.44 christos pt, ptlim, loc); 400 1.1 mrg continue; 401 1.1 mrg case 'm': 402 1.44 christos pt = _conv(t->tm_mon + 1, 403 1.44 christos fmt_padding[PAD_FMT_MONTH][PadIndex], 404 1.44 christos pt, ptlim, loc); 405 1.1 mrg continue; 406 1.1 mrg case 'n': 407 1.12 kleink pt = _add("\n", pt, ptlim); 408 1.1 mrg continue; 409 1.1 mrg case 'p': 410 1.12 kleink pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? 411 1.44 christos tptr->am_pm[1] : 412 1.44 christos tptr->am_pm[0], 413 1.12 kleink pt, ptlim); 414 1.1 mrg continue; 415 1.1 mrg case 'R': 416 1.25 joerg pt = _fmt(sp, "%H:%M", t, pt, ptlim, warnp, 417 1.25 joerg loc); 418 1.1 mrg continue; 419 1.1 mrg case 'r': 420 1.44 christos pt = _fmt(sp, tptr->t_fmt_ampm, t, 421 1.25 joerg pt, ptlim, warnp, loc); 422 1.1 mrg continue; 423 1.1 mrg case 'S': 424 1.44 christos pt = _conv(t->tm_sec, 425 1.44 christos fmt_padding[PAD_FMT_HMS][PadIndex], 426 1.44 christos pt, ptlim, loc); 427 1.1 mrg continue; 428 1.1 mrg case 's': 429 1.12 kleink { 430 1.12 kleink struct tm tm; 431 1.12 kleink char buf[INT_STRLEN_MAXIMUM( 432 1.12 kleink time_t) + 1]; 433 1.12 kleink time_t mkt; 434 1.12 kleink 435 1.51 christos tm.tm_sec = t->tm_sec; 436 1.51 christos tm.tm_min = t->tm_min; 437 1.51 christos tm.tm_hour = t->tm_hour; 438 1.51 christos tm.tm_mday = t->tm_mday; 439 1.51 christos tm.tm_mon = t->tm_mon; 440 1.51 christos tm.tm_year = t->tm_year; 441 1.57 christos 442 1.57 christos /* Get the time_t value for TM. 443 1.57 christos Native time_t, or its redefinition 444 1.57 christos by localtime.c above, is wide enough 445 1.57 christos so that this cannot overflow. */ 446 1.57 christos #if defined TM_GMTOFF 447 1.57 christos mkt = timeoff(&tm, t->TM_GMTOFF); 448 1.57 christos #else 449 1.51 christos tm.tm_isdst = t->tm_isdst; 450 1.57 christos mkt = mktime_z(sp, &tm); 451 1.51 christos #endif 452 1.12 kleink /* CONSTCOND */ 453 1.49 christos if (TYPE_SIGNED(time_t)) { 454 1.49 christos intmax_t n = mkt; 455 1.27 christos (void)snprintf(buf, sizeof(buf), 456 1.49 christos "%"PRIdMAX, n); 457 1.49 christos } else { 458 1.53 christos /*LINTED possibly unreached*/ 459 1.49 christos uintmax_t n = mkt; 460 1.49 christos (void)snprintf(buf, sizeof(buf), 461 1.49 christos "%"PRIuMAX, n); 462 1.49 christos } 463 1.12 kleink pt = _add(buf, pt, ptlim); 464 1.12 kleink } 465 1.1 mrg continue; 466 1.1 mrg case 'T': 467 1.25 joerg pt = _fmt(sp, "%H:%M:%S", t, pt, ptlim, warnp, 468 1.25 joerg loc); 469 1.1 mrg continue; 470 1.1 mrg case 't': 471 1.12 kleink pt = _add("\t", pt, ptlim); 472 1.1 mrg continue; 473 1.1 mrg case 'U': 474 1.12 kleink pt = _conv((t->tm_yday + DAYSPERWEEK - 475 1.44 christos t->tm_wday) / DAYSPERWEEK, 476 1.44 christos fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], 477 1.44 christos pt, ptlim, loc); 478 1.1 mrg continue; 479 1.1 mrg case 'u': 480 1.12 kleink /* 481 1.12 kleink ** From Arnold Robbins' strftime version 3.0: 482 1.12 kleink ** "ISO 8601: Weekday as a decimal number 483 1.12 kleink ** [1 (Monday) - 7]" 484 1.12 kleink ** (ado, 1993-05-24) 485 1.12 kleink */ 486 1.12 kleink pt = _conv((t->tm_wday == 0) ? 487 1.12 kleink DAYSPERWEEK : t->tm_wday, 488 1.44 christos "%d", pt, ptlim, loc); 489 1.1 mrg continue; 490 1.8 augustss case 'V': /* ISO 8601 week number */ 491 1.8 augustss case 'G': /* ISO 8601 year (four digits) */ 492 1.8 augustss case 'g': /* ISO 8601 year (two digits) */ 493 1.8 augustss /* 494 1.20 mlelstv ** From Arnold Robbins' strftime version 3.0: "the week number of the 495 1.8 augustss ** year (the first Monday as the first day of week 1) as a decimal number 496 1.8 augustss ** (01-53)." 497 1.8 augustss ** (ado, 1993-05-24) 498 1.8 augustss ** 499 1.40 christos ** From <https://www.cl.cam.ac.uk/~mgk25/iso-time.html> by Markus Kuhn: 500 1.8 augustss ** "Week 01 of a year is per definition the first week which has the 501 1.8 augustss ** Thursday in this year, which is equivalent to the week which contains 502 1.8 augustss ** the fourth day of January. In other words, the first week of a new year 503 1.8 augustss ** is the week which has the majority of its days in the new year. Week 01 504 1.8 augustss ** might also contain days from the previous year and the week before week 505 1.8 augustss ** 01 of a year is the last week (52 or 53) of the previous year even if 506 1.8 augustss ** it contains days from the new year. A week starts with Monday (day 1) 507 1.20 mlelstv ** and ends with Sunday (day 7). For example, the first week of the year 508 1.8 augustss ** 1997 lasts from 1996-12-30 to 1997-01-05..." 509 1.8 augustss ** (ado, 1996-01-02) 510 1.8 augustss */ 511 1.1 mrg { 512 1.8 augustss int year; 513 1.20 mlelstv int base; 514 1.8 augustss int yday; 515 1.8 augustss int wday; 516 1.8 augustss int w; 517 1.8 augustss 518 1.20 mlelstv year = t->tm_year; 519 1.20 mlelstv base = TM_YEAR_BASE; 520 1.8 augustss yday = t->tm_yday; 521 1.8 augustss wday = t->tm_wday; 522 1.8 augustss for ( ; ; ) { 523 1.8 augustss int len; 524 1.8 augustss int bot; 525 1.8 augustss int top; 526 1.8 augustss 527 1.20 mlelstv len = isleap_sum(year, base) ? 528 1.8 augustss DAYSPERLYEAR : 529 1.8 augustss DAYSPERNYEAR; 530 1.8 augustss /* 531 1.8 augustss ** What yday (-3 ... 3) does 532 1.8 augustss ** the ISO year begin on? 533 1.8 augustss */ 534 1.8 augustss bot = ((yday + 11 - wday) % 535 1.8 augustss DAYSPERWEEK) - 3; 536 1.8 augustss /* 537 1.8 augustss ** What yday does the NEXT 538 1.8 augustss ** ISO year begin on? 539 1.8 augustss */ 540 1.8 augustss top = bot - 541 1.8 augustss (len % DAYSPERWEEK); 542 1.8 augustss if (top < -3) 543 1.8 augustss top += DAYSPERWEEK; 544 1.8 augustss top += len; 545 1.8 augustss if (yday >= top) { 546 1.20 mlelstv ++base; 547 1.8 augustss w = 1; 548 1.8 augustss break; 549 1.8 augustss } 550 1.8 augustss if (yday >= bot) { 551 1.8 augustss w = 1 + ((yday - bot) / 552 1.8 augustss DAYSPERWEEK); 553 1.8 augustss break; 554 1.8 augustss } 555 1.20 mlelstv --base; 556 1.20 mlelstv yday += isleap_sum(year, base) ? 557 1.8 augustss DAYSPERLYEAR : 558 1.8 augustss DAYSPERNYEAR; 559 1.8 augustss } 560 1.8 augustss #ifdef XPG4_1994_04_09 561 1.20 mlelstv if ((w == 52 && 562 1.20 mlelstv t->tm_mon == TM_JANUARY) || 563 1.20 mlelstv (w == 1 && 564 1.20 mlelstv t->tm_mon == TM_DECEMBER)) 565 1.20 mlelstv w = 53; 566 1.8 augustss #endif /* defined XPG4_1994_04_09 */ 567 1.12 kleink if (*format == 'V') 568 1.44 christos pt = _conv(w, 569 1.44 christos fmt_padding[ 570 1.44 christos PAD_FMT_WEEKOFYEAR][ 571 1.44 christos PadIndex], pt, ptlim, loc); 572 1.12 kleink else if (*format == 'g') { 573 1.12 kleink *warnp = IN_ALL; 574 1.33 christos pt = _yconv(year, base, 575 1.33 christos false, true, 576 1.44 christos pt, ptlim, loc); 577 1.33 christos } else pt = _yconv(year, base, 578 1.33 christos true, true, 579 1.44 christos pt, ptlim, loc); 580 1.1 mrg } 581 1.1 mrg continue; 582 1.12 kleink case 'v': 583 1.12 kleink /* 584 1.12 kleink ** From Arnold Robbins' strftime version 3.0: 585 1.12 kleink ** "date as dd-bbb-YYYY" 586 1.12 kleink ** (ado, 1993-05-24) 587 1.12 kleink */ 588 1.25 joerg pt = _fmt(sp, "%e-%b-%Y", t, pt, ptlim, warnp, 589 1.25 joerg loc); 590 1.12 kleink continue; 591 1.1 mrg case 'W': 592 1.12 kleink pt = _conv((t->tm_yday + DAYSPERWEEK - 593 1.44 christos (t->tm_wday ? 594 1.44 christos (t->tm_wday - 1) : 595 1.44 christos (DAYSPERWEEK - 1))) / DAYSPERWEEK, 596 1.44 christos fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], 597 1.44 christos pt, ptlim, loc); 598 1.1 mrg continue; 599 1.1 mrg case 'w': 600 1.44 christos pt = _conv(t->tm_wday, "%d", pt, ptlim, loc); 601 1.12 kleink continue; 602 1.12 kleink case 'X': 603 1.44 christos pt = _fmt(sp, tptr->t_fmt, t, pt, 604 1.25 joerg ptlim, warnp, loc); 605 1.1 mrg continue; 606 1.1 mrg case 'x': 607 1.12 kleink { 608 1.40 christos enum warn warn2 = IN_SOME; 609 1.12 kleink 610 1.44 christos pt = _fmt(sp, tptr->d_fmt, t, pt, 611 1.25 joerg ptlim, &warn2, loc); 612 1.12 kleink if (warn2 == IN_ALL) 613 1.12 kleink warn2 = IN_THIS; 614 1.12 kleink if (warn2 > *warnp) 615 1.12 kleink *warnp = warn2; 616 1.12 kleink } 617 1.1 mrg continue; 618 1.1 mrg case 'y': 619 1.12 kleink *warnp = IN_ALL; 620 1.33 christos pt = _yconv(t->tm_year, TM_YEAR_BASE, 621 1.33 christos false, true, 622 1.44 christos pt, ptlim, loc); 623 1.1 mrg continue; 624 1.1 mrg case 'Y': 625 1.33 christos pt = _yconv(t->tm_year, TM_YEAR_BASE, 626 1.33 christos true, true, 627 1.44 christos pt, ptlim, loc); 628 1.1 mrg continue; 629 1.1 mrg case 'Z': 630 1.11 taca #ifdef TM_ZONE 631 1.35 christos pt = _add(t->TM_ZONE, pt, ptlim); 632 1.40 christos #elif HAVE_TZNAME 633 1.45 christos if (t->tm_isdst >= 0) { 634 1.45 christos int oerrno = errno, dst = t->tm_isdst; 635 1.45 christos const char *z = 636 1.45 christos tzgetname(sp, dst); 637 1.45 christos if (z == NULL) 638 1.45 christos z = tzgetname(sp, !dst); 639 1.46 christos if (z != NULL) 640 1.46 christos pt = _add(z, pt, ptlim); 641 1.45 christos errno = oerrno; 642 1.45 christos } 643 1.40 christos #endif 644 1.12 kleink /* 645 1.40 christos ** C99 and later say that %Z must be 646 1.40 christos ** replaced by the empty string if the 647 1.42 christos ** time zone abbreviation is not 648 1.42 christos ** determinable. 649 1.12 kleink */ 650 1.12 kleink continue; 651 1.12 kleink case 'z': 652 1.47 christos #if defined TM_GMTOFF || USG_COMPAT || ALTZONE 653 1.12 kleink { 654 1.30 christos long diff; 655 1.12 kleink char const * sign; 656 1.39 christos bool negative; 657 1.12 kleink 658 1.12 kleink if (t->tm_isdst < 0) 659 1.12 kleink continue; 660 1.38 christos # ifdef TM_GMTOFF 661 1.12 kleink diff = (int)t->TM_GMTOFF; 662 1.38 christos # else 663 1.12 kleink /* 664 1.40 christos ** C99 and later say that the UT offset must 665 1.12 kleink ** be computed by looking only at 666 1.20 mlelstv ** tm_isdst. This requirement is 667 1.12 kleink ** incorrect, since it means the code 668 1.12 kleink ** must rely on magic (in this case 669 1.12 kleink ** altzone and timezone), and the 670 1.12 kleink ** magic might not have the correct 671 1.20 mlelstv ** offset. Doing things correctly is 672 1.40 christos ** tricky and requires disobeying the standard; 673 1.12 kleink ** see GNU C strftime for details. 674 1.12 kleink ** For now, punt and conform to the 675 1.12 kleink ** standard, even though it's incorrect. 676 1.12 kleink ** 677 1.40 christos ** C99 and later say that %z must be replaced by 678 1.40 christos ** the empty string if the time zone is not 679 1.12 kleink ** determinable, so output nothing if the 680 1.12 kleink ** appropriate variables are not available. 681 1.12 kleink */ 682 1.38 christos # ifndef STD_INSPIRED 683 1.12 kleink if (t->tm_isdst == 0) 684 1.40 christos # if USG_COMPAT 685 1.12 kleink diff = -timezone; 686 1.38 christos # else 687 1.12 kleink continue; 688 1.38 christos # endif 689 1.12 kleink else 690 1.47 christos # if ALTZONE 691 1.12 kleink diff = -altzone; 692 1.38 christos # else 693 1.12 kleink continue; 694 1.38 christos # endif 695 1.38 christos # else 696 1.16 kleink { 697 1.16 kleink struct tm tmp; 698 1.16 kleink time_t lct, gct; 699 1.16 kleink 700 1.16 kleink /* 701 1.16 kleink ** Get calendar time from t 702 1.16 kleink ** being treated as local. 703 1.16 kleink */ 704 1.16 kleink tmp = *t; /* mktime discards const */ 705 1.43 christos lct = mktime_z(sp, &tmp); 706 1.16 kleink 707 1.16 kleink if (lct == (time_t)-1) 708 1.16 kleink continue; 709 1.16 kleink 710 1.16 kleink /* 711 1.16 kleink ** Get calendar time from t 712 1.16 kleink ** being treated as GMT. 713 1.16 kleink **/ 714 1.16 kleink tmp = *t; /* mktime discards const */ 715 1.16 kleink gct = timegm(&tmp); 716 1.16 kleink 717 1.16 kleink if (gct == (time_t)-1) 718 1.16 kleink continue; 719 1.16 kleink 720 1.16 kleink /* LINTED difference will fit int */ 721 1.16 kleink diff = (intmax_t)gct - (intmax_t)lct; 722 1.16 kleink } 723 1.38 christos # endif 724 1.38 christos # endif 725 1.39 christos negative = diff < 0; 726 1.39 christos if (diff == 0) { 727 1.50 christos # ifdef TM_ZONE 728 1.39 christos negative = t->TM_ZONE[0] == '-'; 729 1.50 christos # else 730 1.40 christos negative = t->tm_isdst < 0; 731 1.50 christos # if HAVE_TZNAME 732 1.40 christos if (tzname[t->tm_isdst != 0][0] == '-') 733 1.40 christos negative = true; 734 1.50 christos # endif 735 1.40 christos # endif 736 1.39 christos } 737 1.39 christos if (negative) { 738 1.12 kleink sign = "-"; 739 1.12 kleink diff = -diff; 740 1.12 kleink } else sign = "+"; 741 1.12 kleink pt = _add(sign, pt, ptlim); 742 1.20 mlelstv diff /= SECSPERMIN; 743 1.20 mlelstv diff = (diff / MINSPERHOUR) * 100 + 744 1.20 mlelstv (diff % MINSPERHOUR); 745 1.30 christos _DIAGASSERT(__type_fit(int, diff)); 746 1.44 christos pt = _conv((int)diff, 747 1.44 christos fmt_padding[PAD_FMT_YEAR][PadIndex], 748 1.44 christos pt, ptlim, loc); 749 1.12 kleink } 750 1.38 christos #endif 751 1.1 mrg continue; 752 1.12 kleink case '+': 753 1.44 christos #ifdef notyet 754 1.44 christos /* XXX: no date_fmt in _TimeLocale */ 755 1.44 christos pt = _fmt(sp, tptr->date_fmt, t, 756 1.25 joerg pt, ptlim, warnp, loc); 757 1.44 christos #else 758 1.44 christos pt = _fmt(sp, "%a %b %e %H:%M:%S %Z %Y", t, 759 1.44 christos pt, ptlim, warnp, loc); 760 1.44 christos #endif 761 1.12 kleink continue; 762 1.44 christos case '-': 763 1.44 christos if (PadIndex != PAD_DEFAULT) 764 1.44 christos break; 765 1.44 christos PadIndex = PAD_LESS; 766 1.44 christos goto label; 767 1.44 christos case '_': 768 1.44 christos if (PadIndex != PAD_DEFAULT) 769 1.44 christos break; 770 1.44 christos PadIndex = PAD_SPACE; 771 1.44 christos goto label; 772 1.44 christos case '0': 773 1.44 christos if (PadIndex != PAD_DEFAULT) 774 1.44 christos break; 775 1.44 christos PadIndex = PAD_ZERO; 776 1.44 christos goto label; 777 1.1 mrg case '%': 778 1.1 mrg break; 779 1.1 mrg } 780 1.1 mrg } 781 1.12 kleink if (pt == ptlim) 782 1.12 kleink break; 783 1.12 kleink *pt++ = *format; 784 1.1 mrg } 785 1.12 kleink return pt; 786 1.1 mrg } 787 1.1 mrg 788 1.21 christos size_t 789 1.52 christos strftime(char *restrict s, size_t maxsize, char const *restrict format, 790 1.52 christos struct tm const *restrict t) 791 1.21 christos { 792 1.43 christos size_t r; 793 1.43 christos 794 1.43 christos rwlock_wrlock(&__lcl_lock); 795 1.43 christos tzset_unlocked(); 796 1.43 christos r = strftime_z(__lclptr, s, maxsize, format, t); 797 1.43 christos rwlock_unlock(&__lcl_lock); 798 1.43 christos 799 1.43 christos return r; 800 1.21 christos } 801 1.21 christos 802 1.25 joerg size_t 803 1.25 joerg strftime_l(char * __restrict s, size_t maxsize, const char * __restrict format, 804 1.25 joerg const struct tm * __restrict t, locale_t loc) 805 1.25 joerg { 806 1.43 christos size_t r; 807 1.43 christos 808 1.43 christos rwlock_wrlock(&__lcl_lock); 809 1.43 christos tzset_unlocked(); 810 1.43 christos r = strftime_lz(__lclptr, s, maxsize, format, t, loc); 811 1.43 christos rwlock_unlock(&__lcl_lock); 812 1.43 christos 813 1.43 christos return r; 814 1.25 joerg } 815 1.25 joerg 816 1.12 kleink static char * 817 1.44 christos _conv(int n, const char *format, char *pt, const char *ptlim, locale_t loc) 818 1.1 mrg { 819 1.12 kleink char buf[INT_STRLEN_MAXIMUM(int) + 1]; 820 1.1 mrg 821 1.44 christos (void) snprintf_l(buf, sizeof(buf), loc, format, n); 822 1.12 kleink return _add(buf, pt, ptlim); 823 1.1 mrg } 824 1.1 mrg 825 1.12 kleink static char * 826 1.34 christos _add(const char *str, char *pt, const char *ptlim) 827 1.1 mrg { 828 1.12 kleink while (pt < ptlim && (*pt = *str++) != '\0') 829 1.12 kleink ++pt; 830 1.12 kleink return pt; 831 1.1 mrg } 832 1.20 mlelstv 833 1.20 mlelstv /* 834 1.20 mlelstv ** POSIX and the C Standard are unclear or inconsistent about 835 1.20 mlelstv ** what %C and %y do if the year is negative or exceeds 9999. 836 1.20 mlelstv ** Use the convention that %C concatenated with %y yields the 837 1.20 mlelstv ** same output as %Y, and that %Y contains at least 4 bytes, 838 1.20 mlelstv ** with more only if necessary. 839 1.20 mlelstv */ 840 1.20 mlelstv 841 1.20 mlelstv static char * 842 1.33 christos _yconv(int a, int b, bool convert_top, bool convert_yy, 843 1.44 christos char *pt, const char * ptlim, locale_t loc) 844 1.20 mlelstv { 845 1.28 christos int lead; 846 1.28 christos int trail; 847 1.20 mlelstv 848 1.50 christos int DIVISOR = 100; 849 1.20 mlelstv trail = a % DIVISOR + b % DIVISOR; 850 1.20 mlelstv lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; 851 1.20 mlelstv trail %= DIVISOR; 852 1.20 mlelstv if (trail < 0 && lead > 0) { 853 1.20 mlelstv trail += DIVISOR; 854 1.20 mlelstv --lead; 855 1.20 mlelstv } else if (lead < 0 && trail > 0) { 856 1.20 mlelstv trail -= DIVISOR; 857 1.20 mlelstv ++lead; 858 1.20 mlelstv } 859 1.20 mlelstv if (convert_top) { 860 1.20 mlelstv if (lead == 0 && trail < 0) 861 1.20 mlelstv pt = _add("-0", pt, ptlim); 862 1.44 christos else pt = _conv(lead, "%02d", pt, ptlim, loc); 863 1.20 mlelstv } 864 1.20 mlelstv if (convert_yy) 865 1.44 christos pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim, 866 1.44 christos loc); 867 1.20 mlelstv return pt; 868 1.20 mlelstv } 869