Uzyskiwanie pierwszego i ostatniego dnia miesiąca przy użyciu danego obiektu DateTime

196

Chcę uzyskać pierwszy dzień i ostatni dzień miesiąca, w którym znajduje się dana data. Data pochodzi z wartości w polu interfejsu użytkownika.

Jeśli używam próbnika czasu, mogę powiedzieć

var maxDay = dtpAttendance.MaxDate.Day;

Ale próbuję uzyskać go z obiektu DateTime. Więc jeśli mam to ...

DateTime dt = DateTime.today;

Jak zdobyć pierwszy dzień i ostatni dzień miesiąca dt?

CHAM
źródło
Nie jest jasne, o co pytasz. W zmiennej przechowywana jest jedna wartość _Date. Jakie „min i maks” próbujesz uzyskać z tej wartości?
David
robi się to zlekceważone, ponieważ ludzie zastanawiają się, dlaczego chcesz się tak zastanowić i gdzie byś tego użył. Nie mówisz nam nic o swoim pierwszym problemie tutaj
Mo Patel
Lepiej zapytać, co chcesz zrobić? nie tak Jak chcesz to zrobić? może inne użycie może sugerować poprawnie.
Shell
2
@Chathuranga wiesz, jaka będzie minimalna data każdego miesiąca .... ale pytanie brzmi, jaka jest ostatnia data bieżącego miesiąca .. możesz dostać w ten sposób .. dodaj 1 miesiąc w bieżącej dacie i minus 1 dzień od tego data .. teraz dostaniesz ostatnią datę ur bieżącego miesiąca
Shell

Odpowiedzi:

475

DateTimestruktura przechowuje tylko jedną wartość, a nie zakres wartości. MinValuei MaxValuesą polami statycznymi, które zawierają zakres możliwych wartości dla instancji DateTimekonstrukcji. Te pola są statyczne i nie odnoszą się do konkretnego wystąpienia DateTime. Dotyczą one DateTimesamego pisania.

Sugerowana lektura: statyczna (C # Odniesienie)

AKTUALIZACJA: Pobieranie zakresu miesięcy:

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
Siergiej Berezowski
źródło
16
Wiem, że jestem wybredna tutaj, ale nie powinno lastDayofMonthbyć firstDayOfMonth.AddMonths(1).AddSeconds(-1);?
Karl Gjertsen
36
@KarlGjertsen nie jesteś wystarczająco wybredny :) Idealnym rozwiązaniem będzie AddTicks(-1), ale jeśli nie dbamy o część czasu i myślimy tylko o części, to dni będą w porządku
Sergey Berezovskiy
7
To jest wybredne! ;-) Pytanie nie mówi, w jaki sposób mają być używane wartości, więc zwykle koduję defensywnie.
Karl Gjertsen
@SergeyBerezovskiy Nienawidzę podnosić tego punktu, ale czy nie tracisz informacji o strefie czasowej, kiedy zaczynasz w ten sposób DateTime? Wszelkie informacje o strefie czasowej dołączone do oryginalnej instancji DateTime zostaną utracone po utworzeniu takiej nowej instancji.
Marko
2
@KarlGjertsen, chcesz zobaczyć wybredne ... Ja osobiście robię < firstDayOfNextMonthzamiast <= lastDayOfMonth. W ten sposób zawsze będzie działać bez względu na szczegółowość. (Jestem pewien, że kleszcze będą w porządku, ale kto wie, co przyniesie przyszłość ... nanotiksy?)
adam0101
99

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_AddMethodbyło nieco szybciej niż FirstDayOfMonth_NewMethodw większości przebiegów testu. Myślę jednak, że ten drugi cel jest nieco jaśniejszy i dlatego mam na to ochotę.

LastDayOfMonth_AddMethodbyło jasne, przegrany przed LastDayOfMonth_AddMethodWithDaysInMonth, LastDayOfMonth_NewMethodi LastDayOfMonth_NewMethodWithReuseOfExtMethod. Między najszybszymi trzema nie ma w tym nic wielkiego, więc sprowadza się to do twoich osobistych preferencji. Wybieram przejrzystość LastDayOfMonth_NewMethodWithReuseOfExtMethodprzy ponownym użyciu innej użytecznej metody rozszerzenia. IMHO jego cel jest wyraźniejszy i jestem gotów zaakceptować niewielki koszt wydajności.

LastDayOfMonth_SpecialCasezakł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.DaysInMonthaby 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.

WooWaaBob
źródło
5
Masz jednak o wiele za mało środków na swój wysiłek. To jest użyteczne!
Dion V.
2
Dzięki @DionV. - miło być docenionym! Krótkie odpowiedzi są świetne, gdy się spieszysz, ale myślę, że przydaje się głębsza analiza.
WooWaaBob,
Przegapiłeś inną alternatywę: LastDayOfMonth_AddMethod_SpecialCase(lub coś takiego). Spodziewając się pierwszego dnia miesiąca jako parametru, myślę, że najszybszym powinno być to, co LastDayOfMonth_AddMethodrobi! To byłoby tak proste, jak:return value.AddMonths(1).AddDays(-1);
Andrew
1
Dzięki @Andrew i oto moje wyniki przy użyciu: 2835 ms dla LastDayOfMonth_SpecialCase () i; 4685 ms dla LastDayOfMonth_AddMethod_SpecialCase (). Co prawdopodobnie ma sens, biorąc pod uwagę czas utworzenia struktury i że wewnętrzna reprezentacja DateTime prawdopodobnie sprawia, że ​​dodawanie dni jest prostą operacją, a dodawanie miesięcy bardziej złożonym algorytmem.
WooWaaBob,
15

Uzyskiwanie zakresu miesięcy za pomocą .Net API (po prostu inny sposób):

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month));
Steffen Mangold
źródło
6

Last day of month” to „ First day of *next* month, minus 1”. Oto, czego używam, bez potrzeby stosowania metody „DaysInMonth”:

public static DateTime FirstDayOfMonth(this DateTime value)
{
    return new DateTime(value.Year, value.Month, 1);
}

public static DateTime LastDayOfMonth(this DateTime value)
{
    return value.FirstDayOfMonth()
        .AddMonths(1)
        .AddMinutes(-1);
}

UWAGA: Powodem, którego używam AddMinutes(-1), a nie AddDays(-1)tutaj, jest to, że zwykle potrzebujesz tych funkcji daty do raportowania dla pewnego okresu, a kiedy tworzysz raport dla okresu, „data końcowa” powinna być podobna do Oct 31 2015 23:59:59tego, aby raport działał poprawnie - w tym wszystkie dane z ostatniego dnia miesiąca.

To znaczy, że faktycznie dostajesz tutaj „ostatnią chwilę miesiąca”. Nie ostatni dzień.

OK, teraz się zamknę.

jazzcat
źródło
5
DateTime dCalcDate = DateTime.Now;
dtpFromEffDate.Value = new DateTime(dCalcDate.Year, dCalcDate.Month, 1);
dptToEffDate.Value = new DateTime(dCalcDate.Year, dCalcDate.Month, DateTime.DaysInMonth(dCalcDate.Year, dCalcDate.Month));
sphaze
źródło
Generalnie unikaj odpowiedzi tylko na kod. Zastanów się nad dodaniem descriptionkodu, który pomoże wyjaśnić kod. Dzięki
MickyD,
3

Tutaj możesz dodać jeden miesiąc dla pierwszego dnia bieżącego miesiąca, niż usunąć 1 dzień z tego dnia.

DateTime now = DateTime.Now;
var startDate = new DateTime(now.Year, now.Month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);
Chirag Thakar
źródło
2

Jeśli zależy Ci tylko na dacie

var firstDay = new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind);
var lastDay = new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind).AddMonths(1).AddDays(-1);

Jeśli chcesz zaoszczędzić czas

var firstDay = new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind);
var lastDay = new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind).AddMonths(1).AddDays(-1);
Witalij
źródło
To całkowicie zawiedzie w grudniu dudeeeeeee. spróbuje utworzyć datę / godzinę z miesiącem „13” i zgłasza wyjątek, że nie ma takiej daty.
Paweł
Od kiedy 13 grudnia? Łącznie jest 12 miesięcy. Z jakiego kalendarza korzystasz?
Witalij
Jeśli masz datę grudnia, spróbuje dodać jeden miesiąc, co spowoduje wyjątek. nowa data i godzina (data, rok, 12+ 1, 1, 0, 0, 0, data. Rodzaj) .AddDays (-1); Odejmowanie jednego dnia od tego zostanie wykonane po utworzeniu daty i godziny, więc w grudniu nie powiedzie się podczas próby daty miesiąca „13”. Możesz tego spróbować.
Paweł
1

Przyjęta tutaj odpowiedź nie uwzględnia rodzaju instancji DateTime. Na przykład, jeśli twoja oryginalna instancja DateTime była rodzajem UTC, to tworząc nową instancję DateTime, utworzysz instancję Nieznany rodzaj, która będzie następnie traktowana jako czas lokalny na podstawie ustawień serwera. Dlatego bardziej właściwym sposobem uzyskania pierwszej i ostatniej daty miesiąca będzie:

var now = DateTime.UtcNow;
var first = now.Date.AddDays(-(now.Date.Day - 1));
var last = first.AddMonths(1).AddTicks(-1);

W ten sposób zachowany zostanie oryginalny rodzaj instancji DateTime.

Marko
źródło
1

Spróbuj tego:

string strDate = DateTime.Now.ToString("MM/01/yyyy");
Marai
źródło
1

Użyłem tego w swoim skrypcie (działa dla mnie), ale potrzebowałem pełnej daty bez potrzeby przycinania jej tylko do daty i bez godziny.

public DateTime GetLastDayOfTheMonth()
{
    int daysFromNow = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month) - (int)DateTime.Now.Day;
    return DateTime.Now.AddDays(daysFromNow);
}
Maarten Frouws
źródło
1

Spróbuj. Zasadniczo oblicza liczbę dni, które minęły DateTime.Now, a następnie odejmuje jeden od tego i używa nowej wartości do znalezienia pierwszego z bieżącego miesiąca. Stamtąd wykorzystuje to DateTimei wykorzystuje, .AddMonths(-1)aby uzyskać pierwszy z poprzedniego miesiąca.

Uzyskiwanie ostatniego dnia poprzedniego miesiąca robi w zasadzie to samo, z tym wyjątkiem, że dodaje jeden do liczby dni w miesiącu i odejmuje tę wartość DateTime.Now.AddDays, dając ci ostatni dzień poprzedniego miesiąca.

int NumberofDays = DateTime.Now.Day;
int FirstDay = NumberofDays - 1;
int LastDay = NumberofDays + 1;
DateTime FirstofThisMonth = DateTime.Now.AddDays(-FirstDay);
DateTime LastDayOfLastMonth = DateTime.Now.AddDays(-LastDay);
DateTime CheckLastMonth = FirstofThisMonth.AddMonths(-1);
Carl Perumal
źródło
0

Dla kultury perskiej

PersianCalendar pc = new PersianCalendar();            

var today = pc.GetDayOfMonth(DateTime.Now);
var firstDayOfMonth = pc.GetDayOfMonth(DateTime.Now.AddDays(-(today-1)));
var lastDayOfMonth = pc.GetDayOfMonth(DateTime.Now.AddMonths(1).AddDays(-today));            
Console.WriteLine("First day "+ firstDayOfMonth);
Console.WriteLine("Last day " + lastDayOfMonth);
Mohammad Daliri
źródło
0

Możesz to zrobić

DateTime dt = DateTime.Now; 
DateTime firstDayOfMonth = new DateTime(dt.Year, date.Month, 1);
DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
balon
źródło
-1

łatwy sposób to zrobić

Begin = new DateTime(DateTime.Now.Year, DateTime.Now.Month,1).ToShortDateString();
End = new DataFim.Text = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month)).ToShortDateString();
David Ferreira
źródło
Ten kod nie kompiluje się z powodu „nowego DataFim.Text”.
Wazner
-1
DateTime dCalcDate = DateTime.Now;
var startDate = new DateTime(Convert.ToInt32(Year), Convert.ToInt32(Month), 1);
var endDate = new DateTime(Convert.ToInt32(Year), Convert.ToInt32(Month), DateTime.DaysInMonth((Convert.ToInt32(Year)), Convert.ToInt32(Month)));
Shubham Batra
źródło