Jak przeanalizować nazwę miesiąca (ciąg) na liczbę całkowitą w celu porównania w C #?


94

Muszę mieć możliwość porównania nazw miesięcy, które mam w tablicy.

Byłoby miło, gdyby istniał jakiś bezpośredni sposób, taki jak:

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

Moje wyszukiwanie w Google wydaje się sugerować, że jedynym sposobem jest napisanie własnej metody, ale wydaje się to na tyle powszechnym problemem, że myślę, że zostałby już zaimplementowany w .Net, czy ktoś zrobił to wcześniej?

Odpowiedzi:


176

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

Chociaż do twoich celów prawdopodobnie lepiej będzie po prostu stworzyć Dictionary<string, int>mapowanie nazwy miesiąca na jego wartość.


11
Pamiętaj, aby wziąć pod uwagę stackoverflow.com/questions/258793/ ... przy podejmowaniu decyzji, czy użyć CultureInfo.CurrentCulture lub CultureInfo.InvariantCulture
Rasmus Faber


19

Jeśli używasz metody DateTime.ParseExact()-metody, którą sugerowało kilka osób, powinieneś dokładnie przemyśleć, co chcesz się stać, gdy aplikacja będzie działać w środowisku innym niż angielski!

W Danii, które z nich ParseExact("Januar", ...)i które ParseExact("January", ...)powinny działać, a które nie?

To będzie różnica między CultureInfo.CurrentCulturea CultureInfo.InvariantCulture.


10

Jednym prostym rozwiązaniem byłoby utworzenie słownika z nazwami i wartościami. Następnie za pomocą Contains () możesz znaleźć odpowiednią wartość.

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

Możesz użyć metody DateTime.Parse, aby pobrać obiekt DateTime, a następnie sprawdzić jego właściwość Month. Zrób coś takiego:

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

Sztuczka polega na zbudowaniu prawidłowej daty w celu utworzenia obiektu DateTime.


9

Możesz użyć wyliczenia miesięcy:

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

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

Nie mam jednak 100% pewności co do składni enum.Parse ().


1
Musiałby to być „public Month ToInt (string Input) {...}”, ale poza tym jest poprawny.
James Curran

1
Wiem, że to stary komentarz, ale pomyślałem, że powinienem wskazać, że powinieneś zacząć swoje wyliczenie od 1, np. public enum Month { January = 1, Feburary }I rzutować na int zamiast Month.
eth0

@ eth0: Ups ... masz rację. Poprawiłem, dziękuję za wskazanie ;-)
Treb

7

Aby to zrobić, nie musisz tworzyć instancji DateTime. To takie proste:

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

Korzystam z da-DKkultury, więc ten test jednostkowy przechodzi:

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

Pozostawię to czytelnikowi jako ćwiczenie, aby utworzyć przeciążenie, w którym można przekazać jawną wartość CultureInfo.


Niezłe użycie ToLower()- nie byłem świadomy tego, że jedno z przeciążeń konwertuje ciąg, using the casing rules of the specified culturechociaż aby być uczciwym, z nazwy metody nie wynika, że ​​może zapewnić taką funkcjonalność.
David Clarke

1
Ok, więc testowałem to za pomocą LINQPad i nie mogę zmusić go do działania w moim CurrentCulture. Zarówno, jak "January".ToLower(CultureInfo.CurrentCulture).Dump();i "January".ToLower(new CultureInfo("en-NZ")).Dump();dane wyjściowe, januaryale nazwy miesięcy są pisane wielkimi literami w CurrentCulture.DateTimeFormat.MonthNames.
David Clarke

1
@DavidClarke No tak, to jest wywołanie funkcji o nazwie ToLower:) Faktycznie, istnieje niewielka wada logiczny w moim kodu, ponieważ miesiąc nazwy podane jako wszystkimi małymi literami w da-DK. Zatem albo nie należy wpisywać danych małymi literami, albo też powinno się pisać małymi literami wszystkie nazwy miesięcy - w zależności od tego, czy dopasowanie bez uwzględniania wielkości liter jest pożądane, czy nie.
Mark Seemann

1
Tak, interpretowałem dokumentację using the casing rules of the specified culturew taki sposób, że będzie to kapitalizować np. Miesiące i dni zgodnie z CultureInfo. Co działa w twoim przykładzie, ponieważ nazwy miesięcy są pisane małymi literami. Skuteczna demonstracja wykorzystania testów jednostkowych w celu wprowadzenia w błąd. Może warto edytować, aby wyjaśnić, że twój przykład to wyjątkowy przypadek :-)
David Clarke

2

Odpowiadając na to siedem lat po zadaniu pytania, możliwe jest dokonanie tego porównania za pomocą wbudowanych metod:

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

staje się

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

Które można przełożyć na metodę rozszerzenia dla uproszczenia. Oto przykład LINQPad (stąd Dump()wywołania metody):

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

Z wyjściem:

False
True
True

Jest to znacznie lepsze rozwiązanie przy użyciu porównania ciągów IgnoreCase. Działa to dobrze w przypadku pytania OP, jednak jeśli chcesz użyć tej metody do konwersji na tę samą wartość zwróconą przez DateTime.Month, sugerowałbym dodanie +1 do wyniku. Wymóg porównania PO jest nadal spełniony lub oczywiście.
iGanja

1

Jeśli używasz języka C # 3.0 (lub nowszego), możesz użyć rozszerzeń


Więc tak, niestety myślę, że twoja własna metoda byłaby najbardziej eleganckim rozwiązaniem.
Adam Naylor,

1
@AdamNaylor: co to są przedłużacze? czy możesz wyjaśnić swoją odpowiedź na przykładzie?
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

Kod ostrzegawczy jest w wersji Beta.


Zaakceptowana odpowiedź jest znacznie lepsza.
James A Mohler

1

Przekładam to na kod C # w wersji hiszpańskiej, pozdrawiam:

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

To, co zrobiłem, to użycie SimpleDateFormat do utworzenia ciągu formatu i przeanalizowałem tekst do daty, a następnie pobrałem z tego miesiąc. Kod poniżej:

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

Ten kod pomaga ...

using System.Globalization;

....

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

Metoda GetMonthName - zwraca ciąg ...

Jeśli chcesz otrzymać miesiąc jako liczbę całkowitą, po prostu użyj -

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

Mam nadzieję, że Ci to pomoże!!!

Dzięki!!!

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.