1 /*-
2 * Copyright © 2004, 2005, 2006, 2007, 2011
3 * Thorsten “mirabilos” Glaser <tg@mirbsd.org>
4 *
5 * Provided that these terms and disclaimer and all copyright notices
6 * are retained or reproduced in an accompanying document, permission
7 * is granted to deal in this work without restriction, including un‐
8 * limited rights to use, publicly perform, distribute, sell, modify,
9 * merge, give away, or sublicence.
10 *
11 * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
12 * the utmost extent permitted by applicable law, neither express nor
13 * implied; without malicious intent or gross negligence. In no event
14 * may a licensor, author or contributor be held liable for indirect,
15 * direct, other damage, loss, or other issues arising in any way out
16 * of dealing in the work, even if advised of the possibility of such
17 * damage or existence of a defect, except proven that it results out
18 * of said person’s immediate fault when using the work as intended.
19 *-
20 * MirBSD new time functions (common part, mirtime_getleaps is system
21 * specific). The code to convert betwen MJD and broken-down calendar
22 * dates is a re-implementation of algorithms from Dan J. Bernstein.
23 *
24 * This code works by the assumption that leap seconds are always po‐
25 * sitive and happen at most once per cluster (no double leapsecs).
26 */
27
28 #include <sys/types.h>
29 #include <syskern/mirtime.h>
30
31 __RCSID("$MirOS: src/kern/c/mirtime.c,v 1.3 2011/11/20 23:40:10 tg Exp $");
32
33 #ifdef L_timet2posix
34 time_t
timet2posix(time_t t)35 timet2posix(time_t t)
36 {
37 time_t u = t;
38 const time_t *lp;
39
40 if (__predict_true(t > 0)) {
41 lp = mirtime_getleaps();
42 while (__predict_true((*lp) && (t >= *lp))) {
43 --u;
44 ++lp;
45 }
46 }
47 return (u);
48 }
49 #endif
50
51 #ifdef L_posix2timet
52 time_t
posix2timet(time_t t)53 posix2timet(time_t t)
54 {
55 const time_t *lp;
56
57 if (__predict_true(t > 0)) {
58 lp = mirtime_getleaps();
59 while (__predict_true((*lp) && (t >= *lp))) {
60 ++t;
61 ++lp;
62 }
63 }
64 return (t);
65 }
66 #endif
67
68 #ifdef L_timet2mjd
69 mirtime_mjd *
timet2mjd(mirtime_mjd * mjd,time_t t)70 timet2mjd(mirtime_mjd *mjd, time_t t)
71 {
72 time_t u;
73
74 /* POSIX time inherent property: days have 86400 seconds */
75 u = timet2posix(t);
76 mjd->sec = (int32_t)(u % 86400);
77 u /= 86400;
78 u += 40587;
79
80 /* normalise */
81 while (__predict_false(mjd->sec < 0)) {
82 --u;
83 mjd->sec += 86400;
84 }
85
86 /* account for a possible leap second */
87 if (__predict_false(mirtime_isleap(t)))
88 ++mjd->sec;
89
90 mjd->mjd = u;
91 return (mjd);
92 }
93 #endif
94
95 #ifdef L_mjd2timet
96 time_t
mjd2timet(mirtime_mjd * mjd)97 mjd2timet(mirtime_mjd *mjd)
98 {
99 time_t t;
100
101 /* construct a POSIX timestamp again */
102 t = mjd->mjd;
103 t -= 40587;
104 t *= 86400;
105 t += (mjd->sec > 86399 ? 86399 : mjd->sec);
106
107 /* add leap seconds between epoch and t */
108 t = posix2timet(t);
109
110 /* if mjd was a leap second, account for it */
111 if (mjd->sec > 86399)
112 ++t;
113
114 return (t);
115 }
116 #endif
117
118 #ifdef L_mjd_explode
119 struct tm *
mjd_explode(struct tm * tm,const mirtime_mjd * mjd)120 mjd_explode(struct tm *tm, const mirtime_mjd *mjd)
121 {
122 int sec, day, yday, mon;
123 time_t year;
124
125 year = mjd->mjd;
126 sec = mjd->sec;
127
128 /* normalise */
129 while (sec < 0) {
130 --year;
131 sec += 86400;
132 }
133 while (sec > 86400) {
134 ++year;
135 sec -= 86400;
136 }
137
138 /* buffer leap second */
139 tm->tm_sec = (sec == 86400) ? 1 : 0;
140 sec -= tm->tm_sec;
141
142 /* calculate 400-year cycle (year) and offset in it (day) */
143 day = (int)(year % 146097);
144 year /= 146097;
145
146 /* add bias: 678881 = days between "convenient origin" and MJD 0 */
147 /* convenient origin is Wed(3) 1 March 0(fictional)/-1(real) */
148 day += 678881;
149 /* recalculate offset in cycle (Gregorian Period) */
150 year += day / 146097;
151 day %= 146097;
152
153 /* days in 400 years are cyclic, they have 20871 weeks */
154 tm->tm_wday = (day + 3) % 7;
155
156 /* calculate year from period, taking leap years into account */
157 year *= 4;
158 /* a long (Julian) century is at the end of each Gregorian Period */
159 if (__predict_false(day == 146096)) {
160 year += 3;
161 day = 36524;
162 } else {
163 year += day / 36524;
164 day %= 36524;
165 }
166 year *= 25;
167 year += day / 1461;
168 day %= 1461;
169 year *= 4;
170
171 /* March to December, or January/February? */
172 yday = (day < 306) ? 1 : 0;
173 /* a leap year is at the end of each olympiad */
174 if (__predict_false(day == 1460)) {
175 year += 3;
176 day = 365;
177 } else {
178 year += day / 365;
179 day %= 365;
180 }
181 yday += day;
182
183 /* count days and months from 1st March using fixed-point */
184 day *= 10;
185 mon = (day + 5) / 306;
186 day = (day + 5) % 306;
187 day /= 10;
188 /* adjust for Jan/Feb offset */
189 if (__predict_false(mon >= 10)) {
190 mon -= 10;
191 yday -= 306;
192 ++year;
193 } else {
194 mon += 2;
195 yday += 31 + 28;
196 }
197
198 /* adjust for year 0(fictional) which did not exist */
199 if (__predict_false(year < 1))
200 --year;
201
202 /* fill in the values still missing */
203 tm->tm_sec += sec % 60;
204 sec /= 60;
205 tm->tm_min = sec % 60;
206 tm->tm_hour = sec / 60;
207 tm->tm_mday = day + 1;
208 tm->tm_mon = mon;
209 tm->tm_year = year - 1900;
210 tm->tm_yday = yday;
211 /* hardcode these */
212 tm->tm_isdst = 0;
213 tm->tm_gmtoff = 0;
214 tm->tm_zone = (void *)0L;
215 /* return dst */
216 return (tm);
217 }
218 #endif
219
220 #ifdef L_mjd_implode
221 mirtime_mjd *
mjd_implode(mirtime_mjd * mjd,const struct tm * tm)222 mjd_implode(mirtime_mjd *mjd, const struct tm *tm)
223 {
224 time_t day;
225 int x, y;
226
227 /* get the seconds out first */
228 mjd->sec = tm->tm_sec + 60 * tm->tm_min + 3600 * tm->tm_hour -
229 tm->tm_gmtoff;
230
231 /* pull the year; adjust for year 0 not existing */
232 if (__predict_false((day = tm->tm_year + 1900LL) < 0))
233 ++day;
234
235 /* split year into y(ear in Gregorian Period) and convenient day */
236 y = day % 400;
237 day /= 400;
238 day *= 146097;
239 day -= 678882;
240 /* add day of month */
241 day += tm->tm_mday;
242
243 /* normalise seconds into days */
244 while (__predict_false(mjd->sec < 0)) {
245 --day;
246 mjd->sec += 86400;
247 }
248 while (__predict_false(mjd->sec > 86400)) {
249 ++day;
250 mjd->sec -= 86400;
251 }
252
253 /* assign x the month and normalise into years */
254 x = tm->tm_mon;
255 while (__predict_false(x < 0)) {
256 --y;
257 x += 12;
258 }
259 y += x / 12;
260 x %= 12;
261
262 /* calculate x as month since march, adjust year for it */
263 if (__predict_false(x < 2)) {
264 x += 10;
265 --y;
266 } else
267 x -= 2;
268
269 /* add length of month since march by fixed-point arithmetic */
270 day += (306 * x + 5) / 10;
271
272 /* normalise 400-year cycles again */
273 while (__predict_false(y < 0)) {
274 day -= 146097;
275 y += 400;
276 }
277 day += 146097 * (y / 400);
278 y %= 400;
279
280 /* add by year, 4 years, 100 years, 400 years */
281 day += 365 * (y % 4);
282 y /= 4;
283 day += 1461 * (y % 25);
284 day += 36524 * (y / 25);
285
286 mjd->mjd = day;
287 /* return dst */
288 return (mjd);
289 }
290 #endif
291
292 #ifdef L_mirtime_isleap
293 int
mirtime_isleap(time_t t)294 mirtime_isleap(time_t t)
295 {
296 time_t lt;
297 const time_t *lp;
298
299 lp = mirtime_getleaps();
300 while (__predict_true((lt = *lp))) {
301 if (__predict_false(t < lt))
302 return (0);
303 else if (__predict_false(t == lt))
304 return (1);
305 else
306 ++lp;
307 }
308 return (0);
309 }
310 #endif
311
312 #ifdef L_mirtime_macrobodies
313 #undef timet2tm
314 #undef tm2timet
315 struct tm *
timet2tm(struct tm * dst,time_t src)316 timet2tm(struct tm *dst, time_t src)
317 {
318 mirtime_mjd timet2tm_storage;
319
320 return (mjd_explode(dst, timet2mjd(&timet2tm_storage, src)));
321 }
322
323 time_t
tm2timet(struct tm * src)324 tm2timet(struct tm *src)
325 {
326 mirtime_mjd tm2timet_storage;
327
328 return (mjd2timet(mjd_implode(&tm2timet_storage, src)));
329 }
330 #endif
331