1 /* Copyright (c) 2003-2004, Roger Dingledine
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2019, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
8 * \brief Convert to struct tm, portably.
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"
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
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>. */
36 correct_tm(int islocal
, const time_t *timep
, struct tm
*resultbuf
,
37 struct tm
*r
, char **err_out
)
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) {
53 } else if (r
->tm_year
< (1-1900)) {
54 r
->tm_year
= (1-1900);
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. */
72 r
->tm_year
= 70; /* 1970 CE */
80 outcome
= "Rounding up to 1970";
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. */
86 r
->tm_year
= 137; /* 2037 CE */
94 outcome
= "Rounding down to 2037";
99 /* If we get here, then gmtime/localtime failed without getting an extreme
100 * value for *timep */
101 /* LCOV_EXCL_START */
103 memset(resultbuf
, 0, sizeof(struct tm
));
104 outcome
="can't recover";
108 tor_asprintf(err_out
, "%s(%"PRId64
") failed with error %s: %s",
109 islocal
?"localtime":"gmtime",
110 timep
?((int64_t)*timep
):0,
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
125 tor_localtime_r_msg(const time_t *timep
, struct tm
*result
, char **err_out
)
128 r
= localtime_r(timep
, result
);
129 return correct_tm(1, timep
, result
, r
, err_out
);
131 #elif defined(TIME_FNS_NEED_LOCKS)
133 tor_localtime_r_msg(const time_t *timep
, struct tm
*result
, char **err_out
)
136 static tor_mutex_t
*m
=NULL
;
137 if (!m
) { m
=tor_mutex_new(); }
139 tor_mutex_acquire(m
);
140 r
= localtime(timep
);
142 memcpy(result
, r
, sizeof(struct tm
));
143 tor_mutex_release(m
);
144 return correct_tm(1, timep
, result
, r
, err_out
);
148 tor_localtime_r_msg(const time_t *timep
, struct tm
*result
, char **err_out
)
152 r
= localtime(timep
);
154 memcpy(result
, r
, sizeof(struct tm
));
155 return correct_tm(1, timep
, result
, r
, err_out
);
157 #endif /* defined(HAVE_LOCALTIME_R) || ... */
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.
168 tor_gmtime_r_msg(const time_t *timep
, struct tm
*result
, char **err_out
)
171 r
= gmtime_r(timep
, result
);
172 return correct_tm(0, timep
, result
, r
, err_out
);
174 #elif defined(TIME_FNS_NEED_LOCKS)
176 tor_gmtime_r_msg(const time_t *timep
, struct tm
*result
, char **err_out
)
179 static tor_mutex_t
*m
=NULL
;
180 if (!m
) { m
=tor_mutex_new(); }
182 tor_mutex_acquire(m
);
185 memcpy(result
, r
, sizeof(struct tm
));
186 tor_mutex_release(m
);
187 return correct_tm(0, timep
, result
, r
, err_out
);
191 tor_gmtime_r_msg(const time_t *timep
, struct tm
*result
, char **err_out
)
197 memcpy(result
, r
, sizeof(struct tm
));
198 return correct_tm(0, timep
, result
, r
, err_out
);
200 #endif /* defined(HAVE_GMTIME_R) || ... */