]> git.saurik.com Git - apple/libc.git/blob - stdtime/FreeBSD/strptime.c
Libc-1244.1.7.tar.gz
[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 if (tm->tm_hour > 12)
323 return (NULL);
324
325 len = strlen(tptr->am);
326 if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
327 if (tm->tm_hour == 12)
328 tm->tm_hour = 0;
329 buf += len;
330 break;
331 }
332
333 len = strlen(tptr->pm);
334 if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
335 if (tm->tm_hour != 12)
336 tm->tm_hour += 12;
337 buf += len;
338 break;
339 }
340
341 return (NULL);
342
343 case 'A':
344 case 'a':
345 for (i = 0; i < asizeof(tptr->weekday); i++) {
346 len = strlen(tptr->weekday[i]);
347 if (strncasecmp_l(buf, tptr->weekday[i],
348 len, locale) == 0)
349 break;
350 len = strlen(tptr->wday[i]);
351 if (strncasecmp_l(buf, tptr->wday[i],
352 len, locale) == 0)
353 break;
354 }
355 if (i == asizeof(tptr->weekday))
356 return (NULL);
357
358 buf += len;
359 tm->tm_wday = i;
360 flags |= FLAG_WDAY;
361 break;
362
363 case 'U': /* Sunday week */
364 case 'V': /* ISO 8601 week */
365 case 'W': /* Monday week */
366 if (!isdigit_l((unsigned char)*buf, locale))
367 return (NULL);
368
369 len = 2;
370 for (i = 0; len && *buf != 0 &&
371 isdigit_l((unsigned char)*buf, locale); buf++) {
372 i *= 10;
373 i += *buf - '0';
374 len--;
375 }
376 if (i > 53)
377 return (NULL);
378 if (c == WEEK_V && i < 1)
379 return (NULL);
380
381 week_number = i;
382 week_kind = c;
383 flags |= FLAG_WEEK;
384
385 break;
386
387 case 'u': /* [1,7] */
388 case 'w': /* [0,6] */
389 if (!isdigit_l((unsigned char)*buf, locale))
390 return (NULL);
391
392 i = *buf - '0';
393 if (i < 0 || i > 7 || (c == 'u' && i < 1) ||
394 (c == 'w' && i > 6))
395 return (NULL);
396
397 tm->tm_wday = i % 7;
398 flags |= FLAG_WDAY;
399 buf++;
400
401 break;
402
403 case 'e':
404 /*
405 * With %e format, our strftime(3) adds a blank space
406 * before single digits.
407 */
408 if (*buf != 0 &&
409 isspace_l((unsigned char)*buf, locale))
410 buf++;
411 /* FALLTHROUGH */
412 case 'd':
413 /*
414 * The %e specifier was once explicitly documented as
415 * not being zero-padded but was later changed to
416 * equivalent to %d. There is no harm in allowing
417 * such padding.
418 *
419 * XXX The %e specifier may gobble one too many
420 * digits if used incorrectly.
421 */
422 /* Leading space is ok if date is single digit */
423 len = 2;
424 if (isspace_l((unsigned char)buf[0], locale) &&
425 isdigit_l((unsigned char)buf[1], locale) &&
426 !isdigit_l((unsigned char)buf[2], locale)) {
427 len = 1;
428 buf++;
429 }
430 if (!isdigit_l((unsigned char)*buf, locale))
431 return (NULL);
432
433 for (i = 0; len && *buf != 0 &&
434 isdigit_l((unsigned char)*buf, locale); buf++) {
435 i *= 10;
436 i += *buf - '0';
437 len--;
438 }
439 if (i > 31)
440 return (NULL);
441
442 tm->tm_mday = i;
443 flags |= FLAG_MDAY;
444
445 break;
446
447 case 'B':
448 case 'b':
449 case 'h':
450 for (i = 0; i < asizeof(tptr->month); i++) {
451 if (Oalternative) {
452 if (c == 'B') {
453 len = strlen(tptr->alt_month[i]);
454 if (strncasecmp_l(buf,
455 tptr->alt_month[i],
456 len, locale) == 0)
457 break;
458 }
459 } else {
460 len = strlen(tptr->month[i]);
461 if (strncasecmp_l(buf, tptr->month[i],
462 len, locale) == 0)
463 break;
464 }
465 }
466 /*
467 * Try the abbreviated month name if the full name
468 * wasn't found and Oalternative was not requested.
469 */
470 if (i == asizeof(tptr->month) && !Oalternative) {
471 for (i = 0; i < asizeof(tptr->month); i++) {
472 len = strlen(tptr->mon[i]);
473 if (strncasecmp_l(buf, tptr->mon[i],
474 len, locale) == 0)
475 break;
476 }
477 }
478 if (i == asizeof(tptr->month))
479 return (NULL);
480
481 tm->tm_mon = i;
482 buf += len;
483 flags |= FLAG_MONTH;
484
485 break;
486
487 case 'm':
488 if (!isdigit_l((unsigned char)*buf, locale))
489 return (NULL);
490
491 len = 2;
492 for (i = 0; len && *buf != 0 &&
493 isdigit_l((unsigned char)*buf, locale); buf++) {
494 i *= 10;
495 i += *buf - '0';
496 len--;
497 }
498 if (i < 1 || i > 12)
499 return (NULL);
500
501 tm->tm_mon = i - 1;
502 flags |= FLAG_MONTH;
503
504 break;
505
506 case 's':
507 {
508 char *cp;
509 int sverrno;
510 long n;
511 time_t t;
512
513 sverrno = errno;
514 errno = 0;
515 n = strtol_l(buf, &cp, 10, locale);
516 if (errno == ERANGE || (long)(t = n) != n) {
517 errno = sverrno;
518 return (NULL);
519 }
520 errno = sverrno;
521 buf = cp;
522 if (gmtime_r(&t, tm) == NULL)
523 return (NULL);
524 *convp = CONVERT_GMT;
525 flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |
526 FLAG_MDAY | FLAG_YEAR;
527 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
528 }
529 break;
530
531 case 'Y':
532 case 'y':
533 if (*buf == 0 ||
534 isspace_l((unsigned char)*buf, locale))
535 break;
536
537 if (!isdigit_l((unsigned char)*buf, locale))
538 return (NULL);
539
540 #if __DARWIN_UNIX03
541 if (c == 'Y') {
542 int savei = 0;
543 const char *savebuf = buf;
544 int64_t i64 = 0;
545 int overflow = 0;
546
547 for (len = 0; *buf != 0 && isdigit_l((unsigned char)*buf, locale); buf++) {
548 i64 *= 10;
549 i64 += *buf - '0';
550 if (++len <= 4) {
551 savei = i64;
552 savebuf = buf + 1;
553 }
554 if (i64 > INT_MAX) {
555 overflow++;
556 break;
557 }
558 }
559 /*
560 * Conformance requires %Y to be more then 4
561 * digits. However, there are several cases
562 * where %Y is immediately followed by other
563 * digits values. So we do the conformance
564 * case first (as many digits as possible),
565 * and if we fail, we backup and try just 4
566 * digits for %Y.
567 */
568 if (len > 4 && !overflow) {
569 struct tm savetm = *tm;
570 int saveconv = *convp;
571 const char *saveptr = ptr;
572 char *ret;
573
574 if (i64 < 1900)
575 return 0;
576
577 tm->tm_year = i64 - 1900;
578
579 if (*buf != 0 && isspace_l((unsigned char)*buf, locale))
580 while (*ptr != 0 && !isspace_l((unsigned char)*ptr, locale) && *ptr != '%')
581 ptr++;
582 ret = _strptime0(buf, ptr, tm, convp, locale, flags, week_number, week_kind);
583 if (ret) return ret;
584 /* Failed, so try 4-digit year */
585 *tm = savetm;
586 *convp = saveconv;
587 ptr = saveptr;
588 }
589 buf = savebuf;
590 i = savei;
591 } else {
592 len = 2;
593 #else /* !__DARWIN_UNIX03 */
594 len = (c == 'Y') ? 4 : 2;
595 #endif /* __DARWIN_UNIX03 */
596
597 for (i = 0; len && *buf != 0 &&
598 isdigit_l((unsigned char)*buf, locale); buf++) {
599 i *= 10;
600 i += *buf - '0';
601 len--;
602 }
603 #if __DARWIN_UNIX03
604 }
605 #endif /* __DARWIN_UNIX03 */
606
607 if (i < 0)
608 return (NULL);
609
610 if (c == 'Y'){
611 i -= TM_YEAR_BASE;
612 } else if (c == 'y' && flags & FLAG_CENTURY) {
613 i = tm->tm_year + (i % 100);
614 flags &= ~FLAG_CENTURY;
615 } else if (c == 'y'){
616 if (i < 69) i += 100;
617 flags |= FLAG_YEAR_IN_CENTURY;
618 }
619
620 tm->tm_year = i;
621 flags |= FLAG_YEAR;
622 if (c == 'Y'){
623 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
624 }
625
626 break;
627
628 case 'Z':
629 {
630 const char *cp;
631 size_t tzlen, len;
632
633 for (cp = buf; *cp &&
634 isupper_l((unsigned char)*cp, locale); ++cp) {
635 /*empty*/
636 }
637 len = cp - buf;
638 if (len == 3 && strncmp(buf, "GMT", 3) == 0) {
639 *convp = CONVERT_GMT;
640 buf += len;
641 break;
642 }
643
644 tzset();
645 tzlen = strlen(tzname[0]);
646 if (len == tzlen && strncmp(buf, tzname[0], tzlen) == 0) {
647 tm->tm_isdst = 0;
648 buf += len;
649 break;
650 }
651 tzlen = strlen(tzname[1]);
652 if (len == tzlen && strncmp(buf, tzname[1], tzlen) == 0) {
653 tm->tm_isdst = 1;
654 buf += len;
655 break;
656 }
657 return (NULL);
658 }
659
660 case 'z':
661 {
662 char sign;
663 int hr, min;
664 if ((buf[0] != '+' && buf[0] != '-')
665 || !isdigit_l((unsigned char)buf[1], locale)
666 || !isdigit_l((unsigned char)buf[2], locale)
667 || !isdigit_l((unsigned char)buf[3], locale)
668 || !isdigit_l((unsigned char)buf[4], locale))
669 return 0;
670 sscanf(buf, "%c%2d%2d", &sign, &hr, &min);
671 *convp = CONVERT_ZONE;
672 tm->tm_gmtoff = 60 * (60 * hr + min);
673 if (sign == '-')
674 tm->tm_gmtoff = -tm->tm_gmtoff;
675 buf += 5;
676 }
677 break;
678
679 case 'n':
680 case 't':
681 if (!isspace((unsigned char)*buf))
682 return 0;
683 while (isspace_l((unsigned char)*buf, locale))
684 buf++;
685 break;
686
687 default:
688 return (NULL);
689 }
690 }
691
692 if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
693 if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
694 (FLAG_MONTH | FLAG_MDAY)) {
695 tm->tm_yday = start_of_month[isleap(tm->tm_year +
696 TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
697 flags |= FLAG_YDAY;
698 } else if (flags & FLAG_WEEK){
699 int day_offset = week_kind == WEEK_U ? TM_SUNDAY : TM_MONDAY;
700 int fwo = first_wday_of(tm->tm_year + TM_YEAR_BASE);
701
702 /* No incomplete week (week 0). */
703 if (week_number == 0 && fwo == day_offset)
704 return (NULL);
705
706 if (!(flags & FLAG_WDAY)) {
707 /*
708 * Set the date to the first Sunday (or Monday)
709 * of the specified week of the year.
710 */
711 tm->tm_wday = day_offset;
712 flags |= FLAG_WDAY;
713 }
714
715 /*
716 * Start our yday at the first day of the relevant week type.
717 */
718 int tmpyday = (7 - fwo + day_offset) % 7;
719
720 /*
721 * ISO Weeks start counting from the first week with at least
722 * four days. If our first week had that, subtract off a week.
723 */
724 if (week_kind == WEEK_V && fwo > TM_MONDAY && fwo <= TM_THURSDAY) {
725 tmpyday -= 7;
726 }
727 /* Advance the relevant number of weeks */
728 tmpyday += (week_number - 1) * 7;
729 /* And go to the right day of week */
730 tmpyday += (tm->tm_wday - day_offset + 7) % 7;
731
732 /* Impossible yday for incomplete week (week 0). */
733 if (tmpyday < 0) {
734 if (flags & FLAG_WDAY)
735 return (NULL);
736 tmpyday = 0;
737 }
738 tm->tm_yday = tmpyday;
739 flags |= FLAG_YDAY;
740 }
741 }
742
743 if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
744 if (!(flags & FLAG_MONTH)) {
745 i = 0;
746 while (tm->tm_yday >=
747 start_of_month[isleap(tm->tm_year +
748 TM_YEAR_BASE)][i])
749 i++;
750 if (i > 12) {
751 i = 1;
752 tm->tm_yday -=
753 start_of_month[isleap(tm->tm_year +
754 TM_YEAR_BASE)][12];
755 tm->tm_year++;
756 }
757 tm->tm_mon = i - 1;
758 flags |= FLAG_MONTH;
759 }
760 if (!(flags & FLAG_MDAY)) {
761 tm->tm_mday = tm->tm_yday -
762 start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
763 [tm->tm_mon] + 1;
764 flags |= FLAG_MDAY;
765 }
766 if (!(flags & FLAG_WDAY)) {
767 wday_offset = first_wday_of(tm->tm_year + TM_YEAR_BASE);
768 tm->tm_wday = (wday_offset + tm->tm_yday) % 7;
769 flags |= FLAG_WDAY;
770 }
771 }
772
773 return ((char *)buf);
774 }
775
776
777 char *
778 strptime(const char * __restrict buf, const char * __restrict fmt,
779 struct tm * __restrict tm)
780 {
781 return strptime_l(buf, fmt, tm, __current_locale());
782 }
783
784 extern time_t timeoff(struct tm *, long);
785
786 char *
787 strptime_l(const char * __restrict buf, const char * __restrict fmt,
788 struct tm * __restrict tm, locale_t loc)
789 {
790 char *ret;
791 int conv;
792
793 NORMALIZE_LOCALE(loc);
794 conv = CONVERT_NONE;
795 tm->tm_zone = NULL;
796 ret = _strptime(buf, fmt, tm, &conv, loc);
797 if (ret) {
798 time_t t;
799
800 switch(conv) {
801 case CONVERT_GMT:
802 t = timegm(tm);
803 localtime_r(&t, tm);
804 break;
805 case CONVERT_ZONE:
806 {
807 long offset = tm->tm_gmtoff;
808 tm->tm_gmtoff = 0;
809 t = timeoff(tm, offset);
810 localtime_r(&t, tm);
811 break;
812 }
813 }
814 }
815
816 return (ret);
817 }