Timur Safin
authored
Part of #6923 NO_TEST=docrequest @TarantoolBot document Title: Document interval arithmetic 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';
Name | Last commit | Last update |
---|