From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 478656EC40; Mon, 16 Aug 2021 03:01:30 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 478656EC40 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1629072090; bh=9rVplIjxc8rxmwWfuQDOABT8oU/Jk0Hq5WEFbi9aroc=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=uZp9qg/Mvrf+xYhpCXqHS2PWl/E+wjwEgViukOIUgKJxkh2St2IdXcrm5M6MSQlkQ oeVXkNkQLNP7Anc6IH3FtReyCx99D6Uu8d6cnMrujBzoOZJeLKdRDV4CQ7P4ylkqYq 6/kxgRfHcFJmw7O7XaylegGqv/uENG36sXFQVx0Y= Received: from smtp41.i.mail.ru (smtp41.i.mail.ru [94.100.177.101]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 6C08C6EC43 for ; Mon, 16 Aug 2021 02:59:59 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 6C08C6EC43 Received: by smtp41.i.mail.ru with esmtpa (envelope-from ) id 1mFQ2n-00047O-Ty; Mon, 16 Aug 2021 02:59:58 +0300 To: v.shpilevoy@tarantool.org, imun@tarantool.org, imeevma@tarantool.org, tarantool-patches@dev.tarantool.org Date: Mon, 16 Aug 2021 02:59:37 +0300 Message-Id: <8dccf7ee1d6db0f8fd194ab45ae16978b74ec99b.1629071531.git.tsafin@tarantool.org> X-Mailer: git-send-email 2.29.2 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-4EC0790: 10 X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD92087353F0EC44DD91BCCB18F2C129F87F36E61E9E4584E9D182A05F538085040D9A6D2096EC28AD02A1477B91D60FB8683EDD9E0C3513EA2693499F9DA0BC36E X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE71C9A87BC3B243991EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F79006372E000E903B06FBD58638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D84AD90C14F620C160E48FF1C839A46E00117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCF1175FABE1C0F9B6A471835C12D1D9774AD6D5ED66289B52BA9C0B312567BB23117882F44604297287769387670735209ECD01F8117BC8BEA471835C12D1D977C4224003CC836476EB9C4185024447017B076A6E789B0E975F5C1EE8F4F765FC17CFA0A2B4EC57633AA81AA40904B5D9CF19DD082D7633A078D18283394535A93AA81AA40904B5D98AA50765F7900637F50F9EDE1B0BD156D81D268191BDAD3D698AB9A7B718F8C4D1B931868CE1C5781A620F70A64A45A98AA50765F79006372E808ACE2090B5E1725E5C173C3A84C3C5EA940A35A165FF2DBA43225CD8A89FB26E97DCB74E6252156CCFE7AF13BCA4B5C8C57E37DE458BEDA766A37F9254B7 X-B7AD71C0: AC4F5C86D027EB782CDD5689AFBDA7A213B5FB47DCBC3458F0AFF96BAACF4158235E5A14AD4A4A4625E192CAD1D9E79D0B18DC6AC13D9A1CD59730D89322F817 X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975C5B73F950BC6E7FFBCC4210BC36951CA08D77C7439DF858499C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EFE37876E7723AB534DC48ACC2A39D04F89CDFB48F4795C241BDAD6C7F3747799A X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D345C110A855FC0999961B19840867FCA57951766D07214EE823DCBF5B3C944CA6F25C7CC6E46682BAE1D7E09C32AA3244CA19080CDF101FA3AA3197269A8DFEF2CD08D48398F32B4A68D5DD81C2BAB7D1D X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojVT2don9h9Kqq3PUZ25KZ5A== X-Mailru-Sender: B5B6A6EBBD94DAD833E4B9A85F720031806AC487ADECA3547C145E1268641E71150C4F1DE7CAA4291EC9E4A2C82A33BC8C24925A86E657CE0C70AEE3C9A96FBAB3D7EE8ED63280BE112434F685709FCF0DA7A0AF5A3A8387 X-Mras: Ok Subject: [Tarantool-patches] [PATCH v5 3/8] lua, datetime: display datetime X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Timur Safin via Tarantool-patches Reply-To: Timur Safin Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "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 +#include #include #include @@ -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); + 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 + diff --git a/src/lib/core/datetime.h b/src/lib/core/datetime.h index 1a8d7e34f..964e76fcc 100644 --- a/src/lib/core/datetime.h +++ b/src/lib/core/datetime.h @@ -70,6 +70,15 @@ struct datetime_interval { int32_t nsec; }; +/** + * Convert datetime to string using default format + * @param date source datetime value + * @param buf output character buffer + * @param len size ofoutput buffer + */ +int +datetime_to_string(const struct datetime *date, char *buf, uint32_t len); + /** * Convert datetime to string using default asctime format * "Sun Sep 16 01:03:52 1973\n\0" diff --git a/src/lua/datetime.lua b/src/lua/datetime.lua index ce579828f..4d946f194 100644 --- a/src/lua/datetime.lua +++ b/src/lua/datetime.lua @@ -249,6 +249,37 @@ local function datetime_new(obj) return datetime_new_dt(dt, secs, frac, offset) end +local function datetime_tostring(o) + if ffi.typeof(o) == datetime_t then + local sz = 48 + local buff = ffi.new('char[?]', sz) + local len = builtin.datetime_to_string(o, buff, sz) + assert(len < sz) + return ffi.string(buff) + elseif ffi.typeof(o) == interval_t then + local ts = o.timestamp + local sign = '+' + + if ts < 0 then + ts = -ts + sign = '-' + end + + if ts < 60 then + return ('%s%s secs'):format(sign, ts) + elseif ts < 60 * 60 then + return ('%+d minutes, %s seconds'):format(o.minutes, ts % 60) + elseif ts < 24 * 60 * 60 then + return ('%+d hours, %d minutes, %s seconds'):format( + o.hours, o.minutes % 60, ts % 60) + else + return ('%+d days, %d hours, %d minutes, %s seconds'):format( + o.days, o.hours % 24, o.minutes % 60, ts % 60) + end + end +end + + --[[ Basic Extended 20121224 2012-12-24 Calendar date (ISO 8601) @@ -457,6 +488,7 @@ local function strftime(fmt, o) end local datetime_mt = { + __tostring = datetime_tostring, __serialize = datetime_serialize, __eq = datetime_eq, __lt = datetime_lt, @@ -466,6 +498,7 @@ local datetime_mt = { } local interval_mt = { + __tostring = datetime_tostring, __serialize = interval_serialize, __eq = datetime_eq, __lt = datetime_lt, @@ -487,6 +520,8 @@ return setmetatable( parse_time = parse_time, parse_zone = parse_zone, + tostring = datetime_tostring, + now = local_now, strftime = strftime, asctime = asctime, 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}, } 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) +test:test("Stringization of date", function(test) + test:plan(1) + local str = '19700101Z' + local dt = date(str) + test:ok(tostring(dt) == '1970-01-01T00:00Z', ('tostring(%s)'):format(str)) +end) + os.exit(test:check() and 0 or 1) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 31b183a8f..8194a133f 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -57,7 +57,7 @@ target_link_libraries(uuid.test uuid unit) add_executable(random.test random.c core_test_utils.c) target_link_libraries(random.test core unit) add_executable(datetime.test datetime.c) -target_link_libraries(datetime.test cdt unit) +target_link_libraries(datetime.test cdt core unit) add_executable(bps_tree.test bps_tree.cc) target_link_libraries(bps_tree.test small misc) add_executable(bps_tree_iterator.test bps_tree_iterator.cc) diff --git a/test/unit/datetime.c b/test/unit/datetime.c index 64c19dac4..1ae76003b 100644 --- a/test/unit/datetime.c +++ b/test/unit/datetime.c @@ -5,6 +5,7 @@ #include #include "unit.h" +#include "datetime.h" static const char sample[] = "2012-12-24T15:30Z"; @@ -136,18 +137,6 @@ exit: return 0; } -/* avoid introducing external datetime.h dependency - - just copy paste it for today -*/ -#define SECS_PER_DAY 86400 -#define DT_EPOCH_1970_OFFSET 719163 - -struct datetime { - double secs; - int32_t nsec; - int32_t offset; -}; - static int local_rd(const struct datetime *dt) { @@ -211,13 +200,59 @@ static void datetime_test(void) is(secs, parsed_secs, "reversible seconds via strftime for '%s", buff); } + check_plan(); +} + + +static void +tostring_datetime_test(void) +{ + static struct { + const char *string; + int64_t secs; + uint32_t nsec; + uint32_t offset; + } tests[] = { + {"1970-01-01T02:00+02:00", 0, 0, 120}, + {"1970-01-01T01:30+01:30", 0, 0, 90}, + {"1970-01-01T01:00+01:00", 0, 0, 60}, + {"1970-01-01T00:01+00:01", 0, 0, 1}, + {"1970-01-01T00:00Z", 0, 0, 0}, + {"1969-12-31T23:59-00:01", 0, 0, -1}, + {"1969-12-31T23:00-01:00", 0, 0, -60}, + {"1969-12-31T22:30-01:30", 0, 0, -90}, + {"1969-12-31T22:00-02:00", 0, 0, -120}, + {"1970-01-01T00:00:00.123456789Z", 0, 123456789, 0}, + {"1970-01-01T00:00:00.123456Z", 0, 123456000, 0}, + {"1970-01-01T00:00:00.123Z", 0, 123000000, 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}, + }; + size_t index; + + plan(15); + for (index = 0; index < DIM(tests); index++) { + struct datetime date = { + tests[index].secs, + tests[index].nsec, + tests[index].offset + }; + char buf[48]; + datetime_to_string(&date, buf, sizeof buf); + is(strcmp(buf, tests[index].string), 0, + "string '%s' expected, received '%s'", + tests[index].string, buf); + } + check_plan(); } int main(void) { - plan(1); + plan(2); datetime_test(); + tostring_datetime_test(); return check_plan(); } -- 2.29.2