[Tarantool-patches] [PATCH v5 3/8] lua, datetime: display datetime
Serge Petrenko
sergepetrenko at tarantool.org
Tue Aug 17 15:15:41 MSK 2021
16.08.2021 02:59, Timur Safin via Tarantool-patches пишет:
> * introduced output routine for converting datetime
> to their default output format.
>
> * use this routine for tostring() in datetime.lua
>
> Part of #5941
> ---
> extra/exports | 1 +
> src/lib/core/datetime.c | 71 ++++++++++++++++++
> src/lib/core/datetime.h | 9 +++
> src/lua/datetime.lua | 35 +++++++++
> test/app-tap/datetime.test.lua | 131 ++++++++++++++++++---------------
> test/unit/CMakeLists.txt | 2 +-
> test/unit/datetime.c | 61 +++++++++++----
> 7 files changed, 236 insertions(+), 74 deletions(-)
>
> diff --git a/extra/exports b/extra/exports
> index 80eb92abd..2437e175c 100644
> --- a/extra/exports
> +++ b/extra/exports
> @@ -152,6 +152,7 @@ datetime_asctime
> datetime_ctime
> datetime_now
> datetime_strftime
> +datetime_to_string
> decimal_unpack
> decimal_from_string
> decimal_unpack
> diff --git a/src/lib/core/datetime.c b/src/lib/core/datetime.c
> index c48295a6f..c24a0df82 100644
> --- a/src/lib/core/datetime.c
> +++ b/src/lib/core/datetime.c
> @@ -29,6 +29,8 @@
> * SUCH DAMAGE.
> */
>
> +#include <assert.h>
> +#include <limits.h>
> #include <string.h>
> #include <time.h>
>
> @@ -94,3 +96,72 @@ datetime_strftime(const struct datetime *date, const char *fmt, char *buf,
> struct tm *p_tm = datetime_to_tm(date);
> return strftime(buf, len, fmt, p_tm);
> }
> +
> +#define SECS_EPOCH_1970_OFFSET ((int64_t)DT_EPOCH_1970_OFFSET * SECS_PER_DAY)
> +
> +/* NB! buf may be NULL, and we should handle it gracefully, returning
> + * calculated length of output string
> + */
> +int
> +datetime_to_string(const struct datetime *date, char *buf, uint32_t len)
> +{
> +#define ADVANCE(sz) \
> + if (buf != NULL) { \
> + buf += sz; \
> + len -= sz; \
> + } \
> + ret += sz;
> +
> + int offset = date->offset;
> + /* for negative offsets around Epoch date we could get
> + * negative secs value, which should be attributed to
> + * 1969-12-31, not 1970-01-01, thus we first shift
> + * epoch to Rata Die then divide by seconds per day,
> + * not in reverse
> + */
> + int64_t secs = (int64_t)date->secs + offset * 60 + SECS_EPOCH_1970_OFFSET;
> + assert((secs / SECS_PER_DAY) <= INT_MAX);
> + dt_t dt = dt_from_rdn(secs / SECS_PER_DAY);
> +
> + int year, month, day, sec, ns, sign;
> + dt_to_ymd(dt, &year, &month, &day);
> +
> + int hour = (secs / 3600) % 24,
> + minute = (secs / 60) % 60;
> + sec = secs % 60;
> + ns = date->nsec;
> +
> + int ret = 0;
> + uint32_t sz = snprintf(buf, len, "%04d-%02d-%02dT%02d:%02d",
> + year, month, day, hour, minute);
> + ADVANCE(sz);
Please, replace snprintf + ADVANCE() with SNPRINT macro
from src/trivia/util.h
It does exactly what you need.
> + if (sec || ns) {
> + sz = snprintf(buf, len, ":%02d", sec);
> + ADVANCE(sz);
> + if (ns) {
> + if ((ns % 1000000) == 0)
> + sz = snprintf(buf, len, ".%03d", ns / 1000000);
> + else if ((ns % 1000) == 0)
> + sz = snprintf(buf, len, ".%06d", ns / 1000);
> + else
> + sz = snprintf(buf, len, ".%09d", ns);
> + ADVANCE(sz);
> + }
> + }
> + if (offset == 0) {
> + sz = snprintf(buf, len, "Z");
> + ADVANCE(sz);
> + }
> + else {
> + if (offset < 0)
> + sign = '-', offset = -offset;
> + else
> + sign = '+';
> +
> + sz = snprintf(buf, len, "%c%02d:%02d", sign, offset / 60, offset % 60);
> + ADVANCE(sz);
> + }
> + return ret;
> +}
> +#undef ADVANCE
> +
<stripped>
> diff --git a/test/app-tap/datetime.test.lua b/test/app-tap/datetime.test.lua
> index 464d4bd49..244ec2575 100755
> --- a/test/app-tap/datetime.test.lua
> +++ b/test/app-tap/datetime.test.lua
> @@ -6,7 +6,7 @@ local date = require('datetime')
> local ffi = require('ffi')
>
>
> -test:plan(6)
> +test:plan(7)
>
> test:test("Simple tests for parser", function(test)
> test:plan(2)
> @@ -17,74 +17,78 @@ test:test("Simple tests for parser", function(test)
> end)
>
> test:test("Multiple tests for parser (with nanoseconds)", function(test)
> - test:plan(168)
> + test:plan(193)
> -- borrowed from p5-time-moments/t/180_from_string.t
> local tests =
> {
> - { '1970-01-01T00:00:00Z', 0, 0, 0 },
> - { '1970-01-01T02:00:00+02:00', 0, 0, 120 },
> - { '1970-01-01T01:30:00+01:30', 0, 0, 90 },
> - { '1970-01-01T01:00:00+01:00', 0, 0, 60 },
> - { '1970-01-01T00:01:00+00:01', 0, 0, 1 },
> - { '1970-01-01T00:00:00+00:00', 0, 0, 0 },
> - { '1969-12-31T23:59:00-00:01', 0, 0, -1 },
> - { '1969-12-31T23:00:00-01:00', 0, 0, -60 },
> - { '1969-12-31T22:30:00-01:30', 0, 0, -90 },
> - { '1969-12-31T22:00:00-02:00', 0, 0, -120 },
> - { '1970-01-01T00:00:00.123456789Z', 0, 123456789, 0 },
> - { '1970-01-01T00:00:00.12345678Z', 0, 123456780, 0 },
> - { '1970-01-01T00:00:00.1234567Z', 0, 123456700, 0 },
> - { '1970-01-01T00:00:00.123456Z', 0, 123456000, 0 },
> - { '1970-01-01T00:00:00.12345Z', 0, 123450000, 0 },
> - { '1970-01-01T00:00:00.1234Z', 0, 123400000, 0 },
> - { '1970-01-01T00:00:00.123Z', 0, 123000000, 0 },
> - { '1970-01-01T00:00:00.12Z', 0, 120000000, 0 },
> - { '1970-01-01T00:00:00.1Z', 0, 100000000, 0 },
> - { '1970-01-01T00:00:00.01Z', 0, 10000000, 0 },
> - { '1970-01-01T00:00:00.001Z', 0, 1000000, 0 },
> - { '1970-01-01T00:00:00.0001Z', 0, 100000, 0 },
> - { '1970-01-01T00:00:00.00001Z', 0, 10000, 0 },
> - { '1970-01-01T00:00:00.000001Z', 0, 1000, 0 },
> - { '1970-01-01T00:00:00.0000001Z', 0, 100, 0 },
> - { '1970-01-01T00:00:00.00000001Z', 0, 10, 0 },
> - { '1970-01-01T00:00:00.000000001Z', 0, 1, 0 },
> - { '1970-01-01T00:00:00.000000009Z', 0, 9, 0 },
> - { '1970-01-01T00:00:00.00000009Z', 0, 90, 0 },
> - { '1970-01-01T00:00:00.0000009Z', 0, 900, 0 },
> - { '1970-01-01T00:00:00.000009Z', 0, 9000, 0 },
> - { '1970-01-01T00:00:00.00009Z', 0, 90000, 0 },
> - { '1970-01-01T00:00:00.0009Z', 0, 900000, 0 },
> - { '1970-01-01T00:00:00.009Z', 0, 9000000, 0 },
> - { '1970-01-01T00:00:00.09Z', 0, 90000000, 0 },
> - { '1970-01-01T00:00:00.9Z', 0, 900000000, 0 },
> - { '1970-01-01T00:00:00.99Z', 0, 990000000, 0 },
> - { '1970-01-01T00:00:00.999Z', 0, 999000000, 0 },
> - { '1970-01-01T00:00:00.9999Z', 0, 999900000, 0 },
> - { '1970-01-01T00:00:00.99999Z', 0, 999990000, 0 },
> - { '1970-01-01T00:00:00.999999Z', 0, 999999000, 0 },
> - { '1970-01-01T00:00:00.9999999Z', 0, 999999900, 0 },
> - { '1970-01-01T00:00:00.99999999Z', 0, 999999990, 0 },
> - { '1970-01-01T00:00:00.999999999Z', 0, 999999999, 0 },
> - { '1970-01-01T00:00:00.0Z', 0, 0, 0 },
> - { '1970-01-01T00:00:00.00Z', 0, 0, 0 },
> - { '1970-01-01T00:00:00.000Z', 0, 0, 0 },
> - { '1970-01-01T00:00:00.0000Z', 0, 0, 0 },
> - { '1970-01-01T00:00:00.00000Z', 0, 0, 0 },
> - { '1970-01-01T00:00:00.000000Z', 0, 0, 0 },
> - { '1970-01-01T00:00:00.0000000Z', 0, 0, 0 },
> - { '1970-01-01T00:00:00.00000000Z', 0, 0, 0 },
> - { '1970-01-01T00:00:00.000000000Z', 0, 0, 0 },
> - { '1973-11-29T21:33:09Z', 123456789, 0, 0 },
> - { '2013-10-28T17:51:56Z', 1382982716, 0, 0 },
> - { '9999-12-31T23:59:59Z', 253402300799, 0, 0 },
> + {'1970-01-01T00:00Z', 0, 0, 0, 1},
> + {'1970-01-01T02:00+02:00', 0, 0, 120, 1},
> + {'1970-01-01T01:30+01:30', 0, 0, 90, 1},
> + {'1970-01-01T01:00+01:00', 0, 0, 60, 1},
> + {'1970-01-01T00:01+00:01', 0, 0, 1, 1},
> + {'1970-01-01T00:00Z', 0, 0, 0, 1},
> + {'1969-12-31T23:59-00:01', 0, 0, -1, 1},
> + {'1969-12-31T23:00-01:00', 0, 0, -60, 1},
> + {'1969-12-31T22:30-01:30', 0, 0, -90, 1},
> + {'1969-12-31T22:00-02:00', 0, 0, -120, 1},
> + {'1970-01-01T00:00:00.123456789Z', 0, 123456789, 0, 1},
> + {'1970-01-01T00:00:00.12345678Z', 0, 123456780, 0, 0},
> + {'1970-01-01T00:00:00.1234567Z', 0, 123456700, 0, 0},
> + {'1970-01-01T00:00:00.123456Z', 0, 123456000, 0, 1},
> + {'1970-01-01T00:00:00.12345Z', 0, 123450000, 0, 0},
> + {'1970-01-01T00:00:00.1234Z', 0, 123400000, 0, 0},
> + {'1970-01-01T00:00:00.123Z', 0, 123000000, 0, 1},
> + {'1970-01-01T00:00:00.12Z', 0, 120000000, 0, 0},
> + {'1970-01-01T00:00:00.1Z', 0, 100000000, 0, 0},
> + {'1970-01-01T00:00:00.01Z', 0, 10000000, 0, 0},
> + {'1970-01-01T00:00:00.001Z', 0, 1000000, 0, 1},
> + {'1970-01-01T00:00:00.0001Z', 0, 100000, 0, 0},
> + {'1970-01-01T00:00:00.00001Z', 0, 10000, 0, 0},
> + {'1970-01-01T00:00:00.000001Z', 0, 1000, 0, 1},
> + {'1970-01-01T00:00:00.0000001Z', 0, 100, 0, 0},
> + {'1970-01-01T00:00:00.00000001Z', 0, 10, 0, 0},
> + {'1970-01-01T00:00:00.000000001Z', 0, 1, 0, 1},
> + {'1970-01-01T00:00:00.000000009Z', 0, 9, 0, 1},
> + {'1970-01-01T00:00:00.00000009Z', 0, 90, 0, 0},
> + {'1970-01-01T00:00:00.0000009Z', 0, 900, 0, 0},
> + {'1970-01-01T00:00:00.000009Z', 0, 9000, 0, 1},
> + {'1970-01-01T00:00:00.00009Z', 0, 90000, 0, 0},
> + {'1970-01-01T00:00:00.0009Z', 0, 900000, 0, 0},
> + {'1970-01-01T00:00:00.009Z', 0, 9000000, 0, 1},
> + {'1970-01-01T00:00:00.09Z', 0, 90000000, 0, 0},
> + {'1970-01-01T00:00:00.9Z', 0, 900000000, 0, 0},
> + {'1970-01-01T00:00:00.99Z', 0, 990000000, 0, 0},
> + {'1970-01-01T00:00:00.999Z', 0, 999000000, 0, 1},
> + {'1970-01-01T00:00:00.9999Z', 0, 999900000, 0, 0},
> + {'1970-01-01T00:00:00.99999Z', 0, 999990000, 0, 0},
> + {'1970-01-01T00:00:00.999999Z', 0, 999999000, 0, 1},
> + {'1970-01-01T00:00:00.9999999Z', 0, 999999900, 0, 0},
> + {'1970-01-01T00:00:00.99999999Z', 0, 999999990, 0, 0},
> + {'1970-01-01T00:00:00.999999999Z', 0, 999999999, 0, 1},
> + {'1970-01-01T00:00:00.0Z', 0, 0, 0, 0},
> + {'1970-01-01T00:00:00.00Z', 0, 0, 0, 0},
> + {'1970-01-01T00:00:00.000Z', 0, 0, 0, 0},
> + {'1970-01-01T00:00:00.0000Z', 0, 0, 0, 0},
> + {'1970-01-01T00:00:00.00000Z', 0, 0, 0, 0},
> + {'1970-01-01T00:00:00.000000Z', 0, 0, 0, 0},
> + {'1970-01-01T00:00:00.0000000Z', 0, 0, 0, 0},
> + {'1970-01-01T00:00:00.00000000Z', 0, 0, 0, 0},
> + {'1970-01-01T00:00:00.000000000Z', 0, 0, 0, 0},
> + {'1973-11-29T21:33:09Z', 123456789, 0, 0, 1},
> + {'2013-10-28T17:51:56Z', 1382982716, 0, 0, 1},
> + {'9999-12-31T23:59:59Z', 253402300799, 0, 0, 1},
Please, squash this change into the previous commit.
> }
> for _, value in ipairs(tests) do
> - local str, epoch, nsec, offset
> - str, epoch, nsec, offset = unpack(value)
> + local str, epoch, nsec, offset, check
> + str, epoch, nsec, offset, check = unpack(value)
> local dt = date(str)
> test:ok(dt.secs == epoch, ('%s: dt.secs == %d'):format(str, epoch))
> test:ok(dt.nsec == nsec, ('%s: dt.nsec == %d'):format(str, nsec))
> test:ok(dt.offset == offset, ('%s: dt.offset == %d'):format(str, offset))
> + if check > 0 then
> + test:ok(str == tostring(dt), ('%s == tostring(%s)'):
> + format(str, tostring(dt)))
> + end
> end
> end)
>
> @@ -203,4 +207,11 @@ test:test("Parse tiny date into seconds and other parts", function(test)
> test:ok(tiny.hours == 0.00848, "hours")
> end)
>
>
>
<stripped>
--
Serge Petrenko
More information about the Tarantool-patches
mailing list