Как создать и использовать пользовательский IFormatProvider для DateTime?

Я пытался создать реализацию 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"

Я не понимаю разные результаты. Может кто-нибудь объяснить?

Спасибо!

Ответ 1

Проверка метода DateTime.ToString с помощью Reflector показывает, что структура DateTime использует метод DateTimeFormatInfo.GetInstance, чтобы заставить провайдера использоваться для форматирования. DateTimeFormatInfo.GetInstance запрашивает форматировщик типа DateTimeFormatInfo от переданного провайдера, никогда не для ICustomFormmater, поэтому он возвращает экземпляр DateTimeFormatInfo или CultureInfo, если поставщик не найден. Похоже, что метод DateTime.ToString не соблюдает интерфейс ICustomFormatter, как это делает метод StringBuilder.Format, как показывает пример String.Format.

Я согласен с тем, что метод DateTime.ToString должен поддерживать интерфейс ICustomFormatter, но в настоящее время он не выглядит. Все это может измениться или измениться в .NET 4.0.

Ответ 2

Короче говоря, в то время как

DateTime.ToString(string format, IFormatProvider provider)

позволяет передавать что-либо, реализующее IFormatProvider как один из его параметров, фактически поддерживает только 2 возможных типа, реализующих IFormatProvider внутри него код:

DateTimeFormatInfo или CultureInfo

Если ваш параметр не может быть запущен (используя as) как один или те, метод по умолчанию будет CurrentCulture.

String.Format не ограничивается такими границами.

Ответ 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);
        }
    }