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"

我不明白不同的结果。有人可以解释一下吗?

谢谢!

4

3 回答 3

22

简短的解释是,虽然

DateTime.ToString(string format, IFormatProvider provider)

允许您将任何实现IFormatProvider作为其参数之一传递,它实际上仅支持IFormatProvider在其代码中实现的 2 种可能的类型:

DateTimeFormatInfo或者CultureInfo

如果您的参数无法转换(使用as)作为其中一个或那些,则该方法将默认为CurrentCulture.

String.Format不受此类界限的限制。

于 2010-03-04T20:26:40.320 回答
11

使用 Reflector检查该DateTime.ToString方法表明该DateTime结构使用该DateTimeFormatInfo.GetInstance方法获取要用于格式化的提供程序。从传入的提供者DateTimeFormatInfo.GetInstance请求一个类型的格式化DateTimeFormatInfo程序,从不 for ICustomFormmater,所以它只返回一个实例,DateTimeFormatInfo或者CultureInfo如果没有找到提供者。正如您的示例所示,该DateTime.ToString方法似乎不像该方法那样尊重ICustomFormatter接口。StringBuilder.FormatString.Format

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

于 2010-03-04T20:07:03.283 回答
3

使用扩展方法:)

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);
        }
    }
于 2010-03-04T20:14:49.880 回答