Tarantool development patches archive
 help / color / mirror / Atom feed
From: Oleg Babin via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Timur Safin <tsafin@tarantool.org>, v.shpilevoy@tarantool.org
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH resend v2 09/11] lua, datetime: time intervals support
Date: Thu, 29 Jul 2021 21:58:27 +0300
Message-ID: <ff90fb89-1c85-de43-ecb8-b72068127000@tarantool.org> (raw)
In-Reply-To: <072bcee03a63600d918b18cd2863b7c36f666072.1627468002.git.tsafin@tarantool.org>

Thanks for your patch.

I wrote several comments below.


However it makes me think that such approach will work quite slow since

all functions is implemented in Lua, all arithmetic is in Lua.


On 28.07.2021 13:34, Timur Safin via Tarantool-patches wrote:
> * created few entry points (months(N), years(N), days(N), etc.)
>    for easier datetime arithmetic;
> * additions/subtractions of years/months use `dt_add_years()`
>    and `dt_add_months()` from 3rd party c-dt library;
> * also there are `:add{}` and `:sub{}` methods in datetime
>    object to add or substract more complex intervals;
> * introduced `is_datetime()` and `is_interval()` helpers for checking
>    of validity of passed arguments;
> * human-readable stringization implemented for interval objects.
>
> Note, that additions/subtractions completed for all _reasonable_
> combinations of values of date and interval types;
>
> 	Time + Interval	=> Time
> 	Interval + Time => Time
> 	Time - Time 	=> Interval
> 	Time - Interval => Time
> 	Interval + Interval => Interval
> 	Interval - Interval => Interval
>
> Part of #5941
> ---
>   src/exports.h                  |   3 +
>   src/lua/datetime.lua           | 556 +++++++++++++++++++++++++++------
>   test/app-tap/datetime.test.lua | 163 +++++++++-
>   3 files changed, 631 insertions(+), 91 deletions(-)
>
> diff --git a/src/exports.h b/src/exports.h
> index 3a1e8854c..6e7fe206d 100644
> --- a/src/exports.h
> +++ b/src/exports.h
> @@ -535,6 +535,9 @@ EXPORT(uuid_nil)
>   EXPORT(uuid_unpack)
>   EXPORT(datetime_unpack)
>   EXPORT(datetime_pack)
> +EXPORT(dt_add_months)
> +EXPORT(dt_add_years)
> +EXPORT(dt_add_quarters)
>   EXPORT(dt_from_rdn)
>   EXPORT(dt_from_yd)
>   EXPORT(dt_from_ymd)
> diff --git a/src/lua/datetime.lua b/src/lua/datetime.lua
> index 7a208cef9..1466b923f 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,58 +169,146 @@ local DT_EPOCH_1970_OFFSET = 719163LL
>   
>   local datetime_t = ffi.typeof('struct datetime_t')
>   local interval_t = ffi.typeof('struct datetime_interval_t')
> +ffi.cdef [[
> +    struct t_interval_months {
> +        int m;
> +    };
> +
> +    struct t_interval_years {
> +        int y;
> +    };
> +]]
> +local interval_months_t = ffi.typeof('struct t_interval_months')
> +local interval_years_t = ffi.typeof('struct t_interval_years')
> +
> +local function is_interval(o)
> +    return ffi.istype(interval_t, o) or
> +           ffi.istype(interval_months_t, o) or
> +           ffi.istype(interval_years_t, o)
> +end
> +

It will throw for non-cdata values:

tarantool> ffi.istype(interval_t, o)
---
- error: 'bad argument #1 to ''?'' (C type expected, got nil)'
...

tarantool> ffi.istype(interval_t, 123)
---
- error: 'bad argument #1 to ''?'' (C type expected, got nil)'
...


> +local function is_datetime(o)
> +    return ffi.istype(o, datetime_t)
> +end
> +
>   
>   local function interval_new()
>       local interval = ffi.new(interval_t)
>       return interval
>   end
>   
> -local function adjusted_secs(dt)
> -    return dt.secs - dt.offset * 60
> +local function check_number(n, message, lvl)
> +    if lvl == nil then
> +        lvl = 2
> +    end
> +    if type(n) ~= 'number' then
> +        return error(('Usage: %s'):format(message), lvl)
> +    end
>   end
>   
> -local function datetime_sub(lhs, rhs)
> -    local s1 = adjusted_secs(lhs)
> -    local s2 = adjusted_secs(rhs)
> -    local d = interval_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
> +local function check_date(o, message, lvl)
> +    if lvl == nil then
> +        lvl = 2
> +    end
> +    if not is_datetime(o) then
> +        return error(('Usage: %s'):format(message), lvl)
>       end
> -    return d
>   end
>   
> -local function datetime_add(lhs, rhs)
> -    local s1 = adjusted_secs(lhs)
> -    local s2 = adjusted_secs(rhs)
> -    local d = interval_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
> +local function check_date_interval(o, message, lvl)
> +    if lvl == nil then
> +        lvl = 2
> +    end
> +    if not (is_datetime(o) or is_interval(o)) then
> +        return error(('Usage: %s'):format(message), lvl)
>       end
> -    return d
>   end
>   
> -local function datetime_eq(lhs, rhs)
> -    -- we usually don't need to check nullness
> -    -- but older tarantool console will call us checking for equality to nil
> -    if rhs == nil then
> -        return false
> +local function check_interval(o, message, lvl)
> +    if lvl == nil then
> +        lvl = 2
> +    end
> +    if not is_interval(o) then
> +        return error(('Usage: %s'):format(message), lvl)
>       end
> +end
> +
> +local function check_str(o, message, lvl)
> +    if lvl == nil then
> +        lvl = 2
> +    end
> +    if not type(o) == 'string' then
> +        return error(('Usage: %s'):format(message), lvl)
> +    end
> +end
> +
> +local function interval_years_new(y)
> +    check_number(y, "years(number)")
> +    local o = ffi.new(interval_years_t)
> +    o.y = y
> +    return o
> +end
> +
> +local function interval_months_new(m)
> +    check_number(m, "months(number)")
> +    local o = ffi.new(interval_months_t)
> +    o.m = m
> +    return o
> +end
> +
> +local function interval_weeks_new(w)
> +    check_number(w, "weeks(number)")
> +    local o = ffi.new(interval_t)
> +    o.secs = w * SECS_PER_DAY * 7
> +    return o
> +end
> +
> +local function interval_days_new(d)
> +    check_number(d, "days(number)")
> +    local o = ffi.new(interval_t)
> +    o.secs = d * SECS_PER_DAY
> +    return o
> +end
> +
> +local function interval_hours_new(h)
> +    check_number(h, "hours(number)")
> +    local o = ffi.new(interval_t)
> +    o.secs = h * 60 * 60
> +    return o
> +end
> +
> +local function interval_minutes_new(m)
> +    check_number(m, "minutes(number)")
> +    local o = ffi.new(interval_t)
> +    o.secs = m * 60
> +    return o
> +end
> +
> +local function interval_seconds_new(s)
> +    check_number(s, "seconds(number)")
> +    local o = ffi.new(interval_t)
> +    o.nsec = s % 1 * 1e9
> +    o.secs = s - (s % 1)
> +    return o
> +end
> +
> +local function datetime_eq(lhs, rhs)
> +    check_date_interval(lhs, "datetime:__eq(date or interval)")
> +    check_date_interval(rhs, "datetime:__eq(date or interval)")
>       return (lhs.secs == rhs.secs) and (lhs.nsec == rhs.nsec)
>   end
>   
>   
>   local function datetime_lt(lhs, rhs)
> +    check_date_interval(lhs, "datetime:__lt(date or interval)")
> +    check_date_interval(rhs, "datetime:__lt(date or interval)")
>       return (lhs.secs < rhs.secs) or
>              (lhs.secs == rhs.secs and lhs.nsec < rhs.nsec)
>   end
>   
>   local function datetime_le(lhs, rhs)
> +    check_date_interval(lhs, "datetime:__le(date or interval)")
> +    check_date_interval(rhs, "datetime:__le(date or interval)")
>       return (lhs.secs <= rhs.secs) or
>              (lhs.secs == rhs.secs and lhs.nsec <= rhs.nsec)
>   end
> @@ -224,19 +323,123 @@ local function interval_serialize(self)
>       return { secs = self.secs, nsec = self.nsec }
>   end
>   
> +local function local_rd(o)
> +    return math.floor(tonumber(o.secs / SECS_PER_DAY)) + DT_EPOCH_1970_OFFSET
> +end
> +
> +local function local_dt(o)
> +    return cdt.dt_from_rdn(local_rd(o))
> +end
> +
> +local function _normalize_nsec(secs, nsec)
> +    if nsec < 0 then
> +        secs = secs - 1
> +        nsec = nsec + NANOS_PER_SEC
> +    elseif nsec >= NANOS_PER_SEC then
> +        secs = secs + 1
> +        nsec = nsec - NANOS_PER_SEC
> +    end
> +    return secs, nsec
> +end
> +
> +-- addition or subtraction from date/time of a given interval
> +-- described via table direction should be +1 or -1
> +local function interval_increment(self, o, direction)
> +    assert(direction == -1 or direction == 1)
> +    check_date(self, "interval_increment(date, object, -+1)")
> +    assert(type(o) == 'table')
> +
> +    local ym_updated = false
> +    local dhms_updated = false
> +
> +    local dt = local_dt(self)
> +    local secs, nsec
> +    secs, nsec = self.secs, self.nsec
> +
> +    for key, value in pairs(o) do
> +        local handlers = {

The same as in one previous patch. It's too expensive to recreate table 
and functions for each simple

action and for each iteration loop.

> +            years = function(v)
> +                assert(v > 0 and v < 10000)
> +                dt = cdt.dt_add_years(dt, direction * v, cdt.DT_LIMIT)
> +                ym_updated = true
> +            end,
> +
> +            months = function(v)
> +                assert(v > 0 and v < 13 )
> +                dt = cdt.dt_add_months(dt, direction * v, cdt.DT_LIMIT)
> +                ym_updated = true
> +            end,
> +
> +            weeks = function(v)
> +                assert(v > 0 and v < 32)
> +                secs = secs + direction * 7 * v * SECS_PER_DAY
> +                dhms_updated = true
> +            end,
> +
> +            days = function(v)
> +                assert(v > 0 and v < 32)
> +                secs = secs + direction * v * SECS_PER_DAY
> +                dhms_updated = true
> +            end,
> +
> +            hours = function(v)
> +                assert(v >= 0 and v < 24)
> +                secs = secs + direction * 60 * 60 * v
> +                dhms_updated = true
> +            end,
> +
> +            minutes = function(v)
> +                assert(v >= 0 and v < 60)
> +                secs = secs + direction * 60 * v
> +            end,
> +
> +            seconds = function(v)
> +                assert(v >= 0 and v < 61)
> +                local s, frac
> +                frac = v % 1
> +                if frac > 0 then
> +                    s = v - (v % 1)
> +                else
> +                    s = v
> +                end
> +                secs = secs + direction * s
> +                nsec = nsec + direction * frac * 1e9 -- convert fraction to nanoseconds
> +                dhms_updated = true
> +            end,
> +        }
> +        handlers[key](value)
> +    end
> +
> +    secs, nsec = _normalize_nsec(secs, nsec)
> +
> +    -- .days, .hours, .minutes, .seconds
> +    if dhms_updated then
> +        self.secs = secs
> +        self.nsec = nsec
> +    end
> +
> +    -- .years, .months updated
> +    if ym_updated then
> +        self.secs = (cdt.dt_rdn(dt) - DT_EPOCH_1970_OFFSET) * SECS_PER_DAY +
> +                    secs % SECS_PER_DAY
> +    end
> +
> +    return self
> +end
> +
>   local datetime_index = function(self, key)
>       local attributes = {
>           timestamp = function(self)
>               return tonumber(self.secs) + self.nsec / 1e9
>           end,
>           nanoseconds = function(self)
> -            return tonumber(self.secs * 1e9 + self.nsec)
> +            return self.secs * 1e9 + self.nsec
>           end,
>           microseconds = function(self)
> -            return tonumber(self.secs * 1e6 + self.nsec / 1e3)
> +            return self.secs * 1e6 + self.nsec / 1e3
>           end,
>           milliseconds = function(self)
> -            return tonumber(self.secs * 1e3 + self.nsec / 1e6)
> +            return self.secs * 1e3 + self.nsec / 1e6
>           end,
>           seconds = function(self)
>               return tonumber(self.secs) + self.nsec / 1e9
> @@ -250,32 +453,20 @@ local datetime_index = function(self, key)
>           days = function(self)
>               return (tonumber(self.secs) + self.nsec / 1e9) / (60 * 60) / 24
>           end,
> +        add = function(self)
> +            return function(self, o)
> +                return interval_increment(self, o, 1)
> +            end
> +        end,
> +        sub = function(self)
> +            return function(self, o)
> +                return interval_increment(self, o, -1)
> +            end
> +        end,
>       }
>       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 interval_mt = {
> -    -- __tostring = interval_tostring,
> -    __serialize = interval_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
> @@ -284,14 +475,6 @@ local function datetime_new_raw(secs, nsec, offset)
>       return dt_obj
>   end
>   
> -local function local_rd(o)
> -    return math.floor(tonumber(o.secs / SECS_PER_DAY)) + DT_EPOCH_1970_OFFSET
> -end
> -
> -local function local_dt(o)
> -    return cdt.dt_from_rdn(local_rd(o))
> -end
> -
>   local function mk_timestamp(dt, sp, fp, offset)
>       local epochV = dt ~= nil and (cdt.dt_rdn(dt) - DT_EPOCH_1970_OFFSET) * SECS_PER_DAY or 0
>       local spV = sp ~= nil and sp or 0
> @@ -367,11 +550,12 @@ local function datetime_new(o)
>               second = function(v)
>                   assert(v >= 0 and v < 61)
>                   frac = v % 1
> -                if frac then
> +                if frac > 0 then
>                       s = v - (v % 1)
>                   else
>                       s = v
>                   end
> +                frac = frac * 1e9 -- convert fraction to nanoseconds
>                   hms = true
>               end,
>   
> @@ -402,6 +586,153 @@ local function datetime_new(o)
>       return mk_timestamp(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 = native.datetime_to_string(o, buff, sz)
> +        assert(len < sz)
> +        return ffi.string(buff)
> +    elseif ffi.typeof(o) == interval_years_t then
> +        return ('%+d years'):format(o.y)
> +    elseif ffi.typeof(o) == interval_months_t then
> +        return ('%+d months'):format(o.m)
> +    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
> +
> +local function date_first(lhs, rhs)
> +    if is_datetime(lhs) then
> +        return lhs, rhs
> +    else
> +        return rhs, lhs
> +    end
> +end
> +
> +--[[
> +Matrix of subtraction operands eligibility and their result type
> +
> +|                 |  datetime | interval | interval_months | interval_years |
> ++-----------------+-----------+----------+-----------------+----------------+
> +| datetime        |  interval | datetime | datetime        | datetime       |
> +| interval        |           | interval |                 |                |
> +| interval_months |           |          | interval_months |                |
> +| interval_years  |           |          |                 | interval_years |
> +]]
> +local function datetime_sub(lhs, rhs)
> +    check_date_interval(lhs, "datetime:__sub(date or interval)")
> +    local d, s = lhs, rhs
> +    local left_t = ffi.typeof(d)
> +    local right_t = ffi.typeof(s)
> +    local o
> +
> +    if left_t == datetime_t then
> +        -- 1. left is date, right is date or generic interval
> +        if (right_t == datetime_t or right_t == interval_t) then
> +            o = right_t == datetime_t and interval_new() or datetime_new()
> +            o.secs, o.nsec = _normalize_nsec(lhs.secs - rhs.secs,
> +                                            lhs.nsec - rhs.nsec)
> +            return o
> +        -- 2. left is date, right is interval in months
> +        elseif right_t == interval_months_t then
> +            local dt = cdt.dt_add_months(local_dt(lhs), -rhs.m, cdt.DT_LIMIT)
> +            return mk_timestamp(dt, lhs.secs % SECS_PER_DAY,
> +                                lhs.nsec, lhs.offset)
> +
> +        -- 3. left is date, right is interval in years
> +        elseif right_t == interval_years_t then
> +            local dt = cdt.dt_add_years(local_dt(lhs), -rhs.y, cdt.DT_LIMIT)
> +            return mk_timestamp(dt, lhs.secs % SECS_PER_DAY,
> +                                lhs.nsec, lhs.offset)
> +        else
> +            error("datetime:__sub(date or interval) - incompatible type of arguments", 2)
> +        end
> +    -- 4. both left and right are generic intervals
> +    elseif left_t == interval_t and right_t == interval_t then
> +        o = interval_new()
> +        o.secs, o.nsec = _normalize_nsec(lhs.secs - rhs.secs,
> +                                        lhs.nsec - rhs.nsec)
> +        return o
> +    -- 5. both left and right are intervals in months
> +    elseif left_t == interval_months_t and right_t == interval_months_t then
> +        return interval_months_new(lhs.m - rhs.m)
> +    -- 5. both left and right are intervals in years
> +    elseif left_t == interval_years_t and right_t == interval_years_t then
> +        return interval_years_new(lhs.y - rhs.y)
> +    else
> +        error("datetime:__sub(date or interval) - incompatible type of arguments", 2)
> +    end
> +end
> +
> +--[[
> +Matrix of addition operands eligibility and their result type
> +
> +|                 |  datetime | interval | interval_months | interval_years |
> ++-----------------+-----------+----------+-----------------+----------------+
> +| datetime        |  datetime | datetime | datetime        | datetime       |
> +| interval        |  datetime | interval |                 |                |
> +| interval_months |  datetime |          | interval_months |                |
> +| interval_years  |  datetime |          |                 | interval_years |
> +]]
> +local function datetime_add(lhs, rhs)
> +    local d, s = date_first(lhs, rhs)
> +
> +    check_date_interval(d, "datetime:__add(interval)")
> +    check_interval(s, "datetime:__add(interval)")

tarantool> return require('datetime').now() + 1
---
- error: '[string "return require(''datetime'').now() + 1"]:1: Usage: 
datetime:__add(interval)'
...


Looks a bit confusing. User doesn't know about metamethods.


> +    local left_t = ffi.typeof(d)
> +    local right_t = ffi.typeof(s)
> +    local o
> +

  reply	other threads:[~2021-07-29 18:59 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-28 10:34 [Tarantool-patches] [PATCH resend v2 00/11] Initial datetime support Timur Safin via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 01/11] build: add Christian Hansen c-dt to the build Timur Safin via Tarantool-patches
2021-07-29 23:40   ` Vladislav Shpilevoy via Tarantool-patches
2021-07-31  9:22     ` Timur Safin via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 02/11] lua: built-in module datetime Timur Safin via Tarantool-patches
2021-07-29 18:55   ` Oleg Babin via Tarantool-patches
2021-07-30 19:00     ` Timur Safin via Tarantool-patches
2021-07-31  6:29       ` Oleg Babin via Tarantool-patches
2021-07-31 16:51         ` Timur Safin via Tarantool-patches
2021-07-29 23:36   ` Vladislav Shpilevoy via Tarantool-patches
2021-07-30 15:39     ` Timur Safin via Tarantool-patches
2021-08-01 17:01       ` Vladislav Shpilevoy via Tarantool-patches
2021-08-01 20:23         ` Timur Safin via Tarantool-patches
2021-08-04 23:57           ` Vladislav Shpilevoy via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 03/11] lua, datetime: datetime tests Timur Safin via Tarantool-patches
2021-07-29 18:55   ` Oleg Babin via Tarantool-patches
2021-07-30 20:45     ` Timur Safin via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 04/11] lua, datetime: display datetime Timur Safin via Tarantool-patches
2021-07-29 18:55   ` Oleg Babin via Tarantool-patches
2021-07-30 21:48     ` Timur Safin via Tarantool-patches
2021-07-31  6:29       ` Oleg Babin via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 05/11] box, datetime: add messagepack support for datetime Timur Safin via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 06/11] box, datetime: datetime comparison for indices Timur Safin via Tarantool-patches
2021-07-29 18:56   ` Oleg Babin via Tarantool-patches
2021-07-30 22:18     ` Timur Safin via Tarantool-patches
2021-07-31  6:30       ` Oleg Babin via Tarantool-patches
2021-07-31  9:31         ` Timur Safin via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 07/11] lua, datetime: proper datetime encoding Timur Safin via Tarantool-patches
2021-07-29 18:57   ` Oleg Babin via Tarantool-patches
2021-07-30 22:20     ` Timur Safin via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 08/11] lua, datetime: calculated attributes for datetimes Timur Safin via Tarantool-patches
2021-07-29 18:57   ` Oleg Babin via Tarantool-patches
2021-07-30 22:30     ` Timur Safin via Tarantool-patches
2021-07-31  6:31       ` Oleg Babin via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 09/11] lua, datetime: time intervals support Timur Safin via Tarantool-patches
2021-07-29 18:58   ` Oleg Babin via Tarantool-patches [this message]
2021-07-30 22:58     ` Timur Safin via Tarantool-patches
2021-07-31  6:31       ` Oleg Babin via Tarantool-patches
2021-07-31  9:20         ` Timur Safin via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 10/11] lua, datetime: unixtime, timestamp setters in datetime.lua Timur Safin via Tarantool-patches
2021-07-29 18:58   ` Oleg Babin via Tarantool-patches
2021-07-30 23:11     ` Timur Safin via Tarantool-patches
2021-07-31  6:31       ` Oleg Babin via Tarantool-patches
2021-07-31  9:54         ` Timur Safin via Tarantool-patches
2021-07-28 10:34 ` [Tarantool-patches] [PATCH resend v2 11/11] datetime: changelog for datetime module Timur Safin via Tarantool-patches
2021-07-29 18:55 ` [Tarantool-patches] [PATCH resend v2 00/11] Initial datetime support Oleg Babin 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=ff90fb89-1c85-de43-ecb8-b72068127000@tarantool.org \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=olegrok@tarantool.org \
    --cc=tsafin@tarantool.org \
    --cc=v.shpilevoy@tarantool.org \
    /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

Tarantool development patches archive

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://lists.tarantool.org/tarantool-patches/0 tarantool-patches/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 tarantool-patches tarantool-patches/ https://lists.tarantool.org/tarantool-patches \
		tarantool-patches@dev.tarantool.org.
	public-inbox-index tarantool-patches

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git