Merge branch 'tor-gitlab/mr/583' into maint-0.4.7
[tor.git] / src / lib / wallclock / time_to_tm.c
blob35f308861004fd1ad0f4a086850031469e2a60b1
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 */
6 /**
7 * \file time_to_tm.c
8 * \brief Convert to struct tm, portably.
9 **/
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"
18 #include <errno.h>
19 #include <time.h>
20 #include <string.h>
21 #include <stdlib.h>
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
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)
39 const char *outcome;
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;
63 return r;
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;
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);
114 return r;
117 /** @{ */
118 /** As localtime_r, but defined for platforms that don't have it:
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.
123 #ifdef HAVE_LOCALTIME_R
124 struct tm *
125 tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
127 struct tm *r;
128 r = localtime_r(timep, result);
129 return correct_tm(1, timep, result, r, err_out);
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)
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);
146 #else
147 struct tm *
148 tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
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);
157 #endif /* defined(HAVE_LOCALTIME_R) || ... */
158 /** @} */
160 /** @{ */
161 /** As gmtime_r, but defined for platforms that don't have it:
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.
166 #ifdef HAVE_GMTIME_R
167 struct tm *
168 tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
170 struct tm *r;
171 r = gmtime_r(timep, result);
172 return correct_tm(0, timep, result, r, err_out);
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)
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);
189 #else
190 struct tm *
191 tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
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);
200 #endif /* defined(HAVE_GMTIME_R) || ... */
201 /**@}*/