DateTime.TryParseExact single second - c#

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.

Related

Short Time with DateTime.ParseExact

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);

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();
}

C# Regex, formatting a time

I'm making a timecard program, where the user enters the start time, end time, and project name. They can click "Add" and it adds the entered details to a listbox.
I don't allow the data to be added unless the start time and end time are formatted like this: 08:14am It gets annoying, though, having to enter it exactly like that each time, so I decided to, via regexes, have it automatically format. So if you enter 8:14, it will change it to 08:14am. How can I accomplish this via Regex.Replace()?
If you have any other methods, don't hesitate to list them.
Edit 1: I also have other replacements in mind; for example 814 goes to 08:14am, and 8 goes to 08:00am
Edit 2: So Now I'm using this code:
string[] formats = { "h:mm", "h", "hh", "hmm", "hhmm", "h:mmtt", "htt", "hhtt" };
DateTime dt = DateTime.ParseExact(t.Text, formats, new CultureInfo("en-US"), DateTimeStyles.None);
t.Text = dt.ToString("hh:mmtt").ToLower();
And it replaces some things, like h:mm but not others like h.
Have you looked at DateTime.Parse? It accepts "08:14am", "8:14"[1] and other variants, and returns a DateTime set to 8:14 am on today's date.
[1] In the UK culture at least -- consider providing a CultureInfo parameter depending on whether you want to pay attention to the user's local format preferences, or adopt a fixed format.
EDIT I also have other replacements in mind; for example 814 goes to 08:14am, and 8 goes to 08:00am
Take a look at DateTime.ParseExact: you can provide an array of valid time formats (such as "hh:mm", "hmm", "h" etc.)
Coming at it from a completely different direction, how about using a mask on the input?
For example, there are a number of JavaScript and jQuery tools available to do this.
Using this approach retains some user control over the input, and lets the user see their input.
have you considered using a DateTimePicker? Making the user write the time looks like more of a hassle.
See e.g. http://msdn.microsoft.com/en-us/library/system.windows.forms.datetimepicker.aspx
I think regex are overkill for this. Why not simply append the "am" at the end, in case nto already added is a 2 line code in case you are working with strings in the method.
I would recommend against using regular expressions if possible (as others have said, it seems like overkill) and try using datetime formats. See here: http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx
The "HH" format tag will format the hour as a 24-hour time from 00 to 23. Traditionally, 12 hour times are not zero-padded to the left, but 24 hour times are (at least as often as I see them), probably to avoid confusion between 1800 and 0800 . Alternatively, if you don't want 24 hour times, you could probably just left-pad the hour part of the string with a zero to up to 2 digits.
EDIT:
Based on your new requirements, I'd say write a simple parser to let users input "814" meaning "08:14 AM" and make use of the time-formatting functionality for display.

How to create a .NET DateTime from ISO 8601 format

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.

.NET: Why is TryParseExact failing on Hmm and Hmmss?

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.

Categories