locale.library: use system default locale if locale argument is NULL.
[AROS.git] / workbench / libs / locale / parsedate.c
blob1ecaa5a96781c9a59916e8d808db08b124fa4f7d
1 /*
2 Copyright © 1995-2011, 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] =
14 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
15 static const UBYTE monthday[12] =
16 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
18 BOOL _getnum(LONG numchars,
19 LONG * valPtr,
20 ULONG * cPtr,
21 CONST_STRPTR * fmtTemplatePtr,
22 BOOL * checkEOFPtr,
23 const struct Locale *locale,
24 const struct Hook *getCharFunc, struct LocaleBase *LocaleBase);
25 #define get2num(x) _getnum(2, (x), &c, &fmtTemplate, &checkEOF, locale, getCharFunc, LocaleBase)
26 #define get4num(x) _getnum(4, (x), &c, &fmtTemplate, &checkEOF, locale, getCharFunc, LocaleBase)
28 /*****************************************************************************
30 NAME */
31 #include <proto/locale.h>
33 AROS_LH4(BOOL, ParseDate,
35 /* SYNOPSIS */
36 AROS_LHA(const struct Locale *, locale, A0),
37 AROS_LHA(struct DateStamp *, date, A1),
38 AROS_LHA(CONST_STRPTR , fmtTemplate, A2),
39 AROS_LHA(const struct Hook *, getCharFunc, A3),
41 /* LOCATION */
42 struct LocaleBase *, LocaleBase, 27, Locale)
44 /* FUNCTION
45 This function will convert a stream of characters into an AmigaDOS
46 DateStamp structure. It will obtain its characters from the
47 getCharFunc callback hook, and the given formatting template will
48 be used to direct the parse.
50 INPUTS
51 locale - the locale to use for the formatting or NULL for
52 the system default locale.
53 date - where to put the converted date. If this is NULL,
54 then this function can be used to verify a date
55 string.
56 fmtTemplate - the date template used to direct the parse of the
57 data. The following FormatDate() formatting
58 controls can be used:
59 %a %A %b %B %d %e %h %H %I %m %M %p %S %y %Y
61 See FormatDate() autodoc for more information.
62 getCharFunc - A callback Hook which is used to read the data
63 from a stream. The hook is called with:
65 A0 - address of the Hook structure
66 A2 - locale pointer
67 A1 - NULL
69 BTW: The AmigaOS autodocs which state that A1
70 gets locale pointer and A2 NULL are wrong!!
72 The read character should be returned in D0. Note
73 that this is a 32 bit character not an 8 bit
74 character. Return a NULL character if you reach the
75 end of the stream.
77 RESULT
78 TRUE - If the parse could be performed.
79 FALSE - If the format of the data did not match the formatting
80 string.
82 NOTES
83 This has a few differences from the implementation in locale.library
84 v38. In particular:
85 - %p does not have to be at the end of the line.
86 - %d and %e are not effectively the same: leading spaces are
87 allowed before %e, but not before %d.
89 EXAMPLE
91 BUGS
92 %p, %b, %A and probably others accept substrings and superstrings of
93 valid strings.
95 SEE ALSO
96 FormatDate()
98 INTERNALS
100 *****************************************************************************/
102 AROS_LIBFUNC_INIT
104 ULONG c;
105 LONG day = 0, month = 0, hour = 0, min = 0, sec = 0;
106 LONG year = 1978;
107 BOOL leap, am = FALSE, pm = FALSE, checkEOF = TRUE;
108 struct Locale *def_locale = NULL;
109 BOOL retval = FALSE;
111 if ((fmtTemplate == NULL)
112 || (getCharFunc == NULL)
113 || (*fmtTemplate == '\0'))
114 return FALSE;
116 if (locale == NULL)
118 locale = OpenLocale(NULL);
119 if (locale == NULL)
120 return FALSE;
121 def_locale = (struct Locale *)locale;
124 #define GetChar()\
125 AROS_UFC3(ULONG, getCharFunc->h_Entry, \
126 AROS_UFCA(const struct Hook *, getCharFunc, A0), \
127 AROS_UFCA(const struct Locale *, locale, A2), \
128 AROS_UFCA(ULONG, 0, A1))
130 while (*fmtTemplate)
132 /* Check for EOF if we leave the loop */
133 checkEOF = TRUE;
135 if (*fmtTemplate == '%')
137 UBYTE strOffs = 0;
138 fmtTemplate++;
140 switch (*fmtTemplate++)
142 /* abbreviated weekday name */
143 case 'a':
144 strOffs = 7;
145 /* weekday name */
146 case 'A':
148 CONST_STRPTR dayStr[7];
149 BOOL dayOk[7];
150 ULONG i, a;
152 for (i = 0; i < 7; i++)
154 dayOk[i] = TRUE;
155 dayStr[i] = GetLocaleStr(locale, i + strOffs + 1);
158 c = GetChar();
159 while ((c != '\0') && (c != *fmtTemplate))
161 for (i = 0; i < 7; i++)
163 a = ConvToUpper(locale, *(dayStr[i])++);
164 c = ConvToUpper(locale, c);
166 if (dayOk[i] && a)
167 if (a != c)
168 dayOk[i] = FALSE;
170 c = GetChar();
173 /* End of stream in wrong place, or invalid */
174 if (((c == '\0') && *fmtTemplate)
175 || (c != *fmtTemplate))
176 goto end;
178 /* If we didn't get a valid day, fail */
179 i = 0;
180 while ((i < 7) && !dayOk[i++])
182 if ((i == 7) && !dayOk[6])
183 goto end;
185 if (*fmtTemplate)
186 fmtTemplate++;
187 checkEOF = FALSE;
189 break; /* case 'A': */
191 /* abbreviated month name */
192 case 'b':
193 /* abbreviated month name */
194 case 'h':
195 /* month name */
196 case 'B':
198 CONST_STRPTR monthStr[24];
199 BOOL monthOk[24];
200 ULONG i, a;
202 for (i = 0; i < 24; i++)
204 monthOk[i] = TRUE;
205 monthStr[i] =
206 GetLocaleStr(locale, i + strOffs + MON_1);
209 c = GetChar();
210 while ((c != '\0') && (c != *fmtTemplate))
212 for (i = 0; i < 24; i++)
214 a = ConvToUpper(locale, *monthStr[i]);
215 if (a != '\0')
216 monthStr[i]++;
217 c = ConvToUpper(locale, c);
219 if (monthOk[i])
220 if (a != c)
221 monthOk[i] = FALSE;
223 c = GetChar();
225 for (i = 0; i < 24; i++)
227 if (monthOk[i] && *monthStr[i] == '\0')
228 month = i % 12 + 1;
231 /* If we didn't get a valid month, fail */
232 if (month == 0)
233 goto end;
235 /* End of stream in wrong place, or invalid */
236 if (((c == '\0') && *fmtTemplate)
237 || (c != *fmtTemplate))
238 goto end;
240 if (*fmtTemplate)
241 fmtTemplate++;
242 checkEOF = FALSE;
244 break;
245 } /* case 'B': */
247 /* Day no */
248 case 'd':
249 day = 0;
250 c = GetChar();
251 if (!get2num(&day))
252 goto end;
253 if (day-- == 0)
254 goto end;
256 break;
257 /* Day no., leading spaces. */
258 case 'e':
259 day = 0;
260 c = GetChar();
261 while (IsSpace(locale, c))
262 c = GetChar();
263 if (!get2num(&day))
264 goto end;
265 if (day-- == 0)
266 goto end;
268 break;
270 /* hour 24-hr style */
271 case 'H':
272 am = pm = FALSE;
273 c = GetChar();
274 if (!get2num(&hour))
275 goto end;
276 if (hour > 23)
277 goto end;
278 break;
280 /* hour 12-hr style */
281 case 'I':
282 c = GetChar();
283 if (!get2num(&hour))
284 goto end;
285 if ((hour > 12) || (hour == 0))
286 goto end;
287 break;
289 /* month num */
290 case 'm':
291 c = GetChar();
292 if (!get2num(&month))
293 goto end;
294 if ((month > 12) || (month == 0))
295 goto end;
296 break;
298 /* minutes */
299 case 'M':
300 c = GetChar();
301 if (!get2num(&min))
302 goto end;
304 if (min > 59)
305 goto end;
306 break;
308 /* AM or PM string */
309 case 'p':
311 CONST_STRPTR amStr, pmStr;
312 BOOL amOk = TRUE, pmOk = TRUE;
313 ULONG a, b;
314 amStr = GetLocaleStr(locale, AM_STR);
315 pmStr = GetLocaleStr(locale, PM_STR);
317 c = GetChar();
318 while ((c != '\0') && (c != *fmtTemplate))
320 a = ConvToUpper(locale, *amStr++);
321 b = ConvToUpper(locale, *pmStr++);
322 c = ConvToUpper(locale, c);
324 if (amOk && a)
325 if (a != c)
326 amOk = FALSE;
328 if (pmOk && b)
329 if (b != c)
330 pmOk = FALSE;
332 c = GetChar();
335 /* End of stream in wrong place, or invalid */
336 if (c != *fmtTemplate)
337 goto end;
339 /* Check whether we got AM or PM */
340 am = amOk;
341 pm = pmOk;
343 if (*fmtTemplate)
344 fmtTemplate++;
345 checkEOF = FALSE;
346 break;
349 /* the number of seconds */
350 case 'S':
351 c = GetChar();
352 if (!get2num(&sec))
353 goto end;
354 if (sec > 59)
355 goto end;
356 break;
358 /* the year using two or four digits */
359 case 'y':
360 c = GetChar();
361 if (!get4num(&year))
362 goto end;
364 if (year >= 100 && year < 1978)
365 goto end;
366 if (year < 78)
367 year += 100;
368 if (year < 1900)
369 year += 1900;
370 break;
372 /* the year using four digits */
373 case 'Y':
374 c = GetChar();
375 if (IsDigit(locale, c) == FALSE)
376 goto end;
377 year = (c - '0') * 1000;
379 c = GetChar();
380 if (IsDigit(locale, c) == FALSE)
381 goto end;
382 year += (c - '0') * 100;
384 c = GetChar();
385 if (IsDigit(locale, c) == FALSE)
386 goto end;
387 year += (c - '0') * 10;
389 c = GetChar();
390 if (IsDigit(locale, c) == FALSE)
391 goto end;
392 year += (c - '0');
394 if (year < 1978)
395 goto end;
396 break;
398 default:
399 goto end;
400 break;
401 } /* switch() */
402 } /* if (char == '%') */
403 else
405 c = GetChar();
406 if (c != *fmtTemplate++)
407 goto end;
409 } /* while (*fmtTemplate) */
411 /* Reached end of fmtTemplate, end of input stream? */
412 if (checkEOF)
413 if ((GetChar() != 0))
414 goto end;
416 /* Is this year a leap year ? */
417 leap = (((year % 400) == 0) ||
418 (((year % 4) == 0) && !((year % 100) == 0)));
420 /* Sanity check */
421 if (month != 0 && day >=
422 (monthday[month - 1] + ((leap && (month == 2)) ? 1 : 0)))
424 goto end;
427 if (date)
429 /* Add the days for all years (without leap years) */
430 day += (year - 1978) * 365;
432 year--;
434 /* Add leap years */
435 day += ((year / 4) - (year / 100) + (year / 400) - (494 - 19 + 4));
437 /* Add days of months */
438 day += monthdays[month - 1];
441 in monthdays, February has 28 days. Correct this in
442 leap years if month is >= March.
445 if (leap && (month >= 3))
446 day++;
448 date->ds_Days = day;
450 date->ds_Minute = hour * 60 + min;
451 if ((hour == 12) && am)
452 date->ds_Minute -= 720;
453 if ((hour < 12) && pm)
454 date->ds_Minute += 720;
455 date->ds_Tick = sec * TICKS_PER_SECOND;
457 retval = TRUE;
459 end:
460 CloseLocale(def_locale);
462 return retval;
464 AROS_LIBFUNC_EXIT
468 BOOL _getnum(LONG numchars,
469 LONG * valPtr,
470 ULONG * cPtr,
471 CONST_STRPTR * fmtTemplatePtr,
472 BOOL * checkEOFPtr,
473 const struct Locale * locale,
474 const struct Hook * getCharFunc, struct LocaleBase * LocaleBase)
476 LONG val;
477 ULONG c;
479 c = *cPtr;
480 //*c = GetChar();
481 if (IsDigit(locale, c) == FALSE)
482 return FALSE;
484 val = c - '0';
486 while (--numchars >= 1)
488 c = GetChar();
489 if (IsDigit(locale, c))
490 val = val * 10 + c - '0';
491 else
493 *cPtr = c;
494 if (c != **fmtTemplatePtr)
495 return FALSE;
496 if (c == '\0')
497 *checkEOFPtr = FALSE;
498 else
499 (*fmtTemplatePtr)++;
501 break;
505 *valPtr = val;
507 return TRUE;