/* pivotal_gmtime_r - a replacement for gmtime/localtime/mktime that works around the 2038 bug on 32-bit systems. (Version 3) Copyright (C) 2005 Paul Sheer Redistribution and use in source form, with or without modification, is permitted provided that the above copyright notice, this list of conditions, the following disclaimer, and the following char array are retained. Redistribution and use in binary form must reproduce an acknowledgment: 'With software provided by http://2038bug.com/' in the documentation and/or other materials provided with the distribution, and wherever such acknowledgments are usually accessible in Your program. This software is provided "AS IS" and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THIS SOFTWARE IS WITH YOU. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the copyright owners be liable for any direct, indirect, special, incidental, or consequential damages of any character arising as a result of the use of this software including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to liability for death or personal injury resulting from copyright owners' negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You. */ const char pivotal_gmtime_r_stamp[] = "pivotal_gmtime_r. Copyright (C) 2005 Paul Sheer. Terms and " "conditions apply. Visit http://2038bug.com/ for more info."; /* On 32-bit machines, with 'now' passed as NULL, pivotal_gmtime_r() gives the same result as gmtime() (i.e. gmtime() of the GNU C library 2.2) for all values of time_t positive and negative. See the gmtime() man page for more info. It is intended that you pass 'now' as the current time (as previously retrieved with a call such as time(&now);). In this case, pivotal_gmtime_r() returns the correct broken down time in the range of now - 2147483648 seconds through to now + 2147483647 seconds For example, on 10-Jan-2008, pivotal_gmtime_r() will return the correct broken down time format from 23-Dec-1939 through 29-Jan-2076. For all values of 'now' before Jan-23-2005 and after Jan-19-2038, pivotal_gmtime_r() will return the correct broken down time format from exactly 01-Jan-1970 through to 07-Feb-2106. In other words, if, for example, pivotal_gmtime_r() is used in a program that needs to convert time values of 25 years into the future and 68 years in the past, the program will operate as expected until the year 2106-25=2081. This will be true even on 32-bit systems. Note that "Jan-23-2005" is the date of the authoring of this code. Programmers who have available to them 64-bit time values as a 'long long' type can use gmtime64_r() instead, which correctly converts the time even on 32-bit systems. Whether you have 64-bit time values will depend on the operating system. Both functions are 64-bit clean and should work as expected on 64-bit systems. They have not yet been tested on 64-bit systems however. The localtime() equivalent functions do both a POSIX localtime_r() and gmtime_r() and work out the time zone offset from their difference. This is inefficient but gives the correct timezone offset and daylight savings time adjustments. The function prototypes are: long long pivot_time_t (const time_t * now, long long *t); long long mktime64 (struct tm *t); struct tm *localtime64_r (const long long *t, struct tm *p); struct tm *pivotal_localtime_r (const time_t * now, const time_t * t, struct tm *p); struct tm *gmtime64_r (const long long *t, struct tm *p); struct tm *pivotal_gmtime_r (const time_t * now, const time_t * t, struct tm *p); pivot_time_t() takes a 64-bit time that may have had its top 32-bits set to zero, and adjusts it so that it is in the range explained above. You can use pivot_time_t() to convert any time that may be incorrect. pivot_time_t() returns its argument unchanged if either now is NULL or sizeof(time_t) is not 4. mktime64() is a 64-bit equivalent of mktime(). localtime64_r() is a 64-bit equivalent of localtime_r(). pivotal_localtime_r() is 32-bit equivalent of localtime_r() with pivoting. gmtime64_r() is a 64-bit equivalent of gmtime_r(). pivotal_gmtime_r() is a 32-bit equivalent of gmtime_r() with pivoting. Note that none of these functions handle leap seconds. RATIONALE: The purpose of pivotal_gmtime_r() is as a replacement for the functions gmtime(), localtime() and their corresponding reentrant versions gmtime_r() and localtime_r(). pivotal_gmtime_r() is meant for 32-bit systems that must still correctly convert 32-bit time into broken down time format through the year 2038. Since most programs tend to operate within a range of time no more than 68 years in the future or the past, it is possible to determine the correct interpretation of a 32-bit time value in spite of the wrap that occurs in the year 2038. Many databases are likely to store time in 32-bit format and not be easily upgradeable to 64-bit. By using pivot_time_t(), these time values can be correctly used. Changes: 06-Feb-2005 v3.0: Some optimizations. mktime() no-longer zeros tm struct. */ #include <stdlib.h> #include <stdio.h> #include <time.h> static const int days[4][13] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}, }; #define LEAP_CHECK(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0) #define WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a)) long long pivot_time_t (const time_t * now, long long *_t) { long long t; t = *_t; if (now && sizeof (time_t) == 4) { time_t _now; _now = *now; if (_now < 1106500000 /* Jan 23 2005 - date of writing */ ) _now = 2147483647; if ((long long) t + ((long long) 1 << 31) < (long long) _now) t += (long long) 1 << 32; } return t; } static struct tm *_gmtime64_r (const time_t * now, long long *_t, struct tm *p) { int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday, v_tm_tday; int leap; long long t; long m; t = pivot_time_t (now, _t); v_tm_sec = ((long long) t % (long long) 60); t /= 60; v_tm_min = ((long long) t % (long long) 60); t /= 60; v_tm_hour = ((long long) t % (long long) 24); t /= 24; v_tm_tday = t; WRAP (v_tm_sec, v_tm_min, 60); WRAP (v_tm_min, v_tm_hour, 60); WRAP (v_tm_hour, v_tm_tday, 24); if ((v_tm_wday = (v_tm_tday + 4) % 7) < 0) v_tm_wday += 7; m = (long) v_tm_tday; if (m >= 0) { p->tm_year = 70; leap = LEAP_CHECK (p->tm_year); while (m >= (long) days[leap + 2][12]) { m -= (long) days[leap + 2][12]; p->tm_year++; leap = LEAP_CHECK (p->tm_year); } v_tm_mon = 0; while (m >= (long) days[leap][v_tm_mon]) { m -= (long) days[leap][v_tm_mon]; v_tm_mon++; } } else { p->tm_year = 69; leap = LEAP_CHECK (p->tm_year); while (m < (long) -days[leap + 2][12]) { m += (long) days[leap + 2][12]; p->tm_year--; leap = LEAP_CHECK (p->tm_year); } v_tm_mon = 11; while (m < (long) -days[leap][v_tm_mon]) { m += (long) days[leap][v_tm_mon]; v_tm_mon--; } m += (long) days[leap][v_tm_mon]; } p->tm_mday = (int) m + 1; p->tm_yday = days[leap + 2][v_tm_mon] + m; p->tm_sec = v_tm_sec, p->tm_min = v_tm_min, p->tm_hour = v_tm_hour, p->tm_mon = v_tm_mon, p->tm_wday = v_tm_wday; return p; } struct tm *gmtime64_r (const long long *_t, struct tm *p) { long long t; t = *_t; return _gmtime64_r (NULL, &t, p); } struct tm *pivotal_gmtime_r (const time_t * now, const time_t * _t, struct tm *p) { long long t; t = *_t; return _gmtime64_r (now, &t, p); } long long mktime64 (struct tm *t) { int i, y; long day = 0; long long r; if (t->tm_year < 70) { y = 69; do { day -= 365 + LEAP_CHECK (y); y--; } while (y >= t->tm_year); } else { y = 70; while (y < t->tm_year) { day += 365 + LEAP_CHECK (y); y++; } } for (i = 0; i < t->tm_mon; i++) day += days[LEAP_CHECK (t->tm_year)][i]; day += t->tm_mday - 1; t->tm_wday = (int) ((day + 4) % 7); r = (long long) day *86400; r += t->tm_hour * 3600; r += t->tm_min * 60; r += t->tm_sec; return r; } static struct tm *THIS_FUNCTION_DOES_NOT_WORK_FOR_DAYLIGHT_SAVINGS_localtime64_r (const time_t * now, long long *_t, struct tm *p) { long long tl; time_t t; struct tm tm, tm_localtime, tm_gmtime; _gmtime64_r (now, _t, &tm); if (tm.tm_year > (2037 - 1900)) tm.tm_year = 2037 - 1900; t = mktime64 (&tm); localtime_r (&t, &tm_localtime); gmtime_r (&t, &tm_gmtime); tl = *_t; tl += (mktime64 (&tm_localtime) - mktime64 (&tm_gmtime)); _gmtime64_r (now, &tl, p); p->tm_isdst = tm_localtime.tm_isdst; return p; } struct tm *pivotal_localtime_r (const time_t * now, const time_t * _t, struct tm *p) { long long tl; tl = *_t; return _localtime64_r (now, &tl, p); } struct tm *localtime64_r (const long long *_t, struct tm *p) { long long tl; tl = *_t; return _localtime64_r (NULL, &tl, p); }