Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: Add gmtime_r() to rule A.5 exceptions #68414

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/contribute/coding_guidelines/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1456,6 +1456,7 @@ Zephyr codebase:
:header: Function,Source
:widths: auto

`gmtime_r()`_,POSIX.1-2001
`strnlen()`_,POSIX.1-2008
`strtok_r()`_,POSIX.1-2001

Expand All @@ -1482,5 +1483,6 @@ these functions can lead to compatibility issues with the third-party
toolchains that come with their own C standard libraries.

.. _main Zephyr repository: https://github.com/zephyrproject-rtos/zephyr
.. _gmtime_r(): https://pubs.opengroup.org/onlinepubs/9699919799/functions/gmtime_r.html
.. _strnlen(): https://pubs.opengroup.org/onlinepubs/9699919799/functions/strlen.html
.. _strtok_r(): https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtok.html
1 change: 1 addition & 0 deletions lib/libc/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ zephyr_system_include_directories(include)
zephyr_library()
zephyr_library_property(ALLOW_EMPTY TRUE)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_ABORT source/stdlib/abort.c)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_GMTIME_R source/time/gmtime_r.c)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_TIME source/time/time.c)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_MALLOC source/stdlib/malloc.c)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_STRNLEN source/string/strnlen.c)
Expand Down
5 changes: 5 additions & 0 deletions lib/libc/common/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ config COMMON_LIBC_ABORT
help
common implementation of abort().

config COMMON_LIBC_GMTIME_R
bool
help
common implementation of gmtime_r().

config COMMON_LIBC_TIME
bool
help
Expand Down
97 changes: 97 additions & 0 deletions lib/libc/common/source/time/gmtime_r.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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 <time.h>

/* A signed type with the representation of time_t without its
* implications.
*/
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 *ZRESTRICT 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 *ZRESTRICT timep,
struct tm *ZRESTRICT result)
{
time_t z = *timep;
bigint_type days = (z >= 0 ? z : z - 86399) / 86400;
unsigned int rem = z - days * 86400;

*result = (struct tm){ 0 };

time_civil_from_days(days, result);

result->tm_hour = rem / 60U / 60U;
rem -= result->tm_hour * 60 * 60;
result->tm_min = rem / 60;
result->tm_sec = rem - result->tm_min * 60;

return result;
}
1 change: 1 addition & 0 deletions lib/libc/minimal/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ config MINIMAL_LIBC_RAND
config MINIMAL_LIBC_TIME
bool "Time functions"
select COMMON_LIBC_TIME if POSIX_CLOCK
select COMMON_LIBC_GMTIME_R
default y
help
Enable time() and gmtime_r() for the minimal libc.
Expand Down
90 changes: 0 additions & 90 deletions lib/libc/minimal/source/time/gmtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,99 +4,9 @@
* 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 <time.h>
#include <zephyr/sys/libc-hooks.h>

/* A signed type with the representation of time_t without its
* implications.
*/
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 *ZRESTRICT 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 *ZRESTRICT timep,
struct tm *ZRESTRICT result)
{
time_t z = *timep;
bigint_type days = (z >= 0 ? z : z - 86399) / 86400;
unsigned int rem = z - days * 86400;

*result = (struct tm){ 0 };

time_civil_from_days(days, result);

result->tm_hour = rem / 60U / 60U;
rem -= result->tm_hour * 60 * 60;
result->tm_min = rem / 60;
result->tm_sec = rem - result->tm_min * 60;

return result;
}

#ifdef CONFIG_MINIMAL_LIBC_NON_REENTRANT_FUNCTIONS
static Z_LIBC_DATA struct tm gmtime_result;

Expand Down
Loading