Oracle の add_months を SQL Server dateadd(month, ~) の結果と同じにするのを考えてみる‏

twitter で見かけたのでちょっと考えてみる。

SQL Server

select dateadd(month, 1, cast('2016/02/29' as date)) -- 2016/03/29

Oracle

select add_months('2016/02/29', 1) from dual -- 2016/3/31

足す前の日付と足した後の日付の 日 の部分が違った場合で、足した後の方が大きい時だけ問題になるで良いのかな。

  1. 日が同じ => 問題無し
  2. 日が違って、足した時の方が小さい => 足す前の 日 が足した後の月に存在しない 例:1/30 に 1ヶ月足したら 2/28 or 2/29 になるパターン
  3. 日が違って、足した時の方が大きい => 足す前が月末日、足す前の月の日数が足した後の月の日数より少ない。例:4/30 に 1ヶ月足したら 5/31

最後のパターンだけ差し替えるとこんな感じ。*1

create or replace function ADD_MONTHS_SQLSERVER(target date, plus number) return date
is 
  result date;
begin
  select add_months(target, plus) into result from dual;
  select 
    case 
      when to_char(result, 'DD') > to_char(target, 'DD')   
        then to_date(to_char(result, 'YYYYMM') || to_char(target, 'DD'), 'YYYYMMDD')
      else result 
    end into result
  from dual;
  
  return result;
end;

function 作るのいやだったらこんな感じ?

select
  case when to_char(add_months(sysdate, 2), 'DD') > to_char(sysdate, 'DD')
    then to_date(to_char(add_months(sysdate, 2), 'YYYYMM') || to_char(sysdate, 'DD'), 'YYYYMMDD')
    else add_months(sysdate, 2)
  end as add_months_sqlserver
from dual

LAST_DAY とか使うかなーと思ったけど、そうでもなかった。
※雰囲気動くとこまでしか確認してないから利用は自己責任で!

*1:時刻は考慮してない