Writing dates and ages.
[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 typedef long int (*cal_func_type) (int, int, int);
53
54 cal_func_type cal_func[] = { &GregorianToSdn,   /* CAL_GREGORIAN */
55                              &JulianToSdn,      /* CAL_JULIAN */
56                              &JewishToSdn,      /* CAL_JEWISH */
57                              &FrenchToSdn       /* CAL_FRENCH_REV */
58                            };
59
60 void copy_date(struct date *to, struct date from)
61 {
62   memcpy(to, &from, sizeof(struct date));
63 }
64
65 void init_date(struct date *d)
66 {
67   d->cal = CAL_UNKNOWN;
68   d->day_str[0] = '\0';
69   d->month_str[0] = '\0';
70   d->year_str[0] = '\0';
71   d->day = -1;
72   d->month = -1;
73   d->year = -1;
74   d->year_type = YEAR_SINGLE;
75   d->type = DATE_UNRECOGNIZED;
76   d->sdn1 = -1;
77   d->sdn2 = -1;
78 }
79
80 struct date_value make_date_value(Date_value_type t, struct date d1,
81                                   struct date d2, const char* p)
82 {
83   dv_s.type = t;
84   copy_date(&dv_s.date1, d1);
85   copy_date(&dv_s.date2, d2);
86   strncpy(dv_s.phrase, p, MAX_PHRASE_LEN + 1);
87   return dv_s;
88 }
89
90 void make_date_complete(struct date *d)
91 {
92   if (d->cal == CAL_UNKNOWN)
93     d->type = DATE_UNRECOGNIZED;
94   else {
95     struct date end_date;
96     cal_func_type to_sdn;
97     if (d->day == -1 || d->month == -1 || d->year == -1) {
98       d->type = DATE_BOUNDED;
99       copy_date(&end_date, *d);
100       if (d->month == -1) {
101         d->month = 1; end_date.month = 1;
102         d->day   = 1; end_date.day   = 1;
103         end_date.year += 1;
104       }
105       else if (d->day == -1) {
106         d->day   = 1; end_date.day   = 1;
107         end_date.month += 1;
108         if (end_date.month > max_month[d->cal]) {
109           end_date.month -= max_month[d->cal];
110           end_date.year  += 1;
111         }
112       }
113     }
114     else {
115       d->type = DATE_EXACT;
116     }
117
118     to_sdn = cal_func[d->cal];
119     d->sdn1 = (*to_sdn)(d->year, d->month, d->day);
120     if (d->type == DATE_BOUNDED) {
121       d->sdn2 = (*to_sdn)(end_date.year, end_date.month, end_date.day);
122       d->sdn2 -= 1;
123     }
124   }
125 }
126
127 struct date_value gedcom_parse_date(const char* line_value)
128 {
129   init_date(&date_s);
130   init_date(&def_date);
131   curr_line_value = line_value;
132   init_gedcom_date_lex(line_value);
133   gedcom_date_parse();
134   close_gedcom_date_lex();
135   make_date_complete(&dv_s.date1);
136   make_date_complete(&dv_s.date2);
137   return dv_s;
138 }
139
140 void add_date(struct date* d)
141 {
142   switch (d->cal) {
143     case CAL_GREGORIAN: break;
144     case CAL_JULIAN:
145       safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
146     case CAL_HEBREW:
147       safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
148     case CAL_FRENCH_REV:
149       safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
150     case CAL_UNKNOWN:
151       safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
152     default:
153       break;
154   }
155   if (d->day_str)
156     safe_buf_append(&date_buffer, "%s ", d->day_str);
157   if (d->month_str)
158     safe_buf_append(&date_buffer, "%s ", d->month_str);
159   safe_buf_append(&date_buffer, "%s", d->year_str);
160 }
161
162 char* gedcom_date_to_string(struct date_value* val)
163 {
164   reset_buffer(&date_buffer);
165   
166   switch (val->type) {
167     case DV_NO_MODIFIER:
168       add_date(&val->date1); break;
169     case DV_BEFORE:
170       safe_buf_append(&date_buffer, "BEF ");
171       add_date(&val->date1); break;
172     case DV_AFTER:
173       safe_buf_append(&date_buffer, "AFT ");
174       add_date(&val->date1); break;
175     case DV_BETWEEN:
176       safe_buf_append(&date_buffer, "BET ");
177       add_date(&val->date1);
178       safe_buf_append(&date_buffer, " AND ");
179       add_date(&val->date2); break;
180     case DV_FROM:
181       safe_buf_append(&date_buffer, "FROM ");
182       add_date(&val->date1); break;
183     case DV_TO:
184       safe_buf_append(&date_buffer, "TO ");
185       add_date(&val->date1); break;
186     case DV_FROM_TO:
187       safe_buf_append(&date_buffer, "FROM ");
188       add_date(&val->date1);
189       safe_buf_append(&date_buffer, " TO ");
190       add_date(&val->date2); break;
191     case DV_ABOUT:
192       safe_buf_append(&date_buffer, "ABT ");
193       add_date(&val->date1); break;
194     case DV_CALCULATED:
195       safe_buf_append(&date_buffer, "CAL ");
196       add_date(&val->date1); break;
197     case DV_ESTIMATED:
198       safe_buf_append(&date_buffer, "EST ");
199       add_date(&val->date1); break;
200     case DV_INTERPRETED:
201       safe_buf_append(&date_buffer, "INT ");
202       add_date(&val->date1);
203       safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
204     case DV_PHRASE:
205       safe_buf_append(&date_buffer, "(%s)", val->phrase); break;
206     default:
207       break;
208   }
209   
210   return get_buf_string(&date_buffer);
211 }