Как преобразовать название месяца (строку) в целое число для сравнения в C #?


94

Мне нужно иметь возможность сравнивать названия месяцев, которые у меня есть в массиве.

Было бы неплохо, если бы был какой-то прямой путь вроде:

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

Мой поиск в Google, кажется, предлагает единственный способ - написать свой собственный метод, но это кажется достаточно распространенной проблемой, и я бы подумал, что она уже реализована в .Net, кто-нибудь делал это раньше?

Ответы:


176

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

Хотя для ваших целей вам, вероятно, будет лучше просто Dictionary<string, int>сопоставить название месяца с его значением.


11
Обязательно учитывайте stackoverflow.com/questions/258793/… при принятии решения, использовать ли CultureInfo.CurrentCulture или CultureInfo.InvariantCulture
Расмус Фабер


19

Если вы используете DateTime.ParseExact()-метод, предложенный несколькими людьми, вы должны тщательно обдумать, что вы хотите, чтобы произошло, когда приложение работает в неанглоязычной среде!

В Дании, какие из ParseExact("Januar", ...)и ParseExact("January", ...)должны работать и которые должны потерпеть неудачу?

В этом будет разница между CultureInfo.CurrentCultureи CultureInfo.InvariantCulture.


10

Одним из простых решений было бы создать Словарь с именами и значениями. Затем с помощью Contains () вы можете найти правильное значение.

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

Вы можете использовать метод DateTime.Parse, чтобы получить объект DateTime, а затем проверить его свойство Month. Сделайте что-нибудь вроде этого:

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

Уловка состоит в том, чтобы создать действительную дату для создания объекта DateTime.


9

Вы можете использовать перечисление месяцев:

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

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

Однако я не уверен на 100% в синтаксисе enum.Parse ().


1
Это должно быть «public Month ToInt (string Input) {...}», но в остальном это правильно.
Джеймс Карран

1
Я знаю, что это старый комментарий, но подумал, что хочу указать, что вам следует начинать перечисление с 1, например, public enum Month { January = 1, Feburary }а также приводить к int вместо месяца.
eth0

@ eth0: Ой ... ты прав. Исправил, спасибо за указание ;-)
Treb

7

Для этого не нужно создавать экземпляр DateTime. Это так просто:

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

Я использую da-DKкультуру, поэтому этот модульный тест проходит:

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

Я оставлю это в качестве упражнения для читателя, чтобы создать перегрузку, в которую вы можете передать явную CultureInfo.


Хорошее использование ToLower()- я не знал, что одна из перегрузок преобразует строку, using the casing rules of the specified cultureхотя, честно говоря, из имени метода не очевидно, что она может позволить себе такую ​​функциональность.
Дэвид Кларк

1
Хорошо, я тестировал это с помощью LINQPad и не могу заставить его работать в моем CurrentCulture. Оба "January".ToLower(CultureInfo.CurrentCulture).Dump();и "January".ToLower(new CultureInfo("en-NZ")).Dump();выводятся, januaryно названия месяцев пишутся с заглавной буквы CurrentCulture.DateTimeFormat.MonthNames.
Дэвид Кларк

1
@DavidClarke Ну да, вы являетесь вызовом функции с именем ToLower:) На самом деле, есть небольшой логический изъян в моем коде, так как названия месяцев будут приведены в нижнем регистре в да-DK. Таким образом, либо не следует вводить строчные буквы, либо следует также строчными буквами все названия месяцев - в зависимости от того, требуется ли совпадение без учета регистра или нет.
Марк Земанн

1
Да, я интерпретировал документацию using the casing rules of the specified cultureкак означающую, что она будет использовать, например, месяцы и дни для CultureInfo. Что работает в вашем примере, потому что названия месяцев строчные. Эффективная демонстрация использования модульных тестов для введения в заблуждение. Может быть, стоит отредактировать, чтобы прояснить, что ваш пример - крайний случай :-)
Дэвид Кларк

2

И, отвечая на этот вопрос через семь лет после того, как вопрос был задан, можно провести это сравнение, используя встроенные методы:

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

становится

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

Который может быть преобразован в метод расширения для простоты. Ниже приведен пример LINQPad (отсюда и Dump()вызовы методов):

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

С выходом:

False
True
True

Это гораздо лучшее решение, использующее сравнение строк IgnoreCase. Это отлично работает для вопроса OP, однако, если вы хотите использовать этот метод для преобразования в то же значение, возвращаемое DateTime.Month, я бы предложил добавить +1 к результату. Требование сравнения OP все еще выполняется или конечно.
iGanja

1

Если вы используете C # 3.0 (или выше), вы можете использовать расширители


Тогда да, к сожалению, я думаю, что ваш собственный метод был бы самым элегантным решением.
Адам Нейлор

1
@AdamNaylor: что такое расширители? вы можете объяснить свой ответ примером?
Амир

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

код предупреждения находится в бета-версии.


Принятый ответ намного лучше.
Джеймс А. Молер,

1

Перевожу на C # код в испанской версии, касаемо:

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

Я использовал SimpleDateFormat для создания строки формата и синтаксического анализа текста по дате, а затем извлек из нее месяц. Код ниже:

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

Этот код поможет вам ...

using System.Globalization;

....

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

GetMonthName Method - возвращает строку ...

Если вы хотите получить месяц как целое число, просто используйте -

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

Надеюсь, это вам поможет !!!

Благодарность!!!

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.