From: Timur Safin via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: v.shpilevoy@tarantool.org
Cc: tarantool-patches@dev.tarantool.org
Subject: [Tarantool-patches] [RFC PATCH 12/13] lua: initial time duration support
Date: Thu, 15 Jul 2021 11:18:18 +0300 [thread overview]
Message-ID: <5f3f523b33ba209621f769582dc16e612b649a05.1626335242.git.tsafin@tarantool.org> (raw)
In-Reply-To: <cover.1626335241.git.tsafin@tarantool.org>
* created few entries (months(N), years(N), days(N), etc.)
for easier date/time arithmetic;
* at the moment we require that on either side of + or -
we should have at least one datetime. There is no (yet) support
for duration + duration operations.
---
src/lua/datetime.lua | 230 +++++++++++++++++++++++++++++++++----------
1 file changed, 176 insertions(+), 54 deletions(-)
diff --git a/src/lua/datetime.lua b/src/lua/datetime.lua
index 081e759fd..698c55ebd 100644
--- a/src/lua/datetime.lua
+++ b/src/lua/datetime.lua
@@ -37,6 +37,17 @@ ffi.cdef [[
int dt_rdn (dt_t dt);
dt_dow_t dt_dow (dt_t dt);
+ // dt_arithmetic.h
+ typedef enum {
+ DT_EXCESS,
+ DT_LIMIT,
+ DT_SNAP
+ } dt_adjust_t;
+
+ dt_t dt_add_years (dt_t dt, int delta, dt_adjust_t adjust);
+ dt_t dt_add_quarters (dt_t dt, int delta, dt_adjust_t adjust);
+ dt_t dt_add_months (dt_t dt, int delta, dt_adjust_t adjust);
+
// dt_parse_iso.h
size_t dt_parse_iso_date (const char *str, size_t len, dt_t *dt);
@@ -158,40 +169,51 @@ local DT_EPOCH_1970_OFFSET = 719163LL
local datetime_t = ffi.typeof('struct datetime_t')
local duration_t = ffi.typeof('struct t_datetime_duration')
+ffi.cdef [[
+ struct t_duration_months {
+ int m;
+ };
+
+ struct t_duration_years {
+ int y;
+ };
+]]
+local duration_months_t = ffi.typeof('struct t_duration_months')
+local duration_years_t = ffi.typeof('struct t_duration_years')
local function duration_new()
local delta = ffi.new(duration_t)
return delta
end
-local function adjusted_secs(dt)
- return dt.secs - dt.offset * 60
+local function duration_years_new(y)
+ local o = ffi.new(duration_years_t)
+ o.y = y
+ return o
end
-local function datetime_sub(lhs, rhs)
- local s1 = adjusted_secs(lhs)
- local s2 = adjusted_secs(rhs)
- local d = duration_new()
- d.secs = s2 - s1
- d.nsec = rhs.nsec - lhs.nsec
- if d.nsec < 0 then
- d.secs = d.secs - 1
- d.nsec = d.nsec + NANOS_PER_SEC
- end
- return d
+local function duration_months_new(m)
+ local o = ffi.new(duration_months_t)
+ o.m = m
+ return o
end
-local function datetime_add(lhs, rhs)
- local s1 = adjusted_secs(lhs)
- local s2 = adjusted_secs(rhs)
- local d = duration_new()
- d.secs = s2 + s1
- d.nsec = rhs.nsec + lhs.nsec
- if d.nsec >= NANOS_PER_SEC then
- d.secs = d.secs + 1
- d.nsec = d.nsec - NANOS_PER_SEC
- end
- return d
+local function duration_days_new(d)
+ local o = ffi.new(duration_t)
+ o.secs = d * SECS_PER_DAY
+ return o
+end
+
+local function duration_hours_new(h)
+ local o = ffi.new(duration_t)
+ o.secs = h * 60 * 60
+ return o
+end
+
+local function duration_minutes_new(m)
+ local o = ffi.new(duration_t)
+ o.secs = m * 60
+ return o
end
local function datetime_eq(lhs, rhs)
@@ -254,28 +276,6 @@ local datetime_index = function(self, key)
return attributes[key] ~= nil and attributes[key](self) or nil
end
-local datetime_mt = {
- -- __tostring = datetime_tostring,
- __serialize = datetime_serialize,
- __eq = datetime_eq,
- __lt = datetime_lt,
- __le = datetime_le,
- __sub = datetime_sub,
- __add = datetime_add,
- __index = datetime_index,
-}
-
-local duration_mt = {
- -- __tostring = duration_tostring,
- __serialize = duration_serialize,
- __eq = datetime_eq,
- __lt = datetime_lt,
- __le = datetime_le,
- __sub = datetime_sub,
- __add = datetime_add,
- __index = datetime_index,
-}
-
local function datetime_new_raw(secs, nsec, offset)
local dt_obj = ffi.new(datetime_t)
dt_obj.secs = secs
@@ -402,6 +402,110 @@ local function datetime_new(o)
return mk_timestamp(dt, secs, frac, offset)
end
+local function datetime_tostring(o)
+ print(ffi.typeof(o))
+ assert(ffi.typeof(o) == datetime_t)
+ local sz = 48
+ local buff = ffi.new('char[?]', sz)
+ local len = native.datetime_to_string(o, buff, sz)
+ assert(len < sz)
+ return ffi.string(buff)
+end
+
+local function dt_to_ymd(dt)
+ local y, m, d
+ y = ffi.new('int[1]')
+ m = ffi.new('int[1]')
+ d = ffi.new('int[1]')
+ cdt.dt_to_ymd(dt, y, m, d)
+ return y[0], m[0], d[0]
+end
+
+local function check_date(o)
+ assert(ffi.typeof(o) == datetime_t, "date/time expected")
+end
+
+local function date_first(lhs, rhs)
+ if (ffi.typeof(lhs) == datetime_t) then
+ return lhs, rhs
+ else
+ return rhs, lhs
+ end
+end
+
+local function shift_months(y, M, deltaM)
+ M = M + deltaM
+ local newM = (M - 1) % 12 + 1
+ local newY = y + math.floor((M - 1)/12)
+ assert(newM >= 1 and newM <= 12, "month value is outside of range")
+ return newY, newM
+end
+
+local function datetime_sub(lhs, rhs)
+ check_date(lhs) -- make sure left is date
+ local d, s = lhs, rhs
+
+ -- 1. left is date, right is date or delta
+ if (ffi.typeof(s) == datetime_t) or (ffi.typeof(s) == duration_t) then
+ d.secs = d.secs - s.secs
+ d.nsec = s.nsec - s.nsec
+ if d.nsec < 0 then
+ d.secs = d.secs - 1
+ d.nsec = d.nsec + NANOS_PER_SEC
+ end
+
+ -- 2. left is date, right is duration in months
+ elseif ffi.typeof(s) == duration_months_t then
+ local y, M, D = dt_to_ymd(local_dt(d))
+ y, M = shift_months(y, M, -s.m)
+ local dt = cdt.dt_from_ymd(y, M, D)
+ local secs = d.secs % SECS_PER_DAY
+ return mk_timestamp(dt, secs, d.nsec, d.offset or 0)
+
+ -- 2. left is date, right is duration in years
+ elseif ffi.typeof(s) == duration_years_t then
+ local y, M, D = dt_to_ymd(local_dt(d))
+ y = y - s.y
+ local dt = cdt.dt_from_ymd(y, M, D)
+ local secs = d.secs % SECS_PER_DAY
+ return mk_timestamp(dt, secs, d.nsec, d.offset or 0)
+ else
+ assert(false, "unexpected type")
+ end
+end
+
+local function datetime_add(lhs, rhs)
+ local d, s = date_first(lhs, rhs)
+
+ -- 1. left is date, right is date or delta
+ if (ffi.typeof(s) == datetime_t) or (ffi.typeof(s) == duration_t) then
+ d.secs = d.secs + s.secs
+ d.nsec = d.nsec + s.nsec
+ if d.nsec >= NANOS_PER_SEC then
+ d.secs = d.secs + 1
+ d.nsec = d.nsec - NANOS_PER_SEC
+ end
+ return d
+
+ -- 2. left is date, right is duration in months
+ elseif ffi.typeof(s) == duration_months_t then
+ local y, M, D = dt_to_ymd(local_dt(d))
+ y, M = shift_months(y, M, s.m)
+ local dt = cdt.dt_from_ymd(y, M, D)
+ local secs = d.secs % SECS_PER_DAY
+ return mk_timestamp(dt, secs, d.nsec, d.offset or 0)
+
+ -- 2. left is date, right is duration in years
+ elseif ffi.typeof(s) == duration_years_t then
+ local y, M, D = dt_to_ymd(local_dt(d))
+ y = y + s.y
+ local dt = cdt.dt_from_ymd(y, M, D)
+ local secs = d.secs % SECS_PER_DAY
+ return mk_timestamp(dt, secs, d.nsec, d.offset or 0)
+ else
+ assert(false, "unexpected type")
+ end
+end
-- simple parse functions:
-- parse_date/parse_time/parse_zone
@@ -572,15 +676,28 @@ local function strftime(fmt, o)
return ffi.string(buff)
end
-local function datetime_tostring(o)
- assert(ffi.typeof(o) == datetime_t)
- local sz = 48
- local buff = ffi.new('char[?]', sz)
- local len = native.datetime_to_string(o, buff, sz)
- assert(len < sz)
- return ffi.string(buff)
-end
+local datetime_mt = {
+ -- __tostring = datetime_tostring,
+ __serialize = datetime_serialize,
+ __eq = datetime_eq,
+ __lt = datetime_lt,
+ __le = datetime_le,
+ __sub = datetime_sub,
+ __add = datetime_add,
+ __index = datetime_index,
+}
+
+local duration_mt = {
+ -- __tostring = duration_tostring,
+ __serialize = duration_serialize,
+ __eq = datetime_eq,
+ __lt = datetime_lt,
+ __le = datetime_le,
+ __sub = datetime_sub,
+ __add = datetime_add,
+ __index = datetime_index,
+}
ffi.metatype(duration_t, duration_mt)
ffi.metatype(datetime_t, datetime_mt)
@@ -588,6 +705,11 @@ ffi.metatype(datetime_t, datetime_mt)
return setmetatable(
{
datetime = datetime_new,
+ years = duration_years_new,
+ months = duration_months_new,
+ days = duration_days_new,
+ hours = duration_hours_new,
+ minutes = duration_minutes_new,
delta = duration_new,
parse = parse_str,
--
2.29.2
next prev parent reply other threads:[~2021-07-15 8:24 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-07-15 8:18 [Tarantool-patches] [RFC PATCH 00/13] Initial datetime support Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 01/13] build: add Christian Hansen c-dt to the build Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 02/13] lua: built-in module datetime Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 03/13] test: datetime test Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 04/13] test: datetime string formatting Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 05/13] box: add messagepack support for datetime Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 06/13] lua: positive/negative cases in datetime test Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 07/13] lua: asctime and strfime fixed Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 08/13] box, lua: renamed t_datetime_tz structure to datetime_t Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 09/13] lua: calculated attributes for date Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 10/13] lua: tostring formatization in datetime.lua Timur Safin via Tarantool-patches
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 11/13] test: allow relaxed date format without tz Timur Safin via Tarantool-patches
2021-07-15 8:18 ` Timur Safin via Tarantool-patches [this message]
2021-07-15 8:18 ` [Tarantool-patches] [RFC PATCH 13/13] lua: complete time duration support Timur Safin via Tarantool-patches
2021-07-15 16:56 ` [Tarantool-patches] [RFC PATCH 00/13] Initial datetime support Oleg Babin via Tarantool-patches
2021-07-15 23:03 ` Timur Safin via Tarantool-patches
2021-07-16 6:58 ` Oleg Babin via Tarantool-patches
2021-07-22 10:01 ` Igor Munkin via Tarantool-patches
2021-07-22 12:54 ` Timur Safin via Tarantool-patches
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5f3f523b33ba209621f769582dc16e612b649a05.1626335242.git.tsafin@tarantool.org \
--to=tarantool-patches@dev.tarantool.org \
--cc=tsafin@tarantool.org \
--cc=v.shpilevoy@tarantool.org \
--subject='Re: [Tarantool-patches] [RFC PATCH 12/13] lua: initial time duration support' \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox