add casts to zune macros to silence some warnings
[tangerine.git] / workbench / libs / locale / parsedate.c
blob3eeab7a07a40ed5396e58331e23b96edcbcc0def
1 /*
2 Copyright © 1995-2007, 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 #define STOP_BRAIN_DAMAGE 1 /* Oh please, do single char numbers! - Piru */
15 static const UWORD monthdays[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
16 static const UBYTE monthday[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
18 #if STOP_BRAIN_DAMAGE
19 BOOL _getnum(LONG numchars,
20 LONG *valPtr,
21 ULONG *cPtr,
22 STRPTR *fmtTemplatePtr,
23 BOOL *checkEOFPtr,
24 struct Locale *locale,
25 struct Hook *getCharFunc,
26 struct LocaleBase *LocaleBase);
27 #define get2num(x) _getnum(2, (x), &c, &fmtTemplate, &checkEOF, locale, getCharFunc, LocaleBase)
28 #define get4num(x) _getnum(4, (x), &c, &fmtTemplate, &checkEOF, locale, getCharFunc, LocaleBase)
29 #endif
31 /*****************************************************************************
33 NAME */
34 #include <proto/locale.h>
36 AROS_LH4(BOOL, ParseDate,
38 /* SYNOPSIS */
39 AROS_LHA(struct Locale *, locale, A0),
40 AROS_LHA(struct DateStamp *, date, A1),
41 AROS_LHA(STRPTR , fmtTemplate, A2),
42 AROS_LHA(struct Hook *, getCharFunc, A3),
44 /* LOCATION */
45 struct LocaleBase *, LocaleBase, 27, Locale)
47 /* FUNCTION
48 This function will convert a stream of characters into an AmigaDOS
49 DateStamp structure. It will obtain its characters from the
50 getCharFunc callback hook, and the given formatting template will
51 be used to direct the parse.
53 INPUTS
54 locale - the locale to use for the formatting
55 date - where to put the converted date. If this is NULL,
56 then this function can be used to verify a date
57 string.
58 fmtTemplate - the date template used to direct the parse of the
59 data. The following FormatDate() formatting
60 controls can be used:
61 %a %A %b %B %d %e %h %H %I %m %M %p %S %y %Y
63 See FormatDate() autodoc for more information.
64 getCharFunc - A callback Hook which is used to read the data
65 from a stream. The hook is called with:
67 A0 - address of the Hook structure
68 A2 - locale pointer
69 A1 - NULL
71 BTW: The AmigaOS autodocs which state that A1
72 gets locale pointer and A2 NULL are wrong!!
74 The read character should be returned in D0. Note
75 that this is a 32 bit character not an 8 bit
76 character. Return a NULL character if you reach the
77 end of the stream.
79 RESULT
80 TRUE - If the parse could be performed.
81 FALSE - If the format of the data did not match the formatting
82 string.
84 NOTES
85 This has a few differences from the implementation in locale.library
86 v38. In particular:
87 - %p does not have to be at the end of the line.
88 - %d and %e are not effectively the same, ie %d requires a leading
89 zero, but %e can not handle leading 0's.
91 EXAMPLE
93 BUGS
94 %d, %e probably needs some work.
96 SEE ALSO
97 FormatDate()
99 INTERNALS
101 *****************************************************************************/
103 AROS_LIBFUNC_INIT
105 ULONG c;
106 LONG day = 0, month = 0, hour = 0, min = 0, sec = 0;
107 LONG year = 1978;
108 BOOL leap, ampm = FALSE, checkEOF = TRUE;
109 if ( (fmtTemplate == NULL)
110 || (getCharFunc == NULL)
111 || (locale == NULL)
112 || (*fmtTemplate == '\0')
114 return FALSE;
116 #define GetChar()\
117 AROS_UFC3(ULONG, getCharFunc->h_Entry, \
118 AROS_UFCA(struct Hook *, getCharFunc, A0), \
119 AROS_UFCA(struct Locale *, locale, A2), \
120 AROS_UFCA(ULONG, 0, A1))
122 while (*fmtTemplate)
124 /* Check for EOF if we leave the loop */
125 checkEOF = TRUE;
127 if (*fmtTemplate == '%')
129 UBYTE strOffs = 0;
130 fmtTemplate++;
132 switch(*fmtTemplate++)
134 /* abbreviated weekday name */
135 case 'a':
136 strOffs = 7;
137 /* weekday name */
138 case 'A':
140 STRPTR dayStr[7];
141 BOOL dayOk[7];
142 ULONG i, a;
144 for (i= 0; i < 7; i++)
146 dayOk[i] = TRUE;
147 dayStr[i] = GetLocaleStr(locale, i + strOffs + 1);
150 c = GetChar();
151 while ((c != '\0') && (c != *fmtTemplate))
153 for (i=0; i < 7; i++)
155 a = ConvToUpper(locale, *(dayStr[i])++);
156 c = ConvToUpper(locale, c);
158 if (dayOk[i] && a)
159 if (a != c) dayOk[i] = FALSE;
161 c = GetChar();
164 /* End of stream in wrong place, or invalid */
165 if (((c == '\0') && *fmtTemplate) || (c != *fmtTemplate))
166 return FALSE;
168 /* If we didn't get a valid day, fail */
169 i = 0;
170 while ((i < 7) && (dayOk[i++] == FALSE))
172 if ((i == 7) && (dayOk[6] == FALSE))
173 return FALSE;
175 if (*fmtTemplate) fmtTemplate++;
176 checkEOF = FALSE;
177 } break; /* case 'A': */
179 /* abbreviated month name */
180 case 'b':
181 /* abbreviated month name */
182 case 'h':
183 strOffs = 12;
184 /* month name */
185 case 'B':
187 STRPTR monthStr[12];
188 BOOL monthOk[12];
189 ULONG i, a;
191 for (i = 0; i < 12; i++)
193 monthOk[i] = TRUE;
194 monthStr[i] = GetLocaleStr(locale, i + strOffs + MON_1);
197 c = GetChar();
198 while ((c != '\0') && (c != *fmtTemplate))
200 for (i=0; i < 12; i++)
202 a = ConvToUpper(locale, *(monthStr[i])++);
203 c = ConvToUpper(locale, c);
205 if (monthOk[i] && a)
206 if (a != c) monthOk[i] = FALSE;
208 c = GetChar();
211 /* End of stream in wrong place, or invalid */
212 if (((c == '\0') && *fmtTemplate) || (c != *fmtTemplate))
213 return FALSE;
215 /* If we didn't get a valid month, fail */
216 i = 0;
217 while ((i < 12) && (monthOk[i++] == FALSE))
219 if ((i == 12) && (monthOk[11] == FALSE))
220 return FALSE;
221 month = i;
223 if (*fmtTemplate) fmtTemplate++;
224 checkEOF = FALSE;
226 break;
227 } /* case 'B': */
229 #if 0
230 /* Day no */
231 case 'd':
232 c = GetChar();
233 if (IsDigit(locale, c) == FALSE)
234 return FALSE;
236 day = (c - '0') * 10;
237 c = GetChar();
238 if (IsDigit(locale, c) == FALSE)
239 return FALSE;
240 day += (c - '0');
242 /* Day 0 is undefined. */
243 if (day == 0) return FALSE;
244 day--;
246 /* day is unsigned, so day < 0 is not possible */
247 if (day > 31)
248 return FALSE;
250 break;
251 #endif
252 #if 1
253 /* These are really the same - Piru. */
255 /* Day no */
256 case 'd':
257 /* Day no., leading spaces. */
258 case 'e':
259 day = 0;
261 c = GetChar();
262 while (IsSpace(locale, c) == TRUE)
263 c = GetChar();
265 if (!get2num(&day))
266 return FALSE;
268 /* Day 0 is undefined. */
269 if (day == 0)
270 return FALSE;
271 day--;
273 /* day is unsigned, so day < 0 is not possible */
274 if (day > 31)
275 return FALSE;
277 break;
278 #else
279 /* Day no., leading spaces. */
280 case 'e':
281 day = 0;
283 c = GetChar();
284 while (IsSpace(locale, c) == TRUE)
285 c = GetChar();
287 if (IsDigit(locale, c) == FALSE)
288 return FALSE;
289 day = (c - '0');
291 c = GetChar();
292 if (IsDigit(locale, c) == TRUE)
294 day *= 10;
295 day += (c - '0');
297 else
299 if (c != *fmtTemplate++)
300 return FALSE;
301 if (c == '\0')
302 checkEOF = FALSE;
304 if (day == 0) return FALSE;
305 day--;
306 break;
308 #endif
310 /* hour 24-hr style */
311 case 'H':
312 ampm = FALSE;
313 #if STOP_BRAIN_DAMAGE
314 c = GetChar();
315 if (!get2num(&hour))
316 return FALSE;
317 #else
318 c = GetChar();
319 if (IsDigit(locale, c) == FALSE)
320 return FALSE;
322 c = c - '0';
323 hour = c * 10;
325 c = GetChar();
326 if (IsDigit(locale, c) == FALSE)
327 return FALSE;
328 hour += (c - '0');
329 #endif
330 if (hour > 23) return FALSE;
331 break;
333 /* hour 12-hr style */
334 case 'I':
335 #if STOP_BRAIN_DAMAGE
336 c = GetChar();
337 if (!get2num(&hour))
338 return FALSE;
339 #else
340 c = GetChar();
341 if (IsDigit(locale, c) == FALSE)
342 return FALSE;
344 c = c - '0';
345 hour = c * 10;
347 c = GetChar();
348 if (IsDigit(locale, c) == FALSE)
349 return FALSE;
350 hour += (c - '0');
351 #endif
352 if (hour > 11) return FALSE;
353 break;
355 /* month num */
356 case 'm':
357 #if STOP_BRAIN_DAMAGE
358 c = GetChar();
359 if (!get2num(&month))
360 return FALSE;
361 #else
362 c = GetChar();
363 if (IsDigit(locale, c) == FALSE)
364 return FALSE;
366 month = (c - '0') * 10;
367 c = GetChar();
368 if (IsDigit(locale, c) == FALSE)
369 return FALSE;
370 month += (c - '0');
371 #endif
372 if ((month > 12) || (month == 0))
373 return FALSE;
374 break;
376 /* minutes */
377 case 'M':
378 #if STOP_BRAIN_DAMAGE
379 c = GetChar();
380 if (!get2num(&min))
381 return FALSE;
382 #else
383 c = GetChar();
384 if (IsDigit(locale, c) == FALSE)
385 return FALSE;
387 min = (c - '0') * 10;
388 c = GetChar();
389 if (IsDigit(locale, c) == FALSE)
390 return FALSE;
391 min += (c - '0');
392 #endif
394 if (min > 59) return FALSE;
395 break;
397 /* AM or PM string */
398 case 'p':
400 STRPTR amStr, pmStr;
401 BOOL amOk = TRUE, pmOk = TRUE;
402 ULONG a, b;
403 amStr = GetLocaleStr(locale, AM_STR);
404 pmStr = GetLocaleStr(locale, PM_STR);
406 c = GetChar();
407 while ((c != '\0') && (c != *fmtTemplate))
409 a = ConvToUpper(locale, *amStr++);
410 b = ConvToUpper(locale, *pmStr++);
411 c = ConvToUpper(locale, c);
413 if (amOk && a)
414 if (a != c) amOk = FALSE;
416 if (pmOk && b)
417 if (b != c) pmOk = FALSE;
419 c = GetChar();
422 /* End of stream in wrong place, or invalid */
423 if (((c == '\0') && *fmtTemplate) || (c != *fmtTemplate))
424 return FALSE;
426 /* Check whether we got AM or PM */
427 if (pmOk == TRUE) ampm = TRUE;
428 else if (amOk == TRUE) ampm = FALSE;
430 if (*fmtTemplate) fmtTemplate++;
431 checkEOF = FALSE;
432 break;
435 /* the number of seconds */
436 case 'S':
437 #if STOP_BRAIN_DAMAGE
438 c = GetChar();
439 if (!get2num(&sec))
440 return FALSE;
441 #else
442 c = GetChar();
443 if (IsDigit(locale, c) == FALSE)
444 return FALSE;
446 sec = (c - '0') * 10;
447 c = GetChar();
448 if (IsDigit(locale, c) == FALSE)
449 return FALSE;
450 sec += (c - '0');
451 #endif
452 if (sec > 59) return FALSE;
453 break;
455 /* the year using two or four digits */
456 case 'y':
457 #if STOP_BRAIN_DAMAGE
458 c = GetChar();
459 if (!get4num(&year))
460 return FALSE;
462 if (year < 78)
464 year += 100;
466 if (year < 1900)
468 year += 1900;
470 #else
471 c = GetChar();
472 if (IsDigit(locale, c) == FALSE)
473 return FALSE;
475 year = (c - '0') * 10;
476 c = GetChar();
477 if (IsDigit(locale, c) == FALSE)
478 return FALSE;
479 year += (c - '0');
480 year += (year < 78) ? 2000 : 1900;
481 #endif
482 break;
484 /* the year using four digits */
485 case 'Y':
486 c = GetChar();
487 if (IsDigit(locale, c) == FALSE)
488 return FALSE;
489 year = (c - '0') * 1000;
491 c = GetChar();
492 if (IsDigit(locale, c) == FALSE)
493 return FALSE;
494 year += (c - '0') * 100;
496 c = GetChar();
497 if (IsDigit(locale, c) == FALSE)
498 return FALSE;
499 year += (c - '0') * 10;
501 c = GetChar();
502 if (IsDigit(locale, c) == FALSE)
503 return FALSE;
504 year += (c - '0');
506 if (year < 1978)
507 return FALSE;
508 break;
510 default:
511 return FALSE;
512 break;
513 } /* switch() */
514 } /* if (char == '%') */
515 else
517 c = GetChar();
518 if (c != *fmtTemplate++)
519 return FALSE;
521 } /* while (*fmtTemplate) */
523 /* Reached end of fmtTemplate, end of input stream? */
524 if (checkEOF)
525 if ((GetChar() != 0)) return FALSE;
527 /* Is this year a leap year ? */
528 leap = (((year % 400) == 0) ||
529 (((year % 4) == 0) && !((year % 100) == 0)));
531 /* Sanity check - Piru */
532 if (month && day >=
533 (monthday[month - 1] + ((leap && (month == 2)) ? 1 : 0)))
535 return FALSE;
538 if (date)
540 #if 1
541 /* stegerg: based on dos.library/strtodate */
543 /* First year must be 1978 */
544 if (year < 1978)
545 return FALSE;
547 /* Add the days for all years (without leap years) */
548 day += (year - 1978) * 365;
550 year--;
552 /* Add leap years */
553 day += ((year / 4) - (year / 100) + (year / 400)
554 - (494 - 19 + 4));
556 /* Add days of months */
557 day += monthdays[month - 1];
560 in monthdays, February has 28 days. Correct this in
561 leap years if month is >= March.
564 if (leap && (month >= 3)) day++;
566 date->ds_Days = day;
568 #else
569 year -= 1978;
571 if (year > 2)
572 day += (year-3) / 4 + 1;
573 else if ((year == 2) && (month > 2))
574 day += 1;
576 date->ds_Days = year * 365 + day + monthdays[month - 1];
577 #endif
579 date->ds_Minute = hour * 60 + min;
580 if ((hour < 12) && ampm)
581 date->ds_Minute += 720;
582 date->ds_Tick = sec * TICKS_PER_SECOND;
584 return TRUE;
586 AROS_LIBFUNC_EXIT
587 } /* ParseDate */
590 #if STOP_BRAIN_DAMAGE
592 BOOL _getnum(LONG numchars,
593 LONG *valPtr,
594 ULONG *cPtr,
595 STRPTR *fmtTemplatePtr,
596 BOOL *checkEOFPtr,
597 struct Locale *locale,
598 struct Hook *getCharFunc,
599 struct LocaleBase *LocaleBase)
601 LONG val;
602 ULONG c;
604 c = *cPtr;
605 //*c = GetChar();
606 if (IsDigit(locale, c) == FALSE)
607 return FALSE;
609 val = c - '0';
611 while (--numchars >= 1)
613 c = GetChar();
614 if (IsDigit(locale, c))
615 val = val * 10 + c - '0';
616 else
618 *cPtr = c;
619 if (c != *(*fmtTemplatePtr)++)
620 return FALSE;
621 if (c == '\0')
622 *checkEOFPtr = FALSE;
624 break;
628 *valPtr = val;
630 return TRUE;
633 #endif