xref: /freebsd/contrib/tzcode/strftime.c (revision bc42155199b5b0b479311e05b07aee7f6f9c5172)
1*bc421551SDag-Erling Smørgrav /* Convert a broken-down timestamp to a string.  */
2*bc421551SDag-Erling Smørgrav 
3*bc421551SDag-Erling Smørgrav /* Copyright 1989 The Regents of the University of California.
4*bc421551SDag-Erling Smørgrav    All rights reserved.
5*bc421551SDag-Erling Smørgrav 
6*bc421551SDag-Erling Smørgrav    Redistribution and use in source and binary forms, with or without
7*bc421551SDag-Erling Smørgrav    modification, are permitted provided that the following conditions
8*bc421551SDag-Erling Smørgrav    are met:
9*bc421551SDag-Erling Smørgrav    1. Redistributions of source code must retain the above copyright
10*bc421551SDag-Erling Smørgrav       notice, this list of conditions and the following disclaimer.
11*bc421551SDag-Erling Smørgrav    2. Redistributions in binary form must reproduce the above copyright
12*bc421551SDag-Erling Smørgrav       notice, this list of conditions and the following disclaimer in the
13*bc421551SDag-Erling Smørgrav       documentation and/or other materials provided with the distribution.
14*bc421551SDag-Erling Smørgrav    3. Neither the name of the University nor the names of its contributors
15*bc421551SDag-Erling Smørgrav       may be used to endorse or promote products derived from this software
16*bc421551SDag-Erling Smørgrav       without specific prior written permission.
17*bc421551SDag-Erling Smørgrav 
18*bc421551SDag-Erling Smørgrav    THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
19*bc421551SDag-Erling Smørgrav    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*bc421551SDag-Erling Smørgrav    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*bc421551SDag-Erling Smørgrav    ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22*bc421551SDag-Erling Smørgrav    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*bc421551SDag-Erling Smørgrav    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*bc421551SDag-Erling Smørgrav    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*bc421551SDag-Erling Smørgrav    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*bc421551SDag-Erling Smørgrav    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*bc421551SDag-Erling Smørgrav    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*bc421551SDag-Erling Smørgrav    SUCH DAMAGE.  */
29*bc421551SDag-Erling Smørgrav 
30*bc421551SDag-Erling Smørgrav /*
31*bc421551SDag-Erling Smørgrav ** Based on the UCB version with the copyright notice appearing above.
32*bc421551SDag-Erling Smørgrav **
33*bc421551SDag-Erling Smørgrav ** This is ANSIish only when "multibyte character == plain character".
34*bc421551SDag-Erling Smørgrav */
35*bc421551SDag-Erling Smørgrav 
36*bc421551SDag-Erling Smørgrav #include "private.h"
37*bc421551SDag-Erling Smørgrav 
38*bc421551SDag-Erling Smørgrav #include <fcntl.h>
39*bc421551SDag-Erling Smørgrav #include <locale.h>
40*bc421551SDag-Erling Smørgrav #include <stdio.h>
41*bc421551SDag-Erling Smørgrav 
42*bc421551SDag-Erling Smørgrav #ifndef DEPRECATE_TWO_DIGIT_YEARS
43*bc421551SDag-Erling Smørgrav # define DEPRECATE_TWO_DIGIT_YEARS false
44*bc421551SDag-Erling Smørgrav #endif
45*bc421551SDag-Erling Smørgrav 
46*bc421551SDag-Erling Smørgrav struct lc_time_T {
47*bc421551SDag-Erling Smørgrav 	const char *	mon[MONSPERYEAR];
48*bc421551SDag-Erling Smørgrav 	const char *	month[MONSPERYEAR];
49*bc421551SDag-Erling Smørgrav 	const char *	wday[DAYSPERWEEK];
50*bc421551SDag-Erling Smørgrav 	const char *	weekday[DAYSPERWEEK];
51*bc421551SDag-Erling Smørgrav 	const char *	X_fmt;
52*bc421551SDag-Erling Smørgrav 	const char *	x_fmt;
53*bc421551SDag-Erling Smørgrav 	const char *	c_fmt;
54*bc421551SDag-Erling Smørgrav 	const char *	am;
55*bc421551SDag-Erling Smørgrav 	const char *	pm;
56*bc421551SDag-Erling Smørgrav 	const char *	date_fmt;
57*bc421551SDag-Erling Smørgrav };
58*bc421551SDag-Erling Smørgrav 
59*bc421551SDag-Erling Smørgrav static const struct lc_time_T	C_time_locale = {
60*bc421551SDag-Erling Smørgrav 	{
61*bc421551SDag-Erling Smørgrav 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
62*bc421551SDag-Erling Smørgrav 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
63*bc421551SDag-Erling Smørgrav 	}, {
64*bc421551SDag-Erling Smørgrav 		"January", "February", "March", "April", "May", "June",
65*bc421551SDag-Erling Smørgrav 		"July", "August", "September", "October", "November", "December"
66*bc421551SDag-Erling Smørgrav 	}, {
67*bc421551SDag-Erling Smørgrav 		"Sun", "Mon", "Tue", "Wed",
68*bc421551SDag-Erling Smørgrav 		"Thu", "Fri", "Sat"
69*bc421551SDag-Erling Smørgrav 	}, {
70*bc421551SDag-Erling Smørgrav 		"Sunday", "Monday", "Tuesday", "Wednesday",
71*bc421551SDag-Erling Smørgrav 		"Thursday", "Friday", "Saturday"
72*bc421551SDag-Erling Smørgrav 	},
73*bc421551SDag-Erling Smørgrav 
74*bc421551SDag-Erling Smørgrav 	/* X_fmt */
75*bc421551SDag-Erling Smørgrav 	"%H:%M:%S",
76*bc421551SDag-Erling Smørgrav 
77*bc421551SDag-Erling Smørgrav 	/*
78*bc421551SDag-Erling Smørgrav 	** x_fmt
79*bc421551SDag-Erling Smørgrav 	** C99 and later require this format.
80*bc421551SDag-Erling Smørgrav 	** Using just numbers (as here) makes Quakers happier;
81*bc421551SDag-Erling Smørgrav 	** it's also compatible with SVR4.
82*bc421551SDag-Erling Smørgrav 	*/
83*bc421551SDag-Erling Smørgrav 	"%m/%d/%y",
84*bc421551SDag-Erling Smørgrav 
85*bc421551SDag-Erling Smørgrav 	/*
86*bc421551SDag-Erling Smørgrav 	** c_fmt
87*bc421551SDag-Erling Smørgrav 	** C99 and later require this format.
88*bc421551SDag-Erling Smørgrav 	** Previously this code used "%D %X", but we now conform to C99.
89*bc421551SDag-Erling Smørgrav 	** Note that
90*bc421551SDag-Erling Smørgrav 	**	"%a %b %d %H:%M:%S %Y"
91*bc421551SDag-Erling Smørgrav 	** is used by Solaris 2.3.
92*bc421551SDag-Erling Smørgrav 	*/
93*bc421551SDag-Erling Smørgrav 	"%a %b %e %T %Y",
94*bc421551SDag-Erling Smørgrav 
95*bc421551SDag-Erling Smørgrav 	/* am */
96*bc421551SDag-Erling Smørgrav 	"AM",
97*bc421551SDag-Erling Smørgrav 
98*bc421551SDag-Erling Smørgrav 	/* pm */
99*bc421551SDag-Erling Smørgrav 	"PM",
100*bc421551SDag-Erling Smørgrav 
101*bc421551SDag-Erling Smørgrav 	/* date_fmt */
102*bc421551SDag-Erling Smørgrav 	"%a %b %e %H:%M:%S %Z %Y"
103*bc421551SDag-Erling Smørgrav };
104*bc421551SDag-Erling Smørgrav 
105*bc421551SDag-Erling Smørgrav enum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL };
106*bc421551SDag-Erling Smørgrav 
107*bc421551SDag-Erling Smørgrav static char *	_add(const char *, char *, const char *);
108*bc421551SDag-Erling Smørgrav static char *	_conv(int, const char *, char *, const char *);
109*bc421551SDag-Erling Smørgrav static char *	_fmt(const char *, const struct tm *, char *, const char *,
110*bc421551SDag-Erling Smørgrav 		     enum warn *);
111*bc421551SDag-Erling Smørgrav static char *	_yconv(int, int, bool, bool, char *, char const *);
112*bc421551SDag-Erling Smørgrav 
113*bc421551SDag-Erling Smørgrav #ifndef YEAR_2000_NAME
114*bc421551SDag-Erling Smørgrav # define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
115*bc421551SDag-Erling Smørgrav #endif /* !defined YEAR_2000_NAME */
116*bc421551SDag-Erling Smørgrav 
117*bc421551SDag-Erling Smørgrav #if HAVE_STRFTIME_L
118*bc421551SDag-Erling Smørgrav size_t
119*bc421551SDag-Erling Smørgrav strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
120*bc421551SDag-Erling Smørgrav 	   ATTRIBUTE_MAYBE_UNUSED locale_t locale)
121*bc421551SDag-Erling Smørgrav {
122*bc421551SDag-Erling Smørgrav   /* Just call strftime, as only the C locale is supported.  */
123*bc421551SDag-Erling Smørgrav   return strftime(s, maxsize, format, t);
124*bc421551SDag-Erling Smørgrav }
125*bc421551SDag-Erling Smørgrav #endif
126*bc421551SDag-Erling Smørgrav 
127*bc421551SDag-Erling Smørgrav size_t
128*bc421551SDag-Erling Smørgrav strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
129*bc421551SDag-Erling Smørgrav {
130*bc421551SDag-Erling Smørgrav 	char *	p;
131*bc421551SDag-Erling Smørgrav 	int saved_errno = errno;
132*bc421551SDag-Erling Smørgrav 	enum warn warn = IN_NONE;
133*bc421551SDag-Erling Smørgrav 
134*bc421551SDag-Erling Smørgrav 	tzset();
135*bc421551SDag-Erling Smørgrav 	p = _fmt(format, t, s, s + maxsize, &warn);
136*bc421551SDag-Erling Smørgrav 	if (!p) {
137*bc421551SDag-Erling Smørgrav 	  errno = EOVERFLOW;
138*bc421551SDag-Erling Smørgrav 	  return 0;
139*bc421551SDag-Erling Smørgrav 	}
140*bc421551SDag-Erling Smørgrav 	if (DEPRECATE_TWO_DIGIT_YEARS
141*bc421551SDag-Erling Smørgrav 	    && warn != IN_NONE && getenv(YEAR_2000_NAME)) {
142*bc421551SDag-Erling Smørgrav 		fprintf(stderr, "\n");
143*bc421551SDag-Erling Smørgrav 		fprintf(stderr, "strftime format \"%s\" ", format);
144*bc421551SDag-Erling Smørgrav 		fprintf(stderr, "yields only two digits of years in ");
145*bc421551SDag-Erling Smørgrav 		if (warn == IN_SOME)
146*bc421551SDag-Erling Smørgrav 			fprintf(stderr, "some locales");
147*bc421551SDag-Erling Smørgrav 		else if (warn == IN_THIS)
148*bc421551SDag-Erling Smørgrav 			fprintf(stderr, "the current locale");
149*bc421551SDag-Erling Smørgrav 		else	fprintf(stderr, "all locales");
150*bc421551SDag-Erling Smørgrav 		fprintf(stderr, "\n");
151*bc421551SDag-Erling Smørgrav 	}
152*bc421551SDag-Erling Smørgrav 	if (p == s + maxsize) {
153*bc421551SDag-Erling Smørgrav 		errno = ERANGE;
154*bc421551SDag-Erling Smørgrav 		return 0;
155*bc421551SDag-Erling Smørgrav 	}
156*bc421551SDag-Erling Smørgrav 	*p = '\0';
157*bc421551SDag-Erling Smørgrav 	errno = saved_errno;
158*bc421551SDag-Erling Smørgrav 	return p - s;
159*bc421551SDag-Erling Smørgrav }
160*bc421551SDag-Erling Smørgrav 
161*bc421551SDag-Erling Smørgrav static char *
162*bc421551SDag-Erling Smørgrav _fmt(const char *format, const struct tm *t, char *pt,
163*bc421551SDag-Erling Smørgrav      const char *ptlim, enum warn *warnp)
164*bc421551SDag-Erling Smørgrav {
165*bc421551SDag-Erling Smørgrav 	struct lc_time_T const *Locale = &C_time_locale;
166*bc421551SDag-Erling Smørgrav 
167*bc421551SDag-Erling Smørgrav 	for ( ; *format; ++format) {
168*bc421551SDag-Erling Smørgrav 		if (*format == '%') {
169*bc421551SDag-Erling Smørgrav label:
170*bc421551SDag-Erling Smørgrav 			switch (*++format) {
171*bc421551SDag-Erling Smørgrav 			case '\0':
172*bc421551SDag-Erling Smørgrav 				--format;
173*bc421551SDag-Erling Smørgrav 				break;
174*bc421551SDag-Erling Smørgrav 			case 'A':
175*bc421551SDag-Erling Smørgrav 				pt = _add((t->tm_wday < 0 ||
176*bc421551SDag-Erling Smørgrav 					t->tm_wday >= DAYSPERWEEK) ?
177*bc421551SDag-Erling Smørgrav 					"?" : Locale->weekday[t->tm_wday],
178*bc421551SDag-Erling Smørgrav 					pt, ptlim);
179*bc421551SDag-Erling Smørgrav 				continue;
180*bc421551SDag-Erling Smørgrav 			case 'a':
181*bc421551SDag-Erling Smørgrav 				pt = _add((t->tm_wday < 0 ||
182*bc421551SDag-Erling Smørgrav 					t->tm_wday >= DAYSPERWEEK) ?
183*bc421551SDag-Erling Smørgrav 					"?" : Locale->wday[t->tm_wday],
184*bc421551SDag-Erling Smørgrav 					pt, ptlim);
185*bc421551SDag-Erling Smørgrav 				continue;
186*bc421551SDag-Erling Smørgrav 			case 'B':
187*bc421551SDag-Erling Smørgrav 				pt = _add((t->tm_mon < 0 ||
188*bc421551SDag-Erling Smørgrav 					t->tm_mon >= MONSPERYEAR) ?
189*bc421551SDag-Erling Smørgrav 					"?" : Locale->month[t->tm_mon],
190*bc421551SDag-Erling Smørgrav 					pt, ptlim);
191*bc421551SDag-Erling Smørgrav 				continue;
192*bc421551SDag-Erling Smørgrav 			case 'b':
193*bc421551SDag-Erling Smørgrav 			case 'h':
194*bc421551SDag-Erling Smørgrav 				pt = _add((t->tm_mon < 0 ||
195*bc421551SDag-Erling Smørgrav 					t->tm_mon >= MONSPERYEAR) ?
196*bc421551SDag-Erling Smørgrav 					"?" : Locale->mon[t->tm_mon],
197*bc421551SDag-Erling Smørgrav 					pt, ptlim);
198*bc421551SDag-Erling Smørgrav 				continue;
199*bc421551SDag-Erling Smørgrav 			case 'C':
200*bc421551SDag-Erling Smørgrav 				/*
201*bc421551SDag-Erling Smørgrav 				** %C used to do a...
202*bc421551SDag-Erling Smørgrav 				**	_fmt("%a %b %e %X %Y", t);
203*bc421551SDag-Erling Smørgrav 				** ...whereas now POSIX 1003.2 calls for
204*bc421551SDag-Erling Smørgrav 				** something completely different.
205*bc421551SDag-Erling Smørgrav 				** (ado, 1993-05-24)
206*bc421551SDag-Erling Smørgrav 				*/
207*bc421551SDag-Erling Smørgrav 				pt = _yconv(t->tm_year, TM_YEAR_BASE,
208*bc421551SDag-Erling Smørgrav 					    true, false, pt, ptlim);
209*bc421551SDag-Erling Smørgrav 				continue;
210*bc421551SDag-Erling Smørgrav 			case 'c':
211*bc421551SDag-Erling Smørgrav 				{
212*bc421551SDag-Erling Smørgrav 				enum warn warn2 = IN_SOME;
213*bc421551SDag-Erling Smørgrav 
214*bc421551SDag-Erling Smørgrav 				pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
215*bc421551SDag-Erling Smørgrav 				if (warn2 == IN_ALL)
216*bc421551SDag-Erling Smørgrav 					warn2 = IN_THIS;
217*bc421551SDag-Erling Smørgrav 				if (warn2 > *warnp)
218*bc421551SDag-Erling Smørgrav 					*warnp = warn2;
219*bc421551SDag-Erling Smørgrav 				}
220*bc421551SDag-Erling Smørgrav 				continue;
221*bc421551SDag-Erling Smørgrav 			case 'D':
222*bc421551SDag-Erling Smørgrav 				pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
223*bc421551SDag-Erling Smørgrav 				continue;
224*bc421551SDag-Erling Smørgrav 			case 'd':
225*bc421551SDag-Erling Smørgrav 				pt = _conv(t->tm_mday, "%02d", pt, ptlim);
226*bc421551SDag-Erling Smørgrav 				continue;
227*bc421551SDag-Erling Smørgrav 			case 'E':
228*bc421551SDag-Erling Smørgrav 			case 'O':
229*bc421551SDag-Erling Smørgrav 				/*
230*bc421551SDag-Erling Smørgrav 				** Locale modifiers of C99 and later.
231*bc421551SDag-Erling Smørgrav 				** The sequences
232*bc421551SDag-Erling Smørgrav 				**	%Ec %EC %Ex %EX %Ey %EY
233*bc421551SDag-Erling Smørgrav 				**	%Od %oe %OH %OI %Om %OM
234*bc421551SDag-Erling Smørgrav 				**	%OS %Ou %OU %OV %Ow %OW %Oy
235*bc421551SDag-Erling Smørgrav 				** are supposed to provide alternative
236*bc421551SDag-Erling Smørgrav 				** representations.
237*bc421551SDag-Erling Smørgrav 				*/
238*bc421551SDag-Erling Smørgrav 				goto label;
239*bc421551SDag-Erling Smørgrav 			case 'e':
240*bc421551SDag-Erling Smørgrav 				pt = _conv(t->tm_mday, "%2d", pt, ptlim);
241*bc421551SDag-Erling Smørgrav 				continue;
242*bc421551SDag-Erling Smørgrav 			case 'F':
243*bc421551SDag-Erling Smørgrav 				pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
244*bc421551SDag-Erling Smørgrav 				continue;
245*bc421551SDag-Erling Smørgrav 			case 'H':
246*bc421551SDag-Erling Smørgrav 				pt = _conv(t->tm_hour, "%02d", pt, ptlim);
247*bc421551SDag-Erling Smørgrav 				continue;
248*bc421551SDag-Erling Smørgrav 			case 'I':
249*bc421551SDag-Erling Smørgrav 				pt = _conv((t->tm_hour % 12) ?
250*bc421551SDag-Erling Smørgrav 					(t->tm_hour % 12) : 12,
251*bc421551SDag-Erling Smørgrav 					"%02d", pt, ptlim);
252*bc421551SDag-Erling Smørgrav 				continue;
253*bc421551SDag-Erling Smørgrav 			case 'j':
254*bc421551SDag-Erling Smørgrav 				pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
255*bc421551SDag-Erling Smørgrav 				continue;
256*bc421551SDag-Erling Smørgrav 			case 'k':
257*bc421551SDag-Erling Smørgrav 				/*
258*bc421551SDag-Erling Smørgrav 				** This used to be...
259*bc421551SDag-Erling Smørgrav 				**	_conv(t->tm_hour % 12 ?
260*bc421551SDag-Erling Smørgrav 				**		t->tm_hour % 12 : 12, 2, ' ');
261*bc421551SDag-Erling Smørgrav 				** ...and has been changed to the below to
262*bc421551SDag-Erling Smørgrav 				** match SunOS 4.1.1 and Arnold Robbins'
263*bc421551SDag-Erling Smørgrav 				** strftime version 3.0. That is, "%k" and
264*bc421551SDag-Erling Smørgrav 				** "%l" have been swapped.
265*bc421551SDag-Erling Smørgrav 				** (ado, 1993-05-24)
266*bc421551SDag-Erling Smørgrav 				*/
267*bc421551SDag-Erling Smørgrav 				pt = _conv(t->tm_hour, "%2d", pt, ptlim);
268*bc421551SDag-Erling Smørgrav 				continue;
269*bc421551SDag-Erling Smørgrav #ifdef KITCHEN_SINK
270*bc421551SDag-Erling Smørgrav 			case 'K':
271*bc421551SDag-Erling Smørgrav 				/*
272*bc421551SDag-Erling Smørgrav 				** After all this time, still unclaimed!
273*bc421551SDag-Erling Smørgrav 				*/
274*bc421551SDag-Erling Smørgrav 				pt = _add("kitchen sink", pt, ptlim);
275*bc421551SDag-Erling Smørgrav 				continue;
276*bc421551SDag-Erling Smørgrav #endif /* defined KITCHEN_SINK */
277*bc421551SDag-Erling Smørgrav 			case 'l':
278*bc421551SDag-Erling Smørgrav 				/*
279*bc421551SDag-Erling Smørgrav 				** This used to be...
280*bc421551SDag-Erling Smørgrav 				**	_conv(t->tm_hour, 2, ' ');
281*bc421551SDag-Erling Smørgrav 				** ...and has been changed to the below to
282*bc421551SDag-Erling Smørgrav 				** match SunOS 4.1.1 and Arnold Robbin's
283*bc421551SDag-Erling Smørgrav 				** strftime version 3.0. That is, "%k" and
284*bc421551SDag-Erling Smørgrav 				** "%l" have been swapped.
285*bc421551SDag-Erling Smørgrav 				** (ado, 1993-05-24)
286*bc421551SDag-Erling Smørgrav 				*/
287*bc421551SDag-Erling Smørgrav 				pt = _conv((t->tm_hour % 12) ?
288*bc421551SDag-Erling Smørgrav 					(t->tm_hour % 12) : 12,
289*bc421551SDag-Erling Smørgrav 					"%2d", pt, ptlim);
290*bc421551SDag-Erling Smørgrav 				continue;
291*bc421551SDag-Erling Smørgrav 			case 'M':
292*bc421551SDag-Erling Smørgrav 				pt = _conv(t->tm_min, "%02d", pt, ptlim);
293*bc421551SDag-Erling Smørgrav 				continue;
294*bc421551SDag-Erling Smørgrav 			case 'm':
295*bc421551SDag-Erling Smørgrav 				pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
296*bc421551SDag-Erling Smørgrav 				continue;
297*bc421551SDag-Erling Smørgrav 			case 'n':
298*bc421551SDag-Erling Smørgrav 				pt = _add("\n", pt, ptlim);
299*bc421551SDag-Erling Smørgrav 				continue;
300*bc421551SDag-Erling Smørgrav 			case 'p':
301*bc421551SDag-Erling Smørgrav 				pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
302*bc421551SDag-Erling Smørgrav 					Locale->pm :
303*bc421551SDag-Erling Smørgrav 					Locale->am,
304*bc421551SDag-Erling Smørgrav 					pt, ptlim);
305*bc421551SDag-Erling Smørgrav 				continue;
306*bc421551SDag-Erling Smørgrav 			case 'R':
307*bc421551SDag-Erling Smørgrav 				pt = _fmt("%H:%M", t, pt, ptlim, warnp);
308*bc421551SDag-Erling Smørgrav 				continue;
309*bc421551SDag-Erling Smørgrav 			case 'r':
310*bc421551SDag-Erling Smørgrav 				pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
311*bc421551SDag-Erling Smørgrav 				continue;
312*bc421551SDag-Erling Smørgrav 			case 'S':
313*bc421551SDag-Erling Smørgrav 				pt = _conv(t->tm_sec, "%02d", pt, ptlim);
314*bc421551SDag-Erling Smørgrav 				continue;
315*bc421551SDag-Erling Smørgrav 			case 's':
316*bc421551SDag-Erling Smørgrav 				{
317*bc421551SDag-Erling Smørgrav 					struct tm	tm;
318*bc421551SDag-Erling Smørgrav 					char		buf[INT_STRLEN_MAXIMUM(
319*bc421551SDag-Erling Smørgrav 								time_t) + 1];
320*bc421551SDag-Erling Smørgrav 					time_t		mkt;
321*bc421551SDag-Erling Smørgrav 
322*bc421551SDag-Erling Smørgrav 					tm.tm_sec = t->tm_sec;
323*bc421551SDag-Erling Smørgrav 					tm.tm_min = t->tm_min;
324*bc421551SDag-Erling Smørgrav 					tm.tm_hour = t->tm_hour;
325*bc421551SDag-Erling Smørgrav 					tm.tm_mday = t->tm_mday;
326*bc421551SDag-Erling Smørgrav 					tm.tm_mon = t->tm_mon;
327*bc421551SDag-Erling Smørgrav 					tm.tm_year = t->tm_year;
328*bc421551SDag-Erling Smørgrav 					tm.tm_isdst = t->tm_isdst;
329*bc421551SDag-Erling Smørgrav #if defined TM_GMTOFF && ! UNINIT_TRAP
330*bc421551SDag-Erling Smørgrav 					tm.TM_GMTOFF = t->TM_GMTOFF;
331*bc421551SDag-Erling Smørgrav #endif
332*bc421551SDag-Erling Smørgrav 					mkt = mktime(&tm);
333*bc421551SDag-Erling Smørgrav 					/* If mktime fails, %s expands to the
334*bc421551SDag-Erling Smørgrav 					   value of (time_t) -1 as a failure
335*bc421551SDag-Erling Smørgrav 					   marker; this is better in practice
336*bc421551SDag-Erling Smørgrav 					   than strftime failing.  */
337*bc421551SDag-Erling Smørgrav 					if (TYPE_SIGNED(time_t)) {
338*bc421551SDag-Erling Smørgrav 					  intmax_t n = mkt;
339*bc421551SDag-Erling Smørgrav 					  sprintf(buf, "%"PRIdMAX, n);
340*bc421551SDag-Erling Smørgrav 					} else {
341*bc421551SDag-Erling Smørgrav 					  uintmax_t n = mkt;
342*bc421551SDag-Erling Smørgrav 					  sprintf(buf, "%"PRIuMAX, n);
343*bc421551SDag-Erling Smørgrav 					}
344*bc421551SDag-Erling Smørgrav 					pt = _add(buf, pt, ptlim);
345*bc421551SDag-Erling Smørgrav 				}
346*bc421551SDag-Erling Smørgrav 				continue;
347*bc421551SDag-Erling Smørgrav 			case 'T':
348*bc421551SDag-Erling Smørgrav 				pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
349*bc421551SDag-Erling Smørgrav 				continue;
350*bc421551SDag-Erling Smørgrav 			case 't':
351*bc421551SDag-Erling Smørgrav 				pt = _add("\t", pt, ptlim);
352*bc421551SDag-Erling Smørgrav 				continue;
353*bc421551SDag-Erling Smørgrav 			case 'U':
354*bc421551SDag-Erling Smørgrav 				pt = _conv((t->tm_yday + DAYSPERWEEK -
355*bc421551SDag-Erling Smørgrav 					t->tm_wday) / DAYSPERWEEK,
356*bc421551SDag-Erling Smørgrav 					"%02d", pt, ptlim);
357*bc421551SDag-Erling Smørgrav 				continue;
358*bc421551SDag-Erling Smørgrav 			case 'u':
359*bc421551SDag-Erling Smørgrav 				/*
360*bc421551SDag-Erling Smørgrav 				** From Arnold Robbins' strftime version 3.0:
361*bc421551SDag-Erling Smørgrav 				** "ISO 8601: Weekday as a decimal number
362*bc421551SDag-Erling Smørgrav 				** [1 (Monday) - 7]"
363*bc421551SDag-Erling Smørgrav 				** (ado, 1993-05-24)
364*bc421551SDag-Erling Smørgrav 				*/
365*bc421551SDag-Erling Smørgrav 				pt = _conv((t->tm_wday == 0) ?
366*bc421551SDag-Erling Smørgrav 					DAYSPERWEEK : t->tm_wday,
367*bc421551SDag-Erling Smørgrav 					"%d", pt, ptlim);
368*bc421551SDag-Erling Smørgrav 				continue;
369*bc421551SDag-Erling Smørgrav 			case 'V':	/* ISO 8601 week number */
370*bc421551SDag-Erling Smørgrav 			case 'G':	/* ISO 8601 year (four digits) */
371*bc421551SDag-Erling Smørgrav 			case 'g':	/* ISO 8601 year (two digits) */
372*bc421551SDag-Erling Smørgrav /*
373*bc421551SDag-Erling Smørgrav ** From Arnold Robbins' strftime version 3.0: "the week number of the
374*bc421551SDag-Erling Smørgrav ** year (the first Monday as the first day of week 1) as a decimal number
375*bc421551SDag-Erling Smørgrav ** (01-53)."
376*bc421551SDag-Erling Smørgrav ** (ado, 1993-05-24)
377*bc421551SDag-Erling Smørgrav **
378*bc421551SDag-Erling Smørgrav ** From <https://www.cl.cam.ac.uk/~mgk25/iso-time.html> by Markus Kuhn:
379*bc421551SDag-Erling Smørgrav ** "Week 01 of a year is per definition the first week which has the
380*bc421551SDag-Erling Smørgrav ** Thursday in this year, which is equivalent to the week which contains
381*bc421551SDag-Erling Smørgrav ** the fourth day of January. In other words, the first week of a new year
382*bc421551SDag-Erling Smørgrav ** is the week which has the majority of its days in the new year. Week 01
383*bc421551SDag-Erling Smørgrav ** might also contain days from the previous year and the week before week
384*bc421551SDag-Erling Smørgrav ** 01 of a year is the last week (52 or 53) of the previous year even if
385*bc421551SDag-Erling Smørgrav ** it contains days from the new year. A week starts with Monday (day 1)
386*bc421551SDag-Erling Smørgrav ** and ends with Sunday (day 7). For example, the first week of the year
387*bc421551SDag-Erling Smørgrav ** 1997 lasts from 1996-12-30 to 1997-01-05..."
388*bc421551SDag-Erling Smørgrav ** (ado, 1996-01-02)
389*bc421551SDag-Erling Smørgrav */
390*bc421551SDag-Erling Smørgrav 				{
391*bc421551SDag-Erling Smørgrav 					int	year;
392*bc421551SDag-Erling Smørgrav 					int	base;
393*bc421551SDag-Erling Smørgrav 					int	yday;
394*bc421551SDag-Erling Smørgrav 					int	wday;
395*bc421551SDag-Erling Smørgrav 					int	w;
396*bc421551SDag-Erling Smørgrav 
397*bc421551SDag-Erling Smørgrav 					year = t->tm_year;
398*bc421551SDag-Erling Smørgrav 					base = TM_YEAR_BASE;
399*bc421551SDag-Erling Smørgrav 					yday = t->tm_yday;
400*bc421551SDag-Erling Smørgrav 					wday = t->tm_wday;
401*bc421551SDag-Erling Smørgrav 					for ( ; ; ) {
402*bc421551SDag-Erling Smørgrav 						int	len;
403*bc421551SDag-Erling Smørgrav 						int	bot;
404*bc421551SDag-Erling Smørgrav 						int	top;
405*bc421551SDag-Erling Smørgrav 
406*bc421551SDag-Erling Smørgrav 						len = isleap_sum(year, base) ?
407*bc421551SDag-Erling Smørgrav 							DAYSPERLYEAR :
408*bc421551SDag-Erling Smørgrav 							DAYSPERNYEAR;
409*bc421551SDag-Erling Smørgrav 						/*
410*bc421551SDag-Erling Smørgrav 						** What yday (-3 ... 3) does
411*bc421551SDag-Erling Smørgrav 						** the ISO year begin on?
412*bc421551SDag-Erling Smørgrav 						*/
413*bc421551SDag-Erling Smørgrav 						bot = ((yday + 11 - wday) %
414*bc421551SDag-Erling Smørgrav 							DAYSPERWEEK) - 3;
415*bc421551SDag-Erling Smørgrav 						/*
416*bc421551SDag-Erling Smørgrav 						** What yday does the NEXT
417*bc421551SDag-Erling Smørgrav 						** ISO year begin on?
418*bc421551SDag-Erling Smørgrav 						*/
419*bc421551SDag-Erling Smørgrav 						top = bot -
420*bc421551SDag-Erling Smørgrav 							(len % DAYSPERWEEK);
421*bc421551SDag-Erling Smørgrav 						if (top < -3)
422*bc421551SDag-Erling Smørgrav 							top += DAYSPERWEEK;
423*bc421551SDag-Erling Smørgrav 						top += len;
424*bc421551SDag-Erling Smørgrav 						if (yday >= top) {
425*bc421551SDag-Erling Smørgrav 							++base;
426*bc421551SDag-Erling Smørgrav 							w = 1;
427*bc421551SDag-Erling Smørgrav 							break;
428*bc421551SDag-Erling Smørgrav 						}
429*bc421551SDag-Erling Smørgrav 						if (yday >= bot) {
430*bc421551SDag-Erling Smørgrav 							w = 1 + ((yday - bot) /
431*bc421551SDag-Erling Smørgrav 								DAYSPERWEEK);
432*bc421551SDag-Erling Smørgrav 							break;
433*bc421551SDag-Erling Smørgrav 						}
434*bc421551SDag-Erling Smørgrav 						--base;
435*bc421551SDag-Erling Smørgrav 						yday += isleap_sum(year, base) ?
436*bc421551SDag-Erling Smørgrav 							DAYSPERLYEAR :
437*bc421551SDag-Erling Smørgrav 							DAYSPERNYEAR;
438*bc421551SDag-Erling Smørgrav 					}
439*bc421551SDag-Erling Smørgrav #ifdef XPG4_1994_04_09
440*bc421551SDag-Erling Smørgrav 					if ((w == 52 &&
441*bc421551SDag-Erling Smørgrav 						t->tm_mon == TM_JANUARY) ||
442*bc421551SDag-Erling Smørgrav 						(w == 1 &&
443*bc421551SDag-Erling Smørgrav 						t->tm_mon == TM_DECEMBER))
444*bc421551SDag-Erling Smørgrav 							w = 53;
445*bc421551SDag-Erling Smørgrav #endif /* defined XPG4_1994_04_09 */
446*bc421551SDag-Erling Smørgrav 					if (*format == 'V')
447*bc421551SDag-Erling Smørgrav 						pt = _conv(w, "%02d",
448*bc421551SDag-Erling Smørgrav 							pt, ptlim);
449*bc421551SDag-Erling Smørgrav 					else if (*format == 'g') {
450*bc421551SDag-Erling Smørgrav 						*warnp = IN_ALL;
451*bc421551SDag-Erling Smørgrav 						pt = _yconv(year, base,
452*bc421551SDag-Erling Smørgrav 							false, true,
453*bc421551SDag-Erling Smørgrav 							pt, ptlim);
454*bc421551SDag-Erling Smørgrav 					} else	pt = _yconv(year, base,
455*bc421551SDag-Erling Smørgrav 							true, true,
456*bc421551SDag-Erling Smørgrav 							pt, ptlim);
457*bc421551SDag-Erling Smørgrav 				}
458*bc421551SDag-Erling Smørgrav 				continue;
459*bc421551SDag-Erling Smørgrav 			case 'v':
460*bc421551SDag-Erling Smørgrav 				/*
461*bc421551SDag-Erling Smørgrav 				** From Arnold Robbins' strftime version 3.0:
462*bc421551SDag-Erling Smørgrav 				** "date as dd-bbb-YYYY"
463*bc421551SDag-Erling Smørgrav 				** (ado, 1993-05-24)
464*bc421551SDag-Erling Smørgrav 				*/
465*bc421551SDag-Erling Smørgrav 				pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
466*bc421551SDag-Erling Smørgrav 				continue;
467*bc421551SDag-Erling Smørgrav 			case 'W':
468*bc421551SDag-Erling Smørgrav 				pt = _conv((t->tm_yday + DAYSPERWEEK -
469*bc421551SDag-Erling Smørgrav 					(t->tm_wday ?
470*bc421551SDag-Erling Smørgrav 					(t->tm_wday - 1) :
471*bc421551SDag-Erling Smørgrav 					(DAYSPERWEEK - 1))) / DAYSPERWEEK,
472*bc421551SDag-Erling Smørgrav 					"%02d", pt, ptlim);
473*bc421551SDag-Erling Smørgrav 				continue;
474*bc421551SDag-Erling Smørgrav 			case 'w':
475*bc421551SDag-Erling Smørgrav 				pt = _conv(t->tm_wday, "%d", pt, ptlim);
476*bc421551SDag-Erling Smørgrav 				continue;
477*bc421551SDag-Erling Smørgrav 			case 'X':
478*bc421551SDag-Erling Smørgrav 				pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
479*bc421551SDag-Erling Smørgrav 				continue;
480*bc421551SDag-Erling Smørgrav 			case 'x':
481*bc421551SDag-Erling Smørgrav 				{
482*bc421551SDag-Erling Smørgrav 				enum warn warn2 = IN_SOME;
483*bc421551SDag-Erling Smørgrav 
484*bc421551SDag-Erling Smørgrav 				pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
485*bc421551SDag-Erling Smørgrav 				if (warn2 == IN_ALL)
486*bc421551SDag-Erling Smørgrav 					warn2 = IN_THIS;
487*bc421551SDag-Erling Smørgrav 				if (warn2 > *warnp)
488*bc421551SDag-Erling Smørgrav 					*warnp = warn2;
489*bc421551SDag-Erling Smørgrav 				}
490*bc421551SDag-Erling Smørgrav 				continue;
491*bc421551SDag-Erling Smørgrav 			case 'y':
492*bc421551SDag-Erling Smørgrav 				*warnp = IN_ALL;
493*bc421551SDag-Erling Smørgrav 				pt = _yconv(t->tm_year, TM_YEAR_BASE,
494*bc421551SDag-Erling Smørgrav 					false, true,
495*bc421551SDag-Erling Smørgrav 					pt, ptlim);
496*bc421551SDag-Erling Smørgrav 				continue;
497*bc421551SDag-Erling Smørgrav 			case 'Y':
498*bc421551SDag-Erling Smørgrav 				pt = _yconv(t->tm_year, TM_YEAR_BASE,
499*bc421551SDag-Erling Smørgrav 					true, true,
500*bc421551SDag-Erling Smørgrav 					pt, ptlim);
501*bc421551SDag-Erling Smørgrav 				continue;
502*bc421551SDag-Erling Smørgrav 			case 'Z':
503*bc421551SDag-Erling Smørgrav #ifdef TM_ZONE
504*bc421551SDag-Erling Smørgrav 				pt = _add(t->TM_ZONE, pt, ptlim);
505*bc421551SDag-Erling Smørgrav #elif HAVE_TZNAME
506*bc421551SDag-Erling Smørgrav 				if (t->tm_isdst >= 0)
507*bc421551SDag-Erling Smørgrav 					pt = _add(tzname[t->tm_isdst != 0],
508*bc421551SDag-Erling Smørgrav 						pt, ptlim);
509*bc421551SDag-Erling Smørgrav #endif
510*bc421551SDag-Erling Smørgrav 				/*
511*bc421551SDag-Erling Smørgrav 				** C99 and later say that %Z must be
512*bc421551SDag-Erling Smørgrav 				** replaced by the empty string if the
513*bc421551SDag-Erling Smørgrav 				** time zone abbreviation is not
514*bc421551SDag-Erling Smørgrav 				** determinable.
515*bc421551SDag-Erling Smørgrav 				*/
516*bc421551SDag-Erling Smørgrav 				continue;
517*bc421551SDag-Erling Smørgrav 			case 'z':
518*bc421551SDag-Erling Smørgrav #if defined TM_GMTOFF || USG_COMPAT || ALTZONE
519*bc421551SDag-Erling Smørgrav 				{
520*bc421551SDag-Erling Smørgrav 				long		diff;
521*bc421551SDag-Erling Smørgrav 				char const *	sign;
522*bc421551SDag-Erling Smørgrav 				bool negative;
523*bc421551SDag-Erling Smørgrav 
524*bc421551SDag-Erling Smørgrav # ifdef TM_GMTOFF
525*bc421551SDag-Erling Smørgrav 				diff = t->TM_GMTOFF;
526*bc421551SDag-Erling Smørgrav # else
527*bc421551SDag-Erling Smørgrav 				/*
528*bc421551SDag-Erling Smørgrav 				** C99 and later say that the UT offset must
529*bc421551SDag-Erling Smørgrav 				** be computed by looking only at
530*bc421551SDag-Erling Smørgrav 				** tm_isdst. This requirement is
531*bc421551SDag-Erling Smørgrav 				** incorrect, since it means the code
532*bc421551SDag-Erling Smørgrav 				** must rely on magic (in this case
533*bc421551SDag-Erling Smørgrav 				** altzone and timezone), and the
534*bc421551SDag-Erling Smørgrav 				** magic might not have the correct
535*bc421551SDag-Erling Smørgrav 				** offset. Doing things correctly is
536*bc421551SDag-Erling Smørgrav 				** tricky and requires disobeying the standard;
537*bc421551SDag-Erling Smørgrav 				** see GNU C strftime for details.
538*bc421551SDag-Erling Smørgrav 				** For now, punt and conform to the
539*bc421551SDag-Erling Smørgrav 				** standard, even though it's incorrect.
540*bc421551SDag-Erling Smørgrav 				**
541*bc421551SDag-Erling Smørgrav 				** C99 and later say that %z must be replaced by
542*bc421551SDag-Erling Smørgrav 				** the empty string if the time zone is not
543*bc421551SDag-Erling Smørgrav 				** determinable, so output nothing if the
544*bc421551SDag-Erling Smørgrav 				** appropriate variables are not available.
545*bc421551SDag-Erling Smørgrav 				*/
546*bc421551SDag-Erling Smørgrav 				if (t->tm_isdst < 0)
547*bc421551SDag-Erling Smørgrav 					continue;
548*bc421551SDag-Erling Smørgrav 				if (t->tm_isdst == 0)
549*bc421551SDag-Erling Smørgrav #  if USG_COMPAT
550*bc421551SDag-Erling Smørgrav 					diff = -timezone;
551*bc421551SDag-Erling Smørgrav #  else
552*bc421551SDag-Erling Smørgrav 					continue;
553*bc421551SDag-Erling Smørgrav #  endif
554*bc421551SDag-Erling Smørgrav 				else
555*bc421551SDag-Erling Smørgrav #  if ALTZONE
556*bc421551SDag-Erling Smørgrav 					diff = -altzone;
557*bc421551SDag-Erling Smørgrav #  else
558*bc421551SDag-Erling Smørgrav 					continue;
559*bc421551SDag-Erling Smørgrav #  endif
560*bc421551SDag-Erling Smørgrav # endif
561*bc421551SDag-Erling Smørgrav 				negative = diff < 0;
562*bc421551SDag-Erling Smørgrav 				if (diff == 0) {
563*bc421551SDag-Erling Smørgrav # ifdef TM_ZONE
564*bc421551SDag-Erling Smørgrav 				  negative = t->TM_ZONE[0] == '-';
565*bc421551SDag-Erling Smørgrav # else
566*bc421551SDag-Erling Smørgrav 				  negative = t->tm_isdst < 0;
567*bc421551SDag-Erling Smørgrav #  if HAVE_TZNAME
568*bc421551SDag-Erling Smørgrav 				  if (tzname[t->tm_isdst != 0][0] == '-')
569*bc421551SDag-Erling Smørgrav 				    negative = true;
570*bc421551SDag-Erling Smørgrav #  endif
571*bc421551SDag-Erling Smørgrav # endif
572*bc421551SDag-Erling Smørgrav 				}
573*bc421551SDag-Erling Smørgrav 				if (negative) {
574*bc421551SDag-Erling Smørgrav 					sign = "-";
575*bc421551SDag-Erling Smørgrav 					diff = -diff;
576*bc421551SDag-Erling Smørgrav 				} else	sign = "+";
577*bc421551SDag-Erling Smørgrav 				pt = _add(sign, pt, ptlim);
578*bc421551SDag-Erling Smørgrav 				diff /= SECSPERMIN;
579*bc421551SDag-Erling Smørgrav 				diff = (diff / MINSPERHOUR) * 100 +
580*bc421551SDag-Erling Smørgrav 					(diff % MINSPERHOUR);
581*bc421551SDag-Erling Smørgrav 				pt = _conv(diff, "%04d", pt, ptlim);
582*bc421551SDag-Erling Smørgrav 				}
583*bc421551SDag-Erling Smørgrav #endif
584*bc421551SDag-Erling Smørgrav 				continue;
585*bc421551SDag-Erling Smørgrav 			case '+':
586*bc421551SDag-Erling Smørgrav 				pt = _fmt(Locale->date_fmt, t, pt, ptlim,
587*bc421551SDag-Erling Smørgrav 					warnp);
588*bc421551SDag-Erling Smørgrav 				continue;
589*bc421551SDag-Erling Smørgrav 			case '%':
590*bc421551SDag-Erling Smørgrav 			/*
591*bc421551SDag-Erling Smørgrav 			** X311J/88-090 (4.12.3.5): if conversion char is
592*bc421551SDag-Erling Smørgrav 			** undefined, behavior is undefined. Print out the
593*bc421551SDag-Erling Smørgrav 			** character itself as printf(3) also does.
594*bc421551SDag-Erling Smørgrav 			*/
595*bc421551SDag-Erling Smørgrav 			default:
596*bc421551SDag-Erling Smørgrav 				break;
597*bc421551SDag-Erling Smørgrav 			}
598*bc421551SDag-Erling Smørgrav 		}
599*bc421551SDag-Erling Smørgrav 		if (pt == ptlim)
600*bc421551SDag-Erling Smørgrav 			break;
601*bc421551SDag-Erling Smørgrav 		*pt++ = *format;
602*bc421551SDag-Erling Smørgrav 	}
603*bc421551SDag-Erling Smørgrav 	return pt;
604*bc421551SDag-Erling Smørgrav }
605*bc421551SDag-Erling Smørgrav 
606*bc421551SDag-Erling Smørgrav static char *
607*bc421551SDag-Erling Smørgrav _conv(int n, const char *format, char *pt, const char *ptlim)
608*bc421551SDag-Erling Smørgrav {
609*bc421551SDag-Erling Smørgrav 	char	buf[INT_STRLEN_MAXIMUM(int) + 1];
610*bc421551SDag-Erling Smørgrav 
611*bc421551SDag-Erling Smørgrav 	sprintf(buf, format, n);
612*bc421551SDag-Erling Smørgrav 	return _add(buf, pt, ptlim);
613*bc421551SDag-Erling Smørgrav }
614*bc421551SDag-Erling Smørgrav 
615*bc421551SDag-Erling Smørgrav static char *
616*bc421551SDag-Erling Smørgrav _add(const char *str, char *pt, const char *ptlim)
617*bc421551SDag-Erling Smørgrav {
618*bc421551SDag-Erling Smørgrav 	while (pt < ptlim && (*pt = *str++) != '\0')
619*bc421551SDag-Erling Smørgrav 		++pt;
620*bc421551SDag-Erling Smørgrav 	return pt;
621*bc421551SDag-Erling Smørgrav }
622*bc421551SDag-Erling Smørgrav 
623*bc421551SDag-Erling Smørgrav /*
624*bc421551SDag-Erling Smørgrav ** POSIX and the C Standard are unclear or inconsistent about
625*bc421551SDag-Erling Smørgrav ** what %C and %y do if the year is negative or exceeds 9999.
626*bc421551SDag-Erling Smørgrav ** Use the convention that %C concatenated with %y yields the
627*bc421551SDag-Erling Smørgrav ** same output as %Y, and that %Y contains at least 4 bytes,
628*bc421551SDag-Erling Smørgrav ** with more only if necessary.
629*bc421551SDag-Erling Smørgrav */
630*bc421551SDag-Erling Smørgrav 
631*bc421551SDag-Erling Smørgrav static char *
632*bc421551SDag-Erling Smørgrav _yconv(int a, int b, bool convert_top, bool convert_yy,
633*bc421551SDag-Erling Smørgrav        char *pt, const char *ptlim)
634*bc421551SDag-Erling Smørgrav {
635*bc421551SDag-Erling Smørgrav 	register int	lead;
636*bc421551SDag-Erling Smørgrav 	register int	trail;
637*bc421551SDag-Erling Smørgrav 
638*bc421551SDag-Erling Smørgrav 	int DIVISOR = 100;
639*bc421551SDag-Erling Smørgrav 	trail = a % DIVISOR + b % DIVISOR;
640*bc421551SDag-Erling Smørgrav 	lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
641*bc421551SDag-Erling Smørgrav 	trail %= DIVISOR;
642*bc421551SDag-Erling Smørgrav 	if (trail < 0 && lead > 0) {
643*bc421551SDag-Erling Smørgrav 		trail += DIVISOR;
644*bc421551SDag-Erling Smørgrav 		--lead;
645*bc421551SDag-Erling Smørgrav 	} else if (lead < 0 && trail > 0) {
646*bc421551SDag-Erling Smørgrav 		trail -= DIVISOR;
647*bc421551SDag-Erling Smørgrav 		++lead;
648*bc421551SDag-Erling Smørgrav 	}
649*bc421551SDag-Erling Smørgrav 	if (convert_top) {
650*bc421551SDag-Erling Smørgrav 		if (lead == 0 && trail < 0)
651*bc421551SDag-Erling Smørgrav 			pt = _add("-0", pt, ptlim);
652*bc421551SDag-Erling Smørgrav 		else	pt = _conv(lead, "%02d", pt, ptlim);
653*bc421551SDag-Erling Smørgrav 	}
654*bc421551SDag-Erling Smørgrav 	if (convert_yy)
655*bc421551SDag-Erling Smørgrav 		pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
656*bc421551SDag-Erling Smørgrav 	return pt;
657*bc421551SDag-Erling Smørgrav }
658