Como analisar o nome de um mês (string) para um número inteiro para comparação em C #?


92

Eu preciso ser capaz de comparar alguns nomes de meses que tenho em uma matriz.

Seria bom se houvesse alguma forma direta como:

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

Minha busca no Google parece sugerir que a única maneira é escrever seu próprio método, mas isso parece um problema comum o suficiente que eu acho que já teria sido implementado em .Net, alguém fez isso antes?

Respostas:


174

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

Embora, para seus propósitos, provavelmente seja melhor apenas criar um Dictionary<string, int>mapeamento do nome do mês para seu valor.


11
Certifique-se de considerar stackoverflow.com/questions/258793/… ao decidir se usar CultureInfo.CurrentCulture ou CultureInfo.InvariantCulture
Rasmus Faber

20

Você poderia fazer algo assim:

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

19

Se você usar o DateTime.ParseExact()-método que várias pessoas sugeriram, deve considerar cuidadosamente o que deseja que aconteça quando o aplicativo for executado em um ambiente diferente do inglês!

Na Dinamarca, o que, ParseExact("Januar", ...)e ParseExact("January", ...)deve trabalhar e que deve falhar?

Essa será a diferença entre CultureInfo.CurrentCulturee CultureInfo.InvariantCulture.


10

Uma solução simples seria criar um Dicionário com nomes e valores. Então, usando Contains (), você pode encontrar o valor correto.

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

Você pode usar o método DateTime.Parse para obter um objeto DateTime e, em seguida, verificar sua propriedade Month. Faça algo assim:

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

O truque é construir uma data válida para criar um objeto DateTime.


9

Você pode usar um enum de meses:

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

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

Não estou 100% certo sobre a sintaxe de enum.Parse (), no entanto.


1
Precisaria ser "public Mês ToInt (string Input) {...}" mas, caso contrário, está correto.
James Curran

1
Eu sei que este é um comentário antigo, mas pensei em apontar que você deve começar seu enum a partir de 1, por exemplo, public enum Month { January = 1, Feburary }e também converter para um int em vez de Mês.
eth0

@ eth0: Opa ... você está certo. Corrigido, obrigado por apontar ;-)
Treb

7

Você não precisa criar uma instância DateTime para fazer isso. É tão simples quanto isto:

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

Estou executando na da-DKcultura, então este teste de unidade passa:

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

Vou deixar isso como um exercício para o leitor criar uma sobrecarga onde você possa passar um CultureInfo explícito.


Bom uso de ToLower()- eu não sabia que uma das sobrecargas converte a string, using the casing rules of the specified cultureembora, para ser justo, não seja óbvio, pelo nome do método, que ela possa oferecer essa funcionalidade.
David Clarke

1
Ok, estive testando isso usando o LINQPad e não consigo fazer funcionar no meu CurrentCulture. Ambos "January".ToLower(CultureInfo.CurrentCulture).Dump();e "January".ToLower(new CultureInfo("en-NZ")).Dump();saída de januarynomes, mas mês são capitalizados no CurrentCulture.DateTimeFormat.MonthNames.
David Clarke

1
@DavidClarke Bem, sim, você está chamando uma função chamada ToLower:) Na verdade, há uma pequena falha lógica em meu código, já que os nomes dos meses são fornecidos em minúsculas em da-DK. Portanto, não se deve colocar a entrada em caixa baixa, ou então deve-se também diminuir todas as letras dos nomes dos meses - dependendo se uma correspondência sem distinção entre maiúsculas e minúsculas é desejada ou não.
Mark Seemann

1
Sim, estava interpretando a documentação using the casing rules of the specified culturepara significar que seria maiúscula, por exemplo, meses e dias por CultureInfo. O que funciona no seu exemplo porque os nomes dos meses estão em minúsculas. Demonstração eficaz do uso de testes de unidade para enganar. Pode ser digno de uma edição para deixar claro que seu exemplo é um caso extremo :-)
David Clarke

2

E respondendo a isso sete anos após a pergunta ser feita, é possível fazer esta comparação usando métodos integrados:

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

torna-se

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

Que pode ser refatorado em um método de extensão para simplificar. A seguir está um exemplo do LINQPad (daí as Dump()chamadas de método):

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

Com saída:

False
True
True

Esta é uma solução muito melhor usando a comparação de strings IgnoreCase. Isso funciona bem para a pergunta do OP, no entanto, se você quiser usar esse método para converter para o mesmo valor retornado por DateTime.Month, sugiro adicionar +1 ao resultado. O requisito de comparação do OP ainda está satisfeito ou claro.
iGanja

1

Se você estiver usando c # 3.0 (ou superior), você pode usar extensores


Então, sim, infelizmente, acho que seu próprio método seria a solução mais elegante.
Adam Naylor

@AdamNaylor: o que são extensores? você pode explicar sua resposta com um exemplo?
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

o código de cuidado está na versão Beta.


A resposta aceita é muito melhor.
James A Mohler

1

Eu traduzo para o código C # na versão em espanhol, respeitando:

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

O que fiz foi usar SimpleDateFormat para criar uma string de formato, analisar o texto em uma data e, em seguida, recuperar o mês dessa data. O código está abaixo:

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

Este código ajuda você ...

using System.Globalization;

....

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

Método GetMonthName - retorna string ...

Se você deseja obter um mês como um número inteiro, basta usar -

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

Espero que ajude você !!!

Obrigado!!!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.