Dado un DateTime y un DayOfWeek debería devolver la fecha del último DayOfWeek de ese mes.Calcule el último día de la semana de un mes
E.g. 1 de marzo de 2009 y el domingo volvería 29 de marzo de 2009
Dado un DateTime y un DayOfWeek debería devolver la fecha del último DayOfWeek de ese mes.Calcule el último día de la semana de un mes
E.g. 1 de marzo de 2009 y el domingo volvería 29 de marzo de 2009
No se puede encontrar una aplicación práctica de una sola línea, pero éste funciona:
static DateTime LastDayOfWeekInMonth(DateTime day, DayOfWeek dow)
{
DateTime lastDay = new DateTime(day.Year, day.Month, 1).AddMonths(1).AddDays(-1);
DayOfWeek lastDow = lastDay.DayOfWeek;
int diff = dow - lastDow;
if (diff > 0) diff -= 7;
System.Diagnostics.Debug.Assert(diff <= 0);
return lastDay.AddDays(diff);
}
Obtenga el primer día del mes siguiente, luego encuentre el primer día laborable de ese mes. Remontan a siete días y se encuentra en ese día de la semana en la última semana del mes Corrent:
DateTime date = new DateTime(2009, 3, 12);
DayOfWeek day = DayOfWeek.Sunday;
DateTime nextMonth = new DateTime(date.Year, date.Month, 1).AddMonths(1);
while (nextMonth.DayOfWeek != day) {
nextMonth = nextMonth.AddDays(1);
}
DateTime lastInMonth = nextMonth.AddDays(-7);
(podría reemplazar el bucle con un poco de aritmética que calcula el número de días a añadir sobre la base de los valores numéricos de la DayOfWeek valora, pero esto es más directo.)
Edit:
Por supuesto, también puede obtener el último día en el mes actual, luego retroceda hasta encontrar el día de la semana correcto.
@Guffa, gracias por atrapar eso. Solo estaba tratando de ser el primero en responder, ¡así que mi lógica era buena! –
http://datetimeextensions.codeplex.com/ tiene un (díadelasemana) Última método de extensión que va a hacer esto para usted
prueba incluyó :-)
Implementación inteligente, y una extensión sería dulce. Sin embargo, lo cambiaría a algo más específico. Podría ser Date.LastDayOfWeekForMonth Date.LastDayOfWeekForYear o Date.LastDayOfWeekCentury Date.LastDayOfWeekForMillenium –
¡Gran biblioteca, gracias TFD! Desafortunadamente no puedo usarlo tal como está porque me estoy enfocando en .NET 2.0, así que he marcado la respuesta de Henks, pero de todos modos tienes muchos votos ascendentes. – Ryan
falló la prueba, DateTime dt = DateTime.Today.AddMonths (1) .Last (DayOfWeek.Thursday); Assert.AreEqual (new DateTime (2012, 4, 26), dt); – Vincent
Prueba esto: empezar por el último día y contar hacia atrás hasta llegar el día en que usted está buscando
static DateTime LastOccurenceOfDay(DateTime dt, DayOfWeek dow)
{
DateTime looperDate = new DateTime(dt.Year, dt.Month, 1)
.AddMonths(1).AddDays(-1);
while (looperDate.DayOfWeek!=dow)
looperDate =looperDate.AddDays(-1);
return looperDate;
Editar: Se ha recuperado la versión anterior a la que se refiere Dangph en los comentarios.
static DateTime LastOccurenceOfDay(DateTime dt, DayOfWeek dow)
{
//set start of loop
DateTime looperDate = new DateTime(dt.Year, dt.Month, 1);
//initialze return value
DateTime returnDate = dt;
//loop until the month is over
while (looperDate.Month == dt.Month)
{
//if the current DayOfWeek is the date you're looking for
if (looperDate.DayOfWeek == dow)
//remember it.
returnDate = looperDate;
//increase day
looperDate=looperDate.AddDays(1);
}
return returnDate;
}
¿Esa es tu definición de simple? –
sí, si tiene una mejor idea, ¿cuál es su respuesta? – Sorskoot
Me gustó más tu primera versión. Este es más difícil de pensar.Y dudo mucho que pueda tener una ganancia de rendimiento detectable a menos que esté haciendo un montón de estos cálculos. Iba a sugerir en la versión anterior que podría agregar 7 días a la vez para hacerlo más simple. –
static DateTime LastDateOfWeekForMonth(DayOfWeek weekday, int month, int year)
{
DateTime d = new DateTime(year, month, 1).AddMonths(1);
while (!(d.DayOfWeek == weekday && d.Month == month))
{
d = d.AddDays(-1);
}
return d;
}
¿Por qué el downvote en este caso? Puse el "!" ¡en! –
Supongo que el downvote se debe a que es poco probable que usar un bucle para algo como esto sea óptimo, pero al menos estás comenzando desde el final del mes y trabajando hacia atrás para un máximo de 6 iteraciones. – Ryan
Para optimizar, si el día de la semana> miércoles, hágalo a la manera de @ Guffa, de lo contrario, mío :) –
public DateTime GetLastDayOfMonth(int year, int month, DayOfWeek dayOfWeek)
{
var daysInMonth = DateTime.DaysInMonth(year, month);
var lastDay = new DateTime(year, month, daysInMonth);
while (lastDay.DayOfWeek != dayOfWeek)
{
lastDay = lastDay.AddDays(-1);
}
return lastDay;
}
static DateTime GetDayOfWeekInMonthFromWeekIndex(this DateTime date, DayOfWeek dow, int weekIndex)
{
if (weekIndex > 4)
return LastDayOfWeekInMonth(date, dow);
DateTime newDate = new DateTime(date.Year, date.Month, 1);
DayOfWeek newDow = newDate.DayOfWeek;
int diff = dow - newDow;
diff += ((weekIndex - 1) * 7);
return newDate.AddDays(diff);
}
Aquí es tanto LastDayOfMonth() y LastDayOfMonth (DayOfWeek) más unidad pruebas, que se inspira en la aplicación "Última Viernes" en How to find the 3rd Friday in a month with C#? por @ Pablo freidora:
/**
/// <summary>An extension method that returns the last day of the month.</summary>
/// <param name="d">A date within the month to calculate.</param>
/// <returns>The last day of the current month.</returns>
**/
public static DateTime LastDayOfMonth(this DateTime d)
{
DateTime nextMonth = new DateTime(d.Year, d.Month, 1).AddMonths(1);
return nextMonth.AddDays(-1);
}
/**
/// <summary>An extension method that returns the last <see cref="DayOfWeek"> of the month.</summary>
/// <param name="d">A date within the month to calculate.</param>
/// <returns>The last day of the current month.</returns>
**/
public static DateTime LastDayOfMonth(this DateTime d, DayOfWeek dayOfWeek)
{
DateTime lastDayOfMonth = d.LastDayOfMonth();
int vector = (((int)lastDayOfMonth.DayOfWeek - (int)dayOfWeek + DaysInWeek) % DaysInWeek);
return lastDayOfMonth.AddDays(-vector);
}
#region LastDayOfMonth Tests
[TestCase("1/1/2011", "1/31/2011")]
[TestCase("2/1/2009", "2/28/2009")] //Leap Year
[TestCase("2/1/2008", "2/29/2008")] //Leap Year
[TestCase("3/10/2011", "3/31/2011")]
[TestCase("4/20/2011", "4/30/2011")]
[TestCase("5/15/2011", "5/31/2011")]
[TestCase("6/30/2011", "6/30/2011")]
[TestCase("12/1/2011", "12/31/2011")]
public void LastDayOfMonthReturnsCorrectDay(string startingDate, string expectedDate)
{
//Arrange
DateTime testDate = DateTime.Parse(startingDate);
DateTime expected = DateTime.Parse(expectedDate);
//Act
DateTime actual = testDate.LastDayOfMonth();
//Assert
Assert.That(actual, Is.EqualTo(expected));
}
[TestCase("1/1/2011", DayOfWeek.Monday, "1/31/2011")]
[TestCase("2/1/2009", DayOfWeek.Saturday, "2/28/2009")]
[TestCase("2/1/2009", DayOfWeek.Sunday, "2/22/2009")]
[TestCase("2/1/2008", DayOfWeek.Friday, "2/29/2008")] //Leap Year
[TestCase("2/1/2008", DayOfWeek.Thursday, "2/28/2008")] //Leap Year
[TestCase("3/10/2011", DayOfWeek.Wednesday, "3/30/2011")]
[TestCase("4/20/2011", DayOfWeek.Friday, "4/29/2011")]
[TestCase("4/20/2011", DayOfWeek.Saturday, "4/30/2011")]
[TestCase("5/15/2011", DayOfWeek.Tuesday, "5/31/2011")]
[TestCase("5/15/2011", DayOfWeek.Saturday, "5/28/2011")]
[TestCase("9/30/2011", DayOfWeek.Sunday, "9/25/2011")]
[TestCase("12/1/2011", DayOfWeek.Monday, "12/26/2011")]
public void LastDayOfMonthReturnsCorrectDayOfWeek(string startingDate, DayOfWeek dayOfWeek, string expectedDate)
{
//Arrange
DateTime testDate = DateTime.Parse(startingDate);
DateTime expected = DateTime.Parse(expectedDate);
//Act
DateTime actual = testDate.LastDayOfMonth(dayOfWeek);
//Assert
Assert.That(actual, Is.EqualTo(expected));
}
#endregion
Me gusta, gracias Henk. Punto menor: puede eliminar los últimos .AddDays (-1) y cambiar "if (diff> 0)" a "if (diff> = 0)" - eliminando así la operación 1 datetime.addDays. – Ryan
Ryan, sí, ahorra 1 op pero está al margen de lo que el compilador puede optimizar. Y conceptualmente, "último día del mes" es más fácil para la mente que "primer día del próximo mes". Entonces normalmente no optimizaría esto. –