Comment analyser un nom de mois (chaîne) en un entier pour comparaison en C #?

92

J'ai besoin de pouvoir comparer certains noms de mois que j'ai dans un tableau.

Ce serait bien s'il y avait un moyen direct comme:

Month.toInt("January") > Month.toInt("May")

Ma recherche sur Google semble suggérer que le seul moyen est d'écrire votre propre méthode, mais cela semble être un problème assez courant pour que je pense qu'il aurait déjà été implémenté dans .Net, est-ce que quelqu'un l'a déjà fait?

déversement
la source

Réponses:

174

DateTime.ParseExact(monthName, "MMMM", CultureInfo.CurrentCulture ).Month

Bien que, pour vos besoins, vous feriez probablement mieux de simplement créer une Dictionary<string, int>correspondance entre le nom du mois et sa valeur.

James Curran
la source
11
N'oubliez pas de prendre en compte stackoverflow.com/questions/258793/… lorsque vous décidez d'utiliser CultureInfo.CurrentCulture ou CultureInfo.InvariantCulture
Rasmus Faber
20

Vous pouvez faire quelque chose comme ceci:

Convert.ToDate(month + " 01, 1900").Month
Aaron Palmer
la source
19

Si vous utilisez la DateTime.ParseExact()méthode suggérée par plusieurs personnes, vous devez soigneusement réfléchir à ce que vous souhaitez faire lorsque l'application s'exécute dans un environnement non anglais!

Au Danemark, ce qui ParseExact("Januar", ...)et ParseExact("January", ...)devrait fonctionner et qui devrait échouer?

Ce sera la différence entre CultureInfo.CurrentCultureet CultureInfo.InvariantCulture.

Rasmus Faber
la source
10

Une solution simple serait de créer un dictionnaire avec des noms et des valeurs. Ensuite, en utilisant Contains (), vous pouvez trouver la bonne valeur.

Dictionary<string, string> months = new Dictionary<string, string>()
{
                { "january", "01"},
                { "february", "02"},
                { "march", "03"},
                { "april", "04"},
                { "may", "05"},
                { "june", "06"},
                { "july", "07"},
                { "august", "08"},
                { "september", "09"},
                { "october", "10"},
                { "november", "11"},
                { "december", "12"},
};
foreach (var month in months)
{
    if (StringThatContainsMonth.ToLower().Contains(month.Key))
    {
        string thisMonth = month.Value;
    }
}
Carlos A. Ortiz
la source
9

Vous pouvez utiliser la méthode DateTime.Parse pour obtenir un objet DateTime, puis vérifier sa propriété Month. Faites quelque chose comme ceci:

int month = DateTime.Parse("1." + monthName + " 2008").Month;

L'astuce consiste à créer une date valide pour créer un objet DateTime.

Rune Grimstad
la source
9

Vous pouvez utiliser une énumération de mois:

public enum Month
{
    January,
    February,
    // (...)
    December,
}    

public Month ToInt(Month Input)
{
    return (int)Enum.Parse(typeof(Month), Input, true));
}

Cependant, je ne suis pas sûr à 100% de la syntaxe de enum.Parse ().

Treb
la source
1
Il faudrait que ce soit "public Month ToInt (string Input) {...}" mais sinon c'est correct.
James Curran
1
Je sais que c'est un vieux commentaire, mais j'ai pensé que vous devriez commencer votre énumération à partir de 1, par exemple, public enum Month { January = 1, Feburary }et également convertir un int au lieu de Month.
eth0
@ eth0: Oups ... vous avez raison. Corrigé, merci de l'avoir signalé ;-)
Treb
7

Vous n'avez pas besoin de créer une instance de DateTime pour ce faire. C'est aussi simple que ça:

public static class Month
{
    public static int ToInt(this string month)
    {
        return Array.IndexOf(
            CultureInfo.CurrentCulture.DateTimeFormat.MonthNames,
            month.ToLower(CultureInfo.CurrentCulture))
            + 1;
    }
}

J'exécute sur la da-DKculture, donc ce test unitaire réussit:

[Theory]
[InlineData("Januar", 1)]
[InlineData("Februar", 2)]
[InlineData("Marts", 3)]
[InlineData("April", 4)]
[InlineData("Maj", 5)]
[InlineData("Juni", 6)]
[InlineData("Juli", 7)]
[InlineData("August", 8)]
[InlineData("September", 9)]
[InlineData("Oktober", 10)]
[InlineData("November", 11)]
[InlineData("December", 12)]
public void Test(string monthName, int expected)
{
    var actual = monthName.ToInt();
    Assert.Equal(expected, actual);
}

Je vais laisser au lecteur un exercice pour créer une surcharge où vous pouvez passer un CultureInfo explicite.

Mark Seemann
la source
Bonne utilisation de ToLower()- je n'étais pas au courant que l'une des surcharges convertit la chaîne, using the casing rules of the specified culturebien que pour être honnête, il n'est pas évident d'après le nom de la méthode qu'elle pourrait offrir cette fonctionnalité.
David Clarke
1
Ok, donc j'ai testé ceci en utilisant LINQPad et je ne peux pas le faire fonctionner dans mon CurrentCulture. Les deux "January".ToLower(CultureInfo.CurrentCulture).Dump();et la "January".ToLower(new CultureInfo("en-NZ")).Dump();sortie januarymais les noms de mois sont en majuscules dans CurrentCulture.DateTimeFormat.MonthNames.
David Clarke
1
@DavidClarke Eh bien, oui, vous sont appeler une fonction appelée ToLower:) En fait, il y a une faille légère logique dans mon code, puisque les noms de mois sont donnés à titre minuscules dans da-DK. Donc, soit on ne doit pas minuscules l'entrée, soit on doit également minuscules tous les noms de mois - selon que l'on souhaite ou non une correspondance insensible à la casse.
Mark Seemann
1
Oui, j'interprétais la documentation using the casing rules of the specified culturecomme signifiant qu'elle capitaliserait par exemple des mois et des jours par CultureInfo. Ce qui fonctionne dans votre exemple car les noms de mois sont en minuscules. Démonstration efficace de l'utilisation des tests unitaires pour induire en erreur. Cela pourrait valoir la peine d'être modifié pour indiquer clairement que votre exemple est un cas limite :-)
David Clarke
2

Et en répondant à ces sept ans après que la question a été posée, il est possible de faire cette comparaison en utilisant des méthodes intégrées:

Month.toInt("January") > Month.toInt("May")

devient

Array.FindIndex( CultureInfo.CurrentCulture.DateTimeFormat.MonthNames,
                 t => t.Equals("January", StringComparison.CurrentCultureIgnoreCase)) >
Array.FindIndex( CultureInfo.CurrentCulture.DateTimeFormat.MonthNames,
                 t => t.Equals("May", StringComparison.CurrentCultureIgnoreCase))

Qui peut être refactorisé en une méthode d'extension pour plus de simplicité. Voici un exemple LINQPad (d'où les Dump()appels de méthode):

void Main()
{
    ("January".GetMonthIndex() > "May".GetMonthIndex()).Dump();
    ("January".GetMonthIndex() == "january".GetMonthIndex()).Dump();
    ("January".GetMonthIndex() < "May".GetMonthIndex()).Dump();
}

public static class Extension {
    public static int GetMonthIndex(this string month) {
        return Array.FindIndex( CultureInfo.CurrentCulture.DateTimeFormat.MonthNames,
                         t => t.Equals(month, StringComparison.CurrentCultureIgnoreCase));
    }
}

Avec sortie:

False
True
True
David Clarke
la source
C'est une bien meilleure solution en utilisant la comparaison de chaînes IgnoreCase. Cela fonctionne bien pour la question de l'OP, mais si vous souhaitez utiliser cette méthode pour convertir à la même valeur retournée par DateTime.Month, je suggérerais d'ajouter +1 au résultat. L'exigence de comparaison du PO est toujours satisfaite ou bien sûr.
iGanja du
1

Si vous utilisez c # 3.0 (ou supérieur), vous pouvez utiliser des extendeurs

Adam Naylor
la source
Alors oui, malheureusement, je pense que votre propre méthode serait la solution la plus élégante.
Adam Naylor
@AdamNaylor: qu'est-ce que les extendeurs? pouvez-vous expliquer votre réponse avec un exemple?
Amir
1
Public Function returnMonthNumber(ByVal monthName As String) As Integer
    Select Case monthName.ToLower
        Case Is = "january"
            Return 1
        Case Is = "february"
            Return 2
        Case Is = "march"
            Return 3
        Case Is = "april"
            Return 4
        Case Is = "may"
            Return 5
        Case Is = "june"
            Return 6
        Case Is = "july"
            Return 7
        Case Is = "august"
            Return 8
        Case Is = "september"
            Return 9
        Case Is = "october"
            Return 10
        Case Is = "november"
            Return 11
        Case Is = "december"
            Return 12
        Case Else
            Return 0
    End Select
End Function

le code d'avertissement est en version bêta.

Thabiso
la source
La réponse acceptée est bien meilleure.
James A Mohler
1

Je le traduis en code C # en version espagnole, en ce qui concerne:

public string ObtenerNumeroMes(string NombreMes){

       string NumeroMes;   

       switch(NombreMes) {

        case ("ENERO") :
            NumeroMes = "01";
            return NumeroMes;

        case ("FEBRERO") :
            NumeroMes = "02";
            return NumeroMes;

        case ("MARZO") :
            NumeroMes = "03";
            return NumeroMes;

        case ("ABRIL") :
            NumeroMes = "04";
            return NumeroMes;

        case ("MAYO") :
            NumeroMes = "05";
            return NumeroMes;

        case ("JUNIO") :
            NumeroMes = "06";
            return NumeroMes;

        case ("JULIO") :
            NumeroMes = "07";
            return NumeroMes;

        case ("AGOSTO") :
            NumeroMes = "08";
            return NumeroMes;

        case ("SEPTIEMBRE") :
            NumeroMes = "09";
            return NumeroMes;

        case ("OCTUBRE") :
            NumeroMes = "10";
            return NumeroMes;

        case ("NOVIEMBRE") :
            NumeroMes = "11";
            return NumeroMes;

        case ("DICIEMBRE") :
            NumeroMes = "12";
            return NumeroMes;

            default:
            Console.WriteLine("Error");
            return "ERROR";

        }

   }
Maria Carolina Araujo
la source
0

Ce que j'ai fait, c'est d'utiliser SimpleDateFormat pour créer une chaîne de format, analyser le texte à une date, puis récupérer le mois à partir de là. Le code est ci-dessous:

int year = 2012 \\or any other year
String monthName = "January" \\or any other month
SimpleDateFormat format = new SimpleDateFormat("dd-MMM-yyyy");
int monthNumber = format.parse("01-" + monthName + "-" + year).getMonth();
Ebenezer Ampiah
la source
0

Ce code vous aide ...

using System.Globalization;

....

string FullMonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(DateTime.UtcNow.Month);

Méthode GetMonthName - elle renvoie la chaîne ...

Si vous voulez obtenir un mois sous forme d'entier, utilisez simplement -

DateTime dt= DateTime.UtcNow;
int month= dt.Month;

J'espère que ça t'aide!!!

Merci!!!

Nitika Chopra
la source