New date handling functions.
authorPeter Verthez <Peter.Verthez@advalvas.be>
Sat, 21 Dec 2002 15:36:28 +0000 (15:36 +0000)
committerPeter Verthez <Peter.Verthez@advalvas.be>
Sat, 21 Dec 2002 15:36:28 +0000 (15:36 +0000)
gedcom/date.c

index e866c809cc9441cc1434fea5ff2b242dbc02541a..5deab785aa17884439e64bf1ffa59849bd680a89 100644 (file)
@@ -49,13 +49,62 @@ int max_month[] = { 12,  /* CAL_GREGORIAN */
                    0    /* CAL_UNKNOWN */
                   };
 
-typedef long int (*cal_func_type) (int, int, int);
+char* month_name[][13] =
+{ /* CAL_GREGORIAN */
+  { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+    "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" },
+  /* CAL_JULIAN */
+  { "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+    "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" },
+  /* CAL_JEWISH */
+  { "TSH", "CSH", "KSL", "TVT", "SHV", "ADR", "ADS",
+    "NSN", "IYR", "SVN", "TMZ", "AAV", "ELL" },
+  /* CAL_FRENCH_REF */
+  { "VEND", "BRUM", "FRIM", "NIVO", "PLUV", "VENT", "GERM",
+    "FLOR", "PRAI", "MESS", "THER", "FRUC", "COMP" }
+};
 
-cal_func_type cal_func[] = { &GregorianToSdn,   /* CAL_GREGORIAN */
-                            &JulianToSdn,      /* CAL_JULIAN */
-                            &JewishToSdn,      /* CAL_JEWISH */
-                            &FrenchToSdn       /* CAL_FRENCH_REV */
-                           };
+typedef long int (*to_sdn_func_type) (int, int, int);
+typedef void (*from_sdn_func_type) (long int, int*, int*, int*);
+
+to_sdn_func_type to_sdn_func[] = {
+  &GregorianToSdn,   /* CAL_GREGORIAN */
+  &JulianToSdn,      /* CAL_JULIAN */
+  &JewishToSdn,      /* CAL_JEWISH */
+  &FrenchToSdn       /* CAL_FRENCH_REV */
+};
+
+from_sdn_func_type from_sdn_func[] = {
+  &SdnToGregorian,   /* CAL_GREGORIAN */
+  &SdnToJulian,      /* CAL_JULIAN */
+  &SdnToJewish,      /* CAL_JEWISH */
+  &SdnToFrench       /* CAL_FRENCH_REV */
+};
+
+long int checkedCalToSdn(Calendar_type cal, int year, int month, int day)
+{
+  int y, m, d;
+  long int sdn = (*to_sdn_func[cal])(year,month, day);
+  if (sdn <= 0)
+    return -1;
+  else {
+    (*from_sdn_func[cal])(sdn, &y, &m, &d);
+    if ((year == y) && (month == m) && (day == d))
+      return sdn;
+    else
+      return -1;
+  }
+}
+
+int checkedSdnToCal(Calendar_type cal, long int sdn,
+                   int* year, int* month, int* day)
+{
+  (*from_sdn_func[cal])(sdn, year, month, day);
+  if (*year > 0 && *month > 0 && *day > 0)
+    return 1;
+  else
+    return 0;
+}
 
 void copy_date(struct date *to, struct date from)
 {
@@ -87,41 +136,266 @@ struct date_value make_date_value(Date_value_type t, struct date d1,
   return dv_s;
 }
 
-void make_date_complete(struct date *d)
+/* PRE:     d->cal != CAL_UNKNOWN
+   INPUT:   d->day, d->month, d->year
+   OUTPUT:  d->type, d->sdn1, d->sdn2
+*/
+int numbers_to_sdn(struct date *d)
 {
-  if (d->cal == CAL_UNKNOWN)
+  int result = 0;
+  if (d->cal == CAL_UNKNOWN) {
     d->type = DATE_UNRECOGNIZED;
+    gedcom_error(_("Cannot compute SDN for unknown calendar type"));
+    result = 1;
+  }
   else {
+    struct date begin_date;
     struct date end_date;
-    cal_func_type to_sdn;
+    copy_date(&begin_date, *d);
     if (d->day == -1 || d->month == -1 || d->year == -1) {
       d->type = DATE_BOUNDED;
       copy_date(&end_date, *d);
-      if (d->month == -1) {
-       d->month = 1; end_date.month = 1;
-       d->day   = 1; end_date.day   = 1;
+      if (begin_date.month == -1) {
+       begin_date.month = 1; end_date.month = 1;
+       begin_date.day   = 1; end_date.day   = 1;
        end_date.year += 1;
       }
-      else if (d->day == -1) {
-       d->day   = 1; end_date.day   = 1;
+      else if (begin_date.day == -1) {
+       begin_date.day   = 1; end_date.day   = 1;
        end_date.month += 1;
        if (end_date.month > max_month[d->cal]) {
          end_date.month -= max_month[d->cal];
          end_date.year  += 1;
        }
       }
+      else {
+       gedcom_error(_("Year has to be given in bounded date"));
+       result = 1;
+      }
     }
     else {
       d->type = DATE_EXACT;
     }
 
-    to_sdn = cal_func[d->cal];
-    d->sdn1 = (*to_sdn)(d->year, d->month, d->day);
-    if (d->type == DATE_BOUNDED) {
-      d->sdn2 = (*to_sdn)(end_date.year, end_date.month, end_date.day);
-      d->sdn2 -= 1;
+    d->sdn1 = checkedCalToSdn(d->cal, begin_date.year, begin_date.month,
+                             begin_date.day);
+    if (d->sdn1 == -1) {
+      gedcom_error(_("Error converting date"));
+      result = 1;
+    }
+    else
+      if (d->type == DATE_BOUNDED) {
+       d->sdn2 = checkedCalToSdn(d->cal, end_date.year, end_date.month,
+                                 end_date.day);
+       if (d->sdn2 == -1) {
+         gedcom_error(_("Error converting date"));
+         result = 1;
+       }
+       else
+         d->sdn2 -= 1;
+      }
+  }
+  return result;
+}
+
+/* PRE:     d->cal != CAL_UNKNOWN
+   INPUT:   d->type, d->sdn1, d->sdn2
+   OUTPUT:  d->day, d->month, d->year
+*/
+int sdn_to_numbers(struct date *d)
+{
+  int result = 0;
+  if (d->cal == CAL_UNKNOWN) {
+    gedcom_error(_("Cannot compute from SDN for unknown calendar type"));
+    result = 1;
+  }
+  else {
+    struct date begin_date;
+    struct date end_date;
+
+    if (d->sdn1 <= 0) {
+      gedcom_error(_("SDN 1 should be bigger than zero"));
+      result = 1;
+    }
+    else {
+      copy_date(&begin_date, *d);
+      if (!checkedSdnToCal(d->cal, d->sdn1, &begin_date.year,
+                          &begin_date.month, &begin_date.day)) {
+       gedcom_error(_("SDN 1 isn't a valid date in the given calendar"));
+       result = 1;
+      }
+      else {
+       switch (d->type) {
+         case DATE_EXACT:
+           if (d->sdn2 != -1) {
+             gedcom_error(_("SDN 2 should be -1 for exact dates"));
+             result = 1;
+           }
+           break;
+         case DATE_BOUNDED:
+           if (d->sdn2 <= 0) {
+             gedcom_error(_("SDN 2 should be bigger than zero"));
+             result = 1;
+           }
+           else if (d->sdn2 <= d->sdn1) {
+             gedcom_error(_("SDN 2 should be bigger than SDN 1"));
+             result = 1;
+           }
+           else {
+             copy_date(&end_date, *d);
+             if (!checkedSdnToCal(d->cal, d->sdn2, &end_date.year,
+                                  &end_date.month, &end_date.day)) {
+               gedcom_error(_("SDN 2 isn't a valid date in the given calendar"));
+               result = 1;
+             }
+             else {
+               if (begin_date.year == end_date.year) {
+                 if (begin_date.month == end_date.month) {
+                   if (begin_date.day == end_date.day) {
+                     /* year, month and day are relevant */
+                   }
+                   else {
+                     /* year and month are relevant */
+                     begin_date.day = -1;
+                   }
+                 }
+                 else {
+                   /* only year is relevant */
+                   begin_date.month = -1;
+                   begin_date.day   = -1;
+                 }
+               }
+               else {
+                 gedcom_error(_("SDN1/SDN2 isn't a bounded date"));
+                 result = 1;
+               }
+             }
+           }
+           break;
+         default:
+           break;
+       }
+      }
+      d->year  = begin_date.year;
+      d->month = begin_date.month;
+      d->day   = begin_date.day;
+    }
+  }
+  return result;
+}
+
+/* PRE:     d->cal != CAL_UNKNOWN
+   INPUT:   d->day_str, d->month_str, d->year_str
+   OUTPUT:  d->day, d->month, d->year, d->year_type
+*/
+int strings_to_numbers(struct date *d)
+{
+  int result = 0;
+  if (d->cal == CAL_UNKNOWN) {
+    gedcom_error(_("Cannot compute months for unknown calendar type"));
+    result = 1;
+  }
+  else {
+    if (d->day_str[0]) {
+      d->day = get_day_num(d->day_str);
+      if (d->day == -1) result = 1;
+    }
+    else
+      d->day = -1;
+    
+    if (d->month_str[0]) {
+      d->month = get_month_num(d->cal, d->month_str);
+      if (d->month == -1) result = 1;
+    }
+    else
+      d->month = -1;
+    
+    d->year = get_year_num(d->year_str, &d->year_type);
+    if (d->year == -1) result = 1;
+  }
+  
+  return result;
+}
+
+/* PRE:     d->cal != CAL_UNKNOWN
+   INPUT:   d->day, d->month, d->year, d->year_type
+   OUTPUT:  d->day_str, d->month_str, d->year_str
+*/
+int numbers_to_strings(struct date *d)
+{
+  int result = 0;
+  if (d->cal == CAL_UNKNOWN) {
+    gedcom_error(_("Cannot compute month names for unknown calendar type"));
+    result = 1;
+  }
+  else {
+    if (d->day != -1)
+      sprintf(d->day_str, "%d", d->day);
+    
+    if (d->month != -1)
+      strcpy(d->month_str, month_name[d->cal][d->month - 1]);
+    
+    if (d->year_type == YEAR_SINGLE)
+      sprintf(d->year_str, "%d", d->year);
+    else
+      sprintf(d->year_str, "%d/%d", d->year - 1, (d->year % 100));
+  }
+  return result;
+}
+
+int gedcom_normalize_date(Date_input input, struct date_value *val)
+{
+  int result = 0;
+  if (val->type != DV_PHRASE) {
+    switch (input) {
+      case DI_FROM_STRINGS:
+       result |= strings_to_numbers(&val->date1);
+       result |= numbers_to_sdn(&val->date1);
+       if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
+         result |= strings_to_numbers(&val->date2);
+         result |= numbers_to_sdn(&val->date2);
+       }
+       break;
+      case DI_FROM_NUMBERS:
+       result |= numbers_to_strings(&val->date1);
+       result |= numbers_to_sdn(&val->date1);
+       if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
+         result |= numbers_to_strings(&val->date2);
+         result |= numbers_to_sdn(&val->date2);
+       }
+       break;
+      case DI_FROM_SDN:
+       result |= sdn_to_numbers(&val->date1);
+       result |= numbers_to_strings(&val->date1);
+       if (val->type == DV_BETWEEN || val->type == DV_FROM_TO) {
+         result |= sdn_to_numbers(&val->date2);
+         result |= numbers_to_strings(&val->date2);
+       }
+       break;
+      default:
+       break;
     }
   }
+  return result;
+}
+
+struct date_value* gedcom_new_date_value(struct date_value* copy_from)
+{
+  struct date_value* dv_ptr;
+  dv_ptr = (struct date_value*) malloc(sizeof(struct date_value));
+  if (!dv_ptr)
+    MEMORY_ERROR;
+  else {
+    if (copy_from)
+      memcpy(dv_ptr, copy_from, sizeof(struct date_value));
+    else {
+      dv_ptr->type = DV_NO_MODIFIER;
+      init_date(&dv_ptr->date1);
+      init_date(&dv_ptr->date2);
+      dv_ptr->phrase[0] = '\0';
+    }
+  }
+  return dv_ptr;
 }
 
 struct date_value gedcom_parse_date(const char* line_value)
@@ -132,74 +406,81 @@ struct date_value gedcom_parse_date(const char* line_value)
   init_gedcom_date_lex(line_value);
   gedcom_date_parse();
   close_gedcom_date_lex();
-  make_date_complete(&dv_s.date1);
-  make_date_complete(&dv_s.date2);
+  if (dv_s.date1.cal != CAL_UNKNOWN)
+    numbers_to_sdn(&dv_s.date1);
+  if (dv_s.date2.cal != CAL_UNKNOWN)
+    numbers_to_sdn(&dv_s.date2);
   return dv_s;
 }
 
-void add_date(struct date* d)
+void write_date(struct date* d)
 {
-  switch (d->cal) {
-    case CAL_GREGORIAN: break;
-    case CAL_JULIAN:
-      safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
-    case CAL_HEBREW:
-      safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
-    case CAL_FRENCH_REV:
-      safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
-    case CAL_UNKNOWN:
-      safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
-    default:
-      break;
+  if (! d->year_str[0] || d->year <= 0 || d->sdn1 <= 0)
+    gedcom_error(_("Date is not normalized: some fields are invalid"));
+  else {
+    switch (d->cal) {
+      case CAL_GREGORIAN: break;
+      case CAL_JULIAN:
+       safe_buf_append(&date_buffer, "@#DJULIAN@ "); break;
+      case CAL_HEBREW:
+       safe_buf_append(&date_buffer, "@#DHEBREW@ "); break;
+      case CAL_FRENCH_REV:
+       safe_buf_append(&date_buffer, "@#DFRENCH R@ "); break;
+      case CAL_UNKNOWN:
+       safe_buf_append(&date_buffer, "@#DUNKNOWN@ "); break;
+      default:
+       break;
+    }
+    if (d->day_str[0])
+      safe_buf_append(&date_buffer, "%s ", d->day_str);
+    if (d->month_str[0])
+      safe_buf_append(&date_buffer, "%s ", d->month_str);
+    safe_buf_append(&date_buffer, "%s", d->year_str);
   }
-  if (d->day_str[0])
-    safe_buf_append(&date_buffer, "%s ", d->day_str);
-  if (d->month_str[0])
-    safe_buf_append(&date_buffer, "%s ", d->month_str);
-  safe_buf_append(&date_buffer, "%s", d->year_str);
 }
 
 char* gedcom_date_to_string(struct date_value* val)
 {
+  init_buffer(&date_buffer);
   reset_buffer(&date_buffer);
   
   switch (val->type) {
     case DV_NO_MODIFIER:
-      add_date(&val->date1); break;
+      write_date(&val->date1); break;
     case DV_BEFORE:
       safe_buf_append(&date_buffer, "BEF ");
-      add_date(&val->date1); break;
+      write_date(&val->date1); break;
     case DV_AFTER:
       safe_buf_append(&date_buffer, "AFT ");
-      add_date(&val->date1); break;
+      write_date(&val->date1); break;
     case DV_BETWEEN:
       safe_buf_append(&date_buffer, "BET ");
-      add_date(&val->date1);
+      write_date(&val->date1);
       safe_buf_append(&date_buffer, " AND ");
-      add_date(&val->date2); break;
+      write_date(&val->date2); break;
     case DV_FROM:
       safe_buf_append(&date_buffer, "FROM ");
-      add_date(&val->date1); break;
+      write_date(&val->date1); break;
     case DV_TO:
       safe_buf_append(&date_buffer, "TO ");
-      add_date(&val->date1); break;
+      write_date(&val->date1); break;
     case DV_FROM_TO:
       safe_buf_append(&date_buffer, "FROM ");
-      add_date(&val->date1);
+      write_date(&val->date1);
       safe_buf_append(&date_buffer, " TO ");
-      add_date(&val->date2); break;
+      write_date(&val->date2); break;
     case DV_ABOUT:
       safe_buf_append(&date_buffer, "ABT ");
-      add_date(&val->date1); break;
+      write_date(&val->date1); break;
     case DV_CALCULATED:
       safe_buf_append(&date_buffer, "CAL ");
-      add_date(&val->date1); break;
+      write_date(&val->date1); break;
     case DV_ESTIMATED:
       safe_buf_append(&date_buffer, "EST ");
-      add_date(&val->date1); break;
+      write_date(&val->date1); break;
     case DV_INTERPRETED:
       safe_buf_append(&date_buffer, "INT ");
-      add_date(&val->date1);
+      write_date(&val->date1);
       safe_buf_append(&date_buffer, " (%s)", val->phrase); break;
     case DV_PHRASE:
       safe_buf_append(&date_buffer, "(%s)", val->phrase); break;