diff --git a/lib/libc/minimal/CMakeLists.txt b/lib/libc/minimal/CMakeLists.txt index 4cab02238fd..4a405eeef4f 100644 --- a/lib/libc/minimal/CMakeLists.txt +++ b/lib/libc/minimal/CMakeLists.txt @@ -17,4 +17,5 @@ zephyr_library_sources( source/stdout/stdout_console.c source/stdout/sprintf.c source/stdout/fprintf.c + source/time/gmtime.c ) diff --git a/lib/libc/minimal/include/time.h b/lib/libc/minimal/include/time.h index b5deae5785b..84c976f72c8 100644 --- a/lib/libc/minimal/include/time.h +++ b/lib/libc/minimal/include/time.h @@ -9,6 +9,7 @@ #define ZEPHYR_LIB_LIBC_MINIMAL_INCLUDE_TIME_H_ #include +#include /* Minimal time.h to fulfill the requirements of certain libraries * like mbedTLS and to support time APIs. @@ -38,6 +39,15 @@ struct timespec { long tv_nsec; }; +/* + * Conversion between civil time and UNIX time. The companion + * localtime() and inverse mktime() are not provided here since they + * require access to time zone information. + */ +struct tm *gmtime(const time_t *timep); +struct tm *gmtime_r(const time_t *_MLIBC_RESTRICT timep, + struct tm *_MLIBC_RESTRICT result); + #ifdef __cplusplus } #endif diff --git a/lib/libc/minimal/source/time/gmtime.c b/lib/libc/minimal/source/time/gmtime.c new file mode 100644 index 00000000000..ab6c26d05bb --- /dev/null +++ b/lib/libc/minimal/source/time/gmtime.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 Peter Bigot Consulting, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * The time_civil_from_days function is derived directly from public + * domain content written by Howard Hinnant and available at: + * http://howardhinnant.github.io/date_algorithms.html#civil_from_days + */ + +#include + +/* A signed type with the representation of time_t without its + * impliciations. + */ +typedef time_t bigint_type; + +/** Convert a UNIX time to civil time. + * + * This converts integral seconds since (before) 1970-01-01T00:00:00 + * to the POSIX standard civil time representation. Any adjustments + * due to time zone, leap seconds, or a different epoch must be + * applied to @p time before invoking this function. + * + * @param time the time represented as seconds. + * + * @return the time information for corresponding to the provided + * instant. + * + * @see http://howardhinnant.github.io/date_algorithms.html#civil_from_days + */ +static void time_civil_from_days(bigint_type z, + struct tm *_MLIBC_RESTRICT tp) +{ + tp->tm_wday = (z >= -4) ? ((z + 4) % 7) : ((z + 5) % 7 + 6); + z += 719468; + + bigint_type era = ((z >= 0) ? z : (z - 146096)) / 146097; + unsigned int doe = (z - era * (bigint_type)146097); + unsigned int yoe = (doe - doe / 1460U + doe / 36524U - doe / 146096U) + / 365U; + bigint_type y = (time_t)yoe + era * 400; + unsigned int doy = doe - (365U * yoe + yoe / 4U - yoe / 100U); + unsigned int mp = (5U * doy + 2U) / 153U; + unsigned int d = doy - (153U * mp + 2U) / 5U + 1U; + unsigned int m = mp + ((mp < 10) ? 3 : -9); + + tp->tm_year = y + (m <= 2) - 1900; + tp->tm_mon = m - 1; + tp->tm_mday = d; + + /* Everything above is explained on the referenced page, but + * doy is relative to --03-01 and we need it relative to + * --01-01. + * + * doy=306 corresponds to --01-01, doy=364 to --02-28, and + * doy=365 to --02-29. So we can just subtract 306 to handle + * January and February. + * + * For doy<306 we have to add the number of days before + * --03-01, which is 59 in a common year and 60 in a leap + * year. Note that the first year in the era is a leap year. + */ + if (doy >= 306U) { + tp->tm_yday = doy - 306U; + } else { + tp->tm_yday = doy + 59U + (((yoe % 4U == 0U) && (yoe % 100U != 0U)) || (yoe == 0U)); + } +} + +/* Convert a UNIX time to civil time. + * + * This converts integral seconds since (before) 1970-01-01T00:00:00 + * to the POSIX standard civil time representation. Any adjustments + * due to time zone, leap seconds, or a different epoch must be + * applied to @p time before invoking this function. + */ +struct tm *gmtime_r(const time_t *_MLIBC_RESTRICT timep, + struct tm *_MLIBC_RESTRICT tp) +{ + time_t z = *timep; + bigint_type days = (z >= 0 ? z : z - 86399) / 86400; + unsigned int rem = z - days * 86400; + + time_civil_from_days(days, tp); + + tp->tm_hour = rem / 60U / 60U; + rem -= tp->tm_hour * 60 * 60; + tp->tm_min = rem / 60; + tp->tm_sec = rem - tp->tm_min * 60; + + return tp; +} + +struct tm *gmtime(const time_t *timep) +{ + static struct tm shared; + + return gmtime_r(timep, &shared); +}