diff options
author | Kim Alvefur <zash@zash.se> | 2022-08-14 16:57:31 +0200 |
---|---|---|
committer | Kim Alvefur <zash@zash.se> | 2022-08-14 16:57:31 +0200 |
commit | c85c18b03ab627ee81273d4c0f0009e1a5cc2d03 (patch) | |
tree | 9add9e2fed946e270cc3fe1736c4e78e5d31e8b3 | |
parent | d79609ddc922ef12032c738fdb8e602818f244e0 (diff) | |
download | prosody-c85c18b03ab627ee81273d4c0f0009e1a5cc2d03.tar.gz prosody-c85c18b03ab627ee81273d4c0f0009e1a5cc2d03.zip |
util.datetime: Add support for sub-second precision timestamps
Lua since 5.3 raises a fuss when time functions are handed a number with
a fractional part and the underlying C functions are all based on
integer seconds without support for more precision.
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | spec/util_datetime_spec.lua | 13 | ||||
-rw-r--r-- | util/datetime.lua | 26 |
3 files changed, 36 insertions, 7 deletions
@@ -19,6 +19,10 @@ TRUNK - Advertise supported SASL Channel-Binding types (XEP-0440) - Implement RFC 9266 'tls-exporter' channel binding with TLS 1.3 +## Changes + +- Support sub-second precision timestamps + ## Removed - Lua 5.1 support diff --git a/spec/util_datetime_spec.lua b/spec/util_datetime_spec.lua index aff55c7b..a35a1037 100644 --- a/spec/util_datetime_spec.lua +++ b/spec/util_datetime_spec.lua @@ -18,6 +18,9 @@ describe("util.datetime", function () it("should work", function () assert.equals("2006-01-02", date(1136239445)); end); + it("should ignore fractional parts", function () + assert.equals("2006-01-02", date(1136239445.5)); + end); end); describe("#time", function () local time = util_datetime.time; @@ -34,6 +37,9 @@ describe("util.datetime", function () it("should work", function () assert.equals("22:04:05", time(1136239445)); end); + it("should handle precision", function () + assert.equal("14:46:32.158200", time(1660488392.1582)) + end) end); describe("#datetime", function () local datetime = util_datetime.datetime; @@ -50,6 +56,9 @@ describe("util.datetime", function () it("should work", function () assert.equals("2006-01-02T22:04:05Z", datetime(1136239445)); end); + it("should handle precision", function () + assert.equal("2022-08-14T14:46:32.158200Z", datetime(1660488392.1582)) + end) end); describe("#legacy", function () local legacy = util_datetime.legacy; @@ -72,5 +81,9 @@ describe("util.datetime", function () -- https://xmpp.org/extensions/xep-0082.html#example-2 and 3 assert.equals(parse("1969-07-21T02:56:15Z"), parse("1969-07-20T21:56:15-05:00")); end); + it("should handle precision", function () + -- floating point comparison is not an exact science + assert.truthy(math.abs(1660488392.1582 - parse("2022-08-14T14:46:32.158200Z")) < 0.001) + end) end); end); diff --git a/util/datetime.lua b/util/datetime.lua index 2d27ece4..8fa1a778 100644 --- a/util/datetime.lua +++ b/util/datetime.lua @@ -12,31 +12,42 @@ local os_date = os.date; local os_time = os.time; local os_difftime = os.difftime; +local floor = math.floor; local tonumber = tonumber; local _ENV = nil; -- luacheck: std none local function date(t) - return os_date("!%Y-%m-%d", t); + return os_date("!%Y-%m-%d", t and floor(t) or nil); end local function datetime(t) - return os_date("!%Y-%m-%dT%H:%M:%SZ", t); + if t == nil or t % 1 == 0 then + return os_date("!%Y-%m-%dT%H:%M:%SZ", t); + end + local m = t % 1; + local s = floor(t); + return os_date("!%Y-%m-%dT%H:%M:%S.%%06dZ", s):format(floor(m * 1000000)); end local function time(t) - return os_date("!%H:%M:%S", t); + if t == nil or t % 1 == 0 then + return os_date("!%H:%M:%S", t); + end + local m = t % 1; + local s = floor(t); + return os_date("!%H:%M:%S.%%06d", s):format(floor(m * 1000000)); end local function legacy(t) - return os_date("!%Y%m%dT%H:%M:%S", t); + return os_date("!%Y%m%dT%H:%M:%S", t and floor(t) or nil); end local function parse(s) if s then local year, month, day, hour, min, sec, tzd; - year, month, day, hour, min, sec, tzd = s:match("^(%d%d%d%d)%-?(%d%d)%-?(%d%d)T(%d%d):(%d%d):(%d%d)%.?%d*([Z+%-]?.*)$"); + year, month, day, hour, min, sec, tzd = s:match("^(%d%d%d%d)%-?(%d%d)%-?(%d%d)T(%d%d):(%d%d):(%d%d%.?%d*)([Z+%-]?.*)$"); if year then local now = os_time(); local time_offset = os_difftime(os_time(os_date("*t", now)), os_time(os_date("!*t", now))); -- to deal with local timezone @@ -49,8 +60,9 @@ local function parse(s) tzd_offset = h * 60 * 60 + m * 60; if sign == "-" then tzd_offset = -tzd_offset; end end - sec = (sec + time_offset) - tzd_offset; - return os_time({year=year, month=month, day=day, hour=hour, min=min, sec=sec, isdst=false}); + local prec = sec%1; + sec = floor(sec + time_offset) - tzd_offset; + return os_time({year=year, month=month, day=day, hour=hour, min=min, sec=sec, isdst=false})+prec; end end end |