Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeConverter.IsValid() uses current thread culture but TypeConverter.ConvertFrom() doesn't?

It seems that TypeConverter.IsValid() uses the current thread culture but TypeConverter.ConvertFrom() doesn't.

This makes it pretty useless to use TypeConverter.IsValid() with the DateTime type unless you are in the invariant culture. Indeed, it seems to be a bug.

Does anyone know how to make TypeConverter.IsValid() use the current culture?

The following code demonstrates the problem.

It uses two strings, one in DD/MM/YYYY format and one in MM/DD/YYYY format.

The first part of the test is done in the Invariant Culture. It demonstrates that TypeConverter.IsValid() returns true for the MM/DD/YYYY string and that you can use TypeConverter.ConvertFrom() to convert that string to a DateTime.

The first part also demonstrates that TypeConverter.IsValid() returns false for the DD/MM/YYYY string.

For the second part, I change the current culture "en-GB" which uses "DD/MM/YYYY" dates.

I would now expect the IsValid() results to be reversed, but it returns the same as before for the two strings. So it says that the MM/DD/YYYY string is valid.

However - and this is the important bit - if you try to use TypeConverter.ConvertFrom() to actually convert the string which TypeConverter.IsValid() said was ok, you will get an exception.

Is there a way to work around this?

using System;
using System.ComponentModel;
using System.Globalization;
using System.Threading;

namespace Demo
{
    class Program
    {
        void Run()
        {
            // Start off with the US culture, which has MM/DD/YYYY date format.

            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

            var dateConverter = TypeDescriptor.GetConverter(typeof(DateTime));
            var goodDateString = "07/19/1961";
            var badDateString  = "19/07/1961";

            test(dateConverter, goodDateString, "DateTime"); // Says it's good.
            test(dateConverter, badDateString, "DateTime");  // Says it's bad.

            var dateTimeValue = (DateTime) dateConverter.ConvertFrom(goodDateString);

            Console.WriteLine("dateTimeValue = " + dateTimeValue);
            Console.WriteLine();

            // Now lets change the current culture to the UK, which has DD/MM/YYYY date format.

            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
            dateConverter = TypeDescriptor.GetConverter(typeof(DateTime));

            test(dateConverter, goodDateString, "DateTime"); // Still says it's good.
            test(dateConverter, badDateString, "DateTime");  // Still says it's bad.

            // TypeConverter.IsValid(badDateString) returns false, so we shouldn't be able to convert it.
            // Well, we can, like so:

            dateTimeValue = (DateTime)dateConverter.ConvertFrom(badDateString); // Shouldn't work according to "IsValid()"
            Console.WriteLine("dateTimeValue (bad) = " + dateTimeValue);        // But this is printed ok.

            // TypeConverter.IsValid(goodDateString) returns true, so we can convert it right?
            // Well, no. This now throws an exception, even though "IsValid()" returned true for the same string.

            dateTimeValue = (DateTime)dateConverter.ConvertFrom(goodDateString); // This throws an exception.
        }

        void test(TypeConverter converter, string text, string type)
        {
            if (converter.IsValid(text))
                Console.WriteLine("\"" + text + "\" IS a valid " + type);
            else
                Console.WriteLine("\"" + text + "\" is NOT a valid " + type);
        }

        static void Main()
        {
            new Program().Run();
        }
    }
}
like image 495
Matthew Watson Avatar asked May 30 '13 13:05

Matthew Watson


1 Answers

It is a flaw in TypeConverter.IsValid(). A type converter is culture-aware, its virtual ConvertFrom() method takes a CultureInfo argument. You are using the non-virtual overload that doesn't take a CultureInfo so you'll get the conversion selected by CultureInfo.CurrentCulture.

TypeConverter.IsValid() does not have an overload that accepts a CultureInfo. And flubs the CultureInfo it passes to the TypeConverter.ConvertFrom() method, it passes CultureInfo.InvariantCulture.

Hard to come up with a justification for this behavior. Probably a bug that just got noticed too late to do anything about. It certainly isn't fixable anymore.

The workaround is to do what IsValid() does but by specifying the correct culture. First call CanConvertFrom(), then call ConvertFrom() in a try/catch block.

like image 193
Hans Passant Avatar answered Nov 14 '22 22:11

Hans Passant