To bardziej długi komentarz do odpowiedzi @Sergey i @ Steffen. Po napisaniu podobnego kodu w przeszłości postanowiłem sprawdzić, co było najbardziej wydajne , pamiętając jednocześnie o tym, że jasność jest ważna.
Wynik
Oto przykładowy przebieg testu dla 10 milionów iteracji:
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()
Kod
Użyłem LINQPada 4 (w trybie programu C #) do uruchomienia testów z włączoną optymalizacją kompilatora. Oto przetestowany kod uwzględniony jako metody rozszerzenia dla przejrzystości i wygody:
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();
}
Analiza
Byłem zaskoczony niektórymi z tych wyników.
Chociaż nie ma w nim wiele, FirstDayOfMonth_AddMethod
było nieco szybciej niż FirstDayOfMonth_NewMethod
w większości przebiegów testu. Myślę jednak, że ten drugi cel jest nieco jaśniejszy i dlatego mam na to ochotę.
LastDayOfMonth_AddMethod
było jasne, przegrany przed LastDayOfMonth_AddMethodWithDaysInMonth
, LastDayOfMonth_NewMethod
i LastDayOfMonth_NewMethodWithReuseOfExtMethod
. Między najszybszymi trzema nie ma w tym nic wielkiego, więc sprowadza się to do twoich osobistych preferencji. Wybieram przejrzystość LastDayOfMonth_NewMethodWithReuseOfExtMethod
przy ponownym użyciu innej użytecznej metody rozszerzenia. IMHO jego cel jest wyraźniejszy i jestem gotów zaakceptować niewielki koszt wydajności.
LastDayOfMonth_SpecialCase
zakłada się, że podajesz pierwszy dzień miesiąca w specjalnym przypadku, w którym być może już obliczyłeś tę datę, i używa metody add z, DateTime.DaysInMonth
aby uzyskać wynik. Jest to szybsze niż inne wersje, jak można się spodziewać, ale chyba, że jesteś w rozpaczliwej potrzebie szybkości, nie widzę sensu, aby mieć ten specjalny przypadek w twoim arsenale.
Wniosek
Oto klasa metod rozszerzenia z moimi wyborami i ogólnie w zgodzie z @Steffen Wierzę:
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());
}
}
Jeśli masz tak daleko, dziękuję za czas! Było fajnie: ¬). Proszę o komentarz, jeśli masz jakieś inne sugestie dotyczące tych algorytmów.
_Date
. Jakie „min i maks” próbujesz uzyskać z tej wartości?