[Asterisk-code-review] time: Add timeval create and unit conversion functions (asterisk[18])

Kevin Harwell asteriskteam at digium.com
Mon Mar 22 15:32:29 CDT 2021


Kevin Harwell has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/15675 )


Change subject: time: Add timeval create and unit conversion functions
......................................................................

time: Add timeval create and unit conversion functions

Added a TIME_UNIT enumeration, and a function that converts a
string to one of the enumerated values. Also, added functions
that create and initialize a timeval object using a specified
value, and unit type.

Change-Id: Ic31a1c3262a44f77a5ef78bfc85dcf69a8d47392
---
M include/asterisk/time.h
A main/time.c
M tests/test_time.c
3 files changed, 311 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/75/15675/1

diff --git a/include/asterisk/time.h b/include/asterisk/time.h
index f49d689..e07203d 100644
--- a/include/asterisk/time.h
+++ b/include/asterisk/time.h
@@ -237,4 +237,74 @@
 }
 )
 
+/*!
+ * \brief Time units enumeration.
+ */
+enum TIME_UNIT {
+	TIME_UNIT_ERROR = -1,
+	TIME_UNIT_NANOSECOND,
+	TIME_UNIT_MICROSECOND,
+	TIME_UNIT_MILLISECOND,
+	TIME_UNIT_SECOND,
+	TIME_UNIT_MINUTE,
+	TIME_UNIT_HOUR,
+	TIME_UNIT_DAY,
+	TIME_UNIT_WEEK,
+	TIME_UNIT_MONTH,
+	TIME_UNIT_YEAR,
+};
+
+/*!
+ * \brief Convert a string to a time unit enumeration value.
+ *
+ * This method attempts to be as flexible, and forgiving as possible when
+ * converting. In most cases the algorithm will match on the beginning of
+ * up to three strings (short, medium, long form). So that means if the
+ * given string at least starts with one of the form values it will match.
+ *
+ * For example: mu, usec, microsecond will all map to TIME_UNIT_MICROSECOND.
+ * So will mus, usecs, miscroseconds, or even microsecondvals
+ *
+ * Matching is also not case sensitive.
+ *
+ * \param unit The string to map to an enumeration
+ *
+ * \return A time unit enumeration
+ */
+enum TIME_UNIT ast_time_str_to_unit(const char *unit);
+
+/*!
+ * \brief Create a timeval object initialized to given values.
+ *
+ * \param sec The timeval seconds value
+ * \param usec The timeval microseconds value
+ *
+ * \return A timeval object
+ */
+struct timeval ast_time_create(ast_time_t sec, ast_suseconds_t usec);
+
+/*!
+ * \brief Convert the given unit value, and create a timeval object from it.
+ *
+ * \param val The value to convert to a timeval
+ * \param unit The time unit type of val
+ *
+ * \return A timeval object
+ */
+struct timeval ast_time_create_by_unit(long val, enum TIME_UNIT unit);
+
+/*!
+ * \brief Convert the given unit value, and create a timeval object from it.
+ *
+ * This will first attempt to convert the unit from a string to a TIME_UNIT
+ * enumeration. If that conversion fails then a zeroed out timeval object
+ * is returned.
+ *
+ * \param val The value to convert to a timeval
+ * \param unit The time unit type of val
+ *
+ * \return A timeval object
+ */
+struct timeval ast_time_create_by_unit_str(long val, const char *unit);
+
 #endif /* _ASTERISK_TIME_H */
diff --git a/main/time.c b/main/time.c
new file mode 100644
index 0000000..33350da
--- /dev/null
+++ b/main/time.c
@@ -0,0 +1,127 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2021, Sangoma Technologies Corporation
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Date/Time utility functions
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include <inttypes.h>
+#include <string.h>
+#include <time.h>
+
+#include "asterisk/time.h"
+
+const char *nanosecond_labels[] = {"ns", "nsec", "nanosecond"};
+const char *microsecond_labels[] = {"mu", "usec", "microsecond"};
+const char *millisecond_labels[] = {"ms", "msec", "millisecond"};
+const char *second_labels[] = {"s", "sec", "second"};
+const char *minute_labels[] = {"m", "min", "minute"};
+const char *hour_labels[] = {"h", "hr", "hour"};
+const char *day_labels[] = {"d", "", "day"};
+const char *week_labels[] = {"w", "wk", "week"};
+const char *month_labels[] = {"mo", "mth", "month"};
+const char *year_labels[] = {"y", "yr", "year"};
+
+#define MAX_UNIT_LABELS 3
+
+struct time_unit_labels {
+	enum TIME_UNIT unit;
+	const char **values;
+};
+
+static struct time_unit_labels unit_labels[] = {
+	{ TIME_UNIT_NANOSECOND, nanosecond_labels },
+	{ TIME_UNIT_MICROSECOND, microsecond_labels },
+	{ TIME_UNIT_MILLISECOND, millisecond_labels },
+	{ TIME_UNIT_MONTH, month_labels }, /* Here so "mo" matches before "m" */
+	{ TIME_UNIT_SECOND, second_labels },
+	{ TIME_UNIT_MINUTE, minute_labels },
+	{ TIME_UNIT_HOUR, hour_labels },
+	{ TIME_UNIT_DAY, day_labels },
+	{ TIME_UNIT_WEEK, week_labels },
+	{ TIME_UNIT_YEAR, year_labels },
+};
+
+const unsigned int unit_labels_size = sizeof(unit_labels) / sizeof(0[unit_labels]);
+
+enum TIME_UNIT ast_time_str_to_unit(const char *unit)
+{
+	size_t i, j;
+
+	if (!unit) {
+		return TIME_UNIT_ERROR;
+	}
+
+	for (i = 0; i < unit_labels_size; ++i) {
+		for (j = 0; j < MAX_UNIT_LABELS; ++j) {
+			/*
+			 * A lazy pluralization check. If the given unit string at least starts
+			 * with a label assume a match.
+			 */
+			if (*unit_labels[i].values[j] && !strncasecmp(unit, unit_labels[i].values[j],
+					strlen(unit_labels[i].values[j]))) {
+				return unit_labels[i].unit;
+			}
+		}
+	}
+
+	return TIME_UNIT_ERROR;
+}
+
+struct timeval ast_time_create(ast_time_t sec, ast_suseconds_t usec)
+{
+	return ast_tv(sec, usec);
+}
+
+struct timeval ast_time_create_by_unit(long val, enum TIME_UNIT unit)
+{
+	switch (unit) {
+	case TIME_UNIT_NANOSECOND:
+		return ast_time_create(0, val / 1000);
+	case TIME_UNIT_MICROSECOND:
+		return ast_time_create(0, val);
+	case TIME_UNIT_MILLISECOND:
+		return ast_time_create(0, val * 1000);
+	case TIME_UNIT_SECOND:
+		return ast_time_create(val, 0);
+	case TIME_UNIT_MINUTE:
+		return ast_time_create(val * 60, 0);
+	case TIME_UNIT_HOUR:
+		return ast_time_create(val * 3600, 0);
+	case TIME_UNIT_DAY:
+		return ast_time_create(val * 86400, 0);
+	case TIME_UNIT_WEEK:
+		return ast_time_create(val * 604800, 0);
+	case TIME_UNIT_MONTH:
+		/* Using Gregorian mean month - 30.436875 * 86400 */
+		return ast_time_create(val * 2629746, 0);
+	case TIME_UNIT_YEAR:
+		/* Using Gregorian year - 365.2425 * 86400 */
+		return ast_time_create(val * 31556952, 0);
+	default:
+		return ast_time_create(0, 0);
+	}
+}
+
+struct timeval ast_time_create_by_unit_str(long val, const char *unit)
+{
+	return ast_time_create_by_unit(val, ast_time_str_to_unit(unit));
+}
diff --git a/tests/test_time.c b/tests/test_time.c
index b58a473..f78fbc1 100644
--- a/tests/test_time.c
+++ b/tests/test_time.c
@@ -111,8 +111,119 @@
 	return res;
 }
 
+AST_TEST_DEFINE(test_time_str_to_unit)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "time_str_to_unit";
+		info->category = "/main/stdtime/";
+		info->summary = "Verify string to time unit conversions";
+		info->description = info->summary;
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	/* Nominal */
+	ast_test_validate(test, ast_time_str_to_unit("ns") == TIME_UNIT_NANOSECOND);
+	ast_test_validate(test, ast_time_str_to_unit("mu") == TIME_UNIT_MICROSECOND);
+	ast_test_validate(test, ast_time_str_to_unit("ms") == TIME_UNIT_MILLISECOND);
+	ast_test_validate(test, ast_time_str_to_unit("s") == TIME_UNIT_SECOND);
+	ast_test_validate(test, ast_time_str_to_unit("m") == TIME_UNIT_MINUTE);
+	ast_test_validate(test, ast_time_str_to_unit("h") == TIME_UNIT_HOUR);
+	ast_test_validate(test, ast_time_str_to_unit("d") == TIME_UNIT_DAY);
+	ast_test_validate(test, ast_time_str_to_unit("w") == TIME_UNIT_WEEK);
+	ast_test_validate(test, ast_time_str_to_unit("mo") == TIME_UNIT_MONTH);
+	ast_test_validate(test, ast_time_str_to_unit("y") == TIME_UNIT_YEAR);
+
+	/* Plural */
+	ast_test_validate(test, ast_time_str_to_unit("nanoseconds") == TIME_UNIT_NANOSECOND);
+	ast_test_validate(test, ast_time_str_to_unit("microseconds") == TIME_UNIT_MICROSECOND);
+	ast_test_validate(test, ast_time_str_to_unit("milliseconds") == TIME_UNIT_MILLISECOND);
+	ast_test_validate(test, ast_time_str_to_unit("seconds") == TIME_UNIT_SECOND);
+	ast_test_validate(test, ast_time_str_to_unit("minutes") == TIME_UNIT_MINUTE);
+	ast_test_validate(test, ast_time_str_to_unit("hours") == TIME_UNIT_HOUR);
+	ast_test_validate(test, ast_time_str_to_unit("days") == TIME_UNIT_DAY);
+	ast_test_validate(test, ast_time_str_to_unit("weeks") == TIME_UNIT_WEEK);
+	ast_test_validate(test, ast_time_str_to_unit("months") == TIME_UNIT_MONTH);
+	ast_test_validate(test, ast_time_str_to_unit("years") == TIME_UNIT_YEAR);
+
+	/* Case */
+	ast_test_validate(test, ast_time_str_to_unit("Nsec") == TIME_UNIT_NANOSECOND);
+	ast_test_validate(test, ast_time_str_to_unit("Usec") == TIME_UNIT_MICROSECOND);
+	ast_test_validate(test, ast_time_str_to_unit("Msec") == TIME_UNIT_MILLISECOND);
+	ast_test_validate(test, ast_time_str_to_unit("Sec") == TIME_UNIT_SECOND);
+	ast_test_validate(test, ast_time_str_to_unit("Min") == TIME_UNIT_MINUTE);
+	ast_test_validate(test, ast_time_str_to_unit("Hr") == TIME_UNIT_HOUR);
+	ast_test_validate(test, ast_time_str_to_unit("Day") == TIME_UNIT_DAY);
+	ast_test_validate(test, ast_time_str_to_unit("Wk") == TIME_UNIT_WEEK);
+	ast_test_validate(test, ast_time_str_to_unit("Mth") == TIME_UNIT_MONTH);
+	ast_test_validate(test, ast_time_str_to_unit("Yr") == TIME_UNIT_YEAR);
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_time_create_by_unit)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "time_create_by_unit";
+		info->category = "/main/stdtime/";
+		info->summary = "Verify unit value to timeval conversions";
+		info->description = info->summary;
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	/* Nominal */
+	ast_test_validate(test, ast_time_create_by_unit(1000, TIME_UNIT_NANOSECOND).tv_usec == 1);
+	ast_test_validate(test, ast_time_create_by_unit(1, TIME_UNIT_MICROSECOND).tv_usec == 1);
+	ast_test_validate(test, ast_time_create_by_unit(1, TIME_UNIT_MILLISECOND).tv_usec == 1000);
+	ast_test_validate(test, ast_time_create_by_unit(1, TIME_UNIT_SECOND).tv_sec == 1);
+	ast_test_validate(test, ast_time_create_by_unit(1, TIME_UNIT_MINUTE).tv_sec == 60);
+	ast_test_validate(test, ast_time_create_by_unit(1, TIME_UNIT_HOUR).tv_sec == 3600);
+	ast_test_validate(test, ast_time_create_by_unit(1, TIME_UNIT_DAY).tv_sec == 86400);
+	ast_test_validate(test, ast_time_create_by_unit(1, TIME_UNIT_WEEK).tv_sec == 604800);
+	ast_test_validate(test, ast_time_create_by_unit(1, TIME_UNIT_MONTH).tv_sec == 2629746);
+	ast_test_validate(test, ast_time_create_by_unit(1, TIME_UNIT_YEAR).tv_sec == 31556952);
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_time_create_by_unit_str)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "time_create_by_unit_str";
+		info->category = "/main/stdtime/";
+		info->summary = "Verify value with unit as a string to timeval conversions";
+		info->description = info->summary;
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	/* Nominal */
+	ast_test_validate(test, ast_time_create_by_unit_str(1000, "ns").tv_usec == 1);
+	ast_test_validate(test, ast_time_create_by_unit_str(1, "mu").tv_usec == 1);
+	ast_test_validate(test, ast_time_create_by_unit_str(1, "ms").tv_usec == 1000);
+	ast_test_validate(test, ast_time_create_by_unit_str(1, "s").tv_sec == 1);
+	ast_test_validate(test, ast_time_create_by_unit_str(1, "m").tv_sec == 60);
+	ast_test_validate(test, ast_time_create_by_unit_str(1, "h").tv_sec == 3600);
+	ast_test_validate(test, ast_time_create_by_unit_str(1, "d").tv_sec == 86400);
+	ast_test_validate(test, ast_time_create_by_unit_str(1, "w").tv_sec == 604800);
+	ast_test_validate(test, ast_time_create_by_unit_str(1, "mo").tv_sec == 2629746);
+	ast_test_validate(test, ast_time_create_by_unit_str(1, "yr").tv_sec == 31556952);
+
+	return AST_TEST_PASS;
+}
+
 static int unload_module(void)
 {
+	AST_TEST_UNREGISTER(test_time_create_by_unit_str);
+	AST_TEST_UNREGISTER(test_time_create_by_unit);
+	AST_TEST_UNREGISTER(test_time_str_to_unit);
 	AST_TEST_UNREGISTER(test_timezone_watch);
 	return 0;
 }
@@ -120,6 +231,9 @@
 static int load_module(void)
 {
 	AST_TEST_REGISTER(test_timezone_watch);
+	AST_TEST_REGISTER(test_time_str_to_unit);
+	AST_TEST_REGISTER(test_time_create_by_unit);
+	AST_TEST_REGISTER(test_time_create_by_unit_str);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/15675
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: 18
Gerrit-Change-Id: Ic31a1c3262a44f77a5ef78bfc85dcf69a8d47392
Gerrit-Change-Number: 15675
Gerrit-PatchSet: 1
Gerrit-Owner: Kevin Harwell <kharwell at digium.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20210322/303b4f0c/attachment-0001.html>


More information about the asterisk-code-review mailing list