Subtract DateTime in C# exluding weekends AND holidays [duplicate] - c#

This question already has answers here:
C# How to calculate working days excluding specific dates (Holidays)
(4 answers)
Closed 1 year ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
I'm creating an API and I need to subtract a certain number of periods and make sure I only quantify business days (excluding weekends and holidays) but I'm having trouble.
I've found some useful solutions to only capture weekdays when I subtract from dateTime.Now() but I need to exclude US federal holidays as well.

I'd probably just scroll it with a loop, repeatedly adding or subbing a period and testing upon each add/sub if the current day is banned, sub a whole day:
DateTime ScrollSkipping(DateTime start, TimeSpan period, int numPeriods, HashSet<DateTime> hashsetOfPublicHolidays){
var d = start; //eg new DateTime(2021, 12, 26, 0, 30, 0);
var t = period; //eg -1 hour to go backwards
var skip = TimeSpan.FromDays(period<TimeSpan.Zero?-1:1);
var p = numPeriods; // eg 25 times
for(int x = 0; x < p;){
if(d.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday || hashsetOfPublicHolidays.Contains(d.Date))
d += skip;
else{
d += t;
x++;
}
}
}
If you supplied a TimeSpan.FromHours(-1); as the period and 25 as the number of periods, and don't "use up" a period when you're skipping whole days for some reason, then resume decrementing it when you're back on a normal date then you'll skip completely over the weekends/holidays.
To go backwards supply a negative TimeSpan as the period, forwards positive

Obviously you need to know the 'US Federal holidays' There are some paid APIS that provide those data or as US holidays ar very simple, use a list built from this: https://www.law.cornell.edu/uscode/text/5/6103

Related

Find consecutive 12 month rolling periods based on a condition

I'm trying to build a calculator to identify the earliest finish date with 5 consecutive 12 month rolling periods which satisfy a specific condition (absence less than 180 days in my example)
The code starts with a fixed start date and adds 12 months to calculate the absence period in any given window. If the absence period is greater than 180 days, it should adjust the start date so the absence is less than 180 days and continue till 5 such consecutive periods are found
I can think of the brute force solution but would love to know any more elegant solutions.
public void CalculateAbsence()
{
var startDate = new LocalDate(2018, 5, 5);
var qualifyinYears = 0;
while (qualifyinYears < 5)
{
var totalAbsence = 0;
var endDate = startDate.PlusYears(1);
//Retrieve All applicable absences in this qualifying Period
foreach (var abs in Immigration.ApplicableAbsences(startDate, endDate))
{
totalAbsence += Period.DaysBetween((abs.Start < startDate ? startDate : abs.Start), (abs.End > endDate ? endDate : abs.End));
}
Console.WriteLine($"Year {startDate}-{endDate}. Absence: {totalAbsence}");
qualifyinYears++;
startDate = startDate.PlusYears(1);
}
}

How to get work week from two dates [duplicate]

This question already has answers here:
how to calculate number of weeks given 2 dates?
(7 answers)
Closed 9 years ago.
Lets say, I have two date Order date - 1/1/2014 and Delivery date - 6/2/2014. Now if I want to calculate how much work week its taken (Order date-delivery date), how can I do it in c#.
If you want the number of worked days in a date range, you can use this:
var from = DateTime.Today.AddDays(-10);
var to = DateTime.Today;
var daysOfWeek = new DayOfWeek[] { DayOfWeek.Monday, DayOfWeek.Tuesday
, DayOfWeek.Wednesday, DayOfWeek.Friday
, DayOfWeek.Thursday };
var days = Enumerable.Range(0, 1 + to.Subtract(from).Days)
.Select((n, i) => from.AddDays(i).DayOfWeek)
.Where(n => daysOfWeek.Contains(n.DayOfWeek));
If you want the number of weeks during a date range, use this:
(int)((to - from).TotalDays/7)
(int)((DeliveryDate-OrderDate).TotalDays/7)
I am presuming by "how much workweek" you mean "how many workdays". This is not so straightforward as it depends on the culture and you need to take holidays into account.
If you rely on Mon through Fri being the work days you could use a solution similar to what was discussed in c# DateTime to Add/Subtract Working Days, counting each day from Order Date to Delivery Date for which the conditions hold.
That Q&A still leaves you with the issue of how to determine the holidays of a certain region (be warned - in Switzerland each part of the country has different holidays!).
Update: From Nagaraj's suggested link I gather that you might also refer to "weeks" as chunks (that is "how many workweeks it has taken"). If so, in turn, you will need to define how many days of a week must be taken to take the week into account...
I'm using strings and convert that to dates, because I'm not sure where you get your dates and in what form. Adjust your code accordingly.
string orderDate = #"1/1/2014";
string deliveryDate = #"6/2/2014";
// This will give you a total number of days that passed between the two dates.
double daysPassed = Convert.ToDateTime(deliveryDate).
Subtract(Convert.ToDateTime(orderDate)).TotalDays;
// Use this if you want actual weeks. This will give you a double approximate. Change to it to an integer round it off (truncate it).
double weeksPassed = daysPassed / 7;
// Use this if you want to get an approximate number of work days in those weeks (based on 5 days a week schedule).
double workDaysPassed = weeksPassed * 5;
I guess you are not interested in working days but weeks. You can use GetWeekOfYear:
http://msdn.microsoft.com/en-us/library/system.globalization.calendar.getweekofyear%28v=vs.110%29.aspx
EDIT
To respond to the comment, here some code example:
int start = System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(new DateTime(2014, 1, 14), System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
int end = System.Globalization.CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(new DateTime(2014, 2, 3), System.Globalization.CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
int weeks = end - start;
That should give you the weeks needed.

How to get duration between start and end date [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Converting Days into Human Readable Duration Text
How do I calculate difference in years and months given a start and end
I use (end date - start date).TotalDays it returns total days. For example, 145 days
But I don't want total days.
It is possible to convert 145 days to 3 months and 25 days something like this.
A bit harder than it initially seems...
I suppose you could do something like this, which has the advantage of counting actual calendar months rather than estimating months to be 30days or similar.
var now = DateTime.Now;
var future = DateTime.Now.AddDays(new Random().NextDouble() * 365);
//dates above for example only
var xx = Enumerable.Range(0,int.MaxValue)
.Select(i => new{numMonths = i, date = now.AddMonths(i)})
.TakeWhile(x => x.date < future)
.Last();
var remainingDays = (future - xx.date).TotalDays;
Console.WriteLine("{0} months and {1} days",xx.numMonths,remainingDays);
if you assume a month to be 30 days, this below might help.
var start = DateTime.Today;
var end = DateTime.Today.AddDays(99);
var timeSpan = end.Subtract(start);
var months = (int)timeSpan.TotalDays / 30;
var days = timeSpan.TotalDays % 30;
var str = string.Format("{0} months, {1} days", months, days);
The difference of two DateTime objects results in a TimeSpan object. Since the time spanned by a month is not consistent among months, how would a TimeSpan object be represented by a number of months?
In order to calculate the number of months between two dates, you'd need to know the start date and end date ahead of time so you can calculate the actual number of months (keeping in mind leap years, etc.) between them.
If you want years, months, days:
Years = max number of years you can subtract from end date such that the result is still > start date.
Months = max number of months you can subtract from the previous result such that the result is still > start date.
Days = number of days between previous result and start date.
To overcome the number of days problem in a month, just look at the years and months
DateTime d1 = New DateTime(2002, 1, 1);
DateTime d2 = New DateTime(2000, 10, 1);
int years = Math.Abs((d1.Year - d2.Year));
int months = ((years * 12) + Math.Abs((d1.Month - d2.Month)));
Try use the time span to string...

use NodaTime to calculate an inclusive days period

So for example if I have the following code:
var nodaStart = new LocalDate(2012, 5, 1);
var nodaEnd = new LocalDate(2012,5,2);
var daysBetween = Period.Between(nodaStart, nodaEnd,PeriodUnits.Day);
Then daysBetween.Days == 1
However, the range I calculate needs to count that as 2 days. i.e. it needs to be inclusive of the start and end date.
The actual method can take and start and end date (that are no more than a year apart) and needs to calculate the number of days. If there are more than 31 days then the remainder are returned as a number of whole weeks.
I have that logic working fine but because the count is exclusive I'm one day out.
I guess I can just do startDate.addDays(-1) before I create nodaStart from it but I wonder if there's a more elegant / pretty way to have noda return the Period.
Thanks
UPDATE:
I've had a read of the source code for the Period class and the + operator is overloaded so I can add
daysBetween += Period.FromDays(1);
(Sorry it's taken me so long to answer this - I hadn't seen it before.)
Any of:
Adding a day to the end before calculating (this is the most logical approach, IMO - as Roger says, you want the start of the next day, effectively)
Subtracting a day from the start before calculating
Adding 1 to the number of days you get out of the end
should be fine. I don't think Noda Time will change to make this any simpler. Between is a sort of "fuzzy around units" version of a subtraction operator - and you won't find many subtraction operators where 2 - 1 is 2.
For "fuzzy" brained humans, we may consider a period of days to be inclusive of start and end date if it identifies a single day, week, month, etc (cf. whole multiple of), so you could code it:
var start = new NodaTime.LocalDateTime(s.Year, s.Month, s.Day, s.Hour, s.Minute);
var end = new NodaTime.LocalDateTime(e.Year, e.Month, e.Day, e.Hour, e.Minute);
NodaTime.Period periodInclusive = NodaTime.Period.Between(start, end.PlusDays(1), NodaTime.PeriodUnits.AllDateUnits);
NodaTime.Period period = NodaTime.Period.Between(start, end, NodaTime.PeriodUnits.AllDateUnits);
bool isInclusivePeriod = periodInclusive.Days + periodInclusive.Weeks + periodInclusive.Months + periodInclusive.Years <
period.Days + period.Weeks + period.Months + period.Years;
period = isInclusivePeriod ? periodInclusive : period;
// do stuff with period here....

Get Date Range by week number c# [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
In .net, knowing the week number how can I get the weekdays date?
Hello,
I've got a question for ya'll.
How do i get the date range of a given week number.
For example:
If I enter week 12 the output should be:
21-03-2011
22-03-2011
23-03-2011
24-03-2011
25-03-2011
26-03-2011
27-03-2011
I really hope you guys can help me out, i just cant find the awnser anywhere!
Thanks in advance.
Note
I appear to have missed bug. The current code have been updated as of 2012-01-30 to account for this fact and we now derive the daysOffset based on Tuesday which according to Mikael Svenson appears to solve the problem.
These ISO8601 week date calculations are a bit wonky, but this is how you do it:
DateTime jan1 = new DateTime(yyyy, 1, 1);
int daysOffset = DayOfWeek.Tuesday - jan1.DayOfWeek;
DateTime firstMonday = jan1.AddDays(daysOffset);
var cal = CultureInfo.CurrentCulture.Calendar;
int firstWeek = cal.GetWeekOfYear(jan1, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
var weekNum = ww;
if (firstWeek <= 1)
{
weekNum -= 1;
}
var result = firstMonday.AddDays(weekNum * 7 + d - 1);
return result;
Basically calculate a reference point, then add days, the hard stuff has to do with the fact that week 53 can sometimes occur in January and week 1 can sometimes occur in December. You need to adjust for that and this is one way to do that.
The above code calculates the date off a year (yyyy) and week number (ww) and day of week (d).
Find out which day of the week was the first January of the year (e.g. in 2011 it was Saturday)
Add the necessary count of days to become the next monday (2 days)
From this day on, add (Number of weeks - 1) * 7 days to get the first day of the week you are interested in
-Display this day plus the next days to get the whole week
Something like this should do the trick
DateTime d = new DateTime(someYear, 1, 1);
d.AddDays(numWeeks * 7);
for (int x = 0; x < 7; x++)
{
Console.WriteLine(d.ToShortDateString());
d.AddDays(1);
}

Categories

Resources