Tor 0.4.9.0-alpha-dev
time_to_tm.c
Go to the documentation of this file.
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"
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>. */
35static struct tm *
36correct_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
124struct tm *
125tor_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)
132struct tm *
133tor_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);
140 r = localtime(timep);
141 if (r)
142 memcpy(result, r, sizeof(struct tm));
144 return correct_tm(1, timep, result, r, err_out);
145}
146#else
147struct tm *
148tor_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
167struct tm *
168tor_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)
175struct tm *
176tor_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);
183 r = gmtime(timep);
184 if (r)
185 memcpy(result, r, sizeof(struct tm));
187 return correct_tm(0, timep, result, r, err_out);
188}
189#else
190struct tm *
191tor_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/**@}*/
Utility macros to handle different features and behavior in different compilers.
tor_mutex_t * tor_mutex_new(void)
Definition: compat_mutex.c:17
void tor_mutex_release(tor_mutex_t *m)
void tor_mutex_acquire(tor_mutex_t *m)
int tor_asprintf(char **strp, const char *fmt,...)
Definition: printf.c:75
Header for printf.c.
Definitions for timing-related constants.
struct tm * tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
Definition: time_to_tm.c:133
static struct tm * correct_tm(int islocal, const time_t *timep, struct tm *resultbuf, struct tm *r, char **err_out)
Definition: time_to_tm.c:36
struct tm * tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
Definition: time_to_tm.c:176
Header for time_to_tm.c.
Headers for torerr.c.
Integer definitions used throughout Tor.