Obteniendo el primer y último día de un mes, usando un objeto DateTime dado

196

Quiero obtener el primer día y el último día del mes en el que se encuentra una fecha determinada. La fecha proviene de un valor en un campo de IU.

Si estoy usando un selector de tiempo, podría decir

var maxDay = dtpAttendance.MaxDate.Day;

Pero estoy tratando de obtenerlo de un objeto DateTime. Entonces si tengo esto ...

DateTime dt = DateTime.today;

¿Cómo obtener el primer día y el último día del mes dt?

CANALLA
fuente
No está claro lo que estás preguntando. Hay un único valor almacenado en la _Datevariable. ¿Qué "mínimo y máximo" estás tratando de obtener de ese valor?
David
se está rechazando porque la gente se pregunta por qué querrías pensar y dónde usarías algo así. No nos está diciendo nada sobre su problema inicial aquí
Mo Patel
Es mejor preguntar qué quieres hacer? no así ¿Cómo quieres hacer? Otros usos pueden sugerirle correctamente.
Shell
2
@Chathuranga sabes cuál será la fecha mínima de cualquier mes ... pero la pregunta es cuál es la última fecha del mes actual ... puedes obtener así ... agrega 1 mes en la fecha actual y menos 1 día a partir de ese momento fecha ... ahora obtendrá la última fecha de su mes actual
Shell

Respuestas:

472

DateTimeLa estructura almacena un solo valor, no un rango de valores. MinValuey MaxValueson campos estáticos, que contienen un rango de valores posibles para instancias de DateTimeestructura. Estos campos son estáticos y no se relacionan con una instancia particular de DateTime. Se relacionan con el DateTimetipo en sí.

Lectura sugerida: estática (Referencia de C #)

ACTUALIZACIÓN: Obtener rango de mes:

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
Sergey Berezovskiy
fuente
16
Sé que estoy siendo exigente aquí, ¿pero no debería lastDayofMonthserlo firstDayOfMonth.AddMonths(1).AddSeconds(-1);?
Karl Gjertsen
36
@KarlGjertsen no eres lo suficientemente exigente :) La solución perfecta será AddTicks(-1), pero si no nos importa la parte del tiempo y pensamos solo en la parte de la fecha, entonces los días funcionan bien
Sergey Berezovskiy
77
¡Ahora eso es ser exigente! ;-) La pregunta no dice cómo se utilizarán los valores, por lo que tiendo a codificar a la defensiva.
Karl Gjertsen
@SergeyBerezovskiy Odiaría plantear este punto, pero ¿no pierdes la información de la zona horaria cuando actualizas un DateTime como este? Cualquier información de zona horaria adjunta a la instancia original de DateTime se pierde cuando crea una nueva instancia como esta.
Marko
2
@KarlGjertsen, quieres ver exigente ... Yo personalmente lo hago en < firstDayOfNextMonthlugar de <= lastDayOfMonth. De esa manera, siempre funcionará independientemente de la granularidad. (Estoy seguro de que las garrapatas estarán bien, pero quién sabe lo que depara el futuro ... ¿nanoticks?)
adam0101
99

Este es más un comentario largo sobre las respuestas de @Sergey y @ Steffen. Habiendo escrito un código similar en el pasado, decidí verificar lo que era más eficiente mientras recordaba que la claridad también es importante.

Resultado

Aquí hay un ejemplo de un resultado de ejecución de prueba para 10 millones de iteraciones:

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

Solía LINQPad 4 (en C # modo de programa) para ejecutar las pruebas con la optimización del compilador de encendido. Aquí está el código probado factorizado como métodos de extensión para mayor claridad y conveniencia:

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álisis

Me sorprendieron algunos de estos resultados.

Aunque no hay mucho, FirstDayOfMonth_AddMethodfue un poco más rápido que FirstDayOfMonth_NewMethoden la mayoría de las pruebas. Sin embargo, creo que este último tiene una intención un poco más clara y por eso tengo preferencia por eso.

LastDayOfMonth_AddMethodfue un claro perdedor contra LastDayOfMonth_AddMethodWithDaysInMonth, LastDayOfMonth_NewMethody LastDayOfMonth_NewMethodWithReuseOfExtMethod. Entre los tres más rápidos no hay mucho, por lo que se reduce a su preferencia personal. Elijo la claridad LastDayOfMonth_NewMethodWithReuseOfExtMethodcon la reutilización de otro método de extensión útil. En mi humilde opinión, su intención es más clara y estoy dispuesto a aceptar el pequeño costo de rendimiento.

LastDayOfMonth_SpecialCasesupone que está proporcionando el primer día del mes en el caso especial en el que ya haya calculado esa fecha y utiliza el método add DateTime.DaysInMonthpara obtener el resultado. Esto es más rápido que las otras versiones, como era de esperar, pero a menos que tenga una necesidad desesperada de velocidad, no veo el punto de tener este caso especial en su arsenal.

Conclusión

Aquí hay una clase de método de extensión con mis elecciones y, en general, de acuerdo con @Steffen, creo:

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

Si has llegado hasta aquí, ¡gracias por tu tiempo! Ha sido divertido: ¬). Comente si tiene alguna otra sugerencia para estos algoritmos.

WooWaaBob
fuente
55
Sin embargo, tienes mucho menos crédito por tu esfuerzo. ¡Es útil!
Dion V.
2
Gracias @DionV. - ¡Es bueno ser apreciado! Las respuestas cortas son geniales cuando tienes prisa, pero creo que a menudo es útil algún análisis más profundo a seguir.
WooWaaBob
Te perdiste otra alternativa: LastDayOfMonth_AddMethod_SpecialCase(o algo así). Esperando el primer día del mes como parámetro, creo que lo más rápido debería ser lo que LastDayOfMonth_AddMethodhace. Eso sería tan simple como:return value.AddMonths(1).AddDays(-1);
Andrew
1
Gracias @ Andrew y aquí están mis resultados usando eso: 2835 ms para LastDayOfMonth_SpecialCase (), y; 4685 ms para LastDayOfMonth_AddMethod_SpecialCase (). Lo que probablemente tiene sentido cuando se considera el tiempo de creación de estructuras y que la representación interna de DateTime probablemente hace que agregar días sea una operación simple, pero agregar meses es un algoritmo más complejo.
WooWaaBob
15

Obtención del rango de meses con .Net API (solo otra forma)

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

" Last day of month" es en realidad " First day of *next* month, minus 1". Esto es lo que uso, no es necesario el 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: La razón que uso AddMinutes(-1), no AddDays(-1)aquí, es porque generalmente necesita estas funciones de fecha para informar durante un período de fecha, y cuando crea un informe para un período, la "fecha de finalización" debería ser algo así Oct 31 2015 23:59:59para que su informe funcione correctamente - Incluyendo todos los datos del último día del mes.

Es decir, realmente obtienes el "último momento del mes" aquí. No el último día.

OK, me voy a callar ahora.

jazzcat
fuente
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
fuente
En general, evite las respuestas de solo código. Considere agregar uno descriptionque ayude a explicar su código. Gracias
MickyD
3

Aquí puede agregar un mes para el primer día del mes actual que eliminar 1 día de ese día.

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

Si solo te importa la fecha

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

Si quieres ahorrar tiempo

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);
Vitalia
fuente
Va a fallar por completo en diciembre amigo. intentará crear datetime con el mes "13" y lanzará una excepción de que no existe tal fecha.
Pawel
¿Desde cuándo diciembre es el 13? Hay un total de 12 meses. ¿Qué calendario usas?
Vitaly
Si su fecha es diciembre, intentará agregar un mes, lo que resultará en una excepción. nuevo DateTime (date.Year, 12+ 1, 1, 0, 0, 0, date.Kind) .AddDays (-1); Se restará un día después de que se cree la fecha y hora, por lo que para diciembre fallará al intentar la fecha del mes "13". Puedes probarlo.
Pawel
1

La respuesta aceptada aquí no tiene en cuenta el tipo de instancia de DateTime. Por ejemplo, si su instancia original de DateTime era un tipo UTC, al crear una nueva instancia de DateTime, creará una instancia de tipo desconocido que luego se tratará como hora local según la configuración del servidor. Por lo tanto, la forma más adecuada de obtener la primera y la última fecha del mes sería esta:

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

De esta forma, se conserva el tipo original de la instancia de DateTime.

Marko
fuente
1

Prueba este:

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

Utilicé esto en mi script (funciona para mí) pero necesitaba una fecha completa sin la necesidad de recortarla solo a la fecha y no a la hora.

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

Prueba esto. Básicamente, calcula el número de días que han pasado DateTime.Now, luego resta uno de eso y usa el nuevo valor para encontrar el primero del mes actual. A partir de ahí, usa eso DateTimey usa .AddMonths(-1)para obtener el primero del mes anterior.

Obtener el último día del mes pasado hace básicamente lo mismo, excepto que agrega uno al número de días en el mes y resta ese valor DateTime.Now.AddDays, dándole el último día del mes 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
fuente
0

Para la 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
fuente
0

Puedes hacerlo

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

manera fácil de hacerlo

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
fuente
Este código no se compila debido a "nuevo 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
fuente