libc: common: implement multiple time functions
Implemented the following: - `asctime_r()` - `asctime()` - `localtime()` - `localtime_r()` - `ctime()` - `ctime_r()` Specifically: - the implementation of `localtime()` & `localtime_r()` simply wraps around the gmtime() & gmtime_r() functions, the results are always expressed as UTC. - `ctime()` is equivalent to `asctime(localtime(clock))`, it inherits the limitation of `localtime()` as well, which only supports UTC results currently. Added tests for these newly implemented functions. Signed-off-by: Yong Cong Sin <ycsin@meta.com> Signed-off-by: Yong Cong Sin <yongcong.sin@gmail.com>
This commit is contained in:
parent
5978e5231e
commit
fe94d4354a
13 changed files with 256 additions and 5 deletions
|
@ -981,8 +981,8 @@ Enable this option with :kconfig:option:`CONFIG_POSIX_THREAD_SAFE_FUNCTIONS`.
|
||||||
:header: API, Supported
|
:header: API, Supported
|
||||||
:widths: 50,10
|
:widths: 50,10
|
||||||
|
|
||||||
asctime_r(),
|
asctime_r(), yes
|
||||||
ctime_r(),
|
ctime_r(), yes (UTC timezone only)
|
||||||
flockfile(),
|
flockfile(),
|
||||||
ftrylockfile(),
|
ftrylockfile(),
|
||||||
funlockfile(),
|
funlockfile(),
|
||||||
|
@ -993,7 +993,7 @@ Enable this option with :kconfig:option:`CONFIG_POSIX_THREAD_SAFE_FUNCTIONS`.
|
||||||
getpwnam_r(),yes :ref:`†<posix_undefined_behaviour>`
|
getpwnam_r(),yes :ref:`†<posix_undefined_behaviour>`
|
||||||
getpwuid_r(),yes :ref:`†<posix_undefined_behaviour>`
|
getpwuid_r(),yes :ref:`†<posix_undefined_behaviour>`
|
||||||
gmtime_r(), yes
|
gmtime_r(), yes
|
||||||
localtime_r(),
|
localtime_r(), yes (UTC timezone only)
|
||||||
putc_unlocked(),
|
putc_unlocked(),
|
||||||
putchar_unlocked(),
|
putchar_unlocked(),
|
||||||
rand_r(), yes
|
rand_r(), yes
|
||||||
|
|
|
@ -5,7 +5,10 @@ zephyr_system_include_directories(include)
|
||||||
zephyr_library()
|
zephyr_library()
|
||||||
zephyr_library_property(ALLOW_EMPTY TRUE)
|
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_ABORT source/stdlib/abort.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_ASCTIME source/time/asctime.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_GMTIME_R source/time/gmtime_r.c)
|
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_GMTIME_R source/time/gmtime_r.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_LOCALTIME_R_UTC source/time/localtime_r_utc.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_CTIME source/time/ctime.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_TIME source/time/time.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_MALLOC source/stdlib/malloc.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_STRNLEN source/string/strnlen.c)
|
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_STRNLEN source/string/strnlen.c)
|
||||||
|
|
|
@ -6,11 +6,44 @@ config COMMON_LIBC_ABORT
|
||||||
help
|
help
|
||||||
common implementation of abort().
|
common implementation of abort().
|
||||||
|
|
||||||
|
config COMMON_LIBC_ASCTIME
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
common implementation of asctime().
|
||||||
|
|
||||||
|
config COMMON_LIBC_ASCTIME_R
|
||||||
|
bool "Thread-safe version of asctime()"
|
||||||
|
default y if POSIX_THREAD_SAFE_FUNCTIONS
|
||||||
|
select COMMON_LIBC_ASCTIME
|
||||||
|
help
|
||||||
|
common implementation of asctime_r().
|
||||||
|
|
||||||
|
config COMMON_LIBC_CTIME
|
||||||
|
bool
|
||||||
|
select COMMON_LIBC_LOCALTIME_R_UTC
|
||||||
|
help
|
||||||
|
common implementation of ctime().
|
||||||
|
|
||||||
|
config COMMON_LIBC_CTIME_R
|
||||||
|
bool "Thread-safe version of ctime()"
|
||||||
|
default y if POSIX_THREAD_SAFE_FUNCTIONS
|
||||||
|
select COMMON_LIBC_CTIME
|
||||||
|
help
|
||||||
|
common implementation of ctime_r().
|
||||||
|
|
||||||
config COMMON_LIBC_GMTIME_R
|
config COMMON_LIBC_GMTIME_R
|
||||||
bool
|
bool
|
||||||
help
|
help
|
||||||
common implementation of gmtime_r().
|
common implementation of gmtime_r().
|
||||||
|
|
||||||
|
config COMMON_LIBC_LOCALTIME_R_UTC
|
||||||
|
bool
|
||||||
|
select COMMON_LIBC_GMTIME_R
|
||||||
|
help
|
||||||
|
Simple implementation of localtime() & localtime_r().
|
||||||
|
This option just wraps around the gmtime(), the result is always expressed as
|
||||||
|
Coordinated Universal Time (UTC).
|
||||||
|
|
||||||
config COMMON_LIBC_TIME
|
config COMMON_LIBC_TIME
|
||||||
bool
|
bool
|
||||||
help
|
help
|
||||||
|
|
56
lib/libc/common/source/time/asctime.c
Normal file
56
lib/libc/common/source/time/asctime.c
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Meta Platforms
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
|
||||||
|
#define DATE_STRING_BUF_SZ 26U
|
||||||
|
#define DATE_WDAY_STRING_SZ 7U
|
||||||
|
#define DATE_MON_STRING_SZ 12U
|
||||||
|
#define DATE_TM_YEAR_BASE 1900
|
||||||
|
|
||||||
|
static char *asctime_impl(const struct tm *tp, char *buf)
|
||||||
|
{
|
||||||
|
static const char wday_str[DATE_WDAY_STRING_SZ][3] = {
|
||||||
|
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
|
||||||
|
};
|
||||||
|
static const char mon_str[DATE_MON_STRING_SZ][3] = {
|
||||||
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((buf == NULL) || (tp == NULL) || ((unsigned int)tp->tm_wday >= DATE_WDAY_STRING_SZ) ||
|
||||||
|
((unsigned int)tp->tm_mon >= DATE_MON_STRING_SZ)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int n = (unsigned int)snprintf(
|
||||||
|
buf, DATE_STRING_BUF_SZ, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", wday_str[tp->tm_wday],
|
||||||
|
mon_str[tp->tm_mon], tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec,
|
||||||
|
DATE_TM_YEAR_BASE + tp->tm_year);
|
||||||
|
|
||||||
|
if (n >= DATE_STRING_BUF_SZ) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *asctime(const struct tm *tp)
|
||||||
|
{
|
||||||
|
static char buf[DATE_STRING_BUF_SZ];
|
||||||
|
|
||||||
|
return asctime_impl(tp, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_COMMON_LIBC_ASCTIME_R)
|
||||||
|
char *asctime_r(const struct tm *tp, char *buf)
|
||||||
|
{
|
||||||
|
return asctime_impl(tp, buf);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_COMMON_LIBC_ASCTIME_R */
|
26
lib/libc/common/source/time/ctime.c
Normal file
26
lib/libc/common/source/time/ctime.c
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Meta Platforms
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `ctime()` is equivalent to `asctime(localtime(clock))`
|
||||||
|
* See: https://pubs.opengroup.org/onlinepubs/009695399/functions/ctime.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
char *ctime(const time_t *clock)
|
||||||
|
{
|
||||||
|
return asctime(localtime(clock));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_COMMON_LIBC_CTIME_R)
|
||||||
|
char *ctime_r(const time_t *clock, char *buf)
|
||||||
|
{
|
||||||
|
struct tm tmp;
|
||||||
|
|
||||||
|
return asctime_r(localtime_r(clock, &tmp), buf);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_COMMON_LIBC_CTIME_R */
|
17
lib/libc/common/source/time/localtime_r_utc.c
Normal file
17
lib/libc/common/source/time/localtime_r_utc.c
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Meta Platforms
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
struct tm *localtime_r(const time_t *timer, struct tm *result)
|
||||||
|
{
|
||||||
|
return gmtime_r(timer, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tm *localtime(const time_t *timer)
|
||||||
|
{
|
||||||
|
return gmtime(timer);
|
||||||
|
}
|
|
@ -39,6 +39,9 @@ config MINIMAL_LIBC_TIME
|
||||||
bool "Time functions"
|
bool "Time functions"
|
||||||
select COMMON_LIBC_TIME if POSIX_TIMERS
|
select COMMON_LIBC_TIME if POSIX_TIMERS
|
||||||
select COMMON_LIBC_GMTIME_R
|
select COMMON_LIBC_GMTIME_R
|
||||||
|
select COMMON_LIBC_ASCTIME
|
||||||
|
select COMMON_LIBC_LOCALTIME_R_UTC
|
||||||
|
select COMMON_LIBC_CTIME
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
Enable time() and gmtime_r() for the minimal libc.
|
Enable time() and gmtime_r() for the minimal libc.
|
||||||
|
|
|
@ -45,12 +45,22 @@ typedef _SUSECONDS_T_ suseconds_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Conversion between civil time and UNIX time. The companion
|
* Conversion between civil time and UNIX time. The companion
|
||||||
* localtime() and inverse mktime() are not provided here since they
|
* mktime() is not provided here since it
|
||||||
* require access to time zone information.
|
* require access to time zone information.
|
||||||
|
*
|
||||||
|
* The localtime() & localtime_r() simply
|
||||||
|
* wraps around the gmtime() & gmtime_r() functions, the
|
||||||
|
* results are always expressed as UTC.
|
||||||
*/
|
*/
|
||||||
struct tm *gmtime(const time_t *timep);
|
struct tm *gmtime(const time_t *timep);
|
||||||
struct tm *gmtime_r(const time_t *ZRESTRICT timep,
|
struct tm *gmtime_r(const time_t *ZRESTRICT timep,
|
||||||
struct tm *ZRESTRICT result);
|
struct tm *ZRESTRICT result);
|
||||||
|
char *asctime(const struct tm *timeptr);
|
||||||
|
struct tm *localtime(const time_t *timer);
|
||||||
|
char *ctime(const time_t *clock);
|
||||||
|
char *asctime_r(const struct tm *ZRESTRICT tp, char *ZRESTRICT buf);
|
||||||
|
char *ctime_r(const time_t *clock, char *buf);
|
||||||
|
struct tm *localtime_r(const time_t *ZRESTRICT timer, struct tm *ZRESTRICT result);
|
||||||
|
|
||||||
time_t time(time_t *tloc);
|
time_t time(time_t *tloc);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ rsource "Kconfig.profile"
|
||||||
|
|
||||||
rsource "Kconfig.aio"
|
rsource "Kconfig.aio"
|
||||||
rsource "Kconfig.barrier"
|
rsource "Kconfig.barrier"
|
||||||
|
rsource "Kconfig.c_lang_r"
|
||||||
rsource "Kconfig.c_lib_ext"
|
rsource "Kconfig.c_lib_ext"
|
||||||
rsource "Kconfig.device_io"
|
rsource "Kconfig.device_io"
|
||||||
rsource "Kconfig.fd_mgmt"
|
rsource "Kconfig.fd_mgmt"
|
||||||
|
|
18
lib/posix/options/Kconfig.c_lang_r
Normal file
18
lib/posix/options/Kconfig.c_lang_r
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Copyright (c) 2024 Tenstorrent AI ULC
|
||||||
|
# Copyright (c) 2024 Meta Platforms
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config POSIX_C_LANG_SUPPORT_R
|
||||||
|
bool "Thread-Safe General ISO C Library"
|
||||||
|
select COMMON_LIBC_ASCTIME_R
|
||||||
|
select COMMON_LIBC_CTIME_R
|
||||||
|
select COMMON_LIBC_GMTIME_R
|
||||||
|
select COMMON_LIBC_LOCALTIME_R_UTC
|
||||||
|
help
|
||||||
|
Select 'y' here and Zephyr will provide an implementation of the POSIX_C_LANG_SUPPORT_R
|
||||||
|
Option Group, consisting of asctime_r(), ctime_r(), gmtime_r(), localtime_r(), rand_r(),
|
||||||
|
strerror_r(), and strtok_r()
|
||||||
|
|
||||||
|
For more informnation, please see
|
||||||
|
https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_subprofiles.html
|
|
@ -157,6 +157,7 @@ config POSIX_THREAD_PRIO_PROTECT
|
||||||
config POSIX_THREAD_SAFE_FUNCTIONS
|
config POSIX_THREAD_SAFE_FUNCTIONS
|
||||||
bool "POSIX thread-safe functions"
|
bool "POSIX thread-safe functions"
|
||||||
select POSIX_FILE_SYSTEM_R if POSIX_FILE_SYSTEM
|
select POSIX_FILE_SYSTEM_R if POSIX_FILE_SYSTEM
|
||||||
|
select POSIX_C_LANG_SUPPORT_R
|
||||||
help
|
help
|
||||||
Select 'y' here to enable POSIX thread-safe functions including asctime_r(), ctime_r(),
|
Select 'y' here to enable POSIX thread-safe functions including asctime_r(), ctime_r(),
|
||||||
flockfile(), ftrylockfile(), funlockfile(), getc_unlocked(), getchar_unlocked(),
|
flockfile(), ftrylockfile(), funlockfile(), getc_unlocked(), getchar_unlocked(),
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/sys/__assert.h>
|
#include <zephyr/sys/__assert.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
#include <zephyr/ztest.h>
|
#include <zephyr/ztest.h>
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
@ -1075,7 +1076,7 @@ ZTEST(libc_common, test_strtok_r)
|
||||||
*
|
*
|
||||||
* @see gmtime(),gmtime_r().
|
* @see gmtime(),gmtime_r().
|
||||||
*/
|
*/
|
||||||
ZTEST(libc_common, test_time)
|
ZTEST(libc_common, test_time_gmtime)
|
||||||
{
|
{
|
||||||
time_t tests1 = 0;
|
time_t tests1 = 0;
|
||||||
time_t tests2 = -5;
|
time_t tests2 = -5;
|
||||||
|
@ -1092,6 +1093,86 @@ ZTEST(libc_common, test_time)
|
||||||
zassert_not_null(gmtime_r(&tests4, &tp), "gmtime_r failed");
|
zassert_not_null(gmtime_r(&tests4, &tp), "gmtime_r failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test time function
|
||||||
|
*
|
||||||
|
* @see asctime(), asctime_r().
|
||||||
|
*/
|
||||||
|
ZTEST(libc_common, test_time_asctime)
|
||||||
|
{
|
||||||
|
char buf[26] = {0};
|
||||||
|
struct tm tp = {
|
||||||
|
.tm_sec = 10, /* Seconds */
|
||||||
|
.tm_min = 30, /* Minutes */
|
||||||
|
.tm_hour = 14, /* Hour (24-hour format) */
|
||||||
|
.tm_wday = 5, /* Day of the week (0-6, 0 = Sun) */
|
||||||
|
.tm_mday = 1, /* Day of the month */
|
||||||
|
.tm_mon = 5, /* Month (0-11, January = 0) */
|
||||||
|
.tm_year = 124, /* Year (current year - 1900) */
|
||||||
|
};
|
||||||
|
|
||||||
|
zassert_not_null(asctime_r(&tp, buf));
|
||||||
|
zassert_equal(strncmp("Fri Jun 1 14:30:10 2024\n", buf, sizeof(buf)), 0);
|
||||||
|
|
||||||
|
zassert_not_null(asctime(&tp));
|
||||||
|
zassert_equal(strncmp("Fri Jun 1 14:30:10 2024\n", asctime(&tp), sizeof(buf)), 0);
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_COMMON_LIBC_ASCTIME_R)) {
|
||||||
|
zassert_is_null(asctime_r(NULL, buf));
|
||||||
|
zassert_is_null(asctime(NULL));
|
||||||
|
|
||||||
|
zassert_is_null(asctime_r(&tp, NULL));
|
||||||
|
|
||||||
|
tp.tm_wday = 8;
|
||||||
|
zassert_is_null(asctime_r(&tp, buf));
|
||||||
|
zassert_is_null(asctime(&tp));
|
||||||
|
|
||||||
|
tp.tm_wday = 5;
|
||||||
|
tp.tm_mon = 12;
|
||||||
|
zassert_is_null(asctime_r(&tp, buf));
|
||||||
|
zassert_is_null(asctime(&tp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test time function
|
||||||
|
*
|
||||||
|
* @see localtime(), localtime_r().
|
||||||
|
*/
|
||||||
|
ZTEST(libc_common, test_time_localtime)
|
||||||
|
{
|
||||||
|
time_t tests1 = 0;
|
||||||
|
time_t tests2 = -5;
|
||||||
|
time_t tests3 = (time_t) -214748364800;
|
||||||
|
time_t tests4 = 951868800;
|
||||||
|
|
||||||
|
struct tm tp;
|
||||||
|
|
||||||
|
zassert_not_null(localtime(&tests1), "localtime failed");
|
||||||
|
zassert_not_null(localtime(&tests2), "localtime failed");
|
||||||
|
|
||||||
|
tp.tm_wday = -5;
|
||||||
|
zassert_not_null(localtime_r(&tests3, &tp), "localtime_r failed");
|
||||||
|
zassert_not_null(localtime_r(&tests4, &tp), "localtime_r failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test time function
|
||||||
|
*
|
||||||
|
* @see ctime(), ctime_r().
|
||||||
|
*/
|
||||||
|
ZTEST(libc_common, test_time_ctime)
|
||||||
|
{
|
||||||
|
char buf[26] = {0};
|
||||||
|
time_t test1 = 1718260000;
|
||||||
|
|
||||||
|
zassert_not_null(ctime_r(&test1, buf));
|
||||||
|
zassert_equal(strncmp("Thu Jun 13 06:26:40 2024\n", buf, sizeof(buf)), 0);
|
||||||
|
|
||||||
|
zassert_not_null(ctime(&test1));
|
||||||
|
zassert_equal(strncmp("Thu Jun 13 06:26:40 2024\n", ctime(&test1), sizeof(buf)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @brief Test rand function
|
* @brief Test rand function
|
||||||
|
|
|
@ -13,6 +13,8 @@ tests:
|
||||||
- CONFIG_MINIMAL_LIBC=y
|
- CONFIG_MINIMAL_LIBC=y
|
||||||
- CONFIG_MINIMAL_LIBC_NON_REENTRANT_FUNCTIONS=y
|
- CONFIG_MINIMAL_LIBC_NON_REENTRANT_FUNCTIONS=y
|
||||||
- CONFIG_MINIMAL_LIBC_RAND=y
|
- CONFIG_MINIMAL_LIBC_RAND=y
|
||||||
|
- CONFIG_COMMON_LIBC_ASCTIME_R=y
|
||||||
|
- CONFIG_COMMON_LIBC_CTIME_R=y
|
||||||
libraries.libc.common.newlib:
|
libraries.libc.common.newlib:
|
||||||
filter: CONFIG_NEWLIB_LIBC_SUPPORTED
|
filter: CONFIG_NEWLIB_LIBC_SUPPORTED
|
||||||
min_ram: 32
|
min_ram: 32
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue