-
Notifications
You must be signed in to change notification settings - Fork 43
Description
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:
nonelast day mode is not maintained, if there is overflow then value
is truncated to the maximum possible value in month (corresponding to
the c-dtDT_LIMIT). This is default mode if not provided;
-- 29.02.* -> 29.03.*
dt:add( {month = 1, adjust = "none" } )
lastlast day mode ("accountant" mode) maintained. i.e. last day in
original month should stay last day in resultant month. This corresponds
toc-dtDT_SNAPmode;
-- 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" } )
excessoverflow to the next month performed. According toc-dt
DT_EXCESSrules.
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.