revert between 56095 -> 55830 in arch
[AROS.git] / tools / flexcat / src / strptime.c
blob8d8e8868bbe61bdc17bd89ba8a85f71a23fdd9a8
1 /*
2 * $Id$
4 * Copyright (C) 1993-1999 by Jochen Wiedmann and Marcin Orlowski
5 * Copyright (C) 2002-2015 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.
23 #include <string.h>
24 #include <time.h>
25 #include <stdio.h>
27 /// strptime
28 // parse a date string produced by strftime() and put the success in a struct tm
29 enum ScanDateState
31 SDS_DEFAULT = 0,
32 SDS_SPECIFIER,
33 SDS_DONE,
34 SDS_SECOND,
35 SDS_MINUTE,
36 SDS_HOUR,
37 SDS_DAY_OF_MONTH,
38 SDS_MONTH,
39 SDS_YEAR,
40 SDS_DAY_OF_WEEK,
41 SDS_DAY_YEAR,
42 SDS_IS_DST,
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)
58 int success = 1;
59 char fc;
60 char sc;
61 enum ScanDateState state = SDS_DEFAULT;
62 int flags = 0;
64 // start with the first character in both strings
65 fc = *fmt++;
66 sc = *string++;
68 while(state != SDS_DONE)
70 if(fc == '\0' && sc == '\0')
71 state = SDS_DONE;
73 switch(state)
75 case SDS_DEFAULT:
77 if(fc == '%')
79 state = SDS_SPECIFIER;
80 fc = *fmt++;
82 else
84 // the format string seems to be malformed, bail out
85 state = SDS_DONE;
88 break;
90 case SDS_SPECIFIER:
92 switch(fc)
94 case 'd': // %d - day number with leading zeros (01-31)
95 case 'e': // %e - day number with leading spaces ( 1-31)
97 flags |= FLG_MDAY;
98 state = SDS_DAY_OF_MONTH;
99 fc = *fmt++;
101 break;
103 case 'm': // %m - month number with leading zeros (01-12)
105 flags |= FLG_MON;
106 state = SDS_MONTH;
107 fc = *fmt++;
109 break;
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)
119 flags |= FLG_YEAR;
120 state = SDS_YEAR;
121 fc = *fmt++;
123 break;
125 case '-':
127 // ignore any switches between with/without leading zeros/spaces
128 fc = *fmt++;
130 break;
132 default:
134 // unknown specifier, bail out
135 state = SDS_DONE;
137 break;
140 break;
142 case SDS_DAY_OF_MONTH:
144 if(sc == fc)
146 // next separator in format string found
147 state = SDS_DEFAULT;
148 fc = *fmt++;
149 sc = *string++;
151 else if(sc >= '0' && sc <= '9')
153 // valid number found, add it to the day of month
154 res->tm_mday = res->tm_mday * 10 + sc - '0';
155 sc = *string++;
157 else
159 // unexpected character, bail out
160 state = SDS_DONE;
163 break;
165 case SDS_MONTH:
167 if(sc == fc)
169 // next separator in format string found
170 state = SDS_DEFAULT;
171 fc = *fmt++;
172 sc = *string++;
174 else if(sc >= '0' && sc <= '9')
176 // valid number found, add it to the month
177 res->tm_mon = res->tm_mon * 10 + sc - '0';
178 sc = *string++;
180 else
182 // unexpected character, bail out
183 state = SDS_DONE;
186 break;
188 case SDS_YEAR:
190 if(sc == fc)
192 // next separator in format string found
193 state = SDS_DEFAULT;
194 fc = *fmt++;
195 sc = *string++;
197 else if(sc >= '0' && sc <= '9')
199 // valid number found, add it to the year
200 res->tm_year = res->tm_year * 10 + sc - '0';
201 sc = *string++;
203 else
205 // unexpected character, bail out
206 state = SDS_DONE;
209 break;
211 default:
212 // nothing to do
213 break;
217 // finally check if the calculated values are correct, but only those which
218 // were specified in the format string
219 if((flags & FLG_MDAY) || strstr(fmt, "%d") != NULL || strstr(fmt, "%-d") != NULL || strstr(fmt, "%e") != NULL)
221 if(res->tm_mday >= 1 && res->tm_mday <= 31)
223 // nothing to adjust
225 else
227 success = 0;
230 if((flags & FLG_MON) || strstr(fmt, "%m") != NULL || strstr(fmt, "%-m") != NULL)
232 if(res->tm_mon >= 1 && res->tm_mon <= 12)
234 // tm_mon counts from 0 to 11
235 res->tm_mon--;
237 else
239 success = 0;
242 if((flags & FLG_YEAR) || strstr(fmt, "%y") != NULL || strstr(fmt, "%-y") != NULL || strstr(fmt, "%Y") != NULL || strstr(fmt, "%-Y") != NULL)
244 if((flags & FLG_4DIGIT_YEAR) || strstr(fmt, "%Y") != NULL || strstr(fmt, "%-Y") != NULL)
246 if(res->tm_year >= 1900)
248 // tm_year counts the years from 1900
249 res->tm_year -= 1900;
251 else
253 // year numbers less than 1900 are not supported
254 success = 0;
257 else
259 // 2 digit year number, must be less than 100
260 if(res->tm_year < 100)
262 if(res->tm_year < 40)
264 // tm_year counts the years from 1900
265 // if the year number is less than 40 we assume a year between
266 // 2000 and 2039 instead of between 1900 and 1939 to allow a user
267 // age of at least ~70 years.
268 res->tm_year += 100;
271 // Although we expect a two digit year number for %y we got one with more digits.
272 // Better not fail at this even if the entered string is wrong. People tend to
273 // forget the correct formatting.
274 else if(res->tm_year >= 1900)
276 // tm_year counts the years from 1900
277 res->tm_year -= 1900;
279 else
281 // numbers between 100 and 1899 are definitely not allowed
282 success = 0;
287 // finally check if the day value is correct
288 if(success == 1 && (flags & FLG_MDAY))
290 if(res->tm_mon == 1)
292 // February has 29 days at most, but we don't check for leap years here
293 if(res->tm_mday > 29)
295 success = 0;
298 else if(res->tm_mon == 3 ||
299 res->tm_mon == 5 ||
300 res->tm_mon == 8 ||
301 res->tm_mon == 10)
303 // April, June, September and November have 30 days
304 if(res->tm_mday > 30)
306 success = 0;
311 return (char *)string;