From 36bc6f8326de9d90ce3c91e7e02fccc9dfb4093d Mon Sep 17 00:00:00 2001 From: Timur Safin <tsafin@tarantool.org> Date: Tue, 24 May 2022 18:49:13 +0300 Subject: [PATCH] datetime: do not mess with nsec in interval Do not even try to make more readable output of secs/nsec, but rather report them as is, without any [de]normalization. Not the prior way: ``` tarantool> dt.interval.new{min=1, sec=59, nsec=2e9+1} -- - +1 minutes, 61.000000001 seconds ... ``` But instead as: ``` tarantool> dt.interval.new{min=1, sec=59, nsec=2e9+1} -- - +1 minutes, 59 seconds, 2000000001 nanoseconds ... ``` Closes #7045 NO_DOC=internal --- .../gh-7045-interval-nanoseconds.md | 17 +++++ src/lib/core/datetime.c | 74 +++---------------- test/app-tap/datetime.test.lua | 11 ++- test/sql-luatest/interval_test.lua | 2 +- 4 files changed, 37 insertions(+), 67 deletions(-) create mode 100644 changelogs/unreleased/gh-7045-interval-nanoseconds.md diff --git a/changelogs/unreleased/gh-7045-interval-nanoseconds.md b/changelogs/unreleased/gh-7045-interval-nanoseconds.md new file mode 100644 index 0000000000..9fdb28e3e4 --- /dev/null +++ b/changelogs/unreleased/gh-7045-interval-nanoseconds.md @@ -0,0 +1,17 @@ +## feature/datetime + +* Changed format of a string representation of date/time intervals. + There is no seconds/nano-seconds normalization done anymore, and + interval to be displayed as-is. + + Instead of former: +```lua + local ival = date.interval.new{year = 12345, hour = 48, min = 3, sec = 1, + nsec = 12345678} + print(tostring(ival)) -- '+12345 years, 48 hours, 3 minutes, '.. + -- '1.012345678 seconds' +``` +we now output +``` + -- '+12345 years, 48 hours, 3 minutes, 1 seconds, 12345678 nanoseconds' +``` diff --git a/src/lib/core/datetime.c b/src/lib/core/datetime.c index a9d9bd84aa..387db53f0e 100644 --- a/src/lib/core/datetime.c +++ b/src/lib/core/datetime.c @@ -558,51 +558,6 @@ datetime_totable(const struct datetime *date, struct interval *out) #define NANOS_PER_SEC 1000000000LL -static inline bool -zero_or_same_sign(int64_t sec, int64_t nsec) -{ - return sec == 0 || (sec < 0) == (nsec < 0); -} - -/** - * If |nsec| is larger than allowed range 1e9 then modify passed - * sec accordingly. - */ -static void -denormalize_interval_nsec(int64_t *psec, int *pnsec) -{ - assert(psec != NULL); - assert(pnsec != NULL); - int64_t sec = *psec; - int nsec = *pnsec; - /* - * There is nothing to change: - * - if there is a small nsec with 0 in sec; - * - or if both sec and nsec have the same sign, and nsec is - * small enough. - */ - if (nsec == 0) - return; - - if (zero_or_same_sign(sec, nsec) && abs(nsec) < NANOS_PER_SEC) - return; - - sec += DIV(nsec, NANOS_PER_SEC); - nsec = MOD(nsec, NANOS_PER_SEC); - - /* - * We crafted a positive nsec value, so convert it to negative, if secs - * are also negative. - */ - if (sec < 0) { - sec++; - nsec -= NANOS_PER_SEC; - } - - *psec = sec; - *pnsec = nsec; -} - #define SPACE() \ do { \ if (sz > 0) { \ @@ -621,7 +576,6 @@ interval_to_string(const struct interval *ival, char *buf, ssize_t len) "%d", /* false */ "%+d", /* true */ }; - static const char zero_secs[] = "0 seconds"; bool need_sign = true; size_t sz = 0; @@ -667,25 +621,21 @@ interval_to_string(const struct interval *ival, char *buf, ssize_t len) SNPRINT(sz, snprintf, buf, len, " minutes"); need_sign = false; } + int64_t secs = (int64_t)ival->sec; - int nsec = ival->nsec; - if (secs == 0 && nsec == 0) { - if (sz != 0) - return sz; - SNPRINT(sz, snprintf, buf, len, zero_secs); - return sizeof(zero_secs) - 1; + if (secs != 0 || sz == 0) { + SPACE(); + SNPRINT(sz, snprintf, buf, len, long_signed_fmt[need_sign], + secs); + SNPRINT(sz, snprintf, buf, len, " seconds"); + need_sign = false; } - SPACE(); - denormalize_interval_nsec(&secs, &nsec); - bool is_neg = secs < 0 || (secs == 0 && nsec < 0); - - SNPRINT(sz, snprintf, buf, len, is_neg ? "-" : (need_sign ? "+" : "")); - secs = labs(secs); + int32_t nsec = ival->nsec; if (nsec != 0) { - SNPRINT(sz, snprintf, buf, len, "%" PRId64 ".%09d seconds", - secs, abs(nsec)); - } else { - SNPRINT(sz, snprintf, buf, len, "%" PRId64 " seconds", secs); + SPACE(); + SNPRINT(sz, snprintf, buf, len, signed_fmt[need_sign], + nsec); + SNPRINT(sz, snprintf, buf, len, " nanoseconds"); } return sz; } diff --git a/test/app-tap/datetime.test.lua b/test/app-tap/datetime.test.lua index 0fa01b4b8f..8f0edd53a0 100755 --- a/test/app-tap/datetime.test.lua +++ b/test/app-tap/datetime.test.lua @@ -964,8 +964,9 @@ test:test("Time interval __index fields", function(test) local ival = date.interval.new{year = 12345, month = 123, week = 100, day = 45, hour = 48, min = 3, sec = 1, nsec = 12345678} - test:is(tostring(ival), '+12345 years, 123 months, 100 weeks, 45 days, 48 hours, '.. - '3 minutes, 1.012345678 seconds', '__tostring') + test:is(tostring(ival), '+12345 years, 123 months, 100 weeks, 45 days, '.. + '48 hours, 3 minutes, 1 seconds, 12345678 nanoseconds', + '__tostring') test:is(ival.nsec, 12345678, 'nsec') test:is(ival.usec, 12345, 'usec') @@ -1115,10 +1116,12 @@ test:test("Time interval operations", function(test) local i1, i2 i1 = ival_new{nsec = 1e9 - 1} i2 = ival_new{nsec = 2} - test:is(tostring(i1 + i2), '+1.000000001 seconds', 'nsec: 999999999 + 2') + test:is(tostring(i1 + i2), '+0 seconds, 1000000001 nanoseconds', + 'nsec: 999999999 + 2') i1 = ival_new{nsec = -1e9 + 1} i2 = ival_new{nsec = -2} - test:is(tostring(i1 + i2), '-1.000000001 seconds', 'nsec: -999999999 - 2') + test:is(tostring(i1 + i2), '+0 seconds, -1000000001 nanoseconds', + 'nsec: -999999999 - 2') local specific_errors = { {only_one_of, { nsec = 123456, usec = 123}}, diff --git a/test/sql-luatest/interval_test.lua b/test/sql-luatest/interval_test.lua index 456346cf81..cb316b51cc 100644 --- a/test/sql-luatest/interval_test.lua +++ b/test/sql-luatest/interval_test.lua @@ -667,7 +667,7 @@ g.test_interval_17_3 = function() local t = require('luatest') local sql = [[SELECT CAST(itv AS STRING) FROM t0;]] local res = {{'+1 years, 2 months, 3 days, 4 hours'}, - {'+5 minutes, 6.000000007 seconds'}} + {'+5 minutes, 6 seconds, 7 nanoseconds'}} local rows = box.execute(sql).rows t.assert_equals(rows, res) end) -- GitLab