- Tabs to spaces.
[AROS.git] / workbench / libs / locale / parsedate.c
blob52ff4cf61b5451c83070bb25724bf4dcc98b02a0
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
52 date - where to put the converted date. If this is NULL,
53 then this function can be used to verify a date
54 string.
55 fmtTemplate - the date template used to direct the parse of the
56 data. The following FormatDate() formatting
57 controls can be used:
58 %a %A %b %B %d %e %h %H %I %m %M %p %S %y %Y
60 See FormatDate() autodoc for more information.
61 getCharFunc - A callback Hook which is used to read the data
62 from a stream. The hook is called with:
64 A0 - address of the Hook structure
65 A2 - locale pointer
66 A1 - NULL
68 BTW: The AmigaOS autodocs which state that A1
69 gets locale pointer and A2 NULL are wrong!!
71 The read character should be returned in D0. Note
72 that this is a 32 bit character not an 8 bit
73 character. Return a NULL character if you reach the
74 end of the stream.
76 RESULT
77 TRUE - If the parse could be performed.
78 FALSE - If the format of the data did not match the formatting
79 string.
81 NOTES
82 This has a few differences from the implementation in locale.library
83 v38. In particular:
84 - %p does not have to be at the end of the line.
85 - %d and %e are not effectively the same: leading spaces are
86 allowed before %e, but not before %d.
88 EXAMPLE
90 BUGS
91 %p, %b, %A and probably others accept substrings and superstrings of
92 valid strings.
94 SEE ALSO
95 FormatDate()
97 INTERNALS
99 *****************************************************************************/
101 AROS_LIBFUNC_INIT
103 ULONG c;
104 LONG day = 0, month = 0, hour = 0, min = 0, sec = 0;
105 LONG year = 1978;
106 BOOL leap, ampm = FALSE, checkEOF = TRUE;
107 if ((fmtTemplate == NULL)
108 || (getCharFunc == NULL)
109 || (locale == NULL) || (*fmtTemplate == '\0'))
110 return FALSE;
112 #define GetChar()\
113 AROS_UFC3(ULONG, getCharFunc->h_Entry, \
114 AROS_UFCA(const struct Hook *, getCharFunc, A0), \
115 AROS_UFCA(const struct Locale *, locale, A2), \
116 AROS_UFCA(ULONG, 0, A1))
118 while (*fmtTemplate)
120 /* Check for EOF if we leave the loop */
121 checkEOF = TRUE;
123 if (*fmtTemplate == '%')
125 UBYTE strOffs = 0;
126 fmtTemplate++;
128 switch (*fmtTemplate++)
130 /* abbreviated weekday name */
131 case 'a':
132 strOffs = 7;
133 /* weekday name */
134 case 'A':
136 CONST_STRPTR dayStr[7];
137 BOOL dayOk[7];
138 ULONG i, a;
140 for (i = 0; i < 7; i++)
142 dayOk[i] = TRUE;
143 dayStr[i] = GetLocaleStr(locale, i + strOffs + 1);
146 c = GetChar();
147 while ((c != '\0') && (c != *fmtTemplate))
149 for (i = 0; i < 7; i++)
151 a = ConvToUpper(locale, *(dayStr[i])++);
152 c = ConvToUpper(locale, c);
154 if (dayOk[i] && a)
155 if (a != c)
156 dayOk[i] = FALSE;
158 c = GetChar();
161 /* End of stream in wrong place, or invalid */
162 if (((c == '\0') && *fmtTemplate)
163 || (c != *fmtTemplate))
164 return FALSE;
166 /* If we didn't get a valid day, fail */
167 i = 0;
168 while ((i < 7) && !dayOk[i++])
170 if ((i == 7) && !dayOk[6])
171 return FALSE;
173 if (*fmtTemplate)
174 fmtTemplate++;
175 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 CONST_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] =
195 GetLocaleStr(locale, i + strOffs + MON_1);
198 c = GetChar();
199 while ((c != '\0') && (c != *fmtTemplate))
201 for (i = 0; i < 12; i++)
203 a = ConvToUpper(locale, *(monthStr[i])++);
204 c = ConvToUpper(locale, c);
206 if (monthOk[i] && a)
207 if (a != c)
208 monthOk[i] = FALSE;
210 c = GetChar();
213 /* End of stream in wrong place, or invalid */
214 if (((c == '\0') && *fmtTemplate)
215 || (c != *fmtTemplate))
216 return FALSE;
218 /* If we didn't get a valid month, fail */
219 i = 0;
220 while ((i < 12) && !monthOk[i++])
222 if ((i == 12) && !monthOk[11])
223 return FALSE;
224 month = i;
226 if (*fmtTemplate)
227 fmtTemplate++;
228 checkEOF = FALSE;
230 break;
231 } /* case 'B': */
233 /* Day no */
234 case 'd':
235 day = 0;
236 c = GetChar();
237 if (!get2num(&day))
238 return FALSE;
239 if (day-- == 0)
240 return FALSE;
242 break;
243 /* Day no., leading spaces. */
244 case 'e':
245 day = 0;
246 c = GetChar();
247 while (IsSpace(locale, c))
248 c = GetChar();
249 if (!get2num(&day))
250 return FALSE;
251 if (day-- == 0)
252 return FALSE;
254 break;
256 /* hour 24-hr style */
257 case 'H':
258 ampm = FALSE;
259 c = GetChar();
260 if (!get2num(&hour))
261 return FALSE;
262 if (hour > 23)
263 return FALSE;
264 break;
266 /* hour 12-hr style */
267 case 'I':
268 c = GetChar();
269 if (!get2num(&hour))
270 return FALSE;
271 if (hour > 11)
272 return FALSE;
273 break;
275 /* month num */
276 case 'm':
277 c = GetChar();
278 if (!get2num(&month))
279 return FALSE;
280 if ((month > 12) || (month == 0))
281 return FALSE;
282 break;
284 /* minutes */
285 case 'M':
286 c = GetChar();
287 if (!get2num(&min))
288 return FALSE;
290 if (min > 59)
291 return FALSE;
292 break;
294 /* AM or PM string */
295 case 'p':
297 CONST_STRPTR amStr, pmStr;
298 BOOL amOk = TRUE, pmOk = TRUE;
299 ULONG a, b;
300 amStr = GetLocaleStr(locale, AM_STR);
301 pmStr = GetLocaleStr(locale, PM_STR);
303 c = GetChar();
304 while ((c != '\0') && (c != *fmtTemplate))
306 a = ConvToUpper(locale, *amStr++);
307 b = ConvToUpper(locale, *pmStr++);
308 c = ConvToUpper(locale, c);
310 if (amOk && a)
311 if (a != c)
312 amOk = FALSE;
314 if (pmOk && b)
315 if (b != c)
316 pmOk = FALSE;
318 c = GetChar();
321 /* End of stream in wrong place, or invalid */
322 if (c != *fmtTemplate)
323 return FALSE;
325 /* Check whether we got AM or PM */
326 ampm = pmOk;
328 if (*fmtTemplate)
329 fmtTemplate++;
330 checkEOF = FALSE;
331 break;
334 /* the number of seconds */
335 case 'S':
336 c = GetChar();
337 if (!get2num(&sec))
338 return FALSE;
339 if (sec > 59)
340 return FALSE;
341 break;
343 /* the year using two or four digits */
344 case 'y':
345 c = GetChar();
346 if (!get4num(&year))
347 return FALSE;
349 if (year >= 100 && year < 1978)
350 return FALSE;
351 if (year < 78)
352 year += 100;
353 if (year < 1900)
354 year += 1900;
355 break;
357 /* the year using four digits */
358 case 'Y':
359 c = GetChar();
360 if (IsDigit(locale, c) == FALSE)
361 return FALSE;
362 year = (c - '0') * 1000;
364 c = GetChar();
365 if (IsDigit(locale, c) == FALSE)
366 return FALSE;
367 year += (c - '0') * 100;
369 c = GetChar();
370 if (IsDigit(locale, c) == FALSE)
371 return FALSE;
372 year += (c - '0') * 10;
374 c = GetChar();
375 if (IsDigit(locale, c) == FALSE)
376 return FALSE;
377 year += (c - '0');
379 if (year < 1978)
380 return FALSE;
381 break;
383 default:
384 return FALSE;
385 break;
386 } /* switch() */
387 } /* if (char == '%') */
388 else
390 c = GetChar();
391 if (c != *fmtTemplate++)
392 return FALSE;
394 } /* while (*fmtTemplate) */
396 /* Reached end of fmtTemplate, end of input stream? */
397 if (checkEOF)
398 if ((GetChar() != 0))
399 return FALSE;
401 /* Is this year a leap year ? */
402 leap = (((year % 400) == 0) ||
403 (((year % 4) == 0) && !((year % 100) == 0)));
405 /* Sanity check */
406 if (month != 0 && day >=
407 (monthday[month - 1] + ((leap && (month == 2)) ? 1 : 0)))
409 return FALSE;
412 if (date)
414 /* Add the days for all years (without leap years) */
415 day += (year - 1978) * 365;
417 year--;
419 /* Add leap years */
420 day += ((year / 4) - (year / 100) + (year / 400) - (494 - 19 + 4));
422 /* Add days of months */
423 day += monthdays[month - 1];
426 in monthdays, February has 28 days. Correct this in
427 leap years if month is >= March.
430 if (leap && (month >= 3))
431 day++;
433 date->ds_Days = day;
435 date->ds_Minute = hour * 60 + min;
436 if ((hour < 12) && ampm)
437 date->ds_Minute += 720;
438 date->ds_Tick = sec * TICKS_PER_SECOND;
440 return TRUE;
442 AROS_LIBFUNC_EXIT
446 BOOL _getnum(LONG numchars,
447 LONG * valPtr,
448 ULONG * cPtr,
449 CONST_STRPTR * fmtTemplatePtr,
450 BOOL * checkEOFPtr,
451 const struct Locale * locale,
452 const struct Hook * getCharFunc, struct LocaleBase * LocaleBase)
454 LONG val;
455 ULONG c;
457 c = *cPtr;
458 //*c = GetChar();
459 if (IsDigit(locale, c) == FALSE)
460 return FALSE;
462 val = c - '0';
464 while (--numchars >= 1)
466 c = GetChar();
467 if (IsDigit(locale, c))
468 val = val * 10 + c - '0';
469 else
471 *cPtr = c;
472 if (c != **fmtTemplatePtr)
473 return FALSE;
474 if (c == '\0')
475 *checkEOFPtr = FALSE;
476 else
477 (*fmtTemplatePtr)++;
479 break;
483 *valPtr = val;
485 return TRUE;