Updated PCI IDs to latest snapshot.
[tangerine.git] / workbench / libs / locale / parsedate.c
blobd85f0c5e99ce518439d4aed2d23aa04a3f5a91ee
1 /*
2 Copyright © 1995-2008, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <exec/types.h>
7 #include <dos/dos.h>
8 #include <dos/stdio.h>
9 #include <proto/dos.h>
10 #include <aros/asmcall.h>
11 #include "locale_intern.h"
13 static const UWORD monthdays[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
14 static const UBYTE monthday[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
16 BOOL _getnum(LONG numchars,
17 LONG *valPtr,
18 ULONG *cPtr,
19 CONST_STRPTR *fmtTemplatePtr,
20 BOOL *checkEOFPtr,
21 const struct Locale *locale,
22 const struct Hook *getCharFunc,
23 struct LocaleBase *LocaleBase);
24 #define get2num(x) _getnum(2, (x), &c, &fmtTemplate, &checkEOF, locale, getCharFunc, LocaleBase)
25 #define get4num(x) _getnum(4, (x), &c, &fmtTemplate, &checkEOF, locale, getCharFunc, LocaleBase)
27 /*****************************************************************************
29 NAME */
30 #include <proto/locale.h>
32 AROS_LH4(BOOL, ParseDate,
34 /* SYNOPSIS */
35 AROS_LHA(const struct Locale *, locale, A0),
36 AROS_LHA(struct DateStamp *, date, A1),
37 AROS_LHA(CONST_STRPTR , fmtTemplate, A2),
38 AROS_LHA(const struct Hook *, getCharFunc, A3),
40 /* LOCATION */
41 struct LocaleBase *, LocaleBase, 27, Locale)
43 /* FUNCTION
44 This function will convert a stream of characters into an AmigaDOS
45 DateStamp structure. It will obtain its characters from the
46 getCharFunc callback hook, and the given formatting template will
47 be used to direct the parse.
49 INPUTS
50 locale - the locale to use for the formatting
51 date - where to put the converted date. If this is NULL,
52 then this function can be used to verify a date
53 string.
54 fmtTemplate - the date template used to direct the parse of the
55 data. The following FormatDate() formatting
56 controls can be used:
57 %a %A %b %B %d %e %h %H %I %m %M %p %S %y %Y
59 See FormatDate() autodoc for more information.
60 getCharFunc - A callback Hook which is used to read the data
61 from a stream. The hook is called with:
63 A0 - address of the Hook structure
64 A2 - locale pointer
65 A1 - NULL
67 BTW: The AmigaOS autodocs which state that A1
68 gets locale pointer and A2 NULL are wrong!!
70 The read character should be returned in D0. Note
71 that this is a 32 bit character not an 8 bit
72 character. Return a NULL character if you reach the
73 end of the stream.
75 RESULT
76 TRUE - If the parse could be performed.
77 FALSE - If the format of the data did not match the formatting
78 string.
80 NOTES
81 This has a few differences from the implementation in locale.library
82 v38. In particular:
83 - %p does not have to be at the end of the line.
84 - %d and %e are not effectively the same: leading spaces are
85 allowed before %e, but not before %d.
87 EXAMPLE
89 BUGS
90 %p, %b, %A and probably others accept substrings and superstrings of
91 valid strings.
93 SEE ALSO
94 FormatDate()
96 INTERNALS
98 *****************************************************************************/
100 AROS_LIBFUNC_INIT
102 ULONG c;
103 LONG day = 0, month = 0, hour = 0, min = 0, sec = 0;
104 LONG year = 1978;
105 BOOL leap, ampm = FALSE, checkEOF = TRUE;
106 if ( (fmtTemplate == NULL)
107 || (getCharFunc == NULL)
108 || (locale == NULL)
109 || (*fmtTemplate == '\0')
111 return FALSE;
113 #define GetChar()\
114 AROS_UFC3(ULONG, getCharFunc->h_Entry, \
115 AROS_UFCA(const struct Hook *, getCharFunc, A0), \
116 AROS_UFCA(const struct Locale *, locale, A2), \
117 AROS_UFCA(ULONG, 0, A1))
119 while (*fmtTemplate)
121 /* Check for EOF if we leave the loop */
122 checkEOF = TRUE;
124 if (*fmtTemplate == '%')
126 UBYTE strOffs = 0;
127 fmtTemplate++;
129 switch(*fmtTemplate++)
131 /* abbreviated weekday name */
132 case 'a':
133 strOffs = 7;
134 /* weekday name */
135 case 'A':
137 CONST_STRPTR dayStr[7];
138 BOOL dayOk[7];
139 ULONG i, a;
141 for (i= 0; i < 7; i++)
143 dayOk[i] = TRUE;
144 dayStr[i] = GetLocaleStr(locale, i + strOffs + 1);
147 c = GetChar();
148 while ((c != '\0') && (c != *fmtTemplate))
150 for (i=0; i < 7; i++)
152 a = ConvToUpper(locale, *(dayStr[i])++);
153 c = ConvToUpper(locale, c);
155 if (dayOk[i] && a)
156 if (a != c) dayOk[i] = FALSE;
158 c = GetChar();
161 /* End of stream in wrong place, or invalid */
162 if (((c == '\0') && *fmtTemplate) || (c != *fmtTemplate))
163 return FALSE;
165 /* If we didn't get a valid day, fail */
166 i = 0;
167 while ((i < 7) && !dayOk[i++])
169 if ((i == 7) && !dayOk[6])
170 return FALSE;
172 if (*fmtTemplate) fmtTemplate++;
173 checkEOF = FALSE;
174 } break; /* case 'A': */
176 /* abbreviated month name */
177 case 'b':
178 /* abbreviated month name */
179 case 'h':
180 strOffs = 12;
181 /* month name */
182 case 'B':
184 CONST_STRPTR monthStr[12];
185 BOOL monthOk[12];
186 ULONG i, a;
188 for (i = 0; i < 12; i++)
190 monthOk[i] = TRUE;
191 monthStr[i] = GetLocaleStr(locale, i + strOffs + MON_1);
194 c = GetChar();
195 while ((c != '\0') && (c != *fmtTemplate))
197 for (i=0; i < 12; i++)
199 a = ConvToUpper(locale, *(monthStr[i])++);
200 c = ConvToUpper(locale, c);
202 if (monthOk[i] && a)
203 if (a != c) monthOk[i] = FALSE;
205 c = GetChar();
208 /* End of stream in wrong place, or invalid */
209 if (((c == '\0') && *fmtTemplate) || (c != *fmtTemplate))
210 return FALSE;
212 /* If we didn't get a valid month, fail */
213 i = 0;
214 while ((i < 12) && !monthOk[i++])
216 if ((i == 12) && !monthOk[11])
217 return FALSE;
218 month = i;
220 if (*fmtTemplate) fmtTemplate++;
221 checkEOF = FALSE;
223 break;
224 } /* case 'B': */
226 /* Day no */
227 case 'd':
228 day = 0;
229 c = GetChar();
230 if (!get2num(&day))
231 return FALSE;
232 if (day-- == 0)
233 return FALSE;
235 break;
236 /* Day no., leading spaces. */
237 case 'e':
238 day = 0;
239 c = GetChar();
240 while (IsSpace(locale, c))
241 c = GetChar();
242 if (!get2num(&day))
243 return FALSE;
244 if (day-- == 0)
245 return FALSE;
247 break;
249 /* hour 24-hr style */
250 case 'H':
251 ampm = FALSE;
252 c = GetChar();
253 if (!get2num(&hour))
254 return FALSE;
255 if (hour > 23) return FALSE;
256 break;
258 /* hour 12-hr style */
259 case 'I':
260 c = GetChar();
261 if (!get2num(&hour))
262 return FALSE;
263 if (hour > 11) return FALSE;
264 break;
266 /* month num */
267 case 'm':
268 c = GetChar();
269 if (!get2num(&month))
270 return FALSE;
271 if ((month > 12) || (month == 0))
272 return FALSE;
273 break;
275 /* minutes */
276 case 'M':
277 c = GetChar();
278 if (!get2num(&min))
279 return FALSE;
281 if (min > 59) return FALSE;
282 break;
284 /* AM or PM string */
285 case 'p':
287 CONST_STRPTR amStr, pmStr;
288 BOOL amOk = TRUE, pmOk = TRUE;
289 ULONG a, b;
290 amStr = GetLocaleStr(locale, AM_STR);
291 pmStr = GetLocaleStr(locale, PM_STR);
293 c = GetChar();
294 while ((c != '\0') && (c != *fmtTemplate))
296 a = ConvToUpper(locale, *amStr++);
297 b = ConvToUpper(locale, *pmStr++);
298 c = ConvToUpper(locale, c);
300 if (amOk && a)
301 if (a != c) amOk = FALSE;
303 if (pmOk && b)
304 if (b != c) pmOk = FALSE;
306 c = GetChar();
309 /* End of stream in wrong place, or invalid */
310 if (c != *fmtTemplate)
311 return FALSE;
313 /* Check whether we got AM or PM */
314 ampm = pmOk;
316 if (*fmtTemplate) fmtTemplate++;
317 checkEOF = FALSE;
318 break;
321 /* the number of seconds */
322 case 'S':
323 c = GetChar();
324 if (!get2num(&sec))
325 return FALSE;
326 if (sec > 59) return FALSE;
327 break;
329 /* the year using two or four digits */
330 case 'y':
331 c = GetChar();
332 if (!get4num(&year))
333 return FALSE;
335 if (year >= 100 && year < 1978)
336 return FALSE;
337 if (year < 78)
338 year += 100;
339 if (year < 1900)
340 year += 1900;
341 break;
343 /* the year using four digits */
344 case 'Y':
345 c = GetChar();
346 if (IsDigit(locale, c) == FALSE)
347 return FALSE;
348 year = (c - '0') * 1000;
350 c = GetChar();
351 if (IsDigit(locale, c) == FALSE)
352 return FALSE;
353 year += (c - '0') * 100;
355 c = GetChar();
356 if (IsDigit(locale, c) == FALSE)
357 return FALSE;
358 year += (c - '0') * 10;
360 c = GetChar();
361 if (IsDigit(locale, c) == FALSE)
362 return FALSE;
363 year += (c - '0');
365 if (year < 1978)
366 return FALSE;
367 break;
369 default:
370 return FALSE;
371 break;
372 } /* switch() */
373 } /* if (char == '%') */
374 else
376 c = GetChar();
377 if (c != *fmtTemplate++)
378 return FALSE;
380 } /* while (*fmtTemplate) */
382 /* Reached end of fmtTemplate, end of input stream? */
383 if (checkEOF)
384 if ((GetChar() != 0)) return FALSE;
386 /* Is this year a leap year ? */
387 leap = (((year % 400) == 0) ||
388 (((year % 4) == 0) && !((year % 100) == 0)));
390 /* Sanity check */
391 if (month != 0 && day >=
392 (monthday[month - 1] + ((leap && (month == 2)) ? 1 : 0)))
394 return FALSE;
397 if (date)
399 /* Add the days for all years (without leap years) */
400 day += (year - 1978) * 365;
402 year--;
404 /* Add leap years */
405 day += ((year / 4) - (year / 100) + (year / 400)
406 - (494 - 19 + 4));
408 /* Add days of months */
409 day += monthdays[month - 1];
412 in monthdays, February has 28 days. Correct this in
413 leap years if month is >= March.
416 if (leap && (month >= 3)) day++;
418 date->ds_Days = day;
420 date->ds_Minute = hour * 60 + min;
421 if ((hour < 12) && ampm)
422 date->ds_Minute += 720;
423 date->ds_Tick = sec * TICKS_PER_SECOND;
425 return TRUE;
427 AROS_LIBFUNC_EXIT
428 } /* ParseDate */
431 BOOL _getnum(LONG numchars,
432 LONG *valPtr,
433 ULONG *cPtr,
434 CONST_STRPTR *fmtTemplatePtr,
435 BOOL *checkEOFPtr,
436 const struct Locale *locale,
437 const struct Hook *getCharFunc,
438 struct LocaleBase *LocaleBase)
440 LONG val;
441 ULONG c;
443 c = *cPtr;
444 //*c = GetChar();
445 if (IsDigit(locale, c) == FALSE)
446 return FALSE;
448 val = c - '0';
450 while (--numchars >= 1)
452 c = GetChar();
453 if (IsDigit(locale, c))
454 val = val * 10 + c - '0';
455 else
457 *cPtr = c;
458 if (c != **fmtTemplatePtr)
459 return FALSE;
460 if (c == '\0')
461 *checkEOFPtr = FALSE;
462 else
463 (*fmtTemplatePtr)++;
465 break;
469 *valPtr = val;
471 return TRUE;