]> git.saurik.com Git - apple/libc.git/blob - stdtime/FreeBSD/strptime.c
2220c5dac2626479f3d2869f50360c1b2d6cd62d
[apple/libc.git] / stdtime / FreeBSD / strptime.c
1 /*-
2 * Copyright (c) 2014 Gary Mills
3 * Copyright 2011, Nexenta Systems, Inc. All rights reserved.
4 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
5 *
6 * Copyright (c) 2011 The FreeBSD Foundation
7 * All rights reserved.
8 * Portions of this software were developed by David Chisnall
9 * under sponsorship from the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer
18 * in the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
30 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * The views and conclusions contained in the software and documentation
34 * are those of the authors and should not be interpreted as representing
35 * official policies, either expressed or implied, of Powerdog Industries.
36 */
37
38 #include <sys/cdefs.h>
39 #ifndef lint
40 #ifndef NOID
41 static char copyright[] __unused =
42 "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
43 static char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
44 #endif /* !defined NOID */
45 #endif /* not lint */
46 __FBSDID("$FreeBSD$");
47
48 #include "xlocale_private.h"
49
50 #include "namespace.h"
51 #include <time.h>
52 #include <ctype.h>
53 #include <errno.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <pthread.h>
58 #include "un-namespace.h"
59 #include "libc_private.h"
60 #include "timelocal.h"
61 #include "tzfile.h"
62
63 time_t _mktime(struct tm *, const char *);
64
65 #define asizeof(a) (sizeof(a) / sizeof((a)[0]))
66
67 enum {CONVERT_NONE, CONVERT_GMT, CONVERT_ZONE};
68 enum week_kind { WEEK_U = 'U', WEEK_V = 'V', WEEK_W = 'W'};
69
70 #define _strptime(b,f,t,c,l) _strptime0(b,f,t,c,l,FLAG_NONE,0,WEEK_U)
71
72 #define FLAG_NONE 0x01
73 #define FLAG_YEAR 0x02
74 #define FLAG_MONTH 0x04
75 #define FLAG_YDAY 0x08
76 #define FLAG_MDAY 0x10
77 #define FLAG_WDAY 0x20
78 #define FLAG_WEEK 0x40
79 #define FLAG_CENTURY 0x100
80 #define FLAG_YEAR_IN_CENTURY 0x200
81
82 /*
83 * Calculate the week day of the first day of a year. Valid for
84 * the Gregorian calendar, which began Sept 14, 1752 in the UK
85 * and its colonies. Ref:
86 * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
87 */
88
89 static int
90 first_wday_of(int year)
91 {
92 return (((2 * (3 - (year / 100) % 4)) + (year % 100) +
93 ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);
94 }
95
96 static char *
97 _strptime0(const char *buf, const char *fmt, struct tm *tm, int *convp, locale_t locale, int flags, int week_number, enum week_kind week_kind)
98 {
99 char c;
100 const char *ptr;
101 int wday_offset;
102 int i, len;
103 int Ealternative, Oalternative;
104 const struct lc_time_T *tptr = __get_current_time_locale(locale);
105 static int start_of_month[2][13] = {
106 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
107 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
108 };
109
110 ptr = fmt;
111 while (*ptr != 0) {
112 c = *ptr++;
113
114 if (c != '%') {
115 if (isspace_l((unsigned char)c, locale))
116 while (*buf != 0 &&
117 isspace_l((unsigned char)*buf, locale))
118 buf++;
119 else if (c != *buf++)
120 return (NULL);
121 continue;
122 }
123
124 Ealternative = 0;
125 Oalternative = 0;
126 label:
127 c = *ptr++;
128 switch (c) {
129 case '%':
130 if (*buf++ != '%')
131 return (NULL);
132 break;
133
134 case '+':
135 buf = _strptime(buf, tptr->date_fmt, tm, convp, locale);
136 if (buf == NULL)
137 return (NULL);
138 flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
139 break;
140
141 case 'C':
142 if (!isdigit_l((unsigned char)*buf, locale))
143 return (NULL);
144
145 /* XXX This will break for 3-digit centuries. */
146 len = 2;
147 for (i = 0; len && *buf != 0 &&
148 isdigit_l((unsigned char)*buf, locale); buf++) {
149 i *= 10;
150 i += *buf - '0';
151 len--;
152 }
153 if (i < 19)
154 return (NULL);
155
156 if (flags & FLAG_YEAR_IN_CENTURY) {
157 tm->tm_year = i * 100 + (tm->tm_year % 100) - TM_YEAR_BASE;
158 flags &= ~FLAG_YEAR_IN_CENTURY;
159 } else {
160 tm->tm_year = i * 100 - TM_YEAR_BASE;
161 flags |= FLAG_YEAR;
162 flags |= FLAG_CENTURY;
163 }
164
165 break;
166
167 case 'c':
168 buf = _strptime(buf, tptr->c_fmt, tm, convp, locale);
169 if (buf == NULL)
170 return (NULL);
171 flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
172 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
173 break;
174
175 case 'D':
176 buf = _strptime(buf, "%m/%d/%y", tm, convp, locale);
177 if (buf == NULL)
178 return (NULL);
179 flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
180 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
181 break;
182
183 case 'E':
184 if (Ealternative || Oalternative)
185 break;
186 Ealternative++;
187 if (*ptr == '%') return (NULL);
188 goto label;
189
190 case 'O':
191 if (Ealternative || Oalternative)
192 break;
193 Oalternative++;
194 if (*ptr == '%') return (NULL);
195 goto label;
196
197 case 'F':
198 buf = _strptime(buf, "%Y-%m-%d", tm, convp, locale);
199 if (buf == NULL)
200 return (NULL);
201 flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
202 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
203 break;
204
205 case 'R':
206 buf = _strptime(buf, "%H:%M", tm, convp, locale);
207 if (buf == NULL)
208 return (NULL);
209 break;
210
211 case 'r':
212 buf = _strptime(buf, tptr->ampm_fmt, tm, convp, locale);
213 if (buf == NULL)
214 return (NULL);
215 break;
216
217 case 'T':
218 buf = _strptime(buf, "%H:%M:%S", tm, convp, locale);
219 if (buf == NULL)
220 return (NULL);
221 break;
222
223 case 'X':
224 buf = _strptime(buf, tptr->X_fmt, tm, convp, locale);
225 if (buf == NULL)
226 return (NULL);
227 break;
228
229 case 'x':
230 buf = _strptime(buf, tptr->x_fmt, tm, convp, locale);
231 if (buf == NULL)
232 return (NULL);
233 flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
234 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
235 break;
236
237 case 'j':
238 if (!isdigit_l((unsigned char)*buf, locale))
239 return (NULL);
240
241 len = 3;
242 for (i = 0; len && *buf != 0 &&
243 isdigit_l((unsigned char)*buf, locale); buf++){
244 i *= 10;
245 i += *buf - '0';
246 len--;
247 }
248 if (i < 1 || i > 366)
249 return (NULL);
250
251 tm->tm_yday = i - 1;
252 flags |= FLAG_YDAY;
253
254 break;
255
256 case 'M':
257 case 'S':
258 if (*buf == 0 ||
259 isspace_l((unsigned char)*buf, locale))
260 break;
261
262 if (!isdigit_l((unsigned char)*buf, locale))
263 return (NULL);
264
265 len = 2;
266 for (i = 0; len && *buf != 0 &&
267 isdigit_l((unsigned char)*buf, locale); buf++){
268 i *= 10;
269 i += *buf - '0';
270 len--;
271 }
272
273 if (c == 'M') {
274 if (i > 59)
275 return (NULL);
276 tm->tm_min = i;
277 } else {
278 if (i > 60)
279 return (NULL);
280 tm->tm_sec = i;
281 }
282
283 break;
284
285 case 'H':
286 case 'I':
287 case 'k':
288 case 'l':
289 /*
290 * Of these, %l is the only specifier explicitly
291 * documented as not being zero-padded. However,
292 * there is no harm in allowing zero-padding.
293 *
294 * XXX The %l specifier may gobble one too many
295 * digits if used incorrectly.
296 */
297 if (!isdigit_l((unsigned char)*buf, locale))
298 return (NULL);
299
300 len = 2;
301 for (i = 0; len && *buf != 0 &&
302 isdigit_l((unsigned char)*buf, locale); buf++) {
303 i *= 10;
304 i += *buf - '0';
305 len--;
306 }
307 if (c == 'H' || c == 'k') {
308 if (i > 23)
309 return (NULL);
310 } else if (i > 12)
311 return (NULL);
312
313 tm->tm_hour = i;
314
315 break;
316
317 case 'p':
318 /*
319 * XXX This is bogus if parsed before hour-related
320 * specifiers.
321 */
322 len = strlen(tptr->am);
323 if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
324 if (tm->tm_hour > 12)
325 return (NULL);
326 if (tm->tm_hour == 12)
327 tm->tm_hour = 0;
328 buf += len;
329 break;
330 }
331
332 len = strlen(tptr->pm);
333 if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
334 if (tm->tm_hour > 12)
335 return (NULL);
336 if (tm->tm_hour != 12)
337 tm->tm_hour += 12;
338 buf += len;
339 break;
340 }
341
342 return (NULL);
343
344 case 'A':
345 case 'a':
346 for (i = 0; i < asizeof(tptr->weekday); i++) {
347 len = strlen(tptr->weekday[i]);
348 if (strncasecmp_l(buf, tptr->weekday[i],
349 len, locale) == 0)
350 break;
351 len = strlen(tptr->wday[i]);
352 if (strncasecmp_l(buf, tptr->wday[i],
353 len, locale) == 0)
354 break;
355 }
356 if (i == asizeof(tptr->weekday))
357 return (NULL);
358
359 buf += len;
360 tm->tm_wday = i;
361 flags |= FLAG_WDAY;
362 break;
363
364 case 'U': /* Sunday week */
365 case 'V': /* ISO 8601 week */
366 case 'W': /* Monday week */
367 if (!isdigit_l((unsigned char)*buf, locale))
368 return (NULL);
369
370 len = 2;
371 for (i = 0; len && *buf != 0 &&
372 isdigit_l((unsigned char)*buf, locale); buf++) {
373 i *= 10;
374 i += *buf - '0';
375 len--;
376 }
377 if (i > 53)
378 return (NULL);
379 if (c == WEEK_V && i < 1)
380 return (NULL);
381
382 week_number = i;
383 week_kind = c;
384 flags |= FLAG_WEEK;
385
386 break;
387
388 case 'u': /* [1,7] */
389 case 'w': /* [0,6] */
390 if (!isdigit_l((unsigned char)*buf, locale))
391 return (NULL);
392
393 i = *buf - '0';
394 if (i > 6 + (c == 'u'))
395 return (NULL);
396 if (i == 7)
397 i = 0;
398
399 tm->tm_wday = i;
400 flags |= FLAG_WDAY;
401 buf++;
402
403 break;
404
405 case 'e':
406 /*
407 * With %e format, our strftime(3) adds a blank space
408 * before single digits.
409 */
410 if (*buf != 0 &&
411 isspace_l((unsigned char)*buf, locale))
412 buf++;
413 /* FALLTHROUGH */
414 case 'd':
415 /*
416 * The %e specifier was once explicitly documented as
417 * not being zero-padded but was later changed to
418 * equivalent to %d. There is no harm in allowing
419 * such padding.
420 *
421 * XXX The %e specifier may gobble one too many
422 * digits if used incorrectly.
423 */
424 /* Leading space is ok if date is single digit */
425 len = 2;
426 if (isspace_l((unsigned char)buf[0], locale) &&
427 isdigit_l((unsigned char)buf[1], locale) &&
428 !isdigit_l((unsigned char)buf[2], locale)) {
429 len = 1;
430 buf++;
431 }
432 if (!isdigit_l((unsigned char)*buf, locale))
433 return (NULL);
434
435 for (i = 0; len && *buf != 0 &&
436 isdigit_l((unsigned char)*buf, locale); buf++) {
437 i *= 10;
438 i += *buf - '0';
439 len--;
440 }
441 if (i > 31)
442 return (NULL);
443
444 tm->tm_mday = i;
445 flags |= FLAG_MDAY;
446
447 break;
448
449 case 'B':
450 case 'b':
451 case 'h':
452 for (i = 0; i < asizeof(tptr->month); i++) {
453 if (Oalternative) {
454 if (c == 'B') {
455 len = strlen(tptr->alt_month[i]);
456 if (strncasecmp_l(buf,
457 tptr->alt_month[i],
458 len, locale) == 0)
459 break;
460 }
461 } else {
462 len = strlen(tptr->month[i]);
463 if (strncasecmp_l(buf, tptr->month[i],
464 len, locale) == 0)
465 break;
466 }
467 }
468 /*
469 * Try the abbreviated month name if the full name
470 * wasn't found and Oalternative was not requested.
471 */
472 if (i == asizeof(tptr->month) && !Oalternative) {
473 for (i = 0; i < asizeof(tptr->month); i++) {
474 len = strlen(tptr->mon[i]);
475 if (strncasecmp_l(buf, tptr->mon[i],
476 len, locale) == 0)
477 break;
478 }
479 }
480 if (i == asizeof(tptr->month))
481 return (NULL);
482
483 tm->tm_mon = i;
484 buf += len;
485 flags |= FLAG_MONTH;
486
487 break;
488
489 case 'm':
490 if (!isdigit_l((unsigned char)*buf, locale))
491 return (NULL);
492
493 len = 2;
494 for (i = 0; len && *buf != 0 &&
495 isdigit_l((unsigned char)*buf, locale); buf++) {
496 i *= 10;
497 i += *buf - '0';
498 len--;
499 }
500 if (i < 1 || i > 12)
501 return (NULL);
502
503 tm->tm_mon = i - 1;
504 flags |= FLAG_MONTH;
505
506 break;
507
508 case 's':
509 {
510 char *cp;
511 int sverrno;
512 long n;
513 time_t t;
514
515 sverrno = errno;
516 errno = 0;
517 n = strtol_l(buf, &cp, 10, locale);
518 if (errno == ERANGE || (long)(t = n) != n) {
519 errno = sverrno;
520 return (NULL);
521 }
522 errno = sverrno;
523 buf = cp;
524 if (gmtime_r(&t, tm) == NULL)
525 return (NULL);
526 *convp = CONVERT_GMT;
527 flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |
528 FLAG_MDAY | FLAG_YEAR;
529 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
530 }
531 break;
532
533 case 'Y':
534 case 'y':
535 if (*buf == 0 ||
536 isspace_l((unsigned char)*buf, locale))
537 break;
538
539 if (!isdigit_l((unsigned char)*buf, locale))
540 return (NULL);
541
542 #if __DARWIN_UNIX03
543 if (c == 'Y') {
544 int savei = 0;
545 const char *savebuf = buf;
546 int64_t i64 = 0;
547 int overflow = 0;
548
549 for (len = 0; *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) {
550 i64 *= 10;
551 i64 += *buf - '0';
552 if (++len <= 4) {
553 savei = i64;
554 savebuf = buf + 1;
555 }
556 if (i64 > INT_MAX) {
557 overflow++;
558 break;
559 }
560 }
561 /*
562 * Conformance requires %Y to be more then 4
563 * digits. However, there are several cases
564 * where %Y is immediately followed by other
565 * digits values. So we do the conformance
566 * case first (as many digits as possible),
567 * and if we fail, we backup and try just 4
568 * digits for %Y.
569 */
570 if (len > 4 && !overflow) {
571 struct tm savetm = *tm;
572 int saveconv = *convp;
573 const char *saveptr = ptr;
574 char *ret;
575
576 if (i64 < 1900)
577 return 0;
578
579 tm->tm_year = i64 - 1900;
580
581 if (*buf != 0 && isspace_l((unsigned char)*buf, locale))
582 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, locale) && *ptr != '%')
583 ptr++;
584 ret = _strptime0(buf, ptr, tm, convp, locale, flags, week_number, week_kind);
585 if (ret) return ret;
586 /* Failed, so try 4-digit year */
587 *tm = savetm;
588 *convp = saveconv;
589 ptr = saveptr;
590 }
591 buf = savebuf;
592 i = savei;
593 } else {
594 len = 2;
595 #else /* !__DARWIN_UNIX03 */
596 len = (c == 'Y') ? 4 : 2;
597 #endif /* __DARWIN_UNIX03 */
598
599 for (i = 0; len && *buf != 0 &&
600 isdigit_l((unsigned char)*buf, locale); buf++) {
601 i *= 10;
602 i += *buf - '0';
603 len--;
604 }
605 #if __DARWIN_UNIX03
606 }
607 #endif /* __DARWIN_UNIX03 */
608
609 if (i < 0)
610 return (NULL);
611
612 if (c == 'Y'){
613 i -= TM_YEAR_BASE;
614 } else if (c == 'y' && flags & FLAG_CENTURY) {
615 i = tm->tm_year + (i % 100);
616 flags &= ~FLAG_CENTURY;
617 } else if (c == 'y'){
618 if (i < 69) i += 100;
619 flags |= FLAG_YEAR_IN_CENTURY;
620 }
621
622 tm->tm_year = i;
623 flags |= FLAG_YEAR;
624 if (c == 'Y'){
625 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
626 }
627
628 break;
629
630 case 'Z':
631 {
632 const char *cp;
633 size_t tzlen, len;
634
635 for (cp = buf; *cp &&
636 isupper_l((unsigned char)*cp, locale); ++cp) {
637 /*empty*/
638 }
639 len = cp - buf;
640 if (len == 3 && strncmp(buf, "GMT", 3) == 0) {
641 *convp = CONVERT_GMT;
642 buf += len;
643 break;
644 }
645
646 tzset();
647 tzlen = strlen(tzname[0]);
648 if (len == tzlen && strncmp(buf, tzname[0], tzlen) == 0) {
649 tm->tm_isdst = 0;
650 buf += len;
651 break;
652 }
653 tzlen = strlen(tzname[1]);
654 if (len == tzlen && strncmp(buf, tzname[1], tzlen) == 0) {
655 tm->tm_isdst = 1;
656 buf += len;
657 break;
658 }
659 return (NULL);
660 }
661
662 case 'z':
663 {
664 char sign;
665 int hr, min;
666 if ((buf[0] != '+' && buf[0] != '-')
667 || !isdigit_l((unsigned char)buf[1], locale)
668 || !isdigit_l((unsigned char)buf[2], locale)
669 || !isdigit_l((unsigned char)buf[3], locale)
670 || !isdigit_l((unsigned char)buf[4], locale))
671 return 0;
672 sscanf(buf, "%c%2d%2d", &sign, &hr, &min);
673 *convp = CONVERT_ZONE;
674 tm->tm_gmtoff = 60 * (60 * hr + min);
675 if (sign == '-')
676 tm->tm_gmtoff = -tm->tm_gmtoff;
677 buf += 5;
678 }
679 break;
680
681 case 'n':
682 case 't':
683 if (!isspace((unsigned char)*buf))
684 return 0;
685 while (isspace_l((unsigned char)*buf, locale))
686 buf++;
687 break;
688
689 default:
690 return (NULL);
691 }
692 }
693
694 if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
695 if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
696 (FLAG_MONTH | FLAG_MDAY)) {
697 tm->tm_yday = start_of_month[isleap(tm->tm_year +
698 TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
699 flags |= FLAG_YDAY;
700 } else if (flags & FLAG_WEEK){
701 if (!(flags & FLAG_WDAY)) {
702 tm->tm_wday = week_kind == WEEK_U ? TM_SUNDAY : TM_MONDAY;
703 flags |= FLAG_WDAY;
704 }
705
706 struct tm t = {0};
707 t.tm_mday = week_kind == WEEK_V ? 4 : 1;
708 t.tm_hour = 12; /* avoid any DST effects */
709 t.tm_year = tm->tm_year;
710 if (timegm(&t) == (time_t)-1) return 0;
711
712 int off = t.tm_wday;
713 int wday = tm->tm_wday;
714
715 if (week_kind != WEEK_U) {
716 off = (off + 6) % 7;
717 wday = (wday + 6) % 7;
718 }
719
720 if (week_kind == WEEK_V) {
721 t.tm_mday = 7 * week_number + wday - off - 3;
722 } else {
723 if(off == 0) off = 7;
724 t.tm_mday = 7 * week_number + wday - off + 1;
725 }
726 if (timegm(&t) == (time_t)-1) return 0;
727
728 tm->tm_yday = t.tm_yday;
729
730 flags |= FLAG_YDAY;
731 }
732 }
733
734 if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
735 if (!(flags & FLAG_MONTH)) {
736 i = 0;
737 while (tm->tm_yday >=
738 start_of_month[isleap(tm->tm_year +
739 TM_YEAR_BASE)][i])
740 i++;
741 if (i > 12) {
742 i = 1;
743 tm->tm_yday -=
744 start_of_month[isleap(tm->tm_year +
745 TM_YEAR_BASE)][12];
746 tm->tm_year++;
747 }
748 tm->tm_mon = i - 1;
749 flags |= FLAG_MONTH;
750 }
751 if (!(flags & FLAG_MDAY)) {
752 tm->tm_mday = tm->tm_yday -
753 start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
754 [tm->tm_mon] + 1;
755 flags |= FLAG_MDAY;
756 }
757 if (!(flags & FLAG_WDAY)) {
758 i = 0;
759 wday_offset = first_wday_of(tm->tm_year);
760 while (i++ <= tm->tm_yday) {
761 if (wday_offset++ >= 6)
762 wday_offset = 0;
763 }
764 tm->tm_wday = wday_offset;
765 flags |= FLAG_WDAY;
766 }
767 }
768
769 return ((char *)buf);
770 }
771
772
773 char *
774 strptime(const char * __restrict buf, const char * __restrict fmt,
775 struct tm * __restrict tm)
776 {
777 return strptime_l(buf, fmt, tm, __current_locale());
778 }
779
780 extern time_t timeoff(struct tm *, long);
781
782 char *
783 strptime_l(const char * __restrict buf, const char * __restrict fmt,
784 struct tm * __restrict tm, locale_t loc)
785 {
786 char *ret;
787 int conv;
788
789 NORMALIZE_LOCALE(loc);
790 conv = CONVERT_NONE;
791 tm->tm_zone = NULL;
792 ret = _strptime(buf, fmt, tm, &conv, loc);
793 if (ret) {
794 time_t t;
795
796 switch(conv) {
797 case CONVERT_GMT:
798 t = timegm(tm);
799 localtime_r(&t, tm);
800 break;
801 case CONVERT_ZONE:
802 {
803 long offset = tm->tm_gmtoff;
804 tm->tm_gmtoff = 0;
805 t = timeoff(tm, offset);
806 localtime_r(&t, tm);
807 break;
808 }
809 }
810 }
811
812 return (ret);
813 }