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_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_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_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_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_error(_("Cannot compute from SDN for unknown calendar type"));
217 struct date begin_date;
218 struct date end_date;
221 gedcom_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_error(_("SDN 1 isn't a valid date in the given calendar"));
235 gedcom_error(_("SDN 2 should be -1 for exact dates"));
241 gedcom_error(_("SDN 2 should be bigger than zero"));
244 else if (d->sdn2 <= d->sdn1) {
245 gedcom_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_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_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_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_error(_("Cannot compute month names for unknown calendar type"));
337 sprintf(d->day_str, "%d", d->day);
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(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(&def_date);
409 curr_line_value = line_value;
410 if (compat_mode(C_NO_REQUIRED_VALUES)
411 && !strncmp(curr_line_value, "-", 2)) {
412 gedcom_date_error(_("Empty value changed to '-'"));
413 gedcom_date_error(_("Putting date in 'phrase' member"));
414 make_date_value(DV_PHRASE, &def_date, &def_date, curr_line_value);
417 init_gedcom_date_lex(line_value);
419 close_gedcom_date_lex();
420 if (dv_s.date1.cal != CAL_UNKNOWN)
421 numbers_to_sdn(&dv_s.date1);
422 if (dv_s.date2.cal != CAL_UNKNOWN)
423 numbers_to_sdn(&dv_s.date2);
428 void write_date(struct date* d)
430 if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0)
431 gedcom_error(_("Date is not normalized: some fields are invalid"));
434 case CAL_GREGORIAN: break;
436 safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
438 safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
440 safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
442 safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
447 safe_buf_append(&date_buffer, "%s ", d->day_str);
449 safe_buf_append(&date_buffer, "%s ", d->month_str);
450 safe_buf_append(&date_buffer, "%s", d->year_str);
454 char* gedcom_date_to_string(struct date_value* val)
456 init_buffer(&date_buffer);
457 reset_buffer(&date_buffer);
461 write_date(&val->date1); break;
463 safe_buf_append(&date_buffer, "BEF ");
464 write_date(&val->date1); break;
466 safe_buf_append(&date_buffer, "AFT ");
467 write_date(&val->date1); break;
469 safe_buf_append(&date_buffer, "BET ");
470 write_date(&val->date1);
471 safe_buf_append(&date_buffer, " AND ");
472 write_date(&val->date2); break;
474 safe_buf_append(&date_buffer, "FROM ");
475 write_date(&val->date1); break;
477 safe_buf_append(&date_buffer, "TO ");
478 write_date(&val->date1); break;
480 safe_buf_append(&date_buffer, "FROM ");
481 write_date(&val->date1);
482 safe_buf_append(&date_buffer, " TO ");
483 write_date(&val->date2); break;
485 safe_buf_append(&date_buffer, "ABT ");
486 write_date(&val->date1); break;
488 safe_buf_append(&date_buffer, "CAL ");
489 write_date(&val->date1); break;
491 safe_buf_append(&date_buffer, "EST ");
492 write_date(&val->date1); break;
494 safe_buf_append(&date_buffer, "INT ");
495 write_date(&val->date1);
496 safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
498 safe_buf_append(&date_buffer, "(%s)", val->phrase); break;
503 return get_buf_string(&date_buffer);