From 9d25b671bcd499a00ccc98d20789c3691ee07197 Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Mon, 1 Jul 2019 10:35:42 -0500 Subject: [PATCH] sys: timeutil: add module Add a generic API to provide the inverse operation for gmtime and as a home for future generic time-related functions that are not in POSIX. Signed-off-by: Peter A. Bigot --- include/sys/timeutil.h | 41 ++++++++++++++++++++++++++++ lib/os/CMakeLists.txt | 1 + lib/os/timeutil.c | 61 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 include/sys/timeutil.h create mode 100644 lib/os/timeutil.c diff --git a/include/sys/timeutil.h b/include/sys/timeutil.h new file mode 100644 index 00000000000..92acecddc89 --- /dev/null +++ b/include/sys/timeutil.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Peter Bigot Consulting, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Utilities supporting operation on time data structures. + * + * POSIX defines gmtime() to convert from time_t to struct tm, but all + * inverse transformations are non-standard or require access to time + * zone information. timeutil_timegm() implements the functionality + * of the GNU extension timegm() function. + */ + +#ifndef ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_ +#define ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Convert broken-down time to a POSIX epoch offset in seconds. + * + * @param tm pointer to broken down time. + * + * @return the corresponding time in the POSIX epoch time scale. + * + * @see http://man7.org/linux/man-pages/man3/timegm.3.html + */ +time_t timeutil_timegm(struct tm *tm); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_SYS_TIMEUTIL_H_ */ diff --git a/lib/os/CMakeLists.txt b/lib/os/CMakeLists.txt index 926ce27f35a..26c349ab9f9 100644 --- a/lib/os/CMakeLists.txt +++ b/lib/os/CMakeLists.txt @@ -12,6 +12,7 @@ zephyr_sources( mempool.c rb.c thread_entry.c + timeutil.c work_q.c ) diff --git a/lib/os/timeutil.c b/lib/os/timeutil.c new file mode 100644 index 00000000000..745f158b64a --- /dev/null +++ b/lib/os/timeutil.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 Peter Bigot Consulting, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * The time_days_from_civil function is derived directly from public + * domain content written by Howard Hinnant and available at: + * http://howardhinnant.github.io/date_algorithms.html#days_from_civil + */ + +#include +#include + +/** Convert a civil (proleptic Gregorian) date to days relative to + * 1970-01-01. + * + * @param y the calendar year + * @param m the calendar month, in the range [1, 12] + * @param d the day of the month, in the range [1, last_day_of_month(y, m)] + * + * @return the signed number of days between the specified day and + * 1970-01-01 + * + * @see http://howardhinnant.github.io/date_algorithms.html#days_from_civil + */ +static s64_t time_days_from_civil(s64_t y, + unsigned int m, + unsigned int d) +{ + y -= m <= 2; + + s64_t era = (y >= 0 ? y : y - 399) / 400; + unsigned int yoe = y - era * 400; + unsigned int doy = (153U * (m + (m > 2 ? -3 : 9)) + 2U) / 5U + d; + unsigned int doe = yoe * 365U + yoe / 4U - yoe / 100U + doy; + + return era * 146097 + (time_t)doe - 719468; +} + +/** Convert civil time to UNIX time. + * + * @param tvp pointer to a civil time structure. `tm_year`, `tm_mon`, + * `tm_mday`, `tm_hour`, `tm_min`, and `tm_sec` must be valid. All + * other fields are ignored. + * + * @return the signed number of seconds between 1970-01-01T00:00:00 + * and the specified time ignoring leap seconds and DST offsets. + */ +time_t timeutil_timegm(struct tm *tm) +{ + s64_t y = 1900 + (s64_t)tm->tm_year; + unsigned int m = tm->tm_mon + 1; + unsigned int d = tm->tm_mday - 1; + s64_t ndays = time_days_from_civil(y, m, d); + + return (time_t)tm->tm_sec + + 60 * (tm->tm_min + 60 * tm->tm_hour) + + 86400 * ndays; +}