Как создать и использовать пользовательский IFormatProvider для DateTime? - PullRequest
20 голосов
/ 04 марта 2010

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

18 голосов
/ 04 марта 2010

Краткое объяснение состоит в том, что пока

DateTime.ToString(string format, IFormatProvider provider)

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

DateTimeFormatInfo или CultureInfo

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

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

10 голосов
/ 04 марта 2010

Проверка метода 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.

3 голосов
/ 04 марта 2010

Использовать метод расширения:)

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);
        }
    }
...