1 /* Copyright (c) 2008-2018 Dovecot authors, see the included 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 
i_gettimeofday(struct timeval * tv_r)10 void i_gettimeofday(struct timeval *tv_r)
11 {
12 	if (gettimeofday(tv_r, NULL) < 0)
13 		i_fatal("gettimeofday() failed: %m");
14 }
15 
i_nanoseconds(void)16 uint64_t i_nanoseconds(void)
17 {
18 	struct timespec ts;
19 
20 	if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
21 		i_fatal("clock_gettime() failed: %m");
22 	return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
23 }
24 
timeval_cmp(const struct timeval * tv1,const struct timeval * tv2)25 int timeval_cmp(const struct timeval *tv1, const struct timeval *tv2)
26 {
27 	if (tv1->tv_sec < tv2->tv_sec)
28 		return -1;
29 	if (tv1->tv_sec > tv2->tv_sec)
30 		return 1;
31 	if (tv1->tv_usec < tv2->tv_usec)
32 		return -1;
33 	if (tv1->tv_usec > tv2->tv_usec)
34 		return 1;
35 	return 0;
36 }
37 
timeval_cmp_margin(const struct timeval * tv1,const struct timeval * tv2,unsigned int usec_margin)38 int timeval_cmp_margin(const struct timeval *tv1, const struct timeval *tv2,
39 	unsigned int usec_margin)
40 {
41 	long long secs_diff, usecs_diff;
42 	int sec_margin, ret;
43 
44 	if (tv1->tv_sec < tv2->tv_sec) {
45 		sec_margin = ((int)usec_margin / 1000000) + 1;
46 		secs_diff = (long long)tv2->tv_sec - (long long)tv1->tv_sec;
47 		if (secs_diff > sec_margin)
48 			return -1;
49 		usecs_diff = secs_diff * 1000000LL +
50 			(tv2->tv_usec - tv1->tv_usec);
51 		ret = -1;
52 	} else if (tv1->tv_sec > tv2->tv_sec) {
53 		sec_margin = ((int)usec_margin / 1000000) + 1;
54 		secs_diff = (long long)tv1->tv_sec - (long long)tv2->tv_sec;
55 		if (secs_diff > sec_margin)
56 			return 1;
57 		usecs_diff = secs_diff * 1000000LL +
58 			(tv1->tv_usec - tv2->tv_usec);
59 		ret = 1;
60 	} else if (tv1->tv_usec < tv2->tv_usec) {
61 		usecs_diff = tv2->tv_usec - tv1->tv_usec;
62 		ret = -1;
63 	} else {
64 		usecs_diff = tv1->tv_usec - tv2->tv_usec;
65 		ret = 1;
66 	}
67 	i_assert(usecs_diff >= 0);
68 	return (unsigned long long)usecs_diff > usec_margin ? ret : 0;
69 }
70 
timeval_diff_msecs(const struct timeval * tv1,const struct timeval * tv2)71 int timeval_diff_msecs(const struct timeval *tv1, const struct timeval *tv2)
72 {
73 	long long diff = timeval_diff_usecs(tv1, tv2) / 1000LL;
74 #ifdef DEBUG
75 	/* FIXME v2.4: Remove the ifdef */
76 	i_assert(diff <= INT_MAX);
77 #endif
78 	return (int)diff;
79 }
80 
timeval_diff_usecs(const struct timeval * tv1,const struct timeval * tv2)81 long long timeval_diff_usecs(const struct timeval *tv1,
82 			     const struct timeval *tv2)
83 {
84 	time_t secs;
85 	int usecs;
86 
87 	secs = tv1->tv_sec - tv2->tv_sec;
88 	usecs = tv1->tv_usec - tv2->tv_usec;
89 	if (usecs < 0) {
90 		secs--;
91 		usecs += 1000000;
92 	}
93 	return ((long long)secs * 1000000LL) + usecs;
94 }
95 
time_to_local_day_start(time_t t)96 time_t time_to_local_day_start(time_t t)
97 {
98 	const struct tm *day_tm;
99 	struct tm tm;
100 	time_t new_start_time;
101 
102 	day_tm = localtime(&t);
103 	i_zero(&tm);
104 	tm.tm_year = day_tm->tm_year;
105 	tm.tm_mon = day_tm->tm_mon;
106 	tm.tm_mday = day_tm->tm_mday;
107 	tm.tm_isdst = -1;
108 	new_start_time = mktime(&tm);
109 	i_assert(new_start_time != (time_t)-1);
110 	return new_start_time;
111 }
112 
strftime_real(const char * fmt,const struct tm * tm)113 static const char *strftime_real(const char *fmt, const struct tm *tm)
114 {
115 	size_t bufsize = strlen(fmt) + 32;
116 	char *buf = t_buffer_get(bufsize);
117 	size_t ret;
118 
119 	while ((ret = strftime(buf, bufsize, fmt, tm)) == 0) {
120 		bufsize *= 2;
121 		i_assert(bufsize <= STRFTIME_MAX_BUFSIZE);
122 		buf = t_buffer_get(bufsize);
123 	}
124 	t_buffer_alloc(ret + 1);
125 	return buf;
126 }
127 
t_strftime(const char * fmt,const struct tm * tm)128 const char *t_strftime(const char *fmt, const struct tm *tm)
129 {
130 	return strftime_real(fmt, tm);
131 }
132 
t_strflocaltime(const char * fmt,time_t t)133 const char *t_strflocaltime(const char *fmt, time_t t)
134 {
135 	return strftime_real(fmt, localtime(&t));
136 }
137 
t_strfgmtime(const char * fmt,time_t t)138 const char *t_strfgmtime(const char *fmt, time_t t)
139 {
140 	return strftime_real(fmt, gmtime(&t));
141 }
142 
str_to_timeval(const char * str,struct timeval * tv_r)143 int str_to_timeval(const char *str, struct timeval *tv_r)
144 {
145 	tv_r->tv_usec = 0;
146 
147 	const char *p = strchr(str, '.');
148 	if (p == NULL)
149 		return str_to_time(str, &tv_r->tv_sec);
150 
151 	int ret;
152 	T_BEGIN {
153 		ret = str_to_time(t_strdup_until(str, p++), &tv_r->tv_sec);
154 	} T_END;
155 	if (ret < 0 || p[0] == '\0')
156 		return -1;
157 
158 	unsigned int len = strlen(p);
159 	if (len > 6)
160 		return -1; /* we don't support sub-microseconds */
161 	char usecs_str[7] = "000000";
162 	memcpy(usecs_str, p, len);
163 
164 	unsigned int usec;
165 	if (str_to_uint(usecs_str, &usec) < 0)
166 		return -1;
167 	tv_r->tv_usec = usec;
168 	return 0;
169 }
170