Skip to content

Commit fd9166d

Browse files
ukleinekopsiff
authored andcommitted
rtc: Optimize calculations in rtc_time64_to_tm()
mainline inclusion from mainline-v6.17-rc1 category: performance Recently (in commit 7df4cfe ("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> (cherry picked from commit ae48d35) Signed-off-by: Wentao Guan <guanwentao@uniontech.com>
1 parent 0d3594d commit fd9166d

File tree

1 file changed

+19
-21
lines changed

1 file changed

+19
-21
lines changed

drivers/rtc/lib.c

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,36 +51,34 @@ EXPORT_SYMBOL(rtc_year_days);
5151
*/
5252
void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
5353
{
54-
int days, secs;
54+
int secs;
5555

5656
u64 u64tmp;
5757
u32 u32tmp, udays, century, day_of_century, year_of_century, year,
5858
day_of_year, month, day;
5959
bool is_Jan_or_Feb, is_leap_year;
6060

6161
/*
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.
6469
*/
65-
days = div_s64_rem(time, 86400, &secs);
70+
time += (u64)719468 * 86400;
71+
72+
udays = div_s64_rem(time, 86400, &secs);
6673

6774
/*
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)
7077
*/
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;
8179

8280
/*
83-
* The following algorithm is, basically, Proposition 6.3 of Neri
81+
* The following algorithm is, basically, Figure 12 of Neri
8482
* and Schneider [1]. In a few words: it works on the computational
8583
* (fictitious) calendar where the year starts in March, month = 2
8684
* (*), and finishes in February, month = 13. This calendar is
@@ -100,15 +98,15 @@ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
10098
* (using just arithmetics) it's easy to convert it to the
10199
* corresponding date in the Gregorian calendar.
102100
*
103-
* [1] "Euclidean Affine Functions and Applications to Calendar
104-
* Algorithms". https://arxiv.org/abs/2102.06959
101+
* [1] Neri C, Schneider L. Euclidean affine functions and their
102+
* application to calendar algorithms. Softw Pract Exper.
103+
* 2023;53(4):937-970. doi: 10.1002/spe.3172
104+
* https://doi.org/10.1002/spe.3172
105105
*
106106
* (*) The numbering of months follows rtc_time more closely and
107107
* thus, is slightly different from [1].
108108
*/
109109

110-
udays = days + 719468;
111-
112110
u32tmp = 4 * udays + 3;
113111
century = u32tmp / 146097;
114112
day_of_century = u32tmp % 146097 / 4;

0 commit comments

Comments
 (0)