Questo è più un lungo commento sulle risposte di @Sergey e @ Steffen. Avendo scritto io stesso un codice simile in passato, ho deciso di controllare ciò che era più performante , ricordando che anche la chiarezza è importante.
Risultato
Ecco un esempio di risultato dell'esecuzione di test per 10 milioni di iterazioni:
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()
Codice
Ho usato LINQPad 4 (in modalità Programma C #) per eseguire i test con l'ottimizzazione del compilatore attivata. Ecco il codice testato considerato come metodi di estensione per chiarezza e praticità:
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();
}
Analisi
Sono stato sorpreso da alcuni di questi risultati.
Sebbene non ci sia molto in esso, è FirstDayOfMonth_AddMethod
stato leggermente più veloce rispetto FirstDayOfMonth_NewMethod
alla maggior parte delle prove del test. Tuttavia, penso che quest'ultimo abbia un intento leggermente più chiaro e quindi preferisco quello.
LastDayOfMonth_AddMethod
era un chiaro perdente contro LastDayOfMonth_AddMethodWithDaysInMonth
, LastDayOfMonth_NewMethod
e LastDayOfMonth_NewMethodWithReuseOfExtMethod
. Tra i tre più veloci non c'è molto in esso e quindi dipende dalle tue preferenze personali. Scelgo la chiarezza LastDayOfMonth_NewMethodWithReuseOfExtMethod
con il suo riutilizzo di un altro metodo di estensione utile. IMHO il suo intento è più chiaro e sono disposto ad accettare il piccolo costo della prestazione.
LastDayOfMonth_SpecialCase
presume che tu stia fornendo il primo del mese nel caso speciale in cui potresti aver già calcolato quella data e utilizza il metodo add con DateTime.DaysInMonth
per ottenere il risultato. Questo è più veloce delle altre versioni, come ti aspetteresti, ma a meno che tu non abbia un disperato bisogno di velocità, non vedo il punto di avere questo caso speciale nel tuo arsenale.
Conclusione
Ecco una classe di metodi di estensione con le mie scelte e in generale accordo con @Steffen credo:
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 sei arrivato così lontano, grazie per il tempo! È stato divertente: ¬). Si prega di commentare se avete altri suggerimenti per questi algoritmi.
_Date
variabile. Quali "min e max" stai cercando di ottenere da quel valore?