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"
31 struct date_value dv_s;
34 struct date_value def_date_val;
37 const char* curr_line_value;
39 void cleanup_date_buffer();
40 struct safe_buffer date_buffer = { NULL, 0, NULL, 0, cleanup_date_buffer };
42 void cleanup_date_buffer()
44 cleanup_buffer(&date_buffer);
47 int max_month[] = { 12, /* CAL_GREGORIAN */
50 13, /* CAL_FRENCH_REV */
54 char* month_name[][13] =
56 { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
57 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" },
59 { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
60 "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" },
62 { "TSH", "CSH", "KSL", "TVT", "SHV", "ADR", "ADS",
63 "NSN", "IYR", "SVN", "TMZ", "AAV", "ELL" },
65 { "VEND", "BRUM", "FRIM", "NIVO", "PLUV", "VENT", "GERM",
66 "FLOR", "PRAI", "MESS", "THER", "FRUC", "COMP" }
69 typedef long int (*to_sdn_func_type) (int, int, int);
70 typedef void (*from_sdn_func_type) (long int, int*, int*, int*);
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 */
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 */
86 long int checkedCalToSdn(Calendar_type cal, int year, int month, int day)
89 long int sdn = (*to_sdn_func[cal])(year,month, day);
93 (*from_sdn_func[cal])(sdn, &y, &m, &d);
94 if ((year == y) && (month == m) && (day == d))
101 int checkedSdnToCal(Calendar_type cal, long int sdn,
102 int* year, int* month, int* day)
104 (*from_sdn_func[cal])(sdn, year, month, day);
105 if (*year > 0 && *month > 0 && *day > 0)
111 void copy_date(struct date *to, struct date *from)
113 memcpy(to, from, sizeof(struct date));
116 void init_date(struct date *d)
118 d->cal = CAL_UNKNOWN;
119 d->day_str[0] = '\0';
120 d->month_str[0] = '\0';
121 d->year_str[0] = '\0';
125 d->year_type = YEAR_SINGLE;
126 d->type = DATE_UNRECOGNIZED;
131 struct date_value* make_date_value(Date_value_type t, struct date *d1,
132 struct date *d2, const char* p)
135 copy_date(&dv_s.date1, d1);
136 copy_date(&dv_s.date2, d2);
137 strncpy(dv_s.phrase, p, MAX_PHRASE_LEN + 1);
141 /* PRE: d->cal != CAL_UNKNOWN
142 INPUT: d->day, d->month, d->year
143 OUTPUT: d->type, d->sdn1, d->sdn2
145 int numbers_to_sdn(struct date *d)
148 if (d->cal == CAL_UNKNOWN) {
149 d->type = DATE_UNRECOGNIZED;
150 gedcom_date_error(_("Cannot compute SDN for unknown calendar type"));
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;
165 else if (begin_date.day == -1) {
166 begin_date.day = 1; end_date.day = 1;
168 if (end_date.month > max_month[d->cal]) {
169 end_date.month -= max_month[d->cal];
174 gedcom_date_error(_("Year has to be given in bounded date"));
179 d->type = DATE_EXACT;
182 d->sdn1 = checkedCalToSdn(d->cal, begin_date.year, begin_date.month,
185 gedcom_date_error(_("Error converting date: year %d, month %d, day %d"),
186 begin_date.year, begin_date.month, begin_date.day);
190 if (d->type == DATE_BOUNDED) {
191 d->sdn2 = checkedCalToSdn(d->cal, end_date.year, end_date.month,
194 gedcom_date_error(_("Error converting date: year %d, month %d, day %d"),
195 end_date.year, end_date.month, end_date.day);
205 /* PRE: d->cal != CAL_UNKNOWN
206 INPUT: d->type, d->sdn1, d->sdn2
207 OUTPUT: d->day, d->month, d->year
209 int sdn_to_numbers(struct date *d)
212 if (d->cal == CAL_UNKNOWN) {
213 gedcom_date_error(_("Cannot compute from SDN for unknown calendar type"));
217 struct date begin_date;
218 struct date end_date;
221 gedcom_date_error(_("SDN 1 should be bigger than zero"));
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"));
235 gedcom_date_error(_("SDN 2 should be -1 for exact dates"));
241 gedcom_date_error(_("SDN 2 should be bigger than zero"));
244 else if (d->sdn2 <= d->sdn1) {
245 gedcom_date_error(_("SDN 2 should be bigger than SDN 1"));
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"));
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 */
262 /* year and month are relevant */
267 /* only year is relevant */
268 begin_date.month = -1;
273 gedcom_date_error(_("SDN1/SDN2 isn't a bounded date"));
283 d->year = begin_date.year;
284 d->month = begin_date.month;
285 d->day = begin_date.day;
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
295 int strings_to_numbers(struct date *d)
298 if (d->cal == CAL_UNKNOWN) {
299 gedcom_date_error(_("Cannot compute months for unknown calendar type"));
304 d->day = get_day_num(d->day_str);
305 if (d->day == -1) result = 1;
310 if (d->month_str[0]) {
311 d->month = get_month_num(d->cal, d->month_str);
312 if (d->month == -1) result = 1;
317 d->year = get_year_num(d->year_str, &d->year_type);
318 if (d->year == -1) result = 1;
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
328 int numbers_to_strings(struct date *d)
331 if (d->cal == CAL_UNKNOWN) {
332 gedcom_date_error(_("Cannot compute month names for unknown calendar type"));
337 sprintf(d->day_str, "%d", d->day);
339 if (d->month > 0 && d->month <= max_month[d->cal])
340 strcpy(d->month_str, month_name[d->cal][d->month - 1]);
342 if (d->year_type == YEAR_SINGLE)
343 sprintf(d->year_str, "%d", d->year);
345 sprintf(d->year_str, "%d/%d", d->year - 1, (d->year % 100));
350 int gedcom_normalize_date(Date_input input, struct date_value *val)
353 if (val->type != DV_PHRASE) {
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);
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);
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);
386 struct date_value* gedcom_new_date_value(const struct date_value* copy_from)
388 struct date_value* dv_ptr;
389 dv_ptr = (struct date_value*) malloc(sizeof(struct date_value));
394 memcpy(dv_ptr, copy_from, sizeof(struct date_value));
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';
405 struct date_value gedcom_parse_date(const char* line_value)
408 init_date(&dv_s.date1);
409 init_date(&dv_s.date2);
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);
419 init_gedcom_date_lex(line_value);
421 close_gedcom_date_lex();
422 if (dv_s.date1.cal != CAL_UNKNOWN)
423 result |= numbers_to_sdn(&dv_s.date1);
424 if (dv_s.date2.cal != CAL_UNKNOWN)
425 result |= numbers_to_sdn(&dv_s.date2);
428 gedcom_date_error(_("Putting date '%s' in 'phrase' member"),
430 make_date_value(DV_PHRASE, &dv_s.date1, &dv_s.date2, curr_line_value);
435 void write_date(const struct date* d)
437 if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0)
438 gedcom_error(_("Date is not normalized: some fields are invalid"));
441 case CAL_GREGORIAN: break;
443 safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
445 safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
447 safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
449 safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
454 safe_buf_append(&date_buffer, "%s ", d->day_str);
456 safe_buf_append(&date_buffer, "%s ", d->month_str);
457 safe_buf_append(&date_buffer, "%s", d->year_str);
461 char* gedcom_date_to_string(const struct date_value* val)
463 init_buffer(&date_buffer);
464 reset_buffer(&date_buffer);
468 write_date(&val->date1); break;
470 safe_buf_append(&date_buffer, "BEF ");
471 write_date(&val->date1); break;
473 safe_buf_append(&date_buffer, "AFT ");
474 write_date(&val->date1); break;
476 safe_buf_append(&date_buffer, "BET ");
477 write_date(&val->date1);
478 safe_buf_append(&date_buffer, " AND ");
479 write_date(&val->date2); break;
481 safe_buf_append(&date_buffer, "FROM ");
482 write_date(&val->date1); break;
484 safe_buf_append(&date_buffer, "TO ");
485 write_date(&val->date1); break;
487 safe_buf_append(&date_buffer, "FROM ");
488 write_date(&val->date1);
489 safe_buf_append(&date_buffer, " TO ");
490 write_date(&val->date2); break;
492 safe_buf_append(&date_buffer, "ABT ");
493 write_date(&val->date1); break;
495 safe_buf_append(&date_buffer, "CAL ");
496 write_date(&val->date1); break;
498 safe_buf_append(&date_buffer, "EST ");
499 write_date(&val->date1); break;
501 safe_buf_append(&date_buffer, "INT ");
502 write_date(&val->date1);
503 safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
505 safe_buf_append(&date_buffer, "(%s)", val->phrase); break;
510 return get_buf_string(&date_buffer);