b28a12929d444859ce3983bcbf29e20b6257afd5
[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: year %d, month %d, day %d"),
184                    begin_date.year, begin_date.month, begin_date.day);
185       result = 1;
186     }
187     else
188       if (d->type == DATE_BOUNDED) {
189         d->sdn2 = checkedCalToSdn(d->cal, end_date.year, end_date.month,
190                                   end_date.day);
191         if (d->sdn2 == -1) {
192           gedcom_error(_("Error converting date: year %d, month %d, day %d"),
193                        end_date.year, end_date.month, end_date.day);
194           result = 1;
195         }
196         else
197           d->sdn2 -= 1;
198       }
199   }
200   return result;
201 }
202
203 /* PRE:     d->cal != CAL_UNKNOWN
204    INPUT:   d->type, d->sdn1, d->sdn2
205    OUTPUT:  d->day, d->month, d->year
206 */
207 int sdn_to_numbers(struct date *d)
208 {
209   int result = 0;
210   if (d->cal == CAL_UNKNOWN) {
211     gedcom_error(_("Cannot compute from SDN for unknown calendar type"));
212     result = 1;
213   }
214   else {
215     struct date begin_date;
216     struct date end_date;
217
218     if (d->sdn1 <= 0) {
219       gedcom_error(_("SDN 1 should be bigger than zero"));
220       result = 1;
221     }
222     else {
223       copy_date(&begin_date, d);
224       if (!checkedSdnToCal(d->cal, d->sdn1, &begin_date.year,
225                            &begin_date.month, &begin_date.day)) {
226         gedcom_error(_("SDN 1 isn't a valid date in the given calendar"));
227         result = 1;
228       }
229       else {
230         switch (d->type) {
231           case DATE_EXACT:
232             if (d->sdn2 != -1) {
233               gedcom_error(_("SDN 2 should be -1 for exact dates"));
234               result = 1;
235             }
236             break;
237           case DATE_BOUNDED:
238             if (d->sdn2 <= 0) {
239               gedcom_error(_("SDN 2 should be bigger than zero"));
240               result = 1;
241             }
242             else if (d->sdn2 <= d->sdn1) {
243               gedcom_error(_("SDN 2 should be bigger than SDN 1"));
244               result = 1;
245             }
246             else {
247               copy_date(&end_date, d);
248               if (!checkedSdnToCal(d->cal, d->sdn2, &end_date.year,
249                                    &end_date.month, &end_date.day)) {
250                 gedcom_error(_("SDN 2 isn't a valid date in the given calendar"));
251                 result = 1;
252               }
253               else {
254                 if (begin_date.year == end_date.year) {
255                   if (begin_date.month == end_date.month) {
256                     if (begin_date.day == end_date.day) {
257                       /* year, month and day are relevant */
258                     }
259                     else {
260                       /* year and month are relevant */
261                       begin_date.day = -1;
262                     }
263                   }
264                   else {
265                     /* only year is relevant */
266                     begin_date.month = -1;
267                     begin_date.day   = -1;
268                   }
269                 }
270                 else {
271                   gedcom_error(_("SDN1/SDN2 isn't a bounded date"));
272                   result = 1;
273                 }
274               }
275             }
276             break;
277           default:
278             break;
279         }
280       }
281       d->year  = begin_date.year;
282       d->month = begin_date.month;
283       d->day   = begin_date.day;
284     }
285   }
286   return result;
287 }
288
289 /* PRE:     d->cal != CAL_UNKNOWN
290    INPUT:   d->day_str, d->month_str, d->year_str
291    OUTPUT:  d->day, d->month, d->year, d->year_type
292 */
293 int strings_to_numbers(struct date *d)
294 {
295   int result = 0;
296   if (d->cal == CAL_UNKNOWN) {
297     gedcom_error(_("Cannot compute months for unknown calendar type"));
298     result = 1;
299   }
300   else {
301     if (d->day_str[0]) {
302       d->day = get_day_num(d->day_str);
303       if (d->day == -1) result = 1;
304     }
305     else
306       d->day = -1;
307     
308     if (d->month_str[0]) {
309       d->month = get_month_num(d->cal, d->month_str);
310       if (d->month == -1) result = 1;
311     }
312     else
313       d->month = -1;
314     
315     d->year = get_year_num(d->year_str, &d->year_type);
316     if (d->year == -1) result = 1;
317   }
318   
319   return result;
320 }
321
322 /* PRE:     d->cal != CAL_UNKNOWN
323    INPUT:   d->day, d->month, d->year, d->year_type
324    OUTPUT:  d->day_str, d->month_str, d->year_str
325 */
326 int numbers_to_strings(struct date *d)
327 {
328   int result = 0;
329   if (d->cal == CAL_UNKNOWN) {
330     gedcom_error(_("Cannot compute month names for unknown calendar type"));
331     result = 1;
332   }
333   else {
334     if (d->day != -1)
335       sprintf(d->day_str, "%d", d->day);
336     
337     if (d->month != -1)
338       strcpy(d->month_str, month_name[d->cal][d->month - 1]);
339     
340     if (d->year_type == YEAR_SINGLE)
341       sprintf(d->year_str, "%d", d->year);
342     else
343       sprintf(d->year_str, "%d/%d", d->year - 1, (d->year % 100));
344   }
345   return result;
346 }
347
348 int gedcom_normalize_date(Date_input input, struct date_value *val)
349 {
350   int result = 0;
351   if (val->type != DV_PHRASE) {
352     switch (input) {
353       case DI_FROM_STRINGS:
354         result |= strings_to_numbers(&val->date1);
355         result |= numbers_to_sdn(&val->date1);
356         if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
357           result |= strings_to_numbers(&val->date2);
358           result |= numbers_to_sdn(&val->date2);
359         }
360         break;
361       case DI_FROM_NUMBERS:
362         result |= numbers_to_strings(&val->date1);
363         result |= numbers_to_sdn(&val->date1);
364         if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
365           result |= numbers_to_strings(&val->date2);
366           result |= numbers_to_sdn(&val->date2);
367         }
368         break;
369       case DI_FROM_SDN:
370         result |= sdn_to_numbers(&val->date1);
371         result |= numbers_to_strings(&val->date1);
372         if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
373           result |= sdn_to_numbers(&val->date2);
374           result |= numbers_to_strings(&val->date2);
375         }
376         break;
377       default:
378         break;
379     }
380   }
381   return result;
382 }
383
384 struct date_value* gedcom_new_date_value(struct date_value* copy_from)
385 {
386   struct date_value* dv_ptr;
387   dv_ptr = (struct date_value*) malloc(sizeof(struct date_value));
388   if (!dv_ptr)
389     MEMORY_ERROR;
390   else {
391     if (copy_from)
392       memcpy(dv_ptr, copy_from, sizeof(struct date_value));
393     else {
394       dv_ptr->type = DV_NO_MODIFIER;
395       init_date(&dv_ptr->date1);
396       init_date(&dv_ptr->date2);
397       dv_ptr->phrase[0] = '\0';
398     }
399   }
400   return dv_ptr;
401 }
402
403 struct date_value gedcom_parse_date(const char* line_value)
404 {
405   init_date(&date_s);
406   init_date(&def_date);
407   curr_line_value = line_value;
408   init_gedcom_date_lex(line_value);
409   gedcom_date_parse();
410   close_gedcom_date_lex();
411   if (dv_s.date1.cal != CAL_UNKNOWN)
412     numbers_to_sdn(&dv_s.date1);
413   if (dv_s.date2.cal != CAL_UNKNOWN)
414     numbers_to_sdn(&dv_s.date2);
415   return dv_s;
416 }
417
418 void write_date(struct date* d)
419 {
420   if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0)
421     gedcom_error(_("Date is not normalized: some fields are invalid"));
422   else {
423     switch (d->cal) {
424       case CAL_GREGORIAN: break;
425       case CAL_JULIAN:
426         safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
427       case CAL_HEBREW:
428         safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
429       case CAL_FRENCH_REV:
430         safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
431       case CAL_UNKNOWN:
432         safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
433       default:
434         break;
435     }
436     if (d->day_str[0])
437       safe_buf_append(&date_buffer, "%s ", d->day_str);
438     if (d->month_str[0])
439       safe_buf_append(&date_buffer, "%s ", d->month_str);
440     safe_buf_append(&date_buffer, "%s", d->year_str);
441   }
442 }
443
444 char* gedcom_date_to_string(struct date_value* val)
445 {
446   init_buffer(&date_buffer);
447   reset_buffer(&date_buffer);
448   
449   switch (val->type) {
450     case DV_NO_MODIFIER:
451       write_date(&val->date1); break;
452     case DV_BEFORE:
453       safe_buf_append(&date_buffer, "BEF ");
454       write_date(&val->date1); break;
455     case DV_AFTER:
456       safe_buf_append(&date_buffer, "AFT ");
457       write_date(&val->date1); break;
458     case DV_BETWEEN:
459       safe_buf_append(&date_buffer, "BET ");
460       write_date(&val->date1);
461       safe_buf_append(&date_buffer, " AND ");
462       write_date(&val->date2); break;
463     case DV_FROM:
464       safe_buf_append(&date_buffer, "FROM ");
465       write_date(&val->date1); break;
466     case DV_TO:
467       safe_buf_append(&date_buffer, "TO ");
468       write_date(&val->date1); break;
469     case DV_FROM_TO:
470       safe_buf_append(&date_buffer, "FROM ");
471       write_date(&val->date1);
472       safe_buf_append(&date_buffer, " TO ");
473       write_date(&val->date2); break;
474     case DV_ABOUT:
475       safe_buf_append(&date_buffer, "ABT ");
476       write_date(&val->date1); break;
477     case DV_CALCULATED:
478       safe_buf_append(&date_buffer, "CAL ");
479       write_date(&val->date1); break;
480     case DV_ESTIMATED:
481       safe_buf_append(&date_buffer, "EST ");
482       write_date(&val->date1); break;
483     case DV_INTERPRETED:
484       safe_buf_append(&date_buffer, "INT ");
485       write_date(&val->date1);
486       safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
487     case DV_PHRASE:
488       safe_buf_append(&date_buffer, "(%s)", val->phrase); break;
489     default:
490       break;
491   }
492   
493   return get_buf_string(&date_buffer);
494 }