diff --git a/lib/filterx/filterx-globals.c b/lib/filterx/filterx-globals.c index 923f2a3156b..f7b446afc62 100644 --- a/lib/filterx/filterx-globals.c +++ b/lib/filterx/filterx-globals.c @@ -76,6 +76,7 @@ filterx_builtin_functions_init(void) g_assert(filterx_builtin_function_register("bool", filterx_typecast_boolean)); g_assert(filterx_builtin_function_register("int", filterx_typecast_integer)); g_assert(filterx_builtin_function_register("double", filterx_typecast_double)); + g_assert(filterx_builtin_function_register("strptime", filterx_datetime_strptime)); } void diff --git a/lib/filterx/object-datetime.c b/lib/filterx/object-datetime.c index e3c766315a0..5d0a61fc3dc 100644 --- a/lib/filterx/object-datetime.c +++ b/lib/filterx/object-datetime.c @@ -200,6 +200,64 @@ _repr(FilterXObject *s, GString *repr) return TRUE; } +FilterXObject * +filterx_datetime_strptime(GPtrArray *args) +{ + if (args == NULL || args->len < 2) + { + msg_error("FilterX: Failed to create datetime object: invalid number of arguments. " + "Usage: strptime(time_str, format_str0, ..., format_strN)"); + return NULL; + } + + FilterXObject *object = g_ptr_array_index(args, 0); + if (!object || !filterx_object_is_type(object, &FILTERX_TYPE_NAME(string))) + { + msg_error("FilterX: Failed to create datetime object: bad argument. " + "Usage: strptime(time_str, format_str0, ..., format_strN)", + evt_tag_int("arg_pos", 0)); + return NULL; + } + const gchar *timestr = filterx_string_get_value(object, NULL); + + FilterXObject *result = NULL; + + WallClockTime wct = WALL_CLOCK_TIME_INIT; + UnixTime ut = UNIX_TIME_INIT; + gchar *end = NULL; + + for (int i = 1; i < args->len; i++) + { + FilterXObject *time_fmt_obj = g_ptr_array_index(args, i); + if (!time_fmt_obj || !filterx_object_is_type(time_fmt_obj, &FILTERX_TYPE_NAME(string))) + { + msg_error("FilterX: Failed to create datetime object: bad argument. " + "Usage: strptime(time_str, format_str0, ..., format_strN)", + evt_tag_int("arg_pos", i)); + return NULL; + } + + const gchar *time_fmt = filterx_string_get_value(time_fmt_obj, NULL); + end = wall_clock_time_strptime(&wct, time_fmt, timestr); + if (!end) + { + msg_error("filterx: unable to parse time", + evt_tag_str("time_string", timestr), + evt_tag_str("format", time_fmt)); + } + else + break; + } + + if (end) + { + convert_wall_clock_time_to_unix_time(&wct, &ut); + result = filterx_datetime_new(&ut); + } + + return result; +} + FILTERX_DEFINE_TYPE(datetime, FILTERX_TYPE_NAME(object), .truthy = _truthy, .map_to_json = _map_to_json, diff --git a/lib/filterx/object-datetime.h b/lib/filterx/object-datetime.h index 498fe070241..4f172a17fe2 100644 --- a/lib/filterx/object-datetime.h +++ b/lib/filterx/object-datetime.h @@ -33,5 +33,6 @@ FilterXObject *filterx_datetime_new(const UnixTime *ut); UnixTime filterx_datetime_get_value(FilterXObject *s); FilterXObject *filterx_typecast_datetime(GPtrArray *args); FilterXObject *filterx_typecast_datetime_isodate(GPtrArray *args); +FilterXObject *filterx_datetime_strptime(GPtrArray *args); #endif diff --git a/lib/filterx/tests/test_object_datetime.c b/lib/filterx/tests/test_object_datetime.c index 189ddb248ec..2e912d55f3c 100644 --- a/lib/filterx/tests/test_object_datetime.c +++ b/lib/filterx/tests/test_object_datetime.c @@ -217,6 +217,158 @@ Test(filterx_datetime, test_filterx_datetime_repr_isodate_Z) #undef test_time_str } +Test(filterx_datetime, test_filterx_datetime_strptime_with_null_args) +{ + FilterXObject *obj = filterx_datetime_strptime(NULL); + cr_assert_null(obj); + + filterx_object_unref(obj); +} + +Test(filterx_datetime, test_filterx_datetime_strptime_without_args) +{ + GPtrArray *args = g_ptr_array_new_with_free_func((GDestroyNotify) filterx_object_unref); + + FilterXObject *obj = filterx_datetime_strptime(args); + cr_assert_null(obj); + + g_ptr_array_free(args, TRUE); + filterx_object_unref(obj); +} + + +Test(filterx_datetime, test_filterx_datetime_strptime_without_timefmt) +{ +#define test_time_str "2024-04-08T10:11:12Z" + GPtrArray *args = g_ptr_array_new_with_free_func((GDestroyNotify) filterx_object_unref); + FilterXObject *in = filterx_string_new(test_time_str, -1); + g_ptr_array_add(args, in); + + FilterXObject *obj = filterx_datetime_strptime(args); + cr_assert_null(obj); + + g_ptr_array_free(args, TRUE); + filterx_object_unref(obj); +#undef test_time_str +} + +Test(filterx_datetime, test_filterx_datetime_strptime_non_matching_timefmt) +{ +#define test_time_str "2024-04-08T10:11:12Z" + GPtrArray *args = g_ptr_array_new_with_free_func((GDestroyNotify) filterx_object_unref); + FilterXObject *in = filterx_string_new(test_time_str, -1); + g_ptr_array_add(args, in); + + FilterXObject *time_fmt = filterx_string_new("non matching timefmt", -1); + g_ptr_array_add(args, time_fmt); + + FilterXObject *obj = filterx_datetime_strptime(args); + cr_assert_null(obj); + + g_ptr_array_free(args, TRUE); + filterx_object_unref(obj); +#undef test_time_str +} + +Test(filterx_datetime, test_filterx_datetime_strptime_matching_timefmt) +{ +#define test_time_str "2024-04-08T10:11:12Z" + GPtrArray *args = g_ptr_array_new_with_free_func((GDestroyNotify) filterx_object_unref); + FilterXObject *in = filterx_string_new(test_time_str, -1); + g_ptr_array_add(args, in); + + FilterXObject *time_fmt = filterx_string_new(datefmt_isodate, -1); + g_ptr_array_add(args, time_fmt); + + FilterXObject *obj = filterx_datetime_strptime(args); + cr_assert_not_null(obj); + cr_assert(filterx_object_is_type(obj, &FILTERX_TYPE_NAME(datetime))); + + GString *repr = scratch_buffers_alloc(); + + cr_assert(filterx_object_repr(in, repr)); + cr_assert_str_eq(test_time_str, repr->str); + + g_ptr_array_free(args, TRUE); + filterx_object_unref(obj); +#undef test_time_str +} + +Test(filterx_datetime, test_filterx_datetime_strptime_matching_nth_timefmt) +{ +#define test_time_str "2024-04-08T10:11:12+01:00" + GPtrArray *args = g_ptr_array_new_with_free_func((GDestroyNotify) filterx_object_unref); + FilterXObject *in = filterx_string_new(test_time_str, -1); + g_ptr_array_add(args, in); + + FilterXObject *bad_fmt1 = filterx_string_new("bad format 1", -1); + g_ptr_array_add(args, bad_fmt1); + + FilterXObject *bad_fmt2 = filterx_string_new("bad format 2", -1); + g_ptr_array_add(args, bad_fmt2); + + FilterXObject *time_fmt = filterx_string_new(datefmt_isodate, -1); + g_ptr_array_add(args, time_fmt); + + FilterXObject *obj = filterx_datetime_strptime(args); + cr_assert_not_null(obj); + cr_assert(filterx_object_is_type(obj, &FILTERX_TYPE_NAME(datetime))); + + GString *repr = scratch_buffers_alloc(); + + cr_assert(filterx_object_repr(in, repr)); + cr_assert_str_eq(test_time_str, repr->str); + + g_ptr_array_free(args, TRUE); + filterx_object_unref(obj); +#undef test_time_str +} + +Test(filterx_datetime, test_filterx_datetime_strptime_non_matching_nth_timefmt) +{ +#define test_time_str "2024-04-08T10:11:12Z" + GPtrArray *args = g_ptr_array_new_with_free_func((GDestroyNotify) filterx_object_unref); + FilterXObject *in = filterx_string_new(test_time_str, -1); + g_ptr_array_add(args, in); + + FilterXObject *bad_fmt1 = filterx_string_new("bad format 1", -1); + g_ptr_array_add(args, bad_fmt1); + + FilterXObject *bad_fmt2 = filterx_string_new("bad format 2", -1); + g_ptr_array_add(args, bad_fmt2); + + FilterXObject *time_fmt = filterx_string_new("non matching fmt", -1); + g_ptr_array_add(args, time_fmt); + + FilterXObject *obj = filterx_datetime_strptime(args); + cr_assert_null(obj); + + g_ptr_array_free(args, TRUE); + filterx_object_unref(obj); +#undef test_time_str +} + +Test(filterx_datetime, test_filterx_datetime_strptime_invalid_arg_type) +{ +#define test_time_str "2024-04-08T10:11:12Z" + GPtrArray *args = g_ptr_array_new_with_free_func((GDestroyNotify) filterx_object_unref); + FilterXObject *in = filterx_string_new(test_time_str, -1); + g_ptr_array_add(args, in); + + FilterXObject *bad_fmt1 = filterx_integer_new(1337); + g_ptr_array_add(args, bad_fmt1); + + FilterXObject *time_fmt = filterx_string_new(datefmt_isodate, -1); + g_ptr_array_add(args, time_fmt); + + FilterXObject *obj = filterx_datetime_strptime(args); + cr_assert_null(obj); + + g_ptr_array_free(args, TRUE); + filterx_object_unref(obj); +#undef test_time_str +} + static void setup(void) {