Skip to content

Commit

Permalink
libc: common: implement multiple time functions
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
ycsin committed Aug 15, 2024
1 parent 88b5693 commit 60b56a8
Show file tree
Hide file tree
Showing 13 changed files with 256 additions and 5 deletions.
6 changes: 3 additions & 3 deletions doc/services/portability/posix/option_groups/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -981,8 +981,8 @@ Enable this option with :kconfig:option:`CONFIG_POSIX_THREAD_SAFE_FUNCTIONS`.
:header: API, Supported
:widths: 50,10

asctime_r(),
ctime_r(),
asctime_r(), yes
ctime_r(), yes (UTC timezone only)
flockfile(),
ftrylockfile(),
funlockfile(),
Expand All @@ -993,7 +993,7 @@ Enable this option with :kconfig:option:`CONFIG_POSIX_THREAD_SAFE_FUNCTIONS`.
getpwnam_r(),yes :ref:`†<posix_undefined_behaviour>`
getpwuid_r(),yes :ref:`†<posix_undefined_behaviour>`
gmtime_r(), yes
localtime_r(),
localtime_r(), yes (UTC timezone only)
putc_unlocked(),
putchar_unlocked(),
rand_r(), yes
Expand Down
3 changes: 3 additions & 0 deletions lib/libc/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ 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_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_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_MALLOC source/stdlib/malloc.c)
zephyr_library_sources_ifdef(CONFIG_COMMON_LIBC_STRNLEN source/string/strnlen.c)
Expand Down
33 changes: 33 additions & 0 deletions lib/libc/common/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,44 @@ config COMMON_LIBC_ABORT
help
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
bool
help
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
bool
help
Expand Down
56 changes: 56 additions & 0 deletions lib/libc/common/source/time/asctime.c
Original file line number Diff line number Diff line change
@@ -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 changes: 26 additions & 0 deletions lib/libc/common/source/time/ctime.c
Original file line number Diff line number Diff line change
@@ -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 changes: 17 additions & 0 deletions lib/libc/common/source/time/localtime_r_utc.c
Original file line number Diff line number Diff line change
@@ -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);
}
3 changes: 3 additions & 0 deletions lib/libc/minimal/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ config MINIMAL_LIBC_TIME
bool "Time functions"
select COMMON_LIBC_TIME if POSIX_TIMERS
select COMMON_LIBC_GMTIME_R
select COMMON_LIBC_ASCTIME
select COMMON_LIBC_LOCALTIME_R_UTC
select COMMON_LIBC_CTIME
default y
help
Enable time() and gmtime_r() for the minimal libc.
Expand Down
12 changes: 11 additions & 1 deletion lib/libc/minimal/include/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,22 @@ typedef _SUSECONDS_T_ suseconds_t;

/*
* 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.
*
* 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_r(const time_t *ZRESTRICT timep,
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);

Expand Down
1 change: 1 addition & 0 deletions lib/posix/options/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ rsource "Kconfig.profile"

rsource "Kconfig.aio"
rsource "Kconfig.barrier"
rsource "Kconfig.c_lang_r"
rsource "Kconfig.c_lib_ext"
rsource "Kconfig.device_io"
rsource "Kconfig.fd_mgmt"
Expand Down
18 changes: 18 additions & 0 deletions lib/posix/options/Kconfig.c_lang_r
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions lib/posix/options/Kconfig.pthread
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ config POSIX_THREAD_PRIO_PROTECT
config POSIX_THREAD_SAFE_FUNCTIONS
bool "POSIX thread-safe functions"
select POSIX_FILE_SYSTEM_R if POSIX_FILE_SYSTEM
select POSIX_C_LANG_SUPPORT_R
help
Select 'y' here to enable POSIX thread-safe functions including asctime_r(), ctime_r(),
flockfile(), ftrylockfile(), funlockfile(), getc_unlocked(), getchar_unlocked(),
Expand Down
83 changes: 82 additions & 1 deletion tests/lib/c_lib/common/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <zephyr/kernel.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/util.h>
#include <zephyr/ztest.h>

#include <limits.h>
Expand Down Expand Up @@ -1075,7 +1076,7 @@ ZTEST(libc_common, test_strtok_r)
*
* @see gmtime(),gmtime_r().
*/
ZTEST(libc_common, test_time)
ZTEST(libc_common, test_time_gmtime)
{
time_t tests1 = 0;
time_t tests2 = -5;
Expand All @@ -1092,6 +1093,86 @@ ZTEST(libc_common, test_time)
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;

Check notice on line 1147 in tests/lib/c_lib/common/src/main.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

tests/lib/c_lib/common/src/main.c:1147 - time_t tests3 = (time_t) -214748364800; + time_t tests3 = (time_t)-214748364800;

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 time = 1718260000;

zassert_not_null(ctime_r(&time, buf));
zassert_equal(strncmp("Thu Jun 13 06:26:40 2024\n", buf, sizeof(buf)), 0);

zassert_not_null(ctime(&time));
zassert_equal(strncmp("Thu Jun 13 06:26:40 2024\n", ctime(&time), sizeof(buf)), 0);
}

/**
*
* @brief Test rand function
Expand Down
2 changes: 2 additions & 0 deletions tests/lib/c_lib/common/testcase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ tests:
- CONFIG_MINIMAL_LIBC=y
- CONFIG_MINIMAL_LIBC_NON_REENTRANT_FUNCTIONS=y
- CONFIG_MINIMAL_LIBC_RAND=y
- CONFIG_COMMON_LIBC_ASCTIME_R=y
- CONFIG_COMMON_LIBC_CTIME_R=y
libraries.libc.common.newlib:
filter: CONFIG_NEWLIB_LIBC_SUPPORTED
min_ram: 32
Expand Down

0 comments on commit 60b56a8

Please sign in to comment.