I would use:
set @YRS = DATEDIFF(year,@FromDt,GETDATE()) +
CASE WHEN DATEADD(year,DATEDIFF(year,@FromDt,GETDATE()),@FromDt) > GETDATE()
THEN -1 ELSE 0 END
set @FromDt = DATEADD(year,@YRS,@FromDt)
set @MNT = DATEDIFF(month,@FromDt,GETDATE()) +
CASE WHEN DATEADD(month,DATEDIFF(month,@FromDt,GETDATE()),@FromDt) > GETDATE()
THEN -1 ELSE 0 END
set @FromDt = DATEADD(month,@MNT,@FromDt)
set @DY = DATEDIFF(day,@FromDt,GETDATE())
For me, these are far more readily understood - we first get a years value that we're happy with, then add that onto @FromDt
. Then we can do a direct DATEDIFF
for months without much fussing around with modulo arithmetic. Similarly, once we're happy with a months value, we adjust @FromDt
again so that a simple DATEDIFF
gives us the correct number of days.
(You can eliminate some further calculation by first setting @YRS
and then working out whether to adjust it, but not sure if that's needed here)
The reason we have to do the slight fiddle around to get the correct @YRS
and @MNT
values is that DATEDIFF
:
Returns the count (signed integer) of the specified datepart boundaries crossed between the specified startdate and enddate.
Which is to say, that DATEDIFF(year,'20121231','20130101')
returns 1 (as does DATEDIFF(year,'20120101','20131231')
). Which is correct per its own definition. But we want a value that never overshoots if we add that value back onto the earlier of the two dates - so we have to compensate by sometimes subtracting 1.