views:

326

answers:

6

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).

+3  A: 

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.

Mitch Wheat
TryParse will fail on problems like this. So you'll have to write your own parser one way or the other.
Jan Jongboom
+1  A: 

I think the DateTime.DaysInMonth method can help a lot, after a TryParse ... you can implement the logic you are talking about

Ahmed Khalaf
+11  A: 

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...

Jon Skeet
Well there is always the option of making your own Calendar based off the Gregorian Calendar, to handle date overflows... http://msdn.microsoft.com/en-us/library/system.globalization.gregoriancalendar.aspx However for this situation it is probably overkill. Or you might also be able to create your own IFormatProvider to handle what Jon suggested under the covers.
Nick Berardi
A: 

The compiler will just throw a cannot parse exception, so you'll have to write your own parser for this kind of problems.

Jan Jongboom
Not the compiler - the BCL.
Jon Skeet
A: 

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.

Jon Seigel
+1  A: 

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;
}
Jason
Soft-parsing errant dates may be bad UI design but how does morality enter into it?
bill weaver
There are two types of shoulds: that what we must do (imperative) and that what we ought to do (moral). We ought not (moral should) silently parse errant data.
Jason
Ah, i see where you're coming from. "Morally" just sounded a bit... something. After reading the original question, i was thinking it was weird, but supposed he had a reason. Sometimes important customers insist on an ill-advised "feature," where it's dumb but contractually required. He'd be "morally" obligated to soft-parse the date. :)
bill weaver