Tor  0.4.7.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"
13 #include "lib/cc/compat_compiler.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);
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
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);
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
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 /**@}*/
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
struct tm * tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
Definition: time_to_tm.c:176
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
Header for time_to_tm.c.
Headers for torerr.c.
Integer definitions used throughout Tor.