Project: YugabyteDB (App Definition and Development / Database)
File: interval-interval-addition.md

title: Interval addition and subtraction [YSQL] headerTitle: Adding or subtracting a pair of interval values linkTitle: Interval-interval addition and subtraction description: Explains the semantics of adding or subtracting a pair of interval values. [YSQL] menu: v2.18: identifier: interval-interval-addition parent: interval-arithmetic weight: 20 type: docs {{< tip title="Download and install the date-time utilities code." >}} The code on this page depends on the code presented in the section . This is included in the larger that includes all of the reusable code that the overall section describes and uses. {{< /tip >}} This section presents a PL/pgSQL implementation of the model that explains how adding two interval values or subtracting one interval value from another works. Create a table function to display the model in action: ```plpgsql drop function if exists intervalintervaladdition_result(interval, interval); create function intervalintervaladdition_result(i1 in interval, i2 in interval) returns table(x text) language plpgsql as $body$ declare mmddss1 constant intervalmmddsst not null := intervalmmddss(i1); mmddss2 constant intervalmmddsst not null := intervalmmddss(i2); mmmodel constant int not null := mmddss1.mm + mmddss_2.mm; ddmodel constant int not null := mmddss1.dd + mmddss_2.dd; ssmodel constant double precision not null := mmddss1.ss + mmddss_2.ss; mmddssmodel constant intervalmmddsst not null := (mmmodel, ddmodel, ssmodel)::intervalmmddsst; imodel constant interval not null := intervalvalue(mmddss_model); i_actual constant interval not null := i1 + i2; mmddssactual constant intervalmmddsst not null := intervalmmddss(i_actual); begin x := 'input 1 mmddss: '|| tochar(mmddss1.mm, '999999990.9999')||' months' || tochar(mmddss1.dd, '999999990.9999')||' days' || tochar(mmddss1.ss, '999999990.9999')||' seconds' ; return next; x := 'input 2 mmddss: '|| tochar(mmddss2.mm, '999999990.9999')||' months' || tochar(mmddss2.dd, '999999990.9999')||' days' || tochar(mmddss2.ss, '999999990.9999')||' seconds' ; return next; x := 'intermediate model mmddss: '|| tochar(mmmodel, '999999990.9999')||' months' || tochar(ddmodel, '999999990.9999')||' days' || tochar(ssmodel, '999999990.9999')||' seconds' ; return next; x := 'ultimate model mmddss: '|| tochar(mmddssmodel.mm, '999999990.9999')||' months' || tochar(mmddssmodel.dd, '999999990.9999')||' days' || tochar(mmddssmodel.ss, '999999990.9999')||' seconds' ; return next; x := 'actual mmddss: '|| tochar(mmddssactual.mm, '999999990.9999')||' months' || tochar(mmddssactual.dd, '999999990.9999')||' days' || tochar(mmddssactual.ss, '999999990.9999')||' seconds' ; return next; x := ''; return next; x := 'ultimate model result: '||i_model::text; return next; end; $body$; ``` Test the function with values that are easy to understand: ```plpgsql select x from intervalintervaladdition_result( '6 months'::interval, '2 days'::interval); ``` This is the result: ```output input 1 mmddss: 6.0000 months 0.0000 days 0.0000 seconds input 2 mmddss: 0.0000 months 2.0000 days 0.0000 seconds intermediate model mmddss: 6.0000 months 2.0000 days 0.0000 seconds ultimate model mmddss: 6.0000 months 2.0000 days 0.0000 seconds actual mmddss: 6.0000 months 2.0000 days 0.0000 seconds ultimate model result: 6 mons 2 days ``` Test it with values that lead to remainder "spill-down" (in the ) from the mm field to the dd field and from the dd field to the ss field: ```plpgsql select x from intervalintervaladdition_result( '6.6 months 7.8 days 8 hours'::interval, '2.9 months 4.3 days 5 hours'::interval); ``` This is the result: ```output input 1 mmddss: 6.0000 months 25.0000 days

seconds input 2 mmddss: 2.0000 months 31.0000 days 43920.0000 seconds intermediate model mmddss: 8.0000 months 56.0000 days 141840.0000 seconds ultimate model mmddss: 8.0000 months 56.0000 days 141840.0000 seconds actual mmddss: 8.0000 months 56.0000 days 141840.0000 seconds ultimate model result: 8 mons 56 days 39:24:00 ``` Notice that the "spill-down" is an explicit consequence of the design of the algorithm that transforms an interval specification that uses values for each of years, months, days, hours, minutes, and seconds to the target .) So it affects the calculation of the values mmddss1 and mmddss2. But thereafter, addition or subtraction of the already integral mm and dd fields can only produce integral totals; and so no further "spill-down" can take place. This is why the "intermediate model mmddss" row and the "ultimate model mmddss" row in the output are identical. (Multiplication and division of an interval value by a real number are critically different in this respect.) This result shows that a practice that the user might adopt to use only interval values that have just a single non-zero mm, dd, or ss value in the internal representation can easily be thwarted by interval-interval addition or subtraction. The section shows how you can guarantee that you avoid this problem. The procedure assertintervalintervaladditionmodelok() is a mechanical re-write of the function intervalintervaladditionresult() that produces no output. Instead, it uses an assert statement to check that the model produces the same result as the native implementation. This makes it suitable for testing the model with a wide range of input values. Create it thus: ```plpgsql drop procedure if exists assertintervalintervaladditionmodel_ok(interval, interval); create procedure assertintervalintervaladditionmodel_ok(i1 in interval, i2 in interval) language plpgsql as $body$ declare mmddss1 constant intervalmmddsst not null := intervalmmddss(i1); mmddss2 constant intervalmmddsst not null := intervalmmddss(i2); mmmodel constant int not null := mmddss1.mm + mmddss_2.mm; ddmodel constant int not null := mmddss1.dd + mmddss_2.dd; ssmodel constant double precision not null := mmddss1.ss + mmddss_2.ss; mmddssmodel constant intervalmmddsst not null := (mmmodel, ddmodel, ssmodel)::intervalmmddsst; imodel constant interval not null := intervalvalue(mmddss_model); i_actual constant interval not null := i1 + i2; begin assert iactual == imodel, 'assert failed'; end; $body$; ``` Notice the use of the , `==`. It's essential to use this, and not the native `=`, because two interval values that compare as true with the native `=` operator but as false with the strict `==` operator can produce different results when added to a timestamptz valuesee the section . Use the assert procedure thus: ```plpgsql call assertintervalintervaladditionmodel_ok( '6 months 7 days 8 hours'::interval, '2 months 4 days 5 hours'::interval); call assertintervalintervaladditionmodel_ok( '6 months 4 days 8 hours'::interval, -'2 months 7 days 5 hours'::interval); call assertintervalintervaladditionmodel_ok( '6 months 4 days 8 hours'::interval, ' -9.123456 years, 18.123456 months, -700.123456 days, 97.123456 hours, -86.123456 minutes, 75.123456 seconds '::interval); ``` The tests finish silently, showing that the hypothesised model has not been disproved.