1 /*
2 * Copyright (c) 2019 Peter Bigot Consulting, LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * The time_civil_from_days function is derived directly from public
9 * domain content written by Howard Hinnant and available at:
10 * http://howardhinnant.github.io/date_algorithms.html#civil_from_days
11 */
12
13 #include <zephyr/toolchain.h>
14 #include <time.h>
15
16 /* A signed type with the representation of time_t without its
17 * implications.
18 */
19 typedef time_t bigint_type;
20
21 /** Convert a UNIX time to civil time.
22 *
23 * This converts integral seconds since (before) 1970-01-01T00:00:00
24 * to the POSIX standard civil time representation. Any adjustments
25 * due to time zone, leap seconds, or a different epoch must be
26 * applied to @p time before invoking this function.
27 *
28 * @param time the time represented as seconds.
29 *
30 * @return the time information for corresponding to the provided
31 * instant.
32 *
33 * @see http://howardhinnant.github.io/date_algorithms.html#civil_from_days
34 */
time_civil_from_days(bigint_type z,struct tm * ZRESTRICT tp)35 static void time_civil_from_days(bigint_type z,
36 struct tm *ZRESTRICT tp)
37 {
38 tp->tm_wday = (z >= -4) ? ((z + 4) % 7) : ((z + 5) % 7 + 6);
39 z += 719468;
40
41 bigint_type era = ((z >= 0) ? z : (z - 146096)) / 146097;
42 unsigned int doe = (z - era * (bigint_type)146097);
43 unsigned int yoe = (doe - doe / 1460U + doe / 36524U - doe / 146096U)
44 / 365U;
45 bigint_type y = (time_t)yoe + era * 400;
46 unsigned int doy = doe - (365U * yoe + yoe / 4U - yoe / 100U);
47 unsigned int mp = (5U * doy + 2U) / 153U;
48 unsigned int d = doy - (153U * mp + 2U) / 5U + 1U;
49 unsigned int m = mp + ((mp < 10) ? 3 : -9);
50
51 tp->tm_year = y + (m <= 2) - 1900;
52 tp->tm_mon = m - 1;
53 tp->tm_mday = d;
54
55 /* Everything above is explained on the referenced page, but
56 * doy is relative to --03-01 and we need it relative to
57 * --01-01.
58 *
59 * doy=306 corresponds to --01-01, doy=364 to --02-28, and
60 * doy=365 to --02-29. So we can just subtract 306 to handle
61 * January and February.
62 *
63 * For doy<306 we have to add the number of days before
64 * --03-01, which is 59 in a common year and 60 in a leap
65 * year. Note that the first year in the era is a leap year.
66 */
67 if (doy >= 306U) {
68 tp->tm_yday = doy - 306U;
69 } else {
70 tp->tm_yday = doy + 59U + (((yoe % 4U == 0U) && (yoe % 100U != 0U)) || (yoe == 0U));
71 }
72 }
73
74 /* Convert a UNIX time to civil time.
75 *
76 * This converts integral seconds since (before) 1970-01-01T00:00:00
77 * to the POSIX standard civil time representation. Any adjustments
78 * due to time zone, leap seconds, or a different epoch must be
79 * applied to @p time before invoking this function.
80 */
gmtime_r(const time_t * ZRESTRICT timep,struct tm * ZRESTRICT result)81 struct tm *gmtime_r(const time_t *ZRESTRICT timep,
82 struct tm *ZRESTRICT result)
83 {
84 time_t z = *timep;
85 bigint_type days = (z >= 0 ? z : z - 86399) / 86400;
86 unsigned int rem = z - days * 86400;
87
88 *result = (struct tm){ 0 };
89
90 time_civil_from_days(days, result);
91
92 result->tm_hour = rem / 60U / 60U;
93 rem -= result->tm_hour * 60 * 60;
94 result->tm_min = rem / 60;
95 result->tm_sec = rem - result->tm_min * 60;
96
97 return result;
98 }
99