103

I need to be able to compare some month names I have in an array.

It would be nice if there were some direct way like:

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

My Google searching seems to suggest the only way is to write your own method, but this seems like a common enough problem that I would think it would have been already implemented in .Net, anyone done this before?

Joe Doyle
  • 6,363
  • 3
  • 42
  • 45
spilliton
  • 3,811
  • 5
  • 35
  • 35

15 Answers15

197

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

Although, for your purposes, you'll probably be better off just creating a Dictionary<string, int> mapping the month's name to its value.

Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 12
    Be sure to consider http://stackoverflow.com/questions/258793/how-to-parse-a-month-name-string-to-an-integer-for-comparison-in-c#258895 when deciding whether to use CultureInfo.CurrentCulture or CultureInfo.InvariantCulture – Rasmus Faber Nov 03 '08 at 14:59
24

You could do something like this:

Convert.ToDate(month + " 01, 1900").Month
Aaron Palmer
  • 8,912
  • 9
  • 48
  • 77
20

If you use the DateTime.ParseExact()-method that several people have suggested, you should carefully consider what you want to happen when the application runs in a non-English environment!

In Denmark, which of ParseExact("Januar", ...) and ParseExact("January", ...) should work and which should fail?

That will be the difference between CultureInfo.CurrentCulture and CultureInfo.InvariantCulture.

Liam
  • 27,717
  • 28
  • 128
  • 190
Rasmus Faber
  • 48,631
  • 24
  • 141
  • 189
10

One simply solution would be create a Dictionary with names and values. Then using Contains() you can find the right value.

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;
    }
}
Mattias
  • 3,907
  • 4
  • 28
  • 50
Carlos A. Ortiz
  • 169
  • 2
  • 6
9

You can use the DateTime.Parse method to get a DateTime object and then check its Month property. Do something like this:

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

The trick is to build a valid date to create a DateTime object.

Rune Grimstad
  • 35,612
  • 10
  • 61
  • 76
9

You can use an enum of months:

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

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

I am not 100% certain on the syntax for enum.Parse(), though.

Treb
  • 19,903
  • 7
  • 54
  • 87
  • 1
    It would need to be "public Month ToInt(string Input) {...}" but otherwise it is correct. – James Curran Nov 03 '08 at 14:51
  • 2
    I know this is an old comment but thought I'd point out you should start your enum from 1, e.g. `public enum Month { January = 1, Feburary }` and also cast to a int instead of Month. – eth0 Nov 04 '11 at 08:26
  • @eth0: Oops... you're right. Corrected it, thanks for pointing it out ;-) – Treb Nov 04 '11 at 12:30
8

You don't have to create a DateTime instance to do this. It's as simple as this:

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

I'm running on the da-DK culture, so this unit test passes:

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

I'll leave it as an exercise to the reader to create an overload where you can pass in an explicit CultureInfo.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Nice use of `ToLower()` - I wasn't aware one of the overloads converts the string `using the casing rules of the specified culture` although to be fair it's not obvious from the method name that it might afford that functionality. – David Clarke Jun 03 '15 at 21:43
  • 1
    Ok, so I've been testing this using LINQPad and I cannot get it to work in my `CurrentCulture`. Both `"January".ToLower(CultureInfo.CurrentCulture).Dump();` and `"January".ToLower(new CultureInfo("en-NZ")).Dump();` output `january` but month names are capitalised in the `CurrentCulture.DateTimeFormat.MonthNames`. – David Clarke Jun 03 '15 at 23:55
  • 1
    @DavidClarke Well, yes, you *are* calling a function called `ToLower` :) Actually, there's a slight logical flaw in my code, since month names *are* given as all lower case in da-DK. So, either one shouldn't lower case the input, or else one ought to also lower case all the month names - depending on whether a case-insensitive match is desired or not. – Mark Seemann Jun 04 '15 at 05:59
  • 1
    Yes I was interpreting the documentation `using the casing rules of the specified culture` to mean that it would capitalise e.g. months and days per the `CultureInfo`. Which works in your example because month names are lower case. Effective demonstration of using unit tests to mislead. Might be worthy of an edit to make it clear that your example is an edge case :-) – David Clarke Jun 05 '15 at 22:53
  • This one is perfect. I have changed the function a bit so you can pass a culture name cause I can and do know before hand what language the month is. And it can be any language any time. – Niels Lucas Apr 15 '22 at 09:58
3
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

caution code is in Beta version.

Thabiso
  • 51
  • 1
2

And answering this seven years after the question was asked, it is possible to do this comparison using built-in methods:

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

becomes

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

Which can be refactored into an extension method for simplicity. The following is a LINQPad example (hence the Dump() method calls):

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

With output:

False
True
True
David Clarke
  • 12,888
  • 9
  • 86
  • 116
  • This is a much better solution using the IgnoreCase string comparison. This works fine for the OP's question, however if you want to use this method for converting to the same value returned by DateTime.Month, I would suggest adding +1 to the result. The OP's comparison requirement is still satisfied or course. – iGanja Dec 26 '18 at 18:26
1

I translate it into C# code in Spanish version, regards:

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

        }

   }
Niall C.
  • 10,878
  • 7
  • 69
  • 61
1

If you are using c# 3.0 (or above) you can use extenders

Adam Naylor
  • 6,172
  • 10
  • 49
  • 69
0

What I did was to use SimpleDateFormat to create a format string, and parse the text to a date, and then retrieve the month from that. The code is below:

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

This code helps you...

using System.Globalization;

....

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

GetMonthName Method - it returns string...

If you want to get a month as an integer, then simply use -

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

I hope, it helps you!!!

Thanks!!!

Nitika Chopra
  • 1,281
  • 17
  • 22
  • 1
    This doesn't answer the question in any way. OP has a string that needs to be converted to an integer. He doesn't have a DateTime object. – Niels Lucas Apr 15 '22 at 09:42
0
int selectedValue = 0;
            switch (curentMonth)
            {
                case "January":
                    selectedValue = 1;
                    break;
                case "February":
                    selectedValue = 2;
                    break;
            }
            if (selectedValue != 0)
            {
               /* var list= db.model_name.Where(x => x.column== selectedValue);
                return list; */
            }
            return Ok(selectedValue);
StupidWolf
  • 45,075
  • 17
  • 40
  • 72
0

This code is for awk, but easily adaptable to C/C++/C#

in awk, all indices are "1-based" instead of "0-based" - the leading edge "=" of the reference string is simply pre-shifting the positions. remove that "=" for any 0-based languages`

function __(_) {  #  input - Eng. month names, any casing, min. 3 letters
                  # output - MM : [01-12], zero-padded
    return \
    ((_=toupper(_)) ~ "^[OND]" ? "" : _<_) \
    (index("=ANEBARPRAYUNULUGEPCTOVEC", substr(_ "",_+=_^=_<_,_))/_)
}

The reference string might look odd at first -

the 2nd + 3rd letters of month names constitute a unique set

So OP can input the english name of the months, full or abbreviated, and it'll return a zero-padded 2-digit month number. If you need it to be in integer form, then just scrub out the middle line that performs the padding.

You'll notice only 1 input variable declared and no other temp variables whatsoever - one of awk's major strengths is its extreme agility when it comes to dynamic typing of variables, even for truly illogical operations like taking the "0th-power" of a string variable like

"Boston" ^ 0 

This would seamlessly coerce that variable to a numeric data type, with a new value of 1.

This flexibility enables the recycling and re-using of the input temp variable(s) for any other purpose the moment the original input value(s) is/are no longer needed.

RARE Kpop Manifesto
  • 2,453
  • 3
  • 11