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.
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.
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.
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
24 #include "gedcom_internal.h"
29 struct date_value dv_s;
32 struct date_value def_date_val;
35 const char* curr_line_value;
37 void cleanup_date_buffer();
38 struct safe_buffer date_buffer = { NULL, 0, NULL, 0, cleanup_date_buffer };
40 void cleanup_date_buffer()
42 cleanup_buffer(&date_buffer);
45 int max_month[] = { 12, /* CAL_GREGORIAN */
48 13, /* CAL_FRENCH_REV */
52 char* month_name[][13] =
54 { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
55 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" },
57 { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
58 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" },
60 { "TSH", "CSH", "KSL", "TVT", "SHV", "ADR", "ADS",
61 "NSN", "IYR", "SVN", "TMZ", "AAV", "ELL" },
63 { "VEND", "BRUM", "FRIM", "NIVO", "PLUV", "VENT", "GERM",
64 "FLOR", "PRAI", "MESS", "THER", "FRUC", "COMP" }
67 typedef long int (*to_sdn_func_type) (int, int, int);
68 typedef void (*from_sdn_func_type) (long int, int*, int*, int*);
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 */
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 */
84 long int checkedCalToSdn(Calendar_type cal, int year, int month, int day)
87 long int sdn = (*to_sdn_func[cal])(year,month, day);
91 (*from_sdn_func[cal])(sdn, &y, &m, &d);
92 if ((year == y) && (month == m) && (day == d))
99 int checkedSdnToCal(Calendar_type cal, long int sdn,
100 int* year, int* month, int* day)
102 (*from_sdn_func[cal])(sdn, year, month, day);
103 if (*year > 0 && *month > 0 && *day > 0)
109 void copy_date(struct date *to, struct date from)
111 memcpy(to, &from, sizeof(struct date));
114 void init_date(struct date *d)
116 d->cal = CAL_UNKNOWN;
117 d->day_str[0] = '\0';
118 d->month_str[0] = '\0';
119 d->year_str[0] = '\0';
123 d->year_type = YEAR_SINGLE;
124 d->type = DATE_UNRECOGNIZED;
129 struct date_value make_date_value(Date_value_type t, struct date d1,
130 struct date d2, const char* p)
133 copy_date(&dv_s.date1, d1);
134 copy_date(&dv_s.date2, d2);
135 strncpy(dv_s.phrase, p, MAX_PHRASE_LEN + 1);
139 /* PRE: d->cal != CAL_UNKNOWN
140 INPUT: d->day, d->month, d->year
141 OUTPUT: d->type, d->sdn1, d->sdn2
143 int numbers_to_sdn(struct date *d)
146 if (d->cal == CAL_UNKNOWN) {
147 d->type = DATE_UNRECOGNIZED;
148 gedcom_error(_("Cannot compute SDN for unknown calendar type"));
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;
163 else if (begin_date.day == -1) {
164 begin_date.day = 1; end_date.day = 1;
166 if (end_date.month > max_month[d->cal]) {
167 end_date.month -= max_month[d->cal];
172 gedcom_error(_("Year has to be given in bounded date"));
177 d->type = DATE_EXACT;
180 d->sdn1 = checkedCalToSdn(d->cal, begin_date.year, begin_date.month,
183 gedcom_error(_("Error converting date"));
187 if (d->type == DATE_BOUNDED) {
188 d->sdn2 = checkedCalToSdn(d->cal, end_date.year, end_date.month,
191 gedcom_error(_("Error converting date"));
201 /* PRE: d->cal != CAL_UNKNOWN
202 INPUT: d->type, d->sdn1, d->sdn2
203 OUTPUT: d->day, d->month, d->year
205 int sdn_to_numbers(struct date *d)
208 if (d->cal == CAL_UNKNOWN) {
209 gedcom_error(_("Cannot compute from SDN for unknown calendar type"));
213 struct date begin_date;
214 struct date end_date;
217 gedcom_error(_("SDN 1 should be bigger than zero"));
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"));
231 gedcom_error(_("SDN 2 should be -1 for exact dates"));
237 gedcom_error(_("SDN 2 should be bigger than zero"));
240 else if (d->sdn2 <= d->sdn1) {
241 gedcom_error(_("SDN 2 should be bigger than SDN 1"));
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"));
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 */
258 /* year and month are relevant */
263 /* only year is relevant */
264 begin_date.month = -1;
269 gedcom_error(_("SDN1/SDN2 isn't a bounded date"));
279 d->year = begin_date.year;
280 d->month = begin_date.month;
281 d->day = begin_date.day;
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
291 int strings_to_numbers(struct date *d)
294 if (d->cal == CAL_UNKNOWN) {
295 gedcom_error(_("Cannot compute months for unknown calendar type"));
300 d->day = get_day_num(d->day_str);
301 if (d->day == -1) result = 1;
306 if (d->month_str[0]) {
307 d->month = get_month_num(d->cal, d->month_str);
308 if (d->month == -1) result = 1;
313 d->year = get_year_num(d->year_str, &d->year_type);
314 if (d->year == -1) result = 1;
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
324 int numbers_to_strings(struct date *d)
327 if (d->cal == CAL_UNKNOWN) {
328 gedcom_error(_("Cannot compute month names for unknown calendar type"));
333 sprintf(d->day_str, "%d", d->day);
336 strcpy(d->month_str, month_name[d->cal][d->month - 1]);
338 if (d->year_type == YEAR_SINGLE)
339 sprintf(d->year_str, "%d", d->year);
341 sprintf(d->year_str, "%d/%d", d->year - 1, (d->year % 100));
346 int gedcom_normalize_date(Date_input input, struct date_value *val)
349 if (val->type != DV_PHRASE) {
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);
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);
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);
382 struct date_value* gedcom_new_date_value(struct date_value* copy_from)
384 struct date_value* dv_ptr;
385 dv_ptr = (struct date_value*) malloc(sizeof(struct date_value));
390 memcpy(dv_ptr, copy_from, sizeof(struct date_value));
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';
401 struct date_value gedcom_parse_date(const char* line_value)
404 init_date(&def_date);
405 curr_line_value = line_value;
406 init_gedcom_date_lex(line_value);
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);
416 void write_date(struct date* d)
418 if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0)
419 gedcom_error(_("Date is not normalized: some fields are invalid"));
422 case CAL_GREGORIAN: break;
424 safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
426 safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
428 safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
430 safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
435 safe_buf_append(&date_buffer, "%s ", d->day_str);
437 safe_buf_append(&date_buffer, "%s ", d->month_str);
438 safe_buf_append(&date_buffer, "%s", d->year_str);
442 char* gedcom_date_to_string(struct date_value* val)
444 init_buffer(&date_buffer);
445 reset_buffer(&date_buffer);
449 write_date(&val->date1); break;
451 safe_buf_append(&date_buffer, "BEF ");
452 write_date(&val->date1); break;
454 safe_buf_append(&date_buffer, "AFT ");
455 write_date(&val->date1); break;
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;
462 safe_buf_append(&date_buffer, "FROM ");
463 write_date(&val->date1); break;
465 safe_buf_append(&date_buffer, "TO ");
466 write_date(&val->date1); break;
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;
473 safe_buf_append(&date_buffer, "ABT ");
474 write_date(&val->date1); break;
476 safe_buf_append(&date_buffer, "CAL ");
477 write_date(&val->date1); break;
479 safe_buf_append(&date_buffer, "EST ");
480 write_date(&val->date1); break;
482 safe_buf_append(&date_buffer, "INT ");
483 write_date(&val->date1);
484 safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
486 safe_buf_append(&date_buffer, "(%s)", val->phrase); break;
491 return get_buf_string(&date_buffer);