1267677Spfg/*- 2268043Spfg * Copyright (c) 2014 Gary Mills 3268043Spfg * Copyright 2011, Nexenta Systems, Inc. All rights reserved. 428019Sjoerg * Copyright (c) 1994 Powerdog Industries. All rights reserved. 528019Sjoerg * 6227753Stheraven * Copyright (c) 2011 The FreeBSD Foundation 7227753Stheraven * All rights reserved. 8227753Stheraven * Portions of this software were developed by David Chisnall 9227753Stheraven * under sponsorship from the FreeBSD Foundation. 10227753Stheraven * 1128021Sjoerg * Redistribution and use in source and binary forms, with or without 1228019Sjoerg * modification, are permitted provided that the following conditions 1328019Sjoerg * are met: 1428019Sjoerg * 1. Redistributions of source code must retain the above copyright 1528019Sjoerg * notice, this list of conditions and the following disclaimer. 1628019Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1728019Sjoerg * notice, this list of conditions and the following disclaimer 1828019Sjoerg * in the documentation and/or other materials provided with the 1928019Sjoerg * distribution. 2028019Sjoerg * 2128019Sjoerg * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY 2228019Sjoerg * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2328019Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2428019Sjoerg * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE 2528019Sjoerg * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2628019Sjoerg * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2728019Sjoerg * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 2828019Sjoerg * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 2928019Sjoerg * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 3028019Sjoerg * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 3128019Sjoerg * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32267677Spfg * 33267677Spfg * The views and conclusions contained in the software and documentation 34267677Spfg * are those of the authors and should not be interpreted as representing 35267677Spfg * official policies, either expressed or implied, of Powerdog Industries. 3628019Sjoerg */ 3728019Sjoerg 38111010Snectar#include <sys/cdefs.h> 3928019Sjoerg#ifndef lint 4028021Sjoerg#ifndef NOID 41111010Snectarstatic char copyright[] __unused = 4228019Sjoerg"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved."; 43111010Snectarstatic char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27"; 4428021Sjoerg#endif /* !defined NOID */ 4528019Sjoerg#endif /* not lint */ 4692986Sobrien__FBSDID("$FreeBSD: releng/10.2/lib/libc/stdtime/strptime.c 273290 2014-10-19 21:16:24Z ache $"); 4728019Sjoerg 4871579Sdeischen#include "namespace.h" 4928019Sjoerg#include <time.h> 5028019Sjoerg#include <ctype.h> 51122830Snectar#include <errno.h> 5279664Sdd#include <stdlib.h> 5328019Sjoerg#include <string.h> 5448614Sobrien#include <pthread.h> 5571579Sdeischen#include "un-namespace.h" 5671579Sdeischen#include "libc_private.h" 5728021Sjoerg#include "timelocal.h" 58272758Spfg#include "tzfile.h" 5928019Sjoerg 60227753Stheravenstatic char * _strptime(const char *, const char *, struct tm *, int *, locale_t); 6148614Sobrien 62272758Spfg#define asizeof(a) (sizeof(a) / sizeof((a)[0])) 6328019Sjoerg 64272758Spfg#define FLAG_NONE (1 << 0) 65272758Spfg#define FLAG_YEAR (1 << 1) 66272758Spfg#define FLAG_MONTH (1 << 2) 67272758Spfg#define FLAG_YDAY (1 << 3) 68272758Spfg#define FLAG_MDAY (1 << 4) 69272758Spfg#define FLAG_WDAY (1 << 5) 70272758Spfg 71272758Spfg/* 72272758Spfg * Calculate the week day of the first day of a year. Valid for 73272758Spfg * the Gregorian calendar, which began Sept 14, 1752 in the UK 74272758Spfg * and its colonies. Ref: 75272758Spfg * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week 76272758Spfg */ 77272758Spfg 78272758Spfgstatic int 79272758Spfgfirst_wday_of(int year) 80272758Spfg{ 81272758Spfg return (((2 * (3 - (year / 100) % 4)) + (year % 100) + 82272758Spfg ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7); 83272758Spfg} 84272758Spfg 8548614Sobrienstatic char * 86227753Stheraven_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp, 87227753Stheraven locale_t locale) 8828019Sjoerg{ 8928021Sjoerg char c; 9028021Sjoerg const char *ptr; 91272758Spfg int day_offset = -1, wday_offset; 92272758Spfg int week_offset; 93267798Spfg int i, len; 94272758Spfg int flags; 9553941Sache int Ealternative, Oalternative; 96272758Spfg const struct lc_time_T *tptr = __get_current_time_locale(locale); 97272758Spfg static int start_of_month[2][13] = { 98272758Spfg {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, 99272758Spfg {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} 100272758Spfg }; 10128019Sjoerg 102272758Spfg flags = FLAG_NONE; 103272758Spfg 10428021Sjoerg ptr = fmt; 10528021Sjoerg while (*ptr != 0) { 10628021Sjoerg c = *ptr++; 10728019Sjoerg 10828021Sjoerg if (c != '%') { 109227753Stheraven if (isspace_l((unsigned char)c, locale)) 110227753Stheraven while (*buf != 0 && 111227753Stheraven isspace_l((unsigned char)*buf, locale)) 11228021Sjoerg buf++; 11328021Sjoerg else if (c != *buf++) 114267798Spfg return (NULL); 11528021Sjoerg continue; 11628021Sjoerg } 11728019Sjoerg 11853941Sache Ealternative = 0; 11953941Sache Oalternative = 0; 12053941Sachelabel: 12128021Sjoerg c = *ptr++; 12228021Sjoerg switch (c) { 12328021Sjoerg case '%': 12428021Sjoerg if (*buf++ != '%') 125267798Spfg return (NULL); 12628021Sjoerg break; 12728019Sjoerg 12853941Sache case '+': 129227753Stheraven buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale); 130267798Spfg if (buf == NULL) 131267798Spfg return (NULL); 132272758Spfg flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 13328021Sjoerg break; 13428019Sjoerg 13553941Sache case 'C': 136227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 137267798Spfg return (NULL); 13853941Sache 13954316Ssheldonh /* XXX This will break for 3-digit centuries. */ 14054316Ssheldonh len = 2; 141227753Stheraven for (i = 0; len && *buf != 0 && 142227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++) { 14353941Sache i *= 10; 14453941Sache i += *buf - '0'; 14554316Ssheldonh len--; 14653941Sache } 14753941Sache if (i < 19) 148267798Spfg return (NULL); 14953941Sache 150272758Spfg tm->tm_year = i * 100 - TM_YEAR_BASE; 151272758Spfg flags |= FLAG_YEAR; 152272758Spfg 15353941Sache break; 15453941Sache 15528021Sjoerg case 'c': 156227753Stheraven buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale); 157267798Spfg if (buf == NULL) 158267798Spfg return (NULL); 159272758Spfg flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 16028021Sjoerg break; 16128019Sjoerg 16228021Sjoerg case 'D': 163227753Stheraven buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale); 164267798Spfg if (buf == NULL) 165267798Spfg return (NULL); 166272758Spfg flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 16728021Sjoerg break; 16828019Sjoerg 16953941Sache case 'E': 17053960Sache if (Ealternative || Oalternative) 17153960Sache break; 17253941Sache Ealternative++; 17353941Sache goto label; 17453941Sache 17553941Sache case 'O': 17653960Sache if (Ealternative || Oalternative) 17753960Sache break; 17853941Sache Oalternative++; 17953941Sache goto label; 18053941Sache 18153960Sache case 'F': 182227753Stheraven buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale); 183267798Spfg if (buf == NULL) 184267798Spfg return (NULL); 185272758Spfg flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 18674412Sache break; 18774412Sache 18828021Sjoerg case 'R': 189227753Stheraven buf = _strptime(buf, "%H:%M", tm, GMTp, locale); 190267798Spfg if (buf == NULL) 191267798Spfg return (NULL); 19228021Sjoerg break; 19328019Sjoerg 19428021Sjoerg case 'r': 195227753Stheraven buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale); 196267798Spfg if (buf == NULL) 197267798Spfg return (NULL); 19828021Sjoerg break; 19928019Sjoerg 20028021Sjoerg case 'T': 201227753Stheraven buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale); 202267798Spfg if (buf == NULL) 203267798Spfg return (NULL); 20428021Sjoerg break; 20528019Sjoerg 20628021Sjoerg case 'X': 207227753Stheraven buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale); 208267798Spfg if (buf == NULL) 209267798Spfg return (NULL); 21028021Sjoerg break; 21128019Sjoerg 21228021Sjoerg case 'x': 213227753Stheraven buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale); 214267798Spfg if (buf == NULL) 215267798Spfg return (NULL); 216272758Spfg flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR; 21728021Sjoerg break; 21828019Sjoerg 21928021Sjoerg case 'j': 220227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 221267798Spfg return (NULL); 22228019Sjoerg 22354316Ssheldonh len = 3; 224227753Stheraven for (i = 0; len && *buf != 0 && 225227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++){ 22628021Sjoerg i *= 10; 22728021Sjoerg i += *buf - '0'; 22854316Ssheldonh len--; 22928021Sjoerg } 23053083Ssheldonh if (i < 1 || i > 366) 231267798Spfg return (NULL); 23228019Sjoerg 23353083Ssheldonh tm->tm_yday = i - 1; 234272758Spfg flags |= FLAG_YDAY; 235272758Spfg 23628021Sjoerg break; 23728019Sjoerg 23828021Sjoerg case 'M': 23928021Sjoerg case 'S': 240227753Stheraven if (*buf == 0 || 241227753Stheraven isspace_l((unsigned char)*buf, locale)) 24228021Sjoerg break; 24328019Sjoerg 244227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 245267798Spfg return (NULL); 24628019Sjoerg 24754316Ssheldonh len = 2; 248227753Stheraven for (i = 0; len && *buf != 0 && 249227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++){ 25028021Sjoerg i *= 10; 25128021Sjoerg i += *buf - '0'; 25254316Ssheldonh len--; 25328021Sjoerg } 25428019Sjoerg 25553083Ssheldonh if (c == 'M') { 25653083Ssheldonh if (i > 59) 257267798Spfg return (NULL); 25828021Sjoerg tm->tm_min = i; 25953083Ssheldonh } else { 26053083Ssheldonh if (i > 60) 261267798Spfg return (NULL); 26228021Sjoerg tm->tm_sec = i; 26353083Ssheldonh } 26428019Sjoerg 26528021Sjoerg break; 26628019Sjoerg 26728021Sjoerg case 'H': 26828021Sjoerg case 'I': 26928021Sjoerg case 'k': 27028021Sjoerg case 'l': 27154316Ssheldonh /* 27254316Ssheldonh * Of these, %l is the only specifier explicitly 27354316Ssheldonh * documented as not being zero-padded. However, 27454316Ssheldonh * there is no harm in allowing zero-padding. 27554316Ssheldonh * 27654316Ssheldonh * XXX The %l specifier may gobble one too many 27754316Ssheldonh * digits if used incorrectly. 27854316Ssheldonh */ 279227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 280267798Spfg return (NULL); 28128019Sjoerg 28254316Ssheldonh len = 2; 283227753Stheraven for (i = 0; len && *buf != 0 && 284227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++) { 28528021Sjoerg i *= 10; 28628021Sjoerg i += *buf - '0'; 28754316Ssheldonh len--; 28828021Sjoerg } 28928021Sjoerg if (c == 'H' || c == 'k') { 29028021Sjoerg if (i > 23) 291267798Spfg return (NULL); 29254301Ssheldonh } else if (i > 12) 293267798Spfg return (NULL); 29428019Sjoerg 29528021Sjoerg tm->tm_hour = i; 29628019Sjoerg 29728021Sjoerg break; 29828019Sjoerg 29928021Sjoerg case 'p': 30054316Ssheldonh /* 30154316Ssheldonh * XXX This is bogus if parsed before hour-related 30254316Ssheldonh * specifiers. 30354316Ssheldonh */ 30472168Sphantom len = strlen(tptr->am); 305227753Stheraven if (strncasecmp_l(buf, tptr->am, len, locale) == 0) { 30628021Sjoerg if (tm->tm_hour > 12) 307267798Spfg return (NULL); 30828021Sjoerg if (tm->tm_hour == 12) 30928021Sjoerg tm->tm_hour = 0; 31028021Sjoerg buf += len; 31128021Sjoerg break; 31228021Sjoerg } 31328019Sjoerg 31472168Sphantom len = strlen(tptr->pm); 315227753Stheraven if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) { 31628021Sjoerg if (tm->tm_hour > 12) 317267798Spfg return (NULL); 31828021Sjoerg if (tm->tm_hour != 12) 31928021Sjoerg tm->tm_hour += 12; 32028021Sjoerg buf += len; 32128021Sjoerg break; 32228021Sjoerg } 32328019Sjoerg 324267798Spfg return (NULL); 32528019Sjoerg 32628021Sjoerg case 'A': 32728021Sjoerg case 'a': 32872168Sphantom for (i = 0; i < asizeof(tptr->weekday); i++) { 32974409Sache len = strlen(tptr->weekday[i]); 330227753Stheraven if (strncasecmp_l(buf, tptr->weekday[i], 331227753Stheraven len, locale) == 0) 33274409Sache break; 33374409Sache len = strlen(tptr->wday[i]); 334227753Stheraven if (strncasecmp_l(buf, tptr->wday[i], 335227753Stheraven len, locale) == 0) 33674409Sache break; 33728021Sjoerg } 33872168Sphantom if (i == asizeof(tptr->weekday)) 339267798Spfg return (NULL); 34028019Sjoerg 341272758Spfg buf += len; 34228021Sjoerg tm->tm_wday = i; 343272758Spfg flags |= FLAG_WDAY; 34428021Sjoerg break; 34528019Sjoerg 34653083Ssheldonh case 'U': 34753083Ssheldonh case 'W': 34853083Ssheldonh /* 34953083Ssheldonh * XXX This is bogus, as we can not assume any valid 35053083Ssheldonh * information present in the tm structure at this 35153083Ssheldonh * point to calculate a real value, so just check the 35253083Ssheldonh * range for now. 35353083Ssheldonh */ 354227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 355267798Spfg return (NULL); 35653083Ssheldonh 35754316Ssheldonh len = 2; 358227753Stheraven for (i = 0; len && *buf != 0 && 359227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++) { 36053083Ssheldonh i *= 10; 36153083Ssheldonh i += *buf - '0'; 36254316Ssheldonh len--; 36353083Ssheldonh } 36453083Ssheldonh if (i > 53) 365267798Spfg return (NULL); 36653083Ssheldonh 367272758Spfg if (c == 'U') 368272758Spfg day_offset = TM_SUNDAY; 369272758Spfg else 370272758Spfg day_offset = TM_MONDAY; 371272758Spfg 372272758Spfg 373272758Spfg week_offset = i; 374272758Spfg 37553083Ssheldonh break; 37653083Ssheldonh 37753083Ssheldonh case 'w': 378227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 379267798Spfg return (NULL); 38053083Ssheldonh 38154316Ssheldonh i = *buf - '0'; 38253083Ssheldonh if (i > 6) 383267798Spfg return (NULL); 38453083Ssheldonh 38553083Ssheldonh tm->tm_wday = i; 386272758Spfg flags |= FLAG_WDAY; 38753083Ssheldonh 38853083Ssheldonh break; 38953083Ssheldonh 39028021Sjoerg case 'e': 39154316Ssheldonh /* 392268043Spfg * With %e format, our strftime(3) adds a blank space 393268043Spfg * before single digits. 394268043Spfg */ 395268043Spfg if (*buf != 0 && 396268043Spfg isspace_l((unsigned char)*buf, locale)) 397268043Spfg buf++; 398268043Spfg /* FALLTHROUGH */ 399268043Spfg case 'd': 400268043Spfg /* 401268043Spfg * The %e specifier was once explicitly documented as 402268043Spfg * not being zero-padded but was later changed to 403268043Spfg * equivalent to %d. There is no harm in allowing 40454316Ssheldonh * such padding. 40554316Ssheldonh * 40654316Ssheldonh * XXX The %e specifier may gobble one too many 40754316Ssheldonh * digits if used incorrectly. 40854316Ssheldonh */ 409227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 410267798Spfg return (NULL); 41128019Sjoerg 41254316Ssheldonh len = 2; 413227753Stheraven for (i = 0; len && *buf != 0 && 414227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++) { 41528021Sjoerg i *= 10; 41628021Sjoerg i += *buf - '0'; 41754316Ssheldonh len--; 41828021Sjoerg } 41928021Sjoerg if (i > 31) 420267798Spfg return (NULL); 42128019Sjoerg 42228021Sjoerg tm->tm_mday = i; 423272758Spfg flags |= FLAG_MDAY; 42428019Sjoerg 42528021Sjoerg break; 42628019Sjoerg 42728021Sjoerg case 'B': 42828021Sjoerg case 'b': 42928021Sjoerg case 'h': 43072168Sphantom for (i = 0; i < asizeof(tptr->month); i++) { 43153941Sache if (Oalternative) { 43253941Sache if (c == 'B') { 43372168Sphantom len = strlen(tptr->alt_month[i]); 434227753Stheraven if (strncasecmp_l(buf, 43572168Sphantom tptr->alt_month[i], 436227753Stheraven len, locale) == 0) 43753941Sache break; 43853941Sache } 43953941Sache } else { 44074409Sache len = strlen(tptr->month[i]); 441227753Stheraven if (strncasecmp_l(buf, tptr->month[i], 442227753Stheraven len, locale) == 0) 44374409Sache break; 444207830Sedwin } 445207830Sedwin } 446207830Sedwin /* 447207830Sedwin * Try the abbreviated month name if the full name 448207830Sedwin * wasn't found and Oalternative was not requested. 449207830Sedwin */ 450207830Sedwin if (i == asizeof(tptr->month) && !Oalternative) { 451207830Sedwin for (i = 0; i < asizeof(tptr->month); i++) { 45274409Sache len = strlen(tptr->mon[i]); 453227753Stheraven if (strncasecmp_l(buf, tptr->mon[i], 454227753Stheraven len, locale) == 0) 45574409Sache break; 45653941Sache } 45728021Sjoerg } 45872168Sphantom if (i == asizeof(tptr->month)) 459267798Spfg return (NULL); 46028019Sjoerg 46128021Sjoerg tm->tm_mon = i; 46228021Sjoerg buf += len; 463272758Spfg flags |= FLAG_MONTH; 464272758Spfg 46528021Sjoerg break; 46628019Sjoerg 46728021Sjoerg case 'm': 468227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 469267798Spfg return (NULL); 47028019Sjoerg 47154316Ssheldonh len = 2; 472227753Stheraven for (i = 0; len && *buf != 0 && 473227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++) { 47428021Sjoerg i *= 10; 47528021Sjoerg i += *buf - '0'; 47654316Ssheldonh len--; 47728021Sjoerg } 47828021Sjoerg if (i < 1 || i > 12) 479267798Spfg return (NULL); 48028019Sjoerg 48128021Sjoerg tm->tm_mon = i - 1; 482272758Spfg flags |= FLAG_MONTH; 48328019Sjoerg 48428021Sjoerg break; 48528019Sjoerg 48679664Sdd case 's': 48779664Sdd { 48879664Sdd char *cp; 489122830Snectar int sverrno; 490122830Snectar long n; 49179664Sdd time_t t; 49279664Sdd 493122830Snectar sverrno = errno; 494122830Snectar errno = 0; 495227753Stheraven n = strtol_l(buf, &cp, 10, locale); 496122830Snectar if (errno == ERANGE || (long)(t = n) != n) { 497122830Snectar errno = sverrno; 498267798Spfg return (NULL); 499122830Snectar } 500122830Snectar errno = sverrno; 50179664Sdd buf = cp; 502272758Spfg if (gmtime_r(&t, tm) == NULL) 503272758Spfg return (NULL); 504112156Smtm *GMTp = 1; 505272758Spfg flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH | 506272758Spfg FLAG_MDAY | FLAG_YEAR; 50779664Sdd } 50879664Sdd break; 50979664Sdd 51028021Sjoerg case 'Y': 51128021Sjoerg case 'y': 512227753Stheraven if (*buf == 0 || 513227753Stheraven isspace_l((unsigned char)*buf, locale)) 51428021Sjoerg break; 51528019Sjoerg 516227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 517267798Spfg return (NULL); 51828019Sjoerg 51954316Ssheldonh len = (c == 'Y') ? 4 : 2; 520227753Stheraven for (i = 0; len && *buf != 0 && 521227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++) { 52228021Sjoerg i *= 10; 52328021Sjoerg i += *buf - '0'; 52454316Ssheldonh len--; 52528021Sjoerg } 52628021Sjoerg if (c == 'Y') 527272758Spfg i -= TM_YEAR_BASE; 52846051Swes if (c == 'y' && i < 69) 52946042Swes i += 100; 53028021Sjoerg if (i < 0) 531267798Spfg return (NULL); 53228019Sjoerg 53328021Sjoerg tm->tm_year = i; 534272758Spfg flags |= FLAG_YEAR; 53528019Sjoerg 53628021Sjoerg break; 53748550Sobrien 53848550Sobrien case 'Z': 53948550Sobrien { 54048550Sobrien const char *cp; 54148550Sobrien char *zonestr; 54248550Sobrien 543227753Stheraven for (cp = buf; *cp && 544227753Stheraven isupper_l((unsigned char)*cp, locale); ++cp) { 545227753Stheraven /*empty*/} 54648550Sobrien if (cp - buf) { 54748550Sobrien zonestr = alloca(cp - buf + 1); 54848550Sobrien strncpy(zonestr, buf, cp - buf); 54948550Sobrien zonestr[cp - buf] = '\0'; 55048550Sobrien tzset(); 551273290Sache if (0 == strcmp(zonestr, "GMT") || 552273290Sache 0 == strcmp(zonestr, "UTC")) { 553112156Smtm *GMTp = 1; 55448550Sobrien } else if (0 == strcmp(zonestr, tzname[0])) { 55548614Sobrien tm->tm_isdst = 0; 55648550Sobrien } else if (0 == strcmp(zonestr, tzname[1])) { 55748614Sobrien tm->tm_isdst = 1; 55848550Sobrien } else { 559267798Spfg return (NULL); 56048550Sobrien } 56148550Sobrien buf += cp - buf; 56248550Sobrien } 56348550Sobrien } 56448550Sobrien break; 565195015Sdelphij 566195015Sdelphij case 'z': 567195015Sdelphij { 568195015Sdelphij int sign = 1; 569195015Sdelphij 570195015Sdelphij if (*buf != '+') { 571195015Sdelphij if (*buf == '-') 572195015Sdelphij sign = -1; 573195015Sdelphij else 574267798Spfg return (NULL); 575195015Sdelphij } 576195015Sdelphij 577195015Sdelphij buf++; 578195015Sdelphij i = 0; 579195015Sdelphij for (len = 4; len > 0; len--) { 580227753Stheraven if (isdigit_l((unsigned char)*buf, locale)) { 581195015Sdelphij i *= 10; 582195015Sdelphij i += *buf - '0'; 583195015Sdelphij buf++; 584195015Sdelphij } else 585267798Spfg return (NULL); 586195015Sdelphij } 587195015Sdelphij 588195015Sdelphij tm->tm_hour -= sign * (i / 100); 589195015Sdelphij tm->tm_min -= sign * (i % 100); 590195015Sdelphij *GMTp = 1; 591195015Sdelphij } 592195015Sdelphij break; 593268043Spfg 594268043Spfg case 'n': 595268043Spfg case 't': 596268043Spfg while (isspace_l((unsigned char)*buf, locale)) 597268043Spfg buf++; 598268043Spfg break; 599273290Sache 600273290Sache default: 601273290Sache return (NULL); 60228021Sjoerg } 60328021Sjoerg } 604272758Spfg 605272758Spfg if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) { 606272758Spfg if ((flags & (FLAG_MONTH | FLAG_MDAY)) == 607272758Spfg (FLAG_MONTH | FLAG_MDAY)) { 608272758Spfg tm->tm_yday = start_of_month[isleap(tm->tm_year + 609272758Spfg TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); 610272758Spfg flags |= FLAG_YDAY; 611272758Spfg } else if (day_offset != -1) { 612272758Spfg /* Set the date to the first Sunday (or Monday) 613272758Spfg * of the specified week of the year. 614272758Spfg */ 615272758Spfg if (!(flags & FLAG_WDAY)) { 616272758Spfg tm->tm_wday = day_offset; 617272758Spfg flags |= FLAG_WDAY; 618272758Spfg } 619272758Spfg tm->tm_yday = (7 - 620272758Spfg first_wday_of(tm->tm_year + TM_YEAR_BASE) + 621272758Spfg day_offset) % 7 + (week_offset - 1) * 7 + 622272758Spfg tm->tm_wday - day_offset; 623272758Spfg flags |= FLAG_YDAY; 624272758Spfg } 625272758Spfg } 626272758Spfg 627272758Spfg if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) { 628272758Spfg if (!(flags & FLAG_MONTH)) { 629272758Spfg i = 0; 630272758Spfg while (tm->tm_yday >= 631272758Spfg start_of_month[isleap(tm->tm_year + 632272758Spfg TM_YEAR_BASE)][i]) 633272758Spfg i++; 634272758Spfg if (i > 12) { 635272758Spfg i = 1; 636272758Spfg tm->tm_yday -= 637272758Spfg start_of_month[isleap(tm->tm_year + 638272758Spfg TM_YEAR_BASE)][12]; 639272758Spfg tm->tm_year++; 640272758Spfg } 641272758Spfg tm->tm_mon = i - 1; 642272758Spfg flags |= FLAG_MONTH; 643272758Spfg } 644272758Spfg if (!(flags & FLAG_MDAY)) { 645272758Spfg tm->tm_mday = tm->tm_yday - 646272758Spfg start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)] 647272758Spfg [tm->tm_mon] + 1; 648272758Spfg flags |= FLAG_MDAY; 649272758Spfg } 650272758Spfg if (!(flags & FLAG_WDAY)) { 651272758Spfg i = 0; 652272758Spfg wday_offset = first_wday_of(tm->tm_year); 653272758Spfg while (i++ <= tm->tm_yday) { 654272758Spfg if (wday_offset++ >= 6) 655272758Spfg wday_offset = 0; 656272758Spfg } 657272758Spfg tm->tm_wday = wday_offset; 658272758Spfg flags |= FLAG_WDAY; 659272758Spfg } 660272758Spfg } 661272758Spfg 662267798Spfg return ((char *)buf); 66348614Sobrien} 66428019Sjoerg 66548614Sobrienchar * 666227753Stheravenstrptime_l(const char * __restrict buf, const char * __restrict fmt, 667227753Stheraven struct tm * __restrict tm, locale_t loc) 66848614Sobrien{ 66948614Sobrien char *ret; 670112156Smtm int gmt; 671227753Stheraven FIX_LOCALE(loc); 67248614Sobrien 673112156Smtm gmt = 0; 674227753Stheraven ret = _strptime(buf, fmt, tm, &gmt, loc); 675114285Smtm if (ret && gmt) { 676114285Smtm time_t t = timegm(tm); 677273290Sache 67871579Sdeischen localtime_r(&t, tm); 67948550Sobrien } 68048614Sobrien 681112156Smtm return (ret); 68228019Sjoerg} 683272758Spfg 684227753Stheravenchar * 685227753Stheravenstrptime(const char * __restrict buf, const char * __restrict fmt, 686227753Stheraven struct tm * __restrict tm) 687227753Stheraven{ 688227753Stheraven return strptime_l(buf, fmt, tm, __get_locale()); 689227753Stheraven} 690