-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Enhancements for civil time conversions #17003
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
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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, | ||
| 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_ */ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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_ */ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| zephyr_sources( | ||
| civiltime.c | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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}) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| CONFIG_ZTEST=y |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.