Este é mais um longo comentário sobre as respostas de @Sergey e @ Steffen. Tendo escrito um código semelhante no passado, decidi verificar o que tinha melhor desempenho , lembrando que a clareza também é importante.
Resultado
Aqui está um exemplo de resultado da execução de teste para 10 milhões de iterações:
2257 ms for FirstDayOfMonth_AddMethod()
2406 ms for FirstDayOfMonth_NewMethod()
6342 ms for LastDayOfMonth_AddMethod()
4037 ms for LastDayOfMonth_AddMethodWithDaysInMonth()
4160 ms for LastDayOfMonth_NewMethod()
4212 ms for LastDayOfMonth_NewMethodWithReuseOfExtMethod()
2491 ms for LastDayOfMonth_SpecialCase()
Código
Usei o LINQPad 4 (no modo de programa C #) para executar os testes com a otimização do compilador ativada. Aqui está o código testado consignado como métodos de extensão para maior clareza e conveniência:
public static class DateTimeDayOfMonthExtensions
{
public static DateTime FirstDayOfMonth_AddMethod(this DateTime value)
{
return value.Date.AddDays(1 - value.Day);
}
public static DateTime FirstDayOfMonth_NewMethod(this DateTime value)
{
return new DateTime(value.Year, value.Month, 1);
}
public static DateTime LastDayOfMonth_AddMethod(this DateTime value)
{
return value.FirstDayOfMonth_AddMethod().AddMonths(1).AddDays(-1);
}
public static DateTime LastDayOfMonth_AddMethodWithDaysInMonth(this DateTime value)
{
return value.Date.AddDays(DateTime.DaysInMonth(value.Year, value.Month) - value.Day);
}
public static DateTime LastDayOfMonth_SpecialCase(this DateTime value)
{
return value.AddDays(DateTime.DaysInMonth(value.Year, value.Month) - 1);
}
public static int DaysInMonth(this DateTime value)
{
return DateTime.DaysInMonth(value.Year, value.Month);
}
public static DateTime LastDayOfMonth_NewMethod(this DateTime value)
{
return new DateTime(value.Year, value.Month, DateTime.DaysInMonth(value.Year, value.Month));
}
public static DateTime LastDayOfMonth_NewMethodWithReuseOfExtMethod(this DateTime value)
{
return new DateTime(value.Year, value.Month, value.DaysInMonth());
}
}
void Main()
{
Random rnd = new Random();
DateTime[] sampleData = new DateTime[10000000];
for(int i = 0; i < sampleData.Length; i++) {
sampleData[i] = new DateTime(1970, 1, 1).AddDays(rnd.Next(0, 365 * 50));
}
GC.Collect();
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].FirstDayOfMonth_AddMethod();
}
string.Format("{0} ms for FirstDayOfMonth_AddMethod()", sw.ElapsedMilliseconds).Dump();
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].FirstDayOfMonth_NewMethod();
}
string.Format("{0} ms for FirstDayOfMonth_NewMethod()", sw.ElapsedMilliseconds).Dump();
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].LastDayOfMonth_AddMethod();
}
string.Format("{0} ms for LastDayOfMonth_AddMethod()", sw.ElapsedMilliseconds).Dump();
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].LastDayOfMonth_AddMethodWithDaysInMonth();
}
string.Format("{0} ms for LastDayOfMonth_AddMethodWithDaysInMonth()", sw.ElapsedMilliseconds).Dump();
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].LastDayOfMonth_NewMethod();
}
string.Format("{0} ms for LastDayOfMonth_NewMethod()", sw.ElapsedMilliseconds).Dump();
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].LastDayOfMonth_NewMethodWithReuseOfExtMethod();
}
string.Format("{0} ms for LastDayOfMonth_NewMethodWithReuseOfExtMethod()", sw.ElapsedMilliseconds).Dump();
for(int i = 0; i < sampleData.Length; i++) {
sampleData[i] = sampleData[i].FirstDayOfMonth_AddMethod();
}
GC.Collect();
sw.Restart();
for(int i = 0; i < sampleData.Length; i++) {
DateTime test = sampleData[i].LastDayOfMonth_SpecialCase();
}
string.Format("{0} ms for LastDayOfMonth_SpecialCase()", sw.ElapsedMilliseconds).Dump();
}
Análise
Fiquei surpreso com alguns desses resultados.
Embora não haja muito, FirstDayOfMonth_AddMethod
foi um pouco mais rápido do que FirstDayOfMonth_NewMethod
na maioria das execuções do teste. No entanto, acho que o último tem uma intenção um pouco mais clara e, portanto, tenho uma preferência por isso.
LastDayOfMonth_AddMethod
foi um perdedor claro contra LastDayOfMonth_AddMethodWithDaysInMonth
, LastDayOfMonth_NewMethod
e LastDayOfMonth_NewMethodWithReuseOfExtMethod
. Entre os três mais rápidos, não há muito, e isso se resume a sua preferência pessoal. Escolho a clareza LastDayOfMonth_NewMethodWithReuseOfExtMethod
com a reutilização de outro método de extensão útil. IMHO sua intenção é mais clara e estou disposto a aceitar o pequeno custo de desempenho.
LastDayOfMonth_SpecialCase
assume que você está fornecendo o primeiro dia do mês no caso especial em que você já pode ter calculado essa data e que usa o método add com DateTime.DaysInMonth
para obter o resultado. Isso é mais rápido do que as outras versões, como seria de esperar, mas, a menos que você esteja precisando desesperadamente de velocidade, não vejo sentido em ter esse caso especial em seu arsenal.
Conclusão
Aqui está uma classe de método de extensão com minhas escolhas e, de acordo com o @Steffen, acredito:
public static class DateTimeDayOfMonthExtensions
{
public static DateTime FirstDayOfMonth(this DateTime value)
{
return new DateTime(value.Year, value.Month, 1);
}
public static int DaysInMonth(this DateTime value)
{
return DateTime.DaysInMonth(value.Year, value.Month);
}
public static DateTime LastDayOfMonth(this DateTime value)
{
return new DateTime(value.Year, value.Month, value.DaysInMonth());
}
}
Se você chegou até aqui, obrigado pelo tempo! Tem sido divertido: ¬). Por favor, comente se você tiver outras sugestões para esses algoritmos.
_Date
variável. Que "mínimo e máximo" você está tentando obter desse valor?