1 /* 2 * sys-time.h - time.h wrapper for localtime_r() and gmtime_r() 3 * 4 * Copyright(c) 2015 Glenn Strauss gstrauss()gluelogic.com All rights reserved 5 * License: BSD 3-clause (same as lighttpd) 6 */ 7 #ifndef LI_SYS_TIME_H 8 #define LI_SYS_TIME_H 9 #include "first.h" 10 11 #ifdef HAVE_SYS_TIME_H 12 #include <sys/time.h>/* gettimeofday() */ 13 #endif 14 #include <time.h> /* time() localtime_r() gmtime_r() strftime() */ 15 16 /*(provide rudimentary localtime_r() and gmtime_r() for platforms lacking) 17 *(Note that 'result' preprocessor arg is repeated, so callers should avoid 18 * side-effects. Also note that there is still a race condition before the 19 * result of localtime()/gmtime() is copied. In any case, this exists here 20 * so that the rest of the code can use localtime_r() and gmtime_r() syntax. 21 * Platforms requiring thread-safety and lacking localtime_r() or gmtime_r() 22 * could turn these into subroutines which take a local mutex to protect the 23 * calls to localtime() or gmtime()) */ 24 #ifndef HAVE_LOCALTIME_R 25 #define localtime_r(timep,result) ((*(result) = *(localtime(timep))), (result)) 26 #endif 27 #ifndef HAVE_GMTIME_R 28 #define gmtime_r(timep,result) ((*(result) = *(gmtime(timep))), (result)) 29 #endif 30 31 #ifndef HAVE_TIMEGM 32 #ifdef _WIN32 33 #define timegm(tm) _mkgmtime(tm) 34 #else 35 __attribute_pure__ 36 static inline time_t 37 timegm (const struct tm * const tm); 38 static inline time_t 39 timegm (const struct tm * const tm) 40 { 41 int y = tm->tm_year + 1900; 42 int m = tm->tm_mon + 1; 43 int d = tm->tm_mday; 44 45 /* days_from_civil() http://howardhinnant.github.io/date_algorithms.html */ 46 y -= m <= 2; 47 int era = y / 400; 48 int yoe = y - era * 400; // [0, 399] 49 int doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; // [0, 365] 50 int doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096] 51 int days_since_1970 = era * 146097 + doe - 719468; 52 53 return 60*(60*(24L*days_since_1970+tm->tm_hour)+tm->tm_min)+tm->tm_sec; 54 } 55 #endif 56 #endif 57 58 59 /* non-standard functions created for lighttpd for Y2038 problem 60 * reference: https://en.wikipedia.org/wiki/Year_2038_problem 61 * 62 * lighttpd does not expect to deal with dates earlier than 1970, 63 * and can therefore treat dates earlier than 1970 as having overflowed 64 * 32-bit signed time_t, signifying dates after Tue, 19 Jan 2038 03:14:07 GMT 65 * 66 * 1954, 1982, 2010, 2038 begin on Thu and are two years offset from leap year. 67 * Attempt to adjust a timestamp > INT32_MAX back to a similar year, starting on 68 * same weekday, and being same distance from next leap year; use gmtime_r() 69 * or localtime_r(); and then adjust tm.tm_year back to the year of the original 70 * timestamp. (Note that the calculations here are valid for the specific range 71 * of input timestamps mapped to 1970 and 2099. The year 2000 was a leap year, 72 * so crossing 2000 *does not* require special-casing here, but the year 2100 73 * *is not* a leap year in the Gregorian calendar, so this code is not valid 74 * after 28 Feb 2100. Since the max validity of these kludges could only be 75 * until 7 Feb 2106, this code has chosen not to special-case 2100-2106 and 76 * produces incorrect results after 28 Feb 2100.) 77 */ 78 #if HAS_TIME_BITS64 79 80 #define gmtime64_r(timep,result) gmtime_r((timep),(result)) 81 #define localtime64_r(timep,result) localtime_r((timep),(result)) 82 83 #else /* !HAS_TIME_BITS64 */ 84 85 #define gmtime64_r(timep,result) gmtime_y2038_kludge32(*(timep),(result)) 86 #define localtime64_r(timep,result) localtime_y2038_kludge32(*(timep),(result)) 87 88 static inline struct tm * 89 gmtime_y2038_kludge32 (unix_time64_t t, struct tm *result); 90 static inline struct tm * 91 gmtime_y2038_kludge32 (unix_time64_t t, struct tm *result) 92 { 93 if ((uint64_t)t <= INT32_MAX) { 94 time_t tt = (time_t)t; 95 return gmtime_r(&tt, result); 96 } 97 else { 98 /*(treat negative time as having overflowed 32-bit time_t)*/ 99 if (t < 0) 100 t = TIME64_CAST(t); 101 time_t tt = (time_t)(t - 2650838400LL); 102 if (gmtime_r(&tt, result)) { 103 result->tm_year += 84; 104 return result; 105 } 106 else if (t < 3914709247LL) { 107 /*(ok through Tue, 19 Jan 2094 03:14:07 GMT)*/ 108 tt = (time_t)(t - 1767225600LL); 109 if (gmtime_r(&tt, result)) { 110 result->tm_year += 56; 111 return result; 112 } 113 } 114 115 /*(choose to forever return gmtime_r() equivalent of 116 * Thu, 01 Jan 1970 00:00:00 GMT) 117 *(returning NULL not expected by lighttpd and might crash)*/ 118 tt = 0; 119 gmtime_r(&tt, result); 120 return result; 121 } 122 } 123 124 static inline struct tm * 125 localtime_y2038_kludge32 (unix_time64_t t, struct tm *result); 126 static inline struct tm * 127 localtime_y2038_kludge32 (unix_time64_t t, struct tm *result) 128 { 129 if ((uint64_t)t <= INT32_MAX) { 130 time_t tt = (time_t)t; 131 return localtime_r(&tt, result); 132 } 133 else { 134 /*(treat negative time as having overflowed 32-bit time_t)*/ 135 if (t < 0) 136 t = TIME64_CAST(t); 137 time_t tt = (time_t)(t - 2650838400LL); 138 if (localtime_r(&tt, result)) { 139 result->tm_year += 84; 140 return result; 141 } 142 else if (t < 3914709247LL) { 143 /*(ok through Tue, 19 Jan 2094 03:14:07 GMT)*/ 144 tt = (time_t)(t - 1767225600LL); 145 if (localtime_r(&tt, result)) { 146 result->tm_year += 56; 147 return result; 148 } 149 } 150 151 /*(choose to forever return localtime_r() equivalent of 152 * Thu, 01 Jan 1970 00:00:00 GMT) 153 *(returning NULL not expected by lighttpd and might crash)*/ 154 tt = 0; 155 localtime_r(&tt, result); 156 return result; 157 } 158 } 159 160 #endif /* !HAS_TIME_BITS64 */ 161 162 163 #endif 164