Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rtc: Optimize calculations in rtc_time64_to_tm()

Recently (in commit 7df4cfef8b35 ("rtc: Make rtc_time64_to_tm() support
dates before 1970")) the function rtc_time64_to_tm() was repaired for
times before 1970. This introduced two if blocks. Cassio Neri pointed
out that to be not neccessary and suggested an adaption that allows to
drop the two branch points again.

This is implemented here.

Also adapt the reference to the theoretical paper to link to the final
published article instead of the preprint on Cassio's request.

Suggested-by: Cassio Neri <cassio.neri@gmail.com>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Link: https://lore.kernel.org/r/20250613142405.253420-2-u.kleine-koenig@baylibre.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Uwe Kleine-König and committed by
Alexandre Belloni
ae48d354 db22fd88

+19 -21
+19 -21
drivers/rtc/lib.c
··· 51 51 */ 52 52 void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) 53 53 { 54 - int days, secs; 54 + int secs; 55 55 56 56 u64 u64tmp; 57 57 u32 u32tmp, udays, century, day_of_century, year_of_century, year, ··· 59 59 bool is_Jan_or_Feb, is_leap_year; 60 60 61 61 /* 62 - * Get days and seconds while preserving the sign to 63 - * handle negative time values (dates before 1970-01-01) 62 + * The time represented by `time` is given in seconds since 1970-01-01 63 + * (UTC). As the division done below might misbehave for negative 64 + * values, we convert it to seconds since 0000-03-01 and then assume it 65 + * will be non-negative. 66 + * Below we do 4 * udays + 3 which should fit into a 32 bit unsigned 67 + * variable. So the latest date this algorithm works for is 1073741823 68 + * days after 0000-03-01 which is in the year 2939805. 64 69 */ 65 - days = div_s64_rem(time, 86400, &secs); 70 + time += (u64)719468 * 86400; 71 + 72 + udays = div_s64_rem(time, 86400, &secs); 66 73 67 74 /* 68 - * We need 0 <= secs < 86400 which isn't given for negative 69 - * values of time. Fixup accordingly. 75 + * day of the week, 0000-03-01 was a Wednesday (in the proleptic 76 + * Gregorian calendar) 70 77 */ 71 - if (secs < 0) { 72 - days -= 1; 73 - secs += 86400; 74 - } 75 - 76 - /* day of the week, 1970-01-01 was a Thursday */ 77 - tm->tm_wday = (days + 4) % 7; 78 - /* Ensure tm_wday is always positive */ 79 - if (tm->tm_wday < 0) 80 - tm->tm_wday += 7; 78 + tm->tm_wday = (udays + 3) % 7; 81 79 82 80 /* 83 - * The following algorithm is, basically, Proposition 6.3 of Neri 81 + * The following algorithm is, basically, Figure 12 of Neri 84 82 * and Schneider [1]. In a few words: it works on the computational 85 83 * (fictitious) calendar where the year starts in March, month = 2 86 84 * (*), and finishes in February, month = 13. This calendar is ··· 98 100 * (using just arithmetics) it's easy to convert it to the 99 101 * corresponding date in the Gregorian calendar. 100 102 * 101 - * [1] "Euclidean Affine Functions and Applications to Calendar 102 - * Algorithms". https://arxiv.org/abs/2102.06959 103 + * [1] Neri C, Schneider L. Euclidean affine functions and their 104 + * application to calendar algorithms. Softw Pract Exper. 105 + * 2023;53(4):937-970. doi: 10.1002/spe.3172 106 + * https://doi.org/10.1002/spe.3172 103 107 * 104 108 * (*) The numbering of months follows rtc_time more closely and 105 109 * thus, is slightly different from [1]. 106 110 */ 107 - 108 - udays = days + 719468; 109 111 110 112 u32tmp = 4 * udays + 3; 111 113 century = u32tmp / 146097;