DateTime Resets at midnight - c#

Hi I am using C# web app on visual studio.
I have written code to be able to distinguish if the time is between 6am-2pm - 2pm-10pm and 10pm - 6am...
The code runs like a dream for the 6-2 - 2-10 times but for the 10pm - 6am.. the code runs fine until midnight and then it just resets my counter to 0 and stays at 0 until 6am.. I can't get my head around why this is doing.. Does anybody have a solution..
public DateTime Shiftstart { get; set; }
public DateTime Shiftend { get; set; }
public string Itemseriesmaster { get; set; }
public string SeriesMasterId { get; set; }
public void CalcShiftPeriod() //constructor
{
DateTime now = DateTime.Now; //date time now
int currentHour = now.Hour; //hour now
int shiftHourStart;
if (currentHour >= 6 && currentHour <= 13)
{
shiftHourStart = 6;
}
else if (currentHour >= 14 && currentHour <= 21)
{
shiftHourStart = 14;
}
else
{
shiftHourStart = 22;
}
Shiftstart = now.Date.AddHours(shiftHourStart);
Shiftend = Shiftstart.AddHours(7);
Shiftend = Shiftend.AddMinutes(59);
Shiftend = Shiftend.AddSeconds(59);
}
The code is calculating total units packed and is working fine, and resets after the given time on both the 6-2 and 2 - 10 shifts..
Until it gets to the 10pm - 6am and then just completely stops at midnight.
BizManager biz = new BizManager();
DataTable dt = new DataTable();
if (DDLProduct.SelectedValue.Equals("G120C-2") || DDLProduct.SelectedValue.Equals("G120 PM240-2") )
{
RefreshMainGridTht(selectedProduct, shiftStart, shiftEnd
);
}
dt = biz.GetPacktstatisticsForShift(shiftStart, shiftEnd, selectedProduct);
GridView1.DataSource = dt.DefaultView;
GridView1.DataBind();
int sumActual = 0;
int sumTarget = 0;
biz.CalculatePackingTotals(dt, out sumActual, out sumTarget);
LabelTotal.Text = sumActual.ToString();
DateTime dtmNow = DateTime.Now;
TimeSpan tsIntoShift = dtmNow - shiftStart;
TimeSpan tsTotalShift = shiftEnd - shiftStart;
double p = tsIntoShift.TotalMinutes / tsTotalShift.TotalMinutes;
int adjustedTarget = Convert.ToInt32(sumTarget * p);
if (sumActual > sumTarget)
{
LabelTotal.ForeColor = Color.Lime;
}
else
{
LabelTotal.ForeColor = Color.Red;
}
}
catch (Exception ex)
{
ErrMsg = App.HandleError(MethodBase.GetCurrentMethod(), ex, string.Empty);

You have to distinguish between currentHour from 0 to 5 and currentHour from 22 to 23.
For example, consider currentHour is 1. Your calculation says that the shift starts at 22:00 of the current day (now.Date.AddHours(22)), which is obviously wrong because the shift already started at 22:00 of the previous day.
So from currentHour 0 to 5 you have to subtract a day from your Shiftstart.
One possible way to do that would be to set shiftHourStart to -2 for currentHour between 0 and 5.
if (currentHour < 6)
{
shiftHourStart = -2;
}
else if (currentHour >= 6 && currentHour <= 13)
{
shiftHourStart = 6;
}
else if (currentHour >= 14 && currentHour <= 21)
{
shiftHourStart = 14;
}
else
{
shiftHourStart = 22;
}

The error lies in adding the hours to the current day...
Shiftstart = now.Date.AddHours(shiftHourStart);
At 23:59 you are adding 22 hours to "2018-11-06".
One minute passes...
At 00:00 you are adding 22 hours to "2018-11-07".
That means your start time moves 24 hours over the course of that one minute.
You could distinguish this scenario as part of your if-statement. See the comments a) and b) below.
int currentHour = now.Hour;
DateTime date = now.Date; // a) This is today in most cases...
int shiftHourStart;
if (currentHour >= 6 && currentHour <= 13)
{
shiftHourStart = 6;
}
else if (currentHour >= 14 && currentHour <= 21)
{
shiftHourStart = 14;
}
else if (currentHour >= 22)
{
shiftHourStart = 22;
}
else
{
// midnight to 6am
date = date.AddDays(-1); // b) But not in this case
shiftHourStart = 22;
}

First store your time in a TimeSpan this will give you more flexibility if tomorrow shift start and ends at 30min. Then store those Timespan in a class that represent the time frame. And if you have a list of them make it a List. It will help reading and maintaining your code. For now it's a brunch of magic numbers.
To check if a time is in a Range, I used :
public static bool IsBetween(TimeSpan time, TimeSpan start, TimeSpan end)
=> (start <= end) ? time >= start && time <= end : time >= start || time <= end;
internal void TestMethod()
{
var timeSlots = new[] {
new TimeFrame { start= new TimeSpan(6,0,0) , end = new TimeSpan(13,0,0) },
new TimeFrame { start= new TimeSpan(14,0,0) , end = new TimeSpan(21,0,0) },
new TimeFrame { start= new TimeSpan(22,0,0) , end = new TimeSpan(6,0,0) }
};
var today = DateTime.Today;
var dayHours = Enumerable.Range(0, 24).Select(x => today.AddHours(x)).ToList();
foreach (var currentDateTime in dayHours)
{
var matchingRanges = timeSlots.Where(x => IsBetween(currentDateTime .TimeOfDay, x.start, x.end));
if (matchingRanges.Any())
{
var temp = matchingRanges.First();
Console.WriteLine($"-> {currentDateTime } is in range {temp.start}-{temp.end}");
Console.WriteLine($"\t ShiftHours= {temp.start}-{temp.end.Subtract(new TimeSpan(0, 0, 1))}");
}
else
{
Console.WriteLine($"no Match for {currentDateTime }");
}
}
}
As you added a comment about testing it at midnight , included a TestMethod where I create all hours of a day (from 00h to 23h) so you can easly see what going on.

Related

Calculating time difference in C# and saving to mysql

I've been assigned with the task of calculating a time difference only counting working hours. After searching I was able to get this (it's kinda in Portuguese but I think it's understandable) :
if (!txt_data2.Text.Contains("_") && !string.IsNullOrEmpty(txt_data2.Text) && txt_data2.Text != null && !txt_hora2.Text.Contains("_") && !string.IsNullOrEmpty(txt_hora2.Text) && txt_hora2.Text != null)
{
TimeSpan hi = TimeSpan.Parse(txt_horainicio.Text);
TimeSpan hf = TimeSpan.Parse(txt_hora2.Text);
if (hi.Hours < 9 || hf.Hours > 18)
{
MessageBox.Show("Horas Inválidas");
}
else
{
if (MessageBox.Show("Inserir horas extraordinárias?", "Horas Extraordinárias", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
{
double extra;
TimeSpan horasextra;
Frm_Tempo frm1 = new Frm_Tempo();
if (frm1.ShowDialog() == DialogResult.OK)
{
horasextra = TimeSpan.Parse(frm1.txt_horasextra.Text);
extra = horasextra.TotalHours;
DateTime data1 = Convert.ToDateTime(txt_datainicio.Text);
TimeSpan hora1 = TimeSpan.Parse(txt_horainicio.Text);
DateTime dataentrega1 = Convert.ToDateTime(txt_data2.Text);
TimeSpan horaentrega1 = TimeSpan.Parse(txt_hora2.Text);
data1 = data1.Add(hora1);
dataentrega1 = dataentrega1.Add(horaentrega1);
double horas1 = 0;
double minutos1 = 0;
for (var i = data1; i < dataentrega1; i = i.AddMinutes(1))
{
if (i.DayOfWeek != DayOfWeek.Saturday && i.DayOfWeek != DayOfWeek.Sunday)
{
if (i.TimeOfDay.Hours >= 9 && i.TimeOfDay.Hours <= 18)
{
if (i.TimeOfDay.Hours >= 13 && i.TimeOfDay.Hours < 14)
{
}
else
{
minutos1++;
for (var x = data1; x < dataentrega1; x = x.AddHours(1))
{
horas1 = (minutos1 / 60) + extra;
}
}
}
}
}
TimeSpan tempo1 = TimeSpan.FromHours(horas1);
MySqlCommand UPDATE20 = new MySqlCommand("UPDATE tbl_orcamentos SET tempo ='" + tempo1 + "'WHERE id ='" + txt_cod.Text + "'", ligar);
UPDATE20.ExecuteNonQuery();
}
}
else
{
DateTime data = Convert.ToDateTime(txt_datainicio.Text);
TimeSpan hora = TimeSpan.Parse(txt_horainicio.Text);
DateTime dataentrega = Convert.ToDateTime(txt_data2.Text);
TimeSpan horaentrega = TimeSpan.Parse(txt_hora2.Text);
data = data.Add(hora);
dataentrega = dataentrega.Add(horaentrega);
float horas = 0;
float minutos = 0;
for (var i = data; i < dataentrega; i = i.AddMinutes(1))
{
if (i.DayOfWeek != DayOfWeek.Saturday && i.DayOfWeek != DayOfWeek.Sunday)
{
if (i.TimeOfDay.Hours >= 9 && i.TimeOfDay.Hours < 18)
{
if (i.TimeOfDay.Hours >= 13 && i.TimeOfDay.Hours < 14)
{
}
else
{
minutos++;
for (var x = data; x < dataentrega; x = x.AddHours(1))
{
horas = minutos / 60;
}
}
}
}
}
TimeSpan tempo = TimeSpan.FromHours(horas);
MySqlCommand UPDATE21 = new MySqlCommand("UPDATE tbl_orcamentos SET tempo ='" + tempo + "'WHERE id ='" + txt_cod.Text + "'", ligar);
UPDATE21.ExecuteNonQuery();
}
}
}
I'm using c# and a mysql database.
It seems to work but when the result was 48h, instead of "48:00:00", it's trying to update it to "2.00:00:00" which isn't valid as "tempo" it's a time field in mysql. I don't really know how to solve it and so far I've tried to make "horas" a datetime and then formatting it to the right format but it didn't work.
I'd really appreciate any help and I'm sorry if it's hard to understand, just ask and I'll try to explain further.
EDIT:
Adding the float "horas" which contains the number of hours into the Timespan:
TimeSpan tempo = TimeSpan.FromHours(horas);
The standard SQL data type for a difference in time is "interval". MySQL doesn't support the "interval" data type.
It can be confusing, because times of day and intervals use the same notation, but have different meanings. The value '1:00' means 1 o'clock if it's a time of day ("time" or "timestamp"). But the same value means one hour if it's an interval.
Also, "48:00:00" is a valid interval (48 hours), but it's not a valid time of day.
If you're using MySQL, calculate and store the interval in an integer representing the number of hours, minutes, or seconds, and format for display. For example, store two hours as the integer 7200 (seconds) or as the integer 120 (minutes), depending on the application's requirements. Format that integer as "2:00" for display. C#'s TimeSpan.FromMinutes and TimeSpan.FromSeconds will help.
If you want to play around with an open source dbms that supports intervals, look at PostgreSQL.

Regex c# - where to insert the blank space

I want to allow the user to enter the year in blank but I don´t know where to put \s\s\s\s in the following expression.
Here is an example of what I need to do: if the user inserts 03-07-_____ the program must be executed every 3Th of July of each year(and it proceeds the same way if the user inserts blank date, month or year or twoo of this three)
System.Text.RegularExpressions.Regex rdate =
new System.Text.RegularExpressions.Regex(#"^((((0?[1-9]|[12]\d|3[01]|\s\s|\s\d)[\-](0?[13578]|1[02]|\s\s)[\-]((1[6-9]|[2-9]\d)?\d{2}))|((0?[1-9]|[12]\d|30)[\-](0?[13456789]|1[012])[\-]((1[6-9]|[2-9]\d)?\d{2}))|((0?[1-9]|1\d|2[0-8])[\-]0?2[\-]((1[6-9]|[2-9]\d)?\d{2}))|(29[\-]0?2[\-]((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00)))|(((0[1-9]|[12]\d|3[01])(0[13578]|1[02])((1[6-9]|[2-9]\d)?\d{2}))|((0[1-9]|[12]\d|30)(0[13456789]|1[012])((1[6-9]|[2-9]\d)?\d{2}))|((0[1-9]|1\d|2[0-8])02((1[6-9]|[2-9]\d)?\d{2}))|(2902((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00))))$");
Could someone help me?(this expression validates even a leap year)
Here is what You asked for
System.Text.RegularExpressions.Regex rdate =
new System.Text.RegularExpressions.Regex(#"^((((0?[1-9]|[12]\d|3[01]|\s\s|\s\d)[\-](0?[13578]|1[02]|\s\s)[\-]((1[6-9]|[2-9]\d)?\d{2}|\s\s\s\s))|((0?[1-9]|[12]\d|30)[\-](0?[13456789]|1[012])[\-]((1[6-9]|[2-9]\d)?\d{2}|\s\s\s\s))|((0?[1-9]|1\d|2[0-8])[\-]0?2[\-]((1[6-9]|[2-9]\d)?\d{2}|\s\s\s\s))|(29[\-]0?2[\-]((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00|\s\s\s\s)))|(((0[1-9]|[12]\d|3[01])(0[13578]|1[02])((1[6-9]|[2-9]\d)?\d{2}))|((0[1-9]|[12]\d|30)(0[13456789]|1[012])((1[6-9]|[2-9]\d)?\d{2}))|((0[1-9]|1\d|2[0-8])02((1[6-9]|[2-9]\d)?\d{2}))|(2902((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00))))$");
But I am not sure it is what You really want. The regex was already patched, I continued the way. But not only it is maintenance nightmare, it behaves strange. It accepts empty day, but only when month is january, march, may... or empty. Less lines of code is not always better. I would suggest to rewrite it completely. Something simmilar to this:
protected DateTime? getDateTimeFromParts(string day, string month, string year)
{
DateTime now = DateTime.Now;
int iyear;
if (string.IsNullOrWhiteSpace(year))
{
iyear = now.Year;
}
else
{
iyear = int.Parse(year);
if (iyear >= 0 && iyear < 100) { iyear += 2000; }
}
int imonth;
if (string.IsNullOrWhiteSpace(month))
{
imonth = now.Month;
}
else
{
imonth = int.Parse(month);
}
int iday;
if (string.IsNullOrWhiteSpace(day))
{
iday = now.Day;
}
else
{
iday = int.Parse(day);
}
if (iyear <= DateTime.MaxValue.Year && iyear >= DateTime.MinValue.Year)
{
if (imonth >= 1 && imonth <= 12)
{
if (DateTime.DaysInMonth(iyear, imonth) >= iday && iday >= 1)
return new DateTime(iyear, imonth, iday);
}
}
return null;
}
protected DateTime? getDateTime(string dateStr)
{
Regex r = new Regex(#"^(\d\d)(\d\d)((\d\d)?\d\d)$");
Match m = r.Match(dateStr);
if (m.Success)
{
return getDateTimeFromParts(m.Groups[1].Value, m.Groups[2].Value, m.Groups[3].Value);
}
r = new Regex(#"^(\d?\d|\s\d|\s\s)[-](\d?\d|\s\s)[-]((\d\d)?\d\d|\s\s\s\s)$");
m = r.Match(dateStr);
if (m.Success)
{
return getDateTimeFromParts(m.Groups[1].Value, m.Groups[2].Value, m.Groups[3].Value);
}
return null;
}

How to get the 4. Wednesday of the next Month as DateTime?

I need a function which gives me the nth DateTime of the next Month.
For Example: I need the 4th Wednesday of next Month.
My code delivers a wrong Date:
private static DateTime FindNextDay(DayOfWeek dayOfWeek, DateTime fday, Int32 instance)
{
DateTime day = new DateTime(fday.Year, fday.Month, 1, fday.Hour, fday.Minute, fday.Second);
// DateTime day is in this Example= 2014-08.01 11.00 AM
if (instance == 2)
{
day = day.AddDays(7);
}
else if (instance == 3)
{
day = day.AddDays(14);
}
else if (instance == 4) //if the 4th week is requested
{
day = day.AddDays(28); // i add 28 days
}
while (day.DayOfWeek != dayOfWeek)
{
day = day.AddDays(1); // and search the wednesday and return it back
}
return day;
}
Could you show me a better solution?
Something like this..
Get the first day of the next month, keep a count of the number of wednesdays you've encountered adding to it when you find one. Return when the count is 4.
private static DateTime GetFourthWednesday()
{
DateTime firstOfMonth = new DateTime(DateTime.Now.AddMonths(1).Year, DateTime.Now.AddMonths(1).Month, 1);
int count = 0;
while (count < 4)
{
if (firstOfMonth.DayOfWeek == DayOfWeek.Wednesday)
{
count++;
}
if (count == 4)
{
return firstOfMonth;
}
firstOfMonth = firstOfMonth.AddDays(1);
}
return firstOfMonth;
}
Gives 27/08/2014 if run today
Change the last else if to:
else if (instance == 4) //if the 4th week is requested
{
day = day.AddDays(21); // i add 28 days
}
You should add 21 days not 28.
Since because I'm too lazy, I wrote my code like;
DateTime firstDayOfNextMonth = new DateTime(2014, DateTime.Now.Month + 1, 1);
int count = 0;
if (firstDayOfNextMonth.DayOfWeek == DayOfWeek.Wednesday)
count = 1;
while (count != 4)
{
firstDayOfNextMonth = firstDayOfNextMonth.AddDays(1);
if (firstDayOfNextMonth.DayOfWeek == DayOfWeek.Wednesday)
count++;
}
Console.WriteLine(firstDayOfNextMonth);
Basicly, I check if the first day of next month is Wednesday or not, then I iterate my DateTime to found 4. Wednesday in next month.
It is working for today and it prints 27.08.2014 which is fourth Wednesday of next month.
You can write an extension method like;
public static class DateTimeExtensions
{
public static void FindInstanceNextMonth(DateTime Now, DayOfWeek day, int instance)
{
DateTime firstDayOfNextMonth = new DateTime(Now.Year, Now.Month + 1, 1);
int count = 0;
if (firstDayOfNextMonth.DayOfWeek == day)
count = 1;
while (count != instance)
{
firstDayOfNextMonth = firstDayOfNextMonth.AddDays(1);
if (firstDayOfNextMonth.DayOfWeek == day)
count++;
}
Console.WriteLine(firstDayOfNextMonth);
}
}
And call it as;
DateTimeExtensions.FindInstanceNextMonth(DateTime.Now,
DayOfWeek.Wednesday,
4);

Determine time stamp falling between a time range

I need to run a job/scheduler, which should only update records in database it is falling under the specified time range. In my case, the time range is from 3.30 AM to next day 1.30AM. So, with in this time frame the job needs to update the records. In order to get this time interval, I using TimeOfDay() function, but my logic is getting failed, bcoz if the currenttime is say 6.00 Am, then "currentTime <= todaysJob.ENDTIME.Value.TimeOfDay" is returning false. I am using the below code to check
var currentTime = DateTime.Now.TimeOfDay;
if (currentTime > todaysJob.STARTTIME.Value.TimeOfDay &&
currentTime <= todaysJob.ENDTIME.Value.TimeOfDay)
{
// Do logic
}
bool endTomorrow = true;
DateTime taskDate = new DateTime(2012, 08, 31);
TimeSpan Start = new TimeSpan(03, 30, 00);
TimeSpan End = new TimeSpan(01, 30, 00);
DateTime currentTime = DateTime.Now;
bool flag = false;
if (currentTime.TimeOfDay >= Start)
{
if (endTomorrow)
{
flag = currentTime.Date <= taskDate || (currentTime.Date == taskDate.AddDays(1) && currentTime.TimeOfDay < End);
}
else
{
flag = currentTime.TimeOfDay < End;
}
}
if (flag)
{
//do the task
}
EDIT
So I added:
a boolean flag, determining whether the task should end the next day
a datetime variable (taskDate) saying the date of the task
Start and End are equal to todaysJob.STARTTIME and todaysJob.ENDTIME, so you take them from DB as they are.
EDIT
If you could have your job like this:
public class Job
{
public TimeSpan STARTTIME;
public TimeSpan ENDTIME;
public DayOfWeek taskDayOfWeek;
public bool IsEndingTommorow;
public bool IsTomorrow(DayOfWeek d)
{
if (d == DayOfWeek.Sunday)
return taskDayOfWeek == DayOfWeek.Saturday;
else
return d <= taskDayOfWeek;
}
}
then you could
DateTime currentTime = DateTime.Now;
bool flag = false;
if (currentTime.TimeOfDay >= todaysJob.STARTTIME)
{
if (todaysJob.IsEndingTommorow)
{
flag = currentTime.DayOfWeek == todaysJob.taskDayOfWeek || (todaysJob.IsTomorrow(currentTime.DayOfWeek) && currentTime.TimeOfDay < todaysJob.ENDTIME);
}
else
{
flag = currentTime.TimeOfDay < todaysJob.ENDTIME;
}
}
if (flag)
{
//do the task
}
EDIT
I've edited my code another time: added a method to avoid problems with the DayOfWeek enum
You can use the Time Period Library for .NET, to determine, if a moment falls into multiple time periods:
// ----------------------------------------------------------------------
public bool CheckDateBetweenDatesSample()
{
DateTime now = DateTime.Now;
TimePeriodCollection periods = new TimePeriodCollection();
// read periods (Start/end) from database
// ...
periods.Add( new TimeRange( start, end ) );
return periods.HasIntersectionPeriods( now );
} // CheckDateBetweenDatesSample
Try this, its working for me. Correct me if i'm wrong.
TimeSpan timeOfDay = DateTime.Now.TimeOfDay;
TimeSpan startTimeToD = startTime.TimeOfDay;
TimeSpan endTimeToD = endTime.TimeOfDay;
if (timeOfDay > startTimeToD || timeOfDay < endTimeToD )
{
Console.WriteLine("Hello World");
}
timeOfDay = new TimeSpan(2, 30, 00); //testcase
if (timeOfDay > startTimeToD || timeOfDay < endTimeToD )
{
//will never execute.
}
You cannot use TimeOfDay property as it is the absolute distance(elapsed time) from a midpoint(Midnight). What is wrong with using just DateTime for comparison? Comparison operators( < > <= >=) work perfectly fine with DateTime datatype.
For Eg:
DateTime currentTime = DateTime.Now;
DateTime startTime = DateTime.AddMinutes(-50D); //in your case this would be a DT todaysJob.STARTTIME.Value
DateTime endTime = DateTime.AddMinutes(50);// in your case this would be a DT todaysJob.ENDTIME.Value
if(currentTime > startTime && currentTime <= endTime)
{
Console.Write("Works Fine"); //your logic
}

Adding Days to a Date but Excluding Weekends

Given a date how can I add a number of days to it, but exclude weekends. For example, given 11/12/2008 (Wednesday) and adding five will result in 11/19/2008 (Wednesday) rather than 11/17/2008 (Monday).
I can think of a simple solution like looping through each day to add and checking to see if it is a weekend, but I'd like to see if there is something more elegant. I'd also be interested in any F# solution.
using Fluent DateTime https://github.com/FluentDateTime/FluentDateTime
var dateTime = DateTime.Now.AddBusinessDays(4);
public DateTime AddBusinessDays(DateTime dt, int nDays)
{
int weeks = nDays / 5;
nDays %= 5;
while(dt.DayOfWeek == DayOfWeek.Saturday || dt.DayOfWeek == DayOfWeek.Sunday)
dt = dt.AddDays(1);
while (nDays-- > 0)
{
dt = dt.AddDays(1);
if (dt.DayOfWeek == DayOfWeek.Saturday)
dt = dt.AddDays(2);
}
return dt.AddDays(weeks*7);
}
Without over-complicating the algorithm, you could just create an extension method like this:
public static DateTime AddWorkingDays(this DateTime date, int daysToAdd)
{
while (daysToAdd > 0)
{
date = date.AddDays(1);
if (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday)
{
daysToAdd -= 1;
}
}
return date;
}
I would use this extension, remember since it is an extension method to put it in a static class.
Usage:
var dateTime = DateTime.Now.AddBusinessDays(5);
Code:
namespace ExtensionMethods
{
public static class MyExtensionMethods
{
public static DateTime AddBusinessDays(this DateTime current, int days)
{
var sign = Math.Sign(days);
var unsignedDays = Math.Abs(days);
for (var i = 0; i < unsignedDays; i++)
{
do
{
current = current.AddDays(sign);
} while (current.DayOfWeek == DayOfWeek.Saturday ||
current.DayOfWeek == DayOfWeek.Sunday);
}
return current;
}
}
}
Source:
https://github.com/FluentDateTime/FluentDateTime/blob/master/src/FluentDateTime/DateTime/DateTimeExtensions.cs
int daysToAdd = weekDaysToAdd + ((weekDaysToAdd / 5) * 2) + (((origDate.DOW + (weekDaysToAdd % 5)) >= 5) ? 2 : 0);
To wit; the number of "real" days to add is the number of weekdays you're specifying, plus the number of complete weeks that are in that total (hence the weekDaysToAdd / 5) times two (two days in the weekend); plus a potential offset of two days if the original day of the week plus the number of weekdays to add "within" the week (hence the weekDaysToAdd mod 5) is greater than or equal to 5 (i.e. is a weekend day).
Note: this works assuming that 0 = Monday, 2 = Tuesday, ... 6 = Sunday. Also; this does not work on negative weekday intervals.
I created an extension that allows you to add or subtract business days.
Use a negative number of businessDays to subtract. It seems to work in all cases.
namespace Extensions.DateTime
{
public static class BusinessDays
{
public static System.DateTime AddBusinessDays(this System.DateTime source, int businessDays)
{
var dayOfWeek = businessDays < 0
? ((int)source.DayOfWeek - 12) % 7
: ((int)source.DayOfWeek + 6) % 7;
switch (dayOfWeek)
{
case 6:
businessDays--;
break;
case -6:
businessDays++;
break;
}
return source.AddDays(businessDays + ((businessDays + dayOfWeek) / 5) * 2);
}
}
}
Example:
using System;
using System.Windows.Forms;
using Extensions.DateTime;
namespace AddBusinessDaysTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.Text = DateTime.Now.AddBusinessDays(5).ToString();
label2.Text = DateTime.Now.AddBusinessDays(-36).ToString();
}
}
}
F# flavor of http://stackoverflow.com/questions/1044688 's answer:
namespace FSharpBasics
module BusinessDays =
open System;
let private weekLength = 5
(*operation*)
let addBusinessDays (numberOfBusinessDays: int) (startDate: DateTime) =
let startWeekDay = startDate.DayOfWeek
let sign = Math.Sign(numberOfBusinessDays)
let weekendSlide, businessDaysSlide =
match startWeekDay with
| DayOfWeek.Saturday when sign > 0 -> (2, -1)
| DayOfWeek.Saturday when sign < 0 -> (-1, 1)
| DayOfWeek.Sunday when sign > 0 -> (1, -1)
| DayOfWeek.Sunday when sign < 0 -> (-2, 1)
| _ -> (0, 0)
let baseStartDate = startDate.AddDays (float weekendSlide)
let days = Math.Abs (numberOfBusinessDays + businessDaysSlide) % weekLength
let weeks = Math.Abs (numberOfBusinessDays + businessDaysSlide) / weekLength
let baseWeekDay = int baseStartDate.DayOfWeek
let oneMoreWeekend =
if sign = 1 && days + baseWeekDay > 5 || sign = -1 && days >= baseWeekDay then 2
else 0
let totalDays = (weeks * 7) + days + oneMoreWeekend
baseStartDate.AddDays (float totalDays)
[<EntryPoint>]
let main argv =
let now = DateTime.Now
printfn "Now is %A" now
printfn "13 business days from now would be %A" (addBusinessDays 13 now)
System.Console.ReadLine() |> ignore
0
This is better if anyone is looking for a TSQL solution. One line of code and works with negatives.
CREATE FUNCTION[dbo].[AddBusinessDays](#Date date,#n INT)RETURNS DATE AS BEGIN
DECLARE #d INT;SET #d=4-SIGN(#n)*(4-DATEPART(DW,#Date));
RETURN DATEADD(D,#n+((ABS(#n)+#d-2)/5)*2*SIGN(#n)-#d/7,#Date)END
Here is how I did it.
I had to calculate SLA (Service Level Agreement) due dates based on a start date and number of days, and account for weekends and public holidays:
public DateTime? CalculateSLADueDate(DateTime slaStartDateUTC, double slaDays)
{
if (slaDays < 0)
{
return null;
}
var dayCount = slaDays;
var dueDate = slaStartDateUTC;
var blPublicHoliday = new PublicHoliday();
IList<BusObj.PublicHoliday> publicHolidays = blPublicHoliday.SelectAll();
do
{
dueDate = dueDate.AddDays(1);
if ((dueDate.DayOfWeek != DayOfWeek.Saturday)
&& (dueDate.DayOfWeek != DayOfWeek.Sunday)
&& !publicHolidays.Any(x => x.HolidayDate == dueDate.Date))
{
dayCount--;
}
}
while (dayCount > 0);
return dueDate;
}
blPublicHoliday.SelectAll() is a cached in-memory list of public holidays.
(note: this is a cut down version for sharing publicly, there is a reason its not an extension method)
enter code public static DateTime AddWorkDays(DateTime dt,int daysToAdd)
{
int temp = daysToAdd;
DateTime endDateOri = dt.AddDays(daysToAdd);
while (temp !=0)
{
if ((dt.AddDays(temp).DayOfWeek == DayOfWeek.Saturday)|| (dt.AddDays(temp).DayOfWeek == DayOfWeek.Sunday))
{
daysToAdd++;
temp--;
}
else
{
temp--;
}
}
while (endDateOri.AddDays(temp) != dt.AddDays(daysToAdd))
{
if ((dt.AddDays(temp).DayOfWeek == DayOfWeek.Saturday) || (dt.AddDays(temp).DayOfWeek == DayOfWeek.Sunday))
{
daysToAdd++;
}
temp++;
}
// final enddate check
if (dt.AddDays(daysToAdd).DayOfWeek == DayOfWeek.Saturday)
{
daysToAdd = daysToAdd + 2;
}
else if (dt.AddDays(daysToAdd).DayOfWeek == DayOfWeek.Sunday)
{
daysToAdd++;
}
return dt.AddDays(daysToAdd);
}
DateTime oDate2 = DateTime.Now;
int days = 8;
for(int i = 1; i <= days; i++)
{
if (oDate.DayOfWeek == DayOfWeek.Saturday)
{
oDate = oDate.AddDays(2);
}
if (oDate.DayOfWeek == DayOfWeek.Sunday)
{
oDate = oDate.AddDays(1);
}
oDate = oDate.AddDays(1);
}
Given the number of the original day in the year D and original day in the week W and the number of workdays to add N, the next weekday number is
W + N % 5.
The next day in the year (with no wraparound check) is
D + ((N / 5) * 7) + N % 5).
This is assuming that you have integer division.
Formula will be: Workday(date,no.of days,(weekday(1)))
Try this. This will help.

Categories

Resources