#include "orconfig.h"
#include "lib/cc/torint.h"
#include "lib/cc/compat_compiler.h"
#include "lib/wallclock/time_to_tm.h"
#include "lib/string/printf.h"
#include "lib/err/torerr.h"
#include <errno.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#if !defined(_WIN32)
#define TIME_FNS_NEED_LOCKS
#endif
static struct tm *
correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
struct tm *r, char **err_out)
{
const char *outcome;
if (PREDICT_LIKELY(r)) {
if (r->tm_year > 8099) {
r->tm_year = 8099;
r->tm_mon = 11;
r->tm_mday = 31;
r->tm_yday = 364;
r->tm_wday = 6;
r->tm_hour = 23;
r->tm_min = 59;
r->tm_sec = 59;
} else if (r->tm_year < (1-1900)) {
r->tm_year = (1-1900);
r->tm_mon = 0;
r->tm_mday = 1;
r->tm_yday = 0;
r->tm_wday = 0;
r->tm_hour = 0;
r->tm_min = 0;
r->tm_sec = 0;
}
return r;
}
if (timep) {
if (*timep < 0) {
r = resultbuf;
r->tm_year = 70;
r->tm_mon = 0;
r->tm_mday = 1;
r->tm_yday = 0;
r->tm_wday = 0;
r->tm_hour = 0;
r->tm_min = 0 ;
r->tm_sec = 0;
outcome = "Rounding up to 1970";
goto done;
} else if (*timep >= INT32_MAX) {
r = resultbuf;
r->tm_year = 137;
r->tm_mon = 11;
r->tm_mday = 31;
r->tm_yday = 364;
r->tm_wday = 6;
r->tm_hour = 23;
r->tm_min = 59;
r->tm_sec = 59;
outcome = "Rounding down to 2037";
goto done;
}
}
r = resultbuf;
memset(resultbuf, 0, sizeof(struct tm));
outcome="can't recover";
done:
if (err_out) {
tor_asprintf(err_out, "%s(%"PRId64") failed with error %s: %s",
islocal?"localtime":"gmtime",
timep?((int64_t)*timep):0,
strerror(errno),
outcome);
}
return r;
}
#ifdef HAVE_LOCALTIME_R
struct tm *
tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
{
struct tm *r;
r = localtime_r(timep, result);
return correct_tm(1, timep, result, r, err_out);
}
#elif defined(TIME_FNS_NEED_LOCKS)
struct tm *
tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
{
struct tm *r;
static tor_mutex_t *m=NULL;
if (!m) { m=tor_mutex_new(); }
raw_assert(result);
tor_mutex_acquire(m);
r = localtime(timep);
if (r)
memcpy(result, r, sizeof(struct tm));
tor_mutex_release(m);
return correct_tm(1, timep, result, r, err_out);
}
#else
struct tm *
tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
{
struct tm *r;
raw_assert(result);
r = localtime(timep);
if (r)
memcpy(result, r, sizeof(struct tm));
return correct_tm(1, timep, result, r, err_out);
}
#endif
#ifdef HAVE_GMTIME_R
struct tm *
tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
{
struct tm *r;
r = gmtime_r(timep, result);
return correct_tm(0, timep, result, r, err_out);
}
#elif defined(TIME_FNS_NEED_LOCKS)
struct tm *
tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
{
struct tm *r;
static tor_mutex_t *m=NULL;
if (!m) { m=tor_mutex_new(); }
raw_assert(result);
tor_mutex_acquire(m);
r = gmtime(timep);
if (r)
memcpy(result, r, sizeof(struct tm));
tor_mutex_release(m);
return correct_tm(0, timep, result, r, err_out);
}
#else
struct tm *
tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
{
struct tm *r;
raw_assert(result);
r = gmtime(timep);
if (r)
memcpy(result, r, sizeof(struct tm));
return correct_tm(0, timep, result, r, err_out);
}
#endif