4 * Copyright (C) 1993-1999 by Jochen Wiedmann and Marcin Orlowski
5 * Copyright (C) 2002-2014 by the FlexCat Open Source Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 // parse a date string produced by strftime() and put the success in a struct tm
45 #define FLG_SEC (1<<0)
46 #define FLG_MIN (1<<1)
47 #define FLG_HOUR (1<<2)
48 #define FLG_MDAY (1<<3)
49 #define FLG_MON (1<<4)
50 #define FLG_YEAR (1<<5)
51 #define FLG_WDAY (1<<6)
52 #define FLG_YDAY (1<<7)
53 #define FLG_ISDST (1<<8)
54 #define FLG_4DIGIT_YEAR (1<<9)
56 char *strptime(const char *string
, const char *fmt
, struct tm
*res
)
61 enum ScanDateState state
= SDS_DEFAULT
;
64 // start with the first character in both strings
68 while(state
!= SDS_DONE
)
70 if(fc
== '\0' && sc
== '\0')
79 state
= SDS_SPECIFIER
;
84 // the format string seems to be malformed, bail out
94 case 'd': // %d - day number with leading zeros (01-31)
95 case 'e': // %e - day number with leading spaces ( 1-31)
98 state
= SDS_DAY_OF_MONTH
;
103 case 'm': // %m - month number with leading zeros (01-12)
111 case 'Y': // %Y - year using four digits with leading zeros
113 flags
|= FLG_4DIGIT_YEAR
;
115 // we fall through here
117 case 'y': // %y - year using two digits with leading zeros (00-99)
127 // ignore any switches between with/without leading zeros/spaces
134 // unknown specifier, bail out
142 case SDS_DAY_OF_MONTH
:
146 // next separator in format string found
153 // ignore any spaces within the day spec
156 else if(sc
>= '0' && sc
<= '9')
158 // valid number found, add it to the day of month
159 res
->tm_mday
= res
->tm_mday
* 10 + sc
- '0';
164 // unexpected character, bail out
174 // next separator in format string found
179 else if(sc
>= '0' && sc
<= '9')
181 // valid number found, add it to the month
182 res
->tm_mon
= res
->tm_mon
* 10 + sc
- '0';
187 // unexpected character, bail out
197 // next separator in format string found
202 else if(sc
>= '0' && sc
<= '9')
204 // valid number found, add it to the year
205 res
->tm_year
= res
->tm_year
* 10 + sc
- '0';
210 // unexpected character, bail out
222 // finally check if the calculated values are correct, but only those which
223 // were specified in the format string
224 if((flags
& FLG_MDAY
) || strstr(fmt
, "%d") != NULL
|| strstr(fmt
, "%-d") != NULL
|| strstr(fmt
, "%e") != NULL
)
226 if(res
->tm_mday
>= 1 && res
->tm_mday
<= 31)
235 if((flags
& FLG_MON
) || strstr(fmt
, "%m") != NULL
|| strstr(fmt
, "%-m") != NULL
)
237 if(res
->tm_mon
>= 1 && res
->tm_mon
<= 12)
239 // tm_mon counts from 0 to 11
247 if((flags
& FLG_YEAR
) || strstr(fmt
, "%y") != NULL
|| strstr(fmt
, "%-y") != NULL
|| strstr(fmt
, "%Y") != NULL
|| strstr(fmt
, "%-Y") != NULL
)
249 if((flags
& FLG_4DIGIT_YEAR
) || strstr(fmt
, "%Y") != NULL
|| strstr(fmt
, "%-Y") != NULL
)
251 if(res
->tm_year
>= 1900)
253 // tm_year counts the years from 1900
254 res
->tm_year
-= 1900;
258 // year numbers less than 1900 are not supported
264 // 2 digit year number, must be less than 100
265 if(res
->tm_year
< 100)
267 if(res
->tm_year
< 40)
269 // tm_year counts the years from 1900
270 // if the year number is less than 40 we assume a year between
271 // 2000 and 2039 instead of between 1900 and 1939 to allow a user
272 // age of at least ~70 years.
276 // Although we expect a two digit year number for %y we got one with more digits.
277 // Better not fail at this even if the entered string is wrong. People tend to
278 // forget the correct formatting.
279 else if(res
->tm_year
>= 1900)
281 // tm_year counts the years from 1900
282 res
->tm_year
-= 1900;
286 // numbers between 100 and 1899 are definitely not allowed
292 // finally check if the day value is correct
293 if(success
== 1 && (flags
& FLG_MDAY
))
297 // February has 29 days at most, but we don't check for leap years here
298 if(res
->tm_mday
> 29)
303 else if(res
->tm_mon
== 3 ||
308 // April, June, September and November have 30 days
309 if(res
->tm_mday
> 30)
316 return (char *)string
;