From dfb8c3997e59f23727872d981b19ea59023a4ad5 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Thu, 3 Apr 2014 15:04:30 +0200 Subject: [PATCH] core/time: Added sdb_strfinterval(). This function formats a time interval using the format 1Y2M3D4h5m6.7s. --- src/core/time.c | 68 ++++++++++++++++++++++++++++ src/include/core/time.h | 16 +++++++ t/Makefile.am | 1 + t/core/time_test.c | 99 +++++++++++++++++++++++++++++++++++++++++ t/libsysdb_test.c | 1 + t/libsysdb_test.h | 4 ++ 6 files changed, 189 insertions(+) create mode 100644 t/core/time_test.c diff --git a/src/core/time.c b/src/core/time.c index 877feb1..fe57384 100644 --- a/src/core/time.c +++ b/src/core/time.c @@ -29,16 +29,28 @@ # include "config.h" #endif /* HAVE_CONFIG_H */ +#include "sysdb.h" #include "core/time.h" #include +#include #include /* * public API */ +/* 1 second (in micro-seconds) */ +#define SEC 1000000000L + +const sdb_time_t SDB_INTERVAL_YEAR = 3652425L * 24L * 60L * 60L * 100000L; +const sdb_time_t SDB_INTERVAL_MONTH = 30436875L * 24L * 60L * 60L * 1000L; +const sdb_time_t SDB_INTERVAL_DAY = 24L * 60L * 60L * SEC; +const sdb_time_t SDB_INTERVAL_HOUR = 60L * 60L * SEC; +const sdb_time_t SDB_INTERVAL_MINUTE = 60L * SEC; +const sdb_time_t SDB_INTERVAL_SECOND = SEC; + sdb_time_t sdb_gettime(void) { @@ -79,5 +91,61 @@ sdb_strftime(char *s, size_t len, const char *format, sdb_time_t t) return strftime(s, len, format, &tm); } /* sdb_strftime */ +size_t +sdb_strfinterval(char *s, size_t len, sdb_time_t interval) +{ + size_t n = 0; + size_t i; + + /* special case the optional fractional part for seconds */ + _Bool have_seconds = 0; + + struct { + sdb_time_t interval; + const char *suffix; + } specs[] = { + { SDB_INTERVAL_YEAR, "Y" }, + { SDB_INTERVAL_MONTH, "M" }, + { SDB_INTERVAL_DAY, "D" }, + { SDB_INTERVAL_HOUR, "h" }, + { SDB_INTERVAL_MINUTE, "m" }, + { SDB_INTERVAL_SECOND, "" }, + }; + +#define LEN (len > n ? len - n : 0) + for (i = 0; i < SDB_STATIC_ARRAY_LEN(specs); ++i) { + if (interval >= specs[i].interval) { + n += snprintf(s + n, LEN, "%"PRIscTIME"%s", + interval / specs[i].interval, specs[i].suffix); + interval %= specs[i].interval; + if (i == SDB_STATIC_ARRAY_LEN(specs) - 1) + have_seconds = 1; + } + } + + if (interval) { + n += snprintf(s + n, LEN, ".%09"PRIscTIME, interval); + have_seconds = 1; + + /* removing trailing zeroes */ + if (n <= len) + while (s[n - 1] == '0') + s[n--] = '\0'; + } + + if (! n) { + n = snprintf(s, len, "0"); + have_seconds = 1; + } + + if (have_seconds) + n += snprintf(s + n, LEN, "s"); +#undef LEN + + if (len) + s[len - 1] = '\0'; + return n; +} /* sdb_strfinterval */ + /* vim: set tw=78 sw=4 ts=4 noexpandtab : */ diff --git a/src/include/core/time.h b/src/include/core/time.h index 30e920b..f1a1dd5 100644 --- a/src/include/core/time.h +++ b/src/include/core/time.h @@ -54,6 +54,19 @@ typedef uint64_t sdb_time_t; #define TIMESPEC_TO_SDB_TIME(ts) (SECS_TO_SDB_TIME((ts).tv_sec) \ + NSECS_TO_SDB_TIME((ts).tv_nsec)) +/* + * Interval constants: + * Each constant specifies the time interval, in nano-seconds, of the named + * time-frame. Year, month, and day are approximations which do not work well + * for very large time intervals. + */ +extern const sdb_time_t SDB_INTERVAL_YEAR; +extern const sdb_time_t SDB_INTERVAL_MONTH; +extern const sdb_time_t SDB_INTERVAL_DAY; +extern const sdb_time_t SDB_INTERVAL_HOUR; +extern const sdb_time_t SDB_INTERVAL_MINUTE; +extern const sdb_time_t SDB_INTERVAL_SECOND; + sdb_time_t sdb_gettime(void); @@ -63,6 +76,9 @@ sdb_sleep(sdb_time_t reg, sdb_time_t *rem); size_t sdb_strftime(char *s, size_t len, const char *format, sdb_time_t); +size_t +sdb_strfinterval(char *s, size_t len, sdb_time_t interval); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/t/Makefile.am b/t/Makefile.am index 611b4a9..c63a2bb 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -14,6 +14,7 @@ libsysdb_test_SOURCES = \ core/object_test.c \ core/store_test.c \ core/store_lookup_test.c \ + core/time_test.c \ frontend/parser_test.c \ frontend/sock_test.c \ utils/channel_test.c \ diff --git a/t/core/time_test.c b/t/core/time_test.c new file mode 100644 index 0000000..f22e236 --- /dev/null +++ b/t/core/time_test.c @@ -0,0 +1,99 @@ +/* + * SysDB - t/core/time_test.c + * Copyright (C) 2014 Sebastian 'tokkee' Harl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core/time.h" +#include "libsysdb_test.h" + +#include + +START_TEST(test_strfinterval) +{ + char buf[1024]; + size_t check; + size_t i; + + struct { + sdb_time_t interval; + const char *expected; + } golden_data[] = { + { 0, "0s" }, + { 4711, ".000004711s" }, + { 1000123400, "1.0001234s" }, + { 47940228000000000L, "1Y6M7D" }, + { SDB_INTERVAL_YEAR, "1Y" }, + { SDB_INTERVAL_MONTH, "1M" }, + { SDB_INTERVAL_DAY, "1D" }, + { SDB_INTERVAL_HOUR, "1h" }, + { SDB_INTERVAL_MINUTE, "1m" }, + { SDB_INTERVAL_SECOND, "1s" }, + { SDB_INTERVAL_YEAR + + SDB_INTERVAL_MONTH + + SDB_INTERVAL_DAY + + SDB_INTERVAL_HOUR + + SDB_INTERVAL_MINUTE + + SDB_INTERVAL_SECOND + + 1234, "1Y1M1D1h1m1.000001234s" }, + }; + + /* this should return the number of bytes which would have been written; + * most notably, it should not segfault ;-) */ + check = sdb_strfinterval(NULL, 0, 4711); /* expected: .000004711s */ + fail_unless(check == 11, + "sdb_strfinterval(NULL, 0, 4711) = %zu; expected: 11", check); + + for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) { + check = sdb_strfinterval(buf, sizeof(buf), golden_data[i].interval); + fail_unless(check > 0, + "sdb_strfinterval(, , %"PRIscTIME") = %zu; " + "expected: >0", golden_data[i].interval, check); + fail_unless(!strcmp(buf, golden_data[i].expected), + "sdb_strfinterval(, , %"PRIscTIME") did not " + "format interval correctly; got: '%s'; expected: '%s'", + golden_data[i].interval, buf, golden_data[i].expected); + fail_unless(check == strlen(golden_data[i].expected), + "sdb_strfinterval(, , %"PRIscTIME") = %zu; " + "expected: %zu", golden_data[i].interval, check, + strlen(golden_data[i].expected)); + } +} +END_TEST + +Suite * +core_time_suite(void) +{ + Suite *s = suite_create("core::time"); + TCase *tc; + + tc = tcase_create("core"); + tcase_add_test(tc, test_strfinterval); + suite_add_tcase(s, tc); + + return s; +} /* core_time_suite */ + +/* vim: set tw=78 sw=4 ts=4 noexpandtab : */ + diff --git a/t/libsysdb_test.c b/t/libsysdb_test.c index 8e5f2f4..5b1b34d 100644 --- a/t/libsysdb_test.c +++ b/t/libsysdb_test.c @@ -46,6 +46,7 @@ main(void) { core_object_suite, NULL }, { core_store_suite, NULL }, { core_store_lookup_suite, NULL }, + { core_time_suite, NULL }, { fe_parser_suite, NULL }, { fe_sock_suite, NULL }, { util_channel_suite, NULL }, diff --git a/t/libsysdb_test.h b/t/libsysdb_test.h index 9d28fa0..3357260 100644 --- a/t/libsysdb_test.h +++ b/t/libsysdb_test.h @@ -73,6 +73,10 @@ core_store_suite(void); Suite * core_store_lookup_suite(void); +/* t/core/time_test */ +Suite * +core_time_suite(void); + /* t/frontend/parser_test */ Suite * fe_parser_suite(void); -- 2.30.2