Skip to content
Snippets Groups Projects
Commit 36bc6f83 authored by Timur Safin's avatar Timur Safin Committed by Kirill Yukhin
Browse files

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
parent 79245573
No related branches found
No related tags found
No related merge requests found
## 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'
```
...@@ -558,51 +558,6 @@ datetime_totable(const struct datetime *date, struct interval *out) ...@@ -558,51 +558,6 @@ datetime_totable(const struct datetime *date, struct interval *out)
#define NANOS_PER_SEC 1000000000LL #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() \ #define SPACE() \
do { \ do { \
if (sz > 0) { \ if (sz > 0) { \
...@@ -621,7 +576,6 @@ interval_to_string(const struct interval *ival, char *buf, ssize_t len) ...@@ -621,7 +576,6 @@ interval_to_string(const struct interval *ival, char *buf, ssize_t len)
"%d", /* false */ "%d", /* false */
"%+d", /* true */ "%+d", /* true */
}; };
static const char zero_secs[] = "0 seconds";
bool need_sign = true; bool need_sign = true;
size_t sz = 0; size_t sz = 0;
...@@ -667,25 +621,21 @@ interval_to_string(const struct interval *ival, char *buf, ssize_t len) ...@@ -667,25 +621,21 @@ interval_to_string(const struct interval *ival, char *buf, ssize_t len)
SNPRINT(sz, snprintf, buf, len, " minutes"); SNPRINT(sz, snprintf, buf, len, " minutes");
need_sign = false; need_sign = false;
} }
int64_t secs = (int64_t)ival->sec; int64_t secs = (int64_t)ival->sec;
int nsec = ival->nsec; if (secs != 0 || sz == 0) {
if (secs == 0 && nsec == 0) { SPACE();
if (sz != 0) SNPRINT(sz, snprintf, buf, len, long_signed_fmt[need_sign],
return sz; secs);
SNPRINT(sz, snprintf, buf, len, zero_secs); SNPRINT(sz, snprintf, buf, len, " seconds");
return sizeof(zero_secs) - 1; need_sign = false;
} }
SPACE(); int32_t nsec = ival->nsec;
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);
if (nsec != 0) { if (nsec != 0) {
SNPRINT(sz, snprintf, buf, len, "%" PRId64 ".%09d seconds", SPACE();
secs, abs(nsec)); SNPRINT(sz, snprintf, buf, len, signed_fmt[need_sign],
} else { nsec);
SNPRINT(sz, snprintf, buf, len, "%" PRId64 " seconds", secs); SNPRINT(sz, snprintf, buf, len, " nanoseconds");
} }
return sz; return sz;
} }
......
...@@ -964,8 +964,9 @@ test:test("Time interval __index fields", function(test) ...@@ -964,8 +964,9 @@ test:test("Time interval __index fields", function(test)
local ival = date.interval.new{year = 12345, month = 123, week = 100, local ival = date.interval.new{year = 12345, month = 123, week = 100,
day = 45, hour = 48, min = 3, sec = 1, day = 45, hour = 48, min = 3, sec = 1,
nsec = 12345678} nsec = 12345678}
test:is(tostring(ival), '+12345 years, 123 months, 100 weeks, 45 days, 48 hours, '.. test:is(tostring(ival), '+12345 years, 123 months, 100 weeks, 45 days, '..
'3 minutes, 1.012345678 seconds', '__tostring') '48 hours, 3 minutes, 1 seconds, 12345678 nanoseconds',
'__tostring')
test:is(ival.nsec, 12345678, 'nsec') test:is(ival.nsec, 12345678, 'nsec')
test:is(ival.usec, 12345, 'usec') test:is(ival.usec, 12345, 'usec')
...@@ -1115,10 +1116,12 @@ test:test("Time interval operations", function(test) ...@@ -1115,10 +1116,12 @@ test:test("Time interval operations", function(test)
local i1, i2 local i1, i2
i1 = ival_new{nsec = 1e9 - 1} i1 = ival_new{nsec = 1e9 - 1}
i2 = ival_new{nsec = 2} 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} i1 = ival_new{nsec = -1e9 + 1}
i2 = ival_new{nsec = -2} 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 = { local specific_errors = {
{only_one_of, { nsec = 123456, usec = 123}}, {only_one_of, { nsec = 123456, usec = 123}},
......
...@@ -667,7 +667,7 @@ g.test_interval_17_3 = function() ...@@ -667,7 +667,7 @@ g.test_interval_17_3 = function()
local t = require('luatest') local t = require('luatest')
local sql = [[SELECT CAST(itv AS STRING) FROM t0;]] local sql = [[SELECT CAST(itv AS STRING) FROM t0;]]
local res = {{'+1 years, 2 months, 3 days, 4 hours'}, 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 local rows = box.execute(sql).rows
t.assert_equals(rows, res) t.assert_equals(rows, res)
end) end)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment