]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/timeutils.c
Merge branch 'kill-tests' of https://github.com/rudimeier/util-linux
[thirdparty/util-linux.git] / lib / timeutils.c
1 /***
2 First set of functions in this file are part of systemd, and were
3 copied to util-linux at August 2013.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with util-linux; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <assert.h>
22 #include <ctype.h>
23 #include <string.h>
24 #include <sys/sysinfo.h>
25 #include <sys/time.h>
26 #include <time.h>
27
28 #include "c.h"
29 #include "nls.h"
30 #include "strutils.h"
31 #include "timeutils.h"
32
33 #define WHITESPACE " \t\n\r"
34
35 #define streq(a,b) (strcmp((a),(b)) == 0)
36
37 static int parse_sec(const char *t, usec_t *usec)
38 {
39 static const struct {
40 const char *suffix;
41 usec_t usec;
42 } table[] = {
43 { "seconds", USEC_PER_SEC },
44 { "second", USEC_PER_SEC },
45 { "sec", USEC_PER_SEC },
46 { "s", USEC_PER_SEC },
47 { "minutes", USEC_PER_MINUTE },
48 { "minute", USEC_PER_MINUTE },
49 { "min", USEC_PER_MINUTE },
50 { "months", USEC_PER_MONTH },
51 { "month", USEC_PER_MONTH },
52 { "msec", USEC_PER_MSEC },
53 { "ms", USEC_PER_MSEC },
54 { "m", USEC_PER_MINUTE },
55 { "hours", USEC_PER_HOUR },
56 { "hour", USEC_PER_HOUR },
57 { "hr", USEC_PER_HOUR },
58 { "h", USEC_PER_HOUR },
59 { "days", USEC_PER_DAY },
60 { "day", USEC_PER_DAY },
61 { "d", USEC_PER_DAY },
62 { "weeks", USEC_PER_WEEK },
63 { "week", USEC_PER_WEEK },
64 { "w", USEC_PER_WEEK },
65 { "years", USEC_PER_YEAR },
66 { "year", USEC_PER_YEAR },
67 { "y", USEC_PER_YEAR },
68 { "usec", 1ULL },
69 { "us", 1ULL },
70 { "", USEC_PER_SEC }, /* default is sec */
71 };
72
73 const char *p;
74 usec_t r = 0;
75 int something = FALSE;
76
77 assert(t);
78 assert(usec);
79
80 p = t;
81 for (;;) {
82 long long l, z = 0;
83 char *e;
84 unsigned i, n = 0;
85
86 p += strspn(p, WHITESPACE);
87
88 if (*p == 0) {
89 if (!something)
90 return -EINVAL;
91
92 break;
93 }
94
95 errno = 0;
96 l = strtoll(p, &e, 10);
97
98 if (errno > 0)
99 return -errno;
100
101 if (l < 0)
102 return -ERANGE;
103
104 if (*e == '.') {
105 char *b = e + 1;
106
107 errno = 0;
108 z = strtoll(b, &e, 10);
109 if (errno > 0)
110 return -errno;
111
112 if (z < 0)
113 return -ERANGE;
114
115 if (e == b)
116 return -EINVAL;
117
118 n = e - b;
119
120 } else if (e == p)
121 return -EINVAL;
122
123 e += strspn(e, WHITESPACE);
124
125 for (i = 0; i < ARRAY_SIZE(table); i++)
126 if (startswith(e, table[i].suffix)) {
127 usec_t k = (usec_t) z * table[i].usec;
128
129 for (; n > 0; n--)
130 k /= 10;
131
132 r += (usec_t) l *table[i].usec + k;
133 p = e + strlen(table[i].suffix);
134
135 something = TRUE;
136 break;
137 }
138
139 if (i >= ARRAY_SIZE(table))
140 return -EINVAL;
141
142 }
143
144 *usec = r;
145
146 return 0;
147 }
148
149 int parse_timestamp(const char *t, usec_t *usec)
150 {
151 static const struct {
152 const char *name;
153 const int nr;
154 } day_nr[] = {
155 { "Sunday", 0 },
156 { "Sun", 0 },
157 { "Monday", 1 },
158 { "Mon", 1 },
159 { "Tuesday", 2 },
160 { "Tue", 2 },
161 { "Wednesday", 3 },
162 { "Wed", 3 },
163 { "Thursday", 4 },
164 { "Thu", 4 },
165 { "Friday", 5 },
166 { "Fri", 5 },
167 { "Saturday", 6 },
168 { "Sat", 6 },
169 };
170
171 const char *k;
172 struct tm tm, copy;
173 time_t x;
174 usec_t plus = 0, minus = 0, ret;
175 int r, weekday = -1;
176 unsigned i;
177
178 /*
179 * Allowed syntaxes:
180 *
181 * 2012-09-22 16:34:22
182 * 2012-09-22 16:34 (seconds will be set to 0)
183 * 2012-09-22 (time will be set to 00:00:00)
184 * 16:34:22 (date will be set to today)
185 * 16:34 (date will be set to today, seconds to 0)
186 * now
187 * yesterday (time is set to 00:00:00)
188 * today (time is set to 00:00:00)
189 * tomorrow (time is set to 00:00:00)
190 * +5min
191 * -5days
192 *
193 */
194
195 assert(t);
196 assert(usec);
197
198 x = time(NULL);
199 localtime_r(&x, &tm);
200 tm.tm_isdst = -1;
201
202 if (streq(t, "now"))
203 goto finish;
204
205 else if (streq(t, "today")) {
206 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
207 goto finish;
208
209 } else if (streq(t, "yesterday")) {
210 tm.tm_mday--;
211 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
212 goto finish;
213
214 } else if (streq(t, "tomorrow")) {
215 tm.tm_mday++;
216 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
217 goto finish;
218
219 } else if (t[0] == '+') {
220
221 r = parse_sec(t + 1, &plus);
222 if (r < 0)
223 return r;
224
225 goto finish;
226 } else if (t[0] == '-') {
227
228 r = parse_sec(t + 1, &minus);
229 if (r < 0)
230 return r;
231
232 goto finish;
233
234 } else if (endswith(t, " ago")) {
235 char *z;
236
237 z = strndup(t, strlen(t) - 4);
238 if (!z)
239 return -ENOMEM;
240
241 r = parse_sec(z, &minus);
242 free(z);
243 if (r < 0)
244 return r;
245
246 goto finish;
247 }
248
249 for (i = 0; i < ARRAY_SIZE(day_nr); i++) {
250 size_t skip;
251
252 if (!startswith_no_case(t, day_nr[i].name))
253 continue;
254
255 skip = strlen(day_nr[i].name);
256 if (t[skip] != ' ')
257 continue;
258
259 weekday = day_nr[i].nr;
260 t += skip + 1;
261 break;
262 }
263
264 copy = tm;
265 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
266 if (k && *k == 0)
267 goto finish;
268
269 tm = copy;
270 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
271 if (k && *k == 0)
272 goto finish;
273
274 tm = copy;
275 k = strptime(t, "%y-%m-%d %H:%M", &tm);
276 if (k && *k == 0) {
277 tm.tm_sec = 0;
278 goto finish;
279 }
280
281 tm = copy;
282 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
283 if (k && *k == 0) {
284 tm.tm_sec = 0;
285 goto finish;
286 }
287
288 tm = copy;
289 k = strptime(t, "%y-%m-%d", &tm);
290 if (k && *k == 0) {
291 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
292 goto finish;
293 }
294
295 tm = copy;
296 k = strptime(t, "%Y-%m-%d", &tm);
297 if (k && *k == 0) {
298 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
299 goto finish;
300 }
301
302 tm = copy;
303 k = strptime(t, "%H:%M:%S", &tm);
304 if (k && *k == 0)
305 goto finish;
306
307 tm = copy;
308 k = strptime(t, "%H:%M", &tm);
309 if (k && *k == 0) {
310 tm.tm_sec = 0;
311 goto finish;
312 }
313
314 tm = copy;
315 k = strptime(t, "%Y%m%d%H%M%S", &tm);
316 if (k && *k == 0) {
317 tm.tm_sec = 0;
318 goto finish;
319 }
320
321 return -EINVAL;
322
323 finish:
324 x = mktime(&tm);
325 if (x == (time_t)-1)
326 return -EINVAL;
327
328 if (weekday >= 0 && tm.tm_wday != weekday)
329 return -EINVAL;
330
331 ret = (usec_t) x *USEC_PER_SEC;
332
333 ret += plus;
334 if (ret > minus)
335 ret -= minus;
336 else
337 ret = 0;
338
339 *usec = ret;
340
341 return 0;
342 }