--  These functions create SQL92 operators between datetime and interval
--  and vice versa:
--  -----------------------------------------
--  1st operand|operator|2nd operand|result  
--  -----------+--------+-----------+--------
--  datetime   |   -    |datetime   |interval
--  datetime   |   +    |interval   |datetime
--  datetime   |   -    |interval   |datetime
--  interval   |   +    |datetime   |datetime
--  interval   |   +    |interval   |interval
--  interval   |   -    |interval   |interval
--  interval   |   *    |number     |interval
--  interval   |   /    |number     |interval
--  number     |   *    |interval   |interval
--  -----------+--------+-----------+--------

set datestyle to 'iso';

--DATE + INTERVAL = DATE----------------------------------------------------
drop function date_pl_interval(date,timespan);
create function date_pl_interval(date,timespan) returns date as 
'
begin
	return date(datetime_pl_span($1,$2));
end; ' language 'plpgsql';

drop operator + (date,timespan);
create operator + (
        leftarg=date,
        rightarg=timespan,
        procedure=date_pl_interval,
        commutator='+',
        negator='-',
        restrict=eqsel,
        join=eqjoinsel
        );

--DATE - INTERVAL = DATE----------------------------------------------------
drop function date_mi_interval(date,timespan);
create function date_mi_interval(date,timespan) returns date as 
'
begin
	return date(datetime_mi_span($1,$2));
end; ' language 'plpgsql';

drop operator - (date,timespan);
create operator - (
        leftarg=date,
        rightarg=timespan,
        procedure=date_mi_interval,
        commutator='-',
        negator='+',
        restrict=eqsel,
        join=eqjoinsel
        );

--INTERVAL + DATE = DATE----------------------------------------------------
drop function interval_pl_date(timespan,date);
create function interval_pl_date(timespan,date) returns date as 
'
begin
	return date(datetime_pl_span($2,$1));
end; ' language 'plpgsql';

drop operator + (timespan,date);
create operator + (
        leftarg=timespan,
        rightarg=date,
        procedure=interval_pl_date,
        commutator='+',
        negator='-',
        restrict=eqsel,
        join=eqjoinsel
        );

--INTERVAL + TIMESTAMP = TIMESTAMP-----------------------------------------------
drop function interval_pl_timestamp(timespan,timestamp);
create function interval_pl_timestamp(timespan,timestamp) returns timestamp as 
'
begin
	return datetime_pl_span($2,$1);
end; ' language 'plpgsql';

drop operator + (timespan,timestamp);
create operator + (
        leftarg=timespan,
        rightarg=timestamp,
        procedure=interval_pl_timestamp,
        commutator='+',
        negator='-',
        restrict=eqsel,
        join=eqjoinsel
        );

--INTERVAL / NUMBER = INTERVAL------------------------------------------------
drop function interval_div_int4(timespan,integer);
create function interval_div_int4(timespan,integer) returns timespan as
'
begin
	return timespan_div($1,float8($2));
end; ' language 'plpgsql';

drop operator / (timespan,int4);
create operator / (
        leftarg=timespan,
        rightarg=int4,
        procedure=interval_div_int4,
        commutator='/',
        negator='*',
        restrict=eqsel,
        join=eqjoinsel
        );

drop operator / (timespan,float8);
create operator / (
        leftarg=timespan,
        rightarg=float8,
        procedure=timespan_div,
        commutator='/',
        negator='*',
        restrict=eqsel,
        join=eqjoinsel
        );

--NUMBER * INTERVAL = INTERVAL------------------------------------------------
drop function float8_mul_interval(float8,timespan);
create function float8_mul_interval(float8,timespan) returns timespan as
'declare
	i float8;
begin
	i:= date_part(''epoch'',$2);
	return i * $1;
end;
' language 'plpgsql';

drop function int4_mul_interval(integer,timespan);
create function int4_mul_interval(integer,timespan) returns timespan as
'
begin
	return float8_mul_interval(float8($1),$2);
end;
' language 'plpgsql';

drop operator * (float8,timespan);
create operator * (
        leftarg=float8,
        rightarg=timespan,
        procedure=float8_mul_interval,
        commutator='*',
        negator='/',
        restrict=eqsel,
        join=eqjoinsel
        );
drop operator * (int4,timespan);
create operator * (
        leftarg=int4,
        rightarg=timespan,
        procedure=int4_mul_interval,
        commutator='*',
        negator='/',
        restrict=eqsel,
        join=eqjoinsel
        );

--INTERVAL * NUMBER = INTERVAL------------------------------------------------
drop function interval_mul_float8(timespan,float8);
create function interval_mul_float8(timespan,float8) returns timespan as
'declare
	i float8;
begin
	i:= date_part(''epoch'',$1);
	return i * $2;
end;
' language 'plpgsql';

drop function interval_mul_int4(timespan,integer);
create function interval_mul_int4(timespan,integer) returns timespan as
'
begin
	return interval_mul_float8($1,float8($2));
end;
' language 'plpgsql';
drop operator * (timespan,int4);
create operator * (
        leftarg=timespan,
        rightarg=int4,
        procedure=interval_mul_int4,
        commutator='*',
        negator='/',
        restrict=eqsel,
        join=eqjoinsel
        );
drop operator * (timespan,float8);
create operator * (
        leftarg=timespan,
        rightarg=float8,
        procedure=interval_mul_float8,
        commutator='*',
        negator='/',
        restrict=eqsel,
        join=eqjoinsel
        );

--TIME + INTERVAL = TIME-----------------------------------time??
drop function time_pl_interval(time,timespan);
create function time_pl_interval(time,timespan) returns time as 
'
declare
	i1 timespan;
	i2 timespan;
begin
	i1:= $1;
	i2:= $2;
	i1:=i1 + i2;
	return i1;
end;
' language 'plpgsql';

drop operator + (time,timespan);
create operator + (
        leftarg=time,
        rightarg=timespan,
        procedure=time_pl_interval,
        commutator='+',
        negator='-',
        restrict=eqsel,
        join=eqjoinsel
        );

--INTERVAL + TIME = TIME------------------------------------------------
drop function interval_pl_time(timespan,time);
create function interval_pl_time(timespan,time) returns time as 
'
begin
	return $2 + $1;
 end;
' language 'plpgsql';

drop operator + (timespan,time);
create operator + (
        leftarg=timespan,
        rightarg=time,
        procedure=interval_pl_time,
        commutator='+',
        negator='-',
        restrict=eqsel,
        join=eqjoinsel
        );

--TIME - TIME = INTERVAL-------------------------------------------
drop function time_mi_time(time,time);
create function time_mi_time(time,time) returns timespan as 
'
declare
	i1 interval;
	i2 interval;
begin
	i1:= $1;
	i2:= $2;
	i1:=i1 - i2;
	return i1;
end;
' language 'plpgsql';

drop operator - (time,time);
create operator - (
        leftarg=time,
        rightarg=time,
        procedure=time_mi_time,
        commutator='-',
        negator='+',
        restrict=eqsel,
        join=eqjoinsel
        );

--TIME - INTERVAL = TIME------------------------------------------------
drop function time_mi_interval(time,timespan);
create function time_mi_interval(time,timespan) returns time as 
'
declare
	i2 time;
begin
	i2:= $2;
	return $1 - i2;
end;
' language 'plpgsql';

drop operator - (time,timespan);
create operator - (
        leftarg=time,
        rightarg=timespan,
        procedure=time_mi_interval,
        commutator='-',
        negator='+',
        restrict=eqsel,
        join=eqjoinsel
        );

--TIMESTAMP + INTERVAL = TIMESTAMP-------------------------------------------
drop function timestamp_pl_interval(timestamp,timespan);
create function timestamp_pl_interval(timestamp,timespan) returns timestamp as 
'
begin
	return datetime_pl_span($1,$2);
end;
' language 'plpgsql';

drop operator + (timestamp,timespan);
create operator + (
        leftarg=timestamp,
        rightarg=timespan,
        procedure=timestamp_pl_interval,
        commutator='+',
        negator='-',
        restrict=eqsel,
        join=eqjoinsel
        );

--TIMESTAMP - INTERVAL = TIMESTAMP----------------------------------------------
drop function timestamp_mi_interval(timestamp,timespan);
create function timestamp_mi_interval(timestamp,timespan) returns timestamp as 
'
begin
	return datetime_mi_span($1,$2);
end; ' language 'plpgsql';

drop operator - (timestamp,timespan);
create operator - (
        leftarg=timestamp,
        rightarg=timespan,
        procedure=timestamp_mi_interval,
        commutator='-',
        negator='+',
        restrict=eqsel,
        join=eqjoinsel
        );

--TIMESTAMP - TIMESTAMP = INTERVAL-----------------------------------------------
drop   function timestamp_mi_timestamp(timestamp,timestamp);
create function timestamp_mi_timestamp(timestamp,timestamp) returns timespan as 
'
declare
	i1 timespan;
	i2 timespan;
begin
	i1:= datetime_part(''epoch'',$1);
	i2:= datetime_part(''epoch'',$2);
	return i1 - i2;
end;
' language 'plpgsql';

drop operator - (timestamp,timestamp);
create operator - (
        leftarg=timestamp,
        rightarg=timestamp,
        procedure=timestamp_mi_timestamp,
        commutator='-',
        negator='+',
        restrict=eqsel,
        join=eqjoinsel
        );
--EXAMPLES--------------------------------------------------------------------
--datetimes + interval
SELECT DATE '1999-02-11' + INTERVAL '1 MONTH' as "date 1999-03-11";
SELECT TIME '15:00:00' + INTERVAL '1 HOUR' as "16:00:00 time";
SELECT TIMESTAMP '1999-02-11 15:00:00' + INTERVAL '1 MONTH' as "1999-03-11 15:00:00+01 timestamp";
--datetimes - datetime
SELECT DATE '1999-02-11' - DATE '1999-02-01' as "interval 10";
SELECT TIME '15:00:00'- TIME '01:30:00' as "13:30 interval";
SELECT TIMESTAMP '1999-02-11 15:00:00'- TIMESTAMP '1999-02-01 01:30:00' as "10 13:30 interval";
--datetimes - interval
SELECT DATE '1999-02-11' - INTERVAL '1 MONTH' as "date 1999-01-11";
SELECT TIME '15:00:00'- INTERVAL '1 HOUR' as "14:00:00 time";
SELECT TIMESTAMP '1999-02-11 15:00:00'- INTERVAL '1 MONTH' as "1999-01-11 15:00:00+01 timestamp";
--interval + datetime
SELECT INTERVAL '1 DAY'  + DATE '1999-02-11' as "date 1999-02-12";
SELECT INTERVAL '3 hour' + TIME '15:00:00' as "18:00:00 time";
SELECT INTERVAL '3 HOUR' + TIMESTAMP '1999-02-11 15:00:00' as "1999-02-11 18:00:00+01 timestamp";
--interval + interval
SELECT INTERVAL '3 hour' + INTERVAL '1 hour' as "04:00 interval";
--interval - interval
SELECT INTERVAL '4 hour' - INTERVAL '3 hour' as "01:00 interval";
--interval / number
SELECT INTERVAL '4 hour' / 2.0 as "02:00 interval";
--interval * number
SELECT INTERVAL '4 hour' * 3 as "12:00 interval";
--number * interval
SELECT 3 * INTERVAL '4 hour' as "12:00 interval";
