aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKim Alvefur <zash@zash.se>2022-08-14 16:57:31 +0200
committerKim Alvefur <zash@zash.se>2022-08-14 16:57:31 +0200
commitc85c18b03ab627ee81273d4c0f0009e1a5cc2d03 (patch)
tree9add9e2fed946e270cc3fe1736c4e78e5d31e8b3
parentd79609ddc922ef12032c738fdb8e602818f244e0 (diff)
downloadprosody-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--CHANGES4
-rw-r--r--spec/util_datetime_spec.lua13
-rw-r--r--util/datetime.lua26
3 files changed, 36 insertions, 7 deletions
diff --git a/CHANGES b/CHANGES
index 48fe37d5..7df64d70 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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