2222
2323/*
2424 * Converts the calendar time to broken-down time representation
25- * Based on code from glibc-2.6
2625 *
2726 * 2009-7-14:
2827 * Moved from glibc-2.6 to kernel by Zhaolei<zhaolei@cn.fujitsu.com>
28+ * 2021-06-02:
29+ * Reimplemented by Cassio Neri <cassio.neri@gmail.com>
2930 */
3031
3132#include <linux/time.h>
3233#include <linux/module.h>
33-
34- /*
35- * Nonzero if YEAR is a leap year (every 4 years,
36- * except every 100th isn't, and every 400th is).
37- */
38- static int __isleap (long year )
39- {
40- return (year ) % 4 == 0 && ((year ) % 100 != 0 || (year ) % 400 == 0 );
41- }
42-
43- /* do a mathdiv for long type */
44- static long math_div (long a , long b )
45- {
46- return a / b - (a % b < 0 );
47- }
48-
49- /* How many leap years between y1 and y2, y1 must less or equal to y2 */
50- static long leaps_between (long y1 , long y2 )
51- {
52- long leaps1 = math_div (y1 - 1 , 4 ) - math_div (y1 - 1 , 100 )
53- + math_div (y1 - 1 , 400 );
54- long leaps2 = math_div (y2 - 1 , 4 ) - math_div (y2 - 1 , 100 )
55- + math_div (y2 - 1 , 400 );
56- return leaps2 - leaps1 ;
57- }
58-
59- /* How many days come before each month (0-12). */
60- static const unsigned short __mon_yday [2 ][13 ] = {
61- /* Normal years. */
62- {0 , 31 , 59 , 90 , 120 , 151 , 181 , 212 , 243 , 273 , 304 , 334 , 365 },
63- /* Leap years. */
64- {0 , 31 , 60 , 91 , 121 , 152 , 182 , 213 , 244 , 274 , 305 , 335 , 366 }
65- };
34+ #include <linux/kernel.h>
6635
6736#define SECS_PER_HOUR (60 * 60)
6837#define SECS_PER_DAY (SECS_PER_HOUR * 24)
@@ -77,9 +46,11 @@ static const unsigned short __mon_yday[2][13] = {
7746 */
7847void time64_to_tm (time64_t totalsecs , int offset , struct tm * result )
7948{
80- long days , rem , y ;
49+ u32 u32tmp , day_of_century , year_of_century , day_of_year , month , day ;
50+ u64 u64tmp , udays , century , year ;
51+ bool is_Jan_or_Feb , is_leap_year ;
52+ long days , rem ;
8153 int remainder ;
82- const unsigned short * ip ;
8354
8455 days = div_s64_rem (totalsecs , SECS_PER_DAY , & remainder );
8556 rem = remainder ;
@@ -103,27 +74,68 @@ void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
10374 if (result -> tm_wday < 0 )
10475 result -> tm_wday += 7 ;
10576
106- y = 1970 ;
107-
108- while (days < 0 || days >= (__isleap (y ) ? 366 : 365 )) {
109- /* Guess a corrected year, assuming 365 days per year. */
110- long yg = y + math_div (days , 365 );
111-
112- /* Adjust DAYS and Y to match the guessed year. */
113- days -= (yg - y ) * 365 + leaps_between (y , yg );
114- y = yg ;
115- }
116-
117- result -> tm_year = y - 1900 ;
118-
119- result -> tm_yday = days ;
120-
121- ip = __mon_yday [__isleap (y )];
122- for (y = 11 ; days < ip [y ]; y -- )
123- continue ;
124- days -= ip [y ];
125-
126- result -> tm_mon = y ;
127- result -> tm_mday = days + 1 ;
77+ /*
78+ * The following algorithm is, basically, Proposition 6.3 of Neri
79+ * and Schneider [1]. In a few words: it works on the computational
80+ * (fictitious) calendar where the year starts in March, month = 2
81+ * (*), and finishes in February, month = 13. This calendar is
82+ * mathematically convenient because the day of the year does not
83+ * depend on whether the year is leap or not. For instance:
84+ *
85+ * March 1st 0-th day of the year;
86+ * ...
87+ * April 1st 31-st day of the year;
88+ * ...
89+ * January 1st 306-th day of the year; (Important!)
90+ * ...
91+ * February 28th 364-th day of the year;
92+ * February 29th 365-th day of the year (if it exists).
93+ *
94+ * After having worked out the date in the computational calendar
95+ * (using just arithmetics) it's easy to convert it to the
96+ * corresponding date in the Gregorian calendar.
97+ *
98+ * [1] "Euclidean Affine Functions and Applications to Calendar
99+ * Algorithms". https://arxiv.org/abs/2102.06959
100+ *
101+ * (*) The numbering of months follows tm more closely and thus,
102+ * is slightly different from [1].
103+ */
104+
105+ udays = ((u64 ) days ) + 2305843009213814918ULL ;
106+
107+ u64tmp = 4 * udays + 3 ;
108+ century = div64_u64_rem (u64tmp , 146097 , & u64tmp );
109+ day_of_century = (u32 ) (u64tmp / 4 );
110+
111+ u32tmp = 4 * day_of_century + 3 ;
112+ u64tmp = 2939745ULL * u32tmp ;
113+ year_of_century = upper_32_bits (u64tmp );
114+ day_of_year = lower_32_bits (u64tmp ) / 2939745 / 4 ;
115+
116+ year = 100 * century + year_of_century ;
117+ is_leap_year = year_of_century ? !(year_of_century % 4 ) : !(century % 4 );
118+
119+ u32tmp = 2141 * day_of_year + 132377 ;
120+ month = u32tmp >> 16 ;
121+ day = ((u16 ) u32tmp ) / 2141 ;
122+
123+ /*
124+ * Recall that January 1st is the 306-th day of the year in the
125+ * computational (not Gregorian) calendar.
126+ */
127+ is_Jan_or_Feb = day_of_year >= 306 ;
128+
129+ /* Convert to the Gregorian calendar and adjust to Unix time. */
130+ year = year + is_Jan_or_Feb - 6313183731940000ULL ;
131+ month = is_Jan_or_Feb ? month - 12 : month ;
132+ day = day + 1 ;
133+ day_of_year += is_Jan_or_Feb ? -306 : 31 + 28 + is_leap_year ;
134+
135+ /* Convert to tm's format. */
136+ result -> tm_year = (long ) (year - 1900 );
137+ result -> tm_mon = (int ) month ;
138+ result -> tm_mday = (int ) day ;
139+ result -> tm_yday = (int ) day_of_year ;
128140}
129141EXPORT_SYMBOL (time64_to_tm );
0 commit comments