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