Determine if a date is a "work day off" - c#

At my work, we are on the 9/80 plan where we get every other Friday off. We've got a small program that shows a DevExpress.Scheduler control and I'd like to color our "Friday's off" a different color. What I need to know is how do I know if a date is one of our Friday's off? The Friday's off will always be every other week (in other words, we don't skip a week due to a holiday or something like that). I have the date of our First Friday off of the year, so I think I can use that somehow...I can also get the date from the scheduler as it's drawn so I have something to compare to.
DateTime dtFirstFridayOff = new DateTime(2011, 1, 1);
DateTime dtCellDate = Convert.ToDateTime(e.Cell.Value);
Now I'm a bit lost as to how to check if dtCellDate is a Friday off.

public static bool IsDateMultipleDays(DateTime originalDate, int numberOfDays, DateTime potentialDate)
{
var original = originalDate.Date; // to make sure that it doesn't have a time portion
var potential = potentialDate.Date;
var difference = potential - original;
return (int)difference.TotalDays % numberOfDays == 0;
}
Then you'd call it like this:
IsDateMultipleDays(dtFirstFridayOff, 14, dtCellDate)

Try this:
bool IsFridayOff(DateTime dt)
{
if (dt.DayOfWeek != DayOfWeek.Friday)
{
return false;
}
DateTime dtFirstFridayOff = new DateTime(2011, 1, 1);
TimeSpan span = dtFirstFridayOff - dt.Date;
return (int) span.TotalDays%14 == 0;
}

Related

How to set DateTime to start/end of the day?

I want to calculate the start DateTime and end DateTime of the current week. First of all I created a class holding both information
internal class ReportTimeSpan
{
public DateTime From { get; set; }
public DateTime To { get; set; }
}
After that this is my calculation
public ReportTimeSpan GetTimeSpanForThisWeek()
{
int amountOfDays = GetAmountOfWeekDays();
int currentDayIndex = GetCurrentWeekDayIndex();
DateTime weekStart = DateTime.Now.AddDays(-currentDayIndex);
int differenceCurrentDayIndexToLastDayIndex = amountOfDays - currentDayIndex;
DateTime weekEnd = DateTime.Now.AddDays(differenceCurrentDayIndexToLastDayIndex);
return new ReportTimeSpan()
{
From = weekStart,
To = weekEnd
};
}
private int GetAmountOfWeekDays()
{
string[] dayNames = Enum.GetNames(typeof(DayOfWeek));
return dayNames.Length;
}
private int GetCurrentWeekDayIndex()
{
DayOfWeek dayOfWeek = DateTime.Now.DayOfWeek;
return (int)dayOfWeek;
}
}
The date of both values is correct, but the time is wrong.
The variable weekStart should have a time of "00:00:00"
The variable weekEnd should have a time of "23:59:59" (not sure about that)
Are there any methods I can use for this? (I don't want to use external packages)
I expect you want something like this:
weekStart = DateTime.Now.AddDays(-currentDayIndex).Date;
As Tim notes, you can simplify this to:
weekStart = DateTime.Today.AddDays(-currentDayIndex);
.Date will remove the time component, so you're just left with the date and a 00:00:00 time. .Today will return today's date without a time component.
For weekEnd, we should add the number of days in the week to weekStart, and then step back 1 tick to take it back into the previous day:
weekEnd = weekStart.AddDays(7).AddTicks(-1);
You could also use .AddMilliseconds(-1), .AddSeconds(-1), or whatever amount you require to safely be inside the previous day (some databases will have less than tick precision, etc.).
If you have some reason for using GetAmountOfWeekDays() then substitute 7 in the above with GetAmountOfWeekDays().
Depending on what you're using this for, you might be better off with an inclusive weekStart and an exclusive nextWeekStart comparison:
weekStart = DateTime.Now.AddDays(-currentDayIndex).Date;
nextWeekStart = weekStart.AddDays(7);
bool isInWeek = someDate >= weekStart && somedate < nextWeekStart;
weekStart = weekStart.Date;
weekEnd = weekEnd.AddHours(23).AddMinutes(59).AddSeconds(59).AddMilliseconds(999);
OR
weekStart = weekStart.Date;
weekEnd = weekEnd.AddHours(24).AddMilliseconds(-1);
OR
weekStart = weekStart.Date;
weekEnd = new DateTime(weekEnd .Year, weekEnd .Month, weekEnd .Day, 23, 59, 59);

How to check if between two points in time C#

I'm setting up a scheduling system for one of my projects and one thing in particular that I need to do is allow for multiple windows to be present within each day. A window would represent two points in time, the start and the end.
I am not sure just how I should approach this issue. I can do this in a very hacky way but I would rather know how to do it right, so that I can be satisfied that my code is as it should be.
What I'm currently attempting to do is as seen here:
public class ScheduleWindow
{
public string Name;
public DateTime EndTime;
public DateTime StartTime;
}
I have a name id for my schedule, but for this that is irrelevant.
I have a date in time at which the window will start.
I have a date in time at which the window will end.
The intent for the following method is to add a window to a schedule. I want the schedule to represent my day, so I'm using the current year, month and day and then setting the hours and minutes to the points in time that I would like this window to be active.
public void AddWindow(string name, int startHour, int endHour, int startMinute, int endMinute)
{
var year = DateTime.Now.Year;
var month = DateTime.Now.Month;
var day = DateTime.Now.Day;
var startTime = new DateTime(year: year, month: month, day: day, hour: startHour, minute: startMinute, second: 0, millisecond: 0);
var endTime = new DateTime(year: year, month: month, day: day, hour: endHour, minute: endMinute, second: 0, millisecond: 0);
var window = new ScheduleWindow()
{
EndTime = endTime,
StartTime = startTime,
Name = name
};
_scheduleWindows.Add(window);
}
So now we're to the root of my issue.
I am actually completely unsure of how to check if we are within that time window.
`public bool WindowIsActive()
{
foreach (var window in _scheduleWindows)
{
...
//if any window is currently active, return true
}
}`
I've been fiddling here with this code for some time now, and any help would be super appreciated. If anyone can give me some pointers to perhaps a solution that would work better, that would be awesome!
The goal is to check and see if any window is currently active. Currently, I have no clue how.
I imagine it's look something like this
public bool WindowIsActive()
{
foreach (var window in _scheduleWindows)
{
if (DateTime.Now >= window.StartTime && DateTime.Now <= window.EndTime)
{
return true;
}
}
}
This works because DateTime implements the GreaterThanOrEqual and LessThanOrEqual operators.
Two things to consider about this answer:
This code assumes EndTime is later than StartTime.
If you care about timezones, you should use DateTimeOffset instead.
You can use < and > operators to compare DateTimes.
[edit: I realized that you just wanted to compare the time of day - i.e. disregarding the month and year - you'd use the TimeOfDay property of DateTime]
var timeOfDay = DateTime.Now.TimeOfDay; //this is a TimeSpan type
if(timeOfDay > window.StartTime.TimeOfDay && timeOfDay < window.EndTime.TimeOfDay)
{
//time is within the time window.
}

Get DateTime of the next nth day of the month

If given a date and a variable n, how can I calculate the DateTime for which the day of the month will be the nth Date?
For example, Today is the 17th of June.
I would like a function that when provided 15 would return the DateTime for July 15.
A few more examples:
Today is Feb. 26: Function would return March 30 when provided 30.
Today is Dec. 28. Function would return Jan 4 when provided 4.
Today is Feb. 28. Function would return March 29 when provided 29, unless it was a leap year, in which case it would return Feb 29.
Why not just do?
private DateTime GetNextDate(DateTime dt, int DesiredDay)
{
if (DesiredDay >= 1 && DesiredDay <= 31)
{
do
{
dt = dt.AddDays(1);
} while (dt.Day != DesiredDay);
return dt.Date;
}
else
{
throw new ArgumentOutOfRangeException();
}
}
After many, many edits, corrections and re-writes, here is my final answer:
The method that follows returns a a DateTime representing the next time the day of number day comes up in the calendar. It does so using an iterative approach, and is written in the form of an extension method for DateTime objects, and thus isn't bound to today's date but will work with any date.
The code executes the following steps to get the desired result:
Ensure that the day number provided is valid (greater than zero and smaller than 32).
Enter into a while loop that keeps going forever (until we break).
Check if cDate's month works (the day must not have passed, and the month must have enough days in it).
If so, return.
If not, increase the month by one, set the day to one, set includeToday to true so that the first day of the new month is included, and execute the loop again.
The code:
static DateTime GetNextDate3(this DateTime cDate, int day, bool includeToday = false)
{
// Make sure provided day is valid
if (day > 0 && day <= 31)
{
while (true)
{
// See if day has passed in current month or is not contained in it at all
if ((includeToday && day > cDate.Day || (includeToday && day >= cDate.Day)) && day <= DateTime.DaysInMonth(cDate.Year, cDate.Month))
{
// If so, break and return
break;
}
// Advance month by one and set day to one
// FIXED BUG HERE (note the order of the two calls)
cDate = cDate.AddDays(1 - cDate.Day).AddMonths(1);
// Set includeToday to true so that the first of every month is taken into account
includeToday = true;
}
// Return if the cDate's month contains day and it hasn't passed
return new DateTime(cDate.Year, cDate.Month, day);
}
// Day provided wasn't a valid one
throw new ArgumentOutOfRangeException("day", "Day isn't valid");
}
The spec is a little bit unclear about to do when today is the dayOfMonth. I assumed it was it to return the same. Otherwise it would just be to change to <= today.Day
public DateTime FindNextDate(int dayOfMonth, DateTime today)
{
var nextMonth = new DateTime(today.Year, today.Month, 1).AddMonths(1);
if(dayOfMonth < today.Day){
nextMonth = nextMonth.AddMonths(1);
}
while(nextMonth.AddDays(-1).Day < dayOfMonth){
nextMonth = nextMonth.AddMonths(1);
}
var month = nextMonth.AddMonths(-1);
return new DateTime(month.Year, month.Month, dayOfMonth);
}
Stumbled upon this thread today while trying to figure out this same problem.
From my testing, the following seems to work well and the loop only needs two goes (I think? Maybe 3 max(?)) to get to the answer:
public static DateTime GetNearestSpecificDay(DateTime start, int dayNum)
{
if (dayNum >= 1 && dayNum <= 31)
{
DateTime result = start;
while (result.Day != dayNum)
result = dayNum > result.Day ? result.AddDays(dayNum - result.Day) : new DateTime(result.Month == 12 ? result.Year + 1 : result.Year, (result.Month % 12) + 1, 1);
return result;
}
else
return DateTime.Today;
}
Edit: As requested, here's a less compact version that walks through the logic step by step. I've also updated the original code to account for a required year change when we reach December.
public static DateTime GetNearestSpecificDay(DateTime start, int dayNum)
{
// Check if target day is valid in the Gregorian calendar
if (dayNum >= 1 && dayNum <= 31)
{
// Declare a variable which will hold our result & temporary results
DateTime result = start;
// While the current result's day is not the desired day
while (result.Day != dayNum)
{
// If the desired day is greater than the current day
if (dayNum > result.Day)
{
// Add the difference to try and skip to the target day (if we can, if the current month does not have enough days, we will be pushed into the next month and repeat)
result = result.AddDays(dayNum - result.Day);
}
// Else, get the first day of the next month, then try the first step again (which should get us where we want to be)
else
{
// If the desired day is less than the current result day, it means our result day must be in the next month (it obviously can't be in the current)
// Get the next month by adding 1 to the current month mod 12 (so when we hit december, we go to january instead of trying to use a not real 13th month)
// If result.Month is November, 11%12 = 11; 11 + 1 = 12, which rolls us into December
// If result.Month is December, 12%12 = 0; 0 + 1 = 1, which rolls us into January
var month = (result.Month % 12) + 1;
// Get current/next year.
// Since we are adding 1 to the current month, we can assume if the previous month was 12 that we must be entering into January of next year
// Another way to do this would be to check if the new month is 1. It accomplishes the same thing but I chose 12 since it doesn't require an extra variable in the original code.
// Below can be translated as "If last result month is 12, use current year + 1, else, use current year"
var year = result.Month == 12 ? result.Year + 1 : result.Year;
// Set result to the start of the next month in the current/next year
result = new DateTime(year, month, 1);
}
}
// Return result
return result;
}
else
// If our desired day is invalid, just return Today. This can be an exception or something as well, just using Today fit my use case better.
return DateTime.Today;
}
Fun little puzzle. I generated 100 DateTimes which represent the starting day of each month, then checked each month to see if it had the date we want. It's lazy so we stop when we find a good one.
public DateTime FindNextDate(int dayOfMonth, DateTime today)
{
DateTime yesterday = today.AddDays(-1);
DateTime currentMonthStart = new DateTime(today.Year, today.Month, 1);
var query = Enumerable.Range(0, 100)
.Select(i => currentMonthStart.AddMonths(i))
.Select(monthStart => MakeDateOrDefault(
monthStart.Year, monthStart.Month, dayOfMonth,
yesterday)
.Where(date => today <= date)
.Take(1);
List<DateTime> results = query.ToList();
if (!results.Any())
{
throw new ArgumentOutOfRangeException(nameof(dayOfMonth))
}
return results.Single();
}
public DateTime MakeDateOrDefault(
int year, int month, int dayOfMonth,
DateTime defaultDate)
{
try
{
return new DateTime(year, month, dayOfMonth);
}
catch
{
return defaultDate;
}
}

Get Date of every alternate Friday

I am wondering if i can get the date of every alternate friday starting with 13th of April, 2012 to give it as a parameter to a stored procedure using c#, asp.net?
It should also be most recently passed date. Thank you!
Just set a DateTime with the date you want to start at, and then keep adding 14 days:
So to get every other Friday after 4/13 until the end of the year:
DateTime dt = new DateTime(2012, 04, 13);
while (dt.Year == 2012)
{
Console.WriteLine(dt.ToString());
dt = dt.AddDays(14);
}
More info after comment:
If you want the most recent alternate Friday since 2012/04/13, you can compute the number of days between now and 2012/04/13, take the remainder of that divided by 14, and subtract that many days from today's date:
DateTime baseDate = new DateTime(2012, 04, 13);
DateTime today = DateTime.Today;
int days = (int)(today - baseDate).TotalDays;
int rem = days % 14;
DateTime mostRecentAlternateFriday = today.AddDays(-rem);
You can easily make a generator method that would give you the set of fridays:
public IEnumerable<DateTime> GetAlternatingFridaysStartingFrom(DateTime startDate)
{
DateTime tempDate = new DateTime(startDate.year, startDate.Month, startDate.Day);
if(tempDate.DayOfWeek != DayOfWeek.Friday)
{
// Math may be off, do some testing
tempDate = tempDate.AddDays((7 - ((int)DayOfWeek.Friday - (int)tempDate.DayOfWeek) % 7);
}
while(true)
{
yield return tempDate;
tempDate = tempDate.AddDays(14);
}
}
Then, simply use some LINQ to determine how much you want:
var numberOfFridays = GetAlternatingFridaysStartingFrom(DateTime.Today).Take(10);
Why do you need a stored proc?
If you have a date that is Friday, why not just use AddDays(14) in a loop?
If you want to find the nearest Friday from a start date, just use this:
while(date.DayOfWeek != DayOfWeek.Friday)
{
date.AddDays(1);
}
Then use the 14 day loop to get every other Friday.
You can create simple method that will enumerate them like so:
public static IEnumerable<DateTime> GetAlternatingWeekDay(DateTime startingDate)
{
for (int i = 1; ; i++)
{
yield return startingDate.AddDays(14*i);
}
}
Which you can call like this:
DateTime startingDate = DateTime.Parse("2012-04-13");
foreach (var date in GetAlternatingWeekDay(startingDate).Take(10))
{
Console.WriteLine(date.ToString("R"));
}
Alternately, if you need to know the date for a given number of weeks out, you could use code like this:
DateTime date = DateTime.Parse("2012-04-13").AddDays(7 * numberOfWeeks);

How can I determine if a date is the nth List<WeekDay> in a month?

I've seen this question, which looks for the Nth weekday in a specific month, but I want something that will look for the Nth [weekday list] in a given month. Is there an easy way to get that without looping through every day in the month?
For example, if I say I want the 3rd [Mon, Wed, Fri] in a month, and the month starts on a Wednesday, it should return the 1st Monday (1 = Wed, 2 = Fri, 3 = Mon). If I say I want the 2nd last [Mon, Tue, Wed, Thurs] and the last day of the month is a Sunday, it should return the previous Wednesday.
Meh I mean you might be able to come up with something ugly and incomprehensible that is formula-based and avoids looping, but it will be ugly and incomprehensible so bug-prone and a maintenance disaster, and not at all fun to try to come up with.
Instead, just come up with what is easy to write using loops, and then hide the loops by using LINQ:
static class DateTimeExtensions {
private static IEnumerable<DateTime> DaysOfMonth(int year, int month) {
return Enumerable.Range(1, DateTime.DaysInMonth(year, month))
.Select(day => new DateTime(year, month, day));
}
public static DateTime NthDayOfMonthFrom(
this HashSet<DayOfWeek> daysOfWeek,
int year,
int month,
int nth
) {
return
DateTimeExtensions.DaysOfMonth(year, month)
.Where(
date => daysOfWeek.Contains(date.DayOfWeek)
)
.Skip(nth - 1)
.First();
}
}
Usage:
var daysOfWeek = new HashSet<DayOfWeek> {
DayOfWeek.Monday, DayOfWeek.Wednesday, DayOfWeek.Friday
};
DateTime date = daysOfWeek.NthDayOfMonthFrom(2011, 6, 3));
Assert.Equal(date, new DateTime(2011, 6, 6));
Similarly, using Reverse, you can easily write NthLastDayOfWeekFrom. I'll leave it to you to come up with a better name.
I think I'm accepting Jason's answer because I like his solution, however here's what I had come up with before I saw his post. I thought I'd post it because I like the idea of not looping thorough every date, especially if N is a higher value and I'd like to get a date outside the current month.
Instead of looping through all days, I fast-forwarded (or rewind if I'm going backwards) to the closest week, then looped through the next couple of days to find the Nth instance the weekdays specified.
public static DateTime? GetNthWeekDayInMonth(DateTime month, int nth, List<DayOfWeek> weekdays)
{
var multiplier = (nth < 0 ? -1 : 1);
var day = new DateTime(month.Year, month.Month, 1);
// If we're looking backwards, start at end of month
if (multiplier == -1) day = day.AddMonths(1).AddDays(-1);
var dayCount = weekdays.Count;
if (dayCount == 0)
return null;
// Fast forward (or rewind) a few days to appropriate week
var weeks = ((nth - (1 * multiplier)) / dayCount);
day = day.AddDays(weeks * 7);
// Current Nth value
var currentNth = (1 * multiplier) + (weeks * dayCount);
// Loop through next week looking for Nth value
for (var x = 0; x <= 7; x++)
{
if (weekdays.Contains(day.DayOfWeek))
{
if (currentNth == nth)
{
// Verify the nth instance is still in the current month
if (day.Month == month.Month)
return day;
else
return null;
}
currentNth += multiplier;
}
day = day.AddDays(multiplier);
}
return null;
}

Categories

Resources