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: stable/10/lib/libc/stdtime/strptime.c 306414 2016-09-28 20:49:33Z 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 */ 304306414Sache if (tm->tm_hour > 12) 305306414Sache return (NULL); 306306414Sache 30772168Sphantom len = strlen(tptr->am); 308227753Stheraven if (strncasecmp_l(buf, tptr->am, len, locale) == 0) { 30928021Sjoerg if (tm->tm_hour == 12) 31028021Sjoerg tm->tm_hour = 0; 31128021Sjoerg buf += len; 31228021Sjoerg break; 31328021Sjoerg } 31428019Sjoerg 31572168Sphantom len = strlen(tptr->pm); 316227753Stheraven if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) { 31728021Sjoerg if (tm->tm_hour != 12) 31828021Sjoerg tm->tm_hour += 12; 31928021Sjoerg buf += len; 32028021Sjoerg break; 32128021Sjoerg } 32228019Sjoerg 323267798Spfg return (NULL); 32428019Sjoerg 32528021Sjoerg case 'A': 32628021Sjoerg case 'a': 32772168Sphantom for (i = 0; i < asizeof(tptr->weekday); i++) { 32874409Sache len = strlen(tptr->weekday[i]); 329227753Stheraven if (strncasecmp_l(buf, tptr->weekday[i], 330227753Stheraven len, locale) == 0) 33174409Sache break; 33274409Sache len = strlen(tptr->wday[i]); 333227753Stheraven if (strncasecmp_l(buf, tptr->wday[i], 334227753Stheraven len, locale) == 0) 33574409Sache break; 33628021Sjoerg } 33772168Sphantom if (i == asizeof(tptr->weekday)) 338267798Spfg return (NULL); 33928019Sjoerg 340272758Spfg buf += len; 34128021Sjoerg tm->tm_wday = i; 342272758Spfg flags |= FLAG_WDAY; 34328021Sjoerg break; 34428019Sjoerg 34553083Ssheldonh case 'U': 34653083Ssheldonh case 'W': 34753083Ssheldonh /* 34853083Ssheldonh * XXX This is bogus, as we can not assume any valid 34953083Ssheldonh * information present in the tm structure at this 35053083Ssheldonh * point to calculate a real value, so just check the 35153083Ssheldonh * range for now. 35253083Ssheldonh */ 353227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 354267798Spfg return (NULL); 35553083Ssheldonh 35654316Ssheldonh len = 2; 357227753Stheraven for (i = 0; len && *buf != 0 && 358227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++) { 35953083Ssheldonh i *= 10; 36053083Ssheldonh i += *buf - '0'; 36154316Ssheldonh len--; 36253083Ssheldonh } 36353083Ssheldonh if (i > 53) 364267798Spfg return (NULL); 36553083Ssheldonh 366272758Spfg if (c == 'U') 367272758Spfg day_offset = TM_SUNDAY; 368272758Spfg else 369272758Spfg day_offset = TM_MONDAY; 370272758Spfg 371272758Spfg 372272758Spfg week_offset = i; 373272758Spfg 37453083Ssheldonh break; 37553083Ssheldonh 376306414Sache case 'u': 37753083Ssheldonh case 'w': 378227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 379267798Spfg return (NULL); 38053083Ssheldonh 381306414Sache i = *buf++ - '0'; 382306414Sache if (i < 0 || i > 7 || (c == 'u' && i < 1) || 383306414Sache (c == 'w' && i > 6)) 384267798Spfg return (NULL); 38553083Ssheldonh 386306414Sache tm->tm_wday = i % 7; 387272758Spfg flags |= FLAG_WDAY; 38853083Ssheldonh 38953083Ssheldonh break; 39053083Ssheldonh 39128021Sjoerg case 'e': 39254316Ssheldonh /* 393268043Spfg * With %e format, our strftime(3) adds a blank space 394268043Spfg * before single digits. 395268043Spfg */ 396268043Spfg if (*buf != 0 && 397268043Spfg isspace_l((unsigned char)*buf, locale)) 398268043Spfg buf++; 399268043Spfg /* FALLTHROUGH */ 400268043Spfg case 'd': 401268043Spfg /* 402268043Spfg * The %e specifier was once explicitly documented as 403268043Spfg * not being zero-padded but was later changed to 404268043Spfg * equivalent to %d. There is no harm in allowing 40554316Ssheldonh * such padding. 40654316Ssheldonh * 40754316Ssheldonh * XXX The %e specifier may gobble one too many 40854316Ssheldonh * digits if used incorrectly. 40954316Ssheldonh */ 410227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 411267798Spfg return (NULL); 41228019Sjoerg 41354316Ssheldonh len = 2; 414227753Stheraven for (i = 0; len && *buf != 0 && 415227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++) { 41628021Sjoerg i *= 10; 41728021Sjoerg i += *buf - '0'; 41854316Ssheldonh len--; 41928021Sjoerg } 42028021Sjoerg if (i > 31) 421267798Spfg return (NULL); 42228019Sjoerg 42328021Sjoerg tm->tm_mday = i; 424272758Spfg flags |= FLAG_MDAY; 42528019Sjoerg 42628021Sjoerg break; 42728019Sjoerg 42828021Sjoerg case 'B': 42928021Sjoerg case 'b': 43028021Sjoerg case 'h': 43172168Sphantom for (i = 0; i < asizeof(tptr->month); i++) { 43253941Sache if (Oalternative) { 43353941Sache if (c == 'B') { 43472168Sphantom len = strlen(tptr->alt_month[i]); 435227753Stheraven if (strncasecmp_l(buf, 43672168Sphantom tptr->alt_month[i], 437227753Stheraven len, locale) == 0) 43853941Sache break; 43953941Sache } 44053941Sache } else { 44174409Sache len = strlen(tptr->month[i]); 442227753Stheraven if (strncasecmp_l(buf, tptr->month[i], 443227753Stheraven len, locale) == 0) 44474409Sache break; 445207830Sedwin } 446207830Sedwin } 447207830Sedwin /* 448207830Sedwin * Try the abbreviated month name if the full name 449207830Sedwin * wasn't found and Oalternative was not requested. 450207830Sedwin */ 451207830Sedwin if (i == asizeof(tptr->month) && !Oalternative) { 452207830Sedwin for (i = 0; i < asizeof(tptr->month); i++) { 45374409Sache len = strlen(tptr->mon[i]); 454227753Stheraven if (strncasecmp_l(buf, tptr->mon[i], 455227753Stheraven len, locale) == 0) 45674409Sache break; 45753941Sache } 45828021Sjoerg } 45972168Sphantom if (i == asizeof(tptr->month)) 460267798Spfg return (NULL); 46128019Sjoerg 46228021Sjoerg tm->tm_mon = i; 46328021Sjoerg buf += len; 464272758Spfg flags |= FLAG_MONTH; 465272758Spfg 46628021Sjoerg break; 46728019Sjoerg 46828021Sjoerg case 'm': 469227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 470267798Spfg return (NULL); 47128019Sjoerg 47254316Ssheldonh len = 2; 473227753Stheraven for (i = 0; len && *buf != 0 && 474227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++) { 47528021Sjoerg i *= 10; 47628021Sjoerg i += *buf - '0'; 47754316Ssheldonh len--; 47828021Sjoerg } 47928021Sjoerg if (i < 1 || i > 12) 480267798Spfg return (NULL); 48128019Sjoerg 48228021Sjoerg tm->tm_mon = i - 1; 483272758Spfg flags |= FLAG_MONTH; 48428019Sjoerg 48528021Sjoerg break; 48628019Sjoerg 48779664Sdd case 's': 48879664Sdd { 48979664Sdd char *cp; 490122830Snectar int sverrno; 491122830Snectar long n; 49279664Sdd time_t t; 49379664Sdd 494122830Snectar sverrno = errno; 495122830Snectar errno = 0; 496227753Stheraven n = strtol_l(buf, &cp, 10, locale); 497122830Snectar if (errno == ERANGE || (long)(t = n) != n) { 498122830Snectar errno = sverrno; 499267798Spfg return (NULL); 500122830Snectar } 501122830Snectar errno = sverrno; 50279664Sdd buf = cp; 503272758Spfg if (gmtime_r(&t, tm) == NULL) 504272758Spfg return (NULL); 505112156Smtm *GMTp = 1; 506272758Spfg flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH | 507272758Spfg FLAG_MDAY | FLAG_YEAR; 50879664Sdd } 50979664Sdd break; 51079664Sdd 51128021Sjoerg case 'Y': 51228021Sjoerg case 'y': 513227753Stheraven if (*buf == 0 || 514227753Stheraven isspace_l((unsigned char)*buf, locale)) 51528021Sjoerg break; 51628019Sjoerg 517227753Stheraven if (!isdigit_l((unsigned char)*buf, locale)) 518267798Spfg return (NULL); 51928019Sjoerg 52054316Ssheldonh len = (c == 'Y') ? 4 : 2; 521227753Stheraven for (i = 0; len && *buf != 0 && 522227753Stheraven isdigit_l((unsigned char)*buf, locale); buf++) { 52328021Sjoerg i *= 10; 52428021Sjoerg i += *buf - '0'; 52554316Ssheldonh len--; 52628021Sjoerg } 52728021Sjoerg if (c == 'Y') 528272758Spfg i -= TM_YEAR_BASE; 52946051Swes if (c == 'y' && i < 69) 53046042Swes i += 100; 53128021Sjoerg if (i < 0) 532267798Spfg return (NULL); 53328019Sjoerg 53428021Sjoerg tm->tm_year = i; 535272758Spfg flags |= FLAG_YEAR; 53628019Sjoerg 53728021Sjoerg break; 53848550Sobrien 53948550Sobrien case 'Z': 54048550Sobrien { 54148550Sobrien const char *cp; 54248550Sobrien char *zonestr; 54348550Sobrien 544227753Stheraven for (cp = buf; *cp && 545227753Stheraven isupper_l((unsigned char)*cp, locale); ++cp) { 546227753Stheraven /*empty*/} 54748550Sobrien if (cp - buf) { 54848550Sobrien zonestr = alloca(cp - buf + 1); 54948550Sobrien strncpy(zonestr, buf, cp - buf); 55048550Sobrien zonestr[cp - buf] = '\0'; 55148550Sobrien tzset(); 552273290Sache if (0 == strcmp(zonestr, "GMT") || 553273290Sache 0 == strcmp(zonestr, "UTC")) { 554112156Smtm *GMTp = 1; 55548550Sobrien } else if (0 == strcmp(zonestr, tzname[0])) { 55648614Sobrien tm->tm_isdst = 0; 55748550Sobrien } else if (0 == strcmp(zonestr, tzname[1])) { 55848614Sobrien tm->tm_isdst = 1; 55948550Sobrien } else { 560267798Spfg return (NULL); 56148550Sobrien } 56248550Sobrien buf += cp - buf; 56348550Sobrien } 56448550Sobrien } 56548550Sobrien break; 566195015Sdelphij 567195015Sdelphij case 'z': 568195015Sdelphij { 569195015Sdelphij int sign = 1; 570195015Sdelphij 571195015Sdelphij if (*buf != '+') { 572195015Sdelphij if (*buf == '-') 573195015Sdelphij sign = -1; 574195015Sdelphij else 575267798Spfg return (NULL); 576195015Sdelphij } 577195015Sdelphij 578195015Sdelphij buf++; 579195015Sdelphij i = 0; 580195015Sdelphij for (len = 4; len > 0; len--) { 581227753Stheraven if (isdigit_l((unsigned char)*buf, locale)) { 582195015Sdelphij i *= 10; 583195015Sdelphij i += *buf - '0'; 584195015Sdelphij buf++; 585306414Sache } else if (len == 2) { 586306414Sache i *= 100; 587306414Sache break; 588195015Sdelphij } else 589267798Spfg return (NULL); 590195015Sdelphij } 591195015Sdelphij 592306414Sache if (i > 1400 || (sign == -1 && i > 1200) || 593306414Sache (i % 100) >= 60) 594306414Sache return (NULL); 595195015Sdelphij tm->tm_hour -= sign * (i / 100); 596195015Sdelphij tm->tm_min -= sign * (i % 100); 597195015Sdelphij *GMTp = 1; 598195015Sdelphij } 599195015Sdelphij break; 600268043Spfg 601268043Spfg case 'n': 602268043Spfg case 't': 603268043Spfg while (isspace_l((unsigned char)*buf, locale)) 604268043Spfg buf++; 605268043Spfg break; 606273290Sache 607273290Sache default: 608273290Sache return (NULL); 60928021Sjoerg } 61028021Sjoerg } 611272758Spfg 612272758Spfg if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) { 613272758Spfg if ((flags & (FLAG_MONTH | FLAG_MDAY)) == 614272758Spfg (FLAG_MONTH | FLAG_MDAY)) { 615272758Spfg tm->tm_yday = start_of_month[isleap(tm->tm_year + 616272758Spfg TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); 617272758Spfg flags |= FLAG_YDAY; 618272758Spfg } else if (day_offset != -1) { 619306414Sache int tmpwday, tmpyday, fwo; 620306414Sache 621306414Sache fwo = first_wday_of(tm->tm_year + TM_YEAR_BASE); 622306414Sache /* No incomplete week (week 0). */ 623306414Sache if (week_offset == 0 && fwo == day_offset) 624306414Sache return (NULL); 625306414Sache 626272758Spfg /* Set the date to the first Sunday (or Monday) 627272758Spfg * of the specified week of the year. 628272758Spfg */ 629306414Sache tmpwday = (flags & FLAG_WDAY) ? tm->tm_wday : 630306414Sache day_offset; 631306414Sache tmpyday = (7 - fwo + day_offset) % 7 + 632306414Sache (week_offset - 1) * 7 + 633306414Sache (tmpwday - day_offset + 7) % 7; 634306414Sache /* Impossible yday for incomplete week (week 0). */ 635306414Sache if (tmpyday < 0) { 636306414Sache if (flags & FLAG_WDAY) 637306414Sache return (NULL); 638306414Sache tmpyday = 0; 639272758Spfg } 640306414Sache tm->tm_yday = tmpyday; 641272758Spfg flags |= FLAG_YDAY; 642272758Spfg } 643272758Spfg } 644272758Spfg 645272758Spfg if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) { 646272758Spfg if (!(flags & FLAG_MONTH)) { 647272758Spfg i = 0; 648272758Spfg while (tm->tm_yday >= 649272758Spfg start_of_month[isleap(tm->tm_year + 650272758Spfg TM_YEAR_BASE)][i]) 651272758Spfg i++; 652272758Spfg if (i > 12) { 653272758Spfg i = 1; 654272758Spfg tm->tm_yday -= 655272758Spfg start_of_month[isleap(tm->tm_year + 656272758Spfg TM_YEAR_BASE)][12]; 657272758Spfg tm->tm_year++; 658272758Spfg } 659272758Spfg tm->tm_mon = i - 1; 660272758Spfg flags |= FLAG_MONTH; 661272758Spfg } 662272758Spfg if (!(flags & FLAG_MDAY)) { 663272758Spfg tm->tm_mday = tm->tm_yday - 664272758Spfg start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)] 665272758Spfg [tm->tm_mon] + 1; 666272758Spfg flags |= FLAG_MDAY; 667272758Spfg } 668272758Spfg if (!(flags & FLAG_WDAY)) { 669272758Spfg i = 0; 670272758Spfg wday_offset = first_wday_of(tm->tm_year); 671272758Spfg while (i++ <= tm->tm_yday) { 672272758Spfg if (wday_offset++ >= 6) 673272758Spfg wday_offset = 0; 674272758Spfg } 675272758Spfg tm->tm_wday = wday_offset; 676272758Spfg flags |= FLAG_WDAY; 677272758Spfg } 678272758Spfg } 679272758Spfg 680267798Spfg return ((char *)buf); 68148614Sobrien} 68228019Sjoerg 68348614Sobrienchar * 684227753Stheravenstrptime_l(const char * __restrict buf, const char * __restrict fmt, 685227753Stheraven struct tm * __restrict tm, locale_t loc) 68648614Sobrien{ 68748614Sobrien char *ret; 688112156Smtm int gmt; 689227753Stheraven FIX_LOCALE(loc); 69048614Sobrien 691112156Smtm gmt = 0; 692227753Stheraven ret = _strptime(buf, fmt, tm, &gmt, loc); 693114285Smtm if (ret && gmt) { 694114285Smtm time_t t = timegm(tm); 695273290Sache 69671579Sdeischen localtime_r(&t, tm); 69748550Sobrien } 69848614Sobrien 699112156Smtm return (ret); 70028019Sjoerg} 701272758Spfg 702227753Stheravenchar * 703227753Stheravenstrptime(const char * __restrict buf, const char * __restrict fmt, 704227753Stheraven struct tm * __restrict tm) 705227753Stheraven{ 706227753Stheraven return strptime_l(buf, fmt, tm, __get_locale()); 707227753Stheraven} 708