A time interface class. This class replicates the normal time functions, but goes a couple of steps further. mbed library 82 and prior has a defective gmtime function. Also, this class enables access to setting the time, and adjusting the accuracy of the RTC.

Dependencies:   CalendarPage

Dependents:   CI-data-logger-server WattEye X10Svr SSDP_Server

Files at this revision

API Documentation at this revision

Comitter:
WiredHome
Date:
Sun Jan 22 04:06:16 2017 +0000
Parent:
8:18489e877b0b
Child:
11:1d880a50da8a
Commit message:
Added strptime, with a custom tzo enhancement

Changed in this revision

TimeInterface.cpp Show annotated file Show diff for this revision Revisions of this file
TimeInterface.h Show annotated file Show diff for this revision Revisions of this file
--- a/TimeInterface.cpp	Sat Mar 26 20:36:02 2016 +0000
+++ b/TimeInterface.cpp	Sun Jan 22 04:06:16 2017 +0000
@@ -50,7 +50,7 @@
     res = ntp.setTime(host, port, timeout);
     INFO("  ret: %d\r\n", res);
     if (res == NTP_OK) {
-        // if the time was fetched successfully, then 
+        // if the time was fetched successfully, then
         // let's save the time last set with the local tzo applied
         // and this saves the last time set for later precision
         // tuning.
@@ -63,7 +63,7 @@
 {
     int x;
     dst_event_t test_dst;
-    
+
     x = atoi(dstr);
     if (x >= 1 && x <= 12) {
         test_dst.MM = x;
@@ -99,9 +99,9 @@
 bool TimeInterface::set_dst(const char * dstStart, const char * dstStop)
 {
     dst_event_pair_t test_pair;
-    
+
     if (parseDSTstring(&test_pair.dst_start, dstStart)
-    && parseDSTstring(&test_pair.dst_stop, dstStop)) {
+            && parseDSTstring(&test_pair.dst_stop, dstStop)) {
         memcpy(&dst_pair, &test_pair, sizeof(dst_event_pair_t));
         INFO("set_dst from (%s,%s)", dstStart, dstStop);
         return true;
@@ -118,7 +118,7 @@
 
 bool TimeInterface::get_dst(void)
 {
-    return dst;   
+    return dst;
 }
 
 clock_t TimeInterface::clock(void)
@@ -140,15 +140,15 @@
 {
     time_t privTime;
     struct tm * tminfo;
-    
+
     if (dst_pair.dst_start.MM) {    // may have to change the dst
         std::time(&privTime);
         tminfo = std::localtime(&privTime);
-        
+
         uint32_t min_since_jan = minutesSinceJan(tminfo->tm_mon + 1, tminfo->tm_mday, tminfo->tm_hour, tminfo->tm_min);
         uint32_t min_dst_start = minutesSinceJan(dst_pair.dst_start.MM, dst_pair.dst_start.DD, dst_pair.dst_start.hh, dst_pair.dst_start.mm) + get_tzo_min();
         uint32_t min_dst_stop  = minutesSinceJan(dst_pair.dst_stop.MM, dst_pair.dst_stop.DD, dst_pair.dst_stop.hh, dst_pair.dst_stop.mm) + get_tzo_min();
-        
+
         if (min_since_jan >= min_dst_start && min_since_jan < min_dst_stop) {
             dst = 1;
             //INFO(" is dst: %u - %u - %u", min_since_jan, min_dst_start, min_dst_stop);
@@ -164,7 +164,7 @@
 char * TimeInterface::ctime(const time_t * timer)
 {
     char * p = std::ctime(timer);
-    
+
     if (strlen(p) < sizeof(result)) {
         strcpy(result, p);
         p = strchr(result, '\n');
@@ -185,21 +185,40 @@
         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
     };
+    struct tm_ex tmp = *timeptr;
     
+    tmp.tm_min += tmp.tm_tzo_min;
+    while (tmp.tm_min >= 60) {
+        tmp.tm_min -= 60;
+        tmp.tm_hour++;
+    }
+    while (tmp.tm_min < 0) {
+        tmp.tm_min += 60;
+        tmp.tm_hour--;
+    }
+    while (tmp.tm_hour >= 24) {
+        tmp.tm_wday = (tmp.tm_wday + 1) % 7;
+        tmp.tm_mday++;
+        tmp.tm_hour -= 24;
+    }
+    while (tmp.tm_hour < 0) {
+        tmp.tm_wday = (tmp.tm_wday + 6) % 7;
+        tmp.tm_mday--;
+        tmp.tm_hour += 24;
+    }
     sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d",
-            wday_name[timeptr->tm_wday % 7],
-            mon_name[timeptr->tm_mon % 12],
-            timeptr->tm_mday, timeptr->tm_hour,
-            timeptr->tm_min, timeptr->tm_sec,
-            1900 + timeptr->tm_year);
+            wday_name[tmp.tm_wday % 7],
+            mon_name[tmp.tm_mon % 12],
+            tmp.tm_mday, tmp.tm_hour,
+            tmp.tm_min, tmp.tm_sec,
+            1900 + tmp.tm_year);
     return result;
 }
 
-struct tm_ex * TimeInterface::gmtime(const time_t * timer)
-{
+struct tm_ex * TimeInterface::gmtime(const time_t * timer) {
     time_t priv = *timer + get_tzo_min() * 60 + dst * 3600;
     struct tm * tmp = std::localtime(&priv);
-    
+
     tm_ext.tm_sec     = tmp->tm_sec;
     tm_ext.tm_min     = tmp->tm_min;
     tm_ext.tm_hour    = tmp->tm_hour;
@@ -213,10 +232,9 @@
     return &tm_ext;
 }
 
-struct tm_ex * TimeInterface::localtime(const time_t * timer)
-{
+struct tm_ex * TimeInterface::localtime(const time_t * timer) {
     struct tm * tmp = std::localtime(timer);
-    
+
     tm_ext.tm_sec = tmp->tm_sec;
     tm_ext.tm_min = tmp->tm_min;
     tm_ext.tm_hour = tmp->tm_hour;
@@ -262,7 +280,7 @@
 {
     uint16_t th;
     uint32_t treg;
-    
+
     if (tzo_min >= -720 && tzo_min <= 720) {
         th = (uint16_t)(-tzo_min);
         treg = (th << 16) | (uint16_t)tzo_min;
@@ -274,7 +292,7 @@
 int16_t TimeInterface::get_tzo_min(void)
 {
     uint16_t th, tl;
-    
+
     th = LPC_RTC->GPREG0 >> 16;
     tl = LPC_RTC->GPREG0;
     //printf("get_tzo() is %04X %04X\r\n", th, tl);
@@ -290,16 +308,18 @@
     return LPC_RTC->GPREG1;
 }
 
-int32_t TimeInterface::get_cal() {
+int32_t TimeInterface::get_cal()
+{
     int32_t calvalue = LPC_RTC->CALIBRATION & 0x3FFFF;
 
     if (calvalue & 0x20000) {
         calvalue = -(calvalue & 0x1FFFF);
-    }    
+    }
     return calvalue;
 }
 
-void TimeInterface::set_cal(int32_t calibration) {
+void TimeInterface::set_cal(int32_t calibration)
+{
     if (calibration) {
         if (calibration < 0) {
             calibration = (-calibration & 0x1FFFF) | 0x20000;
@@ -314,7 +334,7 @@
 bool TimeInterface::adjust_sec(int32_t adjustSeconds)
 {
     time_t lastSet = get_timelastset();
-    
+
     if (lastSet != 0) {
         time_t seconds = time(NULL);    // get "now" according to the rtc
         int32_t delta = seconds - lastSet;
@@ -322,11 +342,11 @@
         int32_t calMAX = 131071;
         int32_t secPerDay = 86400;
         float errSecPerDay;
-                
+
         // Convert the current calibration and the adjustment into
         // the new calibration value
         // assume it is +10sec and it has been 2days, then the adjustment
-        // needs to be +5 sec per day, or one adjustment every 1/5th 
+        // needs to be +5 sec per day, or one adjustment every 1/5th
         // of a day, or 1 adjustment every 86400/5 counts.
         // delta = now - then (number of elapsed seconds)
         if (adjustSeconds != 0 && delta != 0) {
@@ -346,3 +366,381 @@
         return false;
     }
 }
+
+
+// #############################################################################
+/*
+ * Enhancement to use a custom tm_ex struct and the time zone by D. Smart
+ *  %Z
+ *
+ * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *      This product includes software developed by Powerdog Industries.
+ * 4. The name of Powerdog Industries may not be used to endorse or
+ *    promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define asizeof(a)      (sizeof (a) / sizeof ((a)[0]))
+
+struct dtconv {
+    char    *abbrev_month_names[12];
+    char    *month_names[12];
+    char    *abbrev_weekday_names[7];
+    char    *weekday_names[7];
+    char    *time_format;
+    char    *sdate_format;
+    char    *dtime_format;
+    char    *am_string;
+    char    *pm_string;
+    char    *ldate_format;
+    char    *zone_names[9];
+    int8_t  zone_offsets[9];
+};
+
+static struct dtconv    En_US = {
+    {
+        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+    },
+    {
+        "January", "February", "March", "April",
+        "May", "June", "July", "August",
+        "September", "October", "November", "December"
+    },
+    { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
+    {
+        "Sunday", "Monday", "Tuesday", "Wednesday",
+        "Thursday", "Friday", "Saturday"
+    },
+    "%H:%M:%S",
+    "%m/%d/%y",
+    "%a %b %e %T %Z %Y",
+    "AM",
+    "PM",
+    "%A, %B, %e, %Y",
+    { "UTC", "EST", "CST", "MST", "PST" },
+    {     0,   -5,    -6,    -7,    -8  },
+};
+
+#ifndef isprint
+#define in_range(c, lo, up)  ((u8_t)c >= lo && (u8_t)c <= up)
+#define isprint(c)           in_range(c, 0x20, 0x7f)
+#define isdigit(c)           in_range(c, '0', '9')
+#define isxdigit(c)          (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
+#define islower(c)           in_range(c, 'a', 'z')
+#define isspace(c)           (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
+#endif
+
+
+const char * TimeInterface::strptime(const char *buf, char *fmt, struct tm_ex *tm)
+{
+    char c, *ptr;
+    int i, len;
+
+    ptr = fmt;
+    while (*ptr != 0) {
+        if (*buf == 0)
+            break;
+
+        c = *ptr++;
+
+        if (c != '%') {
+            if (isspace(c))
+                while (*buf != 0 && isspace(*buf))
+                    buf++;
+            else if (c != *buf++)
+                return 0;
+            continue;
+        }
+
+        c = *ptr++;
+        switch (c) {
+            case 0:
+            case '%':
+                if (*buf++ != '%')
+                    return 0;
+                break;
+
+            case 'C':
+                buf = strptime(buf, En_US.ldate_format, tm);
+                if (buf == 0)
+                    return 0;
+                break;
+
+            case 'c':
+                buf = strptime(buf, "%x %X", tm);
+                if (buf == 0)
+                    return 0;
+                break;
+
+            case 'D':
+                buf = strptime(buf, "%m/%d/%y", tm);
+                if (buf == 0)
+                    return 0;
+                break;
+
+            case 'R':
+                buf = strptime(buf, "%H:%M", tm);
+                if (buf == 0)
+                    return 0;
+                break;
+
+            case 'r':
+                buf = strptime(buf, "%I:%M:%S %p", tm);
+                if (buf == 0)
+                    return 0;
+                break;
+
+            case 'T':
+                buf = strptime(buf, "%H:%M:%S", tm);
+                if (buf == 0)
+                    return 0;
+                break;
+
+            case 'X':
+                buf = strptime(buf, En_US.time_format, tm);
+                if (buf == 0)
+                    return 0;
+                break;
+
+            case 'x':
+                buf = strptime(buf, En_US.sdate_format, tm);
+                if (buf == 0)
+                    return 0;
+                break;
+
+            case 'j':
+                if (!isdigit(*buf))
+                    return 0;
+
+                for (i = 0; *buf != 0 && isdigit(*buf); buf++) {
+                    i *= 10;
+                    i += *buf - '0';
+                }
+                if (i > 365)
+                    return 0;
+
+                tm->tm_yday = i;
+                break;
+
+            case 'M':
+            case 'S':
+                if (*buf == 0 || isspace(*buf))
+                    break;
+
+                if (!isdigit(*buf))
+                    return 0;
+
+                for (i = 0; *buf != 0 && isdigit(*buf); buf++) {
+                    i *= 10;
+                    i += *buf - '0';
+                }
+                if (i > 59)
+                    return 0;
+
+                if (c == 'M')
+                    tm->tm_min = i;
+                else
+                    tm->tm_sec = i;
+
+                if (*buf != 0 && isspace(*buf))
+                    while (*ptr != 0 && !isspace(*ptr))
+                        ptr++;
+                break;
+
+            case 'H':
+            case 'I':
+            case 'k':
+            case 'l':
+                if (!isdigit(*buf))
+                    return 0;
+
+                for (i = 0; *buf != 0 && isdigit(*buf); buf++) {
+                    i *= 10;
+                    i += *buf - '0';
+                }
+                if (c == 'H' || c == 'k') {
+                    if (i > 23)
+                        return 0;
+                } else if (i > 11)
+                    return 0;
+
+                tm->tm_hour = i;
+
+                if (*buf != 0 && isspace(*buf))
+                    while (*ptr != 0 && !isspace(*ptr))
+                        ptr++;
+                break;
+
+            case 'p':
+                len = strlen(En_US.am_string);
+                if (strncasecmp(buf, En_US.am_string, len) == 0) {
+                    if (tm->tm_hour > 12)
+                        return 0;
+                    if (tm->tm_hour == 12)
+                        tm->tm_hour = 0;
+                    buf += len;
+                    break;
+                }
+
+                len = strlen(En_US.pm_string);
+                if (strncasecmp(buf, En_US.pm_string, len) == 0) {
+                    if (tm->tm_hour > 12)
+                        return 0;
+                    if (tm->tm_hour != 12)
+                        tm->tm_hour += 12;
+                    buf += len;
+                    break;
+                }
+
+                return 0;
+
+            case 'A':
+            case 'a':
+                for (i = 0; i < asizeof(En_US.weekday_names); i++) {
+                    len = strlen(En_US.weekday_names[i]);
+                    if (strncasecmp(buf,
+                                    En_US.weekday_names[i],
+                                    len) == 0)
+                        break;
+
+                    len = strlen(En_US.abbrev_weekday_names[i]);
+                    if (strncasecmp(buf,
+                                    En_US.abbrev_weekday_names[i],
+                                    len) == 0)
+                        break;
+                }
+                if (i == asizeof(En_US.weekday_names))
+                    return 0;
+
+                tm->tm_wday = i;
+                buf += len;
+                break;
+
+            case 'd':
+            case 'e':
+                if (!isdigit(*buf))
+                    return 0;
+
+                for (i = 0; *buf != 0 && isdigit(*buf); buf++) {
+                    i *= 10;
+                    i += *buf - '0';
+                }
+                if (i > 31)
+                    return 0;
+
+                tm->tm_mday = i;
+
+                if (*buf != 0 && isspace(*buf))
+                    while (*ptr != 0 && !isspace(*ptr))
+                        ptr++;
+                break;
+
+            case 'B':
+            case 'b':
+            case 'h':
+                for (i = 0; i < asizeof(En_US.month_names); i++) {
+                    len = strlen(En_US.month_names[i]);
+                    if (strncasecmp(buf,
+                                    En_US.month_names[i],
+                                    len) == 0)
+                        break;
+
+                    len = strlen(En_US.abbrev_month_names[i]);
+                    if (strncasecmp(buf,
+                                    En_US.abbrev_month_names[i],
+                                    len) == 0)
+                        break;
+                }
+                if (i == asizeof(En_US.month_names))
+                    return 0;
+
+                tm->tm_mon = i;
+                buf += len;
+                break;
+
+            case 'm':
+                if (!isdigit(*buf))
+                    return 0;
+
+                for (i = 0; *buf != 0 && isdigit(*buf); buf++) {
+                    i *= 10;
+                    i += *buf - '0';
+                }
+                if (i < 1 || i > 12)
+                    return 0;
+
+                tm->tm_mon = i - 1;
+
+                if (*buf != 0 && isspace(*buf))
+                    while (*ptr != 0 && !isspace(*ptr))
+                        ptr++;
+                break;
+
+            case 'Y':
+            case 'y':
+                if (*buf == 0 || isspace(*buf))
+                    break;
+
+                if (!isdigit(*buf))
+                    return 0;
+
+                for (i = 0; *buf != 0 && isdigit(*buf); buf++) {
+                    i *= 10;
+                    i += *buf - '0';
+                }
+                if (c == 'Y')
+                    i -= 1900;
+                if (i < 0)
+                    return 0;
+
+                tm->tm_year = i;
+
+                if (*buf != 0 && isspace(*buf))
+                    while (*ptr != 0 && !isspace(*ptr))
+                        ptr++;
+                break;
+            case 'Z':
+                for (i = 0; i < asizeof(En_US.zone_names); i++) {
+                    len = strlen(En_US.zone_names[i]);
+                    if (strncasecmp(buf,
+                                    En_US.zone_names[i],
+                                    len) == 0)
+                        break;
+                }
+                if (i == asizeof(En_US.zone_names))
+                    return 0;
+printf("z: %s => %d\r\n", En_US.zone_names[i], En_US.zone_offsets[i]);
+                tm->tm_tzo_min = En_US.zone_offsets[i] * 60;
+                buf += len;
+                break;
+        }
+    }
+
+    return buf;
+}
+
--- a/TimeInterface.h	Sat Mar 26 20:36:02 2016 +0000
+++ b/TimeInterface.h	Sun Jan 22 04:06:16 2017 +0000
@@ -178,6 +178,77 @@
     ///
     size_t strftime(char * ptr, size_t maxsize, const char * format, const struct tm_ex * timeptr);
     
+
+    /// Convert a string, in a defined format, to a time value in a tm_ex structure.
+    ///
+    /// Most format details leveraged from The Open Group Base Specifications Issue 6
+    /// IEEE Std 1003.1, 2004 Edition
+    /// Copyright © 2001-2004 The IEEE and The Open Group, All Rights reserved.
+    ///
+    /// Modifications for mbed, and addition of the timezone format option by D. Smart
+    ///
+    /// @code
+    ///     char timesample[] = "Jan 22 2017 01:32:48 UTC";
+    ///     tm_ex tm;
+    ///     strptime(timesample, "%b %d %Y %H:%M:%S %Z", &tm);
+    /// @endcode
+    /// 
+    /// @param[in] buf is a pointer to the string to be parsed.
+    /// @param[in] format is a pointer to a format string. See the format options.
+    /// @param[out] tm is a pointer to a tm_ex struct.
+    /// @returns a pointer to the character following the last one parsed, or null on failure
+    ///
+    /// format options:
+    ///     - %%a The day of the week, using the locale's weekday names; either the abbreviated or 
+    ///         full name may be specified.
+    ///     - %%A Equivalent to %%a.
+    ///     - %%b The month, using the locale's month names; either the abbreviated or full name 
+    ///         may be specified.
+    ///     - %%B Equivalent to %%b.
+    ///     - %%c Replaced by the locale's appropriate date and time representation.
+    ///     - %%C The century number [00,99]; leading zeros are permitted but not required.
+    ///     - %%d The day of the month [01,31]; leading zeros are permitted but not required.
+    ///     - %%D The date as %%m / %%d / %%y.
+    ///     - %%e Equivalent to %%d.
+    ///     - %%h Equivalent to %%b.
+    ///     - %%H The hour (24-hour clock) [00,23]; leading zeros are permitted but not required.
+    ///     - %%I The hour (12-hour clock) [01,12]; leading zeros are permitted but not required.
+    ///     - %%j The day number of the year [001,366]; leading zeros are permitted but not required.
+    ///     - %%m The month number [01,12]; leading zeros are permitted but not required.
+    ///     - %%M The minute [00,59]; leading zeros are permitted but not required.
+    ///     - %%n Any white space.
+    ///     - %%p The locale's equivalent of a.m or p.m.
+    ///     - %%r 12-hour clock time using the AM/PM notation if t_fmt_ampm is not an empty string 
+    ///         in the LC_TIME portion of the current locale; in the POSIX locale, this shall be 
+    ///         equivalent to %%I : %%M : %%S %%p.
+    ///     - %%R The time as %%H : %%M.
+    ///     - %%S The seconds [00,60]; leading zeros are permitted but not required.
+    ///     - %%t Any white space.
+    ///     - %%T The time as %%H : %%M : %%S.
+    ///     - %%U The week number of the year (Sunday as the first day of the week) as a decimal 
+    ///         number [00,53]; leading zeros are permitted but not required.
+    ///     - %%w The weekday as a decimal number [0,6], with 0 representing Sunday; leading zeros 
+    ///         are permitted but not required.
+    ///     - %%W The week number of the year (Monday as the first day of the week) as a decimal 
+    ///         number [00,53]; leading zeros are permitted but not required.
+    ///     - %%x The date, using the locale's date format.
+    ///     - %%X The time, using the locale's time format.
+    ///     - %%y The year within century. When a century is not otherwise specified, values in 
+    ///         the range [69,99] shall refer to years 1969 to 1999 inclusive, and values in the 
+    ///         range [00,68] shall refer to years 2000 to 2068 inclusive; leading zeros shall be 
+    ///         permitted but shall not be required.
+    ///         Note: It is expected that in a future version of IEEE Std 1003.1-2001 
+    ///         the default century inferred from a 2-digit year will change. 
+    ///         (This would apply to all commands accepting a 2-digit year as input.)
+    ///     - %%Y The year, including the century (for example, 1988).
+    ///     - %%Z The timezone offset, as a 3-letter sequence. Only a few whole-hour offsets
+    ///         have been defined.
+    ///     - %%%% Replaced by %%.
+    ///
+    const char * strptime(const char *buf, char *fmt, struct tm_ex *tm);
+
+
+
     // time zone functions
     
     /// Set the internal RTC (clock) to the time value.