LCOV - code coverage report
Current view: top level - lib/wallclock - time_to_tm.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 59 59 100.0 %
Date: 2021-11-24 03:28:48 Functions: 3 3 100.0 %

          Line data    Source code
       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"
      14             : #include "lib/wallclock/time_to_tm.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       11874 : correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
      37             :            struct tm *r, char **err_out)
      38             : {
      39       11874 :   const char *outcome;
      40             : 
      41       11874 :   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       11870 :     if (r->tm_year > 8099) {
      45           2 :       r->tm_year = 8099;
      46           2 :       r->tm_mon = 11;
      47           2 :       r->tm_mday = 31;
      48           2 :       r->tm_yday = 364;
      49           2 :       r->tm_wday = 6;
      50           2 :       r->tm_hour = 23;
      51           2 :       r->tm_min = 59;
      52           2 :       r->tm_sec = 59;
      53       11868 :     } else if (r->tm_year < (1-1900)) {
      54           2 :       r->tm_year = (1-1900);
      55           2 :       r->tm_mon = 0;
      56           2 :       r->tm_mday = 1;
      57           2 :       r->tm_yday = 0;
      58           2 :       r->tm_wday = 0;
      59           2 :       r->tm_hour = 0;
      60           2 :       r->tm_min = 0;
      61           2 :       r->tm_sec = 0;
      62             :     }
      63       11870 :     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           4 :   if (timep) {
      70           4 :     if (*timep < 0) {
      71           2 :       r = resultbuf;
      72           2 :       r->tm_year = 70; /* 1970 CE */
      73           2 :       r->tm_mon = 0;
      74           2 :       r->tm_mday = 1;
      75           2 :       r->tm_yday = 0;
      76           2 :       r->tm_wday = 0;
      77           2 :       r->tm_hour = 0;
      78           2 :       r->tm_min = 0 ;
      79           2 :       r->tm_sec = 0;
      80           2 :       outcome = "Rounding up to 1970";
      81           2 :       goto done;
      82           2 :     } 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           2 :       r = resultbuf;
      86           2 :       r->tm_year = 137; /* 2037 CE */
      87           2 :       r->tm_mon = 11;
      88           2 :       r->tm_mday = 31;
      89           2 :       r->tm_yday = 364;
      90           2 :       r->tm_wday = 6;
      91           2 :       r->tm_hour = 23;
      92           2 :       r->tm_min = 59;
      93           2 :       r->tm_sec = 59;
      94           2 :       outcome = "Rounding down to 2037";
      95           2 :       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           4 :  done:
     107           4 :   if (err_out) {
     108           6 :     tor_asprintf(err_out, "%s(%"PRId64") failed with error %s: %s",
     109             :                  islocal?"localtime":"gmtime",
     110             :                  timep?((int64_t)*timep):0,
     111           4 :                  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       10135 : tor_localtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
     126             : {
     127       10135 :   struct tm *r;
     128       10135 :   r = localtime_r(timep, result);
     129       10135 :   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);
     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);
     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        1739 : tor_gmtime_r_msg(const time_t *timep, struct tm *result, char **err_out)
     169             : {
     170        1739 :   struct tm *r;
     171        1739 :   r = gmtime_r(timep, result);
     172        1739 :   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);
     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);
     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             : /**@}*/

Generated by: LCOV version 1.14