Come analizzare il nome di un mese (stringa) in un numero intero per il confronto in C #?


92

Devo essere in grado di confrontare alcuni nomi di mesi che ho in un array.

Sarebbe bello se ci fosse un modo diretto come:

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

La mia ricerca su Google sembra suggerire che l'unico modo è scrivere il tuo metodo, ma questo sembra un problema abbastanza comune da pensare che sarebbe stato già implementato in .Net, qualcuno lo ha già fatto prima?

Risposte:


174

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

Anche se, per i tuoi scopi, probabilmente starai meglio creando una Dictionary<string, int>mappatura del nome del mese sul suo valore.


11
Assicurati di considerare stackoverflow.com/questions/258793/… quando decidi se usare CultureInfo.CurrentCulture o CultureInfo.InvariantCulture
Rasmus Faber

20

Potresti fare qualcosa del genere:

Convert.ToDate(month + " 01, 1900").Month

19

Se usi il DateTime.ParseExact()metodo suggerito da diverse persone, dovresti considerare attentamente cosa vuoi che accada quando l'applicazione viene eseguita in un ambiente non inglese!

In Danimarca, che di ParseExact("Januar", ...)e ParseExact("January", ...)dovrebbe funzionare, e che dovrebbe fallire?

Questa sarà la differenza tra CultureInfo.CurrentCulturee CultureInfo.InvariantCulture.


10

Una semplice soluzione sarebbe creare un dizionario con nomi e valori. Quindi usando Contains () puoi trovare il valore giusto.

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

9

È possibile utilizzare il metodo DateTime.Parse per ottenere un oggetto DateTime e quindi controllare la relativa proprietà Month. Fai qualcosa del genere:

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

Il trucco è creare una data valida per creare un oggetto DateTime.


9

Puoi usare un enum di mesi:

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

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

Tuttavia, non sono sicuro al 100% sulla sintassi di enum.Parse ().


1
Dovrebbe essere "public Month ToInt (string Input) {...}" ma per il resto è corretto.
James Curran

1
So che questo è un vecchio commento, ma ho pensato di farti notare che dovresti iniziare l'enumerazione da 1, ad esempio, public enum Month { January = 1, Feburary }e anche eseguire il cast su un int anziché su Month.
eth0

@ eth0: Oops ... hai ragione. Corretto, grazie per averlo segnalato ;-)
Treb

7

Non è necessario creare un'istanza DateTime per farlo. È così semplice:

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

Sto correndo sulla da-DKcultura, quindi questo test unitario viene superato:

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

Lascio al lettore come esercizio la creazione di un overload in cui è possibile passare un CultureInfo esplicito.


Buon uso di ToLower(): non sapevo che uno degli overload converte la stringa using the casing rules of the specified cultureanche se per essere onesti non è ovvio dal nome del metodo che potrebbe permettersi quella funzionalità.
David Clarke

1
Ok, quindi l'ho testato usando LINQPad e non riesco a farlo funzionare nel mio CurrentCulture. Sia "January".ToLower(CultureInfo.CurrentCulture).Dump();e "January".ToLower(new CultureInfo("en-NZ")).Dump();uscita januarynomi, ma mese sono capitalizzati nel CurrentCulture.DateTimeFormat.MonthNames.
David Clarke

1
@DavidClarke Beh, sì, si stanno chiamando una funzione chiamata ToLower:) In realtà, c'è un leggero difetto logico nel mio codice, dal momento che i nomi dei mesi sono dati come tutto in minuscolo in da-DK. Quindi, o non si dovrebbe inserire l'input in minuscolo, oppure si dovrebbe anche mettere in minuscolo tutti i nomi dei mesi, a seconda che si desideri o meno una corrispondenza senza distinzione tra maiuscole e minuscole.
Mark Seemann

1
Sì, stavo interpretando la documentazione using the casing rules of the specified culturenel senso che avrebbe capitalizzato, ad esempio, mesi e giorni per CultureInfo. Che funziona nel tuo esempio perché i nomi dei mesi sono minuscoli. Dimostrazione efficace dell'utilizzo di unit test per fuorviare. Potrebbe essere degno di una modifica per chiarire che il tuo esempio è un caso limite :-)
David Clarke,

2

E rispondendo a questo sette anni dopo che è stata posta la domanda, è possibile fare questo confronto utilizzando metodi integrati:

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

diventa

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

Che può essere modificato in un metodo di estensione per semplicità. Il seguente è un esempio di LINQPad (da cui le Dump()chiamate al metodo):

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

Con uscita:

False
True
True

Questa è una soluzione molto migliore utilizzando il confronto di stringhe IgnoreCase. Funziona bene per la domanda dell'OP, tuttavia se si desidera utilizzare questo metodo per la conversione allo stesso valore restituito da DateTime.Month, suggerirei di aggiungere +1 al risultato. Il requisito di confronto del PO è ancora soddisfatto o è ovvio.
iGanja

1

Se stai usando c # 3.0 (o successivo) puoi usare gli extender


Allora sì, sfortunatamente penso che il tuo metodo sarebbe la soluzione più elegante.
Adam Naylor

@AdamNaylor: cosa sono gli estensori? puoi spiegare la tua risposta con un esempio?
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

il codice di attenzione è in versione Beta.


La risposta accettata è molto meglio.
James A Mohler

1

Lo traduco in codice C # in versione spagnola, saluti:

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";

        }

   }

0

Quello che ho fatto è stato utilizzare SimpleDateFormat per creare una stringa di formato e analizzare il testo in una data, quindi recuperare il mese da quella. Il codice è di seguito:

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

0

Questo codice ti aiuta ...

using System.Globalization;

....

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

Metodo GetMonthName: restituisce la stringa ...

Se vuoi ottenere un mese come numero intero, usa semplicemente -

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

Spero ti aiuti !!!

Grazie!!!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.