Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate Years, Months, weeks and Days

In my application, a user enters two dates. A scheduled start date, and a scheduled end date. We have to take those dates, and populate 4 fields, based on the difference.

So, lets say he selects 1st Jan, 2010 as a start, and 2nd of March, 2011 as the end, we need to end up with:

Years: 1 Months: 2 Weeks: 0 Days 1

Meaning the total duration is 1 year, 2 months and 1 day.

Is there a standard way of doing this? Or would I need to write a method that has a lot of pretty tricky logic to work it out? I was hoping I'd be lucky, and there would be a date-diff type .Net class available.

like image 676
Craig Avatar asked Jun 07 '11 03:06

Craig


2 Answers

This should do it. The key is to reduce days by 1 if there are odd number of leap days in between the given 2 dates.

    /// <summary>
    /// //Assume DateTime dt1 < DateTime dt2, print out difference between dt1 to dt2 in years, months, weeks and days
    /// </summary>
    /// <param name="dt1"></param>
    /// <param name="dt2"></param>
    static void DateDiff(DateTime dt1, DateTime dt2)
    {
        DateTime zeroTime = new DateTime(1, 1, 1);

        int leapDaysInBetween = CountLeapDays(dt1, dt2);

        TimeSpan span = dt2 - dt1;

        int years = (zeroTime + span).Year - 1;
        int months = (zeroTime + span).Month - 1;
        int days = (zeroTime + span).Day - (leapDaysInBetween % 2 == 1 ? 1 : 0);
        int weeks = days / 7;
        int remainingdays = days % 7;

        Console.WriteLine(String.Format("\nThe difference between date {0} and date {1} is: \n\t {2} year(s), {3} month(s), and {4} day(s).", dt1, dt2, years, months, days));
        Console.WriteLine(String.Format("\nThe difference between date {0} and date {1} is: \n\t {2} year(s), {3} month(s), {4} week(s) and {5} day(s).", dt1, dt2, years, months, weeks, remainingdays));
    }

    private static int CountLeapDays(DateTime dt1, DateTime dt2)
    {
        int leapDaysInBetween = 0;
        int year1 = dt1.Year, year2 = dt2.Year;
        DateTime dateValue;

        for (int i = year1; i <= year2; i++)
        {
            if (DateTime.TryParse("02/29/" + i.ToString(), out dateValue))
            {
                if (dateValue >= dt1 && dateValue <= dt2)
                    leapDaysInBetween++;
            }
        }

        return leapDaysInBetween;
    }

Had run these tests:

static void Main(string[] args)
{
    DateDiff(new DateTime(2010, 1, 1), new DateTime(2012, 2, 9));
    DateDiff(new DateTime(2010, 1, 1), new DateTime(2012, 4, 9));
    DateDiff(new DateTime(2010, 1, 1), new DateTime(2020, 2, 9));
    DateDiff(new DateTime(2010, 1, 1), new DateTime(2020, 4, 9));
    DateDiff(new DateTime(2020, 2, 29), new DateTime(2021, 2, 28));
    DateDiff(new DateTime(2019, 2, 28), new DateTime(2021, 2, 28));
}

These are the printouts:

The difference between date 1/1/2010 12:00:00 AM and date 4/9/2012 12:00:00 AM is: 2 year(s), 3 month(s), and 9 day(s).

The difference between date 1/1/2010 12:00:00 AM and date 4/9/2012 12:00:00 AM is: 2 year(s), 3 month(s), 1 week(s) and 2 day(s).

The difference between date 1/1/2010 12:00:00 AM and date 2/9/2020 12:00:00 AM is: 10 year(s), 1 month(s), and 9 day(s).

The difference between date 1/1/2010 12:00:00 AM and date 2/9/2020 12:00:00 AM is: 10 year(s), 1 month(s), 1 week(s) and 2 day(s).

The difference between date 1/1/2010 12:00:00 AM and date 4/9/2020 12:00:00 AM is: 10 year(s), 3 month(s), and 9 day(s).

The difference between date 1/1/2010 12:00:00 AM and date 4/9/2020 12:00:00 AM is: 10 year(s), 3 month(s), 1 week(s) and 2 day(s).

The difference between date 2/29/2020 12:00:00 AM and date 2/28/2021 12:00:00 AM is: 1 year(s), 0 month(s), and 0 day(s).

The difference between date 2/29/2020 12:00:00 AM and date 2/28/2021 12:00:00 AM is: 1 year(s), 0 month(s), 0 week(s) and 0 day(s).

The difference between date 2/28/2019 12:00:00 AM and date 2/28/2021 12:00:00 AM is: 2 year(s), 0 month(s), and 1 day(s).

The difference between date 2/28/2019 12:00:00 AM and date 2/28/2021 12:00:00 AM is: 2 year(s), 0 month(s), 0 week(s) and 1 day(s).

like image 160
Steve Liu Avatar answered Nov 08 '22 13:11

Steve Liu


Heres a complete method, weeks are not included, but could be added relatively simply. It's a somewhat complex question (asked in a multitude of ways on stackoverflow and answered poorly in a multitude of ways), but none the less can be answered. The TimeSpan object gives us part of what we need, but only works up through days. I've written a significant number of tests against this method, if you find a hole, please post a comment.

What this will do is compare 2 dates, getting the years, months, days, hours, and minutes. (e.g. some event happened 1 year, 6 months, 3 days, 4 hours and 7 minutes ago)

Because this question has been asked and attempted to be answered so many times, I'm not sure this will ever even get noticed, but if so it should provide value.

    public static void TimeSpanToDateParts(DateTime d1, DateTime d2, out int years, out int months, out int days, out int hours, out int minutes)
    {
        if (d1 < d2)
        {
            var d3 = d2;
            d2 = d1;
            d1 = d3;
        }

        var span = d1 - d2;

        months = 12 * (d1.Year - d2.Year) + (d1.Month - d2.Month);

        //month may need to be decremented because the above calculates the ceiling of the months, not the floor.
        //to do so we increase d2 by the same number of months and compare.
        //(500ms fudge factor because datetimes are not precise enough to compare exactly)
        if (d1.CompareTo(d2.AddMonths(months).AddMilliseconds(-500)) <= 0)
        {
            --months;
        }

        years = months / 12;
        months -= years * 12;

        if (months == 0 && years == 0)
        {
            days = span.Days;
        }
        else
        {
            var md1 = new DateTime(d1.Year, d1.Month, d1.Day);
            // Fixed to use d2.Day instead of d1.Day
            var md2 = new DateTime(d2.Year, d2.Month, d2.Day);
            var mDays = (int) (md1 - md2).TotalDays;

            if (mDays > span.Days)
            {
                mDays = (int)(md1.AddMonths(-1) - md2).TotalDays;
            }

            days = span.Days - mDays;


        }
        hours = span.Hours;
        minutes = span.Minutes;
    }
like image 32
Chuck Rostance Avatar answered Nov 08 '22 15:11

Chuck Rostance