Why has this particular TimeSpan format string stopped working in .NET 4?
Asked Answered
B

5

5

Consider this code (prestuffed with an example):

DateTime dt1 = DateTime.Parse("7/30/2010 9:33:29.1234567 AM");
DateTime dt2 = DateTime.Parse("6/30/2010 9:33:00.7654321 AM");

TimeSpan ts = dt1 - dt2;

Console.WriteLine(string.Format( "{0:d.hh:mm:ss.ff}", ts ));

This is representative of a piece of code that I've had working since .NET 1.1 at least.

It worked fine in 1.1 through 3.5 with the following output (for these dummied up inputs):

30.00:00:28.3580246

But now I'm seeing that it dies in .NET 4 with the error message:

Input string was not in a correct format.

So it's as if .NET 4 has suddenly decided it doesn't like this format for time differences. Changing the line to, say

Console.WriteLine(string.Format( "{0}", ts.ToString("d.hh:mm:ss.ff") ));

has the same effect.

Now the thing I've noticed is that if I just do the default .ToString() I get the same output. I believe the thought process was that this was an insurance policy against the default format changing in a future version. But now it doesn't look like that's even an option.

Does anyone know why this changed and if I'm doing something wrong or if there's a best practices way to do what I'm trying to accomplish?

Besse answered 30/7, 2010 at 15:38 Comment(0)
T
6

There is a configuration switch to restore the old behaviour of TimeSpan.

Tempa answered 30/7, 2010 at 15:53 Comment(2)
So basically that format string's been doing nothing all this time. Outstanding. Thanks.Besse
@Schnapple Exactly. In .NET 3.5 and earlier, the TimeSpan struct wasn't IFormattable. When string.Format and related methods see something like {0:something} and the argument supplied isn't IFormattable, they have nowhere to put that format string, and they just discard it. That's what happened in your code until .NET 4.0 arrived.Alienee
C
3

An alternative to the configuration switch is a format change that is compatible with previous versions.

Console.WriteLine(string.Format( "{0:hh\\:mm\\:ss.ff}", ts )); 

This solution is detail here.

Catholicism answered 30/8, 2010 at 12:40 Comment(0)
T
1

In fact, the composite format string you've been using in your code did not have any effect at all, because TimeSpan does not support custom format strings (.NET < 4.0).

i.e. Your TimeSpan would have always been formatted like 30.00:00:28.3580246 regardless of format string.

From MSDN:

In previous versions of the .NET Framework, the TimeSpan structure did not implement IFormattable and did not support format strings.

However, many developers mistakenly assumed that TimeSpan did support a set of format strings and used them in composite formatting operations with methods such as String.Format. Ordinarily, if a type implements IFormattable and supports format strings, calls to formatting methods with unsupported format strings usually throw a FormatException. However, because TimeSpan did not implement IFormattable, the runtime ignored the format string and instead called the TimeSpan.ToString() method. This means that, although the format strings had no effect on the formatting operation, their presence did not result in a FormatException.

Thew answered 25/8, 2014 at 17:31 Comment(0)
P
1

As indicated by Mitch Wheat and Saeb Amini in their answers, TimeSpan does not implement IFormattable prior to .NET 4.0. Consequently, format strings have no effect on the TimeSpan.ToString() output since they were ignored.

However, because TimeSpan did not implement IFormattable, the runtime ignored the format string and instead called the TimeSpan.ToString method. This means that, although the format strings had no effect on the formatting operation, their presence did not result in a FormatException.

Source

That said, fi you desire to format a TimeSpan value across all versions of the .NET framework, it is much better to convert the TimeSpan value to DateTime and then format that result as shown below:

DateTime dt1 = DateTime.Parse("7/30/2010 9:33:29.1234567 AM");
DateTime dt2 = DateTime.Parse("6/30/2010 9:33:00.7654321 AM");

TimeSpan ts = dt1 - dt2;

Console.WriteLine(String.Format("{0:d.hh:mm:ss.ff}", new DateTime(ts.Ticks))) 
// prints 30.00:00:28.36
Put answered 2/1, 2015 at 5:3 Comment(0)
T
0

I've pasted your piece of code and it seems to be a culture problem :

with .NET 2 an FormatException is thrown too

If I specified the us culture (culture is fr-FR by default) , the code works :

DateTime dt1 = DateTime.Parse("7/30/2010 9:33:29.1234567 AM", CultureInfo.GetCultureInfo("en-US"));

You can also specified an Invariant culture to ignore the culture

DateTime dt1 = DateTime.Parse("7/30/2010 9:33:29.1234567 AM", CultureInfo.InvariantCulture);
Tennies answered 30/7, 2010 at 15:47 Comment(1)
I think you're on to something with the culture but, but it's not the DateTime lines that are giving me fits, it's the TimeSpan format string. Even if I specify the culture in the TimeSpan ToString and both of the DateTime.Parse methods, I'm still getting the same.Besse

© 2022 - 2024 — McMap. All rights reserved.