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