2009-03-08 27 views

Respuesta

12

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); 
} 
+0

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

+1

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. –

6

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.

+0

@Guffa, gracias por atrapar eso. Solo estaba tratando de ser el primero en responder, ¡así que mi lógica era buena! –

11

http://datetimeextensions.codeplex.com/ tiene un (díadelasemana) Última método de extensión que va a hacer esto para usted

prueba incluyó :-)

+0

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 –

+0

¡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

+0

falló la prueba, DateTime dt = DateTime.Today.AddMonths (1) .Last (DayOfWeek.Thursday); Assert.AreEqual (new DateTime (2012, 4, 26), dt); – Vincent

1

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; 
    } 
+0

¿Esa es tu definición de simple? –

+0

sí, si tiene una mejor idea, ¿cuál es su respuesta? – Sorskoot

+0

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. –

4
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; 
} 
+0

¿Por qué el downvote en este caso? Puse el "!" ¡en! –

+0

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

+0

Para optimizar, si el día de la semana> miércoles, hágalo a la manera de @ Guffa, de lo contrario, mío :) –

1
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; 
    } 
0
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); 
} 
1

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 
Cuestiones relacionadas