Calculate business days in future to send reminder emails - c#

I am working on this employee performance review application. I need to send out reminder emails when the user has left 3 and 1 business days before the end of the 15 business day period.
Here is how I plan on doing it:
I have the Last Modified Date for each Review which I should be able to use to find out 15 business days in future(call it deadLineToSubmit for our example). Once I got deadLineToSubmit I should check to see if currentDate + 3 days(date) == deadLineToSubmit Then add that review to my list which I will be using to send email.
I have a function called GetByDayPrior where I am sending values 3 and 1 to check if user have left 3 business days or 1. Below is the code:
public List<int> GetByDaysPrior(int daysPrior)
{
List<int> response = new List<int>();
using (DAL.HumanResourcesEntities context = new DAL.HumanResourcesEntities())
{
DateTime currentDate = DateTime.Now.Date;
var lastModifiedDate = context.Reviews
.Where(s => s.IsActive == false)
.Where(s => s.ReviewStatusId == (int)ReviewStatuses.EmployeeSignature)
.Select(v => v.ModifiedDate)
.ToList();
AddBusinessDays(lastModifiedDate, daysPrior);
var lastRuntime = context.ApplicationRuntimeVariables.Where(y => y.ParameterName == "RemindersSentDT").Select(x => x.ParameterValue).FirstOrDefault().Date;
DateTime deadLineToSubmitReview = currentDate.AddDays(daysPrior).Date;
if (lastRuntime.AddHours(24).Date <= currentDate) //should it be == current date ?
{
var reviewIDs = context.Reviews
.Where(s => s.ModifiedDate < deadLineToSubmitReview)
.Where(s => s.ReviewStatusId == (int)ReviewStatuses.EmployeeSignature) //Check to make sure reminders only go when Review Status is Employee Signature
.Where(s => s.IsActive == false)
.Select(v => v.ReviewId)
.ToList();
response = reviewIDs.ToList();
}
return response;
}
}
I have another function called AddBusinessDays should give me the deadLineToSubmit when I pass in LastModifiedDate and 15 days as parameter. But currently its not working because firstly I cannot find out how to pass List of DataTime as a paramater in this function. Also when I defined date parameter in AddBusinessDays as List now all the instances of date and DayOfWeek are complanining
"Error 32 Cannot implicitly convert type
'System.Collections.Generic.List' to
'System.DateTime'
Below is my AddBusinessDays function.
public static DateTime AddBusinessDays(List<DateTime> date, int days)
{
if (days < 0)
{
throw new ArgumentException("days cannot be negative", "days");
}
if (days == 0) return date;
if (date.DayOfWeek == DayOfWeek.Saturday)
{
date = date.AddDays(2);
days -= 1;
}
else if (date.DayOfWeek == DayOfWeek.Sunday)
{
date = date.AddDays(1);
days -= 1;
}
date = date.AddDays(days / 5 * 7);
int extraDays = days % 5;
if ((int)date.DayOfWeek + extraDays > 5)
{
extraDays += 2;
}
return date.AddDays(extraDays);
}
Here's what I need to know: How to send DateTime List as parameter in AddBusinessDays function. How to make AddBusinessDays function work because now we are passing list instead of single dateTime. Is my thinking logic correct to solve this issue ?
Thanks a Lot! :)

date is a list and not a single date.
You can make an operation on all items of the list by using Select
for example:
lastModifiedDate = AddBusinessDays(lastModifiedDate, daysPrior);
Step 1: Convert your function to receive a single date
private static DateTime AddBusinessDays(DateTime date, int days)
{
if (days < 0)
{
throw new ArgumentException("days cannot be negative", "days");
}
if (days == 0) return date;
if (date.DayOfWeek == DayOfWeek.Saturday)
{
date = date.AddDays(2);
days -= 1;
}
else if (date.DayOfWeek == DayOfWeek.Sunday)
{
date = date.AddDays(1);
days -= 1;
}
date = date.AddDays(days / 5 * 7);
int extraDays = days % 5;
if ((int)date.DayOfWeek + extraDays > 5)
{
extraDays += 2;
}
return date.AddDays(extraDays);
}
Step 2: Use the single date function for the list of dates
public static List<DateTime> AddBusinessDays(List<DateTime> date, int days)
{
return date.Select(d => AddBusinessDays(d, days)).ToList();
}

Related

How to count working 15 days and get enddate?

How to calculate actual working days of my when user checkin in hotel? I want to count working days only except Saturday and Sunday. Please check below function its count working days but in parameter I entered startdate and enddate.
I want send only startdate its automatically count 15 working days and return me enddate.
//Days count
public static double GetBusinessDays(DateTime startD, DateTime endD)
{
double calcBusinessDays =
1 + ((endD - startD).TotalDays * 5 -
(startD.DayOfWeek - endD.DayOfWeek) * 2) / 7;
if (endD.DayOfWeek == DayOfWeek.Saturday) calcBusinessDays--;
if (startD.DayOfWeek == DayOfWeek.Sunday) calcBusinessDays--;
return calcBusinessDays;
}
I want like this:
public static Datetime GetBusinessDays(DateTime startDate)
{
Datetime After15WorkingDaysDate;
return After15WorkingDaysDate;
}
Here are two methods.
The idea is to generate each date in the range, decide whether it is a Business Day, and only then add it to the result list.
GetBusinessDaysInRange returns a list of the dates of the Business Days between the given start and end date. End date is exclusive, i.e. if the end date is a Business Day, it will not be part of the result.
// Returns a list of the dates of the Business Days between the given start and end date
public static IEnumerable<DateTime> GetBusinessDaysInRange(DateTime startDate, DateTime endDate, DayOfWeek[] closedOn) {
if (endDate < startDate) {
throw new ArgumentException("endDate must be before startDate");
}
var businessDays = new List<DateTime>();
var date = startDate;
while (date < endDate) {
if (!closedOn.Contains(date.DayOfWeek)) {
businessDays.Add(date);
}
date = date.AddDays(1);
}
return businessDays;
}
GetFixedNumberOfBusinessDays returns a list of the dates of the Business Days from the given start with the given number of days (the method you asked for).
// Returns a list of the dates of the Business Days from the given start with the given number of days
public static IEnumerable<DateTime> GetFixedNumberOfBusinessDays(DateTime startDate, int numberOfBusinessDays, DayOfWeek[] closedOn) {
if (numberOfBusinessDays < 0) {
throw new ArgumentException("numberOfBusinessDays must be zero or positive.");
}
var businessDays = new List<DateTime>();
var date = startDate;
while (businessDays.Count() < numberOfBusinessDays) {
if (!closedOn.Contains(date.DayOfWeek)) {
businessDays.Add(date);
}
date = date.AddDays(1);
}
return businessDays;
}
The parameter DayOfWeek[] closedOn was introduced because you do not want to hardcode the days of the week that are not Business Days.
The return type was changed to IEnumerable<DateTime> so this method is more universal. If you only want the number of days and are not interested in the actual dates, just run a .Count() on the result. If you want the end date, call .Last().
.Net Fiddle with usage examples:
var closedOn = new DayOfWeek[] { DayOfWeek.Saturday, DayOfWeek.Sunday };
var start = new DateTime(2018, 07, 23);
var numberOfDays = 10;
var businessDays = GetFixedNumberOfBusinessDays(end, numberOfDays, closedOn);
int actualNumberOfBusinessDays = businessDays.Count(); // 10
DateTime endDate = businessDays.Last(); // Friday, August 3, 2018
It should be generic method. You can add different work day in another place.
public static DateTime AddWorkdays(this DateTime originalDate, int workDays)
{
DateTime tmpDate = originalDate;
while (workDays > 0)
{
tmpDate = tmpDate.AddDays(1);
if (tmpDate.DayOfWeek == DayOfWeek.Saturday ||
tmpDate.DayOfWeek == DayOfWeek.Sunday )
workDays--;
}
return tmpDate;
}
DateTime endDate = startDate.AddWorkdays(15);

14 days(fortnightly) logic in advance and sequence order [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Can anyone please help me. What I need to C# logic for the display in advance and sequence order six date list on Monday. In advance date list should change after 12.00PM. For example on Monday 28 March, date will display same day(28 March) till 12.00pm. After 12.01 pm, first date start from 11 April 2016. Also same sequence(attached screen). It is never display 4th April, 18th April etc.. (sequence order )..
This is my effort C# code, it's only display single date list.
public static void Main(string[] args)
{
DateTime time = new DateTime(2016, 3, 25);
var breaks = DateTime.Today.AddHours(12D);
if (breaks>=DateTime.Now)
{
}
else if (time<DateTime.Now)
{
}
DateTime anotherTime = DateTime.Now;
var allTimes = new HashSet<DateTime>();
for (int i = 0; i < 6; i++)
{
anotherTime = time.AddDays(14);
time = anotherTime;
Console.WriteLine(anotherTime.ToLongDateString());
allTimes.Add(time);
}
}
}
This is my final out put in advance six date list
Try this
static void Main(string[] args)
{
DateTime today = DateTime.Now;
if ((today.DayOfWeek == DayOfWeek.Monday) && (today > new DateTime(today.Year, today.Month, today.Day, 12, 0, 0)))
{
today.AddDays(1);
}
int offsetToMonday = 7 - (((int)today.DayOfWeek - 1) % 7);
DateTime nextMonday = today.AddDays(offsetToMonday);
DateTime startDate = (nextMonday.DayOfYear / 7 % 2) == 0 ? nextMonday : nextMonday.AddDays(7);
DateTime endDate = startDate.AddDays(83);
List<DateTime> dates = new List<DateTime>();
for (DateTime date = startDate; date <= endDate; date = date.AddDays(1))
{
dates.Add(date);
}
var groupedDays = dates.AsEnumerable()
.GroupBy(x => x.Subtract(startDate).Days % 14)
.OrderBy(x => x.Key)
.Where(x => x.Key == 0);
foreach (var group in groupedDays)
{
foreach (DateTime day in group)
{
Console.WriteLine(day.ToString("dddd, dd MMMM yyyy"));
}
Console.WriteLine();
}
Console.ReadLine();
}
I am not sure if I understand what you exactly want. But I have a similar function in my repository.
public static DateTime GetNextDayOfWeek(DateTime _date, DayOfWeek _dayOfWeek)
{
DateTime Result;
int diff = _date.DayOfWeek - _dayOfWeek;
if (diff == 0 && _date.Hour * 100 + _date.Minute <= 1200)
return _date;
if (diff < 0)
{
diff += 7;
}
Result = _date.AddDays(-1 * diff).Date;
if (Result <= _date)
Result = Result.AddDays(7);
return Result;
}
I added this part for your needs
if (diff == 0 && _date.Hour * 100 + _date.Minute <= 1200) return _date;
This functions takes a date and a desired day of week and returns the closest next date when the desired day of week will be present.
Since I understand you want 6 packs of these, you can put it in a loop like this:
DateTime next;
next=DateTime.Now;
foreach(DayOfWeek dow in Enum.GetValues(typeof(DayOfWeek)))
{
for(var n=0;n<6;n++)
{
next=GetNextDayOfWeek(next, dow);
//do something with your date
}
}
You could add another parameter for how many days you wand advance. Currently it if fixed to 7. In your case you would need 14.

C# check if current date is 1, 2 or 3 of the month, ignore weekend and additional List<DateTime>

I need to check if DateTime.Now is in the first 3 business days of each month (from Mon - Fri). I also need to provide a List<DateTime> with national holidays and these should be handled accordingly.
If DateTime.Now is Saturday and is 1 of the month, first 3 business days are Monday, Tuesday, Wednesday (3, 4, 5 of the month).
public bool IsBusinessDay()
{
DateTime now = DateTime.Now;
DateTime fbd = new DateTime();
DateTime sbd = new DateTime();
DateTime tbd = new DateTime();
DateTime fm = new DateTime(now.Year, now.Month, 1);
DateTime sm = new DateTime(now.Year, now.Month, 2);
DateTime tm = new DateTime(now.Year, now.Month, 3);
// first business day
if (fm.DayOfWeek == DayOfWeek.Sunday)
{
fbd = fm.AddDays(1);
}
else if (fm.DayOfWeek == DayOfWeek.Saturday)
{
fbd = fm.AddDays(2);
}
else
{
fbd = fm;
}
//second business day
if (sm.DayOfWeek == DayOfWeek.Sunday)
{
sbd = sm.AddDays(1);
}
else if (sm.DayOfWeek == DayOfWeek.Saturday)
{
sbd = sm.AddDays(2);
}
else
{
sbd = sm;
}
//third business day
if (tm.DayOfWeek == DayOfWeek.Sunday)
{
tbd = tm.AddDays(1);
}
else if (tm.DayOfWeek == DayOfWeek.Saturday)
{
tbd = tm.AddDays(2);
}
else
{
tbd = tm;
}
if (now == fdb || now == sbd || now == tbd)
{
return true;
}
return false;
}
Is this a good approach? How can I add a List<DateTime> with holidays and check that the current date is not holiday?
I have a feeling I'm over thinking this, and thinking it in a bad way. I don't know why but same feeling tells me there is an easier way to do it.
This should do what you want. You'll have to supply the set of holidays.
public static bool IsFirstThreeBusinessDays(DateTime date, HashSet<DateTime> holidays)
{
DateTime dt = new DateTime(date.Year, date.Month, 1);
int businessDaysSeen = 0;
while (businessDaysSeen < 3)
{
if (dt.DayOfWeek != DayOfWeek.Saturday &&
dt.DayOfWeek != DayOfWeek.Sunday &&
!holidays.Contains(dt))
{
if (dt == date.Date)
{
return true;
}
businessDaysSeen++;
}
dt = dt.AddDays(1);
}
return false;
}
You can also do this using LINQ.
public static bool IsFirstThreeBusinessDays(DateTime date, HashSet<DateTime> holidays)
{
var query =
Enumerable.Range(1, DateTime.DaysInMonth(date.Year, date.Month))
.Select(o => new DateTime(date.Year, date.Month, o))
.Where(o => o.DayOfWeek != DayOfWeek.Saturday && o.DayOfWeek != DayOfWeek.Sunday
&& !holidays.Contains(o))
.Take(3);
return query.Contains(date);
}
EDIT: I didn't read the question carefully enough, although parts of my old answer are still applicable. The approach I would take here is to create a method that enumerates the business days of the month, then take 3 from that.
Here's how:
public static IEnumerable<DateTime> BusinessDaysOfMonth(DateTime time)
{
var month = new DateTime(time.Year, time.Month, 1);
var nextMonth = month.AddMonths(1);
var current = month;
while(current < nextMonth)
{
if (IsWeekday(current) && !IsHoliday(current))
{
yield return current;
}
current = current.AddDays(1);
}
}
(note that some methods are taken from below). Then, all you need where you want to use this is:
// Get first three business days
var firstThreeBizDays = BusinessDaysOfMonth(DateTime.Now).Take(3);
// Check if today is one of them
var result = firstThreeBizDays.Contains(DateTime.Today);
OLD ANSWER:
Ok, so it looks like there's three conditions you need to ensure. They are:
It is the 1st, 2nd, or 3rd day of the month
It is not Saturday or Sunday
The current date is not contained in some set of dates representing holidays
This translates fairly straightforwardly to code:
public static bool IsFirstThreeDays(DateTime time) => time.Day < 4;
public static bool IsWeekday(DateTime time)
{
var dow = time.DayOfWeek;
return dow != DayOfWeek.Saturday && dow != DayOfWeek.Sunday;
}
public bool IsHoliday(DateTime time)
{
ISet<DateTime> holidays = ??; // Decide whether this is a member or an arg
return holidays.Contains(time.Date);
}
Note that the holidays set needs to contain the Day component of any DateTime from each holiday.
Now your method is presumably just:
public static bool IsDayWhatYouWant()
{
var now = DateTime.UtcNow;
return IsFirstThreeDays(now) && IsWeekday(now) && !IsHoliday(now);
}

How to determine if a date it was yesterday, in the last month, in the last year using c#?

I would like to determine if a DateTime was yesterday, if it was in the last month and if it was in the last year.
For example if today is 2013. 10. 21. then 2013. 10. 20. was yesterday, 2013. 09. 23. was in the last month and 2012. 03. 25. was in the last year.
How can i determine these using c#?
// myDate = 2012.02.14 ToDate ... you know
if (myDate == DateTime.Today.AddDays(-1);)
Console.WriteLine("Yestoday");
else if (myDate > DateTime.Today.AddMonth(-1) && myDate < DateTime.Today)
Console.WriteLine("Last month");
// and so on
it needs test and fixes, but it is the way ;)
bool IsYesterday(DateTime dt)
{
DateTime yesterday = DateTime.Today.AddDays(-1);
if (dt >= yesterday && dt < DateTime.Today)
return true;
return false;
}
bool IsInLastMonth(DateTime dt)
{
DateTime lastMonth = DateTime.Today.AddMonths(-1);
return dt.Month == lastMonth.Month && dt.Year == lastMonth.Year;
}
bool IsInLastYear(DateTime dt)
{
return dt.Year == DateTime.Now.Year - 1;
}
I think testing like this could do the trick:
if(new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).AddMonths(-1) > dateToTestIfLastMonth){
http://msdn.microsoft.com/en-us/library/8ysw4sby.aspx
You can subtract dates then check the timespan object.
Straightforward implementation:
public enum DateReference {
Unknown,
Yesterday,
LastMonth,
LastYear,
}
public static DateReference GetDateReference(DateTime dateTime) {
var date = dateTime.Date;
var dateNow = DateTime.Today;
bool isLastYear = date.Year == dateNow.Year - 1;
bool isThisYear = date.Year == dateNow.Year;
bool isLastMonth = date.Month == dateNow.Month - 1;
bool isThisMonth = date.Month == dateNow.Month;
bool isLastDay = date.Day == dateNow.Day - 1;
if (isLastYear)
return DateReference.LastYear;
else if (isThisYear && isLastMonth)
return DateReference.LastMonth;
else if (isThisYear && isThisMonth && isLastDay)
return DateReference.Yesterday;
return DateReference.Unknown;
}

How to get 14 days prior to the given date avoiding holidays

In my system ,the due date of the bill must be 14 days after the issued date.
I have due date and I want to know issued date .
I have to calculate :
issued date = 14 days prior to the due date
but 14 days must be business days ,not holidays.
Holidays is stored in a table 'tblHolidayMaster' like this,
Date Description
2012/05/13 Mother's
Day2012/06/02 Saturnday2012/12/25 Christmas
How can I calculate the issued date avoiding holidays?
Thank you for all of your interests and replies.
I would calculate the Date using a function like the one below (which i use)
public static DateTime AddBusinessDays(DateTime date, int days)
{
if (days == 0) return date;
if (date.DayOfWeek == DayOfWeek.Saturday)
{
date = date.AddDays(2);
days -= 1;
}
else if (date.DayOfWeek == DayOfWeek.Sunday)
{
date = date.AddDays(1);
days -= 1;
}
date = date.AddDays(days / 5 * 7);
int extraDays = days % 5;
if ((int)date.DayOfWeek + extraDays > 5)
{
extraDays += 2;
}
int extraDaysForHolidays =-1;
//Load holidays from DB into list
List<DateTime> dates = GetHolidays();
while(extraDaysForHolidays !=0)
{
var days = dates.Where(x => x >= date && x <= date.AddDays(extraDays)).Count;
extraDaysForHolidays =days;
extraDays+=days;
}
return date.AddDays(extraDays);
}
Haven't tested the ast section that does the holidays
I went with the straight forward looping solution, so it will be slow for long intervals. But for short intervals like 14 days, it should be quite fast.
You need to pass in the holidays in the constructor. An instance of BusinessDays is immutable and can be reused. In practice you probably will use an IoC singleton or a similar construct to get it.
AddBusinessDays throws an ArgumentException if the start date is a non business day, since you didn't specify how to treat that case. In particular AddBusinessDays(0) on a non business day would have strange properties otherwise. It'd either break time reversal symmetry, or return a non business day.
public class BusinessDays
{
private HashSet<DateTime> holidaySet;
public ReadOnlyCollection<DayOfWeek> WeekendDays{get; private set;}
public BusinessDays(IEnumerable<DateTime> holidays, IEnumerable<DayOfWeek> weekendDays)
{
WeekendDays = new ReadOnlyCollection<DayOfWeek>(weekendDays.Distinct().OrderBy(x=>x).ToArray());
if(holidays.Any(d => d != d.Date))
throw new ArgumentException("holidays", "A date must have time set to midnight");
holidaySet = new HashSet<DateTime>(holidays);
}
public BusinessDays(IEnumerable<DateTime> holidays)
:this(holidays, new[]{DayOfWeek.Saturday, DayOfWeek.Sunday})
{
}
public bool IsWeekend(DayOfWeek dayOfWeek)
{
return WeekendDays.Contains(dayOfWeek);
}
public bool IsWeekend(DateTime date)
{
return IsWeekend(date.DayOfWeek);
}
public bool IsHoliday(DateTime date)
{
return holidaySet.Contains(date.Date);
}
public bool IsBusinessDay(DateTime date)
{
return !IsWeekend(date) && !IsHoliday(date);
}
public DateTime AddBusinessDays(DateTime date, int days)
{
if(!IsBusinessDay(date))
throw new ArgumentException("date", "date bust be a business day");
int sign = Math.Sign(days);
while(days != 0)
{
do
{
date.AddDays(sign);
} while(!IsBusinessDay(date));
days -= sign;
}
return date;
}
}
I think that is what you required. It is simple and I have tested it and it is working... And it is not a bad approach to write a function or SP in databases rather to write the complex code in C#... (change column name of date as in your db.)
Make it function or SP as what you want.
Note: Comment the check of 'Saturday' and 'Sunday'. If it is already added in your table reocrds.
declare #NextWorkingDate datetime
declare #CurrentDate datetime
set #CurrentDate = GETDATE()
set #NextWorkingDate = #CurrentDate
declare #i int = 0
While(#i < 14)
Begin
if(((select COUNT(*) from dbo.tblHolidayMaster where convert(varchar(10),[Date],101) like convert(varchar(10),#NextWorkingDate,101)) > 0) OR DATENAME(WEEKDAY,#NextWorkingDate) = 'Saturday' OR DATENAME(WEEKDAY,#NextWorkingDate) = 'Sunday')
Begin
print 'a '
print #NextWorkingDate
set #NextWorkingDate = #NextWorkingDate + 1
CONTINUE
End
else
Begin
print 'b '
print #NextWorkingDate
set #NextWorkingDate = #NextWorkingDate + 1
set #i = #i + 1
CONTINUE
End
End
print #NextWorkingDate
I calculate the issued date avoid your holiday from your table 'tblHolidayMaster' only.
int addDay = -14;
DateTime dtInputDay = System.DateTime.Now;//Your input day
DateTime dtResultDate = new DateTime();
dtResultDate = dtInputDay.AddDays(addDay);
bool result = false;
string strExpression;
DataView haveHoliday;
while (!result) {
strExpression = "Date >='" + Convert.ToDateTime(dtResultDate.ToString("yyyy/MM/dd")) + "' and Date <='" + Convert.ToDateTime(dtInputDay.ToString("yyyy/MM/dd")) + "'";
haveHoliday = new DataView(tblHolidayMaster);
haveHoliday.RowFilter = strExpression;
if (haveHoliday.Count == 0) {
result = true;
} else {
addDay = -(haveHoliday.Count);
dtInputDay = dtResultDate.AddDays(-1);
dtResultDate = dtResultDate.AddDays(addDay);
}
}
Your issued date is dtResultDate
Try the following link,
http://www.c-sharpcorner.com/uploadfile/tirthacs/difference-between-two-dates-excluding-weekends/

Categories