Update from y2038.
[p5sagit/p5-mst-13.2.git] / localtime64.c
CommitLineData
a272e669 1/*
2
3Copyright (c) 2007-2008 Michael G Schwern
4
5This software originally derived from Paul Sheer's pivotal_gmtime_r.c.
6
7The MIT License:
8
9Permission is hereby granted, free of charge, to any person obtaining a copy
10of this software and associated documentation files (the "Software"), to deal
11in the Software without restriction, including without limitation the rights
12to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13copies of the Software, and to permit persons to whom the Software is
14furnished to do so, subject to the following conditions:
15
16The above copyright notice and this permission notice shall be included in
17all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25THE SOFTWARE.
26
27*/
28
29/*
30
31Programmers who have available to them 64-bit time values as a 'long
32long' type can use localtime64_r() and gmtime64_r() which correctly
33converts the time even on 32-bit systems. Whether you have 64-bit time
34values will depend on the operating system.
35
36localtime64_r() is a 64-bit equivalent of localtime_r().
37
38gmtime64_r() is a 64-bit equivalent of gmtime_r().
39
40*/
41
af9b2bf5 42#include "localtime64.h"
43
a272e669 44static const int days_in_month[2][12] = {
45 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
46 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
47};
48
49static const int julian_days_by_month[2][12] = {
50 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
51 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
52};
53
54static const int length_of_year[2] = { 365, 366 };
55
56/* Number of days in a 400 year Gregorian cycle */
57static const int years_in_gregorian_cycle = 400;
58static const int days_in_gregorian_cycle = (365 * 400) + 100 - 4 + 1;
59
60/* 28 year calendar cycle between 2010 and 2037 */
61static const int safe_years[28] = {
62 2016, 2017, 2018, 2019,
63 2020, 2021, 2022, 2023,
64 2024, 2025, 2026, 2027,
65 2028, 2029, 2030, 2031,
66 2032, 2033, 2034, 2035,
67 2036, 2037, 2010, 2011,
68 2012, 2013, 2014, 2015
69};
70
71static const int dow_year_start[28] = {
72 5, 0, 1, 2, /* 2016 - 2019 */
73 3, 5, 6, 0,
74 1, 3, 4, 5,
75 6, 1, 2, 3,
76 4, 6, 0, 1,
77 2, 4, 5, 6, /* 2036, 2037, 2010, 2011 */
78 0, 2, 3, 4 /* 2012, 2013, 2014, 2015 */
79};
80
9af24521 81/* Let's assume people are going to be looking for dates in the future.
82 Let's provide some cheats so you can skip ahead.
83 This has a 4x speed boost when near 2008.
84*/
85/* Number of days since epoch on Jan 1st, 2008 GMT */
86#define CHEAT_DAYS (1199145600 / 24 / 60 / 60)
87#define CHEAT_YEARS 108
a272e669 88
89#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0)
90#define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a))
91
7bda3dfc 92#define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \
93 USE_SYSTEM_LOCALTIME && \
94 (a) <= SYSTEM_LOCALTIME_MAX && \
95 (a) >= SYSTEM_LOCALTIME_MIN \
96)
97#define SHOULD_USE_SYSTEM_GMTIME(a) ( \
98 USE_SYSTEM_GMTIME && \
99 (a) <= SYSTEM_GMTIME_MAX && \
100 (a) >= SYSTEM_GMTIME_MIN \
101)
a64acb40 102
103
9af24521 104int _is_exception_century(Int64 year)
a272e669 105{
106 int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
107 /* printf("is_exception_century: %s\n", is_exception ? "yes" : "no"); */
108
109 return(is_exception);
110}
111
9af24521 112
113/* timegm() is a GNU extension, so emulate it here if we need it */
114#ifdef HAS_TIMEGM
115# define TIMEGM(n) timegm(n);
116#else
117# define TIMEGM(n) _my_timegm(n);
a272e669 118#endif
119
9af24521 120time_t _my_timegm(struct tm *date) {
121 int days = 0;
122 int seconds = 0;
123 time_t time;
124 int year;
a272e669 125
9af24521 126 if( date->tm_year > 70 ) {
127 year = 70;
128 while( year < date->tm_year ) {
129 days += length_of_year[IS_LEAP(year)];
130 year++;
a272e669 131 }
132 }
9af24521 133 else if ( date->tm_year < 70 ) {
134 year = 69;
135 do {
136 days -= length_of_year[IS_LEAP(year)];
137 year--;
138 } while( year >= date->tm_year );
139 }
140
141 days += julian_days_by_month[IS_LEAP(date->tm_year)][date->tm_mon];
142 days += date->tm_mday - 1;
143
144 seconds += date->tm_hour * 60 * 60;
145 seconds += date->tm_min * 60;
146 seconds += date->tm_sec;
147
148 time = (time_t)(days * 60 * 60 * 24) + seconds;
149
150 return(time);
151}
152
153
af9b2bf5 154int _check_tm(struct tm *tm)
9af24521 155{
9af24521 156 /* Don't forget leap seconds */
af9b2bf5 157 assert(tm->tm_sec >= 0);
9af24521 158 assert(tm->tm_sec <= 61);
159
af9b2bf5 160 assert(tm->tm_min >= 0);
9af24521 161 assert(tm->tm_min <= 59);
162
163 assert(tm->tm_hour >= 0);
164 assert(tm->tm_hour <= 23);
165
166 assert(tm->tm_mday >= 1);
af9b2bf5 167 assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
9af24521 168
169 assert(tm->tm_mon >= 0);
170 assert(tm->tm_mon <= 11);
171
172 assert(tm->tm_wday >= 0);
173 assert(tm->tm_wday <= 6);
174
175 assert(tm->tm_yday >= 0);
af9b2bf5 176 assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
9af24521 177
178#ifdef HAS_TM_TM_GMTOFF
179 assert(tm->tm_gmtoff >= -24 * 60 * 60);
180 assert(tm->tm_gmtoff <= 24 * 60 * 60);
181#endif
af9b2bf5 182
183 return 1;
a272e669 184}
a64acb40 185
a272e669 186
187/* The exceptional centuries without leap years cause the cycle to
188 shift by 16
189*/
9af24521 190int _cycle_offset(Int64 year)
a272e669 191{
9af24521 192 const Int64 start_year = 2000;
193 Int64 year_diff = year - start_year - 1;
194 Int64 exceptions = year_diff / 100;
a272e669 195 exceptions -= year_diff / 400;
196
197 assert( year >= 2001 );
198
199 /* printf("year: %d, exceptions: %d\n", year, exceptions); */
200
201 return exceptions * 16;
202}
203
204/* For a given year after 2038, pick the latest possible matching
205 year in the 28 year calendar cycle.
206*/
207#define SOLAR_CYCLE_LENGTH 28
9af24521 208int _safe_year(Int64 year)
a272e669 209{
210 int safe_year;
9af24521 211 Int64 year_cycle = year + _cycle_offset(year);
a272e669 212
213 /* Change non-leap xx00 years to an equivalent */
214 if( _is_exception_century(year) )
215 year_cycle += 11;
216
217 year_cycle %= SOLAR_CYCLE_LENGTH;
218
219 safe_year = safe_years[year_cycle];
220
221 assert(safe_year <= 2037 && safe_year >= 2010);
222
223 /*
224 printf("year: %d, year_cycle: %d, safe_year: %d\n",
225 year, year_cycle, safe_year);
226 */
227
228 return safe_year;
229}
230
231struct tm *gmtime64_r (const Time64_T *in_time, struct tm *p)
232{
233 int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
9af24521 234 Int64 v_tm_tday;
a272e669 235 int leap;
9af24521 236 Int64 m;
a272e669 237 Time64_T time = *in_time;
9af24521 238 Int64 year = 70;
a272e669 239
a64acb40 240 /* Use the system gmtime() if time_t is small enough */
241 if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) {
242 time_t safe_time = *in_time;
243 localtime_r(&safe_time, p);
af9b2bf5 244 assert(_check_tm(p));
a64acb40 245 return p;
246 }
247
9af24521 248#ifdef HAS_TM_TM_GMTOFF
a272e669 249 p->tm_gmtoff = 0;
250#endif
251 p->tm_isdst = 0;
252
9af24521 253#ifdef HAS_TM_TM_ZONE
a272e669 254 p->tm_zone = "UTC";
255#endif
256
257 v_tm_sec = time % 60;
258 time /= 60;
259 v_tm_min = time % 60;
260 time /= 60;
261 v_tm_hour = time % 24;
262 time /= 24;
263 v_tm_tday = time;
264 WRAP (v_tm_sec, v_tm_min, 60);
265 WRAP (v_tm_min, v_tm_hour, 60);
266 WRAP (v_tm_hour, v_tm_tday, 24);
267 if ((v_tm_wday = (v_tm_tday + 4) % 7) < 0)
268 v_tm_wday += 7;
269 m = v_tm_tday;
a272e669 270
9af24521 271 if (m >= CHEAT_DAYS) {
272 year = CHEAT_YEARS;
273 m -= CHEAT_DAYS;
274 }
275
276 if (m >= 0) {
a272e669 277 /* Gregorian cycles, this is huge optimization for distant times */
278 while (m >= (Time64_T) days_in_gregorian_cycle) {
279 m -= (Time64_T) days_in_gregorian_cycle;
280 year += years_in_gregorian_cycle;
281 }
282
283 /* Years */
284 leap = IS_LEAP (year);
285 while (m >= (Time64_T) length_of_year[leap]) {
286 m -= (Time64_T) length_of_year[leap];
287 year++;
288 leap = IS_LEAP (year);
289 }
290
291 /* Months */
292 v_tm_mon = 0;
293 while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
294 m -= (Time64_T) days_in_month[leap][v_tm_mon];
295 v_tm_mon++;
296 }
297 } else {
9af24521 298 year--;
a272e669 299
300 /* Gregorian cycles */
301 while (m < (Time64_T) -days_in_gregorian_cycle) {
302 m += (Time64_T) days_in_gregorian_cycle;
303 year -= years_in_gregorian_cycle;
304 }
305
306 /* Years */
307 leap = IS_LEAP (year);
308 while (m < (Time64_T) -length_of_year[leap]) {
309 m += (Time64_T) length_of_year[leap];
310 year--;
311 leap = IS_LEAP (year);
312 }
313
314 /* Months */
315 v_tm_mon = 11;
316 while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
317 m += (Time64_T) days_in_month[leap][v_tm_mon];
318 v_tm_mon--;
319 }
320 m += (Time64_T) days_in_month[leap][v_tm_mon];
321 }
322
323 p->tm_year = year;
324 if( p->tm_year != year ) {
9af24521 325#ifdef EOVERFLOW
a272e669 326 errno = EOVERFLOW;
9af24521 327#endif
a272e669 328 return NULL;
329 }
330
331 p->tm_mday = (int) m + 1;
332 p->tm_yday = julian_days_by_month[leap][v_tm_mon] + m;
333 p->tm_sec = v_tm_sec, p->tm_min = v_tm_min, p->tm_hour = v_tm_hour,
334 p->tm_mon = v_tm_mon, p->tm_wday = v_tm_wday;
335
af9b2bf5 336 assert(_check_tm(p));
a272e669 337
338 return p;
339}
340
341
342struct tm *localtime64_r (const Time64_T *time, struct tm *local_tm)
343{
344 time_t safe_time;
345 struct tm gm_tm;
9af24521 346 Int64 orig_year;
a272e669 347 int month_diff;
348
a64acb40 349 /* Use the system localtime() if time_t is small enough */
350 if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) {
351 safe_time = *time;
352 localtime_r(&safe_time, local_tm);
af9b2bf5 353 assert(_check_tm(local_tm));
a64acb40 354 return local_tm;
355 }
356
a272e669 357 gmtime64_r(time, &gm_tm);
358 orig_year = gm_tm.tm_year;
359
360 if (gm_tm.tm_year > (2037 - 1900))
361 gm_tm.tm_year = _safe_year(gm_tm.tm_year + 1900) - 1900;
362
9af24521 363 safe_time = TIMEGM(&gm_tm);
a272e669 364 localtime_r(&safe_time, local_tm);
365
366 local_tm->tm_year = orig_year;
367 month_diff = local_tm->tm_mon - gm_tm.tm_mon;
368
369 /* When localtime is Dec 31st previous year and
370 gmtime is Jan 1st next year.
371 */
372 if( month_diff == 11 ) {
373 local_tm->tm_year--;
374 }
375
376 /* When localtime is Jan 1st, next year and
377 gmtime is Dec 31st, previous year.
378 */
379 if( month_diff == -11 ) {
380 local_tm->tm_year++;
381 }
382
383 /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st
384 in a non-leap xx00. There is one point in the cycle
385 we can't account for which the safe xx00 year is a leap
386 year. So we need to correct for Dec 31st comming out as
387 the 366th day of the year.
388 */
389 if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
390 local_tm->tm_yday--;
391
af9b2bf5 392 assert(_check_tm(local_tm));
a272e669 393
394 return local_tm;
395}