Is there an elegant way to be permissive in date input in C# to accommodate user input like '2009-09-31' (e.g. September 31, which doesn't exist and causes DateTime.Parse to choke)? Ideally I would like to parse this as October 1 (e.g. latest possible date plus overflow).
One way to avoid incorrect dates in User input, is not to let them happen in the first instance, i.e. use a date picker control.
What about using DateTime.TryParse
?
Converts the specified string representation of a date and time to its DateTime equivalent using the specified culture-specific format information and formatting style, and returns a value that indicates whether the conversion succeeded.
This MSDN page shows examples of usage.
I think the DateTime.DaysInMonth
method can help a lot, after a TryParse
... you can implement the logic you are talking about
I don't believe this is handled directly for you. What you could do is parse the date yourself, as three separate integers:
- Parse the 2009, and construct a DateTime of 1st January 2009
- Parse the 09 and subtract one, then call
dt = dt.AddMonths(8)
to get September 1st - Parse the 31 and subtract one, then call
dt.AddDays(30)
This will handle things like 2009/13/01 to mean 1st January 2010. It won't do what you want with February 31st though, I suspect...
The compiler will just throw a cannot parse exception, so you'll have to write your own parser for this kind of problems.
Rule #1: As reasonably as possible, don't allow a user to input invalid data in the first place.
If you're doing Windows Forms, use the DateTimePicker as much as possible (although it has its limitations when it comes to locale); or, if your app is in ASP.NET, use a Calendar, or one of the AJAX Control Toolkit controls (I forget the exact name right now), but it does a little popup with a calendar.
I would strongly advise against separating out the date components into different fields (or parsing them as such) if there is ANY chance of needing to localize this application later.
You should (morally) not try to handle inputs like this. Is February 29, 1900 to be interpreted as March 1, 1900 (because February 29, 1900 could be interpreted as the day after February 28, 1900 but since it doesn't exist move the actual day after February 28, 1900) or as February 28, 1900 (because February 29, 1900 could be interpreted as the last day of February, 1900)? Another situation is what if the user means to type "2009-3-3" but because of fast and sticky fingers accidentally types "2009-3-33". Then rather than their error being caught, a custom parser will swallow this into, say, 4/2/2009. Because of situations like this, you should just DateTime.TryParse
the input and inform the user if invalid input occurs. That's what you should do.
Now, if it's a requirement that you handle such input. I would use something along the lines of the following:
static DateTime Parse(int year, int month, int day) {
DateTime date;
if (month < 1 || month > 12) {
int direction = month < 1 ? 1 : -1;
do {
month += direction * 12;
year -= direction;
} while (month < 1 || month > 12);
}
int daysInMonth = DateTime.DaysInMonth(year, month);
if (day < 1 || day > daysInMonth) {
date = new DateTime(year, month, daysInMonth);
int difference = day - daysInMonth;
date = date.AddDays(difference);
}
else {
date = new DateTime(year, month, day);
}
return date;
}