common: Add new function isodate_human_to_tm
authorWerner Koch <wk@gnupg.org>
Fri, 10 Apr 2015 10:02:31 +0000 (12:02 +0200)
committerWerner Koch <wk@gnupg.org>
Fri, 10 Apr 2015 11:09:37 +0000 (13:09 +0200)
* common/gettime.c (isotime_human_p): Add arg date_only.
(isodate_human_to_tm): New.
* common/t-gettime.c (test_isodate_human_to_tm): New.
(main): Call new test.
--

This function in intended as replacement for

 strptime (foo, "%Y-%m-%d", &bar)

which is not available under Windows.

common/gettime.c
common/gettime.h
common/t-gettime.c

index 80a56bb..5a7a745 100644 (file)
@@ -216,9 +216,11 @@ isotime_p (const char *string)
 /* Scan a string and return true if the string represents the human
    readable format of an ISO time.  This format is:
       yyyy-mm-dd[ hh[:mm[:ss]]]
-   Scanning stops at the second space or at a comma.  */
+   Scanning stops at the second space or at a comma.  If DATE_ONLY is
+   true the time part is not expected and the scanning stops at the
+   first space or at a comma. */
 int
-isotime_human_p (const char *string)
+isotime_human_p (const char *string, int date_only)
 {
   const char *s;
   int i;
@@ -247,6 +249,8 @@ isotime_human_p (const char *string)
     return 1; /* Okay; only date given.  */
   if (!spacep (s))
     return 0;
+  if (date_only)
+    return 1; /* Okay; only date was requested.  */
   s++;
   if (spacep (s))
     return 1; /* Okay, second space stops scanning.  */
@@ -303,7 +307,7 @@ string2isotime (gnupg_isotime_t atime, const char *string)
       atime[15] = 0;
       return 15;
     }
-  if (!isotime_human_p (string))
+  if (!isotime_human_p (string, 0))
     return 0;
   atime[0] = string[0];
   atime[1] = string[1];
@@ -393,6 +397,36 @@ epoch2isotime (gnupg_isotime_t timebuf, time_t atime)
 }
 
 
+/* Parse a short ISO date string (YYYY-MM-DD) into a TM structure.
+   Returns 0 on success.  */
+int
+isodate_human_to_tm (const char *string, struct tm *t)
+{
+  int year, month, day;
+
+  if (!isotime_human_p (string, 1))
+    return -1;
+
+  year  = atoi_4 (string);
+  month = atoi_2 (string + 5);
+  day   = atoi_2 (string + 8);
+
+  /* Basic checks.  */
+  if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31)
+    return -1;
+
+  memset (t, 0, sizeof *t);
+  t->tm_sec  = 0;
+  t->tm_min  = 0;
+  t->tm_hour = 0;
+  t->tm_mday = day;
+  t->tm_mon  = month-1;
+  t->tm_year = year - 1900;
+  t->tm_isdst = -1;
+  return 0;
+}
+
+
 /* This function is a copy of gpgme/src/conversion.c:_gpgme_timegm.
    If you change it, then update the other one too.  */
 #ifdef HAVE_W32_SYSTEM
index 10cae17..25886d2 100644 (file)
@@ -37,10 +37,11 @@ char *elapsed_time_string (time_t since, time_t now);
 
 u32    scan_isodatestr (const char *string);
 int    isotime_p (const char *string);
-int    isotime_human_p (const char *string);
+int    isotime_human_p (const char *string, int date_only);
 size_t string2isotime (gnupg_isotime_t atime, const char *string);
 time_t isotime2epoch (const char *string);
 void   epoch2isotime (gnupg_isotime_t timebuf, time_t atime);
+int    isodate_human_to_tm (const char *string, struct tm *t);
 time_t parse_timestamp (const char *timestamp, char **endp);
 u32    add_days_to_timestamp (u32 stamp, u16 days);
 const char *strtimevalue (u32 stamp);
index 5d554ee..9b3139d 100644 (file)
@@ -174,6 +174,80 @@ test_string2isotime (void)
 }
 
 
+static void
+test_isodate_human_to_tm (void)
+{
+  struct {
+    const char *string;
+    int okay;
+    int year, mon, mday;
+  } array [] = {
+    { "1970-01-01",      1, 1970,  1,  1 },
+    { "1970-02-01",      1, 1970,  2,  1 },
+    { "1970-12-31",      1, 1970, 12, 31 },
+    { "1971-01-01",      1, 1971,  1,  1 },
+    { "1998-08-15",      1, 1998,  8, 15 },
+    { "2015-04-10",      1, 2015,  4, 10 },
+    { "2015-04-10 11:30",1, 2015,  4, 10 },
+    { "1969-12-31",      0,    0,  0,  0 },
+    { "1900-01-01",      0,    0,  0,  0 },
+    { "",                0,    0,  0,  0 },
+    { "1970-12-32",      0,    0,  0,  0 },
+    { "1970-13-01",      0,    0,  0,  0 },
+    { "1970-01-00",      0,    0,  0,  0 },
+    { "1970-00-01",      0,    0,  0,  0 },
+    { "1970-00-01",      0,    0,  0,  0 },
+    { "1970",            0,    0,  0,  0 },
+    { "1970-01",         0,    0,  0,  0 },
+    { "1970-01-1",       0,    0,  0,  0 },
+    { "1970-1--01",      0,    0,  0,  0 },
+    { "1970-01-01,",     1, 1970,  1,  1 },
+    { "1970-01-01 ",     1, 1970,  1,  1 },
+    { "1970-01-01\t",    1, 1970,  1,  1 },
+    { "1970-01-01;",     0,    0,  0,  0 },
+    { "1970-01-01:",     0,    0,  0,  0 },
+    { "1970_01-01",      0,    0,  0,  0 },
+    { "1970-01_01",      0,    0,  0,  0 },
+    { NULL, 0 }
+  };
+  int idx;
+  int okay;
+  struct tm tmbuf;
+
+  for (idx=0; array[idx].string; idx++)
+    {
+      okay = !isodate_human_to_tm (array[idx].string, &tmbuf);
+      if (okay != array[idx].okay)
+        {
+          fail (idx);
+          if (verbose)
+            fprintf (stderr, "string '%s' expected: %d, got: %d\n",
+                     array[idx].string, (int)array[idx].okay, okay);
+        }
+      else if (!okay)
+        ;
+      else if (tmbuf.tm_year + 1900 != array[idx].year
+               || tmbuf.tm_mon +1   != array[idx].mon
+               || tmbuf.tm_mday     != array[idx].mday)
+        {
+          fail (idx);
+          if (verbose)
+            fprintf (stderr, "string '%s' returned %04d-%02d-%02d\n",
+                     array[idx].string,
+                     tmbuf.tm_year + 1900, tmbuf.tm_mon + 1, tmbuf.tm_mday);
+        }
+      else if (tmbuf.tm_sec || tmbuf.tm_min || tmbuf.tm_hour
+               || tmbuf.tm_isdst != -1)
+        {
+          fail (idx);
+          if (verbose)
+            fprintf (stderr, "string '%s' returned bad time part\n",
+                     array[idx].string);
+        }
+    }
+}
+
+
 int
 main (int argc, char **argv)
 {
@@ -182,6 +256,7 @@ main (int argc, char **argv)
 
   test_isotime2epoch ();
   test_string2isotime ();
+  test_isodate_human_to_tm ();
 
   return !!errcount;
 }