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);
420 init_gedcom_date_lex(line_value);
422 close_gedcom_date_lex();
423 if (compat_date_check(&dv_s, &curr_line_value)) {
424 init_gedcom_date_lex(curr_line_value);
426 close_gedcom_date_lex();
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);
434 gedcom_date_error(_("Putting date '%s' in 'phrase' member"),
436 make_date_value(DV_PHRASE, &dv_s.date1, &dv_s.date2, curr_line_value);
441 void write_date(const struct date* d)
443 if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0)
444 gedcom_error(_("Date is not normalized: some fields are invalid"));
447 case CAL_GREGORIAN: break;
449 safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
451 safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
453 safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
455 safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
460 safe_buf_append(&date_buffer, "%s ", d->day_str);
462 safe_buf_append(&date_buffer, "%s ", d->month_str);
463 safe_buf_append(&date_buffer, "%s", d->year_str);
467 char* gedcom_date_to_string(const struct date_value* val)
469 init_buffer(&date_buffer);
470 reset_buffer(&date_buffer);
474 write_date(&val->date1); break;
476 safe_buf_append(&date_buffer, "BEF ");
477 write_date(&val->date1); break;
479 safe_buf_append(&date_buffer, "AFT ");
480 write_date(&val->date1); break;
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;
487 safe_buf_append(&date_buffer, "FROM ");
488 write_date(&val->date1); break;
490 safe_buf_append(&date_buffer, "TO ");
491 write_date(&val->date1); break;
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;
498 safe_buf_append(&date_buffer, "ABT ");
499 write_date(&val->date1); break;
501 safe_buf_append(&date_buffer, "CAL ");
502 write_date(&val->date1); break;
504 safe_buf_append(&date_buffer, "EST ");
505 write_date(&val->date1); break;
507 safe_buf_append(&date_buffer, "INT ");
508 write_date(&val->date1);
509 safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
511 safe_buf_append(&date_buffer, "(%s)", val->phrase); break;
516 return get_buf_string(&date_buffer);