Skip to content
Closed
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
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@
/kernel/ @andrewboie @andyross
/lib/gui/ @vanwinkeljan
/lib/libc/ @nashif
/lib/misc/ @pabigot
/lib/os/ @andrewboie @andyross
/lib/posix/ @pfalcon
/lib/cmsis_rtos_v2/ @nashif
Expand Down
74 changes: 74 additions & 0 deletions include/misc/civiltime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2019, Peter Bigot Consulting, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @file
* @brief Utilities for converting between civil time and UNIX time
*/

#ifndef ZEPHYR_INCLUDE_MISC_CIVILTIME_H_
#define ZEPHYR_INCLUDE_MISC_CIVILTIME_H_

#include <zephyr/types.h>
#include <time.h>

/** @brief 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 1970-01-01 and the
* specified day. Negative days are before 1970-01-01.
*/
s64_t time_days_from_civil(s64_t y,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use more descriptive names than "y", etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable names match the reference implementation from which this was translated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reference implementation from which this was translated.

Then it should have licensing and copyright of it, in addition to yours? But even then, as long as you modify it at all, you can use more descriptive names.

unsigned int m,
unsigned int d);

/** @brief Convert civil time to UNIX time.
*
* @param tvp pointer to a civil time structure. @c tm_year, @c
* tm_mon, @c tm_mday, @c tm_hour, @c tm_min, and @c 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 time_unix_from_civil(const struct tm *tvp);

/** @brief Convert days relative to 1970-01-01 to date information
*
* This converts signed days relative to 1970-01-01 to the POSIX
* standard civil time structure assuming a proleptic Gregorian
* calendar.
*
* @param days the time represented as days.
*
* @return the time information for corresponding to the provided
* instant. The @c tm_year, @c tm_mon@, @c tm_mday, @c tm_wday, and
* @c tm_yday fields are set; all other fields are zero.
*/
struct tm time_civil_from_days(s64_t days);

/** @brief 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.
*
* @note On systems where @c time_t has only 32 bits the range of
* supported conversions is restricted to 1901-12-14T20:45:51 to
* 2038-01-19T03:14:07.
*
* @param time the time represented as seconds.
*
* @return the time information for corresponding to the provided instant.
*/
struct tm time_civil_from_unix(time_t time);

#endif /* ZEPHYR_INCLUDE_MISC_CIVILTIME_H_ */
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ add_subdirectory_ifdef(CONFIG_POSIX_API posix)
add_subdirectory_ifdef(CONFIG_CMSIS_RTOS_V1 cmsis_rtos_v1)
add_subdirectory_ifdef(CONFIG_CMSIS_RTOS_V2 cmsis_rtos_v2)
add_subdirectory(gui)
add_subdirectory(misc)
add_subdirectory(os)
add_subdirectory_ifdef(CONFIG_UPDATEHUB updatehub)
36 changes: 35 additions & 1 deletion lib/libc/minimal/include/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,41 @@
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_LIB_LIBC_MINIMAL_INCLUDE_TIME_H_
#define ZEPHYR_LIB_LIBC_MINIMAL_INCLUDE_TIME_H_

#include <stdint.h>

/* Dummy time.h to fulfill the requirements of certain libraries
* i.e. mbedTLS
* i.e. mbedTLS and Zephyr civil time APIs
*/

#ifdef __cplusplus
extern "C" {
#endif

struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};

typedef int64_t time_t;
typedef int32_t suseconds_t;

struct timespec {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This conflicts with #16626 , which takes care about minlibc vs newlib compatibility.

time_t tv_sec;
long tv_nsec;
};

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_LIB_LIBC_MINIMAL_INCLUDE_STDIO_H_ */
5 changes: 5 additions & 0 deletions lib/misc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: Apache-2.0

zephyr_sources(
civiltime.c
)
94 changes: 94 additions & 0 deletions lib/misc/civiltime.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2019, Peter Bigot Consulting, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <misc/civiltime.h>

/* http://howardhinnant.github.io/date_algorithms.html#days_from_civil */
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;
}

time_t time_unix_from_civil(const struct tm *tvp)
{
s64_t y = 1900 + (s64_t)tvp->tm_year;
unsigned int m = tvp->tm_mon + 1;
unsigned int d = tvp->tm_mday - 1;
s64_t ndays = time_days_from_civil(y, m, d);

return (time_t)tvp->tm_sec
+ 60 * (tvp->tm_min + 60 * tvp->tm_hour)
+ 86400 * ndays;
}

/* http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
struct tm time_civil_from_days(s64_t z)
{
struct tm tv = { 0 };

tv.tm_wday = (z >= -4) ? ((z + 4) % 7) : ((z + 5) % 7 + 6);
z += 719468;

s64_t era = ((z >= 0) ? z : (z - 146096)) / 146097;
unsigned int doe = (z - era * (s64_t)146097);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even for local vars, more descriptive names are prefererrable.

unsigned int yoe = (doe - doe / 1460U + doe / 36524U
- doe / 146096U) / 365U;
s64_t 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);

tv.tm_year = y + (m <= 2) - 1900;
tv.tm_mon = m - 1;
tv.tm_mday = d;

/* Everything above is explained on the date algorithms 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) {
tv.tm_yday = doy - 306U;
} else {
tv.tm_yday = doy + 59U +
(((yoe % 4U == 0U) && (yoe % 100U != 0U))
|| (yoe == 0U));
}

return tv;
}

struct tm time_civil_from_unix(time_t z)
{
s64_t days = (z >= 0 ? z : z - 86399) / 86400;
struct tm tv = time_civil_from_days(days);
unsigned int rem = z - days * 86400;

tv.tm_hour = rem / 60U / 60U;
rem -= tv.tm_hour * 60 * 60;
tv.tm_min = rem / 60U;
tv.tm_sec = rem - tv.tm_min * 60;

return tv;
}
8 changes: 8 additions & 0 deletions tests/lib/misc/civiltime/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.13.1)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(civiltime)

FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
1 change: 1 addition & 0 deletions tests/lib/misc/civiltime/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_ZTEST=y
25 changes: 25 additions & 0 deletions tests/lib/misc/civiltime/src/civiltime_test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2019 Peter Bigot Consulting
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef CIVILTIME_TEST_H
#define CIVILTIME_TEST_H

#include <stdlib.h>
#include <misc/civiltime.h>

struct civiltime_test_data {
time_t unix;
const char *civil;
struct tm tm;
};

void civiltime_check(const struct civiltime_test_data *tp,
size_t count);

void test_s32(void);
void test_s64(void);

#endif /* CIVILTIME_TEST_H */
59 changes: 59 additions & 0 deletions tests/lib/misc/civiltime/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2019 Peter Bigot Consulting
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <ztest.h>
#include "civiltime_test.h"

void civiltime_check(const struct civiltime_test_data *tp,
size_t count)
{
const struct civiltime_test_data *tpe = tp + count;

while (tp < tpe) {
struct tm tm = time_civil_from_unix(tp->unix);
time_t unix = time_unix_from_civil(&tm);

zassert_equal(tm.tm_year, tp->tm.tm_year,
"datetime %s year %d != %d",
tp->civil, tm.tm_year, tp->tm.tm_year);
zassert_equal(tm.tm_mon, tp->tm.tm_mon,
"datetime %s mon %d != %d",
tp->civil, tm.tm_mon, tp->tm.tm_mon);
zassert_equal(tm.tm_mday, tp->tm.tm_mday,
"datetime %s mday %d != %d",
tp->civil, tm.tm_mday, tp->tm.tm_mday);
zassert_equal(tm.tm_hour, tp->tm.tm_hour,
"datetime %s hour %d != %d",
tp->civil, tm.tm_hour, tp->tm.tm_hour);
zassert_equal(tm.tm_min, tp->tm.tm_min,
"datetime %s min %d != %d",
tp->civil, tm.tm_min, tp->tm.tm_min);
zassert_equal(tm.tm_sec, tp->tm.tm_sec,
"datetime %s sec %d != %d",
tp->civil, tm.tm_sec, tp->tm.tm_sec);
zassert_equal(tm.tm_wday, tp->tm.tm_wday,
"datetime %s wday %d != %d",
tp->civil, tm.tm_wday, tp->tm.tm_wday);
zassert_equal(tm.tm_yday, tp->tm.tm_yday,
"datetime %s yday %d != %d",
tp->civil, tm.tm_yday, tp->tm.tm_yday);
zassert_equal(tp->unix, unix,
"datetime %s reverse != %ld",
tp->civil, unix);

++tp;
}
}

/*test case main entry*/
void test_main(void)
{
ztest_test_suite(test_civiltime_api,
ztest_unit_test(test_s32),
ztest_unit_test(test_s64)
);
ztest_run_test_suite(test_civiltime_api);
}
Loading