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: year %d, month %d, day %d"),
184 begin_date.year, begin_date.month, begin_date.day);
188 if (d->type == DATE_BOUNDED) {
189 d->sdn2 = checkedCalToSdn(d->cal, end_date.year, end_date.month,
192 gedcom_error(_("Error converting date: year %d, month %d, day %d"),
193 end_date.year, end_date.month, end_date.day);
203 /* PRE: d->cal != CAL_UNKNOWN
204 INPUT: d->type, d->sdn1, d->sdn2
205 OUTPUT: d->day, d->month, d->year
207 int sdn_to_numbers(struct date *d)
210 if (d->cal == CAL_UNKNOWN) {
211 gedcom_error(_("Cannot compute from SDN for unknown calendar type"));
215 struct date begin_date;
216 struct date end_date;
219 gedcom_error(_("SDN 1 should be bigger than zero"));
223 copy_date(&begin_date, d);
224 if (!checkedSdnToCal(d->cal, d->sdn1, &begin_date.year,
225 &begin_date.month, &begin_date.day)) {
226 gedcom_error(_("SDN 1 isn't a valid date in the given calendar"));
233 gedcom_error(_("SDN 2 should be -1 for exact dates"));
239 gedcom_error(_("SDN 2 should be bigger than zero"));
242 else if (d->sdn2 <= d->sdn1) {
243 gedcom_error(_("SDN 2 should be bigger than SDN 1"));
247 copy_date(&end_date, d);
248 if (!checkedSdnToCal(d->cal, d->sdn2, &end_date.year,
249 &end_date.month, &end_date.day)) {
250 gedcom_error(_("SDN 2 isn't a valid date in the given calendar"));
254 if (begin_date.year == end_date.year) {
255 if (begin_date.month == end_date.month) {
256 if (begin_date.day == end_date.day) {
257 /* year, month and day are relevant */
260 /* year and month are relevant */
265 /* only year is relevant */
266 begin_date.month = -1;
271 gedcom_error(_("SDN1/SDN2 isn't a bounded date"));
281 d->year = begin_date.year;
282 d->month = begin_date.month;
283 d->day = begin_date.day;
289 /* PRE: d->cal != CAL_UNKNOWN
290 INPUT: d->day_str, d->month_str, d->year_str
291 OUTPUT: d->day, d->month, d->year, d->year_type
293 int strings_to_numbers(struct date *d)
296 if (d->cal == CAL_UNKNOWN) {
297 gedcom_error(_("Cannot compute months for unknown calendar type"));
302 d->day = get_day_num(d->day_str);
303 if (d->day == -1) result = 1;
308 if (d->month_str[0]) {
309 d->month = get_month_num(d->cal, d->month_str);
310 if (d->month == -1) result = 1;
315 d->year = get_year_num(d->year_str, &d->year_type);
316 if (d->year == -1) result = 1;
322 /* PRE: d->cal != CAL_UNKNOWN
323 INPUT: d->day, d->month, d->year, d->year_type
324 OUTPUT: d->day_str, d->month_str, d->year_str
326 int numbers_to_strings(struct date *d)
329 if (d->cal == CAL_UNKNOWN) {
330 gedcom_error(_("Cannot compute month names for unknown calendar type"));
335 sprintf(d->day_str, "%d", d->day);
338 strcpy(d->month_str, month_name[d->cal][d->month - 1]);
340 if (d->year_type == YEAR_SINGLE)
341 sprintf(d->year_str, "%d", d->year);
343 sprintf(d->year_str, "%d/%d", d->year - 1, (d->year % 100));
348 int gedcom_normalize_date(Date_input input, struct date_value *val)
351 if (val->type != DV_PHRASE) {
353 case DI_FROM_STRINGS:
354 result |= strings_to_numbers(&val->date1);
355 result |= numbers_to_sdn(&val->date1);
356 if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
357 result |= strings_to_numbers(&val->date2);
358 result |= numbers_to_sdn(&val->date2);
361 case DI_FROM_NUMBERS:
362 result |= numbers_to_strings(&val->date1);
363 result |= numbers_to_sdn(&val->date1);
364 if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
365 result |= numbers_to_strings(&val->date2);
366 result |= numbers_to_sdn(&val->date2);
370 result |= sdn_to_numbers(&val->date1);
371 result |= numbers_to_strings(&val->date1);
372 if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
373 result |= sdn_to_numbers(&val->date2);
374 result |= numbers_to_strings(&val->date2);
384 struct date_value* gedcom_new_date_value(struct date_value* copy_from)
386 struct date_value* dv_ptr;
387 dv_ptr = (struct date_value*) malloc(sizeof(struct date_value));
392 memcpy(dv_ptr, copy_from, sizeof(struct date_value));
394 dv_ptr->type = DV_NO_MODIFIER;
395 init_date(&dv_ptr->date1);
396 init_date(&dv_ptr->date2);
397 dv_ptr->phrase[0] = '\0';
403 struct date_value gedcom_parse_date(const char* line_value)
406 init_date(&def_date);
407 curr_line_value = line_value;
408 init_gedcom_date_lex(line_value);
410 close_gedcom_date_lex();
411 if (dv_s.date1.cal != CAL_UNKNOWN)
412 numbers_to_sdn(&dv_s.date1);
413 if (dv_s.date2.cal != CAL_UNKNOWN)
414 numbers_to_sdn(&dv_s.date2);
418 void write_date(struct date* d)
420 if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0)
421 gedcom_error(_("Date is not normalized: some fields are invalid"));
424 case CAL_GREGORIAN: break;
426 safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
428 safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
430 safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
432 safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
437 safe_buf_append(&date_buffer, "%s ", d->day_str);
439 safe_buf_append(&date_buffer, "%s ", d->month_str);
440 safe_buf_append(&date_buffer, "%s", d->year_str);
444 char* gedcom_date_to_string(struct date_value* val)
446 init_buffer(&date_buffer);
447 reset_buffer(&date_buffer);
451 write_date(&val->date1); break;
453 safe_buf_append(&date_buffer, "BEF ");
454 write_date(&val->date1); break;
456 safe_buf_append(&date_buffer, "AFT ");
457 write_date(&val->date1); break;
459 safe_buf_append(&date_buffer, "BET ");
460 write_date(&val->date1);
461 safe_buf_append(&date_buffer, " AND ");
462 write_date(&val->date2); break;
464 safe_buf_append(&date_buffer, "FROM ");
465 write_date(&val->date1); break;
467 safe_buf_append(&date_buffer, "TO ");
468 write_date(&val->date1); break;
470 safe_buf_append(&date_buffer, "FROM ");
471 write_date(&val->date1);
472 safe_buf_append(&date_buffer, " TO ");
473 write_date(&val->date2); break;
475 safe_buf_append(&date_buffer, "ABT ");
476 write_date(&val->date1); break;
478 safe_buf_append(&date_buffer, "CAL ");
479 write_date(&val->date1); break;
481 safe_buf_append(&date_buffer, "EST ");
482 write_date(&val->date1); break;
484 safe_buf_append(&date_buffer, "INT ");
485 write_date(&val->date1);
486 safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
488 safe_buf_append(&date_buffer, "(%s)", val->phrase); break;
493 return get_buf_string(&date_buffer);