Mark Gilbert's Blog

Science and technology, served light and fluffy.

Now where did I put that day? .NET Date Math, and Reversibility

This past week, I hit an interesting quirk with dates in .NET (or possibly it’s a quirk with the Gregorian calendar).  I started by subtracting 6 months from "today" to get a new date, and then later would add 6 months back to make sure I would get "today" again.  This scenario was part of a very simple unit test that I had written weeks ago, and had been passing fine for all of that time.

That is, until "today" was 8/29/2013.  On that day, my test failed.

If "today" were 8/15/2013, then subtracting 6 months would yield 2/15/2013 – just what I’d expect;

            DateTime InitialDate, NewDate;
            InitialDate = DateTime.Parse("8/15/2013");
            NewDate = InitialDate.AddMonths(-6);
            // NewDate will be 2/15/2013

However, if I started with 8/29/2013, there’s a snag:

            DateTime InitialDate, NewDate;
            InitialDate = DateTime.Parse("8/29/2013");
            NewDate = InitialDate.AddMonths(-6);
            // NewDate will be 2/28/2013

There is no 29th day of February in 2013, so to return a valid date .NET returns 2/28/2013 instead.  What happens when I add 6 months to 2/28/2013?

            DateTime InitialDate, NewDate;
            InitialDate = DateTime.Parse("2/28/2013");
            NewDate = InitialDate.AddMonths(6);
            // NewDate will be 8/28/2013

I get 8/28/2013 – not what I originally started with.  I actually end up losing a day due to the equivalent of a rounding error for calendars.

What if I were to go in the other direction originally – start with 8/29/2013 and ADD 6 months?

            DateTime InitialDate, NewDate;
            InitialDate = DateTime.Parse("8/29/2013");
            NewDate = InitialDate.AddMonths(6);
            // NewDate will be 2/28/2014

As before, since February 2014 has no 29th day, .NET gives me the next oldest day – the 28th.

The lesson here is that date-math using MONTHS is not always a reversible operation.  If I were to switch to adding and subtracting DAYS, however, that appears to be reversible:

            DateTime InitialDate, NewDate;
            InitialDate = DateTime.Parse("8/29/2013");
            NewDate = InitialDate.AddDays(-183).AddDays(183);
            // NewDate will be 8/29/2013

This isn’t limited to February, either.  Any time my starting month has more days than the month I end up with, this will happen (for example, October and April, December and June).

Ah, fun with dates.

Advertisements

September 2, 2013 - Posted by | Visual Studio/.NET

Sorry, the comment form is closed at this time.

%d bloggers like this: