]> git.saurik.com Git - apple/libc.git/blob - stdtime/FreeBSD/strptime.c
Libc-1353.11.2.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 inline bool is_plus(char c) {
97 return c == '+';
98 }
99
100 static inline bool is_minus(char c) {
101 return c == '-';
102 }
103
104 static inline bool is_zero(char c) {
105 return c == '0';
106 }
107
108 static char *
109 _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)
110 {
111 char c;
112 const char *ptr;
113 int wday_offset;
114 int i, len;
115 bool negative;
116 int Ealternative, Oalternative;
117 const struct lc_time_T *tptr = __get_current_time_locale(locale);
118 static int start_of_month[2][13] = {
119 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
120 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
121 };
122
123 ptr = fmt;
124 while (*ptr != 0) {
125 int field_width = 0;
126 c = *ptr++;
127
128 if (c != '%') {
129 if (isspace_l((unsigned char)c, locale))
130 while (*buf != 0 &&
131 isspace_l((unsigned char)*buf, locale))
132 buf++;
133 else if (c != *buf++)
134 return (NULL);
135 continue;
136 }
137
138 Ealternative = 0;
139 Oalternative = 0;
140 label:
141 c = *ptr++;
142 if (is_zero(c)) {
143 // Leading '0' is to be ignored.
144 c = *ptr++;
145 } else if (is_plus(c)) {
146 // POSIX sats leading '+' should be ignored, but FreeBSD interprets
147 // "%+" to mean locale-specific date format. Try to handle both by
148 // checking the next character.
149 char next = *ptr;
150 if (next != '\0' && next != '%' && !isspace_l(next, locale)) {
151 // Use POSIX interpretation.
152 c = *ptr++;
153 }
154 }
155
156 if (isdigit_l(c, locale)) {
157 // Field width
158 field_width = c - '0';
159 while (*ptr != '\0' && isdigit_l(*ptr, locale)) {
160 field_width *= 10;
161 field_width += *ptr++ - '0';
162 }
163 c = *ptr++;
164 }
165 switch (c) {
166 case '%':
167 if (*buf++ != '%')
168 return (NULL);
169 break;
170
171 case '+':
172 buf = _strptime(buf, tptr->date_fmt, tm, convp, locale);
173 if (buf == NULL)
174 return (NULL);
175 flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
176 break;
177
178 case 'C':
179 if (!isdigit_l((unsigned char)*buf, locale) && !is_plus(*buf) && !is_minus(*buf))
180 return (NULL);
181
182 /* XXX This will break for 3-digit centuries. */
183 negative = false;
184 len = field_width ? field_width : 2;
185 if (is_plus(*buf)) {
186 len--;
187 buf++;
188 } else if (is_minus(*buf)) {
189 len--;
190 negative = true;
191 buf++;
192 }
193 for (i = 0; len && *buf != 0 &&
194 isdigit_l((unsigned char)*buf, locale); buf++) {
195 i *= 10;
196 i += *buf - '0';
197 len--;
198 }
199
200 if (negative) {
201 i = -i;
202 }
203
204 if (flags & FLAG_YEAR_IN_CENTURY) {
205 tm->tm_year = i * 100 + (tm->tm_year % 100) - TM_YEAR_BASE;
206 flags &= ~FLAG_YEAR_IN_CENTURY;
207 } else {
208 tm->tm_year = i * 100 - TM_YEAR_BASE;
209 flags |= FLAG_YEAR;
210 flags |= FLAG_CENTURY;
211 }
212
213 break;
214
215 case 'c':
216 buf = _strptime(buf, tptr->c_fmt, tm, convp, locale);
217 if (buf == NULL)
218 return (NULL);
219 flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
220 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
221 break;
222
223 case 'D':
224 buf = _strptime(buf, "%m/%d/%y", tm, convp, locale);
225 if (buf == NULL)
226 return (NULL);
227 flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
228 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
229 break;
230
231 case 'E':
232 if (Ealternative || Oalternative)
233 break;
234 Ealternative++;
235 if (*ptr == '%') return (NULL);
236 goto label;
237
238 case 'O':
239 if (Ealternative || Oalternative)
240 break;
241 Oalternative++;
242 if (*ptr == '%') return (NULL);
243 goto label;
244
245 case 'F':
246 buf = _strptime(buf, "%Y-%m-%d", tm, convp, locale);
247 if (buf == NULL)
248 return (NULL);
249 flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
250 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
251 break;
252
253 case 'R':
254 buf = _strptime(buf, "%H:%M", tm, convp, locale);
255 if (buf == NULL)
256 return (NULL);
257 break;
258
259 case 'r':
260 buf = _strptime(buf, tptr->ampm_fmt, tm, convp, locale);
261 if (buf == NULL)
262 return (NULL);
263 break;
264
265 case 'T':
266 buf = _strptime(buf, "%H:%M:%S", tm, convp, locale);
267 if (buf == NULL)
268 return (NULL);
269 break;
270
271 case 'X':
272 buf = _strptime(buf, tptr->X_fmt, tm, convp, locale);
273 if (buf == NULL)
274 return (NULL);
275 break;
276
277 case 'x':
278 buf = _strptime(buf, tptr->x_fmt, tm, convp, locale);
279 if (buf == NULL)
280 return (NULL);
281 flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
282 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
283 break;
284
285 case 'j':
286 if (!isdigit_l((unsigned char)*buf, locale))
287 return (NULL);
288
289 len = field_width ? field_width : 3;
290 for (i = 0; len && *buf != 0 &&
291 isdigit_l((unsigned char)*buf, locale); buf++){
292 i *= 10;
293 i += *buf - '0';
294 len--;
295 }
296 if (i < 1 || i > 366)
297 return (NULL);
298
299 tm->tm_yday = i - 1;
300 flags |= FLAG_YDAY;
301
302 break;
303
304 case 'M':
305 case 'S':
306 if (*buf == 0 ||
307 isspace_l((unsigned char)*buf, locale))
308 break;
309
310 if (!isdigit_l((unsigned char)*buf, locale))
311 return (NULL);
312
313 len = field_width ? field_width : 2;
314 for (i = 0; len && *buf != 0 &&
315 isdigit_l((unsigned char)*buf, locale); buf++){
316 i *= 10;
317 i += *buf - '0';
318 len--;
319 }
320
321 if (c == 'M') {
322 if (i > 59)
323 return (NULL);
324 tm->tm_min = i;
325 } else {
326 if (i > 60)
327 return (NULL);
328 tm->tm_sec = i;
329 }
330
331 break;
332
333 case 'H':
334 case 'I':
335 case 'k':
336 case 'l':
337 /*
338 * Of these, %l is the only specifier explicitly
339 * documented as not being zero-padded. However,
340 * there is no harm in allowing zero-padding.
341 *
342 * XXX The %l specifier may gobble one too many
343 * digits if used incorrectly.
344 */
345 if (!isdigit_l((unsigned char)*buf, locale))
346 return (NULL);
347
348 len = field_width ? field_width : 2;
349 for (i = 0; len && *buf != 0 &&
350 isdigit_l((unsigned char)*buf, locale); buf++) {
351 i *= 10;
352 i += *buf - '0';
353 len--;
354 }
355 if (c == 'H' || c == 'k') {
356 if (i > 23)
357 return (NULL);
358 } else if (i > 12)
359 return (NULL);
360
361 tm->tm_hour = i;
362
363 break;
364
365 case 'p':
366 /*
367 * XXX This is bogus if parsed before hour-related
368 * specifiers.
369 */
370 if (tm->tm_hour > 12)
371 return (NULL);
372
373 len = (int)strlen(tptr->am);
374 if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
375 if (tm->tm_hour == 12)
376 tm->tm_hour = 0;
377 buf += len;
378 break;
379 }
380
381 len = (int)strlen(tptr->pm);
382 if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
383 if (tm->tm_hour != 12)
384 tm->tm_hour += 12;
385 buf += len;
386 break;
387 }
388
389 return (NULL);
390
391 case 'A':
392 case 'a':
393 for (i = 0; i < asizeof(tptr->weekday); i++) {
394 len = (int)strlen(tptr->weekday[i]);
395 if (strncasecmp_l(buf, tptr->weekday[i],
396 len, locale) == 0)
397 break;
398 len = (int)strlen(tptr->wday[i]);
399 if (strncasecmp_l(buf, tptr->wday[i],
400 len, locale) == 0)
401 break;
402 }
403 if (i == asizeof(tptr->weekday))
404 return (NULL);
405
406 buf += len;
407 tm->tm_wday = i;
408 flags |= FLAG_WDAY;
409 break;
410
411 case 'U': /* Sunday week */
412 case 'V': /* ISO 8601 week */
413 case 'W': /* Monday week */
414 if (!isdigit_l((unsigned char)*buf, locale))
415 return (NULL);
416
417 len = field_width ? field_width : 2;
418 for (i = 0; len && *buf != 0 &&
419 isdigit_l((unsigned char)*buf, locale); buf++) {
420 i *= 10;
421 i += *buf - '0';
422 len--;
423 }
424 if (i > 53)
425 return (NULL);
426 if (c == WEEK_V && i < 1)
427 return (NULL);
428
429 week_number = i;
430 week_kind = c;
431 flags |= FLAG_WEEK;
432
433 break;
434
435 case 'u': /* [1,7] */
436 case 'w': /* [0,6] */
437 if (!isdigit_l((unsigned char)*buf, locale))
438 return (NULL);
439
440 i = *buf - '0';
441 if (i < 0 || i > 7 || (c == 'u' && i < 1) ||
442 (c == 'w' && i > 6))
443 return (NULL);
444
445 tm->tm_wday = i % 7;
446 flags |= FLAG_WDAY;
447 buf++;
448
449 break;
450
451 case 'e':
452 /*
453 * With %e format, our strftime(3) adds a blank space
454 * before single digits.
455 */
456 if (*buf != 0 &&
457 isspace_l((unsigned char)*buf, locale))
458 buf++;
459 /* FALLTHROUGH */
460 case 'd':
461 /*
462 * The %e specifier was once explicitly documented as
463 * not being zero-padded but was later changed to
464 * equivalent to %d. There is no harm in allowing
465 * such padding.
466 *
467 * XXX The %e specifier may gobble one too many
468 * digits if used incorrectly.
469 */
470 /* Leading space is ok if date is single digit */
471 len = field_width ? field_width : 2;
472 if (isspace_l((unsigned char)buf[0], locale) &&
473 isdigit_l((unsigned char)buf[1], locale) &&
474 !isdigit_l((unsigned char)buf[2], locale)) {
475 len = 1;
476 buf++;
477 }
478 if (!isdigit_l((unsigned char)*buf, locale))
479 return (NULL);
480
481 for (i = 0; len && *buf != 0 &&
482 isdigit_l((unsigned char)*buf, locale); buf++) {
483 i *= 10;
484 i += *buf - '0';
485 len--;
486 }
487 if (i > 31)
488 return (NULL);
489
490 tm->tm_mday = i;
491 flags |= FLAG_MDAY;
492
493 break;
494
495 case 'B':
496 case 'b':
497 case 'h':
498 for (i = 0; i < asizeof(tptr->month); i++) {
499 if (Oalternative) {
500 if (c == 'B') {
501 len = (int)strlen(tptr->alt_month[i]);
502 if (strncasecmp_l(buf,
503 tptr->alt_month[i],
504 len, locale) == 0)
505 break;
506 }
507 } else {
508 len = (int)strlen(tptr->month[i]);
509 if (strncasecmp_l(buf, tptr->month[i],
510 len, locale) == 0)
511 break;
512 }
513 }
514 /*
515 * Try the abbreviated month name if the full name
516 * wasn't found and Oalternative was not requested.
517 */
518 if (i == asizeof(tptr->month) && !Oalternative) {
519 for (i = 0; i < asizeof(tptr->month); i++) {
520 len = (int)strlen(tptr->mon[i]);
521 if (strncasecmp_l(buf, tptr->mon[i],
522 len, locale) == 0)
523 break;
524 }
525 }
526 if (i == asizeof(tptr->month))
527 return (NULL);
528
529 tm->tm_mon = i;
530 buf += len;
531 flags |= FLAG_MONTH;
532
533 break;
534
535 case 'm':
536 if (!isdigit_l((unsigned char)*buf, locale))
537 return (NULL);
538
539 len = field_width ? field_width : 2;
540 for (i = 0; len && *buf != 0 &&
541 isdigit_l((unsigned char)*buf, locale); buf++) {
542 i *= 10;
543 i += *buf - '0';
544 len--;
545 }
546 if (i < 1 || i > 12)
547 return (NULL);
548
549 tm->tm_mon = i - 1;
550 flags |= FLAG_MONTH;
551
552 break;
553
554 case 's':
555 {
556 char *cp;
557 int sverrno;
558 long n;
559 time_t t;
560
561 sverrno = errno;
562 errno = 0;
563 n = strtol_l(buf, &cp, 10, locale);
564 if (errno == ERANGE || (long)(t = n) != n) {
565 errno = sverrno;
566 return (NULL);
567 }
568 errno = sverrno;
569 buf = cp;
570 if (gmtime_r(&t, tm) == NULL)
571 return (NULL);
572 *convp = CONVERT_GMT;
573 flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |
574 FLAG_MDAY | FLAG_YEAR;
575 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
576 }
577 break;
578
579 case 'Y':
580 case 'y':
581 if (*buf == 0 ||
582 isspace_l((unsigned char)*buf, locale))
583 break;
584
585 if (!isdigit_l((unsigned char)*buf, locale) && !is_plus(*buf)
586 && !is_minus(*buf))
587 return (NULL);
588
589 len = field_width ? field_width : ((c == 'Y') ? 4 : 2);
590 negative = false;
591 if (is_plus(*buf)) {
592 len--;
593 buf++;
594 } else if (is_minus(*buf)) {
595 len--;
596 buf++;
597 negative = true;
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
606 if (i < 0)
607 return (NULL);
608
609 if (negative) {
610 i = -i;
611 }
612
613 if (c == 'Y'){
614 i -= TM_YEAR_BASE;
615 } else if (c == 'y' && flags & FLAG_CENTURY) {
616 i = tm->tm_year + (i % 100);
617 flags &= ~FLAG_CENTURY;
618 } else if (c == 'y'){
619 if (i < 69) i += 100;
620 flags |= FLAG_YEAR_IN_CENTURY;
621 }
622
623 tm->tm_year = i;
624 flags |= FLAG_YEAR;
625 if (c == 'Y'){
626 flags &= ~(FLAG_CENTURY | FLAG_YEAR_IN_CENTURY);
627 }
628
629 break;
630
631 case 'Z':
632 {
633 const char *cp;
634 size_t tzlen, len;
635
636 for (cp = buf; *cp &&
637 isupper_l((unsigned char)*cp, locale); ++cp) {
638 /*empty*/
639 }
640 len = field_width ? field_width : cp - buf;
641 if (len == 3 && strncmp(buf, "GMT", 3) == 0) {
642 *convp = CONVERT_GMT;
643 buf += len;
644 break;
645 }
646
647 tzset();
648 tzlen = strlen(tzname[0]);
649 if (len == tzlen && strncmp(buf, tzname[0], tzlen) == 0) {
650 tm->tm_isdst = 0;
651 buf += len;
652 break;
653 }
654 tzlen = strlen(tzname[1]);
655 if (len == tzlen && strncmp(buf, tzname[1], tzlen) == 0) {
656 tm->tm_isdst = 1;
657 buf += len;
658 break;
659 }
660 return (NULL);
661 }
662
663 case 'z':
664 {
665 char sign;
666 int hr, min;
667 if ((buf[0] != '+' && buf[0] != '-')
668 || !isdigit_l((unsigned char)buf[1], locale)
669 || !isdigit_l((unsigned char)buf[2], locale)
670 || !isdigit_l((unsigned char)buf[3], locale)
671 || !isdigit_l((unsigned char)buf[4], locale))
672 return 0;
673 sscanf(buf, "%c%2d%2d", &sign, &hr, &min);
674 *convp = CONVERT_ZONE;
675 tm->tm_gmtoff = 60 * (60 * hr + min);
676 if (sign == '-')
677 tm->tm_gmtoff = -tm->tm_gmtoff;
678 buf += 5;
679 }
680 break;
681
682 case 'n':
683 case 't':
684 if (!isspace((unsigned char)*buf))
685 return 0;
686 while (isspace_l((unsigned char)*buf, locale))
687 buf++;
688 break;
689
690 default:
691 return (NULL);
692 }
693 }
694
695 if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
696 if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
697 (FLAG_MONTH | FLAG_MDAY)) {
698 tm->tm_yday = start_of_month[isleap(tm->tm_year +
699 TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
700 flags |= FLAG_YDAY;
701 } else if (flags & FLAG_WEEK){
702 int day_offset = week_kind == WEEK_U ? TM_SUNDAY : TM_MONDAY;
703 int fwo = first_wday_of(tm->tm_year + TM_YEAR_BASE);
704
705 /* No incomplete week (week 0). */
706 if (week_number == 0 && fwo == day_offset)
707 return (NULL);
708
709 if (!(flags & FLAG_WDAY)) {
710 /*
711 * Set the date to the first Sunday (or Monday)
712 * of the specified week of the year.
713 */
714 tm->tm_wday = day_offset;
715 flags |= FLAG_WDAY;
716 }
717
718 /*
719 * Start our yday at the first day of the relevant week type.
720 */
721 int tmpyday = (7 - fwo + day_offset) % 7;
722
723 /*
724 * ISO Weeks start counting from the first week with at least
725 * four days. If our first week had that, subtract off a week.
726 */
727 if (week_kind == WEEK_V && fwo > TM_MONDAY && fwo <= TM_THURSDAY) {
728 tmpyday -= 7;
729 }
730 /* Advance the relevant number of weeks */
731 tmpyday += (week_number - 1) * 7;
732 /* And go to the right day of week */
733 tmpyday += (tm->tm_wday - day_offset + 7) % 7;
734
735 /* Impossible yday for incomplete week (week 0). */
736 if (tmpyday < 0) {
737 if (flags & FLAG_WDAY)
738 return (NULL);
739 tmpyday = 0;
740 }
741 tm->tm_yday = tmpyday;
742 flags |= FLAG_YDAY;
743 }
744 }
745
746 if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
747 if (!(flags & FLAG_MONTH)) {
748 i = 0;
749 while (tm->tm_yday >=
750 start_of_month[isleap(tm->tm_year +
751 TM_YEAR_BASE)][i])
752 i++;
753 if (i > 12) {
754 i = 1;
755 tm->tm_yday -=
756 start_of_month[isleap(tm->tm_year +
757 TM_YEAR_BASE)][12];
758 tm->tm_year++;
759 }
760 tm->tm_mon = i - 1;
761 flags |= FLAG_MONTH;
762 }
763 if (!(flags & FLAG_MDAY)) {
764 tm->tm_mday = tm->tm_yday -
765 start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
766 [tm->tm_mon] + 1;
767 flags |= FLAG_MDAY;
768 }
769 if (!(flags & FLAG_WDAY)) {
770 wday_offset = first_wday_of(tm->tm_year + TM_YEAR_BASE);
771 tm->tm_wday = (wday_offset + tm->tm_yday) % 7;
772 flags |= FLAG_WDAY;
773 }
774 }
775
776 return ((char *)buf);
777 }
778
779
780 char *
781 strptime(const char * __restrict buf, const char * __restrict fmt,
782 struct tm * __restrict tm)
783 {
784 return strptime_l(buf, fmt, tm, __current_locale());
785 }
786
787 extern time_t timeoff(struct tm *, long);
788
789 char *
790 strptime_l(const char * __restrict buf, const char * __restrict fmt,
791 struct tm * __restrict tm, locale_t loc)
792 {
793 char *ret;
794 int conv;
795
796 NORMALIZE_LOCALE(loc);
797 conv = CONVERT_NONE;
798 tm->tm_zone = NULL;
799 ret = _strptime(buf, fmt, tm, &conv, loc);
800 if (ret) {
801 time_t t;
802
803 switch(conv) {
804 case CONVERT_GMT:
805 t = timegm(tm);
806 localtime_r(&t, tm);
807 break;
808 case CONVERT_ZONE:
809 {
810 long offset = tm->tm_gmtoff;
811 tm->tm_gmtoff = 0;
812 t = timeoff(tm, offset);
813 localtime_r(&t, tm);
814 break;
815 }
816 }
817 }
818
819 return (ret);
820 }