summaryrefslogtreecommitdiffstats
path: root/src/contrib/time.h
blob: b12b3662d4e1ac362c4112281294884b68688740 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*  Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#pragma once

#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <time.h>

#ifdef __APPLE__
 #define st_mtim st_mtimespec
#endif

/*!
 * \brief Specify output format for knot_time_print().
 */
typedef enum {
	TIME_PRINT_UNIX,	// numeric UNIX time
	TIME_PRINT_ISO8601,	// 2016-12-31T23:59:00
	TIME_PRINT_RELSEC,	// relative +6523
	TIME_PRINT_HUMAN_MIXED,	// relative with mixed-case units
	TIME_PRINT_HUMAN_LOWER,	// relative with lower-case units
} knot_time_print_t;

/*!
 * \brief Get current time.
 */
struct timespec time_now(void);

/*!
 * \brief Get time elapsed between two events.
 */
struct timespec time_diff(const struct timespec *begin, const struct timespec *end);

/*!
 * \brief Get time elapsed between two events in milliseconds.
 */
double time_diff_ms(const struct timespec *begin, const struct timespec *end);

/*!
 * \brief Data type for keeping UNIX timestamps.
 *
 * This is because time_t can be 32-bit on some systems, which is bad.
 * Zero value represents infinity.
 */
typedef uint64_t knot_time_t;

/*!
 * \brief Data type for keeping time differences.
 */
typedef int64_t knot_timediff_t;

#define KNOT_TIMEDIFF_MIN INT64_MIN
#define KNOT_TIMEDIFF_MAX INT64_MAX

#define KNOT_TIME_PRINTF PRIu64
#define KNOT_TIMEDIFF_PRINTF PRId64

/*!
 * \brief Returns current time sice epoch.
 */
inline static knot_time_t knot_time(void)
{
	return (knot_time_t)time(NULL);
}

/*!
 * \brief Compare two timestamps.
 *
 * \return 0 if equal, -1 if the former is smaller (=earlier), 1 else.
 */
inline static int knot_time_cmp(knot_time_t a, knot_time_t b)
{
	return (a == b ? 0 : 1) * ((a && b) == 0 ? -1 : 1) * (a < b ? -1 : 1);
}
inline static bool knot_time_lt (knot_time_t a, knot_time_t b) { return knot_time_cmp(a, b) < 0; }
inline static bool knot_time_leq(knot_time_t a, knot_time_t b) { return knot_time_cmp(a, b) <= 0; }
inline static bool knot_time_eq (knot_time_t a, knot_time_t b) { return knot_time_cmp(a, b) == 0; }
inline static bool knot_time_geq(knot_time_t a, knot_time_t b) { return knot_time_cmp(a, b) >= 0; }
inline static bool knot_time_gt (knot_time_t a, knot_time_t b) { return knot_time_cmp(a, b) > 0; }

/*!
 * \brief Return the smaller (=earlier) from given two timestamps.
 */
inline static knot_time_t knot_time_min(knot_time_t a, knot_time_t b)
{
	if ((a && b) == 0) {
		return a + b;
	} else {
		return (a < b ? a : b);
	}
}

/*!
 * \brief Return the difference between two timestamps (to "minus" from).
 *
 * \note If both are zero (=infinity), KNOT_TIMEDIFF_MAX is returned.
 */
inline static knot_timediff_t knot_time_diff(knot_time_t to, knot_time_t from)
{
	if ((to && from) == 0) {
		return (to > from ? KNOT_TIMEDIFF_MIN : KNOT_TIMEDIFF_MAX);
	} else {
		return (knot_timediff_t)to - (knot_timediff_t)from;
	}
}

/*!
 * \brief Add a time difference to timestamp.
 */
inline static knot_time_t knot_time_add(knot_time_t since, knot_timediff_t howlong)
{
	return (since != 0 ? since + howlong : since);
}

inline static knot_time_t knot_time_plus(knot_time_t a, knot_time_t b)
{
	return ((a && b) ? a + b : 0);
}

/*!
 * \brief Convert uint32_t-encoded timestamp to knot_time_t.
 *
 * In RRSIG rdata, there are inception and expiration timestamps in uint32_t format.
 * One shall use 'serial arithmetics' to decode them.
 *
 * The result of this function is a timestamp that equals to
 * given 32-bit time in lower 32 bits, and does not differ from
 * now by more than 2^31.
 */
inline static knot_time_t knot_time_from_u32(uint32_t u32time, knot_time_t now)
{
	if (now == 0) {
		now = knot_time();
	}

	uint32_t now_lower32 = (uint32_t)now;
	uint64_t now_upper32 = now >> 32;
	if (now_lower32 > u32time && now_lower32 - u32time > INT32_MAX) {
		now_upper32++;
	} else if (now_lower32 < u32time && u32time - now_lower32 > INT32_MAX) {
		now_upper32--;
	}

	return (now_upper32 << 32) + u32time;
}

/*!
 * \brief Parse a text-formatted timestamp to knot_time_t using format specification.
 *
 * \param format    The timestamp text format specification.
 * \param timespec  Text-formatted timestamp.
 * \param time      The parsed timestamp.
 *
 * The format specification basics:
 * <format 1>|<format 2> - The pipe sign separates two time format specifications. Leftmost
 *                         specification matching the timespec is used.
 * '<a string>'          - Matches exactly <a string> (not containing apostrophes) in timespec.
 * #                     - Hashtag matches for a number in timespec, stands for either a UNIX timestamp,
 *                         or, within a context of an unit, as a number of such units.
 * Y, M, D, h, m, s      - Matches a number, stands for a number of years, months, days, hours,
 *                         minutes and seconds, respectively.
 * +, -                  - The + and - signs declaring that following timespec is relative to "now".
 *                         A single sign can be used to limit the timestamp being in future or in past,
 *                         or both +- allow the timestamp to select any (just one) of them.
 * U                     - Matches one of Y, M, D, h, m, s in the timespec standing for a time unit.
 * u                     - Like U, but the unit in the timestamp is from: y, mo, d, h, mi, s.
 *
 * \retval -1  An error occurred, out_time has no sense.
 * \return  0  OK, timestamp parsed successfully.
 */
int knot_time_parse(const char *format, const char *timespec, knot_time_t *time);

/*!
 * \brief Print the timestamp in specified format into a string buffer.
 *
 * \param format   The timestamp text format specification.
 * \param time     The timestamp to be printed.
 * \param dst      The destination buffer pointer with text-formatted timestamp.
 * \param dst_len  The destination buffer length.
 *
 * \retval -1 An error occurred, the buffer may be filled with nonsense.
 * \return  0 OK, timestamp printed successfully.
 */
int knot_time_print(knot_time_print_t format, knot_time_t time, char *dst, size_t dst_len);

/*!
 * \brief Print the timestamp in a predefined human format.
 *
 * Condensed format (zone file compatible): 1w2d3h4m5s
 * Normal format: 1 week 2 days 3 hours 4 minutes 5 seconds
 *
 * \param time       The timestamp to be printed.
 * \param dst        The destination buffer pointer with text-formatted timestamp.
 * \param dst_len    The destination buffer length.
 * \param condensed  Condensed format indication.
 *
 * \retval -1 An error occurred, the buffer may be filled with nonsense.
 * \return >0 OK, timestamp printed successfully.
 */
int knot_time_print_human(knot_time_t time, char *dst, size_t dst_len, bool condensed);