[c#] How to get difference between two dates in Year/Month/Week/Day?

How to get difference between two dates in Year/Month/Week/Day in an efficient way?

eg. difference between two dates is 1 Year, 2 Months, 3 Weeks, 4 Days.

Difference represents count of year(s), month(s), week(s) and day(s) between two dates.

This question is related to c# .net datetime

The answer is


Based on Joaquim's answer, but fixing the calculation when end date month is less than start date month, and adding code to handle end date before start date:

        public static class GeneralHelper
        {
          public static int GetYears(DateTime startDate, DateTime endDate)
            {
                if (endDate < startDate)
                    return -GetYears(endDate, startDate);

                int years = (endDate.Year - startDate.Year);

                if (endDate.Year == startDate.Year)
                    return years;

                if (endDate.Month < startDate.Month)
                    return years - 1;

                if (endDate.Month == startDate.Month && endDate.Day < startDate.Day)
                    return years - 1;

                return years;
            }

            public static int GetMonths(DateTime startDate, DateTime endDate)
            {
                if (startDate > endDate)
                    return -GetMonths(endDate, startDate);

                int months = 12 * GetYears(startDate, endDate);

                if (endDate.Month > startDate.Month)
                    months = months + endDate.Month - startDate.Month;
                else
                    months = 12 - startDate.Month + endDate.Month;

                if (endDate.Day < startDate.Day)
                    months = months - 1;

                return months;
            }
       }
    [TestClass()]
    public class GeneralHelperTest
    {
            [TestMethod]
            public void GetYearsTest()
            {
                Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2000, 12, 31)));
                Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 4, 4)));
                Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 5, 4)));
                Assert.AreEqual(1, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 5, 5)));
                Assert.AreEqual(1, GeneralHelper.GetYears(new DateTime(2000, 5, 5), new DateTime(2001, 12, 31)));

                Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2000, 12, 31), new DateTime(2000, 5, 5)));
                Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2001, 4, 4), new DateTime(2000, 5, 5)));
                Assert.AreEqual(0, GeneralHelper.GetYears(new DateTime(2001, 5, 4), new DateTime(2000, 5, 5)));
                Assert.AreEqual(-1, GeneralHelper.GetYears(new DateTime(2001, 5, 5), new DateTime(2000, 5, 5)));
                Assert.AreEqual(-1, GeneralHelper.GetYears(new DateTime(2001, 12, 31), new DateTime(2000, 5, 5)));
            }

            [TestMethod]
            public void GetMonthsTest()
            {
                Assert.AreEqual(0, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2000, 6, 4)));
                Assert.AreEqual(1, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2000, 6, 5)));
                Assert.AreEqual(1, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2000, 6, 6)));
                Assert.AreEqual(11, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2001, 5, 4)));
                Assert.AreEqual(12, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2001, 5, 5)));
                Assert.AreEqual(13, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2001, 6, 6)));

                Assert.AreEqual(0, GeneralHelper.GetMonths(new DateTime(2000, 6, 4), new DateTime(2000, 5, 5)));
                Assert.AreEqual(-1, GeneralHelper.GetMonths(new DateTime(2000, 6, 5), new DateTime(2000, 5, 5)));
                Assert.AreEqual(-1, GeneralHelper.GetMonths(new DateTime(2000, 6, 6), new DateTime(2000, 5, 5)));
                Assert.AreEqual(-11, GeneralHelper.GetMonths(new DateTime(2001, 5, 4), new DateTime(2000, 5, 5)));
                Assert.AreEqual(-12, GeneralHelper.GetMonths(new DateTime(2001, 5, 5), new DateTime(2000, 5, 5)));
                Assert.AreEqual(-13, GeneralHelper.GetMonths(new DateTime(2001, 6, 6), new DateTime(2000, 5, 5)));
            }
   }

EDIT No that still doesn't work. It fails this test:

Assert.AreEqual(24, GeneralHelper.GetMonths(new DateTime(2000, 5, 5), new DateTime(2003, 5, 5)));

Expected:<24>. Actual:<12>


Partly as a preparation for trying to answer this question correctly (and maybe even definitively...), partly to examine how much one can trust code that is pasted on SO, and partly as an exercise in finding bugs, I created a bunch of unit tests for this question, and applied them to many proposed solutions from this page and a couple of duplicates.

The results are conclusive: not a single one of the code contributions accurately answers the question. Update: I now have four correct solutions to this question, including my own, see updates below.

Code tested

From this question, I tested code by the following users: Mohammed Ijas Nasirudeen, ruffin, Malu MN, Dave, pk., Jani, lc.

These were all the answers which provided all three of years, months, and days in their code. Note that two of these, Dave and Jani, gave the total number of days and months, rather than the total number of months left after counting the years, and the total number of days left after counting the months. I think the answers are wrong in terms of what the OP seemed to want, but the unit tests obviously don't tell you much in these cases. (Note that in Jani's case this was my error and his code was actually correct - see Update 4 below)

The answers by Jon Skeet, Aghasoleimani, Mukesh Kumar, Richard, Colin, sheir, just i saw, Chalkey and Andy, were incomplete. This doesn't mean that the answers weren't any good, in fact several of them are useful contributions towards a solution. It just means that there wasn't code taking two DateTimes and returning 3 ints that I could properly test. Four of these do however talk about using TimeSpan. As many people have mentioned, TimeSpan doesn't return counts of anything larger than days.

The other answers I tested were from

  • question 3054715 - LukeH, ho1 and this. ___curious_geek
  • question 6260372 - Chuck Rostance and Jani (same answer as this question)
  • question 9 (!) - Dylan Hayes, Jon and Rajeshwaran S P

this.___curious_geek's answer is code on a page he linked to, which I don't think he wrote. Jani's answer is the only one which uses an external library, Time Period Library for .Net.

All other answers on all these questions seemed to be incomplete. Question 9 is about age in years, and the three answers are ones which exceeded the brief and calculated years, months and days. If anyone finds further duplicates of this question please let me know.

How I tested

Quite simply: I made an interface

public interface IDateDifference
{
  void SetDates(DateTime start, DateTime end);
  int GetYears();
  int GetMonths();
  int GetDays();

}

For each answer I wrote a class implementing this interface, using the copied and pasted code as a basis. Of course I had to adapt functions with different signatures etc, but I tried to make the minimal edits to do so, preserving all the logic code.

I wrote a bunch of NUnit tests in an abstract generic class

[TestFixture]
public abstract class DateDifferenceTests<DDC> where DDC : IDateDifference, new()

and added an empty derived class

public class Rajeshwaran_S_P_Test : DateDifferenceTests<Rajeshwaran_S_P>
{
}

to the source file for each IDateDifference class.

NUnit is clever enough to do the rest.

The tests

A couple of these were written in advance and the rest were written to try and break seemingly working implementations.

[TestFixture]
public abstract class DateDifferenceTests<DDC> where DDC : IDateDifference, new()
{
  protected IDateDifference ddClass;

  [SetUp]
  public void Init()
  {
    ddClass = new DDC();
  }

  [Test]
  public void BasicTest()
  {
    ddClass.SetDates(new DateTime(2012, 12, 1), new DateTime(2012, 12, 25));
    CheckResults(0, 0, 24);
  }

  [Test]
  public void AlmostTwoYearsTest()
  {
    ddClass.SetDates(new DateTime(2010, 8, 29), new DateTime(2012, 8, 14));
    CheckResults(1, 11, 16);
  }

  [Test]
  public void AlmostThreeYearsTest()
  {
    ddClass.SetDates(new DateTime(2009, 7, 29), new DateTime(2012, 7, 14));
    CheckResults(2, 11, 15);
  }

  [Test]
  public void BornOnALeapYearTest()
  {
    ddClass.SetDates(new DateTime(2008, 2, 29), new DateTime(2009, 2, 28));
    CheckControversialResults(0, 11, 30, 1, 0, 0);
  }

  [Test]
  public void BornOnALeapYearTest2()
  {
    ddClass.SetDates(new DateTime(2008, 2, 29), new DateTime(2009, 3, 1));
    CheckControversialResults(1, 0, 0, 1, 0, 1);
  }


  [Test]
  public void LongMonthToLongMonth()
  {
    ddClass.SetDates(new DateTime(2010, 1, 31), new DateTime(2010, 3, 31));
    CheckResults(0, 2, 0);
  }

  [Test]
  public void LongMonthToLongMonthPenultimateDay()
  {
    ddClass.SetDates(new DateTime(2009, 1, 31), new DateTime(2009, 3, 30));
    CheckResults(0, 1, 30);
  }

  [Test]
  public void LongMonthToShortMonth()
  {
    ddClass.SetDates(new DateTime(2009, 8, 31), new DateTime(2009, 9, 30));
    CheckControversialResults(0, 1, 0, 0, 0, 30);
  }

  [Test]
  public void LongMonthToPartWayThruShortMonth()
  {
    ddClass.SetDates(new DateTime(2009, 8, 31), new DateTime(2009, 9, 10));
    CheckResults(0, 0, 10);
  }

  private void CheckResults(int years, int months, int days)
  {
    Assert.AreEqual(years, ddClass.GetYears());
    Assert.AreEqual(months, ddClass.GetMonths());
    Assert.AreEqual(days, ddClass.GetDays());
  }

  private void CheckControversialResults(int years, int months, int days,
    int yearsAlt, int monthsAlt, int daysAlt)
  {
    // gives the right output but unhelpful messages
    bool success = ((ddClass.GetYears() == years
                     && ddClass.GetMonths() == months
                     && ddClass.GetDays() == days)
                    ||
                    (ddClass.GetYears() == yearsAlt
                     && ddClass.GetMonths() == monthsAlt
                     && ddClass.GetDays() == daysAlt));

    Assert.IsTrue(success);
  }
}

Most of the names are slightly silly and don't really explain why code might fail the test, however looking at the two dates and the answer(s) should be enough to understand the test.

There are two functions that do all the Asserts, CheckResults() and CheckControversialResults(). These work well to save typing and give the right results, but unfortunately they make it harder to see exactly what went wrong (because the Assert in CheckControversialResults() will fail with "Expected true", rather than telling you which value was incorrect. If anyone has a better way to do this (avoid writing the same checks each time, but have more useful error messages) please let me know.

CheckControversialResults() is used for a couple of cases where there seem to be two different opinions on what is right. I have an opinion of my own, but I thought I should be liberal in what I accepted here. The gist of this is deciding whether one year after Feb 29 is Feb 28 or Mar 1.

These tests are the crux of the matter, and there could very well be errors in them, so please do comment if you find one which is wrong. It would be also good to hear some suggestions for other tests to check any future iterations of answers.

No test involves time of day - all DateTimes are at midnight. Including times, as long as it's clear how rounding up and down to days works (I think it is), might show up even more flaws.

The results

The complete scoreboard of results is as follows:

ChuckRostance_Test 3 failures               S S S F S S F S F
Dave_Test 6 failures                        F F S F F F F S S
Dylan_Hayes_Test 9 failures                 F F F F F F F F F
ho1_Test 3 failures                         F F S S S S F S S
Jani_Test 6 failures                        F F S F F F F S S
Jon_Test 1 failure                          S S S S S S F S S
lc_Test 2 failures                          S S S S S F F S S
LukeH_Test 1 failure                        S S S S S S F S S
Malu_MN_Test 1 failure                      S S S S S S S F S
Mohammed_Ijas_Nasirudeen_Test 2 failures    F S S F S S S S S
pk_Test 6 failures                          F F F S S F F F S
Rajeshwaran_S_P_Test 7 failures             F F S F F S F F F
ruffin_Test 3 failures                      F S S F S S F S S
this_curious_geek_Test 2 failures           F S S F S S S S S

But note that Jani's solution was actually correct and passed all tests - see update 4 below.

The columns are in alphabetical order of test name:

  • AlmostThreeYearsTest
  • AlmostTwoYearsTest
  • BasicTest
  • BornOnALeapYearTest
  • BornOnALeapYearTest2
  • LongMonthToLongMonth
  • LongMonthToLongMonthPenultimateDay
  • LongMonthToPartWayThruShortMonth
  • LongMonthToShortMonth

Three answers failed only 1 test each, Jon's, LukeH's and Manu MN's. Bear in mind these tests were probably written specifically to address flaws in those answers.

Every test was passed by at least one piece of code, which is slightly reassuring that none of the tests are erroneous.

Some answers failed a lot of tests. I hope no-one feels this is a condemnation of that poster's efforts. Firstly the number of successes is fairly arbitrary as the tests don't evenly cover the problem areas of the question space. Secondly this is not production code - answers are posted so people can learn from them, not copy them exactly into their programs. Code which fails a lot of tests can still have great ideas in it. At least one piece which failed a lot of tests had a small bug in it which I didn't fix. I'm grateful to anyone who took the time to share their work with everyone else, for making this project so interesting.

My conclusions

There are three:

  1. Calendars are hard. I wrote nine tests, including three where two answers are possible. Some of the tests where I only had one answer might not be unanimously agreed with. Just thinking about exactly what we mean when we say '1 month later' or '2 years earlier' is tricky in a lot of situations. And none of this code had to deal with all the complexities of things like working out when leap years are. All of it uses library code to handle dates. If you imagine the 'spec' for telling time in days, weeks, months and years written out, there's all sorts of cruft. Because we know it pretty well since primary school, and use it everyday, we are blind to many of the idiosyncracies. The question is not an academic one - various types of decomposition of time periods into years, quarters and months are essential in accounting software for bonds and other financial products.

  2. Writing correct code is hard. There were a lot of bugs. In slightly more obscure topics or less popular questions than the chances of a bug existing without having been pointed out by a commenter are much, much higher than for this question. You should really never, never copy code from SO into your program without understanding exactly what it does. The flipside of this is that you probably shouldn't write code in your answer that is ready to be copied and pasted, but rather intelligent and expressive pseudo-code that allows someone to understand the solution and implement their own version (with their own bugs!)

  3. Unit tests are helpful. I am still meaning to post my own solution to this when I get round to it (for someone else to find the hidden, incorrect assumptions in!) Doing this was a great example of 'saving the bugs' by turning them into unit tests to fix the next version of the code with.

Update

The whole project is now at https://github.com/jwg4/date-difference This includes my own attempt jwg.cs, which passes all the tests I currently have, including a few new ones which check for proper time of day handling. Feel free to add either more tests to break this and other implementations or better code for answering the question.

Update 2

@MattJohnson has added an implementation which uses Jon Skeet's NodaTime. It passes all the current tests.

Update 3

@KirkWoll's answer to Difference in months between two dates has been added to the project on github. It passes all the current tests.

Update 4

@Jani pointed out in a comment that I had used his code wrongly. He did suggest methods that counted the years, months and days correctly, (alongside some which count the total number of days and months, not the remainders) however I mistakenly used the wrong ones in my test code. I have corrected my wrapper around his code and it now passes all tests. There are now four correct solutions, of which Jani's was the first. Two use libraries (Intenso.TimePeriod and NodaTime) and two are written from scratch.


Use Noda Time:

var ld1 = new LocalDate(2012, 1, 1);
var ld2 = new LocalDate(2013, 12, 25);
var period = Period.Between(ld1, ld2);

Debug.WriteLine(period);        // "P1Y11M24D"  (ISO8601 format)
Debug.WriteLine(period.Years);  // 1
Debug.WriteLine(period.Months); // 11
Debug.WriteLine(period.Days);   // 24

int day=0,month=0,year=0;
DateTime smallDate = Convert.ToDateTime(string.Format("{0}", "01.01.1900"));
DateTime bigDate = Convert.ToDateTime(string.Format("{0}", "05.06.2019"));
TimeSpan timeSpan = new TimeSpan();

//timeSpan is diff between bigDate and smallDate as days
timeSpan = bigDate - smallDate;

//year is totalDays / 365 as int
year = timeSpan.Days / 365;

//smallDate.AddYears(year) is closing the difference for year because we found the year variable
smallDate = smallDate.AddYears(year);

//again subtraction because we don't need the year now
timeSpan = bigDate - smallDate;

//month is totalDays / 30 as int
month = timeSpan.Days / 30;

//smallDate.AddMonths(month) is closing the difference for month because we found the month variable
smallDate = smallDate.AddMonths(month);
if (bigDate > smallDate)
{
    timeSpan = bigDate - smallDate;
    day = timeSpan.Days;
}
//else it is mean already day is 0

TimeSpan period = endDate.AddDays(1) - startDate;
DateTime date = new DateTime(period.Ticks);
int totalYears = date.Year - 1;
int totalMonths = ((date.Year - 1) * 12) + date.Month - 1;
int totalWeeks = (int)period.TotalDays / 7;

date.Year - 1 because the year 0 doesn't exist. date.Month - 1, the month 0 doesn't exist


If you subtract two instances of DateTime, that will return an instance of TimeSpan, which will represent the difference between the two dates.


DateTime startTime = DateTime.Now;

DateTime endTime = DateTime.Now.AddSeconds( 75 );

TimeSpan span = endTime.Subtract ( startTime );
 Console.WriteLine( "Time Difference (seconds): " + span.Seconds );
 Console.WriteLine( "Time Difference (minutes): " + span.Minutes );
 Console.WriteLine( "Time Difference (hours): " + span.Hours );
 Console.WriteLine( "Time Difference (days): " + span.Days );

Output:

Time Difference (seconds): 15
Time Difference (minutes): 1
Time Difference (hours): 0
Time Difference (days): 0

DateTime dt1 = new DateTime(2009, 3, 14);
DateTime dt2 = new DateTime(2008, 3, 15);

int diffMonth = Math.Abs((dt2.Year - dt1.Year)*12 + dt1.Month - dt2.Month)

If you have to find the difference between originalDate and today’s date, Here is a reliable algorithm without so many condition checks.

  1. Declare a intermediateDate variable and initialize to the originalDate
  2. Find difference between years.(yearDiff)
  3. Add yearDiff to intermediateDate and check whether the value is greater than today’s date.
  4. If newly obtained intermediateDate > today’s date adjust the yearDiff and intermediateDate by one.
  5. Continue above steps for month and Days.

I have used System.Data.Linq functions to do find the year, month and day differences. Please find c# code below

        DateTime todaysDate = DateTime.Now;
        DateTime interimDate = originalDate;

        ///Find Year diff
        int yearDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffYear(interimDate, todaysDate);
        interimDate = interimDate.AddYears(yearDiff);
        if (interimDate > todaysDate)
        {
            yearDiff -= 1;
            interimDate = interimDate.AddYears(-1);
        }

        ///Find Month diff
        int monthDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffMonth(interimDate, todaysDate);
        interimDate = interimDate.AddMonths(monthDiff);
        if (interimDate > todaysDate)
        {
            monthDiff -= 1;
            interimDate = interimDate.AddMonths(-1);
        }

        ///Find Day diff
        int daysDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffDay(interimDate, todaysDate);

    Console.WriteLine("Enter your Date of Birth to Know your Current age in DD/MM/YY Format");
    string str = Console.ReadLine();
    DateTime dt1 = DateTime.Parse(str);
    DateTime dt2 = DateTime.Parse("10/06/2012");
    int result = (dt2 - dt1).Days;
    result = result / 365;
    Console.WriteLine("Your Current age is {0} years.",result);

I have below solution which works correctly for me(After doing some Test cases). Extra Test cases are acceptable.

public class DateDiffernce
{
    private int m_nDays = -1;
    private int m_nWeek;
    private int m_nMonth = -1;
    private int m_nYear;

    public int Days
    {
        get
        {
            return m_nDays;
        }
    }

    public int Weeks
    {
        get
        {
            return m_nWeek;
        }
    }

    public int Months
    {
        get
        {
            return m_nMonth;
        }
    }

    public int Years
    {
        get
        {
            return m_nYear;
        }
    }

    public void GetDifferenceBetwwenTwoDate(DateTime objDateTimeFromDate, DateTime objDateTimeToDate)
    {
        if (objDateTimeFromDate.Date > objDateTimeToDate.Date)
        {
            DateTime objDateTimeTemp = objDateTimeFromDate;
            objDateTimeFromDate = objDateTimeToDate;
            objDateTimeToDate = objDateTimeTemp;
        }

        if (objDateTimeFromDate == objDateTimeToDate)
        {
            //textBoxDifferenceDays.Text = " Same dates";
            //textBoxAllDifference.Text = " Same dates";
            return;
        }

        // If From Date's Day is bigger than borrow days from previous month
        // & then subtract.
        if (objDateTimeFromDate.Day > objDateTimeToDate.Day)
        {
            objDateTimeToDate = objDateTimeToDate.AddMonths(-1);
            int nMonthDays = DateTime.DaysInMonth(objDateTimeToDate.Year, objDateTimeToDate.Month);
            m_nDays = objDateTimeToDate.Day + nMonthDays - objDateTimeFromDate.Day;

        }

        // If From Date's Month is bigger than borrow 12 Month 
        // & then subtract.
        if (objDateTimeFromDate.Month > objDateTimeToDate.Month)
        {
            objDateTimeToDate = objDateTimeToDate.AddYears(-1);
            m_nMonth = objDateTimeToDate.Month + 12 - objDateTimeFromDate.Month;

        }

       //Below are best cases - simple subtraction
        if (m_nDays == -1)
        {
            m_nDays = objDateTimeToDate.Day - objDateTimeFromDate.Day;
        }

        if (m_nMonth == -1)
        {
            m_nMonth = objDateTimeToDate.Month - objDateTimeFromDate.Month;
        }

        m_nYear = objDateTimeToDate.Year - objDateTimeFromDate.Year;
        m_nWeek = m_nDays / 7;
        m_nDays = m_nDays % 7;    
    }
}

Days: (endDate - startDate).Days
Weeks: (endDate - startDate).Days / 7
Years: Months / 12
Months: A TimeSpan only provides Days, so use the following code to get the number of whole months between a specified start and end date. For example, the number of whole months between 01/10/2000 and 02/10/2000 is 1. The the number of whole months between 01/10/2000 and 02/09/2000 is 0.

    public int getMonths(DateTime startDate, DateTime endDate)
    {
        int months = 0;

        if (endDate.Month <= startDate.Month)
        {
            if (endDate.Day < startDate.Day)
            {
                months = (12 * (endDate.Year - startDate.Year - 1))
                       + (12 - startDate.Month + endDate.Month - 1);
            }
            else if (endDate.Month < startDate.Month)
            {
                months = (12 * (endDate.Year - startDate.Year - 1))
                       + (12 - startDate.Month + endDate.Month);
            }
            else  // (endDate.Month == startDate.Month) && (endDate.Day >= startDate.Day)
            {
                months = (12 * (endDate.Year - startDate.Year));
            }
        }
        else if (endDate.Day < startDate.Day)
        {
            months = (12 * (endDate.Year - startDate.Year))
                   + (endDate.Month - startDate.Month) - 1;
        }
        else  // (endDate.Month > startDate.Month) && (endDate.Day >= startDate.Day)
        {
            months = (12 * (endDate.Year - startDate.Year))
                   + (endDate.Month - startDate.Month);
        }

        return months;
    }

I was trying to find a clear answer for Years, Months and Days, and I didn't find anything clear, If you are still looking check this method:

        public static string GetDifference(DateTime d1, DateTime d2)
        {
            int[] monthDay = new int[12] { 31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
            DateTime fromDate;
            DateTime toDate;
            int year;
            int month;
            int day; 
            int increment = 0;

            if (d1 > d2)
            {
                fromDate = d2;
                toDate = d1;
            }
            else
            {
                fromDate = d1;
                toDate = d2;
            } 

            // Calculating Days
            if (fromDate.Day > toDate.Day)
            {
                increment = monthDay[fromDate.Month - 1];
            }

            if (increment == -1)
            {
                if (DateTime.IsLeapYear(fromDate.Year))
                {
                    increment = 29;
                }
                else
                {
                    increment = 28;
                }
            }

            if (increment != 0)
            {
                day = (toDate.Day + increment) - fromDate.Day;
                increment = 1;
            }
            else
            {
                day = toDate.Day - fromDate.Day;
            }

            // Month Calculation
            if ((fromDate.Month + increment) > toDate.Month)
            {
                month = (toDate.Month + 12) - (fromDate.Month + increment);
                increment = 1;
            }
            else
            {
                month = (toDate.Month) - (fromDate.Month + increment);
                increment = 0;
            }

            // Year Calculation
            year = toDate.Year - (fromDate.Year + increment);

            return year + " years " + month + " months " + day + " days";
        }

For the correct difference calculation of Years/Months/Weeks, the Calendar of the CultureInfo must be considered:

  • leap vs. non-leap years
  • months with different count of days
  • years with different count of weeks (varying by the first day of week and the calendar week rule)

The DateDiff class of the Time Period Library for .NET respects all these factors:

// ----------------------------------------------------------------------
public void DateDiffSample()
{
  DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
  Console.WriteLine( "Date1: {0}", date1 );
  // > Date1: 08.11.2009 07:13:59
  DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
  Console.WriteLine( "Date2: {0}", date2 );
  // > Date2: 20.03.2011 19:55:28

  DateDiff dateDiff = new DateDiff( date1, date2 );

  // differences
  Console.WriteLine( "DateDiff.Years: {0}", dateDiff.Years );
  // > DateDiff.Years: 1
  Console.WriteLine( "DateDiff.Quarters: {0}", dateDiff.Quarters );
  // > DateDiff.Quarters: 5
  Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
  // > DateDiff.Months: 16
  Console.WriteLine( "DateDiff.Weeks: {0}", dateDiff.Weeks );
  // > DateDiff.Weeks: 70
  Console.WriteLine( "DateDiff.Days: {0}", dateDiff.Days );
  // > DateDiff.Days: 497
  Console.WriteLine( "DateDiff.Weekdays: {0}", dateDiff.Weekdays );
  // > DateDiff.Weekdays: 71
  Console.WriteLine( "DateDiff.Hours: {0}", dateDiff.Hours );
  // > DateDiff.Hours: 11940
  Console.WriteLine( "DateDiff.Minutes: {0}", dateDiff.Minutes );
  // > DateDiff.Minutes: 716441
  Console.WriteLine( "DateDiff.Seconds: {0}", dateDiff.Seconds );
  // > DateDiff.Seconds: 42986489

  // elapsed
  Console.WriteLine( "DateDiff.ElapsedYears: {0}", dateDiff.ElapsedYears );
  // > DateDiff.ElapsedYears: 1
  Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
  // > DateDiff.ElapsedMonths: 4
  Console.WriteLine( "DateDiff.ElapsedDays: {0}", dateDiff.ElapsedDays );
  // > DateDiff.ElapsedDays: 12
  Console.WriteLine( "DateDiff.ElapsedHours: {0}", dateDiff.ElapsedHours );
  // > DateDiff.ElapsedHours: 12
  Console.WriteLine( "DateDiff.ElapsedMinutes: {0}", dateDiff.ElapsedMinutes );
  // > DateDiff.ElapsedMinutes: 41
  Console.WriteLine( "DateDiff.ElapsedSeconds: {0}", dateDiff.ElapsedSeconds );
  // > DateDiff.ElapsedSeconds: 29

  // description
  Console.WriteLine( "DateDiff.GetDescription(1): {0}", dateDiff.GetDescription( 1 ) );
  // > DateDiff.GetDescription(1): 1 Year
  Console.WriteLine( "DateDiff.GetDescription(2): {0}", dateDiff.GetDescription( 2 ) );
  // > DateDiff.GetDescription(2): 1 Year 4 Months
  Console.WriteLine( "DateDiff.GetDescription(3): {0}", dateDiff.GetDescription( 3 ) );
  // > DateDiff.GetDescription(3): 1 Year 4 Months 12 Days
  Console.WriteLine( "DateDiff.GetDescription(4): {0}", dateDiff.GetDescription( 4 ) );
  // > DateDiff.GetDescription(4): 1 Year 4 Months 12 Days 12 Hours
  Console.WriteLine( "DateDiff.GetDescription(5): {0}", dateDiff.GetDescription( 5 ) );
  // > DateDiff.GetDescription(5): 1 Year 4 Months 12 Days 12 Hours 41 Mins
  Console.WriteLine( "DateDiff.GetDescription(6): {0}", dateDiff.GetDescription( 6 ) );
  // > DateDiff.GetDescription(6): 1 Year 4 Months 12 Days 12 Hours 41 Mins 29 Secs
} // DateDiffSample

DateDiff also calculates the difference of Quarters.


Subtract two DateTime instances to give you a TimeSpan which has a Days property. (E.g. in PowerShell):

PS > ([datetime]::today - [datetime]"2009-04-07")


Days              : 89
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 0
Ticks             : 76896000000000
TotalDays         : 89
TotalHours        : 2136
TotalMinutes      : 128160
TotalSeconds      : 7689600
TotalMilliseconds : 7689600000

Converting days into years or weeks is relatively easy (days in a year could be 365, 365.25, ... depending on context). Months is much harder, because without a base date you don't know which month lengths apply.

Assuming you want to start with your base date, you can incrementally substract while counting first years (checking for leap years), then month lengths (indexing from startDate.Month), then weeks (remaining days divided by 7) and then days (remainder).

There are a lot of edge cases to consider, e.g. 2005-03-01 is one year from 2004-03-01, and from 2004-02-29 depending on what you mean by "Year".


What about using the System.Data.Linq namespace and its SqlMethods.DateDiffMonth method?

For example, say:

DateTime starDT = {01-Jul-2009 12:00:00 AM}
DateTime endDT = {01-Nov-2009 12:00:00 AM}

Then:

int monthDiff = System.Data.Linq.SqlClient.SqlMethods.DateDiffMonth(startDT, endDT);

==> 4

There are other DateDiff static methods in the SqlMethods class.


Leap years and uneven months actually make this a non-trivial problem. I'm sure someone can come up with a more efficient way, but here's one option - approximate on the small side first and adjust up (untested):

public static void GetDifference(DateTime date1, DateTime date2, out int Years, 
    out int Months, out int Weeks, out int Days)
{
    //assumes date2 is the bigger date for simplicity

    //years
    TimeSpan diff = date2 - date1;
    Years = diff.Days / 366;
    DateTime workingDate = date1.AddYears(Years);

    while(workingDate.AddYears(1) <= date2)
    {
        workingDate = workingDate.AddYears(1);
        Years++;
    }

    //months
    diff = date2 - workingDate;
    Months = diff.Days / 31;
    workingDate = workingDate.AddMonths(Months);

    while(workingDate.AddMonths(1) <= date2)
    {
        workingDate = workingDate.AddMonths(1);
        Months++;
    }

    //weeks and days
    diff = date2 - workingDate;
    Weeks = diff.Days / 7; //weeks always have 7 days
    Days = diff.Days % 7;
}

I came across this post while looking to solve a similar problem. I was trying to find the age of an animal in units of Years, Months, Weeks, and Days. Those values are then displayed in SpinEdits where the user can manually change the values to find/estimate a birth date. When my form was passed a birth date from a month with less than 31 days, the value calculated was 1 day off. I based my solution off of Ic's answer above.

Main calculation method that is called after my form loads.

        birthDateDisplay.Text = birthDate.ToString("MM/dd/yyyy");

        DateTime currentDate = DateTime.Now;

        Int32 numOfDays = 0; 
        Int32 numOfWeeks = 0;
        Int32 numOfMonths = 0; 
        Int32 numOfYears = 0; 

        // changed code to follow this model http://stackoverflow.com/posts/1083990/revisions
        //years 
        TimeSpan diff = currentDate - birthDate;
        numOfYears = diff.Days / 366;
        DateTime workingDate = birthDate.AddYears(numOfYears);

        while (workingDate.AddYears(1) <= currentDate)
        {
            workingDate = workingDate.AddYears(1);
            numOfYears++;
        }

        //months
        diff = currentDate - workingDate;
        numOfMonths = diff.Days / 31;
        workingDate = workingDate.AddMonths(numOfMonths);

        while (workingDate.AddMonths(1) <= currentDate)
        {
            workingDate = workingDate.AddMonths(1);
            numOfMonths++;
        }

        //weeks and days
        diff = currentDate - workingDate;
        numOfWeeks = diff.Days / 7; //weeks always have 7 days

        // if bday month is same as current month and bday day is after current day, the date is off by 1 day
        if(DateTime.Now.Month == birthDate.Month && DateTime.Now.Day < birthDate.Day)
            numOfDays = diff.Days % 7 + 1;
        else
            numOfDays = diff.Days % 7;

        // If the there are fewer than 31 days in the birth month, the date calculated is 1 off
        // Dont need to add a day for the first day of the month
        int daysInMonth = 0;
        if ((daysInMonth = DateTime.DaysInMonth(birthDate.Year, birthDate.Month)) != 31 && birthDate.Day != 1)
        {
            startDateforCalc = DateTime.Now.Date.AddDays(31 - daysInMonth);
            // Need to add 1 more day if it is a leap year and Feb 29th is the date
            if (DateTime.IsLeapYear(birthDate.Year) && birthDate.Day == 29)
                startDateforCalc = startDateforCalc.AddDays(1);
        }

        yearsSpinEdit.Value = numOfYears;
        monthsSpinEdit.Value = numOfMonths;
        weeksSpinEdit.Value = numOfWeeks;
        daysSpinEdit.Value = numOfDays;

And then, in my spinEdit_EditValueChanged event handler, I calculate the new birth date starting from my startDateforCalc based on the values in the spin edits. (SpinEdits are constrained to only allow >=0)

birthDate = startDateforCalc.Date.AddYears(-((Int32)yearsSpinEdit.Value)).AddMonths(-((Int32)monthsSpinEdit.Value)).AddDays(-(7 * ((Int32)weeksSpinEdit.Value) + ((Int32)daysSpinEdit.Value)));
birthDateDisplay.Text = birthDate.ToString("MM/dd/yyyy");

I know its not the prettiest solution, but it seems to be working for me for all month lengths and years.


private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{

        int gyear = dateTimePicker1.Value.Year; 
        int gmonth = dateTimePicker1.Value.Month; 
        int gday = dateTimePicker1.Value.Day; 
        int syear = DateTime.Now.Year; 
        int smonth = DateTime.Now.Month; 
        int sday = DateTime.Now.Day;

        int difday = DateTime.DaysInMonth(syear, gmonth);

        agedisplay = (syear - gyear); 

        lmonth = (smonth - gmonth);
        lday = (sday - gday);


        if (smonth < gmonth)
        {
            agedisplay = agedisplay - 1;
        }
        if (smonth == gmonth)
        {
            if (sday < (gday))
            {
                agedisplay = agedisplay - 1;
            }
        }

        if (smonth < gmonth)
        {
            lmonth = (-(-smonth)+(-gmonth)+12);
        }
        if (lday < 0)
        {
            lday = difday - (-lday);
            lmonth = lmonth - 1;
        }

        if (smonth == gmonth && sday < gday&&gyear!=syear)
        {
            lmonth = 11;
        }

            ageDisplay.Text = Convert.ToString(agedisplay) + " Years,  " + lmonth + " Months,  " + lday + " Days.";

    }

Well, @Jon Skeet, if we're not worried about getting any more granular than days (and still rolling days into larger units rather than having a total day count), as per the OP, it's really not that difficult in C#. What makes date math so difficult is that the number of units in each composite unit often changes. Imagine if every 3rd gallon of gas was only 3 quarts, but each 12th was 7, except on Fridays, when...

Luckily, dates are just a long ride through the greatest integer function. These crazy exceptions are maddening, unless you've gone all the way through the wackily-comprised unit, when it's not a big deal any more. If you're born on 12/25/1900, you're still EXACTLY 100 on 12/25/2000, regardless of the leap years or seconds or daylight savings periods you've been through. As soon as you've slogged through the percentages that make up the last composite unit, you're back to unity. You've added one, and get to start over.

Which is just to say that if you're doing years to months to days, the only strangely comprised unit is the month (of days). If you need to borrow from the month value to handle a place where you're subtracting more days than you've got, you just need to know the number of days in the previous month. No other outliers matter.

And C# gives that to you in System.DateTime.DaysInMonth(intYear, intMonth).

(If your Now month is smaller than your Then month, there's no issue. Every year has 12 months.)

And the same deal if we go more granular... you just need to know how many (small units) are in the last (composite unit). Once you're past, you get another integer value more of (composite unit). Then subtract how many small units you missed starting where you did Then and add back how many of those you went past the composite unit break-off with your Now.

So here's what I've got from my first cut at subtracting two dates. It might work. Hopefully useful.

(EDIT: Changed NewMonth > OldMonth check to NewMonth >= OldMonth, as we don't need to borrow one if the Months are the same (ditto for days). That is, Nov 11 2011 minus Nov 9 2010 was giving -1 year, 12 months, 2 days (ie, 2 days, but the royal we borrowed when royalty didn't need to.)

(EDIT: Had to check for Month = Month when we needed to borrow days to subtract a dteThen.Day from dteNow.Day & dteNow.Day < dteThen.Day, as we had to subtract a year to get 11 months and the extra days. Okay, so there are a few outliers. ;^D I think I'm close now.)

private void Form1_Load(object sender, EventArgs e) {
DateTime dteThen = DateTime.Parse("3/31/2010");
DateTime dteNow = DateTime.Now;

int intDiffInYears = 0;
int intDiffInMonths = 0;
int intDiffInDays = 0;


if (dteNow.Month >= dteThen.Month)
{
    if (dteNow.Day >= dteThen.Day)
    {   // this is a best case, easy subtraction situation
        intDiffInYears = dteNow.Year - dteThen.Year;
        intDiffInMonths = dteNow.Month - dteThen.Month;
        intDiffInDays = dteNow.Day - dteThen.Day;
    }
    else
    {   // else we need to substract one from the month diff (borrow the one)
        // and days get wacky.

        // Watch for the outlier of Month = Month with DayNow < DayThen, as then we've 
        // got to subtract one from the year diff to borrow a month and have enough
        // days to subtract Then from Now.
        if (dteNow.Month == dteThen.Month)
        {
            intDiffInYears = dteNow.Year - dteThen.Year - 1;
            intDiffInMonths = 11; // we borrowed a year and broke ONLY 
            // the LAST month into subtractable days
            // Stay with me -- because we borrowed days from the year, not the month,
            // this is much different than what appears to be a similar calculation below.
            // We know we're a full intDiffInYears years apart PLUS eleven months.
            // Now we need to know how many days occurred before dteThen was done with 
            // dteThen.Month.  Then we add the number of days we've "earned" in the current
            // month.  
            //
            // So 12/25/2009 to 12/1/2011 gives us 
            // 11-9 = 2 years, minus one to borrow days = 1 year difference.
            // 1 year 11 months - 12 months = 11 months difference
            // (days from 12/25 to the End Of Month) + (Begin of Month to 12/1) = 
            //                (31-25)                +       (0+1)              =
            //                   6                   +         1                = 
            //                                  7 days diff
            //
            // 12/25/2009 to 12/1/2011 is 1 year, 11 months, 7 days apart.  QED.

            int intDaysInSharedMonth = System.DateTime.DaysInMonth(dteThen.Year, dteThen.Month);
            intDiffInDays = intDaysInSharedMonth - dteThen.Day + dteNow.Day;
        }
        else
        {
            intDiffInYears = dteNow.Year - dteThen.Year;
            intDiffInMonths = dteNow.Month - dteThen.Month - 1;

            // So now figure out how many more days we'd need to get from dteThen's 
            // intDiffInMonth-th month to get to the current month/day in dteNow.
            // That is, if we're comparing 2/8/2011 to 11/7/2011, we've got (10/8-2/8) = 8
            // full months between the two dates.  But then we've got to go from 10/8 to
            // 11/07.  So that's the previous month's (October) number of days (31) minus
            // the number of days into the month dteThen went (8), giving the number of days
            // needed to get us to the end of the month previous to dteNow (23).  Now we
            // add back the number of days that we've gone into dteNow's current month (7)
            // to get the total number of days we've gone since we ran the greatest integer
            // function on the month difference (23 to the end of the month + 7 into the
            // next month == 30 total days.  You gotta make it through October before you 
            // get another month, G, and it's got 31 days).

            int intDaysInPrevMonth = System.DateTime.DaysInMonth(dteNow.Year, (dteNow.Month - 1));
            intDiffInDays = intDaysInPrevMonth - dteThen.Day + dteNow.Day;
        }
    }
}
else
{
    // else dteThen.Month > dteNow.Month, and we've got to amend our year subtraction
    // because we haven't earned our entire year yet, and don't want an obo error.
    intDiffInYears = dteNow.Year - dteThen.Year - 1;

    // So if the dates were THEN: 6/15/1999 and NOW: 2/20/2010...
    // Diff in years is 2010-1999 = 11, but since we're not to 6/15 yet, it's only 10.
    // Diff in months is (Months in year == 12) - (Months lost between 1/1/1999 and 6/15/1999
    // when dteThen's clock wasn't yet rolling == 6) = 6 months, then you add the months we
    // have made it into this year already.  The clock's been rolling through 2/20, so two months.
    // Note that if the 20 in 2/20 hadn't been bigger than the 15 in 6/15, we're back to the
    // intDaysInPrevMonth trick from earlier.  We'll do that below, too.
    intDiffInMonths = 12 - dteThen.Month + dteNow.Month;

    if (dteNow.Day >= dteThen.Day)
    {
        intDiffInDays = dteNow.Day - dteThen.Day;
    }
    else
    {
        intDiffInMonths--;  // subtract the month from which we're borrowing days.

        // Maybe we shoulda factored this out previous to the if (dteNow.Month > dteThen.Month)
        // call, but I think this is more readable code.
        int intDaysInPrevMonth = System.DateTime.DaysInMonth(dteNow.Year, (dteNow.Month - 1));
        intDiffInDays = intDaysInPrevMonth - dteThen.Day + dteNow.Day;
    }

}

this.addToBox("Years: " + intDiffInYears + " Months: " + intDiffInMonths + " Days: " + intDiffInDays); // adds results to a rich text box.

}

Use the Subtract method of the DateTime object which returns a TimeSpan...

DateTime dt1 = new DateTime(2009, 3, 14);
DateTime dt2 = new DateTime(2008, 3, 15);

TimeSpan ts = dt1.Subtract(dt2);

Double days = ts.TotalDays;
Double hours = ts.TotalHours;
Double years = ts.TotalDays / 365;

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to .net

You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to use Bootstrap 4 in ASP.NET Core No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization .net Core 2.0 - Package was restored using .NetFramework 4.6.1 instead of target framework .netCore 2.0. The package may not be fully compatible Update .NET web service to use TLS 1.2 EF Core add-migration Build Failed What is the difference between .NET Core and .NET Standard Class Library project types? Visual Studio 2017 - Could not load file or assembly 'System.Runtime, Version=4.1.0.0' or one of its dependencies Nuget connection attempt failed "Unable to load the service index for source" Token based authentication in Web API without any user interface

Examples related to datetime

Comparing two joda DateTime instances How to format DateTime in Flutter , How to get current time in flutter? How do I convert 2018-04-10T04:00:00.000Z string to DateTime? How to get current local date and time in Kotlin Converting unix time into date-time via excel Convert python datetime to timestamp in milliseconds SQL Server date format yyyymmdd Laravel Carbon subtract days from current date Check if date is a valid one Why is ZoneOffset.UTC != ZoneId.of("UTC")?