Why Does Timespan.Parseexact Not Work

Why does TimeSpan.ParseExact not work

From the documentation:

Any other unescaped character in a format string, including a
white-space character, is interpreted as a custom format specifier. In
most cases, the presence of any other unescaped character results in a
FormatException.

There are two ways to include a literal character in a format string:

  • Enclose it in single quotation marks (the literal string delimiter).

  • Precede it with a backslash ("\"), which is interpreted as an escape character. This means that, in C#, the format string must
    either be @-quoted, or the literal character must be preceded by an
    additional backslash.


The .NET Framework does not define a grammar for separators in time
intervals. This means that the separators between days and hours,
hours and minutes, minutes and seconds, and seconds and fractions of a
second must all be treated as character literals in a format string.

So, the solution is to specify the format string as

TimeSpan.ParseExact(tmp, "hh\\:mm\\:ss", CultureInfo.InvariantCulture)

TimeSpan.ParseExact does not recognize input format

I'm don't know why that is so or where it is documented, but three digit timespans seem not to be supported in TimeSpan.ParseExact. So you could workaround it by padding it with leading zeros:

string ts = "800";
var x = TimeSpan.ParseExact(ts.PadLeft(4, '0'), "hhmm", CultureInfo.InvariantCulture);

I guess that the reason why TimeSpan cannot parse this is related to the reason why DateTime.ParseExact cannot parse 7 digits with one or two digit month and without a delimiter.

Any workaround to TimeSpan.ParseExact with more than 59 seconds?

Ok guys, I ended up with this custom method to do the work.

It's not a method to execute many times in a row because of the huge performance issues it will have, but to parse the introduced data from the front end it's more than acceptable:

    /// <summary>
/// Given a time and a format it creates a <see cref="TimeSpan"/> ignoring the format digit limitations.
/// The format is not validated, so better ensure a correct one is provided ;)
/// </summary>
/// <param name="time"></param>
/// <param name="format"></param>
/// <param name="timeSpan"></param>
/// <returns></returns>
public static bool TryParseTime(string time, string format, out TimeSpan timeSpan)
{
// Regex to match the components of the time format (ss:fff matches ss and fff)
var formatRegex = new Regex(@"(?<=(?<!\\)(?:\\{2})*)(%?([fFsmhd])(\2*))");
var matches = formatRegex.Matches(format);
if (matches.Count > 0)
{
// We build a big regex to extract the values from time
string formatExtractionRegex = string.Empty;
int pivot = 0;
foreach (Match match in matches)
{
if (match.Success)
{
char c = match.Value.ToLower()[0];
formatExtractionRegex += $@"{format.Substring(pivot, match.Index - pivot)}(?<{c}>\d+)";

pivot = match.Index + match.Length;
}
}

var timeParts = new Regex(formatExtractionRegex).Match(time);
int d, h, m, s, f;
int.TryParse(timeParts.Groups["d"].ToString(), out d);
int.TryParse(timeParts.Groups["h"].ToString(), out h);
int.TryParse(timeParts.Groups["m"].ToString(), out m);
int.TryParse(timeParts.Groups["s"].ToString(), out s);
int.TryParse(timeParts.Groups["f"].ToString(), out f);
timeSpan = new TimeSpan(d, h, m, s, f);
return true;
}

timeSpan = default;
return false;
}

The method extracts the data from the time by building a big regex that replaces the digit type for the regex expression \d+, so we select entire digit groups when they are longer than what the format specifies.

If we provide a time 100:100:5000 and a format mm\:ss\:fff, the generated regex will be (?<m>\\d+)\\:(?<s>\\d+)\\:(?<f>\\d+).

Finally we parse the matched groups and we parse them to be given to the TimeSpan Constructor.

TimeSpan.TryParseExact not working

The problem is simply in the format string for the TimeSpan, you have specified "HH:mm:ss". The specifier HH (upper case) is not valid for timespan. You should use hh. Format strings are indeed case sensitive.

The colon character (:) also needs to be escaped, so use "hh\\:mm\\:ss", @"hh\:mm\:ss" or "hh':'mm':'ss". All three forms will have the same effect.

You can review a list of valid custom format strings for TimeSpan here. and the standard format strings for TimeSpan are here.

While HH is valid for DateTime and DateTimeOffset where it represents the 24 hour clock and lower case hh represents a 12 hour clock, For TimeSpan - the hours component is always based on 24 hours. You would think that the HH format would be the one chosen, for uniformity, but nope - it's hh.

TimeSpan.ParseExact giving error

I believe you need to parse the colons, basically. I would also suggest using the invariant culture instead of the current thread culture:

var ts = TimeSpan.ParseExact("0:0:4:410", @"h\:m\:s\:fff",
CultureInfo.InvariantCulture);

From the documentation:

The custom TimeSpan format specifiers do not include placeholder separator symbols, such as the symbols that separate days from hours, hours from minutes, or seconds from fractional seconds. Instead, these symbols must be included in the custom format string as string literals. For example, "dd.hh:mm" defines a period (.) as the separator between days and hours, and a colon (:) as the separator between hours and minutes.

I would also suggest using a format of h:mm:ss.fff instead - I believe this would be clearer than your current format. Note that you can use the format directly instead of your currently formatting approach:

const string TimeSpanFormat = @"h\:mm\:ss\.fff";

string text = ts.ToString(TimeSpanFormat, CultureInfo.InvariantCulture);
...
TimeSpan parsed = TimeSpan.ParseExact(text, TimeSpanFormat,
CultureInfo.InvariantCulture);

Can't Parse TimeSpan with commas for separation

You only need to escape out the , parts of the string (as the documentation states), which is achieved by surrounding them with single quotes ('):

var tmp = TimeSpan.TryParseExact("1, 2, 3, 4, 5", "d', 'h', 'm', 's', 'f", null, out ts);

Example fiddle: https://dotnetfiddle.net/M7GOWR

TimeSpan.ParseExact returns System.FormatException

According to ParseExact definition, the syntax is:

input format, etc

The System.FormatException exception message is about the input parameter, the first parameter from the list that is in the function's prototype. I would double check the value of the input parameter from your code.

+/- sign in TimeSpan's ParseExact()?

You could check if it starts with -, then apply the appropriate format string:

string[] timespans = { "-0530", "+0100" };
foreach (string timespan in timespans)
{
bool isNegative = timespan.StartsWith("-");
string format = isNegative ? "\\-hhmm" : "\\+hhmm";
TimeSpanStyles tss = isNegative ? TimeSpanStyles.AssumeNegative : TimeSpanStyles.None;
TimeSpan ts;
if (TimeSpan.TryParseExact(timespan, format, null, tss, out ts))
{
Console.WriteLine("{0} successfully parsed to: {1}", timespan, ts);
}
else
{
Console.WriteLine("Could not be parsed: {0}", timespan);
}
}

Note that i use TimeSpanStyles.AssumeNegative in TryParseExact, otherwise the timespans would be always positive even if they are prepended with a minus.

TimeSpan.ParseExact() returns false for apparently valid format

Use @"hh\:mm\:ss" for your format.

TimeSpan.ParseExact with ms

First, you are using . as a separator, but your string uses :.

Second, that is a quite weird representation of seconds (which is a 60 based number) and milliseconds (which is a 100-based one), so you more likely have:

string time = "12:25:11.97" // remember the quotes

Which should be parsed with:

TimeSpan t = TimeSpan.ParseExact(time, "hh':'mm':'ss.ff", CultureInfo.InvariantCulture);

If you indeed have 12:25:1197 then you can use hh':'mm':'ssff, but that's indeed weird

Btw, if that's two digits for what you call ms, then that's hundreths of seconds, not milliseconds (which woulf be three digits)



Related Topics



Leave a reply



Submit