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:
If go ahead and try new DateTime(2096, 5, 6)
I now get this:
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
}