10413bfcaSPedro F. Giffuni /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3d915a14eSPedro F. Giffuni *
4cd9dcb03SPedro F. Giffuni * Copyright (c) 2014 Gary Mills
5cd9dcb03SPedro F. Giffuni * Copyright 2011, Nexenta Systems, Inc. All rights reserved.
6d7641983SJoerg Wunsch * Copyright (c) 1994 Powerdog Industries. All rights reserved.
7d7641983SJoerg Wunsch *
83c87aa1dSDavid Chisnall * Copyright (c) 2011 The FreeBSD Foundation
95b5fa75aSEd Maste *
103c87aa1dSDavid Chisnall * Portions of this software were developed by David Chisnall
113c87aa1dSDavid Chisnall * under sponsorship from the FreeBSD Foundation.
123c87aa1dSDavid Chisnall *
1337486f03SJoerg Wunsch * Redistribution and use in source and binary forms, with or without
14d7641983SJoerg Wunsch * modification, are permitted provided that the following conditions
15d7641983SJoerg Wunsch * are met:
16d7641983SJoerg Wunsch * 1. Redistributions of source code must retain the above copyright
17d7641983SJoerg Wunsch * notice, this list of conditions and the following disclaimer.
18d7641983SJoerg Wunsch * 2. Redistributions in binary form must reproduce the above copyright
19d7641983SJoerg Wunsch * notice, this list of conditions and the following disclaimer
20d7641983SJoerg Wunsch * in the documentation and/or other materials provided with the
21d7641983SJoerg Wunsch * distribution.
22d7641983SJoerg Wunsch *
23d7641983SJoerg Wunsch * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
24d7641983SJoerg Wunsch * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25d7641983SJoerg Wunsch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26d7641983SJoerg Wunsch * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
27d7641983SJoerg Wunsch * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28d7641983SJoerg Wunsch * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29d7641983SJoerg Wunsch * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30d7641983SJoerg Wunsch * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31d7641983SJoerg Wunsch * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
32d7641983SJoerg Wunsch * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
33d7641983SJoerg Wunsch * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
340413bfcaSPedro F. Giffuni *
350413bfcaSPedro F. Giffuni * The views and conclusions contained in the software and documentation
360413bfcaSPedro F. Giffuni * are those of the authors and should not be interpreted as representing
370413bfcaSPedro F. Giffuni * official policies, either expressed or implied, of Powerdog Industries.
38d7641983SJoerg Wunsch */
39d7641983SJoerg Wunsch
40d201fe46SDaniel Eischen #include "namespace.h"
41d7641983SJoerg Wunsch #include <time.h>
42d7641983SJoerg Wunsch #include <ctype.h>
43c91e947dSJacques Vidrine #include <errno.h>
44427fc5edSDima Dorfman #include <stdlib.h>
45d7641983SJoerg Wunsch #include <string.h>
46cb7a4779SDavid E. O'Brien #include <pthread.h>
47bc421551SDag-Erling Smørgrav #include "private.h"
48d201fe46SDaniel Eischen #include "un-namespace.h"
49d201fe46SDaniel Eischen #include "libc_private.h"
5037486f03SJoerg Wunsch #include "timelocal.h"
512e465587SPedro F. Giffuni #include "tzfile.h"
521a6804c6SPedro F. Giffuni
533c87aa1dSDavid Chisnall static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
54cb7a4779SDavid E. O'Brien
55d7641983SJoerg Wunsch #define asizeof(a) (sizeof(a) / sizeof((a)[0]))
56d7641983SJoerg Wunsch
572e465587SPedro F. Giffuni #define FLAG_NONE (1 << 0)
582e465587SPedro F. Giffuni #define FLAG_YEAR (1 << 1)
592e465587SPedro F. Giffuni #define FLAG_MONTH (1 << 2)
602e465587SPedro F. Giffuni #define FLAG_YDAY (1 << 3)
612e465587SPedro F. Giffuni #define FLAG_MDAY (1 << 4)
622e465587SPedro F. Giffuni #define FLAG_WDAY (1 << 5)
632e465587SPedro F. Giffuni
642e465587SPedro F. Giffuni /*
652e465587SPedro F. Giffuni * Calculate the week day of the first day of a year. Valid for
662e465587SPedro F. Giffuni * the Gregorian calendar, which began Sept 14, 1752 in the UK
672e465587SPedro F. Giffuni * and its colonies. Ref:
682e465587SPedro F. Giffuni * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
692e465587SPedro F. Giffuni */
702e465587SPedro F. Giffuni
712e465587SPedro F. Giffuni static int
first_wday_of(int year)722e465587SPedro F. Giffuni first_wday_of(int year)
732e465587SPedro F. Giffuni {
742e465587SPedro F. Giffuni return (((2 * (3 - (year / 100) % 4)) + (year % 100) +
752e465587SPedro F. Giffuni ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);
762e465587SPedro F. Giffuni }
772e465587SPedro F. Giffuni
78cb7a4779SDavid E. O'Brien static char *
_strptime(const char * buf,const char * fmt,struct tm * tm,int * GMTp,locale_t locale)793c87aa1dSDavid Chisnall _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
803c87aa1dSDavid Chisnall locale_t locale)
81d7641983SJoerg Wunsch {
8237486f03SJoerg Wunsch char c;
8337486f03SJoerg Wunsch const char *ptr;
842e465587SPedro F. Giffuni int day_offset = -1, wday_offset;
852e465587SPedro F. Giffuni int week_offset;
863624c752SPedro F. Giffuni int i, len;
872e465587SPedro F. Giffuni int flags;
881d6c9941SAndrey A. Chernov int Ealternative, Oalternative;
89eb144aa0SYuri Pankov int century, year;
902e465587SPedro F. Giffuni const struct lc_time_T *tptr = __get_current_time_locale(locale);
912e465587SPedro F. Giffuni static int start_of_month[2][13] = {
922e465587SPedro F. Giffuni {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
932e465587SPedro F. Giffuni {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
942e465587SPedro F. Giffuni };
952e465587SPedro F. Giffuni
962e465587SPedro F. Giffuni flags = FLAG_NONE;
97eb144aa0SYuri Pankov century = -1;
98eb144aa0SYuri Pankov year = -1;
99d7641983SJoerg Wunsch
100d7641983SJoerg Wunsch ptr = fmt;
101d7641983SJoerg Wunsch while (*ptr != 0) {
102d7641983SJoerg Wunsch c = *ptr++;
103d7641983SJoerg Wunsch
104d7641983SJoerg Wunsch if (c != '%') {
1053c87aa1dSDavid Chisnall if (isspace_l((unsigned char)c, locale))
1063c87aa1dSDavid Chisnall while (*buf != 0 &&
1073c87aa1dSDavid Chisnall isspace_l((unsigned char)*buf, locale))
108d7641983SJoerg Wunsch buf++;
109d7641983SJoerg Wunsch else if (c != *buf++)
1103624c752SPedro F. Giffuni return (NULL);
111d7641983SJoerg Wunsch continue;
112d7641983SJoerg Wunsch }
113d7641983SJoerg Wunsch
1141d6c9941SAndrey A. Chernov Ealternative = 0;
1151d6c9941SAndrey A. Chernov Oalternative = 0;
1161d6c9941SAndrey A. Chernov label:
117d7641983SJoerg Wunsch c = *ptr++;
118d7641983SJoerg Wunsch switch (c) {
119d7641983SJoerg Wunsch case '%':
120d7641983SJoerg Wunsch if (*buf++ != '%')
1213624c752SPedro F. Giffuni return (NULL);
122d7641983SJoerg Wunsch break;
123d7641983SJoerg Wunsch
1241d6c9941SAndrey A. Chernov case '+':
1253c87aa1dSDavid Chisnall buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
1263624c752SPedro F. Giffuni if (buf == NULL)
1273624c752SPedro F. Giffuni return (NULL);
1282e465587SPedro F. Giffuni flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
129d7641983SJoerg Wunsch break;
130d7641983SJoerg Wunsch
1311d6c9941SAndrey A. Chernov case 'C':
1323c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale))
1333624c752SPedro F. Giffuni return (NULL);
1341d6c9941SAndrey A. Chernov
135398592ffSSheldon Hearn /* XXX This will break for 3-digit centuries. */
136398592ffSSheldon Hearn len = 2;
1373c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 &&
1383c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) {
1391d6c9941SAndrey A. Chernov i *= 10;
1401d6c9941SAndrey A. Chernov i += *buf - '0';
141398592ffSSheldon Hearn len--;
1421d6c9941SAndrey A. Chernov }
1431d6c9941SAndrey A. Chernov
144eb144aa0SYuri Pankov century = i;
1452e465587SPedro F. Giffuni flags |= FLAG_YEAR;
1462e465587SPedro F. Giffuni
1471d6c9941SAndrey A. Chernov break;
1481d6c9941SAndrey A. Chernov
149d7641983SJoerg Wunsch case 'c':
1503c87aa1dSDavid Chisnall buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
1513624c752SPedro F. Giffuni if (buf == NULL)
1523624c752SPedro F. Giffuni return (NULL);
1532e465587SPedro F. Giffuni flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
154d7641983SJoerg Wunsch break;
155d7641983SJoerg Wunsch
156d7641983SJoerg Wunsch case 'D':
1573c87aa1dSDavid Chisnall buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
1583624c752SPedro F. Giffuni if (buf == NULL)
1593624c752SPedro F. Giffuni return (NULL);
1602e465587SPedro F. Giffuni flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
161d7641983SJoerg Wunsch break;
162d7641983SJoerg Wunsch
1631d6c9941SAndrey A. Chernov case 'E':
164c63a4303SAndrey A. Chernov if (Ealternative || Oalternative)
165c63a4303SAndrey A. Chernov break;
1661d6c9941SAndrey A. Chernov Ealternative++;
1671d6c9941SAndrey A. Chernov goto label;
1681d6c9941SAndrey A. Chernov
1691d6c9941SAndrey A. Chernov case 'O':
170c63a4303SAndrey A. Chernov if (Ealternative || Oalternative)
171c63a4303SAndrey A. Chernov break;
1721d6c9941SAndrey A. Chernov Oalternative++;
1731d6c9941SAndrey A. Chernov goto label;
1741d6c9941SAndrey A. Chernov
175c63a4303SAndrey A. Chernov case 'F':
1763c87aa1dSDavid Chisnall buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
1773624c752SPedro F. Giffuni if (buf == NULL)
1783624c752SPedro F. Giffuni return (NULL);
1792e465587SPedro F. Giffuni flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
180c63a4303SAndrey A. Chernov break;
181c63a4303SAndrey A. Chernov
182d7641983SJoerg Wunsch case 'R':
1833c87aa1dSDavid Chisnall buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
1843624c752SPedro F. Giffuni if (buf == NULL)
1853624c752SPedro F. Giffuni return (NULL);
186d7641983SJoerg Wunsch break;
187d7641983SJoerg Wunsch
188d7641983SJoerg Wunsch case 'r':
1893c87aa1dSDavid Chisnall buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
1903624c752SPedro F. Giffuni if (buf == NULL)
1913624c752SPedro F. Giffuni return (NULL);
192d7641983SJoerg Wunsch break;
193d7641983SJoerg Wunsch
194d7641983SJoerg Wunsch case 'T':
1953c87aa1dSDavid Chisnall buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
1963624c752SPedro F. Giffuni if (buf == NULL)
1973624c752SPedro F. Giffuni return (NULL);
198d7641983SJoerg Wunsch break;
199d7641983SJoerg Wunsch
200d7641983SJoerg Wunsch case 'X':
2013c87aa1dSDavid Chisnall buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
2023624c752SPedro F. Giffuni if (buf == NULL)
2033624c752SPedro F. Giffuni return (NULL);
204d7641983SJoerg Wunsch break;
205d7641983SJoerg Wunsch
206d7641983SJoerg Wunsch case 'x':
2073c87aa1dSDavid Chisnall buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
2083624c752SPedro F. Giffuni if (buf == NULL)
2093624c752SPedro F. Giffuni return (NULL);
2102e465587SPedro F. Giffuni flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
211d7641983SJoerg Wunsch break;
212d7641983SJoerg Wunsch
213d7641983SJoerg Wunsch case 'j':
2143c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale))
2153624c752SPedro F. Giffuni return (NULL);
216d7641983SJoerg Wunsch
217398592ffSSheldon Hearn len = 3;
2183c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 &&
2193c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++){
220d7641983SJoerg Wunsch i *= 10;
221d7641983SJoerg Wunsch i += *buf - '0';
222398592ffSSheldon Hearn len--;
223d7641983SJoerg Wunsch }
22433dbb0a6SSheldon Hearn if (i < 1 || i > 366)
2253624c752SPedro F. Giffuni return (NULL);
226d7641983SJoerg Wunsch
22733dbb0a6SSheldon Hearn tm->tm_yday = i - 1;
2282e465587SPedro F. Giffuni flags |= FLAG_YDAY;
2292e465587SPedro F. Giffuni
230d7641983SJoerg Wunsch break;
231d7641983SJoerg Wunsch
232d7641983SJoerg Wunsch case 'M':
233d7641983SJoerg Wunsch case 'S':
2343c87aa1dSDavid Chisnall if (*buf == 0 ||
2353c87aa1dSDavid Chisnall isspace_l((unsigned char)*buf, locale))
236d7641983SJoerg Wunsch break;
237d7641983SJoerg Wunsch
2383c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale))
2393624c752SPedro F. Giffuni return (NULL);
240d7641983SJoerg Wunsch
241398592ffSSheldon Hearn len = 2;
2423c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 &&
2433c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++){
244d7641983SJoerg Wunsch i *= 10;
245d7641983SJoerg Wunsch i += *buf - '0';
246398592ffSSheldon Hearn len--;
247d7641983SJoerg Wunsch }
24833dbb0a6SSheldon Hearn
24933dbb0a6SSheldon Hearn if (c == 'M') {
250d7641983SJoerg Wunsch if (i > 59)
2513624c752SPedro F. Giffuni return (NULL);
252d7641983SJoerg Wunsch tm->tm_min = i;
25333dbb0a6SSheldon Hearn } else {
25433dbb0a6SSheldon Hearn if (i > 60)
2553624c752SPedro F. Giffuni return (NULL);
256d7641983SJoerg Wunsch tm->tm_sec = i;
25733dbb0a6SSheldon Hearn }
258d7641983SJoerg Wunsch
259d7641983SJoerg Wunsch break;
260d7641983SJoerg Wunsch
261d7641983SJoerg Wunsch case 'H':
262d7641983SJoerg Wunsch case 'I':
263d7641983SJoerg Wunsch case 'k':
264d7641983SJoerg Wunsch case 'l':
265398592ffSSheldon Hearn /*
26632a8ec8bSYuri Pankov * %k and %l specifiers are documented as being
26732a8ec8bSYuri Pankov * blank-padded. However, there is no harm in
26832a8ec8bSYuri Pankov * allowing zero-padding.
269398592ffSSheldon Hearn *
27032a8ec8bSYuri Pankov * XXX %k and %l specifiers may gobble one too many
271398592ffSSheldon Hearn * digits if used incorrectly.
272398592ffSSheldon Hearn */
27332a8ec8bSYuri Pankov
27432a8ec8bSYuri Pankov len = 2;
27532a8ec8bSYuri Pankov if ((c == 'k' || c == 'l') &&
27632a8ec8bSYuri Pankov isblank_l((unsigned char)*buf, locale)) {
27732a8ec8bSYuri Pankov buf++;
27832a8ec8bSYuri Pankov len = 1;
27932a8ec8bSYuri Pankov }
28032a8ec8bSYuri Pankov
2813c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale))
2823624c752SPedro F. Giffuni return (NULL);
283d7641983SJoerg Wunsch
2843c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 &&
2853c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) {
286d7641983SJoerg Wunsch i *= 10;
287d7641983SJoerg Wunsch i += *buf - '0';
288398592ffSSheldon Hearn len--;
289d7641983SJoerg Wunsch }
290d7641983SJoerg Wunsch if (c == 'H' || c == 'k') {
291d7641983SJoerg Wunsch if (i > 23)
2923624c752SPedro F. Giffuni return (NULL);
293cbc00629SYuri Pankov } else if (i == 0 || i > 12)
2943624c752SPedro F. Giffuni return (NULL);
295d7641983SJoerg Wunsch
296d7641983SJoerg Wunsch tm->tm_hour = i;
297d7641983SJoerg Wunsch
298d7641983SJoerg Wunsch break;
299d7641983SJoerg Wunsch
300d7641983SJoerg Wunsch case 'p':
301398592ffSSheldon Hearn /*
302398592ffSSheldon Hearn * XXX This is bogus if parsed before hour-related
303398592ffSSheldon Hearn * specifiers.
304398592ffSSheldon Hearn */
305d7641983SJoerg Wunsch if (tm->tm_hour > 12)
3063624c752SPedro F. Giffuni return (NULL);
30762b939ebSAndrey A. Chernov
30862b939ebSAndrey A. Chernov len = strlen(tptr->am);
30962b939ebSAndrey A. Chernov if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
310d7641983SJoerg Wunsch if (tm->tm_hour == 12)
311d7641983SJoerg Wunsch tm->tm_hour = 0;
312d7641983SJoerg Wunsch buf += len;
313d7641983SJoerg Wunsch break;
314d7641983SJoerg Wunsch }
315d7641983SJoerg Wunsch
316930cd711SAlexey Zelkin len = strlen(tptr->pm);
3173c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
318d7641983SJoerg Wunsch if (tm->tm_hour != 12)
319d7641983SJoerg Wunsch tm->tm_hour += 12;
320d7641983SJoerg Wunsch buf += len;
321d7641983SJoerg Wunsch break;
322d7641983SJoerg Wunsch }
323d7641983SJoerg Wunsch
3243624c752SPedro F. Giffuni return (NULL);
325d7641983SJoerg Wunsch
326d7641983SJoerg Wunsch case 'A':
327d7641983SJoerg Wunsch case 'a':
328930cd711SAlexey Zelkin for (i = 0; i < asizeof(tptr->weekday); i++) {
329930cd711SAlexey Zelkin len = strlen(tptr->weekday[i]);
3303c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->weekday[i],
3313c87aa1dSDavid Chisnall len, locale) == 0)
332d7641983SJoerg Wunsch break;
333930cd711SAlexey Zelkin len = strlen(tptr->wday[i]);
3343c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->wday[i],
3353c87aa1dSDavid Chisnall len, locale) == 0)
336d7641983SJoerg Wunsch break;
337d7641983SJoerg Wunsch }
338930cd711SAlexey Zelkin if (i == asizeof(tptr->weekday))
3393624c752SPedro F. Giffuni return (NULL);
340d7641983SJoerg Wunsch
3411a6804c6SPedro F. Giffuni buf += len;
342d7641983SJoerg Wunsch tm->tm_wday = i;
3432e465587SPedro F. Giffuni flags |= FLAG_WDAY;
344d7641983SJoerg Wunsch break;
345d7641983SJoerg Wunsch
34633dbb0a6SSheldon Hearn case 'U':
34733dbb0a6SSheldon Hearn case 'W':
34833dbb0a6SSheldon Hearn /*
34933dbb0a6SSheldon Hearn * XXX This is bogus, as we can not assume any valid
35033dbb0a6SSheldon Hearn * information present in the tm structure at this
35133dbb0a6SSheldon Hearn * point to calculate a real value, so just check the
35233dbb0a6SSheldon Hearn * range for now.
35333dbb0a6SSheldon Hearn */
3543c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale))
3553624c752SPedro F. Giffuni return (NULL);
35633dbb0a6SSheldon Hearn
357398592ffSSheldon Hearn len = 2;
3583c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 &&
3593c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) {
36033dbb0a6SSheldon Hearn i *= 10;
36133dbb0a6SSheldon Hearn i += *buf - '0';
362398592ffSSheldon Hearn len--;
36333dbb0a6SSheldon Hearn }
36433dbb0a6SSheldon Hearn if (i > 53)
3653624c752SPedro F. Giffuni return (NULL);
36633dbb0a6SSheldon Hearn
3672e465587SPedro F. Giffuni if (c == 'U')
3682e465587SPedro F. Giffuni day_offset = TM_SUNDAY;
3692e465587SPedro F. Giffuni else
3702e465587SPedro F. Giffuni day_offset = TM_MONDAY;
3712e465587SPedro F. Giffuni
3722e465587SPedro F. Giffuni
3732e465587SPedro F. Giffuni week_offset = i;
3742e465587SPedro F. Giffuni
37533dbb0a6SSheldon Hearn break;
37633dbb0a6SSheldon Hearn
37762b939ebSAndrey A. Chernov case 'u':
37833dbb0a6SSheldon Hearn case 'w':
3793c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale))
3803624c752SPedro F. Giffuni return (NULL);
38133dbb0a6SSheldon Hearn
38262b939ebSAndrey A. Chernov i = *buf++ - '0';
38362b939ebSAndrey A. Chernov if (i < 0 || i > 7 || (c == 'u' && i < 1) ||
38462b939ebSAndrey A. Chernov (c == 'w' && i > 6))
3853624c752SPedro F. Giffuni return (NULL);
38633dbb0a6SSheldon Hearn
38762b939ebSAndrey A. Chernov tm->tm_wday = i % 7;
3882e465587SPedro F. Giffuni flags |= FLAG_WDAY;
38933dbb0a6SSheldon Hearn
39033dbb0a6SSheldon Hearn break;
39133dbb0a6SSheldon Hearn
392d7641983SJoerg Wunsch case 'e':
393398592ffSSheldon Hearn /*
394cd9dcb03SPedro F. Giffuni * With %e format, our strftime(3) adds a blank space
395cd9dcb03SPedro F. Giffuni * before single digits.
396cd9dcb03SPedro F. Giffuni */
397cd9dcb03SPedro F. Giffuni if (*buf != 0 &&
398cd9dcb03SPedro F. Giffuni isspace_l((unsigned char)*buf, locale))
399cd9dcb03SPedro F. Giffuni buf++;
400cd9dcb03SPedro F. Giffuni /* FALLTHROUGH */
401cd9dcb03SPedro F. Giffuni case 'd':
402cd9dcb03SPedro F. Giffuni /*
403cd9dcb03SPedro F. Giffuni * The %e specifier was once explicitly documented as
404cd9dcb03SPedro F. Giffuni * not being zero-padded but was later changed to
405cd9dcb03SPedro F. Giffuni * equivalent to %d. There is no harm in allowing
406398592ffSSheldon Hearn * such padding.
407398592ffSSheldon Hearn *
408398592ffSSheldon Hearn * XXX The %e specifier may gobble one too many
409398592ffSSheldon Hearn * digits if used incorrectly.
410398592ffSSheldon Hearn */
4113c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale))
4123624c752SPedro F. Giffuni return (NULL);
413d7641983SJoerg Wunsch
414398592ffSSheldon Hearn len = 2;
4153c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 &&
4163c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) {
417d7641983SJoerg Wunsch i *= 10;
418d7641983SJoerg Wunsch i += *buf - '0';
419398592ffSSheldon Hearn len--;
420d7641983SJoerg Wunsch }
42192cbfb63SKonstantin Belousov if (i == 0 || i > 31)
4223624c752SPedro F. Giffuni return (NULL);
423d7641983SJoerg Wunsch
424d7641983SJoerg Wunsch tm->tm_mday = i;
4252e465587SPedro F. Giffuni flags |= FLAG_MDAY;
426d7641983SJoerg Wunsch
427d7641983SJoerg Wunsch break;
428d7641983SJoerg Wunsch
429d7641983SJoerg Wunsch case 'B':
430d7641983SJoerg Wunsch case 'b':
431d7641983SJoerg Wunsch case 'h':
432930cd711SAlexey Zelkin for (i = 0; i < asizeof(tptr->month); i++) {
4331d6c9941SAndrey A. Chernov if (Oalternative) {
4341d6c9941SAndrey A. Chernov if (c == 'B') {
435930cd711SAlexey Zelkin len = strlen(tptr->alt_month[i]);
4363c87aa1dSDavid Chisnall if (strncasecmp_l(buf,
437930cd711SAlexey Zelkin tptr->alt_month[i],
4383c87aa1dSDavid Chisnall len, locale) == 0)
4391d6c9941SAndrey A. Chernov break;
4401d6c9941SAndrey A. Chernov }
4411d6c9941SAndrey A. Chernov } else {
442930cd711SAlexey Zelkin len = strlen(tptr->month[i]);
4433c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->month[i],
4443c87aa1dSDavid Chisnall len, locale) == 0)
445d7641983SJoerg Wunsch break;
4463d74e220SEdwin Groothuis }
4473d74e220SEdwin Groothuis }
4483d74e220SEdwin Groothuis /*
4493d74e220SEdwin Groothuis * Try the abbreviated month name if the full name
4503d74e220SEdwin Groothuis * wasn't found and Oalternative was not requested.
4513d74e220SEdwin Groothuis */
4523d74e220SEdwin Groothuis if (i == asizeof(tptr->month) && !Oalternative) {
4533d74e220SEdwin Groothuis for (i = 0; i < asizeof(tptr->month); i++) {
454930cd711SAlexey Zelkin len = strlen(tptr->mon[i]);
4553c87aa1dSDavid Chisnall if (strncasecmp_l(buf, tptr->mon[i],
4563c87aa1dSDavid Chisnall len, locale) == 0)
457d7641983SJoerg Wunsch break;
458d7641983SJoerg Wunsch }
4591d6c9941SAndrey A. Chernov }
460930cd711SAlexey Zelkin if (i == asizeof(tptr->month))
4613624c752SPedro F. Giffuni return (NULL);
462d7641983SJoerg Wunsch
463d7641983SJoerg Wunsch tm->tm_mon = i;
464d7641983SJoerg Wunsch buf += len;
4652e465587SPedro F. Giffuni flags |= FLAG_MONTH;
4662e465587SPedro F. Giffuni
467d7641983SJoerg Wunsch break;
468d7641983SJoerg Wunsch
469d7641983SJoerg Wunsch case 'm':
4703c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale))
4713624c752SPedro F. Giffuni return (NULL);
472d7641983SJoerg Wunsch
473398592ffSSheldon Hearn len = 2;
4743c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 &&
4753c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) {
476d7641983SJoerg Wunsch i *= 10;
477d7641983SJoerg Wunsch i += *buf - '0';
478398592ffSSheldon Hearn len--;
479d7641983SJoerg Wunsch }
480d7641983SJoerg Wunsch if (i < 1 || i > 12)
4813624c752SPedro F. Giffuni return (NULL);
482d7641983SJoerg Wunsch
483d7641983SJoerg Wunsch tm->tm_mon = i - 1;
4842e465587SPedro F. Giffuni flags |= FLAG_MONTH;
485d7641983SJoerg Wunsch
486d7641983SJoerg Wunsch break;
487d7641983SJoerg Wunsch
488427fc5edSDima Dorfman case 's':
489427fc5edSDima Dorfman {
490427fc5edSDima Dorfman char *cp;
491c91e947dSJacques Vidrine int sverrno;
492c91e947dSJacques Vidrine long n;
493427fc5edSDima Dorfman time_t t;
494427fc5edSDima Dorfman
495c91e947dSJacques Vidrine sverrno = errno;
496c91e947dSJacques Vidrine errno = 0;
4973c87aa1dSDavid Chisnall n = strtol_l(buf, &cp, 10, locale);
498c91e947dSJacques Vidrine if (errno == ERANGE || (long)(t = n) != n) {
499c91e947dSJacques Vidrine errno = sverrno;
5003624c752SPedro F. Giffuni return (NULL);
501c91e947dSJacques Vidrine }
502c91e947dSJacques Vidrine errno = sverrno;
503427fc5edSDima Dorfman buf = cp;
504eff6f444SPedro F. Giffuni if (gmtime_r(&t, tm) == NULL)
505eff6f444SPedro F. Giffuni return (NULL);
506fe71e0b8SMike Makonnen *GMTp = 1;
507eff6f444SPedro F. Giffuni flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |
508eff6f444SPedro F. Giffuni FLAG_MDAY | FLAG_YEAR;
509427fc5edSDima Dorfman }
510427fc5edSDima Dorfman break;
511427fc5edSDima Dorfman
512d7641983SJoerg Wunsch case 'Y':
513d7641983SJoerg Wunsch case 'y':
5143c87aa1dSDavid Chisnall if (*buf == 0 ||
5153c87aa1dSDavid Chisnall isspace_l((unsigned char)*buf, locale))
516d7641983SJoerg Wunsch break;
517d7641983SJoerg Wunsch
5183c87aa1dSDavid Chisnall if (!isdigit_l((unsigned char)*buf, locale))
5193624c752SPedro F. Giffuni return (NULL);
520d7641983SJoerg Wunsch
521398592ffSSheldon Hearn len = (c == 'Y') ? 4 : 2;
5223c87aa1dSDavid Chisnall for (i = 0; len && *buf != 0 &&
5233c87aa1dSDavid Chisnall isdigit_l((unsigned char)*buf, locale); buf++) {
524d7641983SJoerg Wunsch i *= 10;
525d7641983SJoerg Wunsch i += *buf - '0';
526398592ffSSheldon Hearn len--;
527d7641983SJoerg Wunsch }
528d7641983SJoerg Wunsch if (c == 'Y')
529eb144aa0SYuri Pankov century = i / 100;
530eb144aa0SYuri Pankov year = i % 100;
531d7641983SJoerg Wunsch
5322e465587SPedro F. Giffuni flags |= FLAG_YEAR;
533d7641983SJoerg Wunsch
534d7641983SJoerg Wunsch break;
535b47f20dfSDavid E. O'Brien
536b47f20dfSDavid E. O'Brien case 'Z':
537b47f20dfSDavid E. O'Brien {
538b47f20dfSDavid E. O'Brien const char *cp;
539b47f20dfSDavid E. O'Brien char *zonestr;
540b47f20dfSDavid E. O'Brien
5413c87aa1dSDavid Chisnall for (cp = buf; *cp &&
5423c87aa1dSDavid Chisnall isupper_l((unsigned char)*cp, locale); ++cp) {
5433c87aa1dSDavid Chisnall /*empty*/}
544b47f20dfSDavid E. O'Brien if (cp - buf) {
545b47f20dfSDavid E. O'Brien zonestr = alloca(cp - buf + 1);
546b47f20dfSDavid E. O'Brien strncpy(zonestr, buf, cp - buf);
547b47f20dfSDavid E. O'Brien zonestr[cp - buf] = '\0';
548b47f20dfSDavid E. O'Brien tzset();
549420bc6ceSAndrey A. Chernov if (0 == strcmp(zonestr, "GMT") ||
550420bc6ceSAndrey A. Chernov 0 == strcmp(zonestr, "UTC")) {
551fe71e0b8SMike Makonnen *GMTp = 1;
552b47f20dfSDavid E. O'Brien } else if (0 == strcmp(zonestr, tzname[0])) {
553b47f20dfSDavid E. O'Brien tm->tm_isdst = 0;
554b47f20dfSDavid E. O'Brien } else if (0 == strcmp(zonestr, tzname[1])) {
555b47f20dfSDavid E. O'Brien tm->tm_isdst = 1;
556b47f20dfSDavid E. O'Brien } else {
5573624c752SPedro F. Giffuni return (NULL);
558b47f20dfSDavid E. O'Brien }
559b47f20dfSDavid E. O'Brien buf += cp - buf;
560b47f20dfSDavid E. O'Brien }
561b47f20dfSDavid E. O'Brien }
562b47f20dfSDavid E. O'Brien break;
56340523da7SXin LI
56440523da7SXin LI case 'z':
56540523da7SXin LI {
56640523da7SXin LI int sign = 1;
56740523da7SXin LI
56840523da7SXin LI if (*buf != '+') {
56940523da7SXin LI if (*buf == '-')
57040523da7SXin LI sign = -1;
57140523da7SXin LI else
5723624c752SPedro F. Giffuni return (NULL);
57340523da7SXin LI }
57440523da7SXin LI
57540523da7SXin LI buf++;
57640523da7SXin LI i = 0;
57740523da7SXin LI for (len = 4; len > 0; len--) {
5783c87aa1dSDavid Chisnall if (isdigit_l((unsigned char)*buf, locale)) {
57940523da7SXin LI i *= 10;
58040523da7SXin LI i += *buf - '0';
58140523da7SXin LI buf++;
5825fca7e1fSAndrey A. Chernov } else if (len == 2) {
5835fca7e1fSAndrey A. Chernov i *= 100;
5845fca7e1fSAndrey A. Chernov break;
58540523da7SXin LI } else
5863624c752SPedro F. Giffuni return (NULL);
58740523da7SXin LI }
58840523da7SXin LI
5895fca7e1fSAndrey A. Chernov if (i > 1400 || (sign == -1 && i > 1200) ||
5905fca7e1fSAndrey A. Chernov (i % 100) >= 60)
5915fca7e1fSAndrey A. Chernov return (NULL);
59240523da7SXin LI tm->tm_hour -= sign * (i / 100);
59340523da7SXin LI tm->tm_min -= sign * (i % 100);
59440523da7SXin LI *GMTp = 1;
59540523da7SXin LI }
59640523da7SXin LI break;
597cd9dcb03SPedro F. Giffuni
598cd9dcb03SPedro F. Giffuni case 'n':
599cd9dcb03SPedro F. Giffuni case 't':
600cd9dcb03SPedro F. Giffuni while (isspace_l((unsigned char)*buf, locale))
601cd9dcb03SPedro F. Giffuni buf++;
602cd9dcb03SPedro F. Giffuni break;
603097c578cSAndrey A. Chernov
604097c578cSAndrey A. Chernov default:
605097c578cSAndrey A. Chernov return (NULL);
606d7641983SJoerg Wunsch }
607d7641983SJoerg Wunsch }
6082e465587SPedro F. Giffuni
609eb144aa0SYuri Pankov if (century != -1 || year != -1) {
610eb144aa0SYuri Pankov if (year == -1)
611eb144aa0SYuri Pankov year = 0;
612eb144aa0SYuri Pankov if (century == -1) {
613eb144aa0SYuri Pankov if (year < 69)
614eb144aa0SYuri Pankov year += 100;
615eb144aa0SYuri Pankov } else
616eb144aa0SYuri Pankov year += century * 100 - TM_YEAR_BASE;
617eb144aa0SYuri Pankov tm->tm_year = year;
618eb144aa0SYuri Pankov }
619eb144aa0SYuri Pankov
6202e465587SPedro F. Giffuni if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
6212e465587SPedro F. Giffuni if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
6222e465587SPedro F. Giffuni (FLAG_MONTH | FLAG_MDAY)) {
6232e465587SPedro F. Giffuni tm->tm_yday = start_of_month[isleap(tm->tm_year +
6242e465587SPedro F. Giffuni TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
6252e465587SPedro F. Giffuni flags |= FLAG_YDAY;
6262e465587SPedro F. Giffuni } else if (day_offset != -1) {
62762b939ebSAndrey A. Chernov int tmpwday, tmpyday, fwo;
62862b939ebSAndrey A. Chernov
62962b939ebSAndrey A. Chernov fwo = first_wday_of(tm->tm_year + TM_YEAR_BASE);
63062b939ebSAndrey A. Chernov /* No incomplete week (week 0). */
63162b939ebSAndrey A. Chernov if (week_offset == 0 && fwo == day_offset)
63262b939ebSAndrey A. Chernov return (NULL);
63362b939ebSAndrey A. Chernov
6342e465587SPedro F. Giffuni /* Set the date to the first Sunday (or Monday)
6352e465587SPedro F. Giffuni * of the specified week of the year.
6362e465587SPedro F. Giffuni */
63762b939ebSAndrey A. Chernov tmpwday = (flags & FLAG_WDAY) ? tm->tm_wday :
63862b939ebSAndrey A. Chernov day_offset;
63962b939ebSAndrey A. Chernov tmpyday = (7 - fwo + day_offset) % 7 +
64062b939ebSAndrey A. Chernov (week_offset - 1) * 7 +
64162b939ebSAndrey A. Chernov (tmpwday - day_offset + 7) % 7;
64262b939ebSAndrey A. Chernov /* Impossible yday for incomplete week (week 0). */
64362b939ebSAndrey A. Chernov if (tmpyday < 0) {
64462b939ebSAndrey A. Chernov if (flags & FLAG_WDAY)
64562b939ebSAndrey A. Chernov return (NULL);
64662b939ebSAndrey A. Chernov tmpyday = 0;
6472e465587SPedro F. Giffuni }
64862b939ebSAndrey A. Chernov tm->tm_yday = tmpyday;
6492e465587SPedro F. Giffuni flags |= FLAG_YDAY;
6502e465587SPedro F. Giffuni }
651ce1d331eSPedro F. Giffuni }
652cb7a4779SDavid E. O'Brien
6532e465587SPedro F. Giffuni if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
6542e465587SPedro F. Giffuni if (!(flags & FLAG_MONTH)) {
6552e465587SPedro F. Giffuni i = 0;
6562e465587SPedro F. Giffuni while (tm->tm_yday >=
6572e465587SPedro F. Giffuni start_of_month[isleap(tm->tm_year +
6582e465587SPedro F. Giffuni TM_YEAR_BASE)][i])
6592e465587SPedro F. Giffuni i++;
6602e465587SPedro F. Giffuni if (i > 12) {
6612e465587SPedro F. Giffuni i = 1;
6622e465587SPedro F. Giffuni tm->tm_yday -=
6632e465587SPedro F. Giffuni start_of_month[isleap(tm->tm_year +
6642e465587SPedro F. Giffuni TM_YEAR_BASE)][12];
6652e465587SPedro F. Giffuni tm->tm_year++;
6662e465587SPedro F. Giffuni }
6672e465587SPedro F. Giffuni tm->tm_mon = i - 1;
6682e465587SPedro F. Giffuni flags |= FLAG_MONTH;
6692e465587SPedro F. Giffuni }
6702e465587SPedro F. Giffuni if (!(flags & FLAG_MDAY)) {
6712e465587SPedro F. Giffuni tm->tm_mday = tm->tm_yday -
6722e465587SPedro F. Giffuni start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
6732e465587SPedro F. Giffuni [tm->tm_mon] + 1;
6742e465587SPedro F. Giffuni flags |= FLAG_MDAY;
6752e465587SPedro F. Giffuni }
6762e465587SPedro F. Giffuni if (!(flags & FLAG_WDAY)) {
6772e465587SPedro F. Giffuni i = 0;
6782e465587SPedro F. Giffuni wday_offset = first_wday_of(tm->tm_year);
6792e465587SPedro F. Giffuni while (i++ <= tm->tm_yday) {
6802e465587SPedro F. Giffuni if (wday_offset++ >= 6)
6812e465587SPedro F. Giffuni wday_offset = 0;
6822e465587SPedro F. Giffuni }
6832e465587SPedro F. Giffuni tm->tm_wday = wday_offset;
6842e465587SPedro F. Giffuni flags |= FLAG_WDAY;
6852e465587SPedro F. Giffuni }
6862e465587SPedro F. Giffuni }
6872e465587SPedro F. Giffuni
6882e465587SPedro F. Giffuni return ((char *)buf);
6892e465587SPedro F. Giffuni }
690c1cd488cSPedro F. Giffuni
691cb7a4779SDavid E. O'Brien char *
strptime_l(const char * __restrict buf,const char * __restrict fmt,struct tm * __restrict tm,locale_t loc)6923c87aa1dSDavid Chisnall strptime_l(const char * __restrict buf, const char * __restrict fmt,
6933c87aa1dSDavid Chisnall struct tm * __restrict tm, locale_t loc)
694cb7a4779SDavid E. O'Brien {
695cb7a4779SDavid E. O'Brien char *ret;
696fe71e0b8SMike Makonnen int gmt;
6973c87aa1dSDavid Chisnall FIX_LOCALE(loc);
698cb7a4779SDavid E. O'Brien
699fe71e0b8SMike Makonnen gmt = 0;
7003c87aa1dSDavid Chisnall ret = _strptime(buf, fmt, tm, &gmt, loc);
7016c688436SMike Makonnen if (ret && gmt) {
7026c688436SMike Makonnen time_t t = timegm(tm);
703420bc6ceSAndrey A. Chernov
704b47f20dfSDavid E. O'Brien localtime_r(&t, tm);
705b47f20dfSDavid E. O'Brien }
706cb7a4779SDavid E. O'Brien
707fe71e0b8SMike Makonnen return (ret);
708d7641983SJoerg Wunsch }
7092e465587SPedro F. Giffuni
7103c87aa1dSDavid Chisnall char *
strptime(const char * __restrict buf,const char * __restrict fmt,struct tm * __restrict tm)7113c87aa1dSDavid Chisnall strptime(const char * __restrict buf, const char * __restrict fmt,
7123c87aa1dSDavid Chisnall struct tm * __restrict tm)
7133c87aa1dSDavid Chisnall {
7143c87aa1dSDavid Chisnall return strptime_l(buf, fmt, tm, __get_locale());
7153c87aa1dSDavid Chisnall }
716