How to find first Thursday of a month using Oracle SQL? - sql

How to find First Thursday of this month using oracle?
select trunc(sysdate, 'MM')firstday from dual;
above is getting firstday of this month

Try with the below SQL
SELECT NEXT_DAY(TRUNC(SYSDATE, 'MONTH'),'THURSDAY') FROM DUAL;
SQL Fiddle

If you just get the next Thursday after the first day of the month, the after part means that if the 1st is itself a Thursday you will acually get a date a week too late. For instance, 2018-02-01 was a Thursday, and putting that into next_day() gives you 2018-02-08.
You need to get the next Thursday after the last day of the previous month:
next_day(trunc(sysdate, 'MM') - 1, 'THU')
Demo of both values; look at February, March and November in particular:
with t (dt) as (
select add_months(date '2018-01-15' , level - 1) from dual connect by level <= 12
)
select next_day(trunc(dt, 'MM'), 'THU') as wrong,
next_day(trunc(dt, 'MM') - 1, 'THU') as ok
from t;
WRONG OK
---------- ----------
2018-01-04 2018-01-04
2018-02-08 2018-02-01
2018-03-08 2018-03-01
2018-04-05 2018-04-05
2018-05-03 2018-05-03
2018-06-07 2018-06-07
2018-07-05 2018-07-05
2018-08-02 2018-08-02
2018-09-06 2018-09-06
2018-10-04 2018-10-04
2018-11-08 2018-11-01
2018-12-06 2018-12-06

Related

Get quarter start/end dates for more than a year (start year to current year)

I've been trying to get start and end dates range for each quarter given a specific date/year, like this:
SELECT DATEADD(mm, (quarter - 1) * 3, year_date) StartDate,
DATEADD(dd, 0, DATEADD(mm, quarter * 3, year_date)) EndDate
--quarter QuarterNo
FROM
(
SELECT '2012-01-01' year_date
) s CROSS JOIN
(
SELECT 1 quarter UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4
) q
which produces the following output:
2012-01-01 00:00:00 2012-04-01 00:00:00
2012-04-01 00:00:00 2012-07-01 00:00:00
2012-07-01 00:00:00 2012-10-01 00:00:00
2012-10-01 00:00:00 2013-01-01 00:00:00
Problem: I need to do this for a given start_date and end_date, the problem being the end_date=current_day, so how can I achieve this:
2012-01-01 00:00:00 2012-04-01 00:00:00
2012-04-01 00:00:00 2012-07-01 00:00:00
2012-07-01 00:00:00 2012-10-01 00:00:00
2012-10-01 00:00:00 2013-01-01 00:00:00
... ...
2021-01-01 00:00:00 2021-01-06 00:00:00
I think here is what you want to do :
SET startdatevar AS DATEtime = '2020-01-10'
;WITH RECURSIVE cte AS (
SELECT startdatevar AS startdate , DATEADD(QUARTER, 1 , startdatevar) enddate , 1 quarter
UNION ALL
SELECT enddate , CASE WHEN DATEADD(QUARTER, 1 , enddate) > CURRENT_DATE() THEN GETDATE() ELSE DATEADD(QUARTER, 1 , enddate) END enddate, quarter + 1
FROM cte
WHERE
cte.enddate <= CURRENT_DATE()
and quarter < 4
)
SELECT * FROM cte
to use your code , if you want to have more than 4 quarters :
SET quarter_limit = DATEDIFF(quarter , <startdate>,<enddate>)
;WITH RECURSIVE cte(q, qDate,enddate) as
(
select 1,
DATEFROMPARTS(year('2012-01-01'::date), 1, 1) -- First quarter date
,time_slice('2012-01-01'::date, 3, 'MONTH', 'END')
UNION ALL
select q+1,
DATEADD(q, 1, qdate) -- next quarter start date
,time_slice(qdate::date, (q+1)*3, 'MONTH', 'END')
from cte
where q < quarter_limit -- limiting the number of next quarters
AND cte.endDate <= <enddate>
)
SELECT * FROM cte
After #eshirvana's answer, I came up with this slightly change after your answer:
WITH RECURSIVE cte(q, qDate,enddate) as
(
select 1,
DATEFROMPARTS(year('2012-01-01'::date), 1, 1) -- First quarter date
,time_slice('2012-01-01'::date, 3, 'MONTH', 'END')
UNION ALL
select q+1,
DATEADD(q, 1, qdate) -- next quarter start date
,time_slice(qdate::date, (q+1)*3, 'MONTH', 'END')
from cte
where q <4 -- limiting the number of next quarters
AND cte.endDate <= CURRENT_DATE()
)
SELECT * FROM cte
Which works fine for whatever year I pass there (2012 will produce 4 records, 2021 just one, since we're still on the first quarter right now).
[EDIT]: it still doesn't work as expected after your 2nd code sugestion:
WITH RECURSIVE cte(q, qDate,enddate) as
(
select 1,
DATEFROMPARTS(year('2012-01-01'::date), 1, 1) -- First quarter date
,CASE WHEN time_slice('2012-01-01'::date, 3, 'MONTH', 'END') > CURRENT_DATE
THEN current_date
ELSE time_slice('2012-01-01'::date, 3, 'MONTH', 'END')
END
UNION ALL
select q+1,
DATEADD(q, 1, qdate) -- next quarter start date
,time_slice(qdate::date, (q+1)*3, 'MONTH', 'END')
from cte
where q < DATEDIFF(quarter , '2012-01-01'::date,'2021-01-06'::date)
AND cte.endDate <= '2021-01-06'::date
)
SELECT * FROM cte
is outputing this:
Sorry #eshirvana, it doesn't work as expected though. It all goes well to some point, but it's not returning all the records. Instead, it produces less records and wrong one, like this:
1 2012-01-01 2012-04-01
2 2012-04-01 2012-07-01
3 2012-07-01 2012-10-01
4 2012-10-01 2013-01-01
5 2013-01-01 2013-10-01
6 2013-04-01 2013-07-01
7 2013-07-01 2013-10-01
8 2013-10-01 2014-01-01
9 2014-01-01 2015-01-01
10 2014-04-01 2015-01-01
11 2014-07-01 2016-10-01
12 2014-10-01 2015-01-01
13 2015-01-01 2015-07-01
14 2015-04-01 2015-07-01
15 2015-07-01 2018-10-01
16 2015-10-01 2018-01-01
17 2016-01-01 2016-10-01
18 2016-04-01 2019-07-01
19 2016-07-01 2017-07-01
20 2016-10-01 2020-01-01
21 2017-01-01 2017-04-01
22 2017-04-01 2019-07-01
23 2017-07-01 2021-10-01
Although my logic it's still not ok for not printing just Q1 dates for 2021, could this output issues be related to date format or something?
Now, it seems to be working, at least for 2012-01-01 till today (2021-01-06).
The code :
WITH RECURSIVE cte(q, qDate,enddate) as
(
select
-- it might not be the first quarter, so better to protect that:
quarter('2012-01-01'::date)::numeric
, DATEFROMPARTS(year('2012-01-01'::date), 1, 1) -- First quarter date
, CASE WHEN time_slice('2012-01-01'::date, 3, 'MONTH', 'END') > '2021-01-06'::date
THEN '2021-01-06'::date
ELSE time_slice('2012-01-01'::date, 3, 'MONTH', 'END')
END
UNION ALL
select q+1
, DATEADD(q, 1, qdate) -- next quarter start date
,CASE WHEN time_slice(DATEADD(q, 1, qdate), 3, 'MONTH', 'END')> '2021-01-06'::date
THEN '2021-01-06'::date
ELSE time_slice(DATEADD(q, 1, qdate), 3, 'MONTH', 'END')
END
from cte
where q <= DATEDIFF(quarter , '2012-01-01'::date,'2021-01-06'::date)
AND cte.endDate <= '2021-01-06'::date
)
SELECT * FROM cte
The output:
1 2012-01-01 2012-04-01
2 2012-04-01 2012-07-01
3 2012-07-01 2012-10-01
4 2012-10-01 2013-01-01
5 2013-01-01 2013-04-01
6 2013-04-01 2013-07-01
7 2013-07-01 2013-10-01
8 2013-10-01 2014-01-01
9 2014-01-01 2014-04-01
10 2014-04-01 2014-07-01
11 2014-07-01 2014-10-01
12 2014-10-01 2015-01-01
13 2015-01-01 2015-04-01
14 2015-04-01 2015-07-01
15 2015-07-01 2015-10-01
16 2015-10-01 2016-01-01
17 2016-01-01 2016-04-01
18 2016-04-01 2016-07-01
19 2016-07-01 2016-10-01
20 2016-10-01 2017-01-01
21 2017-01-01 2017-04-01
22 2017-04-01 2017-07-01
23 2017-07-01 2017-10-01
24 2017-10-01 2018-01-01
25 2018-01-01 2018-04-01
26 2018-04-01 2018-07-01
27 2018-07-01 2018-10-01
28 2018-10-01 2019-01-01
29 2019-01-01 2019-04-01
30 2019-04-01 2019-07-01
31 2019-07-01 2019-10-01
32 2019-10-01 2020-01-01
33 2020-01-01 2020-04-01
34 2020-04-01 2020-07-01
35 2020-07-01 2020-10-01
36 2020-10-01 2021-01-01
37 2021-01-01 2021-01-06
In case you're wondering: yes, the idea is to present the end_date as last_day of the month+one. But it could easily be adapted.
It's not pretty, but I think it's somehow easy to understand.

2020-02-29 minus one year caused error ORA-01839: date not valid for month specified

I have a simple query as:
select to_date('2020-02-29', 'yyyy-mm-dd') - interval '1' year from dual
I think the result should be 2019-02-28, but oracle throws error as:
Error report -
ORA-01839: date not valid for month specified
That is the documented behaviour; it even gives this as an example:
When interval calculations return a datetime value, the result must be
an actual datetime value or the database returns an error. For
example, the next two statements return errors:
SELECT TO_DATE('31-AUG-2004','DD-MON-YYYY') + TO_YMINTERVAL('0-1')
FROM DUAL;
SELECT TO_DATE('29-FEB-2004','DD-MON-YYYY') + TO_YMINTERVAL('1-0')
FROM DUAL;
The first fails because adding one month to a 31-day month would
result in September 31, which is not a valid date. The second fails
because adding one year to a date that exists only every four years is
not valid. However, the next statement succeeds, because adding four
years to a February 29 date is valid:
SELECT TO_DATE('29-FEB-2004', 'DD-MON-YYYY') + TO_YMINTERVAL('4-0')
FROM DUAL;
TO_DATE('
---------
29-FEB-08
The alternative is to use add_months(..., -12) (docs), which won't error:
select add_months(date '2020-02-29', -12) from dual;
ADD_MONTHS
----------
2019-02-28
But note how that deals with different number of days in the month; not really an issue when you're going back exactly a year, but still something to be aware of:
If date is the last day of the month or if the resulting month has fewer days than the day component of date, then the result is the last day of the resulting month.
So some of these might not do what you expected:
with rcte (dt) as (
select last_day(date '2020-01-01')
from dual
union all
select last_day(trunc(dt, 'MM') + interval '1' month)
from rcte
where dt < date '2020-06-01'
)
select dt,
add_months(dt, -12) as minus12, add_months(dt, -3) as minus3, add_months(dt, -1) as minus1,
add_months(dt, 1) as plus1, add_months(dt, 3) as plus3, add_months(dt, 12) as plus12
from rcte
order by dt;
DT MINUS12 MINUS3 MINUS1 PLUS1 PLUS3 PLUS12
---------- ---------- ---------- ---------- ---------- ---------- ----------
2020-01-31 2019-01-31 2019-10-31 2019-12-31 2020-02-29 2020-04-30 2021-01-31
2020-02-29 2019-02-28 2019-11-30 2020-01-31 2020-03-31 2020-05-31 2021-02-28
2020-03-31 2019-03-31 2019-12-31 2020-02-29 2020-04-30 2020-06-30 2021-03-31
2020-04-30 2019-04-30 2020-01-31 2020-03-31 2020-05-31 2020-07-31 2021-04-30
2020-05-31 2019-05-31 2020-02-29 2020-04-30 2020-06-30 2020-08-31 2021-05-31
2020-06-30 2019-06-30 2020-03-31 2020-05-31 2020-07-31 2020-09-30 2021-06-30

Casting DATE returns "Invalid Month" error

My fiscal year starts on May 1 and ends Apr 30. I am trying to return the fiscal year start date using a CASE statement.
I.e.
CAST(
CASE
WHEN TO_NUMBER (TO_CHAR (GET_DATE, 'MM')) IN (11,
12,
5,
6,
7,
8,
9,
10)
THEN
'05/01/'
|| TO_NUMBER (TO_CHAR (TRUNC (get_date, 'year'), 'YYYY'))
WHEN TO_NUMBER (TO_CHAR (GET_DATE, 'MM')) IN (1,
2,
3,
4)
THEN
'05/01/'
|| TO_NUMBER (TO_CHAR (TRUNC (get_date, 'year'), 'YYYY') - 1)
END AS DATE)
I am getting "invalid month" when I use the cast but when I take it off, it defaults to number. Either way, i'm not getting the results I want.
Just subtract 4 (four) months off your date and pull the year out of it (use trunc to reduce it to the year) then add 4 (four) months back on
SELECT
ADD_MONTHS(
TRUNC(
ADD_MONTHS( <yourdate> ,-4),
'YEAR'),
4)
FROM DUAL;
As to why it works:
We have some example dates: 25-04-2009 13-07-2009
These are in the fiscal years beginning: 01-05-2008 01-05-2009
We subtract 4 months from the date: 25-12-2008 13-03-2009
We trunc down to the year start: 01-01-2008 01-01-2009
We add 4 months back on to get to May: 01-05-2009 01-05-2009
Why is it good/better than converting to string and back? Well, that's the reason right there. Dates are represented as a number, and this method keeps it as a number and works entirely off maths; adding, rounding and substracting. It's always better to avoid unnecessary data type conversions, because thy're slow, resource intensive and can introduce unexpected conversion errors
Using TRUNC with dates is one of the coolest things oracle does with dates that other databases just can't handle. Being able to take any date and TRUNC() it to the start of the year/month/day/hour/minute/weekday etc month is a great help in reports where events are logged to millisecond precision, but you want to summarise or work with them in terms of "number of things that happened this week/month" etc
For Oracle, this get the Start of Fiscal Year for any date.
Just replace "sysdate" function with a variable of type DATE or a column name of type DATE:
select /* for Oracle */
to_date(CASE
WHEN extract(month from sysdate)<5 then
extract(year from sysdate)-1
ELSE
extract(year from sysdate)
end||'-05-01',
'yyyy-mm-dd') as start_fiscal_year
from dual;
Testing on Oracle database using anonymous block:
Declare
/*=========================================================================================
-- objective: calculate Start fiscal date
-- Fisacal year starts on May 1 and ends April 30
--
-- https://stackoverflow.com/questions/52426117/casting-date-returns-invalid-month-error-in-plsql
--
-- Database: Oracle
--
-- 2018-09-20 alvalongo
==========================================================================================*/
dtStart_date date:=to_date('2018-01-10','yyyy-mm-dd');
dtAny_date date;
dtStart_fiscal_date date;
Begin
dbms_output.put_line('I |ANY_DATE |START_FISCAL_YEAR');
for nuI in 0..24 loop
dtAny_date:=add_months(dtStart_date,nuI);
--
select to_date(CASE
WHEN extract(month from dtAny_date)<5 then
extract(year from dtAny_date)-1
ELSE
extract(year from dtAny_date)
end||'-05-01','yyyy-mm-dd') as start_fiscal_year
into dtStart_fiscal_date
from dual;
if extract(month from dtAny_date)=5 then
dbms_output.put_line('--|----------|----------');
end if;
dbms_output.put_line(lpad(nuI,2)
||'|'||to_char(dtAny_date ,'yyyy-mm-dd')
||'|'||to_char(dtStart_fiscal_date,'yyyy-mm-dd')
);
End loop;
End;
/
Output using dbms_output buffer:
I |ANY_DATE |START_FISCAL_YEAR
0|2018-01-10|2017-05-01
1|2018-02-10|2017-05-01
2|2018-03-10|2017-05-01
3|2018-04-10|2017-05-01
--|----------|----------
4|2018-05-10|2018-05-01
5|2018-06-10|2018-05-01
6|2018-07-10|2018-05-01
7|2018-08-10|2018-05-01
8|2018-09-10|2018-05-01
9|2018-10-10|2018-05-01
10|2018-11-10|2018-05-01
11|2018-12-10|2018-05-01
12|2019-01-10|2018-05-01
13|2019-02-10|2018-05-01
14|2019-03-10|2018-05-01
15|2019-04-10|2018-05-01
--|----------|----------
16|2019-05-10|2019-05-01
17|2019-06-10|2019-05-01
18|2019-07-10|2019-05-01
19|2019-08-10|2019-05-01
20|2019-09-10|2019-05-01
21|2019-10-10|2019-05-01
22|2019-11-10|2019-05-01
23|2019-12-10|2019-05-01
24|2020-01-10|2019-05-01
Total execution time 517 ms
Expanding on #CaiusJard's answer, you can get the start of the fiscal year with:
add_months(trunc(add_months(get_date, -4), 'YYYY'), 4)
The add_months(get_date, -4) subtracts four months from the starting date, so a value in January to April will be adjusted to a date in September to December of the previous year. So for instance, 2018-03-11 will become 2017-11-11. But dates dates from May onwards will stay in the same year, so for instance 2018-07-04 becomes 2018-03-04.
Then the trunc(..., 'YYYY') truncates that adjusted value to the first day of its year. So 2018-03-11 becomes 2017-11-11 which becomes 2017-01-01; and 2018-07-04 becomes 2018-03-04 which becomes 2018-01-01.
Then the outer add_months(..., 4) adds four months back on to that adjusted value. So 2018-03-11 becomes 2017-11-11 which becomes 2017-01-01 which finally becomes 2017-05-01; and 2018-07-04 becomes 2018-03-04 which becomes 2018-01-01 which finally becomes 2018-05-01.
To get the last day of the fiscal year you can do the same thing but add an extra 12 months in the final calculation - which gives you the start of the next fiscal year - and then subtract one day:
add_months(trunc(add_months(get_date, -4), 'YYYY'), 16) - 1
Read more about the add_months() and trunc() functions, and about date arithmetic, in the documentation.
Demo with dummy dates in a CTE to show the steps in the adjustment:
with your_table (get_date) as (
select add_months(date '2018-01-15', level)
from dual
connect by level <= 30
)
select get_date,
add_months(get_date, -4) as adjusted_month,
trunc(add_months(get_date, -4), 'YYYY') as adjusted_year,
add_months(trunc(add_months(get_date, -4), 'YYYY'), 4) as start_date,
add_months(trunc(add_months(get_date, -4), 'YYYY'), 16) - 1 as end_date
from your_table
order by get_date;
GET_DATE ADJUSTED_M ADJUSTED_Y START_DATE END_DATE
---------- ---------- ---------- ---------- ----------
2018-02-15 2017-10-15 2017-01-01 2017-05-01 2018-04-30
2018-03-15 2017-11-15 2017-01-01 2017-05-01 2018-04-30
2018-04-15 2017-12-15 2017-01-01 2017-05-01 2018-04-30
2018-05-15 2018-01-15 2018-01-01 2018-05-01 2019-04-30
2018-06-15 2018-02-15 2018-01-01 2018-05-01 2019-04-30
2018-07-15 2018-03-15 2018-01-01 2018-05-01 2019-04-30
...
2019-01-15 2018-09-15 2018-01-01 2018-05-01 2019-04-30
2019-02-15 2018-10-15 2018-01-01 2018-05-01 2019-04-30
2019-03-15 2018-11-15 2018-01-01 2018-05-01 2019-04-30
...
2020-02-15 2019-10-15 2019-01-01 2019-05-01 2020-04-30
2020-03-15 2019-11-15 2019-01-01 2019-05-01 2020-04-30
2020-04-15 2019-12-15 2019-01-01 2019-05-01 2020-04-30
2020-05-15 2020-01-15 2020-01-01 2020-05-01 2021-04-30
2020-06-15 2020-02-15 2020-01-01 2020-05-01 2021-04-30
2020-07-15 2020-03-15 2020-01-01 2020-05-01 2021-04-30
db<>fiddle
As noted elsewhere in comments, your original code is erroring because cast(<string> as date) uses your session's NLS settings, and the string you are constructing does not match that setting. You could use to_date() instead of case so you can supply the expected format mask (see #alvalongo's answer!).
This should be work, if you want to maintain the CASE statement
select CASE WHEN
MONTH(CURRENT_DATE) < 5 THEN
YEAR(CURRENT_DATE)-1 ELSE YEAR(CURRENT_DATE)
end
I tried the following code and it worked for me. DATE is an inbuilt key word so we cannot use that as the column alias. I changed that to FS_START_DATE.
SELECT SYSDATE, TO_DATE(CASE
WHEN TO_NUMBER (TO_CHAR (SYSDATE, 'MM')) IN (11, 12, 5,6,7, 8,9, 10)
THEN '05/01/' || TO_NUMBER (TO_CHAR (TRUNC (SYSDATE, 'year'), 'YYYY'))
WHEN TO_NUMBER (TO_CHAR (SYSDATE, 'MM')) IN (1, 2, 3,4)
THEN '05/01/' || TO_NUMBER (TO_CHAR (TRUNC (SYSDATE, 'year'), 'YYYY') - 1)
END,'MM/DD/YYYY') AS FS_START_DATE FROM DUAL;
I am using TO_DATE function and this returns me 01-MAY-2018.

Select data where days between two dates are part of a given month

My data looks like below, and I need to show the ids where interval between date1 and date2 are part of a given month/year parameter.
Eg.: for July 2018 I need ids from 1 to 7.
date1 date2 id
---------- ---------- --------
2017-11-01 2018-08-28 1
2018-06-05 2018-07-05 2
2018-06-05 2019-05-07 3
2018-06-05 2018-08-08 4
2018-07-01 2018-07-31 5
2018-07-07 2018-07-15 6
2018-07-27 2018-08-05 7
2018-06-01 2018-06-07 8
2018-08-03 2018-09-01 9
solution is quite simple
SELECT
id
FROM
YOUR_TABLE
WHERE
date1<=YOUR_DATE_END_OF_MONTH AND date2>=YOUR_DATE_START_OF_MONTH
e.g. for July 2018
SELECT
id
FROM
YOUR_TABLE
WHERE
date1<='2018-07-31' AND date2>='2018-07-01'
or if you do not need to calculate first end day of the month (but this do not use any indexes if exists on date1 and date2)
SELECT
id
FROM
YOUR_TABLE
WHERE
EXTRACT(YEAR FROM date1)*12 + EXTRACT(MONTH FROM date1)<=2018*12 + 7
AND EXTRACT(YEAR FROM date2)*12 + EXTRACT(MONTH FROM date2)>=2018*12 + 7

Oracle SQL - Same day last year (leap year proof)

I'm looking to find the same day last year in oracle sql. For example Wednesday, March 16, 2016 would be Wednesday, March 18, 2015 for last year. So the closest day.
The following code worked good until the current leap year and broke after Feb 29th 2016.
this was my old statement which does not work for all dates:
NEXT_DAY(TRUNC(ADD_MONTHS(date, -12), 'iw')-2, TO_CHAR(date, 'DY'))
Simply subtract 7*52 = 364 days instead :-)
I think this may be correct
SQL Test:
WITH data
AS ( SELECT SYSDATE - (LEVEL - 1) this_year_date,
TO_NUMBER (TO_CHAR (SYSDATE - (LEVEL - 1), 'D'))
this_year_day_of_week,
ADD_MONTHS (SYSDATE - (LEVEL - 1), -12) last_year_date,
TO_NUMBER (
TO_CHAR (ADD_MONTHS (SYSDATE - (LEVEL - 1), -12), 'D'))
last_year_day_of_week
FROM DUAL
CONNECT BY LEVEL <= 300),
crunching
AS (SELECT data.*,
(CASE
WHEN this_year_day_of_week > last_year_day_of_week
THEN
this_year_day_of_week - last_year_day_of_week
WHEN this_year_day_of_week = last_year_day_of_week
THEN
0
ELSE
last_year_day_of_week - this_year_day_of_week
END)
math
FROM data)
SELECT TO_CHAR (crunching.this_year_date, 'yyyy-MM-dd Day') ty_date,
math,
(CASE
WHEN math = 0
THEN
TO_CHAR (last_year_date, 'yyyy-MM-dd Day')
WHEN math > 2
THEN
TO_CHAR ( (last_year_date - math) + 7, 'yyyy-MM-dd Day')
ELSE
TO_CHAR (last_year_date + math, 'yyyy-MM-dd Day')
END)
final_answer
FROM crunching
SQL Fiddle: http://sqlfiddle.com/#!4/9eecb7d/18211
Output:
TY_DATE MATH FINAL_ANSWER
2016-03-17 Thursday 2 2015-03-19 Thursday
2016-03-16 Wednesday 2 2015-03-18 Wednesday
2016-03-15 Tuesday 2 2015-03-17 Tuesday
2016-03-14 Monday 5 2015-03-16 Monday
2016-03-13 Sunday 5 2015-03-15 Sunday
2016-03-12 Saturday 2 2015-03-14 Saturday
2016-03-11 Friday 2 2015-03-13 Friday
2016-03-10 Thursday 2 2015-03-12 Thursday
2016-03-09 Wednesday 2 2015-03-11 Wednesday
2016-03-08 Tuesday 2 2015-03-10 Tuesday
2016-03-07 Monday 5 2015-03-09 Monday
2016-03-06 Sunday 5 2015-03-08 Sunday
2016-03-05 Saturday 2 2015-03-07 Saturday
2016-03-04 Friday 2 2015-03-06 Friday
2016-03-03 Thursday 2 2015-03-05 Thursday
2016-03-02 Wednesday 2 2015-03-04 Wednesday
2016-03-01 Tuesday 2 2015-03-03 Tuesday
2016-02-29 Monday 5 2015-03-02 Monday
2016-02-28 Sunday 6 2015-03-01 Sunday
2016-02-27 Saturday 1 2015-02-28 Saturday
2016-02-26 Friday 1 2015-02-27 Friday
2016-02-25 Thursday 1 2015-02-26 Thursday
2016-02-24 Wednesday 1 2015-02-25 Wednesday
2016-02-23 Tuesday 1 2015-02-24 Tuesday
2016-02-22 Monday 1 2015-02-23 Monday
2016-02-21 Sunday 6 2015-02-22 Sunday
2016-02-20 Saturday 1 2015-02-21 Saturday
2016-02-19 Friday 1 2015-02-20 Friday
2016-02-18 Thursday 1 2015-02-19 Thursday
2016-02-17 Wednesday 1 2015-02-18 Wednesday
How about add_months( [date], -12 ) ?