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