From f6670100b7a15b2071c2e4062f5c5a678f2a30f6 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 10 Apr 2015 12:02:31 +0200 Subject: [PATCH] common: Add new function isodate_human_to_tm * 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 | 40 +++++++++++++++++++++++-- common/gettime.h | 3 +- common/t-gettime.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 4 deletions(-) diff --git a/common/gettime.c b/common/gettime.c index 80a56bb2e..5a7a7452f 100644 --- a/common/gettime.c +++ b/common/gettime.c @@ -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 diff --git a/common/gettime.h b/common/gettime.h index 10cae17a2..25886d26a 100644 --- a/common/gettime.h +++ b/common/gettime.h @@ -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); diff --git a/common/t-gettime.c b/common/t-gettime.c index 5d554ee34..9b3139d48 100644 --- a/common/t-gettime.c +++ b/common/t-gettime.c @@ -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; }