How to create a .NET DateTime from ISO 8601 format - c#

I've found how to turn a DateTime into an ISO 8601 format, but nothing on how to do the reverse in C#.
I have 2010-08-20T15:00:00Z, and I want to turn it into a DateTime object.
I could separate the parts of the string myself, but that seems like a lot of work for something that is already an international standard.

This solution makes use of the DateTimeStyles enumeration, and it also works with Z.
DateTime d2 = DateTime.Parse("2010-08-20T15:00:00Z", null, System.Globalization.DateTimeStyles.RoundtripKind);
This prints the solution perfectly.

Although MSDN says that "s" and "o" formats reflect the standard, they seem to be able to parse only a limited subset of it. Especially it is a problem if the string contains time zone specification. (Neither it does for basic ISO8601 formats, or reduced precision formats - however this is not exactly your case.) That is why I make use of custom format strings when it comes to parsing ISO8601. Currently my preferred snippet is:
static readonly string[] formats = {
// Basic formats
"yyyyMMddTHHmmsszzz",
"yyyyMMddTHHmmsszz",
"yyyyMMddTHHmmssZ",
// Extended formats
"yyyy-MM-ddTHH:mm:sszzz",
"yyyy-MM-ddTHH:mm:sszz",
"yyyy-MM-ddTHH:mm:ssZ",
// All of the above with reduced accuracy
"yyyyMMddTHHmmzzz",
"yyyyMMddTHHmmzz",
"yyyyMMddTHHmmZ",
"yyyy-MM-ddTHH:mmzzz",
"yyyy-MM-ddTHH:mmzz",
"yyyy-MM-ddTHH:mmZ",
// Accuracy reduced to hours
"yyyyMMddTHHzzz",
"yyyyMMddTHHzz",
"yyyyMMddTHHZ",
"yyyy-MM-ddTHHzzz",
"yyyy-MM-ddTHHzz",
"yyyy-MM-ddTHHZ"
};
public static DateTime ParseISO8601String ( string str )
{
return DateTime.ParseExact ( str, formats,
CultureInfo.InvariantCulture, DateTimeStyles.None );
}
If you don't mind parsing TZ-less strings (I do), you can add an "s" line to greatly extend the number of covered format alterations.

using System.Globalization;
DateTime d;
DateTime.TryParseExact(
"2010-08-20T15:00:00",
"s",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal, out d);

Here is one that works better for me (LINQPad version):
DateTime d;
DateTime.TryParseExact(
"2010-08-20T15:00:00Z",
#"yyyy-MM-dd\THH:mm:ss\Z",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal,
out d);
d.ToString()
produces
true
8/20/2010 8:00:00 AM

It seems important to exactly match the format of the ISO string for TryParseExact to work. I guess Exact is Exact and this answer is obvious to most but anyway...
In my case, Reb.Cabin's answer doesn't work as I have a slightly different input as per my "value" below.
Value: 2012-08-10T14:00:00.000Z
There are some extra 000's in there for milliseconds and there may be more.
However if I add some .fff to the format as shown below, all is fine.
Format String: #"yyyy-MM-dd\THH:mm:ss.fff\Z"
In VS2010 Immediate Window:
DateTime.TryParseExact(value,#"yyyy-MM-dd\THH:mm:ss.fff\Z", CultureInfo.InvariantCulture,DateTimeStyles.AssumeUniversal, out d);
true
You may have to use DateTimeStyles.AssumeLocal as well depending upon what zone your time is for...

This works fine in LINQPad4:
Console.WriteLine(DateTime.Parse("2010-08-20T15:00:00Z"));
Console.WriteLine(DateTime.Parse("2010-08-20T15:00:00"));
Console.WriteLine(DateTime.Parse("2010-08-20 15:00:00"));

DateTime.ParseExact(...) allows you to tell the parser what each character represents.

Related

ParseExact with format "dMMyyyy" not recognizing date

I was looking in the Microsoft doc's and I can't find any explanation why ParseExact doesn't understand my date.
Could somebody explain why this code throws an exception?
DateTime.ParseExact("6092019", "dMMyyyy", CultureInfo.InvariantCulture, DateTimeStyles.None)
From the docs: DateTime.ParseExact Method
If format is a custom format pattern that does not include date or
time separators (such as "yyyyMMddHHmm"), use the invariant culture
for the provider parameter and the widest form of each custom format
specifier. For example, if you want to specify hours in the format
pattern, specify the wider form, "HH", instead of the narrower form,
"H".
So in your case you probably should use approach suggested in John's answer - add "missing" zero and parse with wider date format "dd"
The problem here seems to be that d can be a one-digit or two-digit date, so the parser struggles to determine if "2102019" refers to the 2nd of November 2019, or the 21st of... and then it breaks. With delimiters, the parser is able to act more intelligently. It will happily parse "2-10-2019" using "d-MM-yyyy".
My suggested solution to your problem is to pad the string, and change your format string:
string dateToParse = "6092019";
string paddedDateToParse = dateToParse?.PadLeft(8, '0'); // 06092019
DateTime parsedDate = DateTime.ParseExact(paddedDateToParse, "ddMMyyyy", CultureInfo.InvariantCulture, DateTimeStyles.None);
Try it online

How to parse this DateTimeOffset?

I'm getting a DateTimeOffset string as "2018-10-16T193850+0200", but I think it's none of the standard formats. Mainly, the "+0200" part is not standard, because it lacks the colon.
What format do I have to specify to parse DateTimeOffsets like this? thank you!
You can use ParseExact:
DateTimeOffset offsetDate = DateTimeOffset.ParseExact(
"2018-10-16T193850+0200",
"yyyy-MM-dd'T'HHmmsszzzz",
DateTimeFormatInfo.InvariantInfo,
DateTimeStyles.None);
Read: Custom Date and Time Format Strings
Unfortunately setting DateTimeInfo.TimeSeparator to empty string won't help.
But you can use DateTimeOffset.ParseExact as follows:
DateTimeOffset date = DateTimeOffset.ParseExact("2018-10-16T193850+0200", "yyyy'-'MM'-'dd'T'HH''mm''ss''K", CultureInfo.InvariantCulture.DateTimeFormat);
The trick is to specify 'K' format specifier in order to accept all kinds of offsets. Additionally to the above date string the following will be parsed correctly as well:
"2018-10-16T193850Z" - UTC
"2018-10-16T193850" - local time
EDIT
My answer is similar to Tim Schmelter's one, except by the 'K' part.

Transform between datetime formats

I am facing a problem in which I need to transform dates in a given input format into a target one. Is there any standard way to do this in C#?
As an example say we have yyyy.MM.dd as the source format and the target format is MM/dd/yyy (current culture).
The problem arises since I am using a parsing strategy that gives priority to the current culture and then if it fails it tries to parse from a list of known formats. Now say we have two equivalent dates one in the source culture above (2015.12.9) and the other in the current culture (9/12/2015). Then if we attempt to parse this two dates the month will be 12 for the first case and in the second will be 9, so we have an inconsistency (they were supposed to mean be the same exact date).
I believe that if existing it should be something as
DateTime.Convert(2015.12.9, 'yyyy/MM/dd', CultureInfo.CurrentCulture).
Any ideas?
EDIT:
Thank you all for your ideas and suggestions, however the interpretation most of you gave to my question was not quite right. What most of you have answered is a direct parse in the given format and then a conversion to the CurrentCulture.
DateTime.ParseExact("2015.12.9", "yyyy.MM.dd", CultureInfo.CurrentCulture)
This will still return 12 as month, although it is in the CurrentCulture format. My question thus was, is there any standard way to transform the date in yyyy.MM.d to the format MM/dd/yyy so that the month is now in the correct place and THEN parsed it in the target culture. Such function is likely to be unexisting.
DateTime.ParseExact is what you are looking for:
DateTime parsedDate = DateTime.ParseExact("2015.12.9", "yyyy.MM.d", CultureInfo.InvariantCulture);
Or eventualy DateTime.TryParseExact if you're not confident with input string.
I know it's late but I try to explain little bit deep if you let me..
I am facing a problem in which I need to transform dates in any format
to a target one.
There no such a thing as dates in any format. A DateTime does not have any implicit format. It just has date and time values. Looks like you have a string which formatted as date and you want to convert another string with different format.
Is there any standard way to do this in C#?
Yes. You can parse your string with DateTime.ParseExact or DateTime.TryParseExact first with specific format to DateTime and then generate it's string representation with a different format.
As an example say we have yyyy.MM.dd as the source format and the
target format is MM/dd/yyy (current culture).
I didn't understand what is the meaning of current culture in this sentences and I assume you want yyyy not yyy, but you can generate it as I described above like;
string source = "2015.12.9";
DateTime dt = DateTime.ParseExact(source, "yyyy.MM.d", CultureInfo.InvariantCulture);
string target = dt.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture); // 12/09/201
The problem arises since I am using a parsing strategy that gives
priority to the current culture and then if it fails it tries to parse
from a list of known formats.
Since you didn't show any parsing strategy and there is no DateTime.Convert method in .NET Framework, I couldn't any comment.
Now say we have two equivalent dates one in the source culture above
(2015.12.9) and the other in the current culture (9/12/2015). Then if
we attempt to parse this two dates the month will be 12 and in the
second will be 9, so we have an inconsistency.
Again.. You don't have DateTime's. You have strings. And those formatted strings can't belong on any culture. Sure all cultures might parse or generate different string representations with the same format format a format does not belong any culture.
I assume you have 2 different string which different formatted and you wanna parse the input no matter which one it comes. In such a case, you can use DateTime.TryParseExact overload that takes string array for all possible formats as a parameter. Then generate it's string representation with MM/dd/yyy format and a culture that has / as a DateSeparator like InvariantCulture.
string s = "2015.12.9"; // or 9/12/2015
string[] formats = { "yyyy.MM.d", "d/MM/yyyy" };
DateTime dt;
if (DateTime.TryParseExact(s, formats, CultureInfo.InvariantCulture,
DateTimeStyles.None, out dt))
{
Console.WriteLine(dt.ToString("MM/dd/yyyy", CultureInfo.InvariantCulture));
}
The Simple and Best way to do it is Using .ToString() Method
See this code:
DateTime x =DateTime.Now;
To Convert This Just Write like This:
x.ToString("yyyyMMdd")//20151210
x.ToString("yyyy/MM/dd)//2015/12/10
x.ToString("yyyy/MMM/dd)//2015/DEC/10 //Careful About M type should be capital for month .
Hope helpful

Converting the WhenChanged attribute (Generalized-Time) in LDAP to a DateTime in C#

I recently switch from using S.DS namespace (which uses ADSI) to the S.SD.Protocol namespace. The only problem is that ADSI handled the conversion of Generalized-Time to a DateTime for me. Now I'm getting back a value of "20070828085401.0Z" for the WhenChanged attribute. DateTime.Parse() will not convert this so is there another way?
The format you are getting is close to the round trip date time pattern ("o") and universal sortable round trip date time pattern ("u") standard date time format strings as described here.
One kludgy solution would be to massage the string you get to fit the pattern and then use the "o" or "u" standard format string with ParseExact.
A better way would be to construct a custom format string that matches the data you are already getting. In the "How Standard Format Strings Work" section of the standard date time format strings page you'll see the full custom formatting strings equivalent to "o" and "u". That should give you a good start.
EDIT: Add code
string format = "yyyyMMddHHmmss.f'Z'";
string target = "20070828085401.0Z";
DateTime d = DateTime.ParseExact(target, format, CultureInfo.InvariantCulture);
In the comments lixonn observes that, using the format string above, ParseExact will not successfully parse a time string like 199412160532-0500.
It also won't parse a number of other valid strings such as times without the trailing 'Zulu' indicator (20070828085401.0); times without a fractional part (20070828085401Z) and times that represent minutes and seconds as a fractional hour (2007082808.90028Z).
The format string can be made slightly more forgiving by replacing the hard-coded 'Z' with the K custom specifier which will accept 'Z', an offset like -0500, and nothing. Whether that additional flexibility is a good thing will depend on your application.
Note that even with the K specifier Lixonn's string won't be parsed successfully since it lacks a fractional part to match the .f component of the format string.
You'll have to use DateTime.ParseExact() specifying the exact format.
You might have to play with the format a little bit but it would be something like this.
DateTime result;
CultureInfo provider = CultureInfo.InvariantCulture;
string format="yyyyMMddhhmmss.0Z";
result = DateTime.ParseExact(dateString, format, provider);
You can use datetime's .strptime().
import datetime
# Since 0Z denotes UTC, you can get rid of it and apply the timezone
# later if you would like
time_string = "20070828085401.0Z".split('.')[0]
time_object = datetime.datetime.strptime(time_string, "%Y%m%d%H%M%S")
time_object should output as datetime.datetime(2007, 8, 28, 8, 54, 1). I believe it will be timezone naive, and equivalent to UTC time.
// WIN32 FILETIME is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).
// While the unix timestamp represents the seconds since January 1, 1970 (UTC).
private static long Win32FileTimeToUnixTimestamp(long fileTime)
{
//return fileTime / 10000L - 11644473600000L;
return DateTimeOffset.FromFileTime(fileTime).ToUnixTimeSeconds();
}
// The GeneralizedTime follows ASN.1 format, something like: 20190903130100.0Z and 20190903160100.0+0300
private static long GeneralizedTimeToUnixTimestamp(string generalizedTime)
{
var formats = new string[] { "yyyyMMddHHmmss.fZ", "yyyyMMddHHmmss.fzzz" };
return DateTimeOffset.ParseExact(generalizedTime, formats, System.Globalization.CultureInfo.InvariantCulture).ToUnixTimeSeconds();
}

DateTime.TryParseExact not working as expected

why does this not work?
DateTime.TryParseExact(text, "H", CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out value);
I want to parse an Time value only providing the hour part, but it throws a FormatException.
On the other hand, this works:
DateTime.TryParseExact(text, "HH", CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out value)
Anybody knows the cause?
Thanks.
Okay, I had to look this one up - it seems like it should be working, but it does not because the custom format string is not valid. A custom format string needs to be at least two characters wide - see:
http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx#UsingSingleSpecifiers
So, according to the documentation, you can fix this by using this code:
DateTime.TryParseExact(text, "%H", CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out value);
I guess this means that TryParseExact does not manage to fit the hour part into a single char, and that is understandable enough to me since hour will either be 12 or 24 hour based.
Without more specific information, the DatTime you're constructing can't determine AM / PM given the input. H would only allow a value of 1 - 12, leaving ambiguity. The HH provides the extra info.
The format specifier you pass to DateTime.TryParseExact needs to exactly match the string you are parsing.
E.g. passing "15:20" with format of "H" will fail, because there is other content in the string.
Either parse the whole string and use DateTime.Hour to just get the hour, or create a string with just the hour part and use Int32.Parse.

Categories