Copied from old documentation. Removed all Gedcom_val details.
[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_date_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_date_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_date_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_date_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_date_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_date_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_date_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_date_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_date_error(_("SDN 2 should be bigger than zero"));
242               result = 1;
243             }
244             else if (d->sdn2 <= d->sdn1) {
245               gedcom_date_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_date_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_date_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_date_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_date_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 > 0 && d->month <= max_month[d->cal])
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(const 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   int result = 0;
408   init_date(&dv_s.date1);
409   init_date(&dv_s.date2);
410   init_date(&date_s);
411   init_date(&def_date);
412   curr_line_value = line_value;
413   if (compat_mode(C_NO_REQUIRED_VALUES)
414       && !strncmp(curr_line_value, VALUE_IF_MISSING, 2)) {
415     gedcom_date_error(_("Empty value changed to '%s'"), VALUE_IF_MISSING);
416     result = 1;
417   }
418   else {
419     compat_date_start();
420     init_gedcom_date_lex(line_value);
421     gedcom_date_parse();
422     close_gedcom_date_lex();
423     if (compat_date_check(&dv_s, &curr_line_value)) {
424       init_gedcom_date_lex(curr_line_value);
425       gedcom_date_parse();
426       close_gedcom_date_lex();
427     }
428     if (dv_s.date1.cal != CAL_UNKNOWN)
429       result |= numbers_to_sdn(&dv_s.date1);
430     if (dv_s.date2.cal != CAL_UNKNOWN)
431       result |= numbers_to_sdn(&dv_s.date2);
432   }
433   if (result != 0) {
434     gedcom_date_error(_("Putting date '%s' in 'phrase' member"),
435                       curr_line_value);
436     make_date_value(DV_PHRASE, &dv_s.date1, &dv_s.date2, curr_line_value);
437   }
438   return dv_s;
439 }
440
441 void write_date(const struct date* d)
442 {
443   if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0)
444     gedcom_error(_("Date is not normalized: some fields are invalid"));
445   else {
446     switch (d->cal) {
447       case CAL_GREGORIAN: break;
448       case CAL_JULIAN:
449         safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
450       case CAL_HEBREW:
451         safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
452       case CAL_FRENCH_REV:
453         safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
454       case CAL_UNKNOWN:
455         safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
456       default:
457         break;
458     }
459     if (d->day_str[0])
460       safe_buf_append(&date_buffer, "%s ", d->day_str);
461     if (d->month_str[0])
462       safe_buf_append(&date_buffer, "%s ", d->month_str);
463     safe_buf_append(&date_buffer, "%s", d->year_str);
464   }
465 }
466
467 char* gedcom_date_to_string(const struct date_value* val)
468 {
469   init_buffer(&date_buffer);
470   reset_buffer(&date_buffer);
471   
472   switch (val->type) {
473     case DV_NO_MODIFIER:
474       write_date(&val->date1); break;
475     case DV_BEFORE:
476       safe_buf_append(&date_buffer, "BEF ");
477       write_date(&val->date1); break;
478     case DV_AFTER:
479       safe_buf_append(&date_buffer, "AFT ");
480       write_date(&val->date1); break;
481     case DV_BETWEEN:
482       safe_buf_append(&date_buffer, "BET ");
483       write_date(&val->date1);
484       safe_buf_append(&date_buffer, " AND ");
485       write_date(&val->date2); break;
486     case DV_FROM:
487       safe_buf_append(&date_buffer, "FROM ");
488       write_date(&val->date1); break;
489     case DV_TO:
490       safe_buf_append(&date_buffer, "TO ");
491       write_date(&val->date1); break;
492     case DV_FROM_TO:
493       safe_buf_append(&date_buffer, "FROM ");
494       write_date(&val->date1);
495       safe_buf_append(&date_buffer, " TO ");
496       write_date(&val->date2); break;
497     case DV_ABOUT:
498       safe_buf_append(&date_buffer, "ABT ");
499       write_date(&val->date1); break;
500     case DV_CALCULATED:
501       safe_buf_append(&date_buffer, "CAL ");
502       write_date(&val->date1); break;
503     case DV_ESTIMATED:
504       safe_buf_append(&date_buffer, "EST ");
505       write_date(&val->date1); break;
506     case DV_INTERPRETED:
507       safe_buf_append(&date_buffer, "INT ");
508       write_date(&val->date1);
509       safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
510     case DV_PHRASE:
511       safe_buf_append(&date_buffer, "(%s)", val->phrase); break;
512     default:
513       break;
514   }
515   
516   return get_buf_string(&date_buffer);
517 }