web-dev-qa-db-fra.com

Comment créer et utiliser un IFormatProvider personnalisé pour DateTime?

J'essayais de créer une implémentation IFormatProvider qui reconnaîtrait les chaînes de format personnalisées pour les objets DateTime. Voici ma mise en œuvre:

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

Je m'attendais à pouvoir l'utiliser dans la méthode DateTime.ToString(string format, IFormatProvider provider) comme ça, mais:

DateTime d = new DateTime(2000, 1, 2);
string s = d.ToString("mycustomformat", new MyDateFormatProvider());

Dans cet exemple, exécuté dans la culture américaine, le résultat est "00cu0Ao00or0aA", Apparemment parce que les chaînes de format DateTime standard sont interprétées.

Cependant, lorsque j'utilise la même classe de la manière suivante:

DateTime d = new DateTime(2000, 1, 2);
string s = String.Format(new MyDateFormatProvider(), "{0:mycustomformat}", d);

J'obtiens ce que j'attends, à savoir "Sun Jan 02"

Je ne comprends pas les différents résultats. Quelqu'un pourrait-il expliquer?

Merci!

21
Mark Kennedy

Vérification du DateTime.ToString avec Reflector montre que la structure DateTime utilise le DateTimeFormatInfo.GetInstance méthode pour obtenir le fournisseur à utiliser pour le formatage. Le DateTimeFormatInfo.GetInstance demande un formateur de type DateTimeFormatInfo au fournisseur passé, jamais pour ICustomFormmater, donc il ne retourne qu'une instance de DateTimeFormatInfo ou CultureInfo si aucun fournisseur n'est trouvé. Il semble que le DateTime.ToString ne respecte pas l'interface ICustomFormatter comme le StringBuilder.Format fait, comme votre String.Format montre l'exemple.

Je reconnais que le DateTime.ToString devrait prendre en charge l'interface ICustomFormatter, mais cela ne semble pas le faire actuellement. Tout cela peut avoir changé ou changera dans .NET 4.0.

11
Kelly Ethridge

La brève explication est que si

DateTime.ToString(string format, IFormatProvider provider)

vous permet de passer tout ce qui implémente IFormatProvider comme l'un de ses paramètres, il ne supporte en fait que 2 types possibles implémentant IFormatProvider dans son code:

DateTimeFormatInfo ou CultureInfo

Si votre paramètre ne peut pas être converti (en utilisant as) comme l'un ou l'autre, la méthode sera par défaut CurrentCulture.

String.Format n'est pas limité par de telles limites.

20
Dynami Le Savard

Utilisez la méthode d'extension :)

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);
        }
    }
3
garik