In this article, we will discuss how to parse string representations of date and time to an equivalent DateTime
value in C#. The available methods in the DateTime
class include Parse(), TryParse(), ParseExact()
and TryParseExact()
. These methods can parse a string to a DateTime object differently.
Please refer to DateTime Format In C# to learn more about DateTime format.
Each method has a list of overloads. Let’s see each of these methods and their overloads more closely. But before then, let’s learn more about customizing culture-related information when working with DateTime objects.
Working With DateTime Objects in C#
When we use the methods in the DateTime
class to parse, we need the System.Globalization
namespace as it contains classes that define culture-related information, such as the CultureInfo
class, and the DateTimeStyles
enum, which we will find helpful when parsing.
Current Culture Settings
The combination of a language and a region is known as a culture. The culture settings of our operating system determine the default date and time format.
We can check our current culture using CultureInfo.CurrentCulture
and define a different culture using the GetCultureInfo()
method if we want to:
DateTime date = new DateTime(2023, 1, 15, 14, 21, 37); Console.WriteLine($"My current culture: {CultureInfo.CurrentCulture}"); Console.WriteLine($"My date in the current culture({CultureInfo.CurrentCulture}): {date}"); CultureInfo culture = CultureInfo.GetCultureInfo("en-GB"); Console.WriteLine($"My date in the culture({culture}): {date.ToString(culture)}");
The current culture should be en-US
if we’re based in the US, and the dates are shown in different formats depending on the culture:
My current culture: en-US My date in the current culture(en-US): 1/15/2023 2:21:37 PM My date in the culture(en-GB): 15/01/2023 14:21:37
Another way to specify a different culture is to use the CultureInfo
class that implements the IFormatProvider
interface. Let’s replace the GetCultureInfo()
method with a new CultureInfo
instance and use the type IFormatProvider
:
DateTime date = new DateTime(2023, 1, 15, 14, 21, 37); Console.WriteLine($"My current culture: {CultureInfo.CurrentCulture}"); Console.WriteLine($"My date in the current culture({CultureInfo.CurrentCulture}): {date}"); IFormatProvider provider = new CultureInfo("en-GB"); Console.WriteLine($"My date in the culture({provider}): {date.ToString(culture)}");
The result should be the same:
My current culture: en-US My date in the current culture(en-US): 1/15/2023 2:21:37 PM My date in the culture(en-GB): 15/01/2023 14:21:37
We’ll use the IFormatProvider
interface frequently to define a specific culture in some parsing methods later.
The DateTimeStyles
The DateTimeStyles enum provides formatting options that customize string parsing for some date and time parsing methods. We’ll use a few of the DateTimeStyles
enum options for demonstrations later.
Use Parse() to Parse String to DateTime
The DateTime.Parse()
method has a list of overloads, and each converts the string data to an equivalent date and time. If the input string cannot be parsed by the CultureInfo
associated with the current culture, a FormatException
will be thrown.
Let’s take a look at each overload.
Parse(String)
This Parse(String) overload has a string parameter representing the date and time to be parsed. By default, it uses the operating system’s current culture and the DateTimeStyle.None
option.
Let’s assume the default culture is en-US
:
string dateString = "1/15/2023 02:21:37"; DateTime parsedDate = DateTime.Parse(dateString); Console.WriteLine(parsedDate); /* Output: 1/15/2023 2:21:37 AM */
The dateString
variable stores a string representation of date and time in the format of MM/dd/yyyy hh:mm:ss
. We pass this variable to the Parse()
method, and it should parse it to a DateTime
object with the format of M/dd/yyyy hh:mm:ss tt
, corresponding to the en-US
culture.
If we try to modify the value in the dateString
variable to 15/1/2023
, it will throw a FormatException
:
String '15/01/2023' was not recognized as a valid DateTime.
This is because the parsed DateTime is not valid in the en-US
culture as 15
is not considered a valid month.
Parse(String, IFormatProvider)
The previous overload doesn’t allow us to provide a different culture and uses the operating system’s current culture by default. Thankfully, this overload lets us set a culture-specific format using the IFormatProvider
parameter.
Let’s try to set the culture to fr-FR
and use the string 15/1/2023 02:21:37
this time. Because the new culture will see 15
as an invalid month if we use 1/15/2023 02:21:37
:
string dateString = "15/1/2023 02:21:37"; IFormatProvider provider = new CultureInfo("fr-FR"); DateTime parsedDate = DateTime.Parse(dateString, provider); Console.WriteLine(parsedDate); /* Output: 15/01/2023 2:21:37 AM */
We should expect a parsed DateTime of 15/01/2023 2:21:37 AM
.
Parse(String, IFormatProvider, DateTimeStyles)
This overload has an additional DateTimeStyles
parameter that allows us to customize string parsing.
Let’s use the DateTimeStyles.AssumeUniversal
for this example:
string dateString = "15/1/2023 02:21:37"; IFormatProvider provider = new CultureInfo("fr-FR"); DateTimeStyles styles = DateTimeStyles.AssumeUniversal; DateTime parsedDate = DateTime.Parse(dateString, provider, styles); Console.WriteLine(parsedDate); /* Output: 15/01/2023 1:21:37 PM */
Since we don’t specify the time zone, the string format is assumed to be UTC. The parsed DateTime will be 15/01/2023 1:21:37 PM
.
Parse(ReadOnlySpan<Char>, IFormatProvider)
This method’s overload is similar to the Parse(String, IFormatProvider)
overload, but it has a ReadOnlySpan<Char>
parameter instead of a string parameter.
The only difference here is to create a new read-only span over a string using the AsSpan()
method:
IFormatProvider provider = new CultureInfo("fr-FR"); DateTime parsedDate = DateTime.Parse("15/1/2023 02:21:37".AsSpan(), provider); Console.WriteLine(parsedDate); /* Output: 15/1/2023 2:21:37 AM */
We should have a similar parsed result of 15/01/2023 2:21:37 AM
.
This is a great option to allocate less memory and improve performance. To learn more ways of converting a string to a span, please check out How to Convert a String to a Span in C#.
Parse(ReadOnlySpan<Char>, IFormatProvider, DateTimeStyles)
This overload works the same way as the Parse(String, IFormatProvider, DateTimeStyles)
overload, but it uses a ReadOnlySpan<Char>
parameter instead.
We’ll set a similar example by using the DateTimeStyles.AssumeUniversal
but converting the string to a span this time:
ReadOnlySpan<char> dateSpan = "15/1/2023 02:21:37"; IFormatProvider provider = new CultureInfo("fr-FR"); DateTimeStyles styles = DateTimeStyles.AssumeUniversal; DateTime parsedDate = DateTime.Parse(dateSpan, provider, styles); Console.WriteLine(parsedDate); /* Output: 15/01/2023 1:21:37 PM */
The parsed DateTime should be similar too, 15/01/2023 1:21:37 PM
.
Use TryParse() to Parse String to DateTime
The TryParse()
method is a safer and more convenient way of converting strings to DateTime objects.
Instead of throwing an exception if the conversion is unsuccessful, the TryParse()
method returns a boolean value indicating whether or not the conversion was successful. Additionally, if the conversion is successful, the out
parameter will be populated with the converted value.
Let’s see each overload one by one.
TryParse(String, DateTime)
This is the simplest TryParse() overload with two parameters: a string and a DateTime, which is an out
parameter. If we pass a valid string representation of date and time, it will return true
along with the converted DateTime object. Otherwise, it will return false
with the minimum DateTime value:
string dateString = "1/15/2023"; DateTime parsedDate; bool isValidDate = DateTime.TryParse(dateString, out parsedDate); if (isValidDate) Console.WriteLine(parsedDate); /* Output: 1/15/2023 12:00:00 AM */ else Console.WriteLine($"Unable to parse date:{dateString}");
Considering the current culture is en-US
, the TryParse()
method returns true
and the parsedDate
variable stores the DateTime 1/15/2023 12:00:00 AM
.
TryParse(String, IFormatProvider, DateTime)
This overload has an additional IFormatProvider
parameter that allows us to set a culture-specific format.
Let’s try to set the culture to en-GB
and use the string 15/1/2023
because the new culture will see 15
as an invalid month if we use 1/15/2023
:
string dateString = "15/1/2023"; DateTime parsedDate; IFormatProvider provider = new CultureInfo("en-GB"); bool isValidDate = DateTime.TryParse(dateString, provider, out parsedDate); if (isValidDate) Console.WriteLine(parsedDate); /* Output: 15/01/2023 12:00:00 AM */ else Console.WriteLine($"Unable to parse date:{dateString}");
We should expect the isValidDate
variable to contain a true
value along with a parsed DateTime of 15/01/2023 12:00:00 AM
in the parsedDate
variable. In contrast, if we pass the string 1/15/2023
, it will return false
with a minimum DateTime value.
TryParse(String, IFormatProvider, DateTimeStyles, DateTime)
This TryParse() overload has an additional DateTimeStyles
parameter that allows us to customize string parsing.
Let’s use the DateTimeStyles.AdjustToUniversal
for this example:
string dateString = "2023/1/15 14:21:37-05:00"; DateTime parsedDate; IFormatProvider provider = new CultureInfo("en-GB"); DateTimeStyles styles = DateTimeStyles.AdjustToUniversal; bool isValidDate = DateTime.TryParse(dateString, provider, styles, out parsedDate); if (isValidDate) Console.WriteLine(parsedDate); /* Output: 15/01/2023 7:21:37 PM */ else Console.WriteLine($"Unable to parse date:{dateString}");
Using AdjustToUniversal
as the DateTimeStyle
option means that the parsedDate
variable will receive the date and time in Coordinated Universal Time (UTC) format. If we pass a date and time with the EST time zone, we should get a parsed DateTime 15/01/2012 7:21:37 PM
.
TryParse(ReadOnlySpan<Char>, DateTime)
This overload is similar to the TryParse(String, DateTime)
overload, but it has a ReadOnlySpan<Char>
parameter instead of a string parameter.
The only difference here is to create a new read-only span over a string:
ReadOnlySpan<char> dateSpan = "1/15/2023"; DateTime parsedDate; bool isValidDate = DateTime.TryParse(dateSpan, out parsedDate); if (isValidDate) Console.WriteLine(parsedDate); /* Output: 1/15/2023 12:00:00 AM */ else Console.WriteLine($"Unable to parse date:{dateSpan}");
Since the current culture is en-US
and if the conversion is successful, the parse method returns true
and the parsedDate
variable stores the DateTime 1/15/2023 12:00:00 AM
.
TryParse(ReadOnlySpan<Char>, IFormatProvider, DateTime)
This method’s overload works the same way as the TryParse(String, IFormatProvider, DateTime)
overload, but it uses a ReadOnlySpan<Char>
parameter instead.
We’ll use the same example but convert the string to a span this time:
ReadOnlySpan<char> dateSpan = "15/1/2023"; DateTime parsedDate; IFormatProvider provider = new CultureInfo("en-GB"); bool isValidDate = DateTime.TryParse(dateSpan, provider, out parsedDate); if (isValidDate) Console.WriteLine(parsedDate); /* Output: 15/01/2023 12:00:00 AM */ else Console.WriteLine($"Unable to parse date:{dateSpan}");
The parsed DateTime should be similar, 15/01/2023 12:00:00 AM
.
TryParse(ReadOnlySpan<Char>, IFormatProvider, DateTimeStyles, DateTime)
Finally, this overload isn’t too different from the TryParse(String, IFormatProvider, DateTimeStyles)
overload, the only difference is it uses a ReadOnlySpan<Char>
parameter.
We’ll also use the same example but with a span. To keep it simple, we’ll set the DateTimeStyles.None
option this time:
ReadOnlySpan<char> dateSpan = "15/1/2023"; DateTime parsedDate; IFormatProvider provider = new CultureInfo("en-GB"); DateTimeStyles styles = DateTimeStyles.None; bool isValidDate = DateTime.TryParse(dateSpan, provider, styles, out parsedDate); if (isValidDate) Console.WriteLine(parsedDate, provider); /* Output: 15/01/2023 12:00:00 AM */ else Console.WriteLine($"Unable to parse date:{dateSpan}");
Since we don’t set a specific DateTimeStyle
, the parsed DateTime should be 15/01/2023 12:00:00 AM
.
Use ParseExact() to Parse String to DateTime
The DateTime.ParseExact()
method has some overloaded forms, each allowing for different arguments and options to be passed. This method is used to parse a string that is formatted exactly as specified by the format string argument and culture-specific format information, and it throws a FormatException
if the string cannot be parsed into a DateTime
value that matches the exact format string.
Let’s go to the overloads.
ParseExact(String, String, IFormatProvider)
This overload has three input parameters: two strings and an IFormatProvider
. The first parameter is a string representation of date and time, the second one defines the exact format, and we can set a culture-specific format using the last parameter.
Let’s see how it works:
string dateString = "15/1/2023 10:12:12"; string format = "dd/M/yyyy hh:mm:ss"; DateTime parsedDate; IFormatProvider provider = new CultureInfo("fr-FR"); try { parsedDate = DateTime.ParseExact(dateString, format, provider); Console.WriteLine(parsedDate); /* Output: 15/01/2023 10:12:12 AM */ } catch(FormatException ex) { Console.WriteLine(ex.Message); }
The dateString
variable receives the string representation of date and time 15/1/2023 10:12:12
, with the format of dd/M/yyyy hh:mm:ss
. The Parse()
method will parse the string to a DateTime in fr-FR
format 15/01/2023 10:12:12 AM
.
Note that the format of the string representation must match the specified format exactly. Else it will throw a FormatException
if we try to edit the format slightly differently, for example dd/MM/yyyy hh:mm:ss
:
String '15/1/2023 10:12:12' was not recognized as a valid DateTime.
ParseExact(String, String, IFormatProvider, DateTimeStyles)
Unlike the prior overload, we have an additional DateTimeStyles
parameter for this ParseExact() overload. Let’s try to round-trip a DateTime value this time:
string dateString = "2023-01-15T14:12:12.0000000Z"; DateTime parsedDate; IFormatProvider provider = new CultureInfo("fr-FR"); styles = DateTimeStyles.RoundtripKind; try { parsedDate = DateTime.ParseExact(dateString, "o", provider, styles); Console.WriteLine(parsedDate); /* Output: 15/01/2023 2:12:12 PM */ } catch (FormatException ex) { Console.WriteLine(ex.Message); }
The dateString
variable receives the string representation of date and time 2023-01-15T14:12:12.0000000Z
. This represents an ISO 8601 string which includes time-zone information.
We use the o
round-tripping format specifier to specify the format and the RoundtripKind
DateTimeStyle
.
We should expect a converted DateTime in fr-FR
format 15/01/2023 2:12:12 PM
.
ParseExact(ReadOnlySpan<Char>, ReadOnlySpan<Char>, IFormatProvider, DateTimeStyles)
This overload is quite similar to the previous overload, but this uses ReadOnlySpan<char>
instead of a string. We’ll create a new read-only span over a string using implicit conversion from a string:
ReadOnlySpan<char> dateSpan = "2023-01-15T14:12:12.0000000Z"; DateTime parsedDate; IFormatProvider provider = new CultureInfo("fr-FR"); DateTimeStyles styles = DateTimeStyles.RoundtripKind; try { parsedDate = DateTime.ParseExact(dateSpan, "o", provider, styles); Console.WriteLine(parsedDate); /* Output: 15/01/2023 2:12:12 PM */ } catch (FormatException ex) { Console.WriteLine(ex.Message); }
It should produce the same result by passing similar arguments as the previous example to this overload. Using span is a good option to allocate less memory and improve performance.
ParseExact(String, String[], IFormatProvider, DateTimeStyles)
This method’s overload is similar to the ParseExact(String, String, IFormatProvider, DateTimeStyles)
overload, but the second parameter can accept an array of exact date formats this time:
string[] formats = { "yyyy-MM-dd'T'HH:mm:ss.fff'Z'", "yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyyMMdd'T'HH:mm:ss.fff'Z'", "yyyyMMdd'T'HH:mm:ss'Z'", "dd-MM-yyyy HH:mm:ss", "dd/MM/yyyy HH:mm:ss" }; IFormatProvider provider = CultureInfo.InvariantCulture; DateTimeStyles styles = DateTimeStyles.None; var dateStringsByFormat = new Dictionary<string, string> { { "yyyy-MM-dd'T'HH:mm:ss.fff'Z'", "2023-01-15T14:12:12.000Z" }, { "yyyy-MM-dd'T'HH:mm:ss'Z'", "2023-01-15T14:12:12Z" }, { "yyyyMMdd'T'HH:mm:ss.fff'Z'", "20230115T14:12:12.000Z" }, { "yyyyMMdd'T'HH:mm:ss'Z'", "20230115T14:12:12Z" }, { "dd-MM-yyyy HH:mm:ss", "15-01-2023 14:12:12" }, { "dd/MM/yyyy HH:mm:ss", "15/01/2023 14:12:12" } }; try { foreach (string format in formats) { string dateString = dateStringsByFormat[format]; DateTime parsedDate = DateTime.ParseExact(dateString, formats, provider, styles); Console.WriteLine(parsedDate); } } catch (FormatException ex) { Console.WriteLine(ex.Message); }
We provide multiple formats in a string array and then create a collection of key-value pairs representing the format and the string representation of date and time. The list of keys is similar to the ones in the formats
array.
To demonstrate a simpler example, we use an InvariantCulture
culture and do not specify a DateTimeStyle
.
We then loop through the array and get the string representation of date and time from the list of key-value pairs using format
as the key. We pass the string as the first argument and the formats
array as the second argument to the overload. The parsing should be successful if the array of allowable formats recognizes the string format.
ParseExact(ReadOnlySpan<Char>, String[], IFormatProvider, DateTimeStyles)
For the last overload for the DateTime.ParseExact()
method, it’s similar to the previous overload, but the first parameter takes a ReadOnlySpan<char>
instead of a string:
string[] formats = { "yyyy-MM-dd'T'HH:mm:ss.fff'Z'", "yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyyMMdd'T'HH:mm:ss.fff'Z'", "yyyyMMdd'T'HH:mm:ss'Z'", "dd-MM-yyyy HH:mm:ss", "dd/MM/yyyy HH:mm:ss" }; IFormatProvider provider = CultureInfo.InvariantCulture; DateTimeStyles styles = DateTimeStyles.None; var dateStringsByFormat = new Dictionary<string, string> { { "yyyy-MM-dd'T'HH:mm:ss.fff'Z'", "2023-01-15T14:12:12.000Z" }, { "yyyy-MM-dd'T'HH:mm:ss'Z'", "2023-01-15T14:12:12Z" }, { "yyyyMMdd'T'HH:mm:ss.fff'Z'", "20230115T14:12:12.000Z" }, { "yyyyMMdd'T'HH:mm:ss'Z'", "20230115T14:12:12Z" }, { "dd-MM-yyyy HH:mm:ss", "15-01-2023 14:12:12" }, { "dd/MM/yyyy HH:mm:ss", "15/01/2023 14:12:12" } }; try { foreach (string format in formats) { ReadOnlySpan<char> dateSpan = dateStringsByFormat[format]; DateTime parsedDate = DateTime.ParseExact(dateSpan, formats, provider, styles); Console.WriteLine(parsedDate); } } catch (FormatException ex) { Console.WriteLine(ex.Message); }
We use similar values as the previous example, but this time we’ll first create a new read-only span over a string before parsing.
Use TryParseExact() to Parse String to DateTime
The DateTime.TryParseExact()
method works like the DateTime.TryParse()
method, but the format of the string representation must match the specified format exactly like the DateTime.ParseExact()
method.
We’ve seen a considerable amount of examples using different DateTimeStyles
and IFormatProvider
in the previous overloads, so we’ll keep them simple in the upcoming examples by using the CultureInfo.InvariantCulture
and DateTimeStyle.None
values.
Let’s look at each overload.
TryParseExact(String, String, IFormatProvider, DateTimeStyles, DateTime)
The first four parameters in this overload are similar to the ones in the ParseExact(String, String, IFormatProvider, DateTimeStyles)
overload, but with an additional out
DateTime
parameter.
Let’s try to parse using this overload:
string dateString = "15-01-2023"; string format = "dd-MM-yyyy"; DateTime parsedDate; IFormatProvider provider = CultureInfo.InvariantCulture; DateTimeStyles styles = DateTimeStyles.None; bool isValidDate = DateTime.TryParseExact(dateString, format, provider, styles, out parsedDate); if (isValidDate) Console.WriteLine(parsedDate); /* Output: 1/15/2023 12:00:00 AM */ else Console.WriteLine($"Unable to parse date:{dateString}");
The dateString
variable stores the string representation of the date 15-1-2023
, with the format of dd-MM-yyyy
. By using the CultureInfo.InvariantCulture
and DateTimeStyle.None
, we should expect the parsed DateTime to be 1/15/2023 12:00:00 AM
.
TryParseExact(ReadOnlySpan<Char>, ReadOnlySpan<Char>, IFormatProvider, DateTimeStyles, DateTime)
This TryParseExact() overload is similar to the previous overload, except that we use ReadOnlySpan<Char>
in the first two parameters:
ReadOnlySpan<char> dateSpan = "15-01-2023"; ReadOnlySpan<char> formatSpan = "dd-MM-yyyy"; DateTime parsedDate; IFormatProvider provider = CultureInfo.InvariantCulture; DateTimeStyles styles = DateTimeStyles.None; bool isValidDate = DateTime.TryParseExact(dateSpan, formatSpan, provider, styles, out parsedDate); if (isValidDate) Console.WriteLine(parsedDate); /* 1/15/2023 12:00:00 AM */ else Console.WriteLine($"Unable to parse date:{dateSpan}");
Using the same example, we should expect a similar result of a valid parsed DateTime of 1/15/2023 12:00:00 AM
.
TryParseExact(String, String[], IFormatProvider, DateTimeStyles, DateTime)
We’ve previously introduced the string array parameter that accepts multiple formats in the ParseExact(String, String[], IFormatProvider, DateTimeStyles)
overload. This overload is similar but returns an out
parameter.
We’ll be using the same example:
string[] formats = { "yyyy-MM-dd'T'HH:mm:ss.fff'Z'", ..., "dd/MM/yyyy HH:mm:ss" }; IFormatProvider provider = CultureInfo.InvariantCulture; DateTimeStyles styles = DateTimeStyles.None; var dateStringsByFormat = new Dictionary<string, string> { { "yyyy-MM-dd'T'HH:mm:ss.fff'Z'", "2023-01-15T14:12:12.000Z" }, ... }; foreach (var format in formats) { string dateString = dateStringsByFormat[format]; DateTime parsedDate; bool isValidDate = DateTime.TryParseExact(dateString, formats, provider, styles, out parsedDate); if (isValidDate) Console.WriteLine(parsedDate); else Console.WriteLine($"Unable to parse date:{dateString}"); }
We loop through the formats
array, get the string value from the list of key-value pairs, and try to parse the string to a DateTime that is recognizable within the formats
array. Note that we pass the string as the first argument and the formats
array as the second argument to the overload. If the parsing is successful, the method should return true
and a converted DateTime each time it’s triggered.
TryParseExact(ReadOnlySpan<Char>, String[], IFormatProvider, DateTimeStyles, DateTime)
This last overload is similar to the previous overload but uses span for the first parameter. We’ll be using the same example, but we’ll convert the string representation of the date and time to span each time we want to call the overload:
string[] formats = { "yyyy-MM-dd'T'HH:mm:ss.fff'Z'", ... "dd/MM/yyyy HH:mm:ss" }; IFormatProvider provider = CultureInfo.InvariantCulture; DateTimeStyles styles = DateTimeStyles.None; var dateStringsByFormat = new Dictionary<string, string> { { "yyyy-MM-dd'T'HH:mm:ss.fff'Z'", "2023-01-15T14:12:12.000Z" }, ... }; foreach (var format in formats) { ReadOnlySpan<char> dateSpan = dateStringsByFormat[format]; DateTime parsedDate; bool isValidDate = DateTime.TryParseExact(dateSpan, formats, provider, styles, out parsedDate); if (isValidDate) Console.WriteLine(parsedDate); else Console.WriteLine($"Unable to parse date:{dateSpan}"); }
Since each string representation of the date and time passed has a format that is identifiable within the formats
array, each TryParseExact()
method call should return true
and a successfully parsed DateTime.
Conclusion
In this article, we’ve learned that in C#, we have four different methods Parse()
, TryParse()
, ParseExact()
and TryParseExact()
with their overloads in the DateTime
class to parse a string representation of date and time to a DateTime object, and we can use them according to our needs.