FreeBASIC  0.91.0
time_parsedate.c
Go to the documentation of this file.
1 /* parse a string date */
2 
3 #include "fb.h"
4 #include <ctype.h>
5 
6 /*:::::*/
7 static int fb_hIsMonth( const char *text, size_t text_len, const char **end_text, int short_name, int localized )
8 {
9  const char *txt_end = text;
10  int month;
11  for( month=1; month!=13; ++month ) {
12  FBSTRING *sMonthName = fb_IntlGetMonthName( month, short_name, !localized );
13  DBG_ASSERT( sMonthName!=NULL );
14  {
15  size_t month_len = FB_STRSIZE( sMonthName );
16  size_t len = ((text_len < month_len) ? text_len : month_len );
17  int is_same = FB_MEMCMP( text, sMonthName->data, len ) == 0;
18  fb_hStrDelTemp( sMonthName );
19  if( is_same ) {
20  if( text_len > len ) {
21  if( !isalpha( FB_CHAR_TO_INT(text[len]) ) ) {
22  txt_end = text + len;
23  break;
24  }
25  } else {
26  txt_end = text + len;
27  break;
28  }
29  }
30  }
31  }
32  if( month!=13 ) {
33  if( short_name ) {
34  /* There might follow a dot directly after the
35  * abbreviated month name */
36  if( *txt_end=='.' )
37  ++txt_end;
38  }
39  } else {
40  month = 0;
41  }
42  if( end_text!=NULL )
43  *end_text = txt_end;
44  return month;
45 }
46 
47 /*:::::*/
48 static int fb_hFindMonth( const char *text, size_t text_len, const char **end_text )
49 {
50  int short_name;
51  for( short_name=0; short_name!=2; ++short_name ) {
52  int localized = 2;
53  while( localized-- ) {
54  int month = fb_hIsMonth( text, text_len, end_text, short_name, localized );
55  if( month!=0 ) {
56  return month;
57  }
58  }
59  }
60  return 0;
61 }
62 
63 /*:::::*/
64 static int fb_hDateOrder( int *pOrderDay, int *pOrderMonth, int *pOrderYear )
65 {
66  int order_month = 0, order_day = 1, order_year = 2, order_index = 0;
67  int tmp, got_sep;
68  char short_format[90];
69 
70  tmp = fb_IntlGetDateFormat( short_format, sizeof(short_format), FALSE );
71  if( !tmp ) {
72  return FALSE;
73  }
74 
75  got_sep = TRUE;
76  for( tmp=0; short_format[tmp]; ++tmp ) {
77  int ch = FB_CHAR_TO_INT( short_format[tmp] );
78  if( islower(ch) )
79  ch = toupper( ch );
80  switch ( ch ) {
81  case 'D':
82  order_day = order_index;
83  got_sep = FALSE;
84  break;
85  case 'M':
86  order_month = order_index;
87  got_sep = FALSE;
88  break;
89  case 'Y':
90  order_year = order_index;
91  got_sep = FALSE;
92  break;
93  default:
94  if( !got_sep ) {
95  ++order_index;
96  }
97  got_sep = TRUE;
98  break;
99  }
100  }
101 
102  if( order_day==order_month || order_day==order_year || order_month==order_year )
103  return FALSE;
104  if( order_day > 2 || order_month > 2 || order_year > 2 )
105  return FALSE;
106 
107  if( pOrderDay )
108  *pOrderDay = order_day;
109  if( pOrderMonth )
110  *pOrderMonth = order_month;
111  if( pOrderYear )
112  *pOrderYear = order_year;
113 
114  return TRUE;
115 }
116 
117 /*:::::*/
118 static __inline__
119 int InlineSelect( int index, int num1, int num2, int num3 )
120 {
121  if( index==0 )
122  return num1;
123  if( index==1 )
124  return num2;
125  if( index==2 )
126  return num3;
127  return 0;
128 }
129 
130 /*:::::*/
131 int fb_hDateParse( const char *text, size_t text_len,
132  int *pDay, int *pMonth, int *pYear,
133  size_t *pLength )
134 {
135  size_t length = 0, len = text_len;
136  const char *text_start = text;
137  int result = FALSE;
138  int year = 1899, month = 12, day = 30;
139  int order_year, order_month, order_day;
140  const char *end_month;
141 
142  if( !fb_hDateOrder( &order_day, &order_month, &order_year ) ) {
143  /* switch to US date format */
144  order_month = 0;
145  order_day = 1;
146  order_year = 2;
147  }
148 
149  /* skip white spaces */
150  while ( isspace( *text ) )
151  ++text;
152  len = text_len - (text - text_start);
153 
154  month = fb_hFindMonth( text, len, &end_month );
155  if( month != 0 ) {
156  /* The string has the form: (MMMM|MMM) (d|dd)"," (yy|yyyy) */
157  char *endptr;
158  text = end_month;
159  day = strtol( text, &endptr, 10 );
160  if( day>0 ) {
161 
162  /* skip white spaces */
163  text = endptr;
164  while ( isspace( *text ) )
165  ++text;
166 
167  if( *text==',' ) {
168  size_t year_size;
169  year = strtol( ++text, &endptr, 10 );
170  year_size = endptr - text;
171  if( year_size > 0 ) {
172  if( year_size==2 )
173  year += 1900;
174 
175  result = day <= fb_hTimeDaysInMonth( month, year );
176  text = endptr;
177  }
178  }
179  }
180  } else {
181  /* The string can be in the short or long format.
182  *
183  * The short format can be
184  * [0-9]{1,2}(/|\-|\.)[0-9]{1,2}\1[0-9]{1,4}
185  * ^^ reference to first divider
186  *
187  * The long format can have the form:
188  * (d|dd) (MMMM|MM)"," (yy|yyyy)
189  */
190  size_t day_size;
191  char *endptr;
192  int valid_divider;
193  day = strtol( text, &endptr, 10 );
194  day_size = endptr - text;
195  if( day_size ) {
196  size_t month_size = 0;
197  char chDivider;
198  int is_short_form;
199 
200  /* skip white spaces */
201  text = endptr;
202  while ( isspace( *text ) )
203  ++text;
204 
205  /* read month and additional dividers */
206  chDivider = *text;
207  valid_divider = chDivider=='-' || chDivider=='/' || chDivider=='.';
208  if( chDivider=='.' ) {
209  ++text;
210  /* skip white spaces */
211  while ( isspace( *text ) )
212  ++text;
213  len = text_len - (text - text_start);
214  month = fb_hFindMonth( text, len, &end_month );
215  /* We found a dot but a month name ... so this date
216  * is in LONG format. */
217  is_short_form = month==0;
218  } else if( valid_divider ) {
219  ++text;
220  is_short_form = TRUE;
221  } else {
222  is_short_form = FALSE;
223  }
224  if( is_short_form ) {
225  /* short date */
226  /* skip white spaces */
227  while ( isspace( *text ) )
228  ++text;
229  month = strtol( text, &endptr, 10 );
230  month_size = endptr - text;
231  if( month_size ) {
232  text = endptr;
233  /* skip white spaces */
234  while ( isspace( *text ) )
235  ++text;
236  if( *text==chDivider ) {
237  ++text;
238  result = TRUE;
239  }
240  }
241  } else {
242  /* long date */
243  len = text_len - (text - text_start);
244  month = fb_hFindMonth( text, len, &end_month );
245  if( month != 0 ) {
246  text = end_month;
247  /* skip white spaces */
248  while ( isspace( *text ) )
249  ++text;
250  /* this comma is optional */
251  if( *text==',' ) {
252  ++text;
253  }
254  result = TRUE;
255  }
256  }
257  /* read year */
258  if( result ) {
259  size_t year_size;
260  /* skip white spaces */
261  while ( isspace( *text ) )
262  ++text;
263  year = strtol( text, &endptr, 10 );
264  year_size = endptr - text;
265  if( year_size > 0 ) {
266  /* adjust short form according to the date format */
267  if( is_short_form ) {
268  int tmp_day = InlineSelect( order_day, day, month, year );
269  int tmp_month = InlineSelect( order_month, day, month, year );
270  int tmp_year = InlineSelect( order_year, day, month, year );
271  year_size = InlineSelect( order_year, day_size, month_size, year_size );
272  day = tmp_day;
273  month = tmp_month;
274  year = tmp_year;
275  if( day < 1 || month < 1 || month > 12 )
276  result = FALSE;
277  }
278 
279  if( result ) {
280  if( year_size==2 )
281  year += 1900;
282  result = day <= fb_hTimeDaysInMonth( month, year );
283  }
284  text = endptr;
285  } else {
286  result = FALSE;
287  }
288  }
289  }
290  }
291 
292  if( result ) {
293  /* Update used length */
294  length = text - text_start;
295  }
296 
297  if( result ) {
298  if( pDay )
299  *pDay = day;
300  if( pMonth )
301  *pMonth = month;
302  if( pYear )
303  *pYear = year;
304  if( pLength )
305  *pLength = length;
306  }
307 
308  return result;
309 }