Obtendo o primeiro e o último dia do mês, usando um determinado objeto DateTime

196

Quero obter o primeiro dia e o último dia do mês em que uma determinada data se encontra. A data vem de um valor em um campo da interface do usuário.

Se eu estiver usando um seletor de horas, eu poderia dizer

var maxDay = dtpAttendance.MaxDate.Day;

Mas estou tentando obtê-lo de um objeto DateTime. Então, se eu tiver isso ...

DateTime dt = DateTime.today;

Como obter o primeiro dia e o último dia do mês dt?

cafajeste
fonte
Não está claro o que você está perguntando. Há um único valor armazenado na _Datevariável. Que "mínimo e máximo" você está tentando obter desse valor?
David
está ficando com voto negativo, porque as pessoas estão se perguntando por que você gostaria de pensar assim e onde você usaria isso. Você não está nos dizendo nada sobre o seu problema inicial aqui
Mo Patel
É melhor perguntar o que você quer fazer? não assim Como você quer fazer? outro uso pode sugerir você corretamente.
Shell
2
@Chathuranga você sabe qual será a data mínima de qualquer mês .... mas a questão é qual é a última data do mês atual .. você pode ficar assim .. adicione 1 mês na data atual e menos 1 dia a partir dessa data date .. agora vc terá a última data do seu mês atual
Shell

Respostas:

472

DateTimeestrutura armazena apenas um valor, não intervalo de valores. MinValuee MaxValuesão campos estáticos, que mantêm o intervalo de valores possíveis para instâncias da DateTimeestrutura. Esses campos são estáticos e não estão relacionados a uma instância específica de DateTime. Eles se relacionam com o DateTimepróprio tipo.

Leitura sugerida: estática (Referência de C #)

UPDATE: Obtendo o intervalo do mês:

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
Sergey Berezovskiy
fonte
16
Eu sei que estou sendo exigente aqui, mas não deveria lastDayofMonthser firstDayOfMonth.AddMonths(1).AddSeconds(-1);?
Karl Gjertsen
36
@KarlGjertsen você não é exigente o suficiente :) aperfeiçoe solução será AddTicks(-1), mas se não se preocupam com parte tempo e pensar só em parte data, em seguida, dias de trabalho muito bem
Sergey Berezovskiy
7
Agora isso está sendo exigente! ;-) A pergunta não diz como os valores serão usados, então eu tendem a codificar defensivamente.
Karl Gjertsen
@SergeyBerezovskiy Detestaria levantar esse ponto, mas você não perde as informações de fuso horário quando cria um DateTime como esse? Qualquer informação de fuso horário anexada à instância original do DateTime é perdida quando você cria uma nova instância como esta.
26417 Marko
2
@KarlGjertsen, você quer ver exigente ... Pessoalmente, em < firstDayOfNextMonthvez de <= lastDayOfMonth. Dessa forma, sempre funcionará independentemente da granularidade. (Eu tenho certeza que os carrapatos vai ficar bem, mas quem sabe o que o futuro traz ... nanoticks?)
adam0101
99

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_AddMethodfoi um pouco mais rápido do que FirstDayOfMonth_NewMethodna 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_AddMethodfoi um perdedor claro contra LastDayOfMonth_AddMethodWithDaysInMonth, LastDayOfMonth_NewMethode 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_NewMethodWithReuseOfExtMethodcom 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.

WooWaaBob
fonte
5
Você tem muito menos crédito pelo seu esforço. É útil!
Dion V.
2
Obrigado @DionV. - é bom ser apreciado! Respostas curtas são ótimas quando você está com pressa, mas acho que algumas análises mais profundas a seguir geralmente são úteis.
WooWaaBob
Você perdeu outra alternativa: LastDayOfMonth_AddMethod_SpecialCase(ou algo parecido). Esperando o primeiro dia do mês como parâmetro, acho que o mais rápido deve ser o que LastDayOfMonth_AddMethodfaz! De modo que seria tão simples como:return value.AddMonths(1).AddDays(-1);
Andrew
1
Obrigado @Andrew e aqui estão meus resultados usando isso: 2835 ms para LastDayOfMonth_SpecialCase () e; 4685 ms para LastDayOfMonth_AddMethod_SpecialCase (). O que provavelmente faz sentido ao considerar o tempo de criação da estrutura e que a representação interna do DateTime provavelmente torna a adição de dias uma operação simples, mas a adição de meses um algoritmo mais complexo.
WooWaaBob 04/07/19
15

Obtendo o intervalo de meses com a API .Net (apenas outra maneira):

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
fonte
6

" Last day of month" é realmente " First day of *next* month, minus 1". Então, aqui está o que eu uso, não há necessidade do método "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: O uso razão que eu AddMinutes(-1), não AddDays(-1)aqui é porque geralmente você precisa destas funções de data para relatar para alguma data-período, e quando você constrói um relatório por um período, a "data final" deve realmente ser algo como Oct 31 2015 23:59:59que o seu relatório funciona corretamente - incluindo todos os dados do último dia do mês.

Ou seja, você realmente recebe o "último momento do mês" aqui. Não é o último dia.

OK, eu vou calar a boca agora.

jazzcat
fonte
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));
esfolar
fonte
Geralmente evite respostas somente de código. Considere adicionar um descriptionque ajude a explicar seu código. Graças
MickyD
3

Aqui você pode adicionar um mês para o primeiro dia do mês atual e excluir um dia desse dia.

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

Se você se importa apenas com a 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 você quer economizar 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);
Vitaly
fonte
Vai falhar completamente em dezembro, cara. ele tentará criar datetime com o mês "13" e lançará a exceção de que não existe essa data.
Pawel
Desde quando é 13 de dezembro? Há um total de 12 meses. Que calendário você usa?
Vitaly
Se sua data for dezembro, ele tentará adicionar um mês, o que resultará em exceção. new DateTime (date.Year, 12+ 1, 1, 0, 0, 0, date.Kind) .AddDays (-1); Subtrair um dia disso será feito após a criação da data e hora; portanto, para dezembro, ocorrerá falha ao tentar a data do mês "13". Você pode experimentá-lo.
Pawel
1

A resposta aceita aqui não leva em consideração a instância Kind of the DateTime. Por exemplo, se sua instância original de DateTime era um tipo UTC, criando uma nova instância de DateTime, você criará uma instância de Tipo desconhecido, que será tratada como hora local com base nas configurações do servidor. Portanto, a maneira mais adequada de obter a primeira e a última data do mês seria:

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

Dessa forma, o tipo original da instância DateTime é preservado.

Marko
fonte
1

Tente este:

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

Usei isso no meu script (funciona para mim), mas precisava de uma data completa sem a necessidade de apará-la apenas para a data e sem tempo.

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

Faça uma tentativa. Basicamente, calcula o número de dias que passou DateTime.Now, subtrai um deles e usa o novo valor para encontrar o primeiro do mês atual. A partir daí, ele usa isso DateTimee usa.AddMonths(-1) para obter o primeiro do mês anterior.

Obter o último dia do mês passado faz basicamente a mesma coisa, exceto que ele adiciona um ao número de dias no mês e subtrai esse valor DateTime.Now.AddDays, fornecendo o último dia do mês anterior.

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
fonte
0

Para a cultura persa

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
fonte
0

Você consegue

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

maneira fácil de fazer isso

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
fonte
Este código não é compilado devido a "novo 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
fonte