I am trying to parse date-strings to DateTime objects with the following format:
Tue, 30 Oct 2012 09:51:20 +0000
What I have tried so far is many different variants with DateTime.ParseExact().
I have tried:
DateTime.ParseExact("Mon, 29 Oct 2012 12:13:51 +0000",
"ddd, dd MM yyyy hh':'mm':'ss zzz",
CultureInfo.InvariantCulture);
With thousands different formats as second parameter, using null instead of InvarantCulture as third parameter etc etc. I can't get it to work. How should I parse a string like this?
Many thanks.
How about
var s = "Tue, 30 Oct 2012 09:51:20 +0000";
DateTime.ParseExact(s, "ddd, dd MMM yyyy hh:mm:ss zzz", CultureInfo.InvariantCulture)
The month (Oct) is actually MMM, not MM, and the time (09:51:20) should be hh:mm:ss instead of hh':'mm':'ss.
The correct parsing is
DateTime.ParseExact("Mon, 29 Oct 2012 12:13:51 +0000", "ddd, dd MMM yyyy HH:mm:ss K", CultureInfo.InvariantCulture);
Take a look here
Related
I have an example date / time string that I need to convert to datetimeoffset.
There is one huge inconsistency - if the day of the month < 10 then there is a double space between Month and Day, otherwise just a single space.
For example: 'Tue Dec 4 22:39:38 UTC 2018' and 'Tue Dec 14 22:39:38 UTC 2018'
I currently parse it using DateTimeOffset.ParseExact(dateTime, "ddd MMM dd HH:mm:ss UTC yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal) which fails for dates where the day is < 10 with the error:
FormatException: String 'Tue Dec 4 22:52:42 UTC 2018' was not recognized as a valid DateTime.
I know I can search and replace double space character with a single space, but is there a more elegant way to achieve this using the format string?
This question seemed to have a few comments (including my own erroneous one about using AllowLeadingWhite (I had meant AllowInnerWhite).
However, just using AllowInnerWhite with the existing format string still produces an error:
Console.WriteLine(DateTimeOffset.ParseExact("Tue Dec 4 22:39:38 UTC 2018", "ddd MMM dd HH:mm:ss UTC yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AllowInnerWhite));
Produces:
FormatException: String was not recognized as a valid DateTime.
However, looking at this from a different angle, why not change the date format itself to permit single-digit dates. Use "ddd MMM d HH:mm:ss UTC yyyy" (with a single 'd' for the actual date instead of 'dd'):
Console.WriteLine(DateTimeOffset.ParseExact("Tue Dec 4 22:39:38 UTC 2018", "ddd MMM d HH:mm:ss UTC yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AllowInnerWhite));
Console.WriteLine(DateTimeOffset.ParseExact("Tue Dec 11 22:39:38 UTC 2018", "ddd MMM d HH:mm:ss UTC yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AllowInnerWhite));
Console.WriteLine(DateTimeOffset.ParseExact("Fri Dec 14 22:39:38 UTC 2018", "ddd MMM d HH:mm:ss UTC yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AllowInnerWhite));
Note that your example data of 'Tue Dec 14 22:39:38 UTC 2018' will fail because Dec 14th 2018 is a Friday, not a Tuesday.
That's what the DateTimeStyles.Allow* flags are for: They instruct the parser to ignore whitespaces in the date string.
In your case the string starts with the abbreviated weekday name, so the flag minimally sufficient to ignore the additional space in case of a one-digit day number is
DateTimeStyles.AllowInnerWhite
This and related flags are documented here: https://learn.microsoft.com/en-us/dotnet/api/system.globalization.datetimestyles?view=netframework-4.7.2
From third party I'm getting this string that I'd like to convert to DateTime:
"13 NOV 2018 16:08:52:000:000:000"
I've tried those options but FormatException thrown:
System.DateTime.ParseExact("12 NOV 2018 16:08:52:000:000:000",
"dd MMM yyyy HH:mm:ss:fff:ffffff:fffffffff",
CultureInfo.InvariantCulture)
And the same exception when I've used this:
System.DateTime.ParseExact("12 NOV 2018 16:08:52:000:000:000",
"dd MMM yyyy HH:mm:ss:fff", CultureInfo.InvariantCulture)
Is there a clean way to parse it as is or, I need to use a dirty way such string.Split(new []{':'})?
Note:
For my business needs, I don't need the part of micro/nano seconds.
since this parsing operation occurs many times in a second, the efficiency is a key factor.
Update
I chose the #Tim's suggestion to do string manipulation:
string date = "12 NOV 2018 16:08:52:000:000:000";
date = date.Remove(date.Remove(date.LastIndexOf(':')).LastIndexOf(':'));
DateTime result = System.DateTime.ParseExact(date, "dd MMM yyyy HH:mm:ss:fff", System.Globalization.CultureInfo.InvariantCulture);
The reasons:
I can't promise the structure of the date string, since it comes from third party. So it will be very dangerous to use the format pattern dd MMM yyyy HH:mm:ss:fff:fff:fff
As #Jeroen Mostert wrote the usage of string splitting is not a key factor when you use DateTime.ParseExact()
Also, if you really think efficiency is so important, don't assume any single call to DateTime.ParseExact will be "fast", or that any string splitting you do will be a bottleneck.
Well, isn't it simply this?
System.DateTime.ParseExact("12 NOV 2018 16:08:52:000:000:000",
"dd MMM yyyy HH:mm:ss:fff:fff:fff",
System.Globalization.CultureInfo.InvariantCulture)
This works if all groups of f have the same values, so for example all 0 or 123.
Tbh, i don't understand why you think that fff:ffffff:fffffffff matches 000:000:000
For my business needs, I don't need the part of micro/nano seconds.
You could make your second format working by cutting off the micro/nano part:
string date = "12 NOV 2018 16:08:52:000:000:000";
date = date.Remove(date.Remove(date.LastIndexOf(':')).LastIndexOf(':'));
DateTime result = System.DateTime.ParseExact(date, "dd MMM yyyy HH:mm:ss:fff", System.Globalization.CultureInfo.InvariantCulture);
Of course this assumes that there are always at least two colons. It's obvious that you need a try-catch to log invalid values and to continue with the next one.
Not an exact answer
System.DateTime.ParseExact("25 NOV 2018 16:08:52:000:000:000",
"dd MMM yyyy HH:mm:ss:fff:fff:fff", CultureInfo.InvariantCulture);
Things to consider: count of input string (000) should match with format(fff),
000 = fff good
00 = fff not good
000 = ff not good
You may need to manipulate input date string before trying to parse.
See this for more information regarding use of format specifier (fff)
https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings
.NET Fiddle:
https://dotnetfiddle.net/5UxCkd
I think that having same format for :fff is a key factor here
System.DateTime.ParseExact("12 NOV 2018 16:08:52:000:000:000","dd MMM yyyy H:mm:ss:fff:fff:fff", CultureInfo.InvariantCulture) seems to work
https://dotnetfiddle.net/ZAtchu example code
How can I convert Tue, 01 Nov 2016 02:00 PM EET datetime string to DateTime in C#? What is a good practice to do it?
Use DateTime.TryParseExact with a format string that represents a generic datetime.
If you can have multiple formats then use the DateTime.TryParseExact overload that takes an array of formats.
You can find all the format strings here:
Custom Date and Time Format Strings
For example, "Tue" is represented by "ddd", "Nov" by "MMM" etc.
NOTE: The string formats are case sensitive so while "M" represents the month number, "m" represents the minute number. Getting them mixed up will cause the parse to fail.
By replacing timezone abbreviation with zone offset you can convert using DateTime.ParseExact
string date = "Tue, 01 Nov 2016 02:00 PM EET";
DateTime dt = DateTime.ParseExact(date.Replace("EET", "+2"), "ddd, dd MMM yyyy hh:mm tt z", CultureInfo.InvariantCulture);
and if you want more safer way by checking exception then you can using DateTime.TryParseExact method
Use DateTime.TryParseExact where the format string is built using this table.
Custom date and time formats does not recognize timezone abbrevations. You need to escape them as a string literal delimiter.
var dt = DateTime.ParseExact("Tue, 01 Nov 2016 02:00 PM EET",
"ddd, dd MMM yyyy hh:mm tt 'EET'",
CultureInfo.InvariantCulture);
dt.Dump();
looking at the format : "MMM d yyyy"
This is working
var t="Mar 2013 7";
DateTime dt=DateTime.Parse(t);
But
also this :"Mar 7 2013"
and this :"7 Mar 2013"
is working
looking at :
new DateTimeFormatInfo()
.GetAllDateTimePatterns()
.Select((i,n)=>n+" "+i)
.ToList()
.ForEach(f=>Console.WriteLine(f));
Which is All the standard patterns in which date and time values can be
formatted.
The result is :
0 MM/dd/yyyy
1 yyyy-MM-dd
2 dddd, dd MMMM yyyy
3 dddd, dd MMMM yyyy HH:mm
4 dddd, dd MMMM yyyy hh:mm tt
5 dddd, dd MMMM yyyy H:mm
6 dddd, dd MMMM yyyy h:mm tt
7 dddd, dd MMMM yyyy HH:mm:ss
8 MM/dd/yyyy HH:mm
9 MM/dd/yyyy hh:mm tt
10 MM/dd/yyyy H:mm
11 MM/dd/yyyy h:mm tt
12 yyyy-MM-dd HH:mm
13 yyyy-MM-dd hh:mm tt
14 yyyy-MM-dd H:mm
15 yyyy-MM-dd h:mm tt
16 MM/dd/yyyy HH:mm:ss
17 yyyy-MM-dd HH:mm:ss
18 MMMM dd
19 MMMM dd
20 yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK
21 yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK
22 ddd, dd MMM yyyy HH':'mm':'ss 'GMT'
23 ddd, dd MMM yyyy HH':'mm':'ss 'GMT'
24 yyyy'-'MM'-'dd'T'HH':'mm':'ss
25 HH:mm
26 hh:mm tt
27 H:mm
28 h:mm tt
29 HH:mm:ss
30 yyyy'-'MM'-'dd HH':'mm':'ss'Z'
31 dddd, dd MMMM yyyy HH:mm:ss
32 yyyy MMMM
33 yyyy MMMM
Question :
I dont see in the list the MMM d yyyy format. So how does it do it ? does it try all combinations ?
What about adding a time to format MMM d yyyy ? [Mar 3 2007 13:13:13] it also works but there is no specific format . so how does it do it?
I've used Reflector to look at this. It's really complicated with hundreds of lines of parsing code!
However, ultimately it winds up tokenising the input string and trying to categorise the tokens as day names, month names, years, day numbers etc.
In particular a function called internal TokenHashValue[] CreateTokenHashTable() has this sort of thing:
for (int i = 1; i <= 12; i++)
{
this.InsertHash(dtfiTokenHash, this.GetAbbreviatedMonthName(i), TokenType.MonthToken, i);
}
It uses this (which as you can see has all the abbreviated month names) to determine if a token is a month name. There's similar code for day names.
The parsing code also checks whether one of the numbers is greater than 2 digits long. If it is, it assumes it's a year. This means (and you can verify it) that you can have a 3 digit year and it will still parse it ok. But it gets even more complicated! It also checks if the number is greater than 12, and assumes it's a year if so.
If you put two numbers each less than or equal to 12, it still works, but it assumes the first one is a day and the second a month (for the UK culture - I bet it's different for other cultures).
This does of course allow it to parse ambiguous dates without warning.
Upshot is: NEVER PARSE A DATE LIKE THIS
Always ParseExact().
I'd guess that it is using the format specifiers and not their arrangement.
From the way the literal "Mar 2013 7" is written, i doubt they can be any ambiguity in its parsing.
Mar matches MMM exactly
2003 matches yyyy exactly
7 matches d exactly
I found a link to DateTime.Parse source code here:
http://typedescriptor.net/name/members/5B57671F27DBC0AEA0EB9825243834CF-System.DateTime.Parse(String,IFormatProvider,DateTimeStyles)
You can click the links to dig deeper into the private methods, but it gets complicated. But it looks like it does lexing and parsing much like a compiler. The string is broken up into parts (tokens) and it tries to identify if each is a year, a month, whatever.
Certainly there might be some ambiguity, e.g. maybe you meant dd-MM-yyyy but it parsed as MM-dd-yyyy. But that's why you can specify a culture-specific IFormatProvider.
there are 2 variable in my querystring:
&Start=Mon Apr 02 2012 00:00:00 GMT+0200&End=Thu Apr 26 2012 00:00:00 GMT+0200
when i try to get it
like this:
DateTime EndDate = Convert.ToDateTime(Request.QueryString["End"]);
or this:
DateTime date = DateTime.ParseExact(Request.QueryString["Start"], "dd/MM/yyyy", System.Globalization.CultureInfo.CurrentUICulture.DateTimeFormat);
i got the message :
The string was not recognized as a valid DateTime.
Anybody can help me?
PS: i use devexpress Component
<dx:ASPxDateEdit ID="ASPxDateEdit_Synthe_Fin" runat="server" Width="100px" ClientInstanceName="ASPxDateEdit_Synthe_Fin">
</dx:ASPxDateEdit>
<dx:ASPxButton ID="ASPxButton_Synthese" runat="server" Text="Synthese" AutoPostBack="False">
<ClientSideEvents Click="function (s, e) { e.processOnServer = false; window.open('Report/Syntehse.aspx?CientID='+ASPxComboBox_Client.GetValue()+'&Start='+ASPxDateEdit_Synth_Deb.GetValue()+'&End='+ASPxDateEdit_Synthe_Fin.GetValue());}" />
</dx:ASPxButton>
Thanks you in advance
why do you write "dd/MM/yyyy" ? it aint the format in the QS ...
it should be something like :
ddd MMM dd yyyy HH:mm:ss
--->Mon Apr 02 2012 00:00:00 GMT+0200
Convert will try different formats, but can fail, as you have seen.
When using ParseExact or TryParseExact you need to pass in a format string that directly corresponds to the string you are trying to parse.
You have use dd/MM/yyyy for Mon Apr 02 2012 00:00:00 GMT+0200. These do not correspond to each other.
Try ddd MMM dd yyyy HH:mm:ss G\MTK instead:
DateTime.ParseExact("Mon Apr 02 2012 00:00:00 GMT+0200",
"ddd MMM dd yyyy HH:mm:ss G\\MTK",
CultureInfo.InvariantCulture)
Update:
An additional issue is that the URL parameters are not URL encoded so this:
&Start=Mon Apr 02 2012 00:00:00 GMT+0200&End=Thu Apr 26 2012 00:00:00 GMT+0200
End up with the + characters being seen as spaces on the server side (since + also encodes a space on URLs).
You need to URL encode the date/time values before placing them on the URL.
DateTime.ParseExact(date, "ddd MMM dd yyyy HH:mm:ss \"GMT\"zzz", System.Globalization.CultureInfo.InvariantCulture) works on the format specified in the querystring.