-->

How to find the 3rd Friday in a month with C#?

2020-01-25 01:06发布

问题:

Given a date (of type DateTime), how do I find the 3rd Friday in the month of that date?

回答1:

I haven't tested this, but since the third Friday can't possibly occur before the 15th of the month, create a new DateTime, then just increment until you get to a Friday.

DateTime thirdFriday= new DateTime(yourDate.Year, yourDate.Month, 15);

while (thirdFriday.DayOfWeek != DayOfWeek.Friday)
{
   thirdFriday = thirdFriday.AddDays(1);
}


回答2:

I'm going to repeat my answer from here with one little addition.

The language-agnostic version:

To get the first particular day of the month, start with the first day of the month: yyyy-mm-01. Use whatever function is available to give a number corresponding to the day of the week; in C# this would be DateTime.DayOfWeek. Subtract that number from the day you are looking for; for example, if the first day of the month is Wednesday (3) and you're looking for Friday (5), subtract 3 from 5, leaving 2. If the answer is negative, add 7. Finally add that to the first of the month; for my example, the first Friday would be the 3rd.

To get the last Friday of the month, find the first Friday of the next month and subtract 7 days.

To get the 3rd Friday of the month, add 14 days to the first Friday.



回答3:

I followed User:Mark Ransom's algorithm and wrote a generalized day finder. For example to get the 3rd friday of december 2013,

int thirdFriday = DayFinder.FindDay(2013, 12, DayOfWeek.Friday, 3);

And here is the function definition. It doesn't have any iterative loops, so its efficient.

public class DayFinder
        {

            //For example to find the day for 2nd Friday, February, 2016
            //=>call FindDay(2016, 2, DayOfWeek.Friday, 2)
            public static int FindDay(int year, int month, dayOfWeek Day, int occurance)
            {

               if (occurance <= 0 || occurance > 5) 
                throw new Exception("Occurance is invalid");

               DateTime firstDayOfMonth = new DateTime(year, month, 1);
                //Substract first day of the month with the required day of the week 
               var daysneeded = (int)day - (int)firstDayOfMonth.DayOfWeek;
                //if it is less than zero we need to get the next week day (add 7 days)
               if (daysneeded < 0) daysneeded = daysneeded + 7;
                //DayOfWeek is zero index based; multiply by the Occurance to get the day
               var resultedDay =  (daysneeded + 1)+ (7*(occurance-1));

               if(resultedDay > (firstDayOfMonth.AddMonths(1) - firstDayOfMonth).Days) 
                throw new Exception(String.Format("No {0} occurance(s) of {1} in the required month", occurance, day.ToString()));

               return resultedDay; 
            }
        }


回答4:

Probably best to abstract this to a method to do any date/day combination:

(Extension Method)

public static bool TryGetDayOfMonth(this DateTime instance, 
                                 DayOfWeek dayOfWeek, 
                                 int occurance, 
                                 out DateTime dateOfMonth)
{
    if (instance == null)
    {
        throw new ArgumentNullException("instance");
    }

    if (occurance <= 0 || occurance > 5)
    {
        throw new ArgumentOutOfRangeException("occurance", "Occurance must be greater than zero and less than 6.");
    }

    bool result;
    dateOfMonth = new DateTime();

    // Change to first day of the month
    DateTime dayOfMonth = instance.AddDays(1 - instance.Day);

    // Find first dayOfWeek of this month;
    if (dayOfMonth.DayOfWeek > dayOfWeek)
    {
        dayOfMonth = dayOfMonth.AddDays(7 - (int)dayOfMonth.DayOfWeek + (int)dayOfWeek);
    }
    else
    {
        dayOfMonth = dayOfMonth.AddDays((int)dayOfWeek - (int)dayOfMonth.DayOfWeek);
    }

    // add 7 days per occurance
    dayOfMonth = dayOfMonth.AddDays(7 * (occurance - 1));

    // make sure this occurance is within the original month
    result = dayOfMonth.Month == instance.Month;


    if (result)
    {
        dateOfMonth = dayOfMonth;
    }

    return result;
}

Results:

DateTime myDate = new DateTime(2013, 1, 1)
DateTime dateOfMonth;

myDate.TryGetDayOfMonth(DayOfWeek.Sunday, 1, out dateOfMonth) 
// returns: true; dateOfMonth = Sunday, 1/6/2013

myDate.TryGetDayOfMonth(DayOfWeek.Sunday, 4, out dateOfMonth) 
// returns: true; dateOfMonth = Sunday, 1/27/2013

myDate.TryGetDayOfMonth(DayOfWeek.Sunday, 5, out dateOfMonth) 
// returns: false; 

myDate.TryGetDayOfMonth(DayOfWeek.Wednesday, 1, out dateOfMonth) 
// returns: true; dateOfMonth = Wednesday, 1/2/2013

myDate.TryGetDayOfMonth(DayOfWeek.Wednesday, 4, out dateOfMonth) 
// returns: true; dateOfMonth = Wednesday, 1/23/2013

myDate.TryGetDayOfMonth(DayOfWeek.Wednesday, 5, out dateOfMonth) 
// returns: true; dateOfMonth = Wednesday, 1/30/2013 

// etc


回答5:

Old post, but I found remarkably few decent answers online for this surely quite common problem! Mark Ransom's answer should be the last word on this algorithm-wise, but here is a C# helper class (in this case I think clearer than extensions) for anyone who wants a quick answer to the common problems of "first day of week in month", "xth day of week in month" and "last day of week in month".

I modified it to return DateTime.MinValue if the Xth day of the week falls outside the provided month rather than wrapping to the next month, because that to me seems more useful.

I've thrown in a LINQPad-runnable example program too.

void Main()
{
    DayOfWeek dow = DayOfWeek.Friday;
    int y = 2014;
    int m = 2;

    String.Format("First {0}: {1}", new object[] { dow, DateHelper.FirstDayOfWeekInMonth(y, m, dow) }).Dump();

    "".Dump();

    String.Format("Last {0}: {1}", new object[] { dow, DateHelper.LastDayOfWeekInMonth(y, m, dow) }).Dump();

    "".Dump();

    for(int i = 1; i <= 6; i++)
        String.Format("{0} #{1}: {2}", new object[] { dow, i, DateHelper.XthDayOfWeekInMonth(y, m, dow, i) }).Dump();
}


public class DateHelper
{
    public static DateTime FirstDayOfWeekInMonth(int year, int month, DayOfWeek day)
    {
        DateTime res = new DateTime(year, month, 1);
        int offset = -(res.DayOfWeek - day);

        if (offset < 0)
            offset += 7;

        res = res.AddDays(offset);

        return res;
    }

    public static DateTime LastDayOfWeekInMonth(int year, int month, DayOfWeek day)
    {
        DateTime dt = new DateTime(year, month, 1).AddMonths(1);        
        DateTime res = FirstDayOfWeekInMonth(dt.Year, dt.Month, day);

        res = res.AddDays(-7);

        return res;
    }


    public static DateTime XthDayOfWeekInMonth(int year, int month, DayOfWeek day, int x)
    {
        DateTime res = DateTime.MinValue;

        if (x > 0)
        {
            res = FirstDayOfWeekInMonth(year, month, day);

            if (x > 1)
                res = res.AddDays((x - 1) * 7);

            res = res.Year == year && res.Month == month ? res : DateTime.MinValue;
        }

        return res;
    }
}

Prints:

First Friday: 07/02/2014 00:00:00

Last Friday: 28/02/2014 00:00:00

Friday #1: 07/02/2014 00:00:00
Friday #2: 14/02/2014 00:00:00
Friday #3: 21/02/2014 00:00:00
Friday #4: 28/02/2014 00:00:00
Friday #5: 01/01/0001 00:00:00
Friday #6: 01/01/0001 00:00:00


回答6:

Slightly more optimized version:

    DateTime Now = DateTime.Now;

    DateTime TempDate = new DateTime(Now.Year, Now.Month, 1);

    // find first friday
    while (TempDate.DayOfWeek != DayOfWeek.Friday)
        TempDate = TempDate.AddDays(1);

    // add two weeks
    TempDate = TempDate.AddDays(14);


回答7:

This is a version that uses LINQ and functional programming style.

It works like this.

First, take all of the days of the month. Then select only the ones of the right day (Friday). Finally take the nth (3rd) entry and return.

// dt: The date to start from (usually DateTime.Now)
// n: The nth occurance (3rd)
// weekday: the day of the week to look for
    public DateTime GetNthWeekdayOfMonth(DateTime dt, int n, DayOfWeek weekday)
    {
        var days = Enumerable.Range(1, DateTime.DaysInMonth(dt.Year, dt.Month)).Select(day => new DateTime(dt.Year, dt.Month, day));

        var weekdays = from day in days
                            where day.DayOfWeek == weekday
                            orderby day.Day ascending
                            select day;

        int index = n - 1;

        if (index >= 0 && index < weekdays.Count())
            return weekdays.ElementAt(index);

        else
            throw new InvalidOperationException("The specified day does not exist in this month!");
   }


回答8:

My reasoning goes like this

  • the 15th is the first possible "third Friday" (1,8,15)
  • therefore we're looking for the first Friday on or after the 15th
  • DayOfWeek is an enumeration starting with 0 for Sunday
  • Therefore you have to add an offet of 5-(int)baseDay.DayOfWeek to the 15th
  • Except that the above offset can be negative, which we fix by adding 7, then doing modulo 7.

In code:

public static DateTime GetThirdFriday(int year, int month)
{
   DateTime baseDay = new DateTime(year, month, 15);
   int thirdfriday = 15 + ((12 - (int)baseDay.DayOfWeek) % 7);
   return new DateTime(year, month, thirdfriday);
}

Since there are only 7 possible results, you could also do this:

  private readonly static int[] thirdfridays =
      new int[] { 20, 19, 18, 17, 16, 15, 21 };

  public static int GetThirdFriday(int year, int month)
  {
     DateTime baseDay = new DateTime(year, month, 15);
     return thirdfridays[(int)baseDay.DayOfWeek];
  }


回答9:

I pass this the DateTime for the start of the month I am looking at.

    private DateTime thirdSunday(DateTime timeFrom)
    {
        List<DateTime> days = new List<DateTime>();
        DateTime testDate = timeFrom;

        while (testDate < timeFrom.AddMonths(1))
        {
            if (testDate.DayOfWeek == DayOfWeek.Friday)
            {
                days.Add(testDate);
            }
            testDate = testDate.AddDays(1);
        }

        return days[2];
    }


回答10:

I know of no clean/built in way of doing this. But it's not too hard to code up:

        DateTime now = DateTime.Now;

        for (int i = 0; i < 7; ++i)
        {
            DateTime d = new DateTime(now.Year, now.Month, i+1);
            if (d.DayOfWeek == DayOfWeek.Friday)
            {
                return d.AddDays(14);
            }
        }


回答11:

    public DateTime GetThirdThursday(DateTime now)
    {
        DateTime ThirdThursday;
        now = DateTime.Now;
        string wkday;
        DateTime firstday = new DateTime(now.Year, now.Month, 1);
        ThirdThursday = firstday.AddDays(15);

        // ThirdThursday = now.AddDays((now.Day - 1) * -1).AddDays(14);
        wkday = ThirdThursday.DayOfWeek.ToString();

        while (wkday.CompareTo("Thursday") < 0)

        {
            ThirdThursday.AddDays(1);
        }
        return ThirdThursday;
    }


回答12:

    int numday = 0;
    int dayofweek = 5; //friday
    DateTime thirdfriday;
    for (int i = 0; i < (date.AddMonths(1) - date).Days && numday <3; i++)
    {
        if ((int)date.AddDays(i).DayOfWeek == dayofweek)
        {
            numday++;
        }
        if (numday == 3)
        {
            thirdfriday = date.AddDays(i);
        }

    }


回答13:

Sorry to jump in late on this... Might help someone else tho.

Begin rant: Loops, yuck. Too much code, yuck. Not Generic Enough, yuck.

Here's a simple function with a free overload.

public DateTime DateOfWeekOfMonth(int year, int month, DayOfWeek dayOfWeek, byte weekNumber)
{
    DateTime tempDate = new DateTime(year, month, 1);
    tempDate = tempDate.AddDays(-(tempDate.DayOfWeek - dayOfWeek));

    return
        tempDate.Day > (byte)DayOfWeek.Saturday
            ? tempDate.AddDays(7 * weekNumber)
            : tempDate.AddDays(7 * (weekNumber - 1));
}

public DateTime DateOfWeekOfMonth(DateTime sender, DayOfWeek dayOfWeek, byte weekNumber)
{
    return DateOfWeekOfMonth(sender.Year, sender.Month, dayOfWeek, weekNumber);
}

Your usage:

DateTime thirdFridayOfMonth = DateOfWeekOfMonth(DateTime.Now, DayOfWeek.Friday, 3);


回答14:

Here's my algorithm:

  1. Find the number of days until the upcoming Friday.
  2. Initialize a counter and set it to 1. Subtract seven days from the date returned from [1], then compare the month from the date returned against the date returned from (1).
    1. If the months are not equal, return the counter from [2].
    2. If the months are equal, recurse into [2] and add 1 to the counter created in [2].

The counter will give you the nth Friday of the month for that date (or its upcoming Friday).



回答15:

Following works great, no validation for occurrence is provided. You can find any nth day for the given date month either from start or last. Provide minus occurrence value if you are looking for from the last.

 public static DateTime GetDayOfMonth(DateTime dateValue, DayOfWeek dayOfWeek, int occurance)
    {
        List<DateTime> dayOfWeekRanges = new List<DateTime>();

        //move to the first of th month
        DateTime startOfMonth = new DateTime(dateValue.Year, dateValue.Month, 1);

        //move startOfMonth to the dayOfWeek requested
        while (startOfMonth.DayOfWeek != dayOfWeek)
            startOfMonth = startOfMonth.AddDays(1);

        do
        {
            dayOfWeekRanges.Add(startOfMonth);
            startOfMonth = startOfMonth.AddDays(7);
        } while (startOfMonth.Month == dateValue.Month);

        bool fromLast = occurance < 0;
        if (fromLast)
            occurance = occurance * -1;

        if (fromLast)
            return dayOfWeekRanges[dayOfWeekRanges.Count - occurance];
        else
            return dayOfWeekRanges[occurance - 1];
    }


回答16:

Here is my two cents... An optimized solution without unnecessary loops or tests :

public static DateTime ThirdFridayOfMonth(DateTime dateTime)
{
    int day = dateTime.Day;
    return dateTime.AddDays(21 - day - ((int)dateTime.DayOfWeek + 37 - day) % 7);
}


回答17:

I wrote extended version of @justcoding121's code that can get from the last day of the month. I don't know this algorithm is right, but it works so far.

public static int? GetNthDayOfWeekInMonth(int year, int month, DayOfWeek dow, int weekNumOfMonth)
{
    if (weekNumOfMonth < -5 || weekNumOfMonth == 0 || weekNumOfMonth > 5)
        throw new ArgumentOutOfRangeException("weekNumOfMonth", $"must be between 1~5 or -1~-5. ({weekNumOfMonth})");

    int daysOfMonth = DateTime.DaysInMonth(year, month);

    if (weekNumOfMonth > 0)
    {
        var firstDay = new DateTime(year, month, 1);
        var firstDayOfTargetDOW = (int)dow - (int)firstDay.DayOfWeek;
        if (firstDayOfTargetDOW < 0)
            firstDayOfTargetDOW += 7;
        var resultedDay = (firstDayOfTargetDOW + 1) + (7 * (weekNumOfMonth - 1));

        if (resultedDay > daysOfMonth)
            return null;

        return resultedDay;
    }
    else
    {
        var lastDay = new DateTime(year, month, daysOfMonth);
        var firstDayOfTargetDOW = (int)lastDay.DayOfWeek - (int)dow;
        if (firstDayOfTargetDOW < 0)
            firstDayOfTargetDOW += 7;
        var resultedDay = firstDayOfTargetDOW + (7 * (Math.Abs(weekNumOfMonth) - 1));

        if (resultedDay > daysOfMonth)
            return null;

        return (daysOfMonth - resultedDay);
    }
}

usage

Assert.AreEqual(02, DateTimeHelper.GetNthDayOfWeekInMonth(2019, 11, DayOfWeek.Saturday, 1));
Assert.AreEqual(30, DateTimeHelper.GetNthDayOfWeekInMonth(2019, 11, DayOfWeek.Saturday, -1));


标签: c# .net datetime