14616d0f9Sopenharmony_ci/* Convert a broken-down timestamp to a string. */ 24616d0f9Sopenharmony_ci 34616d0f9Sopenharmony_ci/* Copyright 1989 The Regents of the University of California. 44616d0f9Sopenharmony_ci All rights reserved. 54616d0f9Sopenharmony_ci 64616d0f9Sopenharmony_ci Redistribution and use in source and binary forms, with or without 74616d0f9Sopenharmony_ci modification, are permitted provided that the following conditions 84616d0f9Sopenharmony_ci are met: 94616d0f9Sopenharmony_ci 1. Redistributions of source code must retain the above copyright 104616d0f9Sopenharmony_ci notice, this list of conditions and the following disclaimer. 114616d0f9Sopenharmony_ci 2. Redistributions in binary form must reproduce the above copyright 124616d0f9Sopenharmony_ci notice, this list of conditions and the following disclaimer in the 134616d0f9Sopenharmony_ci documentation and/or other materials provided with the distribution. 144616d0f9Sopenharmony_ci 3. Neither the name of the University nor the names of its contributors 154616d0f9Sopenharmony_ci may be used to endorse or promote products derived from this software 164616d0f9Sopenharmony_ci without specific prior written permission. 174616d0f9Sopenharmony_ci 184616d0f9Sopenharmony_ci THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND 194616d0f9Sopenharmony_ci ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 204616d0f9Sopenharmony_ci IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 214616d0f9Sopenharmony_ci ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 224616d0f9Sopenharmony_ci FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 234616d0f9Sopenharmony_ci DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 244616d0f9Sopenharmony_ci OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 254616d0f9Sopenharmony_ci HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 264616d0f9Sopenharmony_ci LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 274616d0f9Sopenharmony_ci OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 284616d0f9Sopenharmony_ci SUCH DAMAGE. */ 294616d0f9Sopenharmony_ci 304616d0f9Sopenharmony_ci/* 314616d0f9Sopenharmony_ci** Based on the UCB version with the copyright notice appearing above. 324616d0f9Sopenharmony_ci** 334616d0f9Sopenharmony_ci** This is ANSIish only when "multibyte character == plain character". 344616d0f9Sopenharmony_ci*/ 354616d0f9Sopenharmony_ci 364616d0f9Sopenharmony_ci#include "private.h" 374616d0f9Sopenharmony_ci 384616d0f9Sopenharmony_ci#include <fcntl.h> 394616d0f9Sopenharmony_ci#include <locale.h> 404616d0f9Sopenharmony_ci#include <stdio.h> 414616d0f9Sopenharmony_ci 424616d0f9Sopenharmony_ci#ifndef DEPRECATE_TWO_DIGIT_YEARS 434616d0f9Sopenharmony_ci# define DEPRECATE_TWO_DIGIT_YEARS false 444616d0f9Sopenharmony_ci#endif 454616d0f9Sopenharmony_ci 464616d0f9Sopenharmony_cistruct lc_time_T { 474616d0f9Sopenharmony_ci const char * mon[MONSPERYEAR]; 484616d0f9Sopenharmony_ci const char * month[MONSPERYEAR]; 494616d0f9Sopenharmony_ci const char * wday[DAYSPERWEEK]; 504616d0f9Sopenharmony_ci const char * weekday[DAYSPERWEEK]; 514616d0f9Sopenharmony_ci const char * X_fmt; 524616d0f9Sopenharmony_ci const char * x_fmt; 534616d0f9Sopenharmony_ci const char * c_fmt; 544616d0f9Sopenharmony_ci const char * am; 554616d0f9Sopenharmony_ci const char * pm; 564616d0f9Sopenharmony_ci const char * date_fmt; 574616d0f9Sopenharmony_ci}; 584616d0f9Sopenharmony_ci 594616d0f9Sopenharmony_cistatic const struct lc_time_T C_time_locale = { 604616d0f9Sopenharmony_ci { 614616d0f9Sopenharmony_ci "Jan", "Feb", "Mar", "Apr", "May", "Jun", 624616d0f9Sopenharmony_ci "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 634616d0f9Sopenharmony_ci }, { 644616d0f9Sopenharmony_ci "January", "February", "March", "April", "May", "June", 654616d0f9Sopenharmony_ci "July", "August", "September", "October", "November", "December" 664616d0f9Sopenharmony_ci }, { 674616d0f9Sopenharmony_ci "Sun", "Mon", "Tue", "Wed", 684616d0f9Sopenharmony_ci "Thu", "Fri", "Sat" 694616d0f9Sopenharmony_ci }, { 704616d0f9Sopenharmony_ci "Sunday", "Monday", "Tuesday", "Wednesday", 714616d0f9Sopenharmony_ci "Thursday", "Friday", "Saturday" 724616d0f9Sopenharmony_ci }, 734616d0f9Sopenharmony_ci 744616d0f9Sopenharmony_ci /* X_fmt */ 754616d0f9Sopenharmony_ci "%H:%M:%S", 764616d0f9Sopenharmony_ci 774616d0f9Sopenharmony_ci /* 784616d0f9Sopenharmony_ci ** x_fmt 794616d0f9Sopenharmony_ci ** C99 and later require this format. 804616d0f9Sopenharmony_ci ** Using just numbers (as here) makes Quakers happier; 814616d0f9Sopenharmony_ci ** it's also compatible with SVR4. 824616d0f9Sopenharmony_ci */ 834616d0f9Sopenharmony_ci "%m/%d/%y", 844616d0f9Sopenharmony_ci 854616d0f9Sopenharmony_ci /* 864616d0f9Sopenharmony_ci ** c_fmt 874616d0f9Sopenharmony_ci ** C99 and later require this format. 884616d0f9Sopenharmony_ci ** Previously this code used "%D %X", but we now conform to C99. 894616d0f9Sopenharmony_ci ** Note that 904616d0f9Sopenharmony_ci ** "%a %b %d %H:%M:%S %Y" 914616d0f9Sopenharmony_ci ** is used by Solaris 2.3. 924616d0f9Sopenharmony_ci */ 934616d0f9Sopenharmony_ci "%a %b %e %T %Y", 944616d0f9Sopenharmony_ci 954616d0f9Sopenharmony_ci /* am */ 964616d0f9Sopenharmony_ci "AM", 974616d0f9Sopenharmony_ci 984616d0f9Sopenharmony_ci /* pm */ 994616d0f9Sopenharmony_ci "PM", 1004616d0f9Sopenharmony_ci 1014616d0f9Sopenharmony_ci /* date_fmt */ 1024616d0f9Sopenharmony_ci "%a %b %e %H:%M:%S %Z %Y" 1034616d0f9Sopenharmony_ci}; 1044616d0f9Sopenharmony_ci 1054616d0f9Sopenharmony_cienum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL }; 1064616d0f9Sopenharmony_ci 1074616d0f9Sopenharmony_cistatic char * _add(const char *, char *, const char *); 1084616d0f9Sopenharmony_cistatic char * _conv(int, const char *, char *, const char *); 1094616d0f9Sopenharmony_cistatic char * _fmt(const char *, const struct tm *, char *, const char *, 1104616d0f9Sopenharmony_ci enum warn *); 1114616d0f9Sopenharmony_cistatic char * _yconv(int, int, bool, bool, char *, char const *); 1124616d0f9Sopenharmony_ci 1134616d0f9Sopenharmony_ci#ifndef YEAR_2000_NAME 1144616d0f9Sopenharmony_ci# define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" 1154616d0f9Sopenharmony_ci#endif /* !defined YEAR_2000_NAME */ 1164616d0f9Sopenharmony_ci 1174616d0f9Sopenharmony_ci#if HAVE_STRFTIME_L 1184616d0f9Sopenharmony_cisize_t 1194616d0f9Sopenharmony_cistrftime_l(char *restrict s, size_t maxsize, char const *restrict format, 1204616d0f9Sopenharmony_ci struct tm const *restrict t, 1214616d0f9Sopenharmony_ci ATTRIBUTE_MAYBE_UNUSED locale_t locale) 1224616d0f9Sopenharmony_ci{ 1234616d0f9Sopenharmony_ci /* Just call strftime, as only the C locale is supported. */ 1244616d0f9Sopenharmony_ci return strftime(s, maxsize, format, t); 1254616d0f9Sopenharmony_ci} 1264616d0f9Sopenharmony_ci#endif 1274616d0f9Sopenharmony_ci 1284616d0f9Sopenharmony_cisize_t 1294616d0f9Sopenharmony_cistrftime(char *restrict s, size_t maxsize, char const *restrict format, 1304616d0f9Sopenharmony_ci struct tm const *restrict t) 1314616d0f9Sopenharmony_ci{ 1324616d0f9Sopenharmony_ci char * p; 1334616d0f9Sopenharmony_ci int saved_errno = errno; 1344616d0f9Sopenharmony_ci enum warn warn = IN_NONE; 1354616d0f9Sopenharmony_ci 1364616d0f9Sopenharmony_ci tzset(); 1374616d0f9Sopenharmony_ci p = _fmt(format, t, s, s + maxsize, &warn); 1384616d0f9Sopenharmony_ci if (!p) { 1394616d0f9Sopenharmony_ci errno = EOVERFLOW; 1404616d0f9Sopenharmony_ci return 0; 1414616d0f9Sopenharmony_ci } 1424616d0f9Sopenharmony_ci if (DEPRECATE_TWO_DIGIT_YEARS 1434616d0f9Sopenharmony_ci && warn != IN_NONE && getenv(YEAR_2000_NAME)) { 1444616d0f9Sopenharmony_ci fprintf(stderr, "\n"); 1454616d0f9Sopenharmony_ci fprintf(stderr, "strftime format \"%s\" ", format); 1464616d0f9Sopenharmony_ci fprintf(stderr, "yields only two digits of years in "); 1474616d0f9Sopenharmony_ci if (warn == IN_SOME) 1484616d0f9Sopenharmony_ci fprintf(stderr, "some locales"); 1494616d0f9Sopenharmony_ci else if (warn == IN_THIS) 1504616d0f9Sopenharmony_ci fprintf(stderr, "the current locale"); 1514616d0f9Sopenharmony_ci else fprintf(stderr, "all locales"); 1524616d0f9Sopenharmony_ci fprintf(stderr, "\n"); 1534616d0f9Sopenharmony_ci } 1544616d0f9Sopenharmony_ci if (p == s + maxsize) { 1554616d0f9Sopenharmony_ci errno = ERANGE; 1564616d0f9Sopenharmony_ci return 0; 1574616d0f9Sopenharmony_ci } 1584616d0f9Sopenharmony_ci *p = '\0'; 1594616d0f9Sopenharmony_ci errno = saved_errno; 1604616d0f9Sopenharmony_ci return p - s; 1614616d0f9Sopenharmony_ci} 1624616d0f9Sopenharmony_ci 1634616d0f9Sopenharmony_cistatic char * 1644616d0f9Sopenharmony_ci_fmt(const char *format, const struct tm *t, char *pt, 1654616d0f9Sopenharmony_ci const char *ptlim, enum warn *warnp) 1664616d0f9Sopenharmony_ci{ 1674616d0f9Sopenharmony_ci struct lc_time_T const *Locale = &C_time_locale; 1684616d0f9Sopenharmony_ci 1694616d0f9Sopenharmony_ci for ( ; *format; ++format) { 1704616d0f9Sopenharmony_ci if (*format == '%') { 1714616d0f9Sopenharmony_cilabel: 1724616d0f9Sopenharmony_ci switch (*++format) { 1734616d0f9Sopenharmony_ci case '\0': 1744616d0f9Sopenharmony_ci --format; 1754616d0f9Sopenharmony_ci break; 1764616d0f9Sopenharmony_ci case 'A': 1774616d0f9Sopenharmony_ci pt = _add((t->tm_wday < 0 || 1784616d0f9Sopenharmony_ci t->tm_wday >= DAYSPERWEEK) ? 1794616d0f9Sopenharmony_ci "?" : Locale->weekday[t->tm_wday], 1804616d0f9Sopenharmony_ci pt, ptlim); 1814616d0f9Sopenharmony_ci continue; 1824616d0f9Sopenharmony_ci case 'a': 1834616d0f9Sopenharmony_ci pt = _add((t->tm_wday < 0 || 1844616d0f9Sopenharmony_ci t->tm_wday >= DAYSPERWEEK) ? 1854616d0f9Sopenharmony_ci "?" : Locale->wday[t->tm_wday], 1864616d0f9Sopenharmony_ci pt, ptlim); 1874616d0f9Sopenharmony_ci continue; 1884616d0f9Sopenharmony_ci case 'B': 1894616d0f9Sopenharmony_ci pt = _add((t->tm_mon < 0 || 1904616d0f9Sopenharmony_ci t->tm_mon >= MONSPERYEAR) ? 1914616d0f9Sopenharmony_ci "?" : Locale->month[t->tm_mon], 1924616d0f9Sopenharmony_ci pt, ptlim); 1934616d0f9Sopenharmony_ci continue; 1944616d0f9Sopenharmony_ci case 'b': 1954616d0f9Sopenharmony_ci case 'h': 1964616d0f9Sopenharmony_ci pt = _add((t->tm_mon < 0 || 1974616d0f9Sopenharmony_ci t->tm_mon >= MONSPERYEAR) ? 1984616d0f9Sopenharmony_ci "?" : Locale->mon[t->tm_mon], 1994616d0f9Sopenharmony_ci pt, ptlim); 2004616d0f9Sopenharmony_ci continue; 2014616d0f9Sopenharmony_ci case 'C': 2024616d0f9Sopenharmony_ci /* 2034616d0f9Sopenharmony_ci ** %C used to do a... 2044616d0f9Sopenharmony_ci ** _fmt("%a %b %e %X %Y", t); 2054616d0f9Sopenharmony_ci ** ...whereas now POSIX 1003.2 calls for 2064616d0f9Sopenharmony_ci ** something completely different. 2074616d0f9Sopenharmony_ci ** (ado, 1993-05-24) 2084616d0f9Sopenharmony_ci */ 2094616d0f9Sopenharmony_ci pt = _yconv(t->tm_year, TM_YEAR_BASE, 2104616d0f9Sopenharmony_ci true, false, pt, ptlim); 2114616d0f9Sopenharmony_ci continue; 2124616d0f9Sopenharmony_ci case 'c': 2134616d0f9Sopenharmony_ci { 2144616d0f9Sopenharmony_ci enum warn warn2 = IN_SOME; 2154616d0f9Sopenharmony_ci 2164616d0f9Sopenharmony_ci pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2); 2174616d0f9Sopenharmony_ci if (warn2 == IN_ALL) 2184616d0f9Sopenharmony_ci warn2 = IN_THIS; 2194616d0f9Sopenharmony_ci if (warn2 > *warnp) 2204616d0f9Sopenharmony_ci *warnp = warn2; 2214616d0f9Sopenharmony_ci } 2224616d0f9Sopenharmony_ci continue; 2234616d0f9Sopenharmony_ci case 'D': 2244616d0f9Sopenharmony_ci pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); 2254616d0f9Sopenharmony_ci continue; 2264616d0f9Sopenharmony_ci case 'd': 2274616d0f9Sopenharmony_ci pt = _conv(t->tm_mday, "%02d", pt, ptlim); 2284616d0f9Sopenharmony_ci continue; 2294616d0f9Sopenharmony_ci case 'E': 2304616d0f9Sopenharmony_ci case 'O': 2314616d0f9Sopenharmony_ci /* 2324616d0f9Sopenharmony_ci ** Locale modifiers of C99 and later. 2334616d0f9Sopenharmony_ci ** The sequences 2344616d0f9Sopenharmony_ci ** %Ec %EC %Ex %EX %Ey %EY 2354616d0f9Sopenharmony_ci ** %Od %oe %OH %OI %Om %OM 2364616d0f9Sopenharmony_ci ** %OS %Ou %OU %OV %Ow %OW %Oy 2374616d0f9Sopenharmony_ci ** are supposed to provide alternative 2384616d0f9Sopenharmony_ci ** representations. 2394616d0f9Sopenharmony_ci */ 2404616d0f9Sopenharmony_ci goto label; 2414616d0f9Sopenharmony_ci case 'e': 2424616d0f9Sopenharmony_ci pt = _conv(t->tm_mday, "%2d", pt, ptlim); 2434616d0f9Sopenharmony_ci continue; 2444616d0f9Sopenharmony_ci case 'F': 2454616d0f9Sopenharmony_ci pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); 2464616d0f9Sopenharmony_ci continue; 2474616d0f9Sopenharmony_ci case 'H': 2484616d0f9Sopenharmony_ci pt = _conv(t->tm_hour, "%02d", pt, ptlim); 2494616d0f9Sopenharmony_ci continue; 2504616d0f9Sopenharmony_ci case 'I': 2514616d0f9Sopenharmony_ci pt = _conv((t->tm_hour % 12) ? 2524616d0f9Sopenharmony_ci (t->tm_hour % 12) : 12, 2534616d0f9Sopenharmony_ci "%02d", pt, ptlim); 2544616d0f9Sopenharmony_ci continue; 2554616d0f9Sopenharmony_ci case 'j': 2564616d0f9Sopenharmony_ci pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); 2574616d0f9Sopenharmony_ci continue; 2584616d0f9Sopenharmony_ci case 'k': 2594616d0f9Sopenharmony_ci /* 2604616d0f9Sopenharmony_ci ** This used to be... 2614616d0f9Sopenharmony_ci ** _conv(t->tm_hour % 12 ? 2624616d0f9Sopenharmony_ci ** t->tm_hour % 12 : 12, 2, ' '); 2634616d0f9Sopenharmony_ci ** ...and has been changed to the below to 2644616d0f9Sopenharmony_ci ** match SunOS 4.1.1 and Arnold Robbins' 2654616d0f9Sopenharmony_ci ** strftime version 3.0. That is, "%k" and 2664616d0f9Sopenharmony_ci ** "%l" have been swapped. 2674616d0f9Sopenharmony_ci ** (ado, 1993-05-24) 2684616d0f9Sopenharmony_ci */ 2694616d0f9Sopenharmony_ci pt = _conv(t->tm_hour, "%2d", pt, ptlim); 2704616d0f9Sopenharmony_ci continue; 2714616d0f9Sopenharmony_ci#ifdef KITCHEN_SINK 2724616d0f9Sopenharmony_ci case 'K': 2734616d0f9Sopenharmony_ci /* 2744616d0f9Sopenharmony_ci ** After all this time, still unclaimed! 2754616d0f9Sopenharmony_ci */ 2764616d0f9Sopenharmony_ci pt = _add("kitchen sink", pt, ptlim); 2774616d0f9Sopenharmony_ci continue; 2784616d0f9Sopenharmony_ci#endif /* defined KITCHEN_SINK */ 2794616d0f9Sopenharmony_ci case 'l': 2804616d0f9Sopenharmony_ci /* 2814616d0f9Sopenharmony_ci ** This used to be... 2824616d0f9Sopenharmony_ci ** _conv(t->tm_hour, 2, ' '); 2834616d0f9Sopenharmony_ci ** ...and has been changed to the below to 2844616d0f9Sopenharmony_ci ** match SunOS 4.1.1 and Arnold Robbin's 2854616d0f9Sopenharmony_ci ** strftime version 3.0. That is, "%k" and 2864616d0f9Sopenharmony_ci ** "%l" have been swapped. 2874616d0f9Sopenharmony_ci ** (ado, 1993-05-24) 2884616d0f9Sopenharmony_ci */ 2894616d0f9Sopenharmony_ci pt = _conv((t->tm_hour % 12) ? 2904616d0f9Sopenharmony_ci (t->tm_hour % 12) : 12, 2914616d0f9Sopenharmony_ci "%2d", pt, ptlim); 2924616d0f9Sopenharmony_ci continue; 2934616d0f9Sopenharmony_ci case 'M': 2944616d0f9Sopenharmony_ci pt = _conv(t->tm_min, "%02d", pt, ptlim); 2954616d0f9Sopenharmony_ci continue; 2964616d0f9Sopenharmony_ci case 'm': 2974616d0f9Sopenharmony_ci pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); 2984616d0f9Sopenharmony_ci continue; 2994616d0f9Sopenharmony_ci case 'n': 3004616d0f9Sopenharmony_ci pt = _add("\n", pt, ptlim); 3014616d0f9Sopenharmony_ci continue; 3024616d0f9Sopenharmony_ci case 'p': 3034616d0f9Sopenharmony_ci pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? 3044616d0f9Sopenharmony_ci Locale->pm : 3054616d0f9Sopenharmony_ci Locale->am, 3064616d0f9Sopenharmony_ci pt, ptlim); 3074616d0f9Sopenharmony_ci continue; 3084616d0f9Sopenharmony_ci case 'R': 3094616d0f9Sopenharmony_ci pt = _fmt("%H:%M", t, pt, ptlim, warnp); 3104616d0f9Sopenharmony_ci continue; 3114616d0f9Sopenharmony_ci case 'r': 3124616d0f9Sopenharmony_ci pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp); 3134616d0f9Sopenharmony_ci continue; 3144616d0f9Sopenharmony_ci case 'S': 3154616d0f9Sopenharmony_ci pt = _conv(t->tm_sec, "%02d", pt, ptlim); 3164616d0f9Sopenharmony_ci continue; 3174616d0f9Sopenharmony_ci case 's': 3184616d0f9Sopenharmony_ci { 3194616d0f9Sopenharmony_ci struct tm tm; 3204616d0f9Sopenharmony_ci char buf[INT_STRLEN_MAXIMUM( 3214616d0f9Sopenharmony_ci time_t) + 1]; 3224616d0f9Sopenharmony_ci time_t mkt; 3234616d0f9Sopenharmony_ci 3244616d0f9Sopenharmony_ci tm.tm_sec = t->tm_sec; 3254616d0f9Sopenharmony_ci tm.tm_min = t->tm_min; 3264616d0f9Sopenharmony_ci tm.tm_hour = t->tm_hour; 3274616d0f9Sopenharmony_ci tm.tm_mday = t->tm_mday; 3284616d0f9Sopenharmony_ci tm.tm_mon = t->tm_mon; 3294616d0f9Sopenharmony_ci tm.tm_year = t->tm_year; 3304616d0f9Sopenharmony_ci#ifdef TM_GMTOFF 3314616d0f9Sopenharmony_ci mkt = timeoff(&tm, t->TM_GMTOFF); 3324616d0f9Sopenharmony_ci#else 3334616d0f9Sopenharmony_ci tm.tm_isdst = t->tm_isdst; 3344616d0f9Sopenharmony_ci mkt = mktime(&tm); 3354616d0f9Sopenharmony_ci#endif 3364616d0f9Sopenharmony_ci /* If mktime fails, %s expands to the 3374616d0f9Sopenharmony_ci value of (time_t) -1 as a failure 3384616d0f9Sopenharmony_ci marker; this is better in practice 3394616d0f9Sopenharmony_ci than strftime failing. */ 3404616d0f9Sopenharmony_ci if (TYPE_SIGNED(time_t)) { 3414616d0f9Sopenharmony_ci intmax_t n = mkt; 3424616d0f9Sopenharmony_ci sprintf(buf, "%"PRIdMAX, n); 3434616d0f9Sopenharmony_ci } else { 3444616d0f9Sopenharmony_ci uintmax_t n = mkt; 3454616d0f9Sopenharmony_ci sprintf(buf, "%"PRIuMAX, n); 3464616d0f9Sopenharmony_ci } 3474616d0f9Sopenharmony_ci pt = _add(buf, pt, ptlim); 3484616d0f9Sopenharmony_ci } 3494616d0f9Sopenharmony_ci continue; 3504616d0f9Sopenharmony_ci case 'T': 3514616d0f9Sopenharmony_ci pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); 3524616d0f9Sopenharmony_ci continue; 3534616d0f9Sopenharmony_ci case 't': 3544616d0f9Sopenharmony_ci pt = _add("\t", pt, ptlim); 3554616d0f9Sopenharmony_ci continue; 3564616d0f9Sopenharmony_ci case 'U': 3574616d0f9Sopenharmony_ci pt = _conv((t->tm_yday + DAYSPERWEEK - 3584616d0f9Sopenharmony_ci t->tm_wday) / DAYSPERWEEK, 3594616d0f9Sopenharmony_ci "%02d", pt, ptlim); 3604616d0f9Sopenharmony_ci continue; 3614616d0f9Sopenharmony_ci case 'u': 3624616d0f9Sopenharmony_ci /* 3634616d0f9Sopenharmony_ci ** From Arnold Robbins' strftime version 3.0: 3644616d0f9Sopenharmony_ci ** "ISO 8601: Weekday as a decimal number 3654616d0f9Sopenharmony_ci ** [1 (Monday) - 7]" 3664616d0f9Sopenharmony_ci ** (ado, 1993-05-24) 3674616d0f9Sopenharmony_ci */ 3684616d0f9Sopenharmony_ci pt = _conv((t->tm_wday == 0) ? 3694616d0f9Sopenharmony_ci DAYSPERWEEK : t->tm_wday, 3704616d0f9Sopenharmony_ci "%d", pt, ptlim); 3714616d0f9Sopenharmony_ci continue; 3724616d0f9Sopenharmony_ci case 'V': /* ISO 8601 week number */ 3734616d0f9Sopenharmony_ci case 'G': /* ISO 8601 year (four digits) */ 3744616d0f9Sopenharmony_ci case 'g': /* ISO 8601 year (two digits) */ 3754616d0f9Sopenharmony_ci/* 3764616d0f9Sopenharmony_ci** From Arnold Robbins' strftime version 3.0: "the week number of the 3774616d0f9Sopenharmony_ci** year (the first Monday as the first day of week 1) as a decimal number 3784616d0f9Sopenharmony_ci** (01-53)." 3794616d0f9Sopenharmony_ci** (ado, 1993-05-24) 3804616d0f9Sopenharmony_ci** 3814616d0f9Sopenharmony_ci** From <https://www.cl.cam.ac.uk/~mgk25/iso-time.html> by Markus Kuhn: 3824616d0f9Sopenharmony_ci** "Week 01 of a year is per definition the first week which has the 3834616d0f9Sopenharmony_ci** Thursday in this year, which is equivalent to the week which contains 3844616d0f9Sopenharmony_ci** the fourth day of January. In other words, the first week of a new year 3854616d0f9Sopenharmony_ci** is the week which has the majority of its days in the new year. Week 01 3864616d0f9Sopenharmony_ci** might also contain days from the previous year and the week before week 3874616d0f9Sopenharmony_ci** 01 of a year is the last week (52 or 53) of the previous year even if 3884616d0f9Sopenharmony_ci** it contains days from the new year. A week starts with Monday (day 1) 3894616d0f9Sopenharmony_ci** and ends with Sunday (day 7). For example, the first week of the year 3904616d0f9Sopenharmony_ci** 1997 lasts from 1996-12-30 to 1997-01-05..." 3914616d0f9Sopenharmony_ci** (ado, 1996-01-02) 3924616d0f9Sopenharmony_ci*/ 3934616d0f9Sopenharmony_ci { 3944616d0f9Sopenharmony_ci int year; 3954616d0f9Sopenharmony_ci int base; 3964616d0f9Sopenharmony_ci int yday; 3974616d0f9Sopenharmony_ci int wday; 3984616d0f9Sopenharmony_ci int w; 3994616d0f9Sopenharmony_ci 4004616d0f9Sopenharmony_ci year = t->tm_year; 4014616d0f9Sopenharmony_ci base = TM_YEAR_BASE; 4024616d0f9Sopenharmony_ci yday = t->tm_yday; 4034616d0f9Sopenharmony_ci wday = t->tm_wday; 4044616d0f9Sopenharmony_ci for ( ; ; ) { 4054616d0f9Sopenharmony_ci int len; 4064616d0f9Sopenharmony_ci int bot; 4074616d0f9Sopenharmony_ci int top; 4084616d0f9Sopenharmony_ci 4094616d0f9Sopenharmony_ci len = isleap_sum(year, base) ? 4104616d0f9Sopenharmony_ci DAYSPERLYEAR : 4114616d0f9Sopenharmony_ci DAYSPERNYEAR; 4124616d0f9Sopenharmony_ci /* 4134616d0f9Sopenharmony_ci ** What yday (-3 ... 3) does 4144616d0f9Sopenharmony_ci ** the ISO year begin on? 4154616d0f9Sopenharmony_ci */ 4164616d0f9Sopenharmony_ci bot = ((yday + 11 - wday) % 4174616d0f9Sopenharmony_ci DAYSPERWEEK) - 3; 4184616d0f9Sopenharmony_ci /* 4194616d0f9Sopenharmony_ci ** What yday does the NEXT 4204616d0f9Sopenharmony_ci ** ISO year begin on? 4214616d0f9Sopenharmony_ci */ 4224616d0f9Sopenharmony_ci top = bot - 4234616d0f9Sopenharmony_ci (len % DAYSPERWEEK); 4244616d0f9Sopenharmony_ci if (top < -3) 4254616d0f9Sopenharmony_ci top += DAYSPERWEEK; 4264616d0f9Sopenharmony_ci top += len; 4274616d0f9Sopenharmony_ci if (yday >= top) { 4284616d0f9Sopenharmony_ci ++base; 4294616d0f9Sopenharmony_ci w = 1; 4304616d0f9Sopenharmony_ci break; 4314616d0f9Sopenharmony_ci } 4324616d0f9Sopenharmony_ci if (yday >= bot) { 4334616d0f9Sopenharmony_ci w = 1 + ((yday - bot) / 4344616d0f9Sopenharmony_ci DAYSPERWEEK); 4354616d0f9Sopenharmony_ci break; 4364616d0f9Sopenharmony_ci } 4374616d0f9Sopenharmony_ci --base; 4384616d0f9Sopenharmony_ci yday += isleap_sum(year, base) ? 4394616d0f9Sopenharmony_ci DAYSPERLYEAR : 4404616d0f9Sopenharmony_ci DAYSPERNYEAR; 4414616d0f9Sopenharmony_ci } 4424616d0f9Sopenharmony_ci#ifdef XPG4_1994_04_09 4434616d0f9Sopenharmony_ci if ((w == 52 && 4444616d0f9Sopenharmony_ci t->tm_mon == TM_JANUARY) || 4454616d0f9Sopenharmony_ci (w == 1 && 4464616d0f9Sopenharmony_ci t->tm_mon == TM_DECEMBER)) 4474616d0f9Sopenharmony_ci w = 53; 4484616d0f9Sopenharmony_ci#endif /* defined XPG4_1994_04_09 */ 4494616d0f9Sopenharmony_ci if (*format == 'V') 4504616d0f9Sopenharmony_ci pt = _conv(w, "%02d", 4514616d0f9Sopenharmony_ci pt, ptlim); 4524616d0f9Sopenharmony_ci else if (*format == 'g') { 4534616d0f9Sopenharmony_ci *warnp = IN_ALL; 4544616d0f9Sopenharmony_ci pt = _yconv(year, base, 4554616d0f9Sopenharmony_ci false, true, 4564616d0f9Sopenharmony_ci pt, ptlim); 4574616d0f9Sopenharmony_ci } else pt = _yconv(year, base, 4584616d0f9Sopenharmony_ci true, true, 4594616d0f9Sopenharmony_ci pt, ptlim); 4604616d0f9Sopenharmony_ci } 4614616d0f9Sopenharmony_ci continue; 4624616d0f9Sopenharmony_ci case 'v': 4634616d0f9Sopenharmony_ci /* 4644616d0f9Sopenharmony_ci ** From Arnold Robbins' strftime version 3.0: 4654616d0f9Sopenharmony_ci ** "date as dd-bbb-YYYY" 4664616d0f9Sopenharmony_ci ** (ado, 1993-05-24) 4674616d0f9Sopenharmony_ci */ 4684616d0f9Sopenharmony_ci pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp); 4694616d0f9Sopenharmony_ci continue; 4704616d0f9Sopenharmony_ci case 'W': 4714616d0f9Sopenharmony_ci pt = _conv((t->tm_yday + DAYSPERWEEK - 4724616d0f9Sopenharmony_ci (t->tm_wday ? 4734616d0f9Sopenharmony_ci (t->tm_wday - 1) : 4744616d0f9Sopenharmony_ci (DAYSPERWEEK - 1))) / DAYSPERWEEK, 4754616d0f9Sopenharmony_ci "%02d", pt, ptlim); 4764616d0f9Sopenharmony_ci continue; 4774616d0f9Sopenharmony_ci case 'w': 4784616d0f9Sopenharmony_ci pt = _conv(t->tm_wday, "%d", pt, ptlim); 4794616d0f9Sopenharmony_ci continue; 4804616d0f9Sopenharmony_ci case 'X': 4814616d0f9Sopenharmony_ci pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp); 4824616d0f9Sopenharmony_ci continue; 4834616d0f9Sopenharmony_ci case 'x': 4844616d0f9Sopenharmony_ci { 4854616d0f9Sopenharmony_ci enum warn warn2 = IN_SOME; 4864616d0f9Sopenharmony_ci 4874616d0f9Sopenharmony_ci pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2); 4884616d0f9Sopenharmony_ci if (warn2 == IN_ALL) 4894616d0f9Sopenharmony_ci warn2 = IN_THIS; 4904616d0f9Sopenharmony_ci if (warn2 > *warnp) 4914616d0f9Sopenharmony_ci *warnp = warn2; 4924616d0f9Sopenharmony_ci } 4934616d0f9Sopenharmony_ci continue; 4944616d0f9Sopenharmony_ci case 'y': 4954616d0f9Sopenharmony_ci *warnp = IN_ALL; 4964616d0f9Sopenharmony_ci pt = _yconv(t->tm_year, TM_YEAR_BASE, 4974616d0f9Sopenharmony_ci false, true, 4984616d0f9Sopenharmony_ci pt, ptlim); 4994616d0f9Sopenharmony_ci continue; 5004616d0f9Sopenharmony_ci case 'Y': 5014616d0f9Sopenharmony_ci pt = _yconv(t->tm_year, TM_YEAR_BASE, 5024616d0f9Sopenharmony_ci true, true, 5034616d0f9Sopenharmony_ci pt, ptlim); 5044616d0f9Sopenharmony_ci continue; 5054616d0f9Sopenharmony_ci case 'Z': 5064616d0f9Sopenharmony_ci#ifdef TM_ZONE 5074616d0f9Sopenharmony_ci pt = _add(t->TM_ZONE, pt, ptlim); 5084616d0f9Sopenharmony_ci#elif HAVE_TZNAME 5094616d0f9Sopenharmony_ci if (t->tm_isdst >= 0) 5104616d0f9Sopenharmony_ci pt = _add(tzname[t->tm_isdst != 0], 5114616d0f9Sopenharmony_ci pt, ptlim); 5124616d0f9Sopenharmony_ci#endif 5134616d0f9Sopenharmony_ci /* 5144616d0f9Sopenharmony_ci ** C99 and later say that %Z must be 5154616d0f9Sopenharmony_ci ** replaced by the empty string if the 5164616d0f9Sopenharmony_ci ** time zone abbreviation is not 5174616d0f9Sopenharmony_ci ** determinable. 5184616d0f9Sopenharmony_ci */ 5194616d0f9Sopenharmony_ci continue; 5204616d0f9Sopenharmony_ci case 'z': 5214616d0f9Sopenharmony_ci#if defined TM_GMTOFF || USG_COMPAT || ALTZONE 5224616d0f9Sopenharmony_ci { 5234616d0f9Sopenharmony_ci long diff; 5244616d0f9Sopenharmony_ci char const * sign; 5254616d0f9Sopenharmony_ci bool negative; 5264616d0f9Sopenharmony_ci 5274616d0f9Sopenharmony_ci# ifdef TM_GMTOFF 5284616d0f9Sopenharmony_ci diff = t->TM_GMTOFF; 5294616d0f9Sopenharmony_ci# else 5304616d0f9Sopenharmony_ci /* 5314616d0f9Sopenharmony_ci ** C99 and later say that the UT offset must 5324616d0f9Sopenharmony_ci ** be computed by looking only at 5334616d0f9Sopenharmony_ci ** tm_isdst. This requirement is 5344616d0f9Sopenharmony_ci ** incorrect, since it means the code 5354616d0f9Sopenharmony_ci ** must rely on magic (in this case 5364616d0f9Sopenharmony_ci ** altzone and timezone), and the 5374616d0f9Sopenharmony_ci ** magic might not have the correct 5384616d0f9Sopenharmony_ci ** offset. Doing things correctly is 5394616d0f9Sopenharmony_ci ** tricky and requires disobeying the standard; 5404616d0f9Sopenharmony_ci ** see GNU C strftime for details. 5414616d0f9Sopenharmony_ci ** For now, punt and conform to the 5424616d0f9Sopenharmony_ci ** standard, even though it's incorrect. 5434616d0f9Sopenharmony_ci ** 5444616d0f9Sopenharmony_ci ** C99 and later say that %z must be replaced by 5454616d0f9Sopenharmony_ci ** the empty string if the time zone is not 5464616d0f9Sopenharmony_ci ** determinable, so output nothing if the 5474616d0f9Sopenharmony_ci ** appropriate variables are not available. 5484616d0f9Sopenharmony_ci */ 5494616d0f9Sopenharmony_ci if (t->tm_isdst < 0) 5504616d0f9Sopenharmony_ci continue; 5514616d0f9Sopenharmony_ci if (t->tm_isdst == 0) 5524616d0f9Sopenharmony_ci# if USG_COMPAT 5534616d0f9Sopenharmony_ci diff = -timezone; 5544616d0f9Sopenharmony_ci# else 5554616d0f9Sopenharmony_ci continue; 5564616d0f9Sopenharmony_ci# endif 5574616d0f9Sopenharmony_ci else 5584616d0f9Sopenharmony_ci# if ALTZONE 5594616d0f9Sopenharmony_ci diff = -altzone; 5604616d0f9Sopenharmony_ci# else 5614616d0f9Sopenharmony_ci continue; 5624616d0f9Sopenharmony_ci# endif 5634616d0f9Sopenharmony_ci# endif 5644616d0f9Sopenharmony_ci negative = diff < 0; 5654616d0f9Sopenharmony_ci if (diff == 0) { 5664616d0f9Sopenharmony_ci# ifdef TM_ZONE 5674616d0f9Sopenharmony_ci negative = t->TM_ZONE[0] == '-'; 5684616d0f9Sopenharmony_ci# else 5694616d0f9Sopenharmony_ci negative = t->tm_isdst < 0; 5704616d0f9Sopenharmony_ci# if HAVE_TZNAME 5714616d0f9Sopenharmony_ci if (tzname[t->tm_isdst != 0][0] == '-') 5724616d0f9Sopenharmony_ci negative = true; 5734616d0f9Sopenharmony_ci# endif 5744616d0f9Sopenharmony_ci# endif 5754616d0f9Sopenharmony_ci } 5764616d0f9Sopenharmony_ci if (negative) { 5774616d0f9Sopenharmony_ci sign = "-"; 5784616d0f9Sopenharmony_ci diff = -diff; 5794616d0f9Sopenharmony_ci } else sign = "+"; 5804616d0f9Sopenharmony_ci pt = _add(sign, pt, ptlim); 5814616d0f9Sopenharmony_ci diff /= SECSPERMIN; 5824616d0f9Sopenharmony_ci diff = (diff / MINSPERHOUR) * 100 + 5834616d0f9Sopenharmony_ci (diff % MINSPERHOUR); 5844616d0f9Sopenharmony_ci pt = _conv(diff, "%04d", pt, ptlim); 5854616d0f9Sopenharmony_ci } 5864616d0f9Sopenharmony_ci#endif 5874616d0f9Sopenharmony_ci continue; 5884616d0f9Sopenharmony_ci case '+': 5894616d0f9Sopenharmony_ci pt = _fmt(Locale->date_fmt, t, pt, ptlim, 5904616d0f9Sopenharmony_ci warnp); 5914616d0f9Sopenharmony_ci continue; 5924616d0f9Sopenharmony_ci case '%': 5934616d0f9Sopenharmony_ci /* 5944616d0f9Sopenharmony_ci ** X311J/88-090 (4.12.3.5): if conversion char is 5954616d0f9Sopenharmony_ci ** undefined, behavior is undefined. Print out the 5964616d0f9Sopenharmony_ci ** character itself as printf(3) also does. 5974616d0f9Sopenharmony_ci */ 5984616d0f9Sopenharmony_ci default: 5994616d0f9Sopenharmony_ci break; 6004616d0f9Sopenharmony_ci } 6014616d0f9Sopenharmony_ci } 6024616d0f9Sopenharmony_ci if (pt == ptlim) 6034616d0f9Sopenharmony_ci break; 6044616d0f9Sopenharmony_ci *pt++ = *format; 6054616d0f9Sopenharmony_ci } 6064616d0f9Sopenharmony_ci return pt; 6074616d0f9Sopenharmony_ci} 6084616d0f9Sopenharmony_ci 6094616d0f9Sopenharmony_cistatic char * 6104616d0f9Sopenharmony_ci_conv(int n, const char *format, char *pt, const char *ptlim) 6114616d0f9Sopenharmony_ci{ 6124616d0f9Sopenharmony_ci char buf[INT_STRLEN_MAXIMUM(int) + 1]; 6134616d0f9Sopenharmony_ci 6144616d0f9Sopenharmony_ci sprintf(buf, format, n); 6154616d0f9Sopenharmony_ci return _add(buf, pt, ptlim); 6164616d0f9Sopenharmony_ci} 6174616d0f9Sopenharmony_ci 6184616d0f9Sopenharmony_cistatic char * 6194616d0f9Sopenharmony_ci_add(const char *str, char *pt, const char *ptlim) 6204616d0f9Sopenharmony_ci{ 6214616d0f9Sopenharmony_ci while (pt < ptlim && (*pt = *str++) != '\0') 6224616d0f9Sopenharmony_ci ++pt; 6234616d0f9Sopenharmony_ci return pt; 6244616d0f9Sopenharmony_ci} 6254616d0f9Sopenharmony_ci 6264616d0f9Sopenharmony_ci/* 6274616d0f9Sopenharmony_ci** POSIX and the C Standard are unclear or inconsistent about 6284616d0f9Sopenharmony_ci** what %C and %y do if the year is negative or exceeds 9999. 6294616d0f9Sopenharmony_ci** Use the convention that %C concatenated with %y yields the 6304616d0f9Sopenharmony_ci** same output as %Y, and that %Y contains at least 4 bytes, 6314616d0f9Sopenharmony_ci** with more only if necessary. 6324616d0f9Sopenharmony_ci*/ 6334616d0f9Sopenharmony_ci 6344616d0f9Sopenharmony_cistatic char * 6354616d0f9Sopenharmony_ci_yconv(int a, int b, bool convert_top, bool convert_yy, 6364616d0f9Sopenharmony_ci char *pt, const char *ptlim) 6374616d0f9Sopenharmony_ci{ 6384616d0f9Sopenharmony_ci register int lead; 6394616d0f9Sopenharmony_ci register int trail; 6404616d0f9Sopenharmony_ci 6414616d0f9Sopenharmony_ci int DIVISOR = 100; 6424616d0f9Sopenharmony_ci trail = a % DIVISOR + b % DIVISOR; 6434616d0f9Sopenharmony_ci lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR; 6444616d0f9Sopenharmony_ci trail %= DIVISOR; 6454616d0f9Sopenharmony_ci if (trail < 0 && lead > 0) { 6464616d0f9Sopenharmony_ci trail += DIVISOR; 6474616d0f9Sopenharmony_ci --lead; 6484616d0f9Sopenharmony_ci } else if (lead < 0 && trail > 0) { 6494616d0f9Sopenharmony_ci trail -= DIVISOR; 6504616d0f9Sopenharmony_ci ++lead; 6514616d0f9Sopenharmony_ci } 6524616d0f9Sopenharmony_ci if (convert_top) { 6534616d0f9Sopenharmony_ci if (lead == 0 && trail < 0) 6544616d0f9Sopenharmony_ci pt = _add("-0", pt, ptlim); 6554616d0f9Sopenharmony_ci else pt = _conv(lead, "%02d", pt, ptlim); 6564616d0f9Sopenharmony_ci } 6574616d0f9Sopenharmony_ci if (convert_yy) 6584616d0f9Sopenharmony_ci pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim); 6594616d0f9Sopenharmony_ci return pt; 6604616d0f9Sopenharmony_ci} 661