I am trying to write a piece of code where a day and a number of days ahead is given - for example, (Monday, 3).
This should return back "Thursday" as 3 days from Monday is Thursday.
Here is what i have done. The issue with this is, If asked for (Thursdays,5) or (Wednesday,7) or (Friday, 2) it wont be able to return anything.
As you can see I am playing around with the index of the array to get the results. I am not sure how i need to modify the code to get, for example the day 10 days from Friday.
using System;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
string Day = "Friday";
int ahead = 3;
Console.WriteLine(FindDay(Day, ahead));
}
public static string FindDay(string dayGiven, int daysAhead)
{
string[] week = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
int indexOfDayGiven = 0;
string ans = "";
foreach (string day in week)
{
if (day == dayGiven)
{
indexOfDayGiven = Array.IndexOf(week, dayGiven);
}
}
foreach(string day in week)
{
if (Array.IndexOf(week, day) ==indexOfDayGiven + daysAhead)
{
ans = day;
}
}
return ans;
}
}
}
The best way to do this is the modulo operation which is % in c#.
something like this:
var ind = (indexOfDayGiven + daysAhead) % 7;
As others have mentioned, you can use the modulus operator (which returns the remainder after division) to get a value that is within the array. So if you have Friday (5) and want to find out which day is 4 days away, we would add the two together and then take the modulus of the number of items in the array (5 + 4 = 9; 9 % 7 = 2;), or in one line: ((5 + 4) % 7) = 2, and that is the index of our result:
int result = week[(Array.IndexOf(week, "Friday") + 4) % 7];
We can also make use of the DayOfWeek enum instead of creating an array of day names. This allows us to do a case-insensitive parsing of the input day string to get the enum value. The nice thing about this is that the enum contains both the Name and the Index in one object, so we don't have to lookup indexes - we can just cast to an int, do our math, and then cast the result back to a DayOfWeek and get the string result:
public static string FindDay(string dayGiven, int daysAhead)
{
DayOfWeek startDay;
if (!Enum.TryParse(dayGiven, true, out startDay))
{
throw new ArgumentException($"'{dayGiven}' is not a valid day.");
}
return ((DayOfWeek) (((int) startDay + daysAhead) % 7)).ToString();
}
I'd make use of the DayOfWeek Enumeration like in the following example. The way to find the day stays the same as in the comments and other answers: Use the (Integer) remainder operator '%'.
using System;
using System.Globalization;
public class Program
{
public static void Main()
{
int n = 8;
for ( int startDay = 0; startDay < 7 ; startDay++)
Console.WriteLine("{0} days from {1} is {2}", n, GetDayName((DayOfWeek)startDay), GetDayName(GetDaysAhead((DayOfWeek)startDay, n)));
}
public static string GetDayName( DayOfWeek day )
{
return DateTimeFormatInfo.CurrentInfo.GetDayName(day);
}
public static DayOfWeek GetDaysAhead(DayOfWeek start, int daysAhead)
{
if ( daysAhead < 0 ) throw new ArgumentOutOfRangeException(nameof(daysAhead));
if ( daysAhead == 0 ) return start;
return (DayOfWeek) (( (int)start + daysAhead ) % 7);
}
}
DayOfWeek comes as Property of DateTime and DateTimeOffset. It isn't localized itself, but you can easily get localized strings as shown above.
I have a rather specific problem working out the best way to calculate the next time a "task" in my program should run, based on the configuration of that task.
Starting with the definition of some things that come through to configure this "Task". First off, an enumeration which looks much like the framework's DayOfWeek enum, which I have called DaysOfWeek and marked it up with the FlagsAttribute to indicate it can be a multiple thereof:
[Flags]
public enum DaysOfWeek
{
Sunday = 1,
Monday = 2,
Tuesday = 4,
Wednesday = 8,
Thursday = 16,
Friday = 32,
Saturday = 64
}
Secondly the class in question with the appropriate properties, and the method im trying to implement:
public class WeeklySchedule
{
public DaysOfWeek DaysToRun { get; set; }
public TimeSpan TimeToRun{ get; set; }
public override DateTime CalculateNextRunTime(DateTime lastRun)
{
// Here's what im trying to implement
}
}
The requirements should be pretty obvious
If DaysToRun is today, but TimeToRun has already gone today, return the next time/day
If today is not included in DaysToRun, then find the next day/time to run
Im obviously just having a brain-fart-monday because I cant work out an efficient way to calculate this, short of ShouldExecuteToday() method, followed by FindNextExecutionDay() and so on (maybe this is the right way.....)
Edit: Ok the weekend brain-fog is lifting, here's where Im at so far. If anyone can improve on this it would be appreciated:
First off, ive put a mapping of the two enums into a static member of my class, I know I could Parse from one to the other as per #DorCohen's example, but this makes me feel icky.
private static Dictionary<DayOfWeek, DaysOfWeek> DayToDaysMap
= new Dictionary<DayOfWeek, DaysOfWeek>()
{
{DayOfWeek.Monday, DaysOfWeek.Monday},
{DayOfWeek.Tuesday, DaysOfWeek.Tuesday},
{DayOfWeek.Wednesday, DaysOfWeek.Wednesday},
{DayOfWeek.Thursday, DaysOfWeek.Thursday},
{DayOfWeek.Friday, DaysOfWeek.Friday},
{DayOfWeek.Saturday, DaysOfWeek.Saturday},
{DayOfWeek.Sunday, DaysOfWeek.Sunday},
};
Then this method to determine if it should be run on a day:
private bool ShouldRunOn(DateTime now)
{
var days = DayToDaysMap[now.DayOfWeek];
// If the schedule is not set for the specified day, return false
if (!this.DaysToRun.HasFlag(days))
return false;
// Schedule should run on specified day, just determine if it is in the past
return this.TimeOfDay > now.TimeOfDay;
}
Then the implementation becomes; "can I run today" and if not "advance up to 6 days and see if I can run that day". Note that the parameter lastRun is not used in this implementation, it's used for others (such as a repeating schedule).
public override DateTime CalculateNextRunTime(DateTime lastRun)
{
var now = DateTime.Now;
if (ShouldRunOn(now))
return new DateTime(now.Year,now.Month,now.Day,this.TimeOfDay.Hours,
this.TimeOfDay.Minutes,this.TimeOfDay.Seconds);
for (var i = 1; i < 7; i++)
{
now = now.AddDays(1).Date;
if(ShouldRunOn(now))
return new DateTime(now.Year, now.Month, now.Day,
this.TimeOfDay.Hours, this.TimeOfDay.Minutes, this.TimeOfDay.Seconds);
}
return DateTime.MinValue;
}
Improvements welcomed!
Here's a rewrite from me:
public DateTime CalculateNextRunTime()
{
var now = DateTime.Now;
for (var i = 0; i<=7; i++)
{
var potentialRunTime = now.AddDays(i);
if (!DateInDayOfWeek(potentialRunTime))
continue;
potentialRunTime = potentialRunTime.Date + TimeToRun;
if (potentialRunTime < DateTime.Now)
continue;
return potentialRunTime;
}
return DateTime.MinValue;
}
The rough logic is:
For each day starting from today:
-Check if day is valid, if not skip to next day
-Create the runtime for the day
-Check if the runtime is in the past, if it is skip to next day else return this runtime.
The checking if it is in the past is obviously superfluous for all loops after the first but it is neater to do it for all loops and I doubt the extra comparison is likely to be a bottleneck. :)
DateInDayOfWeek in the above is just a method that returns true if the passed day matches one of the days of week held in the DaysToRun property. I couldn't use hasFlags since I wasn't using .NET 4 in writing my test code. You might want to keep it as a separate method though to avoid it getting cluttered. ;-)
in both of else statements you need to return the next day to run the task,
you can do it by simple loop.
DaysOfWeek DaysToRun = DaysOfWeek.Friday | DaysOfWeek.Monday;
TimeSpan timeToRun = new TimeSpan(12,0,0);
DateTime now = DateTime.Today;
DaysOfWeek Day = (DaysOfWeek)Enum.Parse(typeof(DaysOfWeek), now.DayOfWeek.ToString());
if (DaysToRun.HasFlag(Day))
{
if (now.TimeOfDay < timeToRun )
{
MessageBox.Show(nowTime.ToString());
}
else
{
//return next day
}
}
else
{
//return next day
}
I'm using bitwise to collect weekdays or month days.
Here are the bitwise values for weekdays:
2 Sunday
4 Monday
8 Tuesday
16 Wednesday
32 Thursday
64 Friday
128 Saturday
If you need to calculate the next run date for Monday, Thursday and Saturday then you'll need to sum 4, 32 and 128 and pass the result value as a collection day.
Example: (4 + 32 + 128) = 164
You will use the same method as a collection day for month days.
Here are the bitwise values for month days, from day 1 to 31:
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
2147483648
Here is the function:
static DateTime GetNextRun(int hour, int min, bool isDaily, bool isWeekly, bool isMonthly, bool isLastDayOfMonth, int collectionDay)
{
var today = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, hour, min, 0, 0);
var tomorrow = today.AddDays(1);
if (isDaily)
{
return tomorrow;
}
else if (isWeekly)
{
if (collectionDay < 2)
{
throw new Exception("The collection Day is invalid.");
}
if (collectionDay > 255)
{
throw new Exception("The collection Day is invalid.");
}
for (int i = 1; i < 8; i++)
{
var dayOfWeek = (int)today.AddDays(i).DayOfWeek;
var power = (int)(Math.Pow(2, dayOfWeek + 1));
if ((power & collectionDay) > 0)
{
return today.AddDays(i);
}
}
}
else if (isMonthly)
{
var nextDate = tomorrow;
if (collectionDay < 2 && isLastDayOfMonth)
{
return new DateTime(tomorrow.Year, tomorrow.Month, GetDaysInMonth(tomorrow), hour, min, 0, 0);
}
if (collectionDay < 2)
{
throw new Exception("The collection Day is invalid.");
}
while (true)
{
var power = (int)(Math.Pow(2, nextDate.Day));
if ((power & collectionDay) > 0)
{
if (isLastDayOfMonth && nextDate.Month != tomorrow.Month)
{
return new DateTime(tomorrow.Year, tomorrow.Month, GetDaysInMonth(tomorrow), hour, min, 0, 0);
}
return nextDate;
}
nextDate = nextDate.AddDays(1);
}
}
return DateTime.MaxValue;
}
static int GetDaysInMonth(DateTime d)
{
for (int i = 28; i < 33; i++)
{
try
{
new DateTime(d.Year, d.Month, i, 1, 1, 0, 0);
}
catch (Exception)
{
return (i - 1);
}
}
return 31;
}
How to use:
To get the next Monday:
var d = GetNextRun(16, 13, false, true, false, false, 2);
To get the next Monday or Thursday or Saturday "(4 + 32 + 128) = 164":
var d = GetNextRun(16, 13, false, true, false, false, 164);
To get the next second days of a month:
var d = GetNextRun(16, 13, false, false, true, false, 4);
To get the next 2 or 5 or 7 days of a month "(4 + 32 + 128) = 164":
var d = GetNextRun(16, 13, false, false, true, false, 164);
Also you can set the isLastDayOfMonth flag to calculate the last day of month for the next run.
Given a date (of type DateTime), how do I find the 3rd Friday in the month of that date?
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.
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);
}
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;
}
}
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
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
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);
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!");
}
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];
}
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];
}
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);
}
}
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;
}
Late to the game but here's my solution to add DateTime extension functionality which accounts for the January to December issue when subtracting occurrence values and accounts for if the occurrence is 5 - which in my case should return the last occurrence which would either be number 4 or 5 depending on where the day lands in the month:
public static DateTime NthOf(this DateTime CurDate, int Occurrence, DayOfWeek Day)
{
//Last day of month if 5 - return last day.
if (Occurrence == 5)
{
return LastDayOfMonth(CurDate, Day);
}
var fday = new DateTime(CurDate.Year, CurDate.Month, 1, CurDate.Hour, CurDate.Minute, CurDate.Second);
var firstoccurrence = fday.DayOfWeek == Day ? fday : fday.AddDays(Day - fday.DayOfWeek);
// CurDate = 2011.10.1 Occurance = 1, Day = Friday >> 2011.09.30 FIX.
if (firstoccurrence.Month < CurDate.Month)
{
Occurrence = Occurrence + 1;
} else if (firstoccurrence.Month == 12 && CurDate.Month == 1)
{
Occurrence = Occurrence + 1;
}
return firstoccurrence.AddDays(7 * (Occurrence - 1));
}
public static DateTime LastDayOfMonth(this DateTime CurDate, DayOfWeek Day)
{
DateTime EndOfMonth = new DateTime(CurDate.Year, CurDate.Month, 1).AddMonths(1).AddDays(-1);
while (EndOfMonth.DayOfWeek != Day)
{
EndOfMonth = EndOfMonth.AddDays(-1);
}
return EndOfMonth;
}
The you can call your method with something like this:
Console.WriteLine(DateTime.Now.NthOf(3, DayOfWeek.Friday).ToString());
This would return the third Friday of the current month and log it to the console as a string value. It extends from DateTime nicely and does not require any usage of a static helper class or any additional moving parts.
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);
}
}
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);
Here's my algorithm:
Find the number of days until the upcoming Friday.
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).
If the months are not equal, return the counter from [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).
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];
}
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);
}
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));
I know this post is old. I have this solution, trying to find a more clean code. #unclebob
public static DateTime FindTheNthDay(
int year, int month, DayOfWeek day, int occurrence)
{
var startDate = new DateTime(year, month, 1);
while(startDate.DayOfWeek != day)
{
startDate = startDate.AddDays(1);
}
var nDays = 7 * (occurrence - 1);
var result = startDate.AddDays(nDays);
return result;
}
> FindTheNthDay(2006, 11, DayOfWeek.Friday, 4)
[11/24/2006 12:00:00 AM]
> FindTheNthDay(2005, 11, DayOfWeek.Friday, 4)
[11/25/2005 12:00:00 AM]
> FindTheNthDay(2004, 11, DayOfWeek.Friday, 4)
[11/26/2004 12:00:00 AM]
> FindTheNthDay(2003, 11, DayOfWeek.Friday, 4)
[11/28/2003 12:00:00 AM]
> FindTheNthDay(1983, 11, DayOfWeek.Friday, 4)
[11/25/1983 12:00:00 AM]
> FindTheNthDay(1978, 11, DayOfWeek.Friday, 4)
[11/24/1978 12:00:00 AM]
> FindTheNthDay(1972, 11, DayOfWeek.Friday, 4)
[11/24/1972 12:00:00 AM]
public static bool IsThirdWednesday(DateTime inputDate)
{
DateTime firstDayOfMonth = new DateTime(inputDate.Year, inputDate.Month, 1);
DateTime firstDayOfNextMonth = firstDayOfMonth.AddMonths(1);
int wednesdayCount = 0;
while(firstDayOfMonth < firstDayOfNextMonth)
{
if (firstDayOfMonth.DayOfWeek == DayOfWeek.Wednesday)
wednesdayCount++;
if (wednesdayCount == 3)
{
if (inputDate == firstDayOfMonth)
return true;
else
return false;
}
firstDayOfMonth = firstDayOfMonth.AddDays(1);
}
return false;
}
I've got the Day of the week stored in a database table (that I do not control), and I need to use it in my code.
Problem is, I want to use the System.DayOfWeek enum for representation for this, and the sequences are not the same.
In the database, it's as follows:
1 2 3 4 5 6 7
S M T W T F S
I need it as follows:
0 1 2 3 4 5 6
M T W T F S S
What's the most elegant way to do this?
for example, I could do:
i = dayOfWeek;
i = i - 2;
if (i < 0) {
i = 6;
}
but that's a bit inelegant. Any suggestions?
<EDIT>
Ahem. Apparently (.net reflector says) DayOfWeek is 0 indexed starting with Sunday.
Always read the docs before asking daft questions.
However, I'm still interested in an answer, just to satisfy my own curiosity, so go for it.
</EDIT>
The value you want is
(DayOfWeek)((dbDay + 5) % 7)
using the modulo operator %.
Wrap it in a function:
public int DbToDayOfWeek(int dbDay)
{
if (dbDay == 1)
return 6;
return dbDay -2;
}
Or:
public DayOfWeek DbToDayOfWeek(int dbDay)
{
if (dbDay == 1)
return DayOfWeek.Sunday;
return (DayOfWeek)(dbDay - 2);
}
Although I can't imagine the values changing, you should really avoid assuming that the DayOfWeek enumerated values will stay the same - so code accordingly.
static DayOfWeek[] _toDaysTable = new DayOfWeek[] {
DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday,
DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday
};
static DayOfWeek ToDayOfWeek(int myDayOfWeek)
{
int index = myDayOfWeek - 1;
if (index < 0 || index >= _toDaysTable.Length)
throw new ArgumentOutOfRangeException("myDayOfWeek");
return _toDaysTable[index];
}
static int FromDayOfWeek(DayOfWeek day)
{
int index = Array.IndexOf(_toDaysTable, day);
if (index < 0)
throw new ArgumentOutOfRangeException("day");
return index + 1;
}
You could just create your own enum and map the enum directly to the value in the database
public enum DayOfWeek
{
Mon = 2,
Tue = 3,
Wed = 4,
Thu = 5,
Fri = 6,
Sat = 7,
Sun = 1
}
Then you could use an extension method of your DayOfWeek type to retrieve the value:
public static int ToInt(this DayOfWeek dow)
{
return (int)dow;
}
Unless you are relying on the DayOfWeek for actual comparisons with Dates, otherwise you will have to do the conversion between the offsets.