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