gpsd-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gpsd-dev] [PATCH] Add Windows versions of time related functions.


From: Rob Norris
Subject: [gpsd-dev] [PATCH] Add Windows versions of time related functions.
Date: Mon, 21 Mar 2016 20:59:42 +0000

strptime() is not part of the Windows C runtime,
 thus add a minimal implementation that converts iso8601 strings only
 (rather than a full version of strptime() from e.g. FreeBSD that handles all 
formats).

gmtime_r() is gmtime_s() on Windows with the parameter order reversed.

TESTED:
Forced the alternative strptime() implementation under Linux and scons 
build-all check passes.
[Note that iso8601_to_unix is called from at least 'test_json'.]
gpsutils.c builds for Windows via cross compilation.
---
 SConstruct |  2 +-
 gpsutils.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 93 insertions(+), 4 deletions(-)

diff --git a/SConstruct b/SConstruct
index 3eb7045..7bc2a34 100644
--- a/SConstruct
+++ b/SConstruct
@@ -705,7 +705,7 @@ else:
 
     # check function after libraries, because some function require libraries
     # for example clock_gettime() require librt on Linux glibc < 2.17
-    for f in ("daemon", "strlcpy", "strlcat", "clock_gettime"):
+    for f in ("daemon", "strlcpy", "strlcat", "clock_gettime", "strptime", 
"gmtime_r" ):
         if config.CheckFunc(f):
             confdefs.append("#define HAVE_%s 1\n" % f.upper())
         else:
diff --git a/gpsutils.c b/gpsutils.c
index 3add68b..254cd55 100644
--- a/gpsutils.c
+++ b/gpsutils.c
@@ -336,15 +336,99 @@ timestamp_t iso8601_to_unix(char *isotime)
 #ifndef __clang_analyzer__
 #ifndef USE_QT
     char *dp = NULL;
-    double usec;
+    double usec = 0;
     struct tm tm;
     memset(&tm,0,sizeof(tm));
 
+#ifdef HAVE_STRPTIME
     dp = strptime(isotime, "%Y-%m-%dT%H:%M:%S", &tm);
+#else
+    /* Fallback for systems without strptime (i.e. Windows)
+       This is a simplistic conversion for iso8601 strings only,
+       rather than embedding a full copy of strptime() that handles all 
formats */
+    double sec;
+    unsigned int tmp; // Thus avoiding needing to test for (broken) negative 
date/time numbers in token reading - only need to check the upper range
+    bool failed = false;
+    char *isotime_tokenizer = strdup(isotime);
+    if (isotime_tokenizer) {
+      char *tmpbuf;
+      char *pch = strtok_r(isotime_tokenizer, "-T:", &tmpbuf);
+      int token_number = 0;
+      while (pch != NULL) {
+       token_number++;
+       // Give up if encountered way too many tokens.
+       if (token_number > 10) {
+         failed = true;
+         break;
+       }
+       switch (token_number) {
+       case 1: // Year token
+         tmp = atoi(pch);
+         if (tmp < 9999)
+           tm.tm_year = tmp - 1900; // Adjust to tm year
+         else
+           failed = true;
+         break;
+       case 2: // Month token
+         tmp = atoi(pch);
+         if (tmp < 13)
+           tm.tm_mon = tmp - 1; // Month indexing starts from zero
+         else
+           failed = true;
+         break;
+       case 3: // Day token
+         tmp = atoi(pch);
+         if (tmp < 32)
+           tm.tm_mday = tmp;
+         else
+           failed = true;
+         break;
+       case 4: // Hour token
+         tmp = atoi(pch);
+         if (tmp < 24)
+           tm.tm_hour = tmp;
+         else
+           failed = true;
+         break;
+       case 5: // Minute token
+         tmp = atoi(pch);
+         if (tmp < 60)
+           tm.tm_min = tmp;
+         else
+           failed = true;
+         break;
+       case 6: // Seconds token
+         sec = safe_atof(pch);
+         // NB To handle timestamps with leap seconds
+         if (sec >= 0.0 && sec < 61.5 ) {
+           tm.tm_sec = (unsigned int)sec; // Truncate to get integer value
+           usec = sec - (unsigned int)sec; // Get the fractional part (if any)
+         }
+         else
+           failed = true;
+         break;
+       default: break;
+       }
+       pch = strtok_r(NULL, "-T:", &tmpbuf);
+      }
+      free(isotime_tokenizer);
+      // Split may result in more than 6 tokens if the TZ has any t's in it
+      // So check that we've seen enough tokens rather than an exact number
+      if (token_number < 6)
+       failed = true;
+    }
+    if (failed)
+      memset(&tm,0,sizeof(tm));
+    else {
+      // When successful this normalizes tm so that tm_yday is set
+      //  and thus tm is valid for use with other functions
+      if (mktime(&tm) == (time_t)-1)
+       // Failed mktime - so reset the timestamp
+       memset(&tm,0,sizeof(tm));
+    }
+#endif
     if (dp != NULL && *dp == '.')
        usec = strtod(dp, NULL);
-    else
-       usec = 0;
     /*
      * It would be nice if we could say mktime(&tm) - timezone + usec instead,
      * but timezone is not available at all on some BSDs. Besides, when working
@@ -380,7 +464,12 @@ char *unix_to_iso8601(timestamp_t fixtime, /*@ out @*/
 
     fractional = modf(fixtime, &integral);
     intfixtime = (time_t) integral;
+#ifdef HAVE_GMTIME_R
     (void)gmtime_r(&intfixtime, &when);
+#else
+    /* Fallback to try with gmtime_s - primarily for Windows */
+    (void)gmtime_s(&when, &intfixtime);
+#endif
 
     (void)strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S", &when);
     /*
-- 
2.7.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]