The code presented is pretty fast, but it isn't "optimal", and it can throw exceptions if the month number is invalid when getting the month name, and likewise if the month name is invalid when getting the month number.
Using the info in the DateTimeFormatInfo class, from the CultureInfo 's DateTimeFormat property, the month names are already available. Get them directly without the overhead of the .ToString() and Convert.ToDateTime()
The code below is faster (100,000 iterations, in seconds)
Original: DateTime.ToString = 0.1047346
Original: Convert.ToDateTime = 0.4926248
New: initializing new MonthsHelper() = 0.3544563
New: MonthsHelper.FullMonthName = 0.0615322
New: MonthsHelper.FullMonthNameToNumber = 0.0207368
Given these times are for 100K iterations, the differences are not really significant, but this may show some ideas for similar problems where the computation is expensive.
MonthsHelper.cs:
namespace Months
{
using System;
using System.Collections.Generic;
using System.Globalization;
public class MonthsHelper
{
public MonthsHelper()
: this(CultureInfo.CurrentCulture)
{
}
public MonthsHelper(CultureInfo culture)
: this((culture ?? CultureInfo.CurrentCulture).DateTimeFormat)
{
}
public MonthsHelper(DateTimeFormatInfo dateFormatInfo)
{
DateFormatInfo = dateFormatInfo ?? CultureInfo.CurrentCulture.DateTimeFormat;
for (int m = 0; m < MonthArrayCount; m++)
{
var name = DateFormatInfo.MonthNames[m];
FullMonthNameToNumberMap[name] = m + 1;
name = DateFormatInfo.AbbreviatedMonthNames[m];
ShortMonthNameToNumberMap[name] = m + 1;
name = DateFormatInfo.MonthGenitiveNames[m];
FullMonthGenitiveNameToNumberMap[name] = m + 1;
name = DateFormatInfo.AbbreviatedMonthGenitiveNames[m];
ShortMonthGenitiveNameToNumberMap[name] = m + 1;
}
}
private const int MonthArrayCount = 13;
private readonly DateTimeFormatInfo DateFormatInfo;
private readonly Dictionary<string, int> FullMonthNameToNumberMap = new Dictionary<string, int>(MonthArrayCount, StringComparer.Ordinal);
private readonly Dictionary<string, int> ShortMonthNameToNumberMap = new Dictionary<string, int>(MonthArrayCount, StringComparer.Ordinal);
private readonly Dictionary<string, int> FullMonthGenitiveNameToNumberMap = new Dictionary<string, int>(MonthArrayCount, StringComparer.Ordinal);
private readonly Dictionary<string, int> ShortMonthGenitiveNameToNumberMap = new Dictionary<string, int>(MonthArrayCount, StringComparer.Ordinal);
public string FullMonthName(int monthNumber)
{
if (monthNumber < 1 || monthNumber > 12)
return string.Empty;
return DateFormatInfo.MonthNames[monthNumber - 1];
}
public string ShortMonthName(int monthNumber)
{
if (monthNumber < 1 || monthNumber > 12)
return string.Empty;
return DateFormatInfo.AbbreviatedMonthNames[monthNumber - 1];
}
public string FullMonthGenitiveName(int monthNumber)
{
if (monthNumber < 1 || monthNumber > 12)
return string.Empty;
return DateFormatInfo.MonthGenitiveNames[monthNumber - 1];
}
public string ShortMonthGenitiveName(int monthNumber)
{
if (monthNumber < 1 || monthNumber > 12)
return string.Empty;
return DateFormatInfo.AbbreviatedMonthGenitiveNames[monthNumber - 1];
}
public int MonthNameToNumber(string monthName)
{
int monthNumber;
if (!FullMonthNameToNumberMap.TryGetValue(monthName, out monthNumber) &&
!ShortMonthNameToNumberMap.TryGetValue(monthName, out monthNumber) &&
!FullMonthGenitiveNameToNumberMap.TryGetValue(monthName, out monthNumber) &&
!ShortMonthGenitiveNameToNumberMap.TryGetValue(monthName, out monthNumber))
{
monthNumber = -1;
}
return monthNumber;
}
public int FullMonthNameToNumber(string monthName)
{
int monthNumber;
if (!FullMonthNameToNumberMap.TryGetValue(monthName, out monthNumber))
{
monthNumber = -1;
}
return monthNumber;
}
public int ShortMonthNameToNumber(string monthName)
{
int monthNumber;
if (!ShortMonthNameToNumberMap.TryGetValue(monthName, out monthNumber))
{
monthNumber = -1;
}
return monthNumber;
}
public int FullMonthGenitiveNameToNumber(string monthName)
{
int monthNumber;
if (!FullMonthGenitiveNameToNumberMap.TryGetValue(monthName, out monthNumber))
{
monthNumber = -1;
}
return monthNumber;
}
public int ShortMonthGenitiveNameToNumber(string monthName)
{
int monthNumber;
if (!ShortMonthGenitiveNameToNumberMap.TryGetValue(monthName, out monthNumber))
{
monthNumber = -1;
}
return monthNumber;
}
}
}
Usage and timing in Program.cs:
using System;
using System.Diagnostics;
namespace Months
{
class Program
{
static readonly string[] FullMonths = { "null", "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" };
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();
const int repeat = 100000;
for (int r = 0; r < repeat; r++)
{
for (int m = 1; m <= 12; m++)
{
DateTime dtDate = new DateTime(2000, m, 1);
var sMonthFullName = dtDate.ToString("MMMM");
}
}
sw.Stop();
Console.WriteLine("Original: DateTime.ToString = {0}", TimeSpan.FromTicks(sw.ElapsedTicks));
sw.Restart();
for (int r = 0; r < repeat; r++)
{
for (int m = 1; m <= 12; m++)
{
DateTime dtDate = new DateTime(2000, m, 1);
var sMonthName = FullMonths[m];
var iMonthNo = Convert.ToDateTime("01-" + sMonthName + "-2011").Month;
}
}
sw.Stop();
Console.WriteLine("Original: Convert.ToDateTime = {0}", TimeSpan.FromTicks(sw.ElapsedTicks));
MonthsHelper helper = new MonthsHelper();
sw.Restart();
for (int r = 0; r < repeat; r++)
{
helper = new MonthsHelper();
}
sw.Stop();
Console.WriteLine("New: initializing new MonthsHelper() = {0}", TimeSpan.FromTicks(sw.ElapsedTicks));
sw.Restart();
for (int r = 0; r < repeat; r++)
{
for (int m = 1; m <= 12; m++)
{
var sMonthFullName = helper.FullMonthName(m);
}
}
sw.Stop();
Console.WriteLine("New: MonthsHelper.FullMonthName = {0}", TimeSpan.FromTicks(sw.ElapsedTicks));
sw.Restart();
for (int r = 0; r < repeat; r++)
{
for (int m = 1; m <= 12; m++)
{
var sMonthName = FullMonths[m];
var iMonthNo = helper.FullMonthNameToNumber(sMonthName);
}
}
sw.Stop();
Console.WriteLine("New: MonthsHelper.FullMonthNameToNumber = {0}", TimeSpan.FromTicks(sw.ElapsedTicks));
Console.ReadLine();
}
}
}
|