Yet another diagram here. This time we get to see some differences between a parser/validator model where the pieces are separated for purposes of providing enhanced user feedback and more specialized return structures. The end result is that we are able to provide many more services with about the same amount of work. This is the best all-around option because it means a relatively small amount of code, requires the least amount of end-user knowledge (even less so than the regular expressions option), and the abstract mapping is appropriate for nearly any programming language.
User Input:
You start to see the first benefits to a parser/validator model in that the input can now take multiple formats. In general parsing a string into a date not only involves make sure the string is in the appropriate format, but also checking the numeric values of portions of the string. The string parsing model still exists through the use of the Parse or ParseExact method, but there are other options. You can specify already parsed integers when creating a DateTime or optionally pre-split string portions. You'd never enable these features through expressions because you'd wind up coding multiple expressions, processing the strings into integers earlier in the data flow model, and wrting a bunch of code to select which expressions to use for validation. Not that you couldn't enable these features, just that it doesn't buy you anything.
Parse:
I put some range checking notes under parsing. This happens because of basic validation of month, day, and year ranges that occurs during the parse or construction phases of a DateTime. If you were writing a light DateTime parser what you might do is leave the range checking in, but rather than throw exceptions provide more feedback. The nice thing about the DateTime integrated range validation is that as the values are parsed into strongly typed integers and a basic range check is done rather than working at the character comparison level.
The parsing of a DateTime is much faster than the character level parsing of a regular expression even though more work is being done towards the end result in terms of getting the data from the least usable format (a string of several values) to the most usable format (a numeric type). In fact even with conversion in place the parsing is much more functional (the possibility of more format strings) and robust.
Result:
Unfortunately the result of a parse or constructed DateTime has onl two possibilities. Either you get a somewhat valid date returned (still needs additional validation) or you get an exception. If you get the exception it is just like a false return value from the Expression processor. To avoid the exception a TryParse in Whidbey will return a boolean true/false depending on parsing success or failure. You still get the same strongly typed parsed results, just without the exception in the failure case.
The result here is massively different from an Expression result. The reason is that each field is now in a numeric format and has been preconverted. It is ready for work to be done, since undoubtedly you'll want to do some work. It is also in the most optimal storage format in terms of size. If you get to this point there were no parsing errors, but there may still be other invalid ranges within the dates.
Validate:
Some of the validation has already occured in the parsing phase, but the extra validation comes in now. By using the strongly typed results you can quickly perform comparisons on the fields to remove days that never existed within a particular calendar. In fact it becomes possible to chain various validators on the end of the process depending on the level of checking that needs to apply. We have several options at this juncture. We can enforce historical accuracy by removing various days that are valid at the parser level but shouldn't be allowed at a historical accuracy level. We can also enforce programmatic ranges such as only allowing dates after 2000.
Each validator in the chain gets a chance to flag an error. This can provide the user with great granularity in user feedback.
User Feedback:
Tell the user where the process failed. There is a lot of work happening on the date and a number of different things that can go wrong. Arguably if you are only asking for a basic date or you expect your users to be smart maybe this doesn't matter to you. However, if you are enforcing odd constraints, such as historical accuracy then user feedback becomes much more important.
Even more important than giving the feedback to the user is having the ability to give the user the feedback. You might use a once over expression to give you a true/false, but that result doesn't allow you to determine enough information to figure out why. In fact you'd have to reparse the string again just to figure out why. Documentation doesn't fix this problem because if I give you a list of ten things that go wrong, but I only give you enough information to tell you that ONE of them went wrong, you still have to figure out which of the ten happened. Originally the .NET Terrarium just told you if your assembly was valid or invalid, but it didn't tell you why. We added reporting because there are about 15 different things that can happen. This allowed us as programmers to test the validation logic. It also allowed end users to fix their own creature code by telling them exactly what was wrong with their code.
Conclusion:
We are almost to the point of having a truly functional date-time parser. At this level there is still a black-box at the parser level. We can't get an internal peak at which values are missing at the parser level nor do we get a look at the range checking. It would be nice to extend these values to the user. After all, we are doing the range checking already, we are doing the work in order to fail the parsing, there isn't any reason once the parsing has already failed not to give an indication of the condition of failure. A light date-time parser is next. This is a true parser and it gives actual feedback at all steps in the process. From an end-user perspective it is the most functional. It is also most functional as a reusable component for developers because it provides them the fastest parsing with more feedback than any of the other methods. The trade-off is programming complexity, but I can assure you that basic parsing routines require only the most basic programming constructs. There won't be anything strange in this process whatsoever.