简体   繁体   中英

TypeConverter DateTime Format issue

I am using 4.0 and I am trying to make my life easy by developing some helper methods for type conversions. Following method is working perfectly. It will convert from any string to other datatype.

System.ComponentModel;

public static T Convert<T>(string s)
{
      var typeConverter = TypeDescriptor.GetConverter(typeof(T));
      if (typeConverter != null && typeConverter.CanConvertFrom(typeof(string)))
      {
          return (T)typeConverter.ConvertFrom(s);
      }
      return default(T);
}

//calling this method...
var dateTime = MyConverter.Convert<DateTime>("13/07/2013");   // Date format "DD/mm/yyyy" 
// Working as expected... Taking "13" as Day, "07" as month

Now the following method is also in the same class MyConverter but it is not working well with DateTime:

public static bool CanConvertTo<T>(string s)
{
     var typeConverter = TypeDescriptor.GetConverter(typeof(T));

     if (typeConverter != null && typeConverter.CanConvertFrom(typeof(string)))
     {
         return typeConverter.IsValid(s);
     }
     else
         return false;
}

The problem with this method is, it is only accepting dateformat : "MM/dd/yyyy"

// This will give exception. It is taking "13" as month, "07" as Day
bool canConvert = MyConverter.CanConvertTo<DateTime>("13/07/2013");

EDIT As suggested by KeyboardP, I changed my method to :

public static bool CanConvertTo<T>(string s)
{
    TypeConverter typeConverter;
    if (typeof(T) == typeof(DateTime))
    {
         typeConverter = new DateTimeConverter();
    }
    else
    {
        typeConverter = TypeDescriptor.GetConverter(typeof(T));
    }

    if (typeConverter != null && typeConverter.CanConvertFrom(typeof(string)))
    {
        return typeConverter.IsValid(s);
    }
    else
        return false;
}

and tested. Test results are :

var date = MyConverter.Convert<DateTime>("13/07/2013");    //return perfect date
var canConvert = MyConverter.CanConvertTo<DateTime>("13/07/2013");  // returned false...

So no success with DateTimeConverter

Since DateTime can have many different format styles I'd just use DateTimeConverter than attempt to recreate it inside a generic method.

Edit

I did a bit of ILspying and this is what I've concluded (happy to be corrected on any point).

GetCoverter(typeof(DateTime)) will return a DateTimeConverter so calling CanConvertFrom is actually calling DateTimeConverter.CanConvertFrom . CanConvertFrom calls base.CanConvertFrom ( base being the parent TypeConverter class).

The base.CanConvertFrom method looks like this

public virtual bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
    return sourceType == typeof(InstanceDescriptor);
}

Since DateTime.GetType() != typeof(InstanceDescriptor) , the return value is false . CanConvertFrom is called by the IsValid method and since we've just established the return value is false , IsValid returns false .

So how comes the Convert method works even though the same CanConvertFrom method is called?

Well, the parameter you're passing is of type string , not DateTime .

typeConverter.CanConvertFrom(typeof(string))

In the first code snippet above, the CanConvertFrom method is for the TypeConverter base class. If we look at DateTimeConverter.CanConvertFrom override, it looks like this

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
    return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
}

Since we're passing in a String as the type, this method returns true (unlike the IsValid call). Because this returns true, the code continues to call

return (T)typeConverter.ConvertFrom(s);

(where T is a DateTime )

The DateTimeConverter simply calls DateTime.Parse and ignores culture. I'm not sure whether this is by design or a bug but I wouldn't rely on DateTime.Parse unless you know the string is always of the same format (or you format it correctly before calling your method).

DateTimeConverter.IsValid doesn't use current culture. Here's how I got around this

public class FixedDateTimeConverter : DateTimeConverter
{
    public override bool IsValid(ITypeDescriptorContext context, object value)
    {
        DateTime d;
        return DateTime.TryParse(value.ToString(), out d);
    }
}

...

var converter = TypeDescriptor.GetConverter(typeof (T));

if (typeof (T) == typeof (DateTime))
    converter = new FixedDateTimeConverter(); 

return converter.IsValid(null,r);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM