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
This commit is contained in:
parent
72fb6fd757
commit
20af6d23df
|
@ -237,4 +237,83 @@ struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate),
|
|||
}
|
||||
)
|
||||
|
||||
/*!
|
||||
* \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: us, usec, microsecond will all map to TIME_UNIT_MICROSECOND.
|
||||
* So will uss, 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 Convert a timeval structure to microseconds
|
||||
*
|
||||
* \param tv The timeval to convert
|
||||
*
|
||||
* \return The time in microseconds
|
||||
*/
|
||||
ast_suseconds_t ast_time_tv_to_usec(const struct timeval *tv);
|
||||
|
||||
/*!
|
||||
* \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(unsigned 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(unsigned long val, const char *unit);
|
||||
|
||||
#endif /* _ASTERISK_TIME_H */
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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[] = {"us", "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;
|
||||
}
|
||||
|
||||
ast_suseconds_t ast_time_tv_to_usec(const struct timeval *tv)
|
||||
{
|
||||
return tv->tv_sec * 1000000 + tv->tv_usec;
|
||||
}
|
||||
|
||||
struct timeval ast_time_create(ast_time_t sec, ast_suseconds_t usec)
|
||||
{
|
||||
return ast_tv(sec, usec);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Create a timeval first onverting the given microsecond value
|
||||
* into seconds and microseconds
|
||||
*
|
||||
* \param usec microsecond value
|
||||
*
|
||||
* \return A timeval structure
|
||||
*/
|
||||
static struct timeval normalize_and_create(unsigned long usec)
|
||||
{
|
||||
return ast_time_create(usec / 1000000, usec % 1000000);
|
||||
}
|
||||
|
||||
struct timeval ast_time_create_by_unit(unsigned long val, enum TIME_UNIT unit)
|
||||
{
|
||||
switch (unit) {
|
||||
case TIME_UNIT_NANOSECOND:
|
||||
return normalize_and_create(val / 1000);
|
||||
case TIME_UNIT_MICROSECOND:
|
||||
return normalize_and_create(val);
|
||||
case TIME_UNIT_MILLISECOND:
|
||||
return normalize_and_create(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(unsigned long val, const char *unit)
|
||||
{
|
||||
return ast_time_create_by_unit(val, ast_time_str_to_unit(unit));
|
||||
}
|
|
@ -111,8 +111,174 @@ AST_TEST_DEFINE(test_timezone_watch)
|
|||
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("us") == 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)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
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);
|
||||
|
||||
/* timeval normalization */
|
||||
tv = ast_time_create_by_unit(1500000000, TIME_UNIT_NANOSECOND);
|
||||
ast_test_validate(test, tv.tv_sec == 1 && tv.tv_usec == 500000);
|
||||
|
||||
tv = ast_time_create_by_unit(1500000, TIME_UNIT_MICROSECOND);
|
||||
ast_test_validate(test, tv.tv_sec == 1 && tv.tv_usec == 500000);
|
||||
|
||||
tv = ast_time_create_by_unit(1500, TIME_UNIT_MILLISECOND);
|
||||
ast_test_validate(test, tv.tv_sec == 1 && tv.tv_usec == 500000);
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(test_time_create_by_unit_str)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
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, "us").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);
|
||||
|
||||
/* timeval normalization */
|
||||
tv = ast_time_create_by_unit_str(1500000000, "ns");
|
||||
ast_test_validate(test, tv.tv_sec == 1 && tv.tv_usec == 500000);
|
||||
|
||||
tv = ast_time_create_by_unit_str(1500000, "us");
|
||||
ast_test_validate(test, tv.tv_sec == 1 && tv.tv_usec == 500000);
|
||||
|
||||
tv = ast_time_create_by_unit_str(1500, "ms");
|
||||
ast_test_validate(test, tv.tv_sec == 1 && tv.tv_usec == 500000);
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(test_time_tv_to_usec)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "time_tv_to_usec";
|
||||
info->category = "/main/stdtime/";
|
||||
info->summary = "Verify conversion of a timeval structure to microseconds";
|
||||
info->description = info->summary;
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
tv = ast_time_create(0, 0);
|
||||
ast_test_validate(test, ast_time_tv_to_usec(&tv) == 0);
|
||||
|
||||
tv = ast_time_create(0, 1);
|
||||
ast_test_validate(test, ast_time_tv_to_usec(&tv) == 1);
|
||||
|
||||
tv = ast_time_create(1, 0);
|
||||
ast_test_validate(test, ast_time_tv_to_usec(&tv) == 1000000);
|
||||
|
||||
tv = ast_time_create(1, 1);
|
||||
ast_test_validate(test, ast_time_tv_to_usec(&tv) == 1000001);
|
||||
|
||||
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_time_tv_to_usec);
|
||||
AST_TEST_UNREGISTER(test_timezone_watch);
|
||||
return 0;
|
||||
}
|
||||
|
@ -120,6 +286,10 @@ static int unload_module(void)
|
|||
static int load_module(void)
|
||||
{
|
||||
AST_TEST_REGISTER(test_timezone_watch);
|
||||
AST_TEST_REGISTER(test_time_tv_to_usec);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue