summaryrefslogtreecommitdiffstats
path: root/tools/iso8601.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/iso8601.c')
-rw-r--r--tools/iso8601.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/tools/iso8601.c b/tools/iso8601.c
new file mode 100644
index 0000000..e53bca0
--- /dev/null
+++ b/tools/iso8601.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2005-2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+#include <crm/crm.h>
+#include <crm/common/cmdline_internal.h>
+#include <crm/common/iso8601.h>
+#include <crm/common/util.h> /* CRM_ASSERT */
+#include <unistd.h>
+
+#define SUMMARY "Display and parse ISO 8601 dates and times"
+
+struct {
+ char *date_time_s;
+ gchar *duration_s;
+ gchar *expected_s;
+ gchar *period_s;
+ int print_options;
+} options;
+
+#define INDENT " "
+
+static gboolean
+date_now_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
+ if (pcmk__str_any_of(option_name, "--now", "-n", NULL)) {
+ pcmk__str_update(&options.date_time_s, "now");
+ } else if (pcmk__str_any_of(option_name, "--date", "-d", NULL)) {
+ pcmk__str_update(&options.date_time_s, optarg);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+modifier_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
+ if (pcmk__str_any_of(option_name, "--seconds", "-s", NULL)) {
+ options.print_options |= crm_time_seconds;
+ } else if (pcmk__str_any_of(option_name, "--epoch", "-S", NULL)) {
+ options.print_options |= crm_time_epoch;
+ } else if (pcmk__str_any_of(option_name, "--local", "-L", NULL)) {
+ options.print_options |= crm_time_log_with_timezone;
+ } else if (pcmk__str_any_of(option_name, "--ordinal", "-O", NULL)) {
+ options.print_options |= crm_time_ordinal;
+ } else if (pcmk__str_any_of(option_name, "--week", "-W", NULL)) {
+ options.print_options |= crm_time_weeks;
+ }
+
+ return TRUE;
+}
+
+static GOptionEntry command_entries[] = {
+ { "now", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, date_now_cb,
+ "Display the current date/time",
+ NULL },
+
+ { "date", 'd', 0, G_OPTION_ARG_CALLBACK, date_now_cb,
+ "Parse an ISO 8601 date/time (for example,\n"
+ INDENT "'2019-09-24 00:30:00 +01:00' or '2019-040')",
+ "DATE" },
+
+ { "period", 'p', 0, G_OPTION_ARG_STRING, &options.period_s,
+ "Parse an ISO 8601 period (interval) with start time (for example,\n"
+ INDENT "'2005-040/2005-043')",
+ "PERIOD" },
+
+ { "duration", 'D', 0, G_OPTION_ARG_STRING, &options.duration_s,
+ "Parse an ISO 8601 duration (for example, 'P1M')",
+ "DURATION" },
+
+ { "expected", 'E', 0, G_OPTION_ARG_STRING, &options.expected_s,
+ "Exit with error status if result does not match this text.\n"
+ INDENT "Requires: -n or -d",
+ "TEXT" },
+
+ { NULL }
+};
+
+static GOptionEntry modifier_entries[] = {
+ { "seconds", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
+ "Show result as a seconds since 0000-001 00:00:00Z",
+ NULL },
+
+ { "epoch", 'S', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
+ "Show result as a seconds since EPOCH (1970-001 00:00:00Z)",
+ NULL },
+
+ { "local", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
+ "Show result as a 'local' date/time",
+ NULL },
+
+ { "ordinal", 'O', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
+ "Show result as an 'ordinal' date/time",
+ NULL },
+
+ { "week", 'W', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, modifier_cb,
+ "Show result as an 'calendar week' date/time",
+ NULL },
+
+ { NULL }
+};
+
+static void
+log_time_period(int log_level, crm_time_period_t * dtp, int flags)
+{
+ char *start = crm_time_as_string(dtp->start, flags);
+ char *end = crm_time_as_string(dtp->end, flags);
+
+ CRM_ASSERT(start != NULL && end != NULL);
+ do_crm_log(log_level, "Period: %s to %s", start, end);
+ free(start);
+ free(end);
+}
+
+static GOptionContext *
+build_arg_context(pcmk__common_args_t *args) {
+ GOptionContext *context = NULL;
+
+ const char *description = "For more information on the ISO 8601 standard, see " \
+ "https://en.wikipedia.org/wiki/ISO_8601";
+
+ context = pcmk__build_arg_context(args, NULL, NULL, NULL);
+ g_option_context_set_description(context, description);
+
+ pcmk__add_arg_group(context, "commands", "Commands:",
+ "Show command options", command_entries);
+ pcmk__add_arg_group(context, "modifiers", "Output modifiers:",
+ "Show output modifiers", modifier_entries);
+
+ return context;
+}
+
+int
+main(int argc, char **argv)
+{
+ crm_exit_t exit_code = CRM_EX_OK;
+ crm_time_t *duration = NULL;
+ crm_time_t *date_time = NULL;
+
+ GError *error = NULL;
+
+ pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
+ GOptionContext *context = build_arg_context(args);
+ gchar **processed_args = pcmk__cmdline_preproc(argv, "dpDE");
+
+ if (!g_option_context_parse_strv(context, &processed_args, &error)) {
+ exit_code = CRM_EX_USAGE;
+ goto done;
+ }
+
+ pcmk__cli_init_logging("iso8601", args->verbosity);
+
+ if (args->version) {
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+ /* FIXME: When iso8601 is converted to use formatted output, this can go. */
+ pcmk__cli_help('v');
+ }
+
+ if (pcmk__str_eq("now", options.date_time_s, pcmk__str_casei)) {
+ date_time = crm_time_new(NULL);
+
+ if (date_time == NULL) {
+ exit_code = CRM_EX_SOFTWARE;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Internal error: couldn't determine 'now'!");
+ goto done;
+ }
+
+ crm_time_log(LOG_TRACE, "Current date/time", date_time,
+ crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
+ crm_time_log(LOG_STDOUT, "Current date/time", date_time,
+ options.print_options | crm_time_log_date | crm_time_log_timeofday);
+
+ } else if (options.date_time_s) {
+ date_time = crm_time_new(options.date_time_s);
+
+ if (date_time == NULL) {
+ exit_code = CRM_EX_INVALID_PARAM;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Invalid date/time specified: %s", options.date_time_s);
+ goto done;
+ }
+
+ crm_time_log(LOG_TRACE, "Date", date_time,
+ crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
+ crm_time_log(LOG_STDOUT, "Date", date_time,
+ options.print_options | crm_time_log_date | crm_time_log_timeofday);
+ }
+
+ if (options.duration_s) {
+ duration = crm_time_parse_duration(options.duration_s);
+
+ if (duration == NULL) {
+ exit_code = CRM_EX_INVALID_PARAM;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Invalid duration specified: %s", options.duration_s);
+ goto done;
+ }
+
+ crm_time_log(LOG_TRACE, "Duration", duration, crm_time_log_duration);
+ crm_time_log(LOG_STDOUT, "Duration", duration,
+ options.print_options | crm_time_log_duration);
+ }
+
+ if (options.period_s) {
+ crm_time_period_t *period = crm_time_parse_period(options.period_s);
+
+ if (period == NULL) {
+ exit_code = CRM_EX_INVALID_PARAM;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Invalid interval specified: %s", options.period_s);
+ goto done;
+ }
+
+ log_time_period(LOG_TRACE, period,
+ options.print_options | crm_time_log_date | crm_time_log_timeofday);
+ log_time_period(LOG_STDOUT, period,
+ options.print_options | crm_time_log_date | crm_time_log_timeofday);
+ crm_time_free_period(period);
+ }
+
+ if (date_time && duration) {
+ crm_time_t *later = crm_time_add(date_time, duration);
+
+ if (later == NULL) {
+ exit_code = CRM_EX_SOFTWARE;
+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
+ "Unable to calculate ending time of %s plus %s",
+ options.date_time_s, options.duration_s);
+ goto done;
+ }
+
+ crm_time_log(LOG_TRACE, "Duration ends at", later,
+ crm_time_ordinal | crm_time_log_date | crm_time_log_timeofday);
+ crm_time_log(LOG_STDOUT, "Duration ends at", later,
+ options.print_options | crm_time_log_date | crm_time_log_timeofday |
+ crm_time_log_with_timezone);
+
+ if (options.expected_s) {
+ char *dt_s = crm_time_as_string(later,
+ options.print_options | crm_time_log_date |
+ crm_time_log_timeofday);
+ if (!pcmk__str_eq(options.expected_s, dt_s, pcmk__str_casei)) {
+ exit_code = CRM_EX_ERROR;
+ goto done;
+ }
+ free(dt_s);
+ }
+ crm_time_free(later);
+
+ } else if (date_time && options.expected_s) {
+ char *dt_s = crm_time_as_string(date_time,
+ options.print_options | crm_time_log_date | crm_time_log_timeofday);
+
+ if (!pcmk__str_eq(options.expected_s, dt_s, pcmk__str_casei)) {
+ exit_code = CRM_EX_ERROR;
+ goto done;
+ }
+ free(dt_s);
+ }
+
+done:
+ crm_time_free(date_time);
+ crm_time_free(duration);
+
+ g_strfreev(processed_args);
+ pcmk__free_arg_context(context);
+
+ free(options.date_time_s);
+ g_free(options.duration_s);
+ g_free(options.expected_s);
+ g_free(options.period_s);
+
+ pcmk__output_and_clear_error(&error, NULL);
+ crm_exit(exit_code);
+}