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.
Dependents: CI-data-logger-server WattEye X10Svr SSDP_Server
Revision 10:5734dbc2f5cc, committed 2017-01-22
- 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.