I'm trying out the DateTime.TryParseExact method, and I have come over a case that I just don't get. I have some formats and some subjects to parse that each should match one of the formats perfectly:
var formats = new[]
{
"%H",
"HH",
"Hmm",
"HHmm",
"Hmmss",
"HHmmss",
};
var subjects = new[]
{
"1",
"12",
"123",
"1234",
"12345",
"123456",
};
I then try to parse them all and print out the results:
foreach(var subject in subjects)
{
DateTime result;
DateTime.TryParseExact(subject, formats,
CultureInfo.InvariantCulture,
DateTimeStyles.NoCurrentDateDefault,
out result);
Console.WriteLine("{0,-6} : {1}",
subject,
result.ToString("T", CultureInfo.InvariantCulture));
}
I get the following:
1 : 01:00:00
12 : 12:00:00
123 : 00:00:00
1234 : 12:34:00
12345 : 00:00:00
123456 : 12:34:56
And to my question... why is it failing on 123 and 12345? Shouldn't those have become 01:23:00 and 01:23:45? What am I missing here? And how can I get it to work as I would expect it to?
Update: So, seems like we might have figured out why this is failing sort of. Seems like the H is actually grabbing two digits and then leaving just one for the mm, which would then fail. But, does anyone have a good idea on how I can change this code so that I would get the result I am looking for?
Another update: Think I've found a reasonable solution now. Added it as an answer. Will accept it in 2 days unless someone else come up with an even better one. Thanks for the help!
Ok, so I think I have figured this all out now thanks to more reading, experimenting and the other helpful answers here. What's happening is that H, m and s actually grabs two digits when they can, even if there won't be enough digits for the rest of the format. So for example with the format Hmm and the digits 123, H would grab 12 and there would only be a 3 left. And mm requires two digits, so it fails. Tadaa.
So, my solution is currently to instead use just the following three formats:
var formats = new[]
{
"%H",
"Hm",
"Hms",
};
With the rest of the code from my question staying the same, I will then get this as a result:
1 : 01:00:00
12 : 12:00:00
123 : 12:03:00
1234 : 12:34:00
12345 : 12:34:05
123456 : 12:34:56
Which I think should be both reasonable and acceptable :)
0123
012345
I'm guessing it looks for a length of 2/4/6 when it finds a string of numbers like that. Is 123 supposed to be AM or PM? 0123 isn't ambiguous like that.
If you do not use date or time
separators in a custom format pattern,
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 pattern, specify the wider
form, "HH", instead of the narrower
form, "H"
cite:
http://msdn.microsoft.com/en-us/library/ms131044.aspx
As others have pointed out H is ambiguous because it implies a 10 hour day, where as HH is 12
To quote from MSDN's Using Single Custom Format Specifiers:
A custom date and time format string consists of two or more characters. For example, if the format string consists only of the specifier h, the format string is interpreted as a standard date and time format specifier. However, in this particular case, an exception is thrown because there is no h standard date and time format specifier.
To use a single custom date and time format specifier, include a space before or after the date and time specifier, or include a percent (%) format specifier before the single custom date and time specifier. For example, the format strings "h " and "%h" are interpreted as custom date and time format strings that display the hour represented by the current date and time value. Note that, if a space is used, it appears as a literal character in the result string.
So, should that have been % H in the first element in the formats array?
Hope this helps,
Best regards,
Tom.
I could be wrong, but I suspect it may have to do with the ambiguity inherent in the "H" part of your format string -- i.e., given the string "123", you could be dealing with hour "1" (01:00) or hour "12" (12:00); and since TryParseExact doesn't know which is correct, it returns false.
As for why the method does not supply a "best guess": the documentation is not on your side on this one, I'm afraid. From the MSDN documentation on DateTime.TryParse (emphasis mine):
When this method returns, contains the
DateTime value equivalent to the date
and time contained in s, if the
conversion succeeded, or
DateTime.MinValue if the
conversion failed. The conversion
fails if either the s or format
parameter is null, is an empty string, or does not
contain a date and time that
correspond to the pattern specified in
format. This parameter is passed
uninitialized.
"123" and "12345" seem to be ambiguous with respect to the TryParseExact method. "12345" could be either 12:34:50 or 01:23:45 for instance. Just a guess though.
Related
I noticed quite an interesting error when parsing some times.
DateTime fails to parse 24:00:00. Under some Googling and Stacking, I found out that DateTime only recognizes 00 - 23 (what the?????), so if your input is 24:00:00, you're out of luck. You would think someone would put in a condition to equate 24:00:00 as 00:00:00 (the midnight), but not yet..
My question is, how do I allow DateTime to allow me to parse 24:00:00?
Unfortunately I cannot to use NodaTime under specification reasons (sorry Jon. I love your library though).
Experimentation below:
An input of 2014-03-18 24:00:00 would present the following error. Expected.
An input of 2014-03-18 23:59:59 would successfully parse. Expected.
An input of 2014-03-19 00:00:00` would successfully parse. Expected.
There is no "24th hour" support in the DateTime class.
The hour (HH/H, 24-hour clock) must be 0-23, inclusive. This is why 00:00:00 is valid, but 24:00:00 is not.
Change 24:00:00 to 00:00:00 (before parsing) and, if needed, advance the day as appropriate (after parsing).
The following will work on times in the provided format (but only up to the 24th hour) although it doesn't account for an arbitrary format. Supporting different format strings only adds additional complications.
DateTime ParseWithTwentyFourthHourToNextDay (string input) {
var wrapped = Regex.Replace(input, #"24:(\d\d:\d\d)$", "00:$1");
var res = DateTime.ParseExact(wrapped, "yyyy-MM-dd HH:mm:ss", null);
return wrapped != input
? res.AddDays(1)
: res;
}
24:00:00 doesn't exist. It is 00:00:00 - 23:59:59
Why would you like to parse 24:00:00 as a valid time expression when it would be like saying 09:05:60. The roof for time is 23:59:59.99999999999 and after that, it turns over to 00:00:00.
Before parsing, do a simple search and replace - replace '24:00:00' with '00:00:00' and then parse as usual.
Convert to Minute.
if t.TotalMinutes < 0
double _24h = 0;
_24h = 1440 + t.TotalMinutes;
TimeSpan t = TimeSpan.FromMinutes(_24h);
I have time results from races in many different formats (more or less any combination of hour, minute, second and millisecond):
01:22:51.15
22:15.12
15.0
12:21
...
I parse them with a list of valid formats and so far all the formats work except one: The single second format, e.g. "5".
For some reason the following list (containing "s" as the 1st item) does not help me to parse the string "5" into a DateTime, "25" works fine though.
var timeString = "5";
var timeFormats = new[] { "s", "s.f", "s.ff", "ss", "ss.f", "ss.ff", "m:ss", "m:ss.f", "m:ss.ff", "mm:ss", "mm:ss.f", "mm:ss.ff", "H:mm:ss", "H:mm:ss.f", "H:mm:ss.ff", "HH:mm:ss", "HH:mm:ss.f", "HH:mm:ss.ff" };
DateTime timeValue;
DateTime.TryParseExact(timeString, timeFormats, null, DateTimeStyles.None, out timeValue);
Any idea why this does not work as I expect it to? Or is there a cleaner way to cover all these time formats without adding another nuget package...?
Cheers!
Because there is only one character in the format, it is being treated as the Sortable standard format, rather than as a custom format with seconds.
To change this, prefix the custom format with a % character, as in "%s". See "Using single custom format specifiers".
Also, recognize that if these are actual elapsed times of races, TimeSpan is a more appropriate data structure than DateTime. Be careful when switching, as not all formatting tokens are identical between the two objects. In particular, TimeSpan uses h for hours, not H. See Custom TimeSpan Format Strings.
I’m trying to parse a time. I’ve seen this question asked/answered here many times but not for this specific scenario. Here’s my code:
var time1 = DateTime.ParseExact("919", "Hmm", CultureInfo.InvariantCulture);
also
var time2 = DateTime.ParseExact("919", "Hmm", null);
both of these throw the same
"String was not recognized as a valid DateTime"
What I want is 9:19 AM.
For further info I also need to parse “1305” as 1:05 PM, this is working fine.
It seems to me I’m using the correct format. What am I overlooking?
I'm not sure there is any format that can handle this. The problem is that "H" can be either one digit or two, so if there are two digits available, it will grab both - in this case parsing it as hour 91, which is clearly invalid.
Ideally, you'd change the format to HHmm - zero-padding the value where appropriate - so "0919" would parse fine. Alternatively, use a colon in the format, to distinguish between the hours and the minutes. I don't believe there's any way of making DateTime parse a value of "919" as you want it to... so you'll need to adjust the string somehow before parsing it. (We don't have enough context to recommend a particular way of doing that.)
Yes, your format is right but since H specifier might be 2 character, ParseExact method try to parse 91 as an hour, which is an invalid hour, that's why you get FormatException in both case.
I connected to microsoft team about this situation 4 months ago. Take a look;
DateTime conversion from string C#
They suggest to use 2 digit form in your string or insert a date separator between them.
var time1 = DateTime.ParseExact("0919", "Hmm", CultureInfo.InvariantCulture);
or
var time1 = DateTime.ParseExact("9:19", "H:mm", CultureInfo.InvariantCulture);
You cant exclude the 0 prefix to the hour. This works
var time1 = DateTime.ParseExact("0919", "Hmm", CultureInfo.InvariantCulture);
Perhaps you want to just prefix 3-character times with a leading zero before parsing.
Much appreciated for all the answers. I don’t have control of the text being created so the simplest solution for me seemed to be prefixing a zero as opposed to adding a colon in the middle.
var text = "919";
var time = DateTime.ParseExact(text.PadLeft(4, '0'), "Hmm", null);
I have an asp.net application in which i have these three dates:
now = 08-10-13 15:56:19
cloture1 = 01-10-13 00:00:00
cloture2= 01-01-50 00:00:00
The format of dates is DD-MM-YY HH:MM:SS. the problem is that the function DateTime.Compare() gives me the same result ie
DateTime.Compare(now,cloture1) > 0 and DateTime.Compare(now,cloture2) > 0.
So what is the reasons of this problem? How can i fix my snippet?
The problem is your program most probably interpreting cloture2 as 1950, not 2050.
Because you have not posted the code in which you set cloture2, I cannot offer a concrete solution, but the best I can offer is that you use 01-10-2050 explicitly in your code.
From The "yy" Custom Format Specifier
In a parsing operation, a two-digit year that is parsed using the "yy"
custom format specifier is interpreted based on the
Calendar.TwoDigitYearMax property of the format provider's current
calendar.
In a parsing operation, a two-digit year that is parsed using the "yy"
custom format specifier is interpreted based on the
Calendar.TwoDigitYearMax property of the format provider's current
calendar. The following example parses the string representation of a
date that has a two-digit year by using the default Gregorian calendar
of the en-US culture.
From GregorianCalendar.TwoDigitYearMax
This property allows a 2-digit year to be properly translated to a
4-digit year. For example, if this property is set to 2029, the
100-year range is from 1930 to 2029. Therefore, a 2-digit value of 30
is interpreted as 1930, while a 2-digit value of 29 is interpreted as
2029.
Your application should set this value to 99 to indicate that 2-digit
years are to be taken literally. For example, if this property is set
to 99, the 100-year range is from 0 (not a valid value for most
calendars) to 99. Therefore, a 2-digit value of 30 is interpreted as
30.
Even when you decompile GregorianCalendar.TwoDigitYearMax property, you can see yourself;
public override int TwoDigitYearMax
{
get
{
if (this.twoDigitYearMax == -1)
this.twoDigitYearMax = Calendar.GetSystemTwoDigitYearSetting(this.ID, 2029);
return this.twoDigitYearMax;
}
Boluc's answer is completely right. I want to point also your format part.
You can't format two digit year with YYYY format. You need to use yy format which allows two digit formatting.
DateTime dt = DateTime.ParseExact("01-01-50 00:00:00", "dd-MM-yy HH:mm:ss", CultureInfo.InvariantCulture);
Prints
01.01.1950 00:00:00
Here a DEMO.
Check out for more informations from Custom Date and Time Format Strings
If now is later than cloture1 then the returned value will be 1 (or Greater than Zero).
The code you have supplied along with the example dates seems to work fine in reference to the MSDN article for DateTime.Compare method.
Please check the following link for more information on the DateTime.Compare method:
http://msdn.microsoft.com/en-us/library/system.datetime.compare.aspx
If you believe that your code is still incorrect please elaborate on your question.
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.