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