]> git.ipfire.org Git - collecty.git/commitdiff
time: Add function to parse timestamps from humans
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 29 Oct 2025 15:43:30 +0000 (15:43 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 29 Oct 2025 15:43:30 +0000 (15:43 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/daemon/time.c [new file with mode: 0644]
src/daemon/time.h

index a8196320a594b710a3c28146efe8b091910d394e..e7ad419661f5e4ed45ef692b0bc1a834970683f7 100644 (file)
@@ -197,6 +197,7 @@ dist_telemetryd_SOURCES = \
        src/daemon/sources/uptime.c \
        src/daemon/sources/uptime.h \
        src/daemon/string.h \
+       src/daemon/time.c \
        src/daemon/time.h \
        src/daemon/util.c \
        src/daemon/util.h
diff --git a/src/daemon/time.c b/src/daemon/time.c
new file mode 100644 (file)
index 0000000..aa82d5a
--- /dev/null
@@ -0,0 +1,222 @@
+/*#############################################################################
+#                                                                             #
+# telemetryd - The IPFire Telemetry Collection Service                        #
+# Copyright (C) 2025 IPFire Development Team                                  #
+#                                                                             #
+# 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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#include <limits.h>
+#include <time.h>
+
+#include "string.h"
+#include "time.h"
+
+static int find_multiplier(char** p) {
+       int r;
+
+       const struct multiplier {
+               const char* unit;
+               int multiplier;
+       } multipliers[] = {
+               { "seconds", 1 },
+               { "second",  1 },
+               { "sec",     1 },
+               { "s",       1 },
+               { "minutes", 60 },
+               { "minute",  60 },
+               { "min",     60 },
+               { "m",       60 },
+               { "hours",   3600 },
+               { "hour",    3600 },
+               { "hrs",     3600 },
+               { "hr",      3600 },
+               { "h",       3600 },
+               { "days",    3600 * 24 },
+               { "day",     3600 * 24 },
+               { "d",       3600 * 24 },
+               { "months",  3600 * 24 * 30 },
+               { "month",   3600 * 24 * 30 },
+               { "years",   3600 * 24 * 365 },
+               { "year",    3600 * 24 * 365 },
+               { NULL },
+       };
+
+       // Find the multiplier
+       for (const struct multiplier* m = multipliers; m->unit; m++) {
+               r = td_string_consume(p, m->unit);
+               if (r < 0)
+                       continue;
+
+               // Return the multiplier
+               return m->multiplier;
+       }
+
+       return -1;
+}
+
+static int time_parse_relative(time_t* t, const char* arg, time_t now) {
+       unsigned long num = 0;
+       int direction = 0;
+       int multiplier;
+       int r;
+
+       char* p = (char*)arg;
+
+       // Look for a leading +/-
+       switch (*p) {
+               case '+':
+                       direction = 1;
+                       p++;
+                       break;
+
+               case '-':
+                       direction = -1;
+                       p++;
+                       break;
+
+               default:
+                       break;
+       }
+
+       // Parse the number
+       num = strtoul(p, &p, 10);
+
+       // Handle errors
+       if (num == ULONG_MAX)
+               return -errno;
+
+       // Skip any whitespace
+       while (*p && isspace(*p))
+               p++;
+
+       // Find the multiplier
+       multiplier = find_multiplier(&p);
+       if (multiplier < 0)
+               return -EINVAL;
+
+       // Is there more to parse?
+       while (*p) {
+               // Skip whitespace
+               while (*p && isspace(*p))
+                       p++;
+
+               // Break if we have consumed all the whitespace
+               if (!*p)
+                       break;
+
+               // If we already have a direction, there should not be anything left to parse
+               if (direction)
+                       return -EINVAL;
+
+               // We know how to handle "ago"
+               r = td_string_consume(&p, "ago");
+               if (r == 0)
+                       direction = -1;
+
+               // Everything else I don't know
+               else
+                       return -EINVAL;
+       }
+
+       // Now let's do the maths
+       return now + (direction * num * multiplier);
+}
+
+/*
+       Supported strings are:
+       * now
+       * today               (time will be set to 00:00:00)
+       * yesterday           (time will be set to 00:00:00)
+       * tomorrow            (time will be set to 00:00:00)
+       * YYYY-MM-DD HH:MM:SS
+       * YYYY-MM-DD HH:MM
+       * YYYY-MM-DD
+       * HH:MM:SS            (date will be set to today)
+       * HH:MM               (date will be set to today, seconds to zero)
+*/
+
+int time_parse(time_t* t, const char* arg) {
+       struct tm tm = {};
+       time_t now = -1;
+       char* p = NULL;
+
+       // Fetch the current time
+       now = time(NULL);
+       if (now < 0)
+               return -errno;
+
+       // Parse the current time
+       if (!gmtime_r(&now, &tm))
+               return -errno;
+
+       // Handle "now"
+       if (td_string_equals(arg, "now"))
+               goto DONE;
+
+       // Handle "today"
+       if (td_string_equals(arg, "today")) {
+               tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+               goto DONE;
+       }
+
+       // Handle "yesterday"
+       if (td_string_equals(arg, "yesterday")) {
+               tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+               tm.tm_mday--;
+               goto DONE;
+       }
+
+       // Handle "tomorrow"
+       if (td_string_equals(arg, "tomorrow")) {
+               tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+               tm.tm_mday++;
+               goto DONE;
+       }
+
+       // Handle YYYY-MM-DD HH:MM:SS
+       p = strptime(arg, "%Y-%m-%d %H:%M:%S", &tm);
+       if (p && !*p)
+               goto DONE;
+
+       // Handle YYYY-MM-DD HH:MM
+       p = strptime(arg, "%Y-%m-%d %H:%M", &tm);
+       if (p && !*p)
+               goto DONE;
+
+       // Handle YYYY-MM-DD
+       p = strptime(arg, "%Y-%m-%d", &tm);
+       if (p && !*p)
+               goto DONE;
+
+       // Handle HH:MM:SS
+       p = strptime(arg, "%H:%M:%S", &tm);
+       if (p && !*p)
+               goto DONE;
+
+       // Handle HH:MM
+       p = strptime(arg, "%H:%M", &tm);
+       if (p && !*p)
+               goto DONE;
+
+       // Try parsing relative time
+       return time_parse_relative(t, arg, now);
+
+DONE:
+       // Return as integer
+       *t = mktime(&tm);
+
+       return 0;
+}
index f22fbbc72fe91f8c18e446e9fda55426b8a5d8db..2409c0bca40dc34e160af7e39d099a14d18146b8 100644 (file)
@@ -21,6 +21,8 @@
 #ifndef TELEMETRY_TIME_H
 #define TELEMETRY_TIME_H
 
+#include <time.h>
+
 // Seconds to milliseconds
 #define SEC_TO_MSEC(s)         ((s) * 1000UL)
 
@@ -48,4 +50,7 @@
 // Nanoseconds to microseconds
 #define NSEC_TO_USEC(ns)       ((ns) / 1000UL)
 
+// Parse a timestamp
+int time_parse(time_t* t, const char* arg);
+
 #endif /* TELEMETRY_TIME_H */