如何创建和使用自定义的IFormatProvider来处理DateTime类型?

24

我试图创建一个实现IFormatProvider接口的对象,以识别DateTime对象的自定义格式字符串。这是我的实现代码:

 public class MyDateFormatProvider : IFormatProvider, ICustomFormatter
 {
  public object GetFormat(Type formatType)
  {
   if (formatType == typeof(ICustomFormatter))
   {
    return this;
   }
   return null;
  }

  public string Format(string format, object arg, IFormatProvider formatProvider)
  {
   if(arg == null) throw new ArgumentNullException("arg");
   if (arg.GetType() != typeof(DateTime)) return arg.ToString();
   DateTime date = (DateTime)arg;
   switch(format)
   {
    case "mycustomformat":
     switch(CultureInfo.CurrentCulture.Name)
     {
      case "en-GB":
       return date.ToString("ddd dd MMM");
      default:
       return date.ToString("ddd MMM dd");
     }
    default:
     throw new FormatException();
   }
  } 

我本以为可以在DateTime.ToString(string format, IFormatProvider provider)方法中使用它,类似于这样,但是:

DateTime d = new DateTime(2000, 1, 2);
string s = d.ToString("mycustomformat", new MyDateFormatProvider());
在这个例子中,以美国文化方式运行时,得到的结果是"00cu0Ao00or0aA"。看起来是因为解释了标准的DateTime格式字符串。 然而,当我按照以下方式使用相同的类:
DateTime d = new DateTime(2000, 1, 2);
string s = String.Format(new MyDateFormatProvider(), "{0:mycustomformat}", d);

我得到了我所期望的结果,即"Sun Jan 02"

我不明白这些不同的结果。有人能解释一下吗?

谢谢!

3个回答

22

简短的解释是,虽然

DateTime.ToString(string format, IFormatProvider provider)

这个方法允许你将任何实现了IFormatProvider接口的对象作为它的一个参数传入,但是在该方法内部实际上只支持两种实现了IFormatProvider接口的类型:

DateTimeFormatInfoCultureInfo

如果你的参数不能被强制转换(使用as)成其中之一,该方法会默认使用CurrentCulture

String.Format没有这样的限制。


11
检查使用反编译器查看 DateTime.ToString 方法,发现 DateTime 结构使用 DateTimeFormatInfo.GetInstance 方法获取要用于格式化的提供程序。 DateTimeFormatInfo.GetInstance 请求从传入的提供程序中获取 DateTimeFormatInfo 类型的格式化程序,而不是 ICustomFormatter,因此仅在找不到提供程序时返回 DateTimeFormatInfoCultureInfo 的实例。似乎 DateTime.ToString 方法不像您的 String.Format 示例所示的那样支持 ICustomFormatter 接口。

我同意 DateTime.ToString 方法应支持 ICustomFormatter 接口,但目前似乎并没有。这可能已经或将在 .NET 4.0 中发生改变。


感谢您详细的回复。我原本以为自己误解了接口和预期实现,所以很高兴知道是DateTime实现有问题 :-) - Mark Kennedy

4
使用扩展方法 :)
public static class FormatProviderExtension
    {
        public static string FormatIt(string format, object arg, IFormatProvider formatProvider)
        {
            if (arg == null) throw new ArgumentNullException("arg");
            if (arg.GetType() != typeof(DateTime)) return arg.ToString();
            DateTime date = (DateTime)arg;
            switch (format)
            {
                case "mycustomformat":
                    switch (CultureInfo.CurrentCulture.Name)
                    {
                        case "en-GB":
                            return date.ToString("ddd dd MMM");
                        default:
                            return date.ToString("ddd MMM dd");
                    }
                default:
                    throw new FormatException();
            }
        }

        public static string ToString(this DateTime d, IFormatProvider formatProvider, string format)
        {
            return FormatIt(format, d, formatProvider);
        }
    }

是的,看起来扩展方法是我唯一的选择。感谢您的回复! - Mark Kennedy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接