3 Copyright (c) 2007-2008 Michael G Schwern
5 This software originally derived from Paul Sheer's pivotal_gmtime_r.c.
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:
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
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
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.
36 localtime64_r() is a 64-bit equivalent of localtime_r().
38 gmtime64_r() is a 64-bit equivalent of gmtime_r().
42 #include "localtime64.h"
44 static 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},
49 static 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},
54 static const int length_of_year[2] = { 365, 366 };
56 /* Number of days in a 400 year Gregorian cycle */
57 static const int years_in_gregorian_cycle = 400;
58 static const int days_in_gregorian_cycle = (365 * 400) + 100 - 4 + 1;
60 /* 28 year calendar cycle between 2010 and 2037 */
61 static 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
71 static const int dow_year_start[28] = {
72 5, 0, 1, 2, /* 2016 - 2019 */
77 2, 4, 5, 6, /* 2036, 2037, 2010, 2011 */
78 0, 2, 3, 4 /* 2012, 2013, 2014, 2015 */
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.
85 /* Number of days since epoch on Jan 1st, 2008 GMT */
86 #define CHEAT_DAYS (1199145600 / 24 / 60 / 60)
87 #define CHEAT_YEARS 108
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))
92 #define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \
93 USE_SYSTEM_LOCALTIME && \
94 (a) <= SYSTEM_LOCALTIME_MAX && \
95 (a) >= SYSTEM_LOCALTIME_MIN \
97 #define SHOULD_USE_SYSTEM_GMTIME(a) ( \
98 USE_SYSTEM_GMTIME && \
99 (a) <= SYSTEM_GMTIME_MAX && \
100 (a) >= SYSTEM_GMTIME_MIN \
104 int _is_exception_century(Int64 year)
106 int is_exception = ((year % 100 == 0) && !(year % 400 == 0));
107 /* printf("is_exception_century: %s\n", is_exception ? "yes" : "no"); */
109 return(is_exception);
113 /* timegm() is a GNU extension, so emulate it here if we need it */
115 # define TIMEGM(n) timegm(n);
117 # define TIMEGM(n) _my_timegm(n);
120 time_t _my_timegm(struct tm *date) {
126 if( date->tm_year > 70 ) {
128 while( year < date->tm_year ) {
129 days += length_of_year[IS_LEAP(year)];
133 else if ( date->tm_year < 70 ) {
136 days -= length_of_year[IS_LEAP(year)];
138 } while( year >= date->tm_year );
141 days += julian_days_by_month[IS_LEAP(date->tm_year)][date->tm_mon];
142 days += date->tm_mday - 1;
144 seconds += date->tm_hour * 60 * 60;
145 seconds += date->tm_min * 60;
146 seconds += date->tm_sec;
148 time = (time_t)(days * 60 * 60 * 24) + seconds;
154 int _check_tm(struct tm *tm)
156 /* Don't forget leap seconds */
157 assert(tm->tm_sec >= 0);
158 assert(tm->tm_sec <= 61);
160 assert(tm->tm_min >= 0);
161 assert(tm->tm_min <= 59);
163 assert(tm->tm_hour >= 0);
164 assert(tm->tm_hour <= 23);
166 assert(tm->tm_mday >= 1);
167 assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]);
169 assert(tm->tm_mon >= 0);
170 assert(tm->tm_mon <= 11);
172 assert(tm->tm_wday >= 0);
173 assert(tm->tm_wday <= 6);
175 assert(tm->tm_yday >= 0);
176 assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]);
178 #ifdef HAS_TM_TM_GMTOFF
179 assert(tm->tm_gmtoff >= -24 * 60 * 60);
180 assert(tm->tm_gmtoff <= 24 * 60 * 60);
187 /* The exceptional centuries without leap years cause the cycle to
190 int _cycle_offset(Int64 year)
192 const Int64 start_year = 2000;
193 Int64 year_diff = year - start_year - 1;
194 Int64 exceptions = year_diff / 100;
195 exceptions -= year_diff / 400;
197 assert( year >= 2001 );
199 /* printf("year: %d, exceptions: %d\n", year, exceptions); */
201 return exceptions * 16;
204 /* For a given year after 2038, pick the latest possible matching
205 year in the 28 year calendar cycle.
207 #define SOLAR_CYCLE_LENGTH 28
208 int _safe_year(Int64 year)
211 Int64 year_cycle = year + _cycle_offset(year);
213 /* Change non-leap xx00 years to an equivalent */
214 if( _is_exception_century(year) )
217 year_cycle %= SOLAR_CYCLE_LENGTH;
219 safe_year = safe_years[year_cycle];
221 assert(safe_year <= 2037 && safe_year >= 2010);
224 printf("year: %d, year_cycle: %d, safe_year: %d\n",
225 year, year_cycle, safe_year);
231 struct tm *gmtime64_r (const Time64_T *in_time, struct tm *p)
233 int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday;
237 Time64_T time = *in_time;
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);
244 assert(_check_tm(p));
248 #ifdef HAS_TM_TM_GMTOFF
253 #ifdef HAS_TM_TM_ZONE
257 v_tm_sec = time % 60;
259 v_tm_min = time % 60;
261 v_tm_hour = time % 24;
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)
271 if (m >= CHEAT_DAYS) {
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;
284 leap = IS_LEAP (year);
285 while (m >= (Time64_T) length_of_year[leap]) {
286 m -= (Time64_T) length_of_year[leap];
288 leap = IS_LEAP (year);
293 while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) {
294 m -= (Time64_T) days_in_month[leap][v_tm_mon];
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;
307 leap = IS_LEAP (year);
308 while (m < (Time64_T) -length_of_year[leap]) {
309 m += (Time64_T) length_of_year[leap];
311 leap = IS_LEAP (year);
316 while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) {
317 m += (Time64_T) days_in_month[leap][v_tm_mon];
320 m += (Time64_T) days_in_month[leap][v_tm_mon];
324 if( p->tm_year != year ) {
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;
336 assert(_check_tm(p));
342 struct tm *localtime64_r (const Time64_T *time, struct tm *local_tm)
349 /* Use the system localtime() if time_t is small enough */
350 if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) {
352 localtime_r(&safe_time, local_tm);
353 assert(_check_tm(local_tm));
357 gmtime64_r(time, &gm_tm);
358 orig_year = gm_tm.tm_year;
360 if (gm_tm.tm_year > (2037 - 1900))
361 gm_tm.tm_year = _safe_year(gm_tm.tm_year + 1900) - 1900;
363 safe_time = TIMEGM(&gm_tm);
364 localtime_r(&safe_time, local_tm);
366 local_tm->tm_year = orig_year;
367 month_diff = local_tm->tm_mon - gm_tm.tm_mon;
369 /* When localtime is Dec 31st previous year and
370 gmtime is Jan 1st next year.
372 if( month_diff == 11 ) {
376 /* When localtime is Jan 1st, next year and
377 gmtime is Dec 31st, previous year.
379 if( month_diff == -11 ) {
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.
389 if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 )
392 assert(_check_tm(local_tm));