Coverage Report

Created: 2026-09-03 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/time-util.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "time-util.h"
5
6
#include <time.h>
7
8
#define STRFTIME_MAX_BUFSIZE (1024*64)
9
10
void i_gettimeofday(struct timeval *tv_r)
11
259
{
12
259
  if (gettimeofday(tv_r, NULL) < 0)
13
0
    i_fatal("gettimeofday() failed: %m");
14
259
}
15
16
uint64_t i_nanoseconds(void)
17
0
{
18
0
  struct timespec ts;
19
20
0
  if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
21
0
    i_fatal("clock_gettime() failed: %m");
22
0
  return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
23
0
}
24
25
int timeval_cmp(const struct timeval *tv1, const struct timeval *tv2)
26
0
{
27
0
  if (tv1->tv_sec < tv2->tv_sec)
28
0
    return -1;
29
0
  if (tv1->tv_sec > tv2->tv_sec)
30
0
    return 1;
31
0
  if (tv1->tv_usec < tv2->tv_usec)
32
0
    return -1;
33
0
  if (tv1->tv_usec > tv2->tv_usec)
34
0
    return 1;
35
0
  return 0;
36
0
}
37
38
int timeval_cmp_margin(const struct timeval *tv1, const struct timeval *tv2,
39
  unsigned int usec_margin)
40
0
{
41
0
  long long secs_diff, usecs_diff;
42
0
  int sec_margin, ret;
43
44
0
  if (tv1->tv_sec < tv2->tv_sec) {
45
0
    sec_margin = ((int)usec_margin / 1000000) + 1;
46
0
    secs_diff = (long long)tv2->tv_sec - (long long)tv1->tv_sec;
47
0
    if (secs_diff > sec_margin)
48
0
      return -1;
49
0
    usecs_diff = secs_diff * 1000000LL +
50
0
      (tv2->tv_usec - tv1->tv_usec);
51
0
    ret = -1;
52
0
  } else if (tv1->tv_sec > tv2->tv_sec) {
53
0
    sec_margin = ((int)usec_margin / 1000000) + 1;
54
0
    secs_diff = (long long)tv1->tv_sec - (long long)tv2->tv_sec;
55
0
    if (secs_diff > sec_margin)
56
0
      return 1;
57
0
    usecs_diff = secs_diff * 1000000LL +
58
0
      (tv1->tv_usec - tv2->tv_usec);
59
0
    ret = 1;
60
0
  } else if (tv1->tv_usec < tv2->tv_usec) {
61
0
    usecs_diff = tv2->tv_usec - tv1->tv_usec;
62
0
    ret = -1;
63
0
  } else {
64
0
    usecs_diff = tv1->tv_usec - tv2->tv_usec;
65
0
    ret = 1;
66
0
  }
67
0
  i_assert(usecs_diff >= 0);
68
0
  return (unsigned long long)usecs_diff > usec_margin ? ret : 0;
69
0
}
70
71
long long timeval_diff_usecs(const struct timeval *tv1,
72
           const struct timeval *tv2)
73
0
{
74
0
  time_t secs;
75
0
  suseconds_t usecs;
76
77
0
  secs = tv1->tv_sec - tv2->tv_sec;
78
0
  usecs = tv1->tv_usec - tv2->tv_usec;
79
0
  if (usecs < 0) {
80
0
    secs--;
81
0
    usecs += 1000000;
82
0
  }
83
0
  return ((long long)secs * 1000000LL) + usecs;
84
0
}
85
86
time_t time_to_local_day_start(time_t t)
87
0
{
88
0
  const struct tm *day_tm;
89
0
  struct tm tm;
90
0
  time_t new_start_time;
91
92
0
  day_tm = localtime(&t);
93
0
  i_zero(&tm);
94
0
  tm.tm_year = day_tm->tm_year;
95
0
  tm.tm_mon = day_tm->tm_mon;
96
0
  tm.tm_mday = day_tm->tm_mday;
97
0
  tm.tm_isdst = -1;
98
0
  new_start_time = mktime(&tm);
99
0
  i_assert(new_start_time != (time_t)-1);
100
0
  return new_start_time;
101
0
}
102
103
static const char *strftime_real(const char *fmt, const struct tm *tm)
104
0
{
105
0
  size_t bufsize = strlen(fmt) + 32;
106
0
  char *buf = t_buffer_get(bufsize);
107
0
  size_t ret;
108
109
0
  while ((ret = strftime(buf, bufsize, fmt, tm)) == 0) {
110
0
    bufsize *= 2;
111
0
    i_assert(bufsize <= STRFTIME_MAX_BUFSIZE);
112
0
    buf = t_buffer_get(bufsize);
113
0
  }
114
0
  t_buffer_alloc(ret + 1);
115
0
  return buf;
116
0
}
117
118
const char *t_strftime(const char *fmt, const struct tm *tm)
119
0
{
120
0
  return strftime_real(fmt, tm);
121
0
}
122
123
const char *t_strflocaltime(const char *fmt, time_t t)
124
0
{
125
0
  return strftime_real(fmt, localtime(&t));
126
0
}
127
128
const char *t_strfgmtime(const char *fmt, time_t t)
129
0
{
130
0
  return strftime_real(fmt, gmtime(&t));
131
0
}
132
133
int str_to_timeval(const char *str, struct timeval *tv_r)
134
0
{
135
0
  tv_r->tv_usec = 0;
136
137
0
  const char *p = strchr(str, '.');
138
0
  if (p == NULL)
139
0
    return str_to_time(str, &tv_r->tv_sec);
140
141
0
  int ret;
142
0
  T_BEGIN {
143
0
    ret = str_to_time(t_strdup_until(str, p++), &tv_r->tv_sec);
144
0
  } T_END;
145
0
  if (ret < 0 || p[0] == '\0')
146
0
    return -1;
147
148
0
  unsigned int len = strlen(p);
149
0
  if (len > 6)
150
0
    return -1; /* we don't support sub-microseconds */
151
0
  char usecs_str[7] = "000000";
152
0
  memcpy(usecs_str, p, len);
153
154
0
  unsigned int usec;
155
0
  if (str_to_uint(usecs_str, &usec) < 0)
156
0
    return -1;
157
0
  tv_r->tv_usec = usec;
158
0
  return 0;
159
0
}
160
161
time_t time_max_safe_value(void)
162
0
{
163
  /* signed time_t is enforced by configure */
164
#if TIME_T_MAX_BITS >= 63
165
  return (time_t)INT64_MAX;
166
#else
167
  /* compute in uint64_t: with a 32-bit signed time_t
168
     TIME_T_MAX_BITS is 31 and (time_t)1 << 31 would overflow */
169
0
  return (time_t)(((uint64_t)1 << TIME_T_MAX_BITS) - 1);
170
0
#endif
171
0
}
172
173
int time_add_secs(time_t base, int64_t secs, time_t *result_r)
174
0
{
175
0
  int64_t max = (int64_t)time_max_safe_value();
176
0
  int64_t min = -max;
177
0
  int64_t b = (int64_t)base;
178
179
  /* Reject an out-of-range base first: once b is known to be within
180
     [min, max], "max - b" and "min - b" below can't overflow either,
181
     since both min and max are small in magnitude. */
182
0
  if (b < min || b > max)
183
0
    return -1;
184
0
  if (secs > max - b || secs < min - b)
185
0
    return -1;
186
0
  *result_r = (time_t)(b + secs);
187
0
  return 0;
188
0
}
189
190
int time_sub_secs(time_t base, int64_t secs, time_t *result_r)
191
0
{
192
0
  int64_t max = (int64_t)time_max_safe_value();
193
0
  int64_t min = -max;
194
0
  int64_t b = (int64_t)base;
195
196
  /* see time_add_secs() for why the base is checked first */
197
0
  if (b < min || b > max)
198
0
    return -1;
199
0
  if (secs < b - max || secs > b - min)
200
0
    return -1;
201
0
  *result_r = (time_t)(b - secs);
202
0
  return 0;
203
0
}