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

          Line data    Source code
       1             : /* Copyright (c) 2001, Matej Pfajfar.
       2             :  * Copyright (c) 2001-2004, Roger Dingledine.
       3             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       4             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       5             : /* See LICENSE for licensing information */
       6             : 
       7             : /**
       8             :  * \file time_fmt.c
       9             :  *
      10             :  * \brief Encode and decode time in various formats.
      11             :  *
      12             :  * This module is higher-level than the conversion functions in "wallclock",
      13             :  * and handles a larger variety of types.  It converts between different time
      14             :  * formats, and encodes and decodes them from strings.
      15             :  **/
      16             : 
      17             : #include "lib/encoding/time_fmt.h"
      18             : #include "lib/log/log.h"
      19             : #include "lib/log/escape.h"
      20             : #include "lib/log/util_bug.h"
      21             : #include "lib/malloc/malloc.h"
      22             : #include "lib/string/printf.h"
      23             : #include "lib/string/scanf.h"
      24             : #include "lib/wallclock/time_to_tm.h"
      25             : 
      26             : #include <string.h>
      27             : #include <time.h>
      28             : 
      29             : #ifdef HAVE_SYS_TIME_H
      30             : #include <sys/time.h>
      31             : #endif
      32             : 
      33             : #ifdef _WIN32
      34             : /* For struct timeval */
      35             : #include <winsock2.h>
      36             : #endif
      37             : 
      38             : /** As localtime_r, but defined for platforms that don't have it:
      39             :  *
      40             :  * Convert *<b>timep</b> to a struct tm in local time, and store the value in
      41             :  * *<b>result</b>.  Return the result on success, or NULL on failure.
      42             :  *
      43             :  * Treat malformatted inputs localtime outputs as a BUG.
      44             :  */
      45             : struct tm *
      46         236 : tor_localtime_r(const time_t *timep, struct tm *result)
      47             : {
      48         236 :   char *err = NULL;
      49         236 :   struct tm *r = tor_localtime_r_msg(timep, result, &err);
      50         236 :   if (err) {
      51           2 :     log_warn(LD_BUG, "%s", err);
      52           2 :     tor_free(err);
      53             :   }
      54         236 :   return r;
      55             : }
      56             : 
      57             : /** As gmtime_r, but defined for platforms that don't have it:
      58             :  *
      59             :  * Convert *<b>timep</b> to a struct tm in UTC, and store the value in
      60             :  * *<b>result</b>.  Return the result on success, or NULL on failure.
      61             :  *
      62             :  * Treat malformatted inputs or gmtime outputs as a BUG.
      63             :  */
      64             : struct tm *
      65        1739 : tor_gmtime_r(const time_t *timep, struct tm *result)
      66             : {
      67        1739 :   char *err = NULL;
      68        1739 :   struct tm *r = tor_gmtime_r_msg(timep, result, &err);
      69        1739 :   if (err) {
      70           2 :     log_warn(LD_BUG, "%s", err);
      71           2 :     tor_free(err);
      72             :   }
      73        1739 :   return r;
      74             : }
      75             : 
      76             : /** Yield true iff <b>y</b> is a leap-year. */
      77             : #define IS_LEAPYEAR(y) (!(y % 4) && ((y % 100) || !(y % 400)))
      78             : /** Helper: Return the number of leap-days between Jan 1, y1 and Jan 1, y2. */
      79             : static int
      80        9722 : n_leapdays(int year1, int year2)
      81             : {
      82        9722 :   --year1;
      83        9722 :   --year2;
      84        9722 :   return (year2/4 - year1/4) - (year2/100 - year1/100)
      85        9722 :     + (year2/400 - year1/400);
      86             : }
      87             : /** Number of days per month in non-leap year; used by tor_timegm and
      88             :  * parse_rfc1123_time. */
      89             : static const int days_per_month[] =
      90             :   { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
      91             : 
      92             : /** Compute a time_t given a struct tm.  The result is given in UTC, and
      93             :  * does not account for leap seconds.  Return 0 on success, -1 on failure.
      94             :  */
      95             : int
      96        9744 : tor_timegm(const struct tm *tm, time_t *time_out)
      97             : {
      98             :   /* This is a pretty ironclad timegm implementation, snarfed from Python2.2.
      99             :    * It's way more brute-force than fiddling with tzset().
     100             :    *
     101             :    * We use int64_t rather than time_t to avoid overflow on multiplication on
     102             :    * platforms with 32-bit time_t. Since year is clipped to INT32_MAX, and
     103             :    * since 365 * 24 * 60 * 60 is approximately 31 million, it's not possible
     104             :    * for INT32_MAX years to overflow int64_t when converted to seconds. */
     105        9744 :   int64_t year, days, hours, minutes, seconds;
     106        9744 :   int i, invalid_year, dpm;
     107             : 
     108             :   /* Initialize time_out to 0 for now, to avoid bad usage in case this function
     109             :      fails and the caller ignores the return value. */
     110        9744 :   tor_assert(time_out);
     111        9744 :   *time_out = 0;
     112             : 
     113             :   /* avoid int overflow on addition */
     114        9744 :   if (tm->tm_year < INT32_MAX-1900) {
     115        9742 :     year = tm->tm_year + 1900;
     116             :   } else {
     117             :     /* clamp year */
     118             :     year = INT32_MAX;
     119             :   }
     120        9744 :   invalid_year = (year < 1970 || tm->tm_year >= INT32_MAX-1900);
     121             : 
     122        9744 :   if (tm->tm_mon >= 0 && tm->tm_mon <= 11) {
     123        9742 :     dpm = days_per_month[tm->tm_mon];
     124        9742 :     if (tm->tm_mon == 1 && !invalid_year && IS_LEAPYEAR(tm->tm_year)) {
     125        1468 :       dpm = 29;
     126             :     }
     127             :   } else {
     128             :     /* invalid month - default to 0 days per month */
     129             :     dpm = 0;
     130             :   }
     131             : 
     132        9744 :   if (invalid_year ||
     133        9736 :       tm->tm_mon < 0 || tm->tm_mon > 11 ||
     134        9735 :       tm->tm_mday < 1 || tm->tm_mday > dpm ||
     135        9728 :       tm->tm_hour < 0 || tm->tm_hour > 23 ||
     136        9726 :       tm->tm_min < 0 || tm->tm_min > 59 ||
     137        9724 :       tm->tm_sec < 0 || tm->tm_sec > 60) {
     138          22 :     log_warn(LD_BUG, "Out-of-range argument to tor_timegm");
     139          22 :     return -1;
     140             :   }
     141        9722 :   days = 365 * (year-1970) + n_leapdays(1970,(int)year);
     142       85856 :   for (i = 0; i < tm->tm_mon; ++i)
     143       76134 :     days += days_per_month[i];
     144        9722 :   if (tm->tm_mon > 1 && IS_LEAPYEAR(year))
     145        4496 :     ++days;
     146        9722 :   days += tm->tm_mday - 1;
     147        9722 :   hours = days*24 + tm->tm_hour;
     148             : 
     149        9722 :   minutes = hours*60 + tm->tm_min;
     150        9722 :   seconds = minutes*60 + tm->tm_sec;
     151             :   /* Check that "seconds" will fit in a time_t. On platforms where time_t is
     152             :    * 32-bit, this check will fail for dates in and after 2038.
     153             :    *
     154             :    * We already know that "seconds" can't be negative because "year" >= 1970 */
     155             : #if SIZEOF_TIME_T < 8
     156             :   if (seconds < TIME_MIN || seconds > TIME_MAX) {
     157             :     log_warn(LD_BUG, "Result does not fit in tor_timegm");
     158             :     return -1;
     159             :   }
     160             : #endif /* SIZEOF_TIME_T < 8 */
     161        9722 :   *time_out = (time_t)seconds;
     162        9722 :   return 0;
     163             : }
     164             : 
     165             : /* strftime is locale-specific, so we need to replace those parts */
     166             : 
     167             : /** A c-locale array of 3-letter names of weekdays, starting with Sun. */
     168             : static const char *WEEKDAY_NAMES[] =
     169             :   { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
     170             : /** A c-locale array of 3-letter names of months, starting with Jan. */
     171             : static const char *MONTH_NAMES[] =
     172             :   { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
     173             :     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
     174             : 
     175             : /** Set <b>buf</b> to the RFC1123 encoding of the UTC value of <b>t</b>.
     176             :  * The buffer must be at least RFC1123_TIME_LEN+1 bytes long.
     177             :  *
     178             :  * (RFC1123 format is "Fri, 29 Sep 2006 15:54:20 GMT". Note the "GMT"
     179             :  * rather than "UTC".)
     180             :  */
     181             : void
     182          59 : format_rfc1123_time(char *buf, time_t t)
     183             : {
     184          59 :   struct tm tm;
     185             : 
     186          59 :   tor_gmtime_r(&t, &tm);
     187             : 
     188          59 :   strftime(buf, RFC1123_TIME_LEN+1, "___, %d ___ %Y %H:%M:%S GMT", &tm);
     189          59 :   tor_assert(tm.tm_wday >= 0);
     190          59 :   tor_assert(tm.tm_wday <= 6);
     191          59 :   memcpy(buf, WEEKDAY_NAMES[tm.tm_wday], 3);
     192          59 :   tor_assert(tm.tm_mon >= 0);
     193          59 :   tor_assert(tm.tm_mon <= 11);
     194          59 :   memcpy(buf+8, MONTH_NAMES[tm.tm_mon], 3);
     195          59 : }
     196             : 
     197             : /** Parse the (a subset of) the RFC1123 encoding of some time (in UTC) from
     198             :  * <b>buf</b>, and store the result in *<b>t</b>.
     199             :  *
     200             :  * Note that we only accept the subset generated by format_rfc1123_time above,
     201             :  * not the full range of formats suggested by RFC 1123.
     202             :  *
     203             :  * Return 0 on success, -1 on failure.
     204             : */
     205             : int
     206         176 : parse_rfc1123_time(const char *buf, time_t *t)
     207             : {
     208         176 :   struct tm tm;
     209         176 :   char month[4];
     210         176 :   char weekday[4];
     211         176 :   int i, m, invalid_year;
     212         176 :   unsigned tm_mday, tm_year, tm_hour, tm_min, tm_sec;
     213         176 :   unsigned dpm;
     214             : 
     215         176 :   if (strlen(buf) != RFC1123_TIME_LEN)
     216             :     return -1;
     217         175 :   memset(&tm, 0, sizeof(tm));
     218         175 :   if (tor_sscanf(buf, "%3s, %2u %3s %u %2u:%2u:%2u GMT", weekday,
     219             :              &tm_mday, month, &tm_year, &tm_hour,
     220             :              &tm_min, &tm_sec) < 7) {
     221           1 :     char *esc = esc_for_log(buf);
     222           1 :     log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc);
     223           1 :     tor_free(esc);
     224           1 :     return -1;
     225             :   }
     226             : 
     227        1265 :   m = -1;
     228        1265 :   for (i = 0; i < 12; ++i) {
     229        1264 :     if (!strcmp(month, MONTH_NAMES[i])) {
     230             :       m = i;
     231             :       break;
     232             :     }
     233             :   }
     234         174 :   if (m<0) {
     235           1 :     char *esc = esc_for_log(buf);
     236           1 :     log_warn(LD_GENERAL, "Got invalid RFC1123 time %s: No such month", esc);
     237           1 :     tor_free(esc);
     238           1 :     return -1;
     239             :   }
     240         173 :   tm.tm_mon = m;
     241             : 
     242         173 :   invalid_year = (tm_year >= INT32_MAX || tm_year < 1970);
     243         173 :   tor_assert(m >= 0 && m <= 11);
     244         173 :   dpm = days_per_month[m];
     245         173 :   if (m == 1 && !invalid_year && IS_LEAPYEAR(tm_year)) {
     246           1 :     dpm = 29;
     247             :   }
     248             : 
     249         173 :   if (invalid_year || tm_mday < 1 || tm_mday > dpm ||
     250         169 :       tm_hour > 23 || tm_min > 59 || tm_sec > 60) {
     251           8 :     char *esc = esc_for_log(buf);
     252           8 :     log_warn(LD_GENERAL, "Got invalid RFC1123 time %s", esc);
     253           8 :     tor_free(esc);
     254           8 :     return -1;
     255             :   }
     256         165 :   tm.tm_mday = (int)tm_mday;
     257         165 :   tm.tm_year = (int)tm_year;
     258         165 :   tm.tm_hour = (int)tm_hour;
     259         165 :   tm.tm_min = (int)tm_min;
     260         165 :   tm.tm_sec = (int)tm_sec;
     261             : 
     262         165 :   if (tm.tm_year < 1970) {
     263             :     /* LCOV_EXCL_START
     264             :      * XXXX I think this is dead code; we already checked for
     265             :      *      invalid_year above. */
     266             :     tor_assert_nonfatal_unreached();
     267             :     char *esc = esc_for_log(buf);
     268             :     log_warn(LD_GENERAL,
     269             :              "Got invalid RFC1123 time %s. (Before 1970)", esc);
     270             :     tor_free(esc);
     271             :     return -1;
     272             :     /* LCOV_EXCL_STOP */
     273             :   }
     274         165 :   tm.tm_year -= 1900;
     275             : 
     276         165 :   return tor_timegm(&tm, t);
     277             : }
     278             : 
     279             : /** Set <b>buf</b> to the ISO8601 encoding of the local value of <b>t</b>.
     280             :  * The buffer must be at least ISO_TIME_LEN+1 bytes long.
     281             :  *
     282             :  * (ISO8601 format is 2006-10-29 10:57:20)
     283             :  */
     284             : void
     285         221 : format_local_iso_time(char *buf, time_t t)
     286             : {
     287         221 :   struct tm tm;
     288         221 :   strftime(buf, ISO_TIME_LEN+1, "%Y-%m-%d %H:%M:%S", tor_localtime_r(&t, &tm));
     289         221 : }
     290             : 
     291             : /** Set <b>buf</b> to the ISO8601 encoding of the GMT value of <b>t</b>.
     292             :  * The buffer must be at least ISO_TIME_LEN+1 bytes long.
     293             :  */
     294             : void
     295        1380 : format_iso_time(char *buf, time_t t)
     296             : {
     297        1380 :   struct tm tm;
     298        1380 :   strftime(buf, ISO_TIME_LEN+1, "%Y-%m-%d %H:%M:%S", tor_gmtime_r(&t, &tm));
     299        1380 : }
     300             : 
     301             : /** As format_local_iso_time, but use the yyyy-mm-ddThh:mm:ss format to avoid
     302             :  * embedding an internal space. */
     303             : void
     304           2 : format_local_iso_time_nospace(char *buf, time_t t)
     305             : {
     306           2 :   format_local_iso_time(buf, t);
     307           2 :   buf[10] = 'T';
     308           2 : }
     309             : 
     310             : /** As format_iso_time, but use the yyyy-mm-ddThh:mm:ss format to avoid
     311             :  * embedding an internal space. */
     312             : void
     313         134 : format_iso_time_nospace(char *buf, time_t t)
     314             : {
     315         134 :   format_iso_time(buf, t);
     316         134 :   buf[10] = 'T';
     317         134 : }
     318             : 
     319             : /** As format_iso_time_nospace, but include microseconds in decimal
     320             :  * fixed-point format.  Requires that buf be at least ISO_TIME_USEC_LEN+1
     321             :  * bytes long. */
     322             : void
     323           1 : format_iso_time_nospace_usec(char *buf, const struct timeval *tv)
     324             : {
     325           1 :   tor_assert(tv);
     326           1 :   format_iso_time_nospace(buf, (time_t)tv->tv_sec);
     327           1 :   tor_snprintf(buf+ISO_TIME_LEN, 8, ".%06d", (int)tv->tv_usec);
     328           1 : }
     329             : 
     330             : /** Given an ISO-formatted UTC time value (after the epoch) in <b>cp</b>,
     331             :  * parse it and store its value in *<b>t</b>.  Return 0 on success, -1 on
     332             :  * failure.  Ignore extraneous stuff in <b>cp</b> after the end of the time
     333             :  * string, unless <b>strict</b> is set. If <b>nospace</b> is set,
     334             :  * expect the YYYY-MM-DDTHH:MM:SS format. */
     335             : int
     336        9349 : parse_iso_time_(const char *cp, time_t *t, int strict, int nospace)
     337             : {
     338        9349 :   struct tm st_tm;
     339        9349 :   unsigned int year=0, month=0, day=0, hour=0, minute=0, second=0;
     340        9349 :   int n_fields;
     341        9349 :   char extra_char, separator_char;
     342        9349 :   n_fields = tor_sscanf(cp, "%u-%2u-%2u%c%2u:%2u:%2u%c",
     343             :                         &year, &month, &day,
     344             :                         &separator_char,
     345             :                         &hour, &minute, &second, &extra_char);
     346        9349 :   if (strict ? (n_fields != 7) : (n_fields < 7)) {
     347          67 :     char *esc = esc_for_log(cp);
     348          67 :     log_warn(LD_GENERAL, "ISO time %s was unparseable", esc);
     349          67 :     tor_free(esc);
     350          67 :     return -1;
     351             :   }
     352       18311 :   if (separator_char != (nospace ? 'T' : ' ')) {
     353           6 :     char *esc = esc_for_log(cp);
     354           6 :     log_warn(LD_GENERAL, "ISO time %s was unparseable", esc);
     355           6 :     tor_free(esc);
     356           6 :     return -1;
     357             :   }
     358        9276 :   if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
     359        9258 :           hour > 23 || minute > 59 || second > 60 || year >= INT32_MAX) {
     360          30 :     char *esc = esc_for_log(cp);
     361          30 :     log_warn(LD_GENERAL, "ISO time %s was nonsensical", esc);
     362          30 :     tor_free(esc);
     363          30 :     return -1;
     364             :   }
     365        9246 :   st_tm.tm_year = (int)year-1900;
     366        9246 :   st_tm.tm_mon = month-1;
     367        9246 :   st_tm.tm_mday = day;
     368        9246 :   st_tm.tm_hour = hour;
     369        9246 :   st_tm.tm_min = minute;
     370        9246 :   st_tm.tm_sec = second;
     371        9246 :   st_tm.tm_wday = 0; /* Should be ignored. */
     372             : 
     373        9246 :   if (st_tm.tm_year < 70) {
     374             :     /* LCOV_EXCL_START
     375             :      * XXXX I think this is dead code; we already checked for
     376             :      *      year < 1970 above. */
     377             :     tor_assert_nonfatal_unreached();
     378             :     char *esc = esc_for_log(cp);
     379             :     log_warn(LD_GENERAL, "Got invalid ISO time %s. (Before 1970)", esc);
     380             :     tor_free(esc);
     381             :     return -1;
     382             :     /* LCOV_EXCL_STOP */
     383             :   }
     384        9246 :   return tor_timegm(&st_tm, t);
     385             : }
     386             : 
     387             : /** Given an ISO-formatted UTC time value (after the epoch) in <b>cp</b>,
     388             :  * parse it and store its value in *<b>t</b>.  Return 0 on success, -1 on
     389             :  * failure. Reject the string if any characters are present after the time.
     390             :  */
     391             : int
     392        9092 : parse_iso_time(const char *cp, time_t *t)
     393             : {
     394        9092 :   return parse_iso_time_(cp, t, 1, 0);
     395             : }
     396             : 
     397             : /**
     398             :  * As parse_iso_time, but parses a time encoded by format_iso_time_nospace().
     399             :  */
     400             : int
     401         256 : parse_iso_time_nospace(const char *cp, time_t *t)
     402             : {
     403         256 :   return parse_iso_time_(cp, t, 1, 1);
     404             : }
     405             : 
     406             : /** Given a <b>date</b> in one of the three formats allowed by HTTP (ugh),
     407             :  * parse it into <b>tm</b>.  Return 0 on success, negative on failure. */
     408             : int
     409          28 : parse_http_time(const char *date, struct tm *tm)
     410             : {
     411          28 :   const char *cp;
     412          28 :   char month[4];
     413          28 :   char wkday[4];
     414          28 :   int i;
     415          28 :   unsigned tm_mday, tm_year, tm_hour, tm_min, tm_sec;
     416             : 
     417          28 :   tor_assert(tm);
     418          28 :   memset(tm, 0, sizeof(*tm));
     419             : 
     420             :   /* First, try RFC1123 or RFC850 format: skip the weekday.  */
     421          28 :   if ((cp = strchr(date, ','))) {
     422          16 :     ++cp;
     423          16 :     if (*cp != ' ')
     424             :       return -1;
     425          15 :     ++cp;
     426          15 :     if (tor_sscanf(cp, "%2u %3s %4u %2u:%2u:%2u GMT",
     427             :                &tm_mday, month, &tm_year,
     428             :                &tm_hour, &tm_min, &tm_sec) == 6) {
     429             :       /* rfc1123-date */
     430           9 :       tm_year -= 1900;
     431           6 :     } else if (tor_sscanf(cp, "%2u-%3s-%2u %2u:%2u:%2u GMT",
     432             :                       &tm_mday, month, &tm_year,
     433             :                       &tm_hour, &tm_min, &tm_sec) == 6) {
     434             :       /* rfc850-date */
     435             :     } else {
     436             :       return -1;
     437             :     }
     438             :   } else {
     439             :     /* No comma; possibly asctime() format. */
     440          12 :     if (tor_sscanf(date, "%3s %3s %2u %2u:%2u:%2u %4u",
     441             :                wkday, month, &tm_mday,
     442             :                &tm_hour, &tm_min, &tm_sec, &tm_year) == 7) {
     443           3 :       tm_year -= 1900;
     444             :     } else {
     445             :       return -1;
     446             :     }
     447             :   }
     448          17 :   tm->tm_mday = (int)tm_mday;
     449          17 :   tm->tm_year = (int)tm_year;
     450          17 :   tm->tm_hour = (int)tm_hour;
     451          17 :   tm->tm_min = (int)tm_min;
     452          17 :   tm->tm_sec = (int)tm_sec;
     453          17 :   tm->tm_wday = 0; /* Leave this unset. */
     454             : 
     455          17 :   month[3] = '\0';
     456             :   /* Okay, now decode the month. */
     457             :   /* set tm->tm_mon to dummy value so the check below fails. */
     458          17 :   tm->tm_mon = -1;
     459         221 :   for (i = 0; i < 12; ++i) {
     460         204 :     if (!strcasecmp(MONTH_NAMES[i], month)) {
     461          16 :       tm->tm_mon = i;
     462             :     }
     463             :   }
     464             : 
     465          17 :   if (tm->tm_year < 0 ||
     466          16 :       tm->tm_mon < 0  || tm->tm_mon > 11 ||
     467          15 :       tm->tm_mday < 1 || tm->tm_mday > 31 ||
     468          13 :       tm->tm_hour < 0 || tm->tm_hour > 23 ||
     469          13 :       tm->tm_min < 0  || tm->tm_min > 59 ||
     470          13 :       tm->tm_sec < 0  || tm->tm_sec > 60)
     471           4 :     return -1; /* Out of range, or bad month. */
     472             : 
     473             :   return 0;
     474             : }
     475             : 
     476             : /** Given an <b>interval</b> in seconds, try to write it to the
     477             :  * <b>out_len</b>-byte buffer in <b>out</b> in a human-readable form.
     478             :  * Returns a non-negative integer on success, -1 on failure.
     479             :  */
     480             : int
     481          36 : format_time_interval(char *out, size_t out_len, long interval)
     482             : {
     483             :   /* We only report seconds if there's no hours. */
     484          36 :   long sec = 0, min = 0, hour = 0, day = 0;
     485             : 
     486             :   /* -LONG_MIN is LONG_MAX + 1, which causes signed overflow */
     487          36 :   if (interval < -LONG_MAX)
     488             :     interval = LONG_MAX;
     489          35 :   else if (interval < 0)
     490             :     interval = -interval;
     491             : 
     492          35 :   if (interval >= 86400) {
     493          11 :     day = interval / 86400;
     494          11 :     interval %= 86400;
     495             :   }
     496          36 :   if (interval >= 3600) {
     497          15 :     hour = interval / 3600;
     498          15 :     interval %= 3600;
     499             :   }
     500          36 :   if (interval >= 60) {
     501          24 :     min = interval / 60;
     502          24 :     interval %= 60;
     503             :   }
     504          36 :   sec = interval;
     505             : 
     506          36 :   if (day) {
     507          11 :     return tor_snprintf(out, out_len, "%ld days, %ld hours, %ld minutes",
     508             :                         day, hour, min);
     509          25 :   } else if (hour) {
     510           7 :     return tor_snprintf(out, out_len, "%ld hours, %ld minutes", hour, min);
     511          18 :   } else if (min) {
     512          12 :     return tor_snprintf(out, out_len, "%ld minutes, %ld seconds", min, sec);
     513             :   } else {
     514           6 :     return tor_snprintf(out, out_len, "%ld seconds", sec);
     515             :   }
     516             : }

Generated by: LCOV version 1.14