Ottenere il primo e l'ultimo giorno di un mese, utilizzando un determinato oggetto DateTime


196

Voglio ottenere il primo giorno e l'ultimo giorno del mese in cui si trova una determinata data. La data proviene da un valore in un campo dell'interfaccia utente.

Se sto usando un selettore del tempo, potrei dire

var maxDay = dtpAttendance.MaxDate.Day;

Ma sto cercando di ottenerlo da un oggetto DateTime. Quindi se ho questo ...

DateTime dt = DateTime.today;

Come ottenere il primo giorno e l'ultimo giorno del mese da dt?


Non è chiaro cosa stai chiedendo. C'è un singolo valore memorizzato nella _Datevariabile. Quali "min e max" stai cercando di ottenere da quel valore?
David,

viene ridimensionato perché le persone si chiedono perché vorresti fare una simile idea e dove useresti una cosa del genere. Non ci stai dicendo nulla del tuo problema iniziale qui
Mo Patel,

È meglio chiedere cosa vuoi fare? non è così Come vuoi fare? altri usi possono suggerirti correttamente.
Shell,

2
@Chathuranga sai quale sarà la data minima di ogni mese .... ma la domanda è qual è l'ultima data del mese corrente .. puoi ottenere così .. aggiungi 1 mese nella data corrente e meno 1 giorno da quello data .. ora otterrai l'ultima data del tuo mese corrente
Shell,

Risposte:


472

DateTimela struttura archivia un solo valore, non un intervallo di valori. MinValuee MaxValuesono campi statici, che contengono un intervallo di valori possibili per le istanze di DateTimestruttura. Questi campi sono statici e non si riferiscono a un'istanza particolare di DateTime. Si riferiscono al DateTimetipo stesso.

Lettura consigliata: statica (Riferimenti per C #)

AGGIORNAMENTO: Ottenere l'intervallo di mesi:

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);

16
So che posso essere pignoli qui, ma non dovrebbe lastDayofMonthessere firstDayOfMonth.AddMonths(1).AddSeconds(-1);?
Karl Gjertsen,

36
@KarlGjertsen non sei abbastanza esigente :) La soluzione perfetta sarà AddTicks(-1), ma se non ci interessa la parte del tempo e pensiamo solo alla parte della data, allora i giorni funzionano bene
Sergey Berezovskiy

7
Ora che è pignolo! ;-) La domanda non dice come verranno usati i valori, quindi tendo a programmare in modo difensivo.
Karl Gjertsen,

@SergeyBerezovskiy Odio sollevare questo punto ma non perdi le informazioni sul fuso orario quando rinnovi un DateTime come questo? Qualunque informazione sul fuso orario sia stata allegata all'istanza originale di DateTime viene persa quando si crea una nuova istanza come questa.
Marko,

2
@KarlGjertsen, vuoi vedere pignoli ... Io personalmente < firstDayOfNextMonthinvece di <= lastDayOfMonth. In questo modo funzionerà sempre indipendentemente dalla granularità. (Sono sicuro che le zecche andranno bene, ma chissà cosa porterà il futuro ... nanotick?)
adam0101

99

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_AddMethodstato leggermente più veloce rispetto FirstDayOfMonth_NewMethodalla maggior parte delle prove del test. Tuttavia, penso che quest'ultimo abbia un intento leggermente più chiaro e quindi preferisco quello.

LastDayOfMonth_AddMethodera un chiaro perdente contro LastDayOfMonth_AddMethodWithDaysInMonth, LastDayOfMonth_NewMethode LastDayOfMonth_NewMethodWithReuseOfExtMethod. Tra i tre più veloci non c'è molto in esso e quindi dipende dalle tue preferenze personali. Scelgo la chiarezza LastDayOfMonth_NewMethodWithReuseOfExtMethodcon 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.


5
Tuttavia, hai troppo meno credito per i tuoi sforzi. È utile!
Dion V.

2
Grazie @DionV. - è bello essere apprezzato! Le risposte brevi sono ottime quando hai fretta, ma penso che alcune analisi più profonde da seguire siano spesso utili.
WooWaaBob,

Hai perso un'altra alternativa: LastDayOfMonth_AddMethod_SpecialCase(o qualcosa del genere). Aspettando il primo giorno del mese come parametro, penso che il più veloce dovrebbe essere quello che LastDayOfMonth_AddMethodfa! Quindi sarebbe semplice come:return value.AddMonths(1).AddDays(-1);
Andrew

1
Grazie @Andrew ed ecco i miei risultati usando quello: 2835 ms per LastDayOfMonth_SpecialCase (), e; 4685 ms per LastDayOfMonth_AddMethod_SpecialCase (). Il che probabilmente ha senso quando si considera il tempo di creazione della struttura e che la rappresentazione interna di DateTime rende probabilmente l'aggiunta di giorni un'operazione semplice ma l'aggiunta di mesi un algoritmo più complesso.
WooWaaBob,

15

Ottenere l'intervallo di mesi con .Net API (solo un altro modo):

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));

6

" Last day of month" è in realtà " First day of *next* month, minus 1". Quindi, ecco quello che uso, non è necessario il metodo "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);
}

NOTA: il motivo per cui lo uso AddMinutes(-1), non AddDays(-1)qui è perché di solito hai bisogno di queste funzioni di data per i rapporti per un certo periodo di date, e quando crei un rapporto per un periodo, la "data di fine" dovrebbe effettivamente essere qualcosa di simileOct 31 2015 23:59:59 quindi il rapporto funziona correttamente - compresi tutti i dati dell'ultimo giorno del mese.

Cioè in realtà ottieni "l'ultimo momento del mese" qui. Non l'ultimo giorno.

OK, ora sto zitto.


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));

Evita generalmente le risposte solo al codice. Valuta di aggiungere un descriptionche ti aiuti a spiegare il tuo codice. Grazie
MickyD il

3

Qui puoi aggiungere un mese per il primo giorno del mese corrente anziché eliminare 1 giorno da quel giorno.

DateTime now = DateTime.Now;
var startDate = new DateTime(now.Year, now.Month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);

2

Se ti interessa solo la data

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);

Se vuoi risparmiare tempo

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);

Fallirà completamente a dicembre, dudeeeeeee. proverà a creare datetime con il mese "13" e genererà un'eccezione che non esiste tale data.
Pawel,

Da quando dicembre è il 13? Ci sono in totale 12 mesi. Che calendario usi?
Vitaly,

Se la data è dicembre, proverà ad aggiungere un mese, il che comporterà un'eccezione. nuovo DateTime (date.Year, 12+ 1, 1, 0, 0, 0, date.Kind) .AddDays (-1); La sottrazione di un giorno verrà effettuata dopo la creazione del datetime, quindi per dicembre fallirà quando si prova la data per il mese "13". Puoi provarlo.
Pawel,

1

La risposta accettata qui non tiene conto del tipo di istanza DateTime. Ad esempio, se l'istanza DateTime originale era un tipo UTC, creando una nuova istanza DateTime creerai un'istanza Unknown Kind che verrà quindi trattata come ora locale in base alle impostazioni del server. Pertanto, il modo più corretto per ottenere la prima e l'ultima data del mese sarebbe questo:

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

In questo modo viene conservata l'istanza Kind of the DateTime originale.


1

Prova questo:

string strDate = DateTime.Now.ToString("MM/01/yyyy");

1

L'ho usato nella mia sceneggiatura (funziona per me) ma avevo bisogno di una data completa senza la necessità di tagliarla solo alla data e senza tempo.

public DateTime GetLastDayOfTheMonth()
{
    int daysFromNow = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month) - (int)DateTime.Now.Day;
    return DateTime.Now.AddDays(daysFromNow);
}

1

Prova questo. Calcola fondamentalmente il numero di giorni trascorsi DateTime.Now, quindi ne sottrae uno da quello e utilizza il nuovo valore per trovare il primo del mese corrente. Da lì lo usa DateTimee lo usa .AddMonths(-1)per ottenere il primo del mese precedente.

Ottenere l'ultimo giorno del mese scorso fa sostanzialmente la stessa cosa tranne che aggiunge uno al numero di giorni del mese e sottrae quel valore da DateTime.Now.AddDays, dandoti l'ultimo giorno del mese precedente.

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);

0

Per la cultura persiana

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);

0

Puoi farlo

DateTime dt = DateTime.Now; 
DateTime firstDayOfMonth = new DateTime(dt.Year, date.Month, 1);
DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);

-1

modo semplice per farlo

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();

Questo codice non viene compilato a causa di "new 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)));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.