1570af302Sopenharmony_ci#include <stdio.h>
2570af302Sopenharmony_ci#include <stdlib.h>
3570af302Sopenharmony_ci#include <string.h>
4570af302Sopenharmony_ci#include <langinfo.h>
5570af302Sopenharmony_ci#include <locale.h>
6570af302Sopenharmony_ci#include <ctype.h>
7570af302Sopenharmony_ci#include <time.h>
8570af302Sopenharmony_ci#include <limits.h>
9570af302Sopenharmony_ci#include "locale_impl.h"
10570af302Sopenharmony_ci#include "time_impl.h"
11570af302Sopenharmony_ci
12570af302Sopenharmony_cistatic int is_leap(int y)
13570af302Sopenharmony_ci{
14570af302Sopenharmony_ci	/* Avoid overflow */
15570af302Sopenharmony_ci	if (y>INT_MAX-1900) y -= 2000;
16570af302Sopenharmony_ci	y += 1900;
17570af302Sopenharmony_ci	return !(y%4) && ((y%100) || !(y%400));
18570af302Sopenharmony_ci}
19570af302Sopenharmony_ci
20570af302Sopenharmony_cistatic int week_num(const struct tm *tm)
21570af302Sopenharmony_ci{
22570af302Sopenharmony_ci	int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7;
23570af302Sopenharmony_ci	/* If 1 Jan is just 1-3 days past Monday,
24570af302Sopenharmony_ci	 * the previous week is also in this year. */
25570af302Sopenharmony_ci	if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2)
26570af302Sopenharmony_ci		val++;
27570af302Sopenharmony_ci	if (!val) {
28570af302Sopenharmony_ci		val = 52;
29570af302Sopenharmony_ci		/* If 31 December of prev year a Thursday,
30570af302Sopenharmony_ci		 * or Friday of a leap year, then the
31570af302Sopenharmony_ci		 * prev year has 53 weeks. */
32570af302Sopenharmony_ci		int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7;
33570af302Sopenharmony_ci		if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1)))
34570af302Sopenharmony_ci			val++;
35570af302Sopenharmony_ci	} else if (val == 53) {
36570af302Sopenharmony_ci		/* If 1 January is not a Thursday, and not
37570af302Sopenharmony_ci		 * a Wednesday of a leap year, then this
38570af302Sopenharmony_ci		 * year has only 52 weeks. */
39570af302Sopenharmony_ci		int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7;
40570af302Sopenharmony_ci		if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year)))
41570af302Sopenharmony_ci			val = 1;
42570af302Sopenharmony_ci	}
43570af302Sopenharmony_ci	return val;
44570af302Sopenharmony_ci}
45570af302Sopenharmony_ci
46570af302Sopenharmony_ciconst char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad)
47570af302Sopenharmony_ci{
48570af302Sopenharmony_ci	nl_item item;
49570af302Sopenharmony_ci	long long val;
50570af302Sopenharmony_ci	const char *fmt = "-";
51570af302Sopenharmony_ci	int width = 2, def_pad = '0';
52570af302Sopenharmony_ci
53570af302Sopenharmony_ci	switch (f) {
54570af302Sopenharmony_ci	case 'a':
55570af302Sopenharmony_ci		if (tm->tm_wday > 6U) goto string;
56570af302Sopenharmony_ci		item = ABDAY_1 + tm->tm_wday;
57570af302Sopenharmony_ci		goto nl_strcat;
58570af302Sopenharmony_ci	case 'A':
59570af302Sopenharmony_ci		if (tm->tm_wday > 6U) goto string;
60570af302Sopenharmony_ci		item = DAY_1 + tm->tm_wday;
61570af302Sopenharmony_ci		goto nl_strcat;
62570af302Sopenharmony_ci	case 'h':
63570af302Sopenharmony_ci	case 'b':
64570af302Sopenharmony_ci		if (tm->tm_mon > 11U) goto string;
65570af302Sopenharmony_ci		item = ABMON_1 + tm->tm_mon;
66570af302Sopenharmony_ci		goto nl_strcat;
67570af302Sopenharmony_ci	case 'B':
68570af302Sopenharmony_ci		if (tm->tm_mon > 11U) goto string;
69570af302Sopenharmony_ci		item = MON_1 + tm->tm_mon;
70570af302Sopenharmony_ci		goto nl_strcat;
71570af302Sopenharmony_ci	case 'c':
72570af302Sopenharmony_ci		item = D_T_FMT;
73570af302Sopenharmony_ci		goto nl_strftime;
74570af302Sopenharmony_ci	case 'C':
75570af302Sopenharmony_ci		val = (1900LL+tm->tm_year) / 100;
76570af302Sopenharmony_ci		goto number;
77570af302Sopenharmony_ci	case 'e':
78570af302Sopenharmony_ci		def_pad = '_';
79570af302Sopenharmony_ci	case 'd':
80570af302Sopenharmony_ci		val = tm->tm_mday;
81570af302Sopenharmony_ci		goto number;
82570af302Sopenharmony_ci	case 'D':
83570af302Sopenharmony_ci		fmt = "%m/%d/%y";
84570af302Sopenharmony_ci		goto recu_strftime;
85570af302Sopenharmony_ci	case 'F':
86570af302Sopenharmony_ci		fmt = "%Y-%m-%d";
87570af302Sopenharmony_ci		goto recu_strftime;
88570af302Sopenharmony_ci	case 'g':
89570af302Sopenharmony_ci	case 'G':
90570af302Sopenharmony_ci		val = tm->tm_year + 1900LL;
91570af302Sopenharmony_ci		if (tm->tm_yday < 3 && week_num(tm) != 1) val--;
92570af302Sopenharmony_ci		else if (tm->tm_yday > 360 && week_num(tm) == 1) val++;
93570af302Sopenharmony_ci		if (f=='g') val %= 100;
94570af302Sopenharmony_ci		else width = 4;
95570af302Sopenharmony_ci		goto number;
96570af302Sopenharmony_ci	case 'H':
97570af302Sopenharmony_ci		val = tm->tm_hour;
98570af302Sopenharmony_ci		goto number;
99570af302Sopenharmony_ci	case 'I':
100570af302Sopenharmony_ci		val = tm->tm_hour;
101570af302Sopenharmony_ci		if (!val) val = 12;
102570af302Sopenharmony_ci		else if (val > 12) val -= 12;
103570af302Sopenharmony_ci		goto number;
104570af302Sopenharmony_ci	case 'j':
105570af302Sopenharmony_ci		val = tm->tm_yday+1;
106570af302Sopenharmony_ci		width = 3;
107570af302Sopenharmony_ci		goto number;
108570af302Sopenharmony_ci	case 'k':
109570af302Sopenharmony_ci		def_pad = '_';
110570af302Sopenharmony_ci		val = tm->tm_hour;
111570af302Sopenharmony_ci		goto number;
112570af302Sopenharmony_ci	case 'l':
113570af302Sopenharmony_ci		def_pad = '_';
114570af302Sopenharmony_ci		val = tm->tm_hour;
115570af302Sopenharmony_ci		if (!val) {
116570af302Sopenharmony_ci			val = 12;
117570af302Sopenharmony_ci		} else if (val > 12) {
118570af302Sopenharmony_ci			val -= 12;
119570af302Sopenharmony_ci		}
120570af302Sopenharmony_ci		goto number;
121570af302Sopenharmony_ci	case 'm':
122570af302Sopenharmony_ci		val = tm->tm_mon+1;
123570af302Sopenharmony_ci		goto number;
124570af302Sopenharmony_ci	case 'M':
125570af302Sopenharmony_ci		val = tm->tm_min;
126570af302Sopenharmony_ci		goto number;
127570af302Sopenharmony_ci	case 'n':
128570af302Sopenharmony_ci		*l = 1;
129570af302Sopenharmony_ci		return "\n";
130570af302Sopenharmony_ci	case 'p':
131570af302Sopenharmony_ci		item = tm->tm_hour >= 12 ? PM_STR : AM_STR;
132570af302Sopenharmony_ci		goto nl_strcat;
133570af302Sopenharmony_ci	case 'P':
134570af302Sopenharmony_ci		item = tm->tm_hour >= 12 ? PM_STR_LOWER : AM_STR_LOWER;
135570af302Sopenharmony_ci		goto nl_strcat;
136570af302Sopenharmony_ci	case 'r':
137570af302Sopenharmony_ci		item = T_FMT_AMPM;
138570af302Sopenharmony_ci		goto nl_strftime;
139570af302Sopenharmony_ci	case 'R':
140570af302Sopenharmony_ci		fmt = "%H:%M";
141570af302Sopenharmony_ci		goto recu_strftime;
142570af302Sopenharmony_ci	case 's':
143570af302Sopenharmony_ci		val = __tm_to_secs(tm) - tm->__tm_gmtoff;
144570af302Sopenharmony_ci		width = 1;
145570af302Sopenharmony_ci		goto number;
146570af302Sopenharmony_ci	case 'S':
147570af302Sopenharmony_ci		val = tm->tm_sec;
148570af302Sopenharmony_ci		goto number;
149570af302Sopenharmony_ci	case 't':
150570af302Sopenharmony_ci		*l = 1;
151570af302Sopenharmony_ci		return "\t";
152570af302Sopenharmony_ci	case 'T':
153570af302Sopenharmony_ci		fmt = "%H:%M:%S";
154570af302Sopenharmony_ci		goto recu_strftime;
155570af302Sopenharmony_ci	case 'u':
156570af302Sopenharmony_ci		val = tm->tm_wday ? tm->tm_wday : 7;
157570af302Sopenharmony_ci		width = 1;
158570af302Sopenharmony_ci		goto number;
159570af302Sopenharmony_ci	case 'U':
160570af302Sopenharmony_ci		val = (tm->tm_yday + 7U - tm->tm_wday) / 7;
161570af302Sopenharmony_ci		goto number;
162570af302Sopenharmony_ci	case 'W':
163570af302Sopenharmony_ci		val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7;
164570af302Sopenharmony_ci		goto number;
165570af302Sopenharmony_ci	case 'v':
166570af302Sopenharmony_ci		fmt = "%e-%b-%Y";
167570af302Sopenharmony_ci		goto recu_strftime;
168570af302Sopenharmony_ci	case 'V':
169570af302Sopenharmony_ci		val = week_num(tm);
170570af302Sopenharmony_ci		goto number;
171570af302Sopenharmony_ci	case 'w':
172570af302Sopenharmony_ci		val = tm->tm_wday;
173570af302Sopenharmony_ci		width = 1;
174570af302Sopenharmony_ci		goto number;
175570af302Sopenharmony_ci	case 'x':
176570af302Sopenharmony_ci		item = D_FMT;
177570af302Sopenharmony_ci		goto nl_strftime;
178570af302Sopenharmony_ci	case 'X':
179570af302Sopenharmony_ci		item = T_FMT;
180570af302Sopenharmony_ci		goto nl_strftime;
181570af302Sopenharmony_ci	case 'y':
182570af302Sopenharmony_ci		val = (tm->tm_year + 1900LL) % 100;
183570af302Sopenharmony_ci		if (val < 0) val = -val;
184570af302Sopenharmony_ci		goto number;
185570af302Sopenharmony_ci	case 'Y':
186570af302Sopenharmony_ci		val = tm->tm_year + 1900LL;
187570af302Sopenharmony_ci		if (val >= 10000) {
188570af302Sopenharmony_ci			*l = snprintf(*s, sizeof *s, "+%lld", val);
189570af302Sopenharmony_ci			return *s;
190570af302Sopenharmony_ci		}
191570af302Sopenharmony_ci		width = 4;
192570af302Sopenharmony_ci		goto number;
193570af302Sopenharmony_ci	case 'z':
194570af302Sopenharmony_ci		if (tm->tm_isdst < 0) {
195570af302Sopenharmony_ci			*l = 0;
196570af302Sopenharmony_ci			return "";
197570af302Sopenharmony_ci		}
198570af302Sopenharmony_ci		*l = snprintf(*s, sizeof *s, "%+.4ld",
199570af302Sopenharmony_ci			tm->__tm_gmtoff/3600*100 + tm->__tm_gmtoff%3600/60);
200570af302Sopenharmony_ci		return *s;
201570af302Sopenharmony_ci	case 'Z':
202570af302Sopenharmony_ci		if (tm->tm_isdst < 0) {
203570af302Sopenharmony_ci			*l = 0;
204570af302Sopenharmony_ci			return "";
205570af302Sopenharmony_ci		}
206570af302Sopenharmony_ci		fmt = __tm_to_tzname(tm);
207570af302Sopenharmony_ci		goto string;
208570af302Sopenharmony_ci	case '%':
209570af302Sopenharmony_ci		*l = 1;
210570af302Sopenharmony_ci		return "%";
211570af302Sopenharmony_ci	default:
212570af302Sopenharmony_ci		return 0;
213570af302Sopenharmony_ci	}
214570af302Sopenharmony_cinumber:
215570af302Sopenharmony_ci	switch (pad ? pad : def_pad) {
216570af302Sopenharmony_ci	case '-': *l = snprintf(*s, sizeof *s, "%lld", val); break;
217570af302Sopenharmony_ci	case '_': *l = snprintf(*s, sizeof *s, "%*lld", width, val); break;
218570af302Sopenharmony_ci	case '0':
219570af302Sopenharmony_ci	default:  *l = snprintf(*s, sizeof *s, "%0*lld", width, val); break;
220570af302Sopenharmony_ci	}
221570af302Sopenharmony_ci	return *s;
222570af302Sopenharmony_cinl_strcat:
223570af302Sopenharmony_ci	fmt = __nl_langinfo_l(item, loc);
224570af302Sopenharmony_cistring:
225570af302Sopenharmony_ci	*l = strlen(fmt);
226570af302Sopenharmony_ci	return fmt;
227570af302Sopenharmony_cinl_strftime:
228570af302Sopenharmony_ci	fmt = __nl_langinfo_l(item, loc);
229570af302Sopenharmony_cirecu_strftime:
230570af302Sopenharmony_ci	*l = __strftime_l(*s, sizeof *s, fmt, tm, loc);
231570af302Sopenharmony_ci	if (!*l) return 0;
232570af302Sopenharmony_ci	return *s;
233570af302Sopenharmony_ci}
234570af302Sopenharmony_ci
235570af302Sopenharmony_cisize_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc)
236570af302Sopenharmony_ci{
237570af302Sopenharmony_ci	size_t l, k;
238570af302Sopenharmony_ci	char buf[100];
239570af302Sopenharmony_ci	char *p;
240570af302Sopenharmony_ci	const char *t;
241570af302Sopenharmony_ci	int pad, plus;
242570af302Sopenharmony_ci	unsigned long width;
243570af302Sopenharmony_ci	for (l=0; l<n; f++) {
244570af302Sopenharmony_ci		if (!*f) {
245570af302Sopenharmony_ci			s[l] = 0;
246570af302Sopenharmony_ci			return l;
247570af302Sopenharmony_ci		}
248570af302Sopenharmony_ci		if (*f != '%') {
249570af302Sopenharmony_ci			s[l++] = *f;
250570af302Sopenharmony_ci			continue;
251570af302Sopenharmony_ci		}
252570af302Sopenharmony_ci		f++;
253570af302Sopenharmony_ci		pad = 0;
254570af302Sopenharmony_ci		if (*f == '-' || *f == '_' || *f == '0') pad = *f++;
255570af302Sopenharmony_ci		if ((plus = (*f == '+'))) f++;
256570af302Sopenharmony_ci		if (isdigit(*f)) {
257570af302Sopenharmony_ci			width = strtoul(f, &p, 10);
258570af302Sopenharmony_ci		} else {
259570af302Sopenharmony_ci			width = 0;
260570af302Sopenharmony_ci			p = (void *)f;
261570af302Sopenharmony_ci		}
262570af302Sopenharmony_ci		if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') {
263570af302Sopenharmony_ci			if (!width && p!=f) width = 1;
264570af302Sopenharmony_ci		} else {
265570af302Sopenharmony_ci			width = 0;
266570af302Sopenharmony_ci		}
267570af302Sopenharmony_ci		f = p;
268570af302Sopenharmony_ci		if (*f == 'E' || *f == 'O') f++;
269570af302Sopenharmony_ci		t = __strftime_fmt_1(&buf, &k, *f, tm, loc, pad);
270570af302Sopenharmony_ci		if (!t) break;
271570af302Sopenharmony_ci		if (width) {
272570af302Sopenharmony_ci			/* Trim off any sign and leading zeros, then
273570af302Sopenharmony_ci			 * count remaining digits to determine behavior
274570af302Sopenharmony_ci			 * for the + flag. */
275570af302Sopenharmony_ci			if (*t=='+' || *t=='-') t++, k--;
276570af302Sopenharmony_ci			for (; *t=='0' && t[1]-'0'<10U; t++, k--);
277570af302Sopenharmony_ci			if (width < k) width = k;
278570af302Sopenharmony_ci			size_t d;
279570af302Sopenharmony_ci			for (d=0; t[d]-'0'<10U; d++);
280570af302Sopenharmony_ci			if (tm->tm_year < -1900) {
281570af302Sopenharmony_ci				s[l++] = '-';
282570af302Sopenharmony_ci				width--;
283570af302Sopenharmony_ci			} else if (plus && d+(width-k) >= (*p=='C'?3:5)) {
284570af302Sopenharmony_ci				s[l++] = '+';
285570af302Sopenharmony_ci				width--;
286570af302Sopenharmony_ci			}
287570af302Sopenharmony_ci			for (; width > k && l < n; width--)
288570af302Sopenharmony_ci				s[l++] = '0';
289570af302Sopenharmony_ci		}
290570af302Sopenharmony_ci		if (k > n-l) k = n-l;
291570af302Sopenharmony_ci		memcpy(s+l, t, k);
292570af302Sopenharmony_ci		l += k;
293570af302Sopenharmony_ci	}
294570af302Sopenharmony_ci	if (n) {
295570af302Sopenharmony_ci		if (l==n) l=n-1;
296570af302Sopenharmony_ci		s[l] = 0;
297570af302Sopenharmony_ci	}
298570af302Sopenharmony_ci	return 0;
299570af302Sopenharmony_ci}
300570af302Sopenharmony_ci
301570af302Sopenharmony_cisize_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm)
302570af302Sopenharmony_ci{
303570af302Sopenharmony_ci	return __strftime_l(s, n, f, tm, CURRENT_LOCALE);
304570af302Sopenharmony_ci}
305570af302Sopenharmony_ci
306570af302Sopenharmony_ciweak_alias(__strftime_l, strftime_l);
307