Home > Software design >  Find next occurrence of a date in C#
Find next occurrence of a date in C#

Time:05-05

I would like to parse a date, identified by month and day, into a valid DateTime occurring in the future.

Assume today is 2023-05-06, below is a table of input and output values:

Input Output
06-24 2023-06-24
01-05 2024-01-05
02-29 2024-02-29

A naive way of achieving this would be to test if using DateTime.Now.Year for the Year portion produces a date in the future or not:

var today = DateTime.Now;
bool dateInFuture = new DateTime(today.Year, 02, 29)).CompareTo(today);

However you might have noticed that this will cause an exception as 2023-02-29 is not a valid date.

Ideally I would like this code to be able to work out if the date is actually invalid, so if 00-46 is passed as input it can tell me that that is not a real date, but that's a little tricky as my current implementation fails with dates that are actually valid.

CodePudding user response:

Here's my take:

DateTime? GetFutureDateFromMonthDay(DateTime today, string monthDay) =>
    Enumerable
        .Range(today.Year, 9)
        .Select(y => DateTime.TryParse($"{y}-{monthDay}", out DateTime result) ? (DateTime?)result : null)
        .Where(x => x != null)
        .Where(x => x > today)
        .FirstOrDefault();

Note that it looks ahead 9 years as it is possible to have an 8 year gap between leap years and you want to ensure that you are getting a future date, so it may require a further year. Changing today to new DateTime(2096, 5, 6) shows that issue.

So, given this input:

var today = new DateTime(2023, 5, 6);

var data = new[]
{
    new { input = "06-24", output = (DateTime?)new DateTime(2023, 6, 24) },
    new { input = "01-05", output = (DateTime?)new DateTime(2024, 1, 5) },
    new { input = "02-29", output = (DateTime?)new DateTime(2024, 2, 29) },
    new { input = "00-46", output = (DateTime?)null },
};

I run this query:

var query =
    from d in data
    let expected = d.output
    let actual = GetFutureDateFromMonthDay(today, d.input)
    select new
    {
        d.input,
        expected,
        actual,
        check = actual == expected,
    };

I get this result:

result

If go ahead and try new DateTime(2096, 5, 6) I now get this:

2096

This code produces the future date or it returns null to indicate that the input was bad.

CodePudding user response:

You could use DateTime.IsLeapYear(year) - as is shown in this answer in conjuction with DateTime.TryParse for check if the value to convert is a valid DateTime:

You can test this code in dotnetfiddle.net:

DateTime today = DateTime.Now;
Int32 year = DateTime.IsLeapYear(today.Year) ? today.Year : today.AddYears(-2).Year;
DateTime tempDate = DateTime.Now;

// String is concatenated in "yyyy-MM-dd" format: 
if (!DateTime.TryParse(year.ToString()   "-02-29", out tempDate))
{
    // Date is not valid.
    Console.WriteLine("Date ("   year.ToString()   "-02-29"   ") is not valid");
}
else
{
    // Date with "yyyy-MM-dd" format: 
    Console.WriteLine("Date is valid: "   tempDate.ToString("yyyy-MM-dd"));
}

CodePudding user response:

This may help:

var input = "02-29";
var year = DateTime.Today.Year;
if (DateTime.TryParse($"{year}-{input}", out DateTime date))
{
    var isFutureDate = date > DateTime.Now;
}
else
{
    // TODO: Manage error
}

You can define a method to that:

public static bool TryGetDate(string input, out DateTime date, out bool isFutureDate)
{
    var year = DateTime.Today.Year;

    if (input == "02-29")
    {
        while (!DateTime.IsLeapYear(year))
        {
            year  ;
        }
    }

    if (DateTime.TryParse($"{year}-{input}", out date))
    {
        isFutureDate = date > DateTime.Now;
        return true;
    }

    isFutureDate = false;
    return false;
}

And use it:

var input = "02-29";
if (TryGetDate(input, out DateTime date, out bool isFutureDate))
{
    // ...
}
else
{
    // TODO: Manage error
}
  • Related