2009-03-08 40 views
6

Cho một DateTime và một DayOfWeek sẽ trả về ngày của DayOfWeek cuối cùng của tháng đó.Tính DayOfWeek cuối cùng của tháng

Ví dụ: ngày 1 tháng 3 năm 2009 và Chủ nhật sẽ trả lại ngày 29 tháng 3 năm 2009

Trả lời

12

Không thể tìm thấy một tiện dụng one-liner nhưng công trình này:

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

Giống như vậy, cảm ơn Henk. Minor point - bạn có thể loại bỏ cuối cùng .AddDays (-1) và thay đổi "if (diff> 0)" thành "if (diff> = 0)" - do đó xóa 1 datetime.addDays hoạt động. – Ryan

+1

Ryan, vâng, nó giúp tiết kiệm 1 op nhưng nó giáp với những gì trình biên dịch có thể tối ưu hóa. Và khái niệm "ngày cuối cùng của tháng" dễ dàng hơn trong tâm trí hơn "ngày đầu tiên của tháng tiếp theo". Vì vậy, tôi thường sẽ không tối ưu hóa điều này. –

6

Lấy ngày đầu tiên vào tháng tiếp theo, sau đó tìm ngày làm việc đầu tiên trong tháng đó. Quay trở lại bảy ngày và bạn đang ở đó trong tuần trong tuần cuối cùng của tháng 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); 

(Bạn có thể thay thế các vòng lặp với một số số học cho phép tính số ngày để thêm dựa trên các giá trị số của DayOfWeek giá trị, nhưng điều này là thẳng hơn về phía trước.)

Chỉnh sửa:
Bạn cũng có thể nhận ngày cuối cùng trong tháng hiện tại, sau đó lặp lại cho đến khi bạn tìm đúng ngày trong tuần.

+0

@Guffa, cảm ơn vì đã đánh bắt điều đó. Tôi chỉ cố gắng là người đầu tiên trả lời để logic của tôi tốt! –

11

http://datetimeextensions.codeplex.com/ có (dayOfWeek) phương pháp mở rộng cuối mà sẽ làm việc này cho bạn

thử nghiệm bao gồm :-)

+0

Thực hiện thông minh và tiện ích mở rộng sẽ trở nên ngọt ngào. Tôi muốn đổi tên nó thành một cái gì đó cụ thể. Nó có thể là Date.LastDayOfWeekForMonth Date.LastDayOfWeekForYear hoặc Date.LastDayOfWeekCentury Date.LastDayOfWeekForMillenium –

+0

Thư viện tuyệt vời, nhờ TFD! Thật không may tôi không thể sử dụng nó như là vì tôi đang nhắm mục tiêu .NET 2.0 vì vậy tôi đã đánh dấu Henks câu trả lời, nhưng bạn đã có rất nhiều upvotes cho điều này anyway. – Ryan

+0

kiểm tra không thành công, DateTime dt = DateTime.Today.AddMonths (1) .Last (DayOfWeek.Thursday); Assert.AreEqual (ngày giờ mới (2012, 4, 26), dt); – Vincent

1

Hãy thử điều này: bắt đầu từ ngày cuối cùng và đếm lại cho đến khi bạn nhấn ngày bạn đang tìm kiếm

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; 

Chỉnh sửa: Đã khôi phục phiên bản cũ mà Dangph đang tham chiếu trong phần nhận xét.

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

Đó là định nghĩa của bạn về đơn giản? –

+0

vâng, nếu bạn có ý tưởng hay hơn, câu trả lời của bạn ở đâu? – Sorskoot

+0

Tôi thích phiên bản đầu tiên của bạn hơn. Đây là một trong những khó khăn hơn để suy nghĩ về.Và tôi rất nghi ngờ rằng nó có thể có bất kỳ lợi ích hiệu suất phát hiện trừ khi bạn đang làm một gazillion của những tính toán này. Tôi sẽ đề nghị trong phiên bản cũ mà bạn chỉ có thể thêm 7 ngày tại một thời điểm để làm cho nó đơn giản hơn. –

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

Tại sao các downvote về điều này? Tôi đặt "!" trong! –

+0

Tôi đoán downvote là bởi vì sử dụng một vòng lặp cho một cái gì đó như thế này là không thể tối ưu - nhưng ít nhất bạn đang bắt đầu từ cuối tháng và làm việc ngược để tối đa 6 lần lặp. – Ryan

+0

Để tối ưu hóa, nếu dayofweek> thứ tư, làm điều đó @ Guffa cách, nếu không, tôi :) –

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

Đây là cả LastDayOfMonth() và LastDayOfMonth (dayOfWeek) cộng với đơn vị kiểm tra, được lấy cảm hứng từ "cuối thứ sáu" thực hiện trong How to find the 3rd Friday in a month with C#? bởi @ Paul Fryer:

/** 
    /// <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 
Các vấn đề liên quan