Bringing flexcat 2.15 into the main branch
[AROS.git] / tools / flexcat / src / strptime.c
blob45ecd19f816ac69477afcb0124d3bcd28177ca14
1 /*
2 * $Id$
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.
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 == ' ')
153 // ignore any spaces within the day spec
154 sc = *string++;
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';
160 sc = *string++;
162 else
164 // unexpected character, bail out
165 state = SDS_DONE;
168 break;
170 case SDS_MONTH:
172 if(sc == fc)
174 // next separator in format string found
175 state = SDS_DEFAULT;
176 fc = *fmt++;
177 sc = *string++;
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';
183 sc = *string++;
185 else
187 // unexpected character, bail out
188 state = SDS_DONE;
191 break;
193 case SDS_YEAR:
195 if(sc == fc)
197 // next separator in format string found
198 state = SDS_DEFAULT;
199 fc = *fmt++;
200 sc = *string++;
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';
206 sc = *string++;
208 else
210 // unexpected character, bail out
211 state = SDS_DONE;
214 break;
216 default:
217 // nothing to do
218 break;
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)
228 // nothing to adjust
230 else
232 success = 0;
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
240 res->tm_mon--;
242 else
244 success = 0;
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;
256 else
258 // year numbers less than 1900 are not supported
259 success = 0;
262 else
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.
273 res->tm_year += 100;
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;
284 else
286 // numbers between 100 and 1899 are definitely not allowed
287 success = 0;
292 // finally check if the day value is correct
293 if(success == 1 && (flags & FLG_MDAY))
295 if(res->tm_mon == 1)
297 // February has 29 days at most, but we don't check for leap years here
298 if(res->tm_mday > 29)
300 success = 0;
303 else if(res->tm_mon == 3 ||
304 res->tm_mon == 5 ||
305 res->tm_mon == 8 ||
306 res->tm_mon == 10)
308 // April, June, September and November have 30 days
309 if(res->tm_mday > 30)
311 success = 0;
316 return (char *)string;