New function for handling age values.
[gedcom-parse.git] / gedcom / date.c
1 /* Date manipulation routines.
2    Copyright (C) 2001,2002 The Genes Development Team
3    This file is part of the Gedcom parser library.
4    Contributed by Peter Verthez <Peter.Verthez@advalvas.be>, 2001.
5
6    The Gedcom parser library is free software; you can redistribute it
7    and/or modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The Gedcom parser library is distributed in the hope that it will be
12    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the Gedcom parser library; if not, write to the
18    Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 /* $Id$ */
22 /* $Name$ */
23
24 #include "gedcom_internal.h"
25 #include "sdncal.h"
26 #include "buffer.h"
27 #include "date.h"
28
29 struct date_value dv_s;
30 struct date date_s;
31
32 struct date_value def_date_val;
33 struct date def_date;
34
35 const char* curr_line_value;
36
37 void cleanup_date_buffer();
38 struct safe_buffer date_buffer = { NULL, 0, NULL, 0, cleanup_date_buffer };
39
40 void cleanup_date_buffer()
41 {
42   cleanup_buffer(&date_buffer);
43 }
44
45 int max_month[] = { 12,  /* CAL_GREGORIAN */
46                     12,  /* CAL_JULIAN */
47                     13,  /* CAL_HEBREW */
48                     13,  /* CAL_FRENCH_REV */
49                     0    /* CAL_UNKNOWN */
50                   };
51
52 char* month_name[][13] =
53 { /* CAL_GREGORIAN */
54   { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
55     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" },
56   /* CAL_JULIAN */
57   { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
58     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" },
59   /* CAL_JEWISH */
60   { "TSH", "CSH", "KSL", "TVT", "SHV", "ADR", "ADS",
61     "NSN", "IYR", "SVN", "TMZ", "AAV", "ELL" },
62   /* CAL_FRENCH_REF */
63   { "VEND", "BRUM", "FRIM", "NIVO", "PLUV", "VENT", "GERM",
64     "FLOR", "PRAI", "MESS", "THER", "FRUC", "COMP" }
65 };
66
67 typedef long int (*to_sdn_func_type) (int, int, int);
68 typedef void (*from_sdn_func_type) (long int, int*, int*, int*);
69
70 to_sdn_func_type to_sdn_func[] = {
71   &GregorianToSdn,   /* CAL_GREGORIAN */
72   &JulianToSdn,      /* CAL_JULIAN */
73   &JewishToSdn,      /* CAL_JEWISH */
74   &FrenchToSdn       /* CAL_FRENCH_REV */
75 };
76
77 from_sdn_func_type from_sdn_func[] = {
78   &SdnToGregorian,   /* CAL_GREGORIAN */
79   &SdnToJulian,      /* CAL_JULIAN */
80   &SdnToJewish,      /* CAL_JEWISH */
81   &SdnToFrench       /* CAL_FRENCH_REV */
82 };
83
84 long int checkedCalToSdn(Calendar_type cal, int year, int month, int day)
85 {
86   int y, m, d;
87   long int sdn = (*to_sdn_func[cal])(year,month, day);
88   if (sdn <= 0)
89     return -1;
90   else {
91     (*from_sdn_func[cal])(sdn, &y, &m, &d);
92     if ((year == y) && (month == m) && (day == d))
93       return sdn;
94     else
95       return -1;
96   }
97 }
98
99 int checkedSdnToCal(Calendar_type cal, long int sdn,
100                     int* year, int* month, int* day)
101 {
102   (*from_sdn_func[cal])(sdn, year, month, day);
103   if (*year > 0 && *month > 0 && *day > 0)
104     return 1;
105   else
106     return 0;
107 }
108
109 void copy_date(struct date *to, struct date from)
110 {
111   memcpy(to, &from, sizeof(struct date));
112 }
113
114 void init_date(struct date *d)
115 {
116   d->cal = CAL_UNKNOWN;
117   d->day_str[0] = '\0';
118   d->month_str[0] = '\0';
119   d->year_str[0] = '\0';
120   d->day = -1;
121   d->month = -1;
122   d->year = -1;
123   d->year_type = YEAR_SINGLE;
124   d->type = DATE_UNRECOGNIZED;
125   d->sdn1 = -1;
126   d->sdn2 = -1;
127 }
128
129 struct date_value make_date_value(Date_value_type t, struct date d1,
130                                   struct date d2, const char* p)
131 {
132   dv_s.type = t;
133   copy_date(&dv_s.date1, d1);
134   copy_date(&dv_s.date2, d2);
135   strncpy(dv_s.phrase, p, MAX_PHRASE_LEN + 1);
136   return dv_s;
137 }
138
139 /* PRE:     d->cal != CAL_UNKNOWN
140    INPUT:   d->day, d->month, d->year
141    OUTPUT:  d->type, d->sdn1, d->sdn2
142 */
143 int numbers_to_sdn(struct date *d)
144 {
145   int result = 0;
146   if (d->cal == CAL_UNKNOWN) {
147     d->type = DATE_UNRECOGNIZED;
148     gedcom_error(_("Cannot compute SDN for unknown calendar type"));
149     result = 1;
150   }
151   else {
152     struct date begin_date;
153     struct date end_date;
154     copy_date(&begin_date, *d);
155     if (d->day == -1 || d->month == -1 || d->year == -1) {
156       d->type = DATE_BOUNDED;
157       copy_date(&end_date, *d);
158       if (begin_date.month == -1) {
159         begin_date.month = 1; end_date.month = 1;
160         begin_date.day   = 1; end_date.day   = 1;
161         end_date.year += 1;
162       }
163       else if (begin_date.day == -1) {
164         begin_date.day   = 1; end_date.day   = 1;
165         end_date.month += 1;
166         if (end_date.month > max_month[d->cal]) {
167           end_date.month -= max_month[d->cal];
168           end_date.year  += 1;
169         }
170       }
171       else {
172         gedcom_error(_("Year has to be given in bounded date"));
173         result = 1;
174       }
175     }
176     else {
177       d->type = DATE_EXACT;
178     }
179
180     d->sdn1 = checkedCalToSdn(d->cal, begin_date.year, begin_date.month,
181                               begin_date.day);
182     if (d->sdn1 == -1) {
183       gedcom_error(_("Error converting date"));
184       result = 1;
185     }
186     else
187       if (d->type == DATE_BOUNDED) {
188         d->sdn2 = checkedCalToSdn(d->cal, end_date.year, end_date.month,
189                                   end_date.day);
190         if (d->sdn2 == -1) {
191           gedcom_error(_("Error converting date"));
192           result = 1;
193         }
194         else
195           d->sdn2 -= 1;
196       }
197   }
198   return result;
199 }
200
201 /* PRE:     d->cal != CAL_UNKNOWN
202    INPUT:   d->type, d->sdn1, d->sdn2
203    OUTPUT:  d->day, d->month, d->year
204 */
205 int sdn_to_numbers(struct date *d)
206 {
207   int result = 0;
208   if (d->cal == CAL_UNKNOWN) {
209     gedcom_error(_("Cannot compute from SDN for unknown calendar type"));
210     result = 1;
211   }
212   else {
213     struct date begin_date;
214     struct date end_date;
215
216     if (d->sdn1 <= 0) {
217       gedcom_error(_("SDN 1 should be bigger than zero"));
218       result = 1;
219     }
220     else {
221       copy_date(&begin_date, *d);
222       if (!checkedSdnToCal(d->cal, d->sdn1, &begin_date.year,
223                            &begin_date.month, &begin_date.day)) {
224         gedcom_error(_("SDN 1 isn't a valid date in the given calendar"));
225         result = 1;
226       }
227       else {
228         switch (d->type) {
229           case DATE_EXACT:
230             if (d->sdn2 != -1) {
231               gedcom_error(_("SDN 2 should be -1 for exact dates"));
232               result = 1;
233             }
234             break;
235           case DATE_BOUNDED:
236             if (d->sdn2 <= 0) {
237               gedcom_error(_("SDN 2 should be bigger than zero"));
238               result = 1;
239             }
240             else if (d->sdn2 <= d->sdn1) {
241               gedcom_error(_("SDN 2 should be bigger than SDN 1"));
242               result = 1;
243             }
244             else {
245               copy_date(&end_date, *d);
246               if (!checkedSdnToCal(d->cal, d->sdn2, &end_date.year,
247                                    &end_date.month, &end_date.day)) {
248                 gedcom_error(_("SDN 2 isn't a valid date in the given calendar"));
249                 result = 1;
250               }
251               else {
252                 if (begin_date.year == end_date.year) {
253                   if (begin_date.month == end_date.month) {
254                     if (begin_date.day == end_date.day) {
255                       /* year, month and day are relevant */
256                     }
257                     else {
258                       /* year and month are relevant */
259                       begin_date.day = -1;
260                     }
261                   }
262                   else {
263                     /* only year is relevant */
264                     begin_date.month = -1;
265                     begin_date.day   = -1;
266                   }
267                 }
268                 else {
269                   gedcom_error(_("SDN1/SDN2 isn't a bounded date"));
270                   result = 1;
271                 }
272               }
273             }
274             break;
275           default:
276             break;
277         }
278       }
279       d->year  = begin_date.year;
280       d->month = begin_date.month;
281       d->day   = begin_date.day;
282     }
283   }
284   return result;
285 }
286
287 /* PRE:     d->cal != CAL_UNKNOWN
288    INPUT:   d->day_str, d->month_str, d->year_str
289    OUTPUT:  d->day, d->month, d->year, d->year_type
290 */
291 int strings_to_numbers(struct date *d)
292 {
293   int result = 0;
294   if (d->cal == CAL_UNKNOWN) {
295     gedcom_error(_("Cannot compute months for unknown calendar type"));
296     result = 1;
297   }
298   else {
299     if (d->day_str[0]) {
300       d->day = get_day_num(d->day_str);
301       if (d->day == -1) result = 1;
302     }
303     else
304       d->day = -1;
305     
306     if (d->month_str[0]) {
307       d->month = get_month_num(d->cal, d->month_str);
308       if (d->month == -1) result = 1;
309     }
310     else
311       d->month = -1;
312     
313     d->year = get_year_num(d->year_str, &d->year_type);
314     if (d->year == -1) result = 1;
315   }
316   
317   return result;
318 }
319
320 /* PRE:     d->cal != CAL_UNKNOWN
321    INPUT:   d->day, d->month, d->year, d->year_type
322    OUTPUT:  d->day_str, d->month_str, d->year_str
323 */
324 int numbers_to_strings(struct date *d)
325 {
326   int result = 0;
327   if (d->cal == CAL_UNKNOWN) {
328     gedcom_error(_("Cannot compute month names for unknown calendar type"));
329     result = 1;
330   }
331   else {
332     if (d->day != -1)
333       sprintf(d->day_str, "%d", d->day);
334     
335     if (d->month != -1)
336       strcpy(d->month_str, month_name[d->cal][d->month - 1]);
337     
338     if (d->year_type == YEAR_SINGLE)
339       sprintf(d->year_str, "%d", d->year);
340     else
341       sprintf(d->year_str, "%d/%d", d->year - 1, (d->year % 100));
342   }
343   return result;
344 }
345
346 int gedcom_normalize_date(Date_input input, struct date_value *val)
347 {
348   int result = 0;
349   if (val->type != DV_PHRASE) {
350     switch (input) {
351       case DI_FROM_STRINGS:
352         result |= strings_to_numbers(&val->date1);
353         result |= numbers_to_sdn(&val->date1);
354         if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
355           result |= strings_to_numbers(&val->date2);
356           result |= numbers_to_sdn(&val->date2);
357         }
358         break;
359       case DI_FROM_NUMBERS:
360         result |= numbers_to_strings(&val->date1);
361         result |= numbers_to_sdn(&val->date1);
362         if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
363           result |= numbers_to_strings(&val->date2);
364           result |= numbers_to_sdn(&val->date2);
365         }
366         break;
367       case DI_FROM_SDN:
368         result |= sdn_to_numbers(&val->date1);
369         result |= numbers_to_strings(&val->date1);
370         if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
371           result |= sdn_to_numbers(&val->date2);
372           result |= numbers_to_strings(&val->date2);
373         }
374         break;
375       default:
376         break;
377     }
378   }
379   return result;
380 }
381
382 struct date_value* gedcom_new_date_value(struct date_value* copy_from)
383 {
384   struct date_value* dv_ptr;
385   dv_ptr = (struct date_value*) malloc(sizeof(struct date_value));
386   if (!dv_ptr)
387     MEMORY_ERROR;
388   else {
389     if (copy_from)
390       memcpy(dv_ptr, copy_from, sizeof(struct date_value));
391     else {
392       dv_ptr->type = DV_NO_MODIFIER;
393       init_date(&dv_ptr->date1);
394       init_date(&dv_ptr->date2);
395       dv_ptr->phrase[0] = '\0';
396     }
397   }
398   return dv_ptr;
399 }
400
401 struct date_value gedcom_parse_date(const char* line_value)
402 {
403   init_date(&date_s);
404   init_date(&def_date);
405   curr_line_value = line_value;
406   init_gedcom_date_lex(line_value);
407   gedcom_date_parse();
408   close_gedcom_date_lex();
409   if (dv_s.date1.cal != CAL_UNKNOWN)
410     numbers_to_sdn(&dv_s.date1);
411   if (dv_s.date2.cal != CAL_UNKNOWN)
412     numbers_to_sdn(&dv_s.date2);
413   return dv_s;
414 }
415
416 void write_date(struct date* d)
417 {
418   if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0)
419     gedcom_error(_("Date is not normalized: some fields are invalid"));
420   else {
421     switch (d->cal) {
422       case CAL_GREGORIAN: break;
423       case CAL_JULIAN:
424         safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
425       case CAL_HEBREW:
426         safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
427       case CAL_FRENCH_REV:
428         safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
429       case CAL_UNKNOWN:
430         safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
431       default:
432         break;
433     }
434     if (d->day_str[0])
435       safe_buf_append(&date_buffer, "%s ", d->day_str);
436     if (d->month_str[0])
437       safe_buf_append(&date_buffer, "%s ", d->month_str);
438     safe_buf_append(&date_buffer, "%s", d->year_str);
439   }
440 }
441
442 char* gedcom_date_to_string(struct date_value* val)
443 {
444   init_buffer(&date_buffer);
445   reset_buffer(&date_buffer);
446   
447   switch (val->type) {
448     case DV_NO_MODIFIER:
449       write_date(&val->date1); break;
450     case DV_BEFORE:
451       safe_buf_append(&date_buffer, "BEF ");
452       write_date(&val->date1); break;
453     case DV_AFTER:
454       safe_buf_append(&date_buffer, "AFT ");
455       write_date(&val->date1); break;
456     case DV_BETWEEN:
457       safe_buf_append(&date_buffer, "BET ");
458       write_date(&val->date1);
459       safe_buf_append(&date_buffer, " AND ");
460       write_date(&val->date2); break;
461     case DV_FROM:
462       safe_buf_append(&date_buffer, "FROM ");
463       write_date(&val->date1); break;
464     case DV_TO:
465       safe_buf_append(&date_buffer, "TO ");
466       write_date(&val->date1); break;
467     case DV_FROM_TO:
468       safe_buf_append(&date_buffer, "FROM ");
469       write_date(&val->date1);
470       safe_buf_append(&date_buffer, " TO ");
471       write_date(&val->date2); break;
472     case DV_ABOUT:
473       safe_buf_append(&date_buffer, "ABT ");
474       write_date(&val->date1); break;
475     case DV_CALCULATED:
476       safe_buf_append(&date_buffer, "CAL ");
477       write_date(&val->date1); break;
478     case DV_ESTIMATED:
479       safe_buf_append(&date_buffer, "EST ");
480       write_date(&val->date1); break;
481     case DV_INTERPRETED:
482       safe_buf_append(&date_buffer, "INT ");
483       write_date(&val->date1);
484       safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
485     case DV_PHRASE:
486       safe_buf_append(&date_buffer, "(%s)", val->phrase); break;
487     default:
488       break;
489   }
490   
491   return get_buf_string(&date_buffer);
492 }