Ah…error messages.
Sometimes very useful, other times not so much, but always present in a developer?s life. And that?s what today?s post is about.
Not ?error messages? in general, but one message in particular: ?String was not recognized as a valid DateTime.?
As a developer, you?re pretty much guaranteed to have to write code that handles date and time. Which means you?re also bound to have some problems while parsing strings to DateTime values, and that?s what this post will cover.
We?ll start out by talking about the causes of the issue. You’ll learn about globalization and how big a role it plays in the consistency and correction of your application. Then, we?ll close with practical advice on what you can do to prevent such problems from happening in the first place.
Let?s get started!
Download a free trial of CodeIt.Right and get an instant, automated code review.
“String Was Not Recognized as a Valid DateTime.” Why Does That Happen?
I’ll concede?it: As far as error messages go, “String was not recognized as a valid DateTime” isn’t bad at all. It states that you’re trying to get a DateTime value out of a string that is not a valid?DateTime.
Pretty easy, right?
Well, things would be a walk in the park if you got this message only when trying to parse, say, “potato” as a DateTime. The problem is, more often than not, the offending string is something that, at least to you, looks like a perfectly normal date and time.
The really important piece in the last sentence was “at least to you,” as you’ll see in a second.
For instance, consider the following line of C# code:
DateTime d = DateTime.Parse("15/12/2019");
Go ahead, create a console app and place that line of code in it. Run the application. Did it run successfully? Or, did you get the “String was not recognized as a valid DateTime value” message?
As it turns out, whether the code works like a charm or crashes is mostly a matter of geography.
Date Formats Are Tricky
We humans have a messy relationship with time. One of the ways this messiness manifests itself is through the countless date formats used around the world.
For instance, think about the date June 14th, 2019. (There’s nothing special about that date; I got it from this Random Calendar Date Generator.)
Now consider this list, which shows just some of the possible ways that date can be represented, in actual date/time formats used by real people every day across the globe:
- 14/06/2019
- 14.06.2019
- 14-06-2019
- 14/06/19
- 06/14/2019
- 2019-06-14
It doesn’t take long to reach the conclusion that some strings that are perfectly valid dates in one format are not in others. For example, if you pass the “06/14/2019” string when the “dd/MM/yyyy” format is expected, the parsing will evidently fail, since there’s no month 14.
So, who gets to decide which format is acceptable when parsing dates? Read on to find out.
“String Not Recognized…” No More: Making the Error Go Away
Let’s revisit the code example from the previous section:
DateTime d = DateTime.Parse("15/12/2019");
We’ll now make a small edit to this line of code that will ensure it always works. (We’ll edit first and explain later.) Here’s the new version of the code:
DateTime d = DateTime.Parse( "15/12/2019", new System.Globalization.CultureInfo("pt-BR"));
Go ahead. Edit your code and run the app again. If it worked for you the first time, then it’ll just continue working with no alterations whatsoever. But if the first version crashed for you, it’ll work this time.
Why is that? What is this CultureInfo thing? And what does that “pt-BR” thing mean?
It’s All a Matter of Culture
There are many methods in the .NET BCL (Base Class Library) that are sensitive to culture. But what does culture mean in this context?
Some people will say it refers only to the language. And sure, language is involved in culture, but it’s far from being the only factor.
Here goes my definition: Culture is a set of conventions and expectations about the way information is presented. I know that sounds broad, so let’s get more specific. A given culture will contain, among other things, information about
- the calendar used in the country;
- number formats (e.g., do the people in the country use commas or periods as decimal separators); and
- a plethora of date and time formats.
In the example from the previous section, you saw the “pt-BR” string. That identifies the “Portuguese – Brazilian” culture. Since in Brazil the standard short date format is “dd/MM/yyyy”, by explicitly passing the pt-BR culture as a parameter, we enabled the parsing to work as expected.
Taking CultureInfo for Granted: Yay or Nay?
So now you know that the CultureInfo class represents a culture in the .NET BCL. But how does the concept of culture fit in the “String Not Recognized” feature?
As I said earlier, there are a lot of methods in .NET that take an instance of CultureInfo as one of its parameters. What also usually happens is that those methods have overloads that don’t take a CultureInfo as a parameter but assume the culture of the current thread instead.
Which means that the answer to the question above?to assume a default culture or not?is the dreaded “It depends.”
Consider an example. Let’s say a Brazilian developer is writing an application and needs to display the current date in the standard Brazilian short date format. To format the date, they write something like this:
var formattedDate = DateTimeOffset.Now.ToString("dd/MM/yyyy");
The code seems to work and it passes code review, so they call it a day. Not long after that, the company for which the developer works starts getting clients in the US. That’s exciting news…but Americans use a different date format. How to handle that?
If the developer in our story is unlucky enough, it won’t take long until someone suggests something like this:
var format = country == "br" ? "dd/MM/yyyy" : "MM/dd/yyyy"; var formattedDate = DateTimeOffset.Now.ToString(format);
The code above is definitely not OK, and I’m not even talking about the fact that it doesn’t handle the possibility of a third (or fourth, or fifth) format. No, the proper solution would be this:
var formattedDate = DateTimeOffset.Now.ToString("d");
The “d” format code refers to the short date format. There’s an overload for that method that takes a CultureInfo object as a parameter, but we’re not using it. Instead, we’re using the overload that assumes the current culture. That way, it will use the correct format for whatever the current culture is in the system.
Culture, Date Formats, and All That Jazz: How to Get Rid of “String Not Recognized as a Valid DateTime” for Good
As we’ve just seen, there are situations in which it makes sense for us to take the culture for granted.
However, there are scenarios in which the opposite is true. Parsing is one of those situations: More often than not, you want to explicitly declare the culture.
In C#/.NET, you have the following options for parsing DateTime values:
- DateTime.Parse
- DateTime.ParseExact
- DateTime.TryParse
- DateTime.TryParseExact
The “Try” versions of the methods return a Boolean value to indicate whether the parsing was successful or not, while their “non-try” counterparts throw when the parsing fails. The “Exact” variations allow you to explicitly pass one or more formats for the parsing.
The advice here is this: Always explicitly pass the culture, unless you’ve got a pretty decent reason not to. If you’re sure of the format the date is supposed to be in, then provide that format using the “Exact” variations of the method you’ve picked.
Tools at Your Disposal
SubMain has an offering called CodeIt.Right, which is an automated code review tool. It will check your source code against a variety of rules and give you instant feedback about its quality. As it turns out, CodeIt.Right actually has a rule that will gently nag you to always specify a CultureInfo instance whenever there’s a method overload that requires it.
Neat, right?
I suggest you download CodeIt.Right today and give it a try. It can definitely help you avoid not only “String Not Recognized…” but a lot of other nasty errors as well. Thanks for reading, and until next time!