1*5fad251fSJean-Sébastien Pédron /*
2*5fad251fSJean-Sébastien Pédron * This file is in the public domain, so clarified as of
3*5fad251fSJean-Sébastien Pédron * 1996-06-05 by Arthur David Olson.
4*5fad251fSJean-Sébastien Pédron */
5*5fad251fSJean-Sébastien Pédron
6*5fad251fSJean-Sébastien Pédron /*
7*5fad251fSJean-Sébastien Pédron * The whole implementation of `timesub()` and its dependencies are copied from
8*5fad251fSJean-Sébastien Pédron * `contrib/tzcode/localtime.c` as of FreeBSD commit
9*5fad251fSJean-Sébastien Pédron * 28f617de7d9b9c708eacb3c2c13e5287e1b7354d.
10*5fad251fSJean-Sébastien Pédron */
11*5fad251fSJean-Sébastien Pédron
12*5fad251fSJean-Sébastien Pédron #include <linux/time.h>
13*5fad251fSJean-Sébastien Pédron
14*5fad251fSJean-Sébastien Pédron enum {
15*5fad251fSJean-Sébastien Pédron SECSPERMIN = 60,
16*5fad251fSJean-Sébastien Pédron MINSPERHOUR = 60,
17*5fad251fSJean-Sébastien Pédron SECSPERHOUR = SECSPERMIN * MINSPERHOUR,
18*5fad251fSJean-Sébastien Pédron HOURSPERDAY = 24,
19*5fad251fSJean-Sébastien Pédron DAYSPERWEEK = 7,
20*5fad251fSJean-Sébastien Pédron DAYSPERNYEAR = 365,
21*5fad251fSJean-Sébastien Pédron DAYSPERLYEAR = DAYSPERNYEAR + 1,
22*5fad251fSJean-Sébastien Pédron MONSPERYEAR = 12,
23*5fad251fSJean-Sébastien Pédron YEARSPERREPEAT = 400 /* years before a Gregorian repeat */
24*5fad251fSJean-Sébastien Pédron };
25*5fad251fSJean-Sébastien Pédron
26*5fad251fSJean-Sébastien Pédron enum {
27*5fad251fSJean-Sébastien Pédron TM_SUNDAY,
28*5fad251fSJean-Sébastien Pédron TM_MONDAY,
29*5fad251fSJean-Sébastien Pédron TM_TUESDAY,
30*5fad251fSJean-Sébastien Pédron TM_WEDNESDAY,
31*5fad251fSJean-Sébastien Pédron TM_THURSDAY,
32*5fad251fSJean-Sébastien Pédron TM_FRIDAY,
33*5fad251fSJean-Sébastien Pédron TM_SATURDAY
34*5fad251fSJean-Sébastien Pédron };
35*5fad251fSJean-Sébastien Pédron
36*5fad251fSJean-Sébastien Pédron enum {
37*5fad251fSJean-Sébastien Pédron TM_JANUARY,
38*5fad251fSJean-Sébastien Pédron TM_FEBRUARY,
39*5fad251fSJean-Sébastien Pédron TM_MARCH,
40*5fad251fSJean-Sébastien Pédron TM_APRIL,
41*5fad251fSJean-Sébastien Pédron TM_MAY,
42*5fad251fSJean-Sébastien Pédron TM_JUNE,
43*5fad251fSJean-Sébastien Pédron TM_JULY,
44*5fad251fSJean-Sébastien Pédron TM_AUGUST,
45*5fad251fSJean-Sébastien Pédron TM_SEPTEMBER,
46*5fad251fSJean-Sébastien Pédron TM_OCTOBER,
47*5fad251fSJean-Sébastien Pédron TM_NOVEMBER,
48*5fad251fSJean-Sébastien Pédron TM_DECEMBER
49*5fad251fSJean-Sébastien Pédron };
50*5fad251fSJean-Sébastien Pédron
51*5fad251fSJean-Sébastien Pédron enum {
52*5fad251fSJean-Sébastien Pédron TM_YEAR_BASE = 1900,
53*5fad251fSJean-Sébastien Pédron TM_WDAY_BASE = TM_MONDAY,
54*5fad251fSJean-Sébastien Pédron EPOCH_YEAR = 1970,
55*5fad251fSJean-Sébastien Pédron EPOCH_WDAY = TM_THURSDAY
56*5fad251fSJean-Sébastien Pédron };
57*5fad251fSJean-Sébastien Pédron
58*5fad251fSJean-Sébastien Pédron #define SECSPERDAY (SECSPERHOUR * HOURSPERDAY)
59*5fad251fSJean-Sébastien Pédron #define DAYSPERREPEAT (400 * 365 + 100 - 4 + 1)
60*5fad251fSJean-Sébastien Pédron
61*5fad251fSJean-Sébastien Pédron #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
62*5fad251fSJean-Sébastien Pédron
63*5fad251fSJean-Sébastien Pédron #define TYPE_SIGNED(type) (((type) -1) < 0)
64*5fad251fSJean-Sébastien Pédron
65*5fad251fSJean-Sébastien Pédron static const int mon_lengths[2][MONSPERYEAR] = {
66*5fad251fSJean-Sébastien Pédron { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
67*5fad251fSJean-Sébastien Pédron { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
68*5fad251fSJean-Sébastien Pédron };
69*5fad251fSJean-Sébastien Pédron
70*5fad251fSJean-Sébastien Pédron static const int year_lengths[2] = {
71*5fad251fSJean-Sébastien Pédron DAYSPERNYEAR, DAYSPERLYEAR
72*5fad251fSJean-Sébastien Pédron };
73*5fad251fSJean-Sébastien Pédron
74*5fad251fSJean-Sébastien Pédron static time_t
leaps_thru_end_of_nonneg(time_t y)75*5fad251fSJean-Sébastien Pédron leaps_thru_end_of_nonneg(time_t y)
76*5fad251fSJean-Sébastien Pédron {
77*5fad251fSJean-Sébastien Pédron return y / 4 - y / 100 + y / 400;
78*5fad251fSJean-Sébastien Pédron }
79*5fad251fSJean-Sébastien Pédron
80*5fad251fSJean-Sébastien Pédron static time_t
leaps_thru_end_of(time_t y)81*5fad251fSJean-Sébastien Pédron leaps_thru_end_of(time_t y)
82*5fad251fSJean-Sébastien Pédron {
83*5fad251fSJean-Sébastien Pédron return (y < 0
84*5fad251fSJean-Sébastien Pédron ? -1 - leaps_thru_end_of_nonneg(-1 - y)
85*5fad251fSJean-Sébastien Pédron : leaps_thru_end_of_nonneg(y));
86*5fad251fSJean-Sébastien Pédron }
87*5fad251fSJean-Sébastien Pédron
88*5fad251fSJean-Sébastien Pédron static struct tm *
timesub(const time64_t * timep,int offset,struct tm * tmp)89*5fad251fSJean-Sébastien Pédron timesub(const time64_t *timep, int offset, struct tm *tmp)
90*5fad251fSJean-Sébastien Pédron {
91*5fad251fSJean-Sébastien Pédron time_t tdays;
92*5fad251fSJean-Sébastien Pédron const int * ip;
93*5fad251fSJean-Sébastien Pédron int corr;
94*5fad251fSJean-Sébastien Pédron int idays, rem, dayoff, dayrem;
95*5fad251fSJean-Sébastien Pédron time_t y;
96*5fad251fSJean-Sébastien Pédron
97*5fad251fSJean-Sébastien Pédron /* If less than SECSPERMIN, the number of seconds since the
98*5fad251fSJean-Sébastien Pédron most recent positive leap second; otherwise, do not add 1
99*5fad251fSJean-Sébastien Pédron to localtime tm_sec because of leap seconds. */
100*5fad251fSJean-Sébastien Pédron time_t secs_since_posleap = SECSPERMIN;
101*5fad251fSJean-Sébastien Pédron
102*5fad251fSJean-Sébastien Pédron corr = 0;
103*5fad251fSJean-Sébastien Pédron
104*5fad251fSJean-Sébastien Pédron /* Calculate the year, avoiding integer overflow even if
105*5fad251fSJean-Sébastien Pédron time_t is unsigned. */
106*5fad251fSJean-Sébastien Pédron tdays = *timep / SECSPERDAY;
107*5fad251fSJean-Sébastien Pédron rem = *timep % SECSPERDAY;
108*5fad251fSJean-Sébastien Pédron rem += offset % SECSPERDAY - corr % SECSPERDAY + 3 * SECSPERDAY;
109*5fad251fSJean-Sébastien Pédron dayoff = offset / SECSPERDAY - corr / SECSPERDAY + rem / SECSPERDAY - 3;
110*5fad251fSJean-Sébastien Pédron rem %= SECSPERDAY;
111*5fad251fSJean-Sébastien Pédron /* y = (EPOCH_YEAR
112*5fad251fSJean-Sébastien Pédron + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT),
113*5fad251fSJean-Sébastien Pédron sans overflow. But calculate against 1570 (EPOCH_YEAR -
114*5fad251fSJean-Sébastien Pédron YEARSPERREPEAT) instead of against 1970 so that things work
115*5fad251fSJean-Sébastien Pédron for localtime values before 1970 when time_t is unsigned. */
116*5fad251fSJean-Sébastien Pédron dayrem = tdays % DAYSPERREPEAT;
117*5fad251fSJean-Sébastien Pédron dayrem += dayoff % DAYSPERREPEAT;
118*5fad251fSJean-Sébastien Pédron y = (EPOCH_YEAR - YEARSPERREPEAT
119*5fad251fSJean-Sébastien Pédron + ((1 + dayoff / DAYSPERREPEAT + dayrem / DAYSPERREPEAT
120*5fad251fSJean-Sébastien Pédron - ((dayrem % DAYSPERREPEAT) < 0)
121*5fad251fSJean-Sébastien Pédron + tdays / DAYSPERREPEAT)
122*5fad251fSJean-Sébastien Pédron * YEARSPERREPEAT));
123*5fad251fSJean-Sébastien Pédron /* idays = (tdays + dayoff) mod DAYSPERREPEAT, sans overflow. */
124*5fad251fSJean-Sébastien Pédron idays = tdays % DAYSPERREPEAT;
125*5fad251fSJean-Sébastien Pédron idays += dayoff % DAYSPERREPEAT + 2 * DAYSPERREPEAT;
126*5fad251fSJean-Sébastien Pédron idays %= DAYSPERREPEAT;
127*5fad251fSJean-Sébastien Pédron /* Increase Y and decrease IDAYS until IDAYS is in range for Y. */
128*5fad251fSJean-Sébastien Pédron while (year_lengths[isleap(y)] <= idays) {
129*5fad251fSJean-Sébastien Pédron int tdelta = idays / DAYSPERLYEAR;
130*5fad251fSJean-Sébastien Pédron int_fast32_t ydelta = tdelta + !tdelta;
131*5fad251fSJean-Sébastien Pédron time_t newy = y + ydelta;
132*5fad251fSJean-Sébastien Pédron register int leapdays;
133*5fad251fSJean-Sébastien Pédron leapdays = leaps_thru_end_of(newy - 1) -
134*5fad251fSJean-Sébastien Pédron leaps_thru_end_of(y - 1);
135*5fad251fSJean-Sébastien Pédron idays -= ydelta * DAYSPERNYEAR;
136*5fad251fSJean-Sébastien Pédron idays -= leapdays;
137*5fad251fSJean-Sébastien Pédron y = newy;
138*5fad251fSJean-Sébastien Pédron }
139*5fad251fSJean-Sébastien Pédron
140*5fad251fSJean-Sébastien Pédron if (!TYPE_SIGNED(time_t) && y < TM_YEAR_BASE) {
141*5fad251fSJean-Sébastien Pédron int signed_y = y;
142*5fad251fSJean-Sébastien Pédron tmp->tm_year = signed_y - TM_YEAR_BASE;
143*5fad251fSJean-Sébastien Pédron } else if ((!TYPE_SIGNED(time_t) || INT_MIN + TM_YEAR_BASE <= y)
144*5fad251fSJean-Sébastien Pédron && y - TM_YEAR_BASE <= INT_MAX)
145*5fad251fSJean-Sébastien Pédron tmp->tm_year = y - TM_YEAR_BASE;
146*5fad251fSJean-Sébastien Pédron else {
147*5fad251fSJean-Sébastien Pédron return NULL;
148*5fad251fSJean-Sébastien Pédron }
149*5fad251fSJean-Sébastien Pédron tmp->tm_yday = idays;
150*5fad251fSJean-Sébastien Pédron /*
151*5fad251fSJean-Sébastien Pédron ** The "extra" mods below avoid overflow problems.
152*5fad251fSJean-Sébastien Pédron */
153*5fad251fSJean-Sébastien Pédron tmp->tm_wday = (TM_WDAY_BASE
154*5fad251fSJean-Sébastien Pédron + ((tmp->tm_year % DAYSPERWEEK)
155*5fad251fSJean-Sébastien Pédron * (DAYSPERNYEAR % DAYSPERWEEK))
156*5fad251fSJean-Sébastien Pédron + leaps_thru_end_of(y - 1)
157*5fad251fSJean-Sébastien Pédron - leaps_thru_end_of(TM_YEAR_BASE - 1)
158*5fad251fSJean-Sébastien Pédron + idays);
159*5fad251fSJean-Sébastien Pédron tmp->tm_wday %= DAYSPERWEEK;
160*5fad251fSJean-Sébastien Pédron if (tmp->tm_wday < 0)
161*5fad251fSJean-Sébastien Pédron tmp->tm_wday += DAYSPERWEEK;
162*5fad251fSJean-Sébastien Pédron tmp->tm_hour = rem / SECSPERHOUR;
163*5fad251fSJean-Sébastien Pédron rem %= SECSPERHOUR;
164*5fad251fSJean-Sébastien Pédron tmp->tm_min = rem / SECSPERMIN;
165*5fad251fSJean-Sébastien Pédron tmp->tm_sec = rem % SECSPERMIN;
166*5fad251fSJean-Sébastien Pédron
167*5fad251fSJean-Sébastien Pédron /* Use "... ??:??:60" at the end of the localtime minute containing
168*5fad251fSJean-Sébastien Pédron the second just before the positive leap second. */
169*5fad251fSJean-Sébastien Pédron tmp->tm_sec += secs_since_posleap <= tmp->tm_sec;
170*5fad251fSJean-Sébastien Pédron
171*5fad251fSJean-Sébastien Pédron ip = mon_lengths[isleap(y)];
172*5fad251fSJean-Sébastien Pédron for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
173*5fad251fSJean-Sébastien Pédron idays -= ip[tmp->tm_mon];
174*5fad251fSJean-Sébastien Pédron tmp->tm_mday = idays + 1;
175*5fad251fSJean-Sébastien Pédron tmp->tm_isdst = 0;
176*5fad251fSJean-Sébastien Pédron return tmp;
177*5fad251fSJean-Sébastien Pédron }
178*5fad251fSJean-Sébastien Pédron
179*5fad251fSJean-Sébastien Pédron void
linuxkpi_time64_to_tm(time64_t totalsecs,int offset,struct tm * result)180*5fad251fSJean-Sébastien Pédron linuxkpi_time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
181*5fad251fSJean-Sébastien Pédron {
182*5fad251fSJean-Sébastien Pédron timesub(&totalsecs, offset, result);
183*5fad251fSJean-Sébastien Pédron }
184