Skip to content

Document interval arithmetic #2835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Totktonada opened this issue Apr 26, 2022 · 0 comments · Fixed by #3082
Closed

Document interval arithmetic #2835

Totktonada opened this issue Apr 26, 2022 · 0 comments · Fixed by #3082
Assignees
Labels
datetime feature A new functionality reference [location] Tarantool manual, Reference part server [area] Task relates to Tarantool's server (core) functionality

Comments

@Totktonada
Copy link
Member

Totktonada commented Apr 26, 2022

Product: Tarantool
Since: 2.10
Audience/target: dev
Root document: https://www.tarantool.io/en/doc/latest/reference/reference_lua/datetime
SME: @ tsafin

Can be done in conjunction with the Datetime epic.

Related to and should be mentioned in #2836

Interval arithmetic

If we need to shift date values by the given period of time we call either
modifier methods (i.e. :add or :sub) or apply interval arithmetic
using overloaded + (__add) or - (__sub) methods.

:add/:sub modify current object, but __add/__sub create copy of
object for operation result.

In the interval operation we sequentially calculate each interval
subcomponents
heading from largest (year) to smallest (nanosecond):

  • year - year(s);
  • month - month(s);
  • week - week(s);
  • day - day(s);
  • hour - hour(s);
  • min - minute(s);
  • sec - second(s);
  • nsec - nanosecond(s).

If results of operation exceed allowed range for any of component then
exception to be raised.

Modifier methods :add/:sub return self object thus it''s possible to
chain their calls together, i.e.

-- add 9000 years, 82 months, 5 weeks, 201 days, 183 hours, 292 minutes
-- and 191.001239234 seconds to given date
dt:add{
	year  = 9000,
	month = 82,
	week  = 5,
	day   = 201,

	sec   = 191,
	min   = 292,
	hour  = 183,

	nsec  = 1239234,
}

dt:sub{
	year  = 9000,
	month = 82,
	week  = 5,
	day   = 201,

	sec   = 191,
	min   = 292,
	hour  = 183,

	nsec  = 1239234,
}

-- chaining
dt:add{year = 2}:add{month = 2}:sub{day = 2}

-- create a copy of current date, while moving it to the next day
local dt2 = datetime.new(dt:totable()):add{day = 1}

Arithmetic operations

Date and interval objects may participate in arithmetic operations:

  • Sum of 2 intervals is interval object, which fields would be sum of each
    particular components of operands;
  • Subtraction is similar to summation above - result is interval object
    where each subcomponent would be result of subtraction of particular
    fields in original operands;
  • If you add date and interval then result is a date. Addition to be
    performed in a determined order from largest component (i.e. year) to
    smallest (nanosecond);
  • Subtraction of dates to produce an interval object. Difference of 2 time
    moments is performed not as difference of epoch seconds, but as
    difference of all subcomponents (i.e. years, months, days, hours,
    minutes and seconds);
  • Untyped table object may be used in each context where typed date or
    interval object used if left operand is typed object with overloaded
    operation of '+' or '-'.

Matrix of addition operands eligibility and their result type:

datetime interval table
datetime datetime datetime
interval datetime interval interval
table

Matrix of subtraction operands eligibility and their result type:

datetime interval table
datetime interval datetime datetime
interval interval interval
table

Date adjustions and leap years

It''s always tricky to operate with days if we move date between months of
different lengths. The default mode is - the day number in month of
original date should become the same day but not exceed the length of
resultant month.

  • 28 February of non-leap year if added +1 year should become 28 february
    of a leap year;
  • Addition of year to the 29 February of a leap year would result with 28
    February of a non-leap year;

The general rule is as following: addition of months to the date should
produce (if possible) the same day number, if this number is not exceeding
number of days in resultant month. Otherwise it produces the last day in
month.

  • 31 january + 1 month = 28 or 29 february;
  • 30 january + 1 month = 28 or 29 february;
  • 29 february + 1 month = 29 march;
  • 31 march + 1 month = 30 april.

Optionally you may prefer to use "accountant" logics of operations when
month operations (addition or subtraction) should leave last day in month
as last day, i.e.:

  • 31 january + 1 month = 28 or 29 february (depending on leap year);
  • 29 february + 1 month = 31 march;
  • 31 march + 1 month = 30 april;
  • 30 april + 1 month = 31 may.
  • 28 february 2001 + 1 month = 28 march 2001;
  • february 2004 + 1 month = 28 марта 2004;

This adjustment option named adjust may be passed as additional argument
to :add/:sub methods or additional argument to interval constructor
call.

There are 3 supported adjust values:

  • none last day mode is not maintained, if there is overflow then value
    is truncated to the maximum possible value in month (corresponding to
    the c-dt DT_LIMIT). This is default mode if not provided;
    -- 29.02.* -> 29.03.*
    dt:add( {month = 1, adjust = "none" } )
  • last last day mode ("accountant" mode) maintained. i.e. last day in
    original month should stay last day in resultant month. This corresponds
    to c-dt DT_SNAP mode;
-- 28.02.2001 -> 31.03.2001 -- last day in Feb 2001
-- 28.02.2004 -> 28.03.2004 -- not a last day in Feb => no abjustments
-- 29.02.2004 -> 31.03.2001 -- last day in Feb 2004
dt:add( {month = 1,  adjust = "last" } )
  • excess overflow to the next month performed. According to c-dt
    DT_EXCESS rules.

Stringization

Intervals show each their subcomponents as negative or positive value.
Seconds and nanoseconds normalized to the human-interpretable form.

  • '+1 seconds';
  • '+12 hours, 10 minutes, 30 seconds';
  • '-20 months, -10 weeks, -8 hours, -10 minutes, -30 seconds';
  • '-5000000 years, -20 months, -10 weeks, -10 minutes, -30 seconds';

Requested by @tsafin in tarantool/tarantool@08d6f7f.

@patiencedaur patiencedaur added this to the Estimate [@patiencedaur] milestone May 30, 2022
@patiencedaur patiencedaur added 3sp server [area] Task relates to Tarantool's server (core) functionality reference [location] Tarantool manual, Reference part feature A new functionality datetime and removed 3sp labels May 30, 2022
@patiencedaur patiencedaur removed this from the Estimate [@patiencedaur] milestone Jun 15, 2022
@veod32 veod32 self-assigned this Jul 25, 2022
@patiencedaur patiencedaur added 3sp and removed 5sp labels Aug 3, 2022
veod32 added a commit that referenced this issue Aug 12, 2022
…ct the list of the datetime functions and methods

Part of #2835
veod32 added a commit that referenced this issue Aug 15, 2022
veod32 added a commit that referenced this issue Aug 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
datetime feature A new functionality reference [location] Tarantool manual, Reference part server [area] Task relates to Tarantool's server (core) functionality
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants