time_to_tm.c (5635B)
1 /* Copyright (c) 2003-2004, Roger Dingledine 2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 3 * Copyright (c) 2007-2021, The Tor Project, Inc. */ 4 /* See LICENSE for licensing information */ 5 6 /** 7 * \file time_to_tm.c 8 * \brief Convert to struct tm, portably. 9 **/ 10 11 #include "orconfig.h" 12 #include "lib/cc/torint.h" 13 #include "lib/cc/compat_compiler.h" 14 #include "lib/wallclock/time_to_tm.h" 15 #include "lib/string/printf.h" 16 #include "lib/err/torerr.h" 17 18 #include <errno.h> 19 #include <time.h> 20 #include <string.h> 21 #include <stdlib.h> 22 23 #if !defined(_WIN32) 24 /** Defined iff we need to add locks when defining fake versions of reentrant 25 * versions of time-related functions. */ 26 #define TIME_FNS_NEED_LOCKS 27 #endif 28 29 /** Helper: Deal with confused or out-of-bounds values from localtime_r and 30 * friends. (On some platforms, they can give out-of-bounds values or can 31 * return NULL.) If <b>islocal</b>, this is a localtime result; otherwise 32 * it's from gmtime. The function returns <b>r</b>, when given <b>timep</b> 33 * as its input. If we need to store new results, store them in 34 * <b>resultbuf</b>. */ 35 static struct tm * 36 correct_tm(int islocal, const time_t *timep, struct tm *resultbuf, 37 struct tm *r, char **err_out) 38 { 39 const char *outcome; 40 41 if (PREDICT_LIKELY(r)) { 42 /* We can't strftime dates after 9999 CE, and we want to avoid dates 43 * before 1 CE (avoiding the year 0 issue and negative years). */ 44 if (r->tm_year > 8099) { 45 r->tm_year = 8099; 46 r->tm_mon = 11; 47 r->tm_mday = 31; 48 r->tm_yday = 364; 49 r->tm_wday = 6; 50 r->tm_hour = 23; 51 r->tm_min = 59; 52 r->tm_sec = 59; 53 } else if (r->tm_year < (1-1900)) { 54 r->tm_year = (1-1900); 55 r->tm_mon = 0; 56 r->tm_mday = 1; 57 r->tm_yday = 0; 58 r->tm_wday = 0; 59 r->tm_hour = 0; 60 r->tm_min = 0; 61 r->tm_sec = 0; 62 } 63 return r; 64 } 65 66 /* If we get here, gmtime or localtime returned NULL. It might have done 67 * this because of overrun or underrun, or it might have done it because of 68 * some other weird issue. */ 69 if (timep) { 70 if (*timep < 0) { 71 r = resultbuf; 72 r->tm_year = 70; /* 1970 CE */ 73 r->tm_mon = 0; 74 r->tm_mday = 1; 75 r->tm_yday = 0; 76 r->tm_wday = 0; 77 r->tm_hour = 0; 78 r->tm_min = 0 ; 79 r->tm_sec = 0; 80 outcome = "Rounding up to 1970"; 81 goto done; 82 } else if (*timep >= INT32_MAX) { 83 /* Rounding down to INT32_MAX isn't so great, but keep in mind that we 84 * only do it if gmtime/localtime tells us NULL. */ 85 r = resultbuf; 86 r->tm_year = 137; /* 2037 CE */ 87 r->tm_mon = 11; 88 r->tm_mday = 31; 89 r->tm_yday = 364; 90 r->tm_wday = 6; 91 r->tm_hour = 23; 92 r->tm_min = 59; 93 r->tm_sec = 59; 94 outcome = "Rounding down to 2037"; 95 goto done; 96 } 97 } 98 99 /* If we get here, then gmtime/localtime failed without getting an extreme 100 * value for *timep */ 101 /* LCOV_EXCL_START */ 102 r = resultbuf; 103 memset(resultbuf, 0, sizeof(struct tm)); 104 outcome="can't recover"; 105 /* LCOV_EXCL_STOP */ 106 done: 107 if (err_out) { 108 tor_asprintf(err_out, "%s(%"PRId64") failed with error %s: %s", 109 islocal?"localtime":"gmtime", 110 timep?((int64_t)*timep):0, 111 strerror(errno), 112 outcome); 113 } 114 return r; 115 } 116 117 /** @{ */ 118 /** As localtime_r, but defined for platforms that don't have it: 119 * 120 * Convert *<b>timep</b> to a struct tm in local time, and store the value in 121 * *<b>result</b>. Return the result on success, or NULL on failure. 122 */ 123 #ifdef HAVE_LOCALTIME_R 124 struct tm * 125 tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out) 126 { 127 struct tm *r; 128 r = localtime_r(timep, result); 129 return correct_tm(1, timep, result, r, err_out); 130 } 131 #elif defined(TIME_FNS_NEED_LOCKS) 132 struct tm * 133 tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out) 134 { 135 struct tm *r; 136 static tor_mutex_t *m=NULL; 137 if (!m) { m=tor_mutex_new(); } 138 raw_assert(result); 139 tor_mutex_acquire(m); 140 r = localtime(timep); 141 if (r) 142 memcpy(result, r, sizeof(struct tm)); 143 tor_mutex_release(m); 144 return correct_tm(1, timep, result, r, err_out); 145 } 146 #else 147 struct tm * 148 tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out) 149 { 150 struct tm *r; 151 raw_assert(result); 152 r = localtime(timep); 153 if (r) 154 memcpy(result, r, sizeof(struct tm)); 155 return correct_tm(1, timep, result, r, err_out); 156 } 157 #endif /* defined(HAVE_LOCALTIME_R) || ... */ 158 /** @} */ 159 160 /** @{ */ 161 /** As gmtime_r, but defined for platforms that don't have it: 162 * 163 * Convert *<b>timep</b> to a struct tm in UTC, and store the value in 164 * *<b>result</b>. Return the result on success, or NULL on failure. 165 */ 166 #ifdef HAVE_GMTIME_R 167 struct tm * 168 tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out) 169 { 170 struct tm *r; 171 r = gmtime_r(timep, result); 172 return correct_tm(0, timep, result, r, err_out); 173 } 174 #elif defined(TIME_FNS_NEED_LOCKS) 175 struct tm * 176 tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out) 177 { 178 struct tm *r; 179 static tor_mutex_t *m=NULL; 180 if (!m) { m=tor_mutex_new(); } 181 raw_assert(result); 182 tor_mutex_acquire(m); 183 r = gmtime(timep); 184 if (r) 185 memcpy(result, r, sizeof(struct tm)); 186 tor_mutex_release(m); 187 return correct_tm(0, timep, result, r, err_out); 188 } 189 #else 190 struct tm * 191 tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out) 192 { 193 struct tm *r; 194 raw_assert(result); 195 r = gmtime(timep); 196 if (r) 197 memcpy(result, r, sizeof(struct tm)); 198 return correct_tm(0, timep, result, r, err_out); 199 } 200 #endif /* defined(HAVE_GMTIME_R) || ... */ 201 /**@}*/