Add support for MTIME and YMDHMS.
[pspp] / src / data / data-in.c
index 14a24fffe26d367497e9262fd9daa1a0709a68c0..1588400e694ac88c1c556f1e1d9bef8965f1f676 100644 (file)
@@ -76,7 +76,11 @@ static int hexit_value (int c);
    Stores the parsed representation in OUTPUT, which the caller must have
    initialized with the given WIDTH (0 for a numeric field, otherwise the
    string width).  If FORMAT is FMT_A, then OUTPUT_ENCODING must specify the
-   correct encoding for OUTPUT (normally obtained via dict_get_encoding()). */
+   correct encoding for OUTPUT (normally obtained via dict_get_encoding()).
+
+   If successful NULL is the return value.  Otherwise a string describing
+   the problem is returned.  The caller must free this string.
+ */
 char *
 data_in (struct substring input, const char *input_encoding,
          enum fmt_type format,
@@ -109,7 +113,7 @@ data_in (struct substring input, const char *input_encoding,
     }
 
   cat = fmt_get_category (format);
-  if (cat & (FMT_CAT_BASIC | FMT_CAT_HEXADECIMAL
+  if (cat & (FMT_CAT_BASIC | FMT_CAT_HEXADECIMAL | FMT_CAT_CUSTOM
              | FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT))
     {
       /* We're going to parse these into numbers.  For this purpose we want to
@@ -281,7 +285,6 @@ parse_number (struct data_in *i)
 
   struct string tmp;
 
-  bool explicit_decimals = false;
   int save_errno;
   char *tail;
 
@@ -334,7 +337,6 @@ parse_number (struct data_in *i)
   /* Decimal point and following digits. */
   if (ss_match_byte (&i->input, style->decimal))
     {
-      explicit_decimals = true;
       ds_put_byte (&tmp, '.');
       while (c_isdigit (ss_first (i->input)))
         ds_put_byte (&tmp, ss_get_byte (&i->input));
@@ -345,7 +347,6 @@ parse_number (struct data_in *i)
       && !ss_is_empty (i->input)
       && strchr ("eEdD-+", ss_first (i->input)))
     {
-      explicit_decimals = true;
       ds_put_byte (&tmp, 'e');
 
       if (strchr ("eEdD", ss_first (i->input)))
@@ -782,20 +783,11 @@ parse_day (struct data_in *i, long *day)
   return xasprintf (_("Day (%ld) must be between 1 and 31."), *day);
 }
 
-/* Parses an integer from the beginning of I.
-   Adds SECONDS_PER_UNIT times the absolute value of the integer
-   to *TIME.
-   If *TIME_SIGN is SIGN_NO_TIME, allows a sign to precede the
-   time and sets *TIME_SIGN.  Otherwise, does not allow a sign.
-   Returns true if successful, false if no integer was present. */
-static char *
-parse_time_units (struct data_in *i, double seconds_per_unit,
-                  enum time_sign *time_sign, double *time)
-
+/* If *TIME_SIGN is SIGN_NO_TIME, allows a sign to precede the
+   time and sets *TIME_SIGN.  Otherwise, does not allow a sign. */
+static void
+parse_time_sign (struct data_in *i, enum time_sign *time_sign)
 {
-  char *error;
-  long units;
-
   if (*time_sign == SIGN_NO_TIME)
     {
       if (ss_match_byte (&i->input, '-'))
@@ -806,6 +798,19 @@ parse_time_units (struct data_in *i, double seconds_per_unit,
           *time_sign = SIGN_POSITIVE;
         }
     }
+}
+
+/* Parses an integer from the beginning of I.
+   Adds SECONDS_PER_UNIT times the absolute value of the integer
+   to *TIME.
+   Returns true if successful, false if no integer was present. */
+static char *
+parse_time_units (struct data_in *i, double seconds_per_unit, double *time)
+
+{
+  char *error;
+  long units;
+
   error = parse_int (i, &units, SIZE_MAX);
   if (error != NULL)
     return error;
@@ -922,7 +927,7 @@ parse_year (struct data_in *i, long *year, size_t max_digits)
       else
         *year += epoch_century + 100;
     }
-  if (*year >= 1582 || *year <= 19999)
+  if (*year >= 1582 && *year <= 19999)
     return NULL;
 
   return xasprintf (_("Year (%ld) must be between 1582 and 19999."), *year);
@@ -1028,7 +1033,7 @@ parse_minute_second (struct data_in *i, double *time)
   error = parse_int (i, &minute, SIZE_MAX);
   if (error != NULL)
     return error;
-  if (minute < 0 || minute > 59)
+  if (i->format != FMT_MTIME && (minute < 0 || minute > 59))
     return xasprintf (_("Minute (%ld) must be between 0 and 59."), minute);
   *time += 60. * minute;
 
@@ -1047,7 +1052,7 @@ parse_minute_second (struct data_in *i, double *time)
     *cp++ = ss_get_byte (&i->input);
   *cp = '\0';
 
-  *time += strtod (buf, NULL);
+  *time += c_strtod (buf, NULL);
 
   return NULL;
 }
@@ -1112,7 +1117,7 @@ parse_MONTH (struct data_in *i)
 }
 
 /* Parses DATE, ADATE, EDATE, JDATE, SDATE, QYR, MOYR, KWYR,
-   DATETIME, TIME and DTIME formats. */
+   DATETIME, YMDHMS, MTIME, TIME, and DTIME formats. */
 static char *
 parse_date (struct data_in *i)
 {
@@ -1169,12 +1174,16 @@ parse_date (struct data_in *i)
           error = parse_week (i, &yday);
           break;
         case 'D':
-          error = parse_time_units (i, 60. * 60. * 24., &time_sign, &time);
+          parse_time_sign (i, &time_sign);
+          error = parse_time_units (i, 60. * 60. * 24., &time);
           break;
         case 'H':
-          error = parse_time_units (i, 60. * 60., &time_sign, &time);
+          parse_time_sign (i, &time_sign);
+          error = parse_time_units (i, 60. * 60., &time);
           break;
         case 'M':
+          if (i->format == FMT_MTIME)
+            parse_time_sign (i, &time_sign);
           error = parse_minute_second (i, &time);
           break;
         case '-':
@@ -1184,6 +1193,7 @@ parse_date (struct data_in *i)
           break;
         case ':':
           error = parse_time_delimiter (i);
+          break;
         case ' ':
           if (i->format != FMT_MOYR)
             {