Calculate End Date when specific duration of days given [duplicate] - c#

This question already has answers here:
Calculate the number of business days between two dates?
(39 answers)
Closed 7 years ago.
I am developing c#.net solution where i have to calculate End Date from start date in date time picker based on provided duration date.(excluding weekends)
i.e.
Start Date: 02/09/2015 days: 5
Start Date = 02/09/2015 + 5 days (Excludes weekends)
End Date = 08/09/2015
thanks you,

Try this,
var startDate = DateTime.Now; // Put your actual start date here
for (int i = 0; i < 5;) // Put number of days to add instead of 5
{
startDate = startDate.AddDays(1);
if (startDate.DayOfWeek == DayOfWeek.Saturday || startDate.DayOfWeek == DayOfWeek.Sunday)
{
continue;
}
i++;
}
var finalDate = startDate;

Assuming your start date is a weekday, just add 7 days to it. The calculation for an arbitrary number of days would be as follows:
DateTime StartDate = new DateTime(2015, 9, 2);
DateTime EndDate = StartDate.AddDays(duration/5*7+duration%5);
Assuming we have an array of businessHolidays, we could account for those as well:
for( var index = 0; index < businessHolidays.length; index++) {
var holiday = businessHolidays[index];
if (holiday.Date >= StartDate.Date && holiday.Date <= EndDate.Date) {
EndDate.AddDays(1);
}
}
Finally, we check the resulting date to see if it falls on a weekend. This is necessary if the start date was a weekend or we pushed the date due to a holiday.
if ( EndDate.DayOfWeek == DayOfWeek.Saturday ) {
EndDate.AddDays(2);
} else if ( EndDate.DayOfWeek == DayOfWeek.Sunday ) {
EndDate.AddDays(1);
}

Related

Calculating no of weekends in between the two given date and time fields by considering the time difference and output the difference in minutes

I have two fields startdate and enddate. I need to calculate how many weekends in between two date and time fields and show the result in minutes.
For example start date is 01/11/2019 00:00:00 and end date as 03/11/2019 11:00:00. Below code is returning the difference in minutes correctly as 2100 minutes but when I keep the dates as02/11/2019 08:00 and 03/11/2019 00:00 I am getting the result as 1440 but my expected result is 960 minutes.
I understand that's because I am adding 1440 in code so how to correct this?
public double CountOfWeekEnds(DateTime startDate, DateTime endDate)
{
double weekEndCount = 0;
if (startDate > endDate)
{
DateTime temp = startDate;
startDate = endDate;
endDate = temp;
}
TimeSpan diff = endDate - startDate;
int days = diff.Days;
for (var i = 0; i <= days; i++)
{
var testDate = startDate.AddDays(i);
if (testDate.DayOfWeek == DayOfWeek.Saturday || testDate.DayOfWeek == DayOfWeek.Sunday)
{
if (testDate.Date < endDate.Date)
{
weekEndCount += 1440; // 24h * 60 min
}
else
{
var todayStart = new DateTime(testDate.Year, testDate.Month, testDate.Day, 0, 0, 0);
var difference = (endDate - todayStart).TotalMinutes;
weekEndCount += difference;
}
}
}
return weekEndCount;
}
OK, i simplified what i said a little down to:
DateTime start = new DateTime(2019,11,1,0,0,0);
DateTime end = new DateTime(2019, 11, 3, 11, 0, 0);
TimeSpan diff = end - start;
Console.WriteLine(diff.TotalDays);
int total = 0;
for (int i = 0; i<Math.Ceiling(diff.TotalDays); i++)
{
DateTime test = start.AddDays(i);
Console.WriteLine(test.DayOfWeek);
if (test.DayOfWeek == DayOfWeek.Saturday || test.DayOfWeek == DayOfWeek.Sunday)
{
if (test.Date==start.Date)
{
Console.WriteLine("start");
total += (23 - start.Hour) * 60 + (60 - start.Minute);
}
else if (test.Date==end.Date)
{
Console.WriteLine("end");
total += end.Hour * 60 + end.Minute;
}
else
{
total += 24 * 60;
}
}
Console.WriteLine(test + " total " + total);
}
Console.WriteLine("done");
Console.WriteLine(total);
which counts all saturdays and sundays and allows for start and ends to be partials
(and can someone send a keyboard with actual keys this membrain lark is hampering typings)
Trying to remain as much of the original code as possible, only three minor changes have to be made:
1. Use the actual dates to calculate diff:
TimeSpan diff = endDate.Date - startDate.Date; instead of TimeSpan diff = endDate - startDate;
This is because later in the upcoming for-loop you are trying to evaluate each date in order to say if is a saturday or sunday. Otherwise, you are evaluating if the date 24 (, 48, …) hours after your starting time stamp is a saturday or sunday.
2. Use testDate instead of todayStart in order to calculate difference
difference = (endDate - testDate).TotalMinutes;
instead of
var todayStart = new DateTime(testDate.Year, testDate.Month, testDate.Day, 0, 0, 0);
var difference = (endDate - todayStart).TotalMinutes;
This is because testDate does contain the hours and minutes to calculate the difference in minutes. Otherwise you are just ignoring the day time of the starting day. Note that this correction can lead to a negative difference value if the startDate day time is later than the endDate day time.
3. do not add a whole day if there is only one day to examine in total
That means that if startDate.Date == endDate.Date, you should just calculate the difference between the dates.
if (testDate.Date < endDate.Date && startDate.Date != endDate.Date)
This has to be done because of the code logic: a full day is added for every new day other than the final day and for the final day ~24hours are added or substracted to the final value depending on the day times of the startDate and endDate.
The complete corrected code:
public static double CountOfWeekEnds(DateTime startDate, DateTime endDate)
{
double weekEndCount = 0;
if (startDate > endDate)
{
DateTime temp = startDate;
startDate = endDate;
endDate = temp;
}
TimeSpan diff = endDate.Date - startDate.Date; //instead of endDate - startDate
int days = diff.Days;
for (var i = 0; i <= days; i++)
{
var testDate = startDate.AddDays(i);
//Console.WriteLine(testDate);
if (testDate.DayOfWeek == DayOfWeek.Saturday || testDate.DayOfWeek == DayOfWeek.Sunday) //only weekends count
{
if (testDate.Date < endDate.Date && startDate.Date != endDate.Date) { // added startDate.Date != endDate.Date
weekEndCount += 1440; // 24h * 60 min
//Console.WriteLine("************************add 1440 ");
}
else
{
double difference;
difference = (endDate - testDate).TotalMinutes; //instead of endDate - todayStart
//Console.WriteLine("************************add " + difference);
weekEndCount += difference;
}
}
}
//return days;
return weekEndCount;
}
You need to have a look at this condition:
if (testDate.Date < endDate.Date)
It means that "as long as the ticks of testDate is less than the ticks of endDate".
This condition will be true for all conditions that makes your variable "days" positive.
I think you need to extend this, condition e.g.
if ((endDate - todayStart).TotalMinutes > 1440 )
This way it will check whether it is AT LEAST 24 hours earlier. If it isn't it should go forth with your "else" condition and take the used fraction of the start day into consideration.
Here is a (somewhat) simple solution. Please note that the code could (and probably should) be refactored if it was to be production code. But I tried to optimize it for understandability, since it was your first post...
public static int CalculateWeekendMinutes(DateTime start, DateTime end)
{
int weekendMinutes = 0;
// First and last day will be handled seperately in the end
var firstFullDay = start.AddDays(1).Date;
var lastFullDay = end.AddDays(-1).Date;
TimeSpan limitedSpan = lastFullDay - firstFullDay;
int spanLengthDays = (int)limitedSpan.TotalDays;
var dateIterator = firstFullDay;
// Looping over the limited span allows us to analyse all the full days
while (dateIterator <= lastFullDay)
{
if (dateIterator.DayOfWeek == DayOfWeek.Saturday || dateIterator.DayOfWeek == DayOfWeek.Sunday)
{
weekendMinutes += (24 * 60);
}
dateIterator = dateIterator.AddDays(1);
}
// Finally we can calculate the partial days and add that to our total
weekendMinutes += CalculateMinutesOnFirstDay(start);
weekendMinutes += CalculateMinutesOnLastDay(end);
return weekendMinutes;
}
// Helps us calculate the minutes of the first day in the span
private static int CalculateMinutesOnFirstDay(DateTime date)
{
if (date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday)
{
// We want to know how many minutes there are UNTIL the next midnight
int minutes = (int)(date.Date.AddDays(1) - date).TotalMinutes;
return minutes;
}
else
{
return 0;
}
}
// Helps us calculate the minutes of the last day in the span
private static int CalculateMinutesOnLastDay(DateTime date)
{
if (date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday)
{
// We want to know how many minutes there are SINCE the last midnight
int minutes = (int)(date - date.Date).TotalMinutes;
return minutes;
}
else
{
return 0;
}
}

How to calculate WeekEnds in minutes inbetween the two given dates by considering the time also

I have two fields startdate and enddate where I need to calculate how many weekends in between those two dates and show them in minutes. For example start date is 01/11/2019 00:00:00 and end date as 03/11/2019 12:00:00, I should get the output in total Saturday and partial Sunday as 1.5 days weekend in between the given dates
I tried the following code which is not calculating the time on weekends with given scenario
public int CountOfWeekEnds(DateTime startDate, DateTime endDate)
{
int weekEndCount = 0;
if (startDate > endDate)
{
DateTime temp = startDate;
startDate = endDate;
endDate = temp;
}
TimeSpan diff = endDate - startDate;
int days = diff.Days;
for (var i = 0; i <= days; i++)
{
var testDate = startDate.AddDays(i);
if (testDate.DayOfWeek == DayOfWeek.Saturday || testDate.DayOfWeek == DayOfWeek.Sunday)
{
if (testDate.Minute > 0)
{
weekEndCount += 1;
}
}
}
return weekEndCount;
}
Showing output as 2 days of weekend instead of 1.5 days in between the dates. Please suggest how I achieve this
If I understand correctly, by weekends you mean both saturdayand sunday.
I use this code to compute how many DayOfWeek exists between two dates.
public static int CountOfWeekEnds(DateTime start, DateTime end) {
return CountDays(DayOfWeek.Saturday, start, end) + CountDays(DayOfWeek.Sunday, start, end);
}
public static int CountDays(DayOfWeek day, DateTime start, DateTime end)
{
TimeSpan ts = end - start; // Total duration
int count = (int)Math.Floor(ts.TotalDays / 7); // Number of whole weeks
int remainder = (int)(ts.TotalDays % 7); // Number of remaining days
int sinceLastDay = end.DayOfWeek - day; // Number of days since last [day]
if (sinceLastDay < 0) sinceLastDay += 7; // Adjust for negative days since last [day]
// If the days in excess of an even week are greater than or equal to the number days since the last [day], then count this one, too.
if (remainder >= sinceLastDay) count++;
return count;
}
Reference
There are a few things you should change to make this work:
Since we want to return the number of weekend days as a decimal, we need to change our return type to something that represents that, like a double.
Then, when we calculate our days, we need to get the fraction of the day by dividing the hours by 24. In my example below, I went even further and calculated the fraction of days based on the number of Ticks instead of the Hours.
And finally, when we add days, we should use only the Date part of the result so that the time is set to midnight, except for the very last day, where we want to use the specified time.
For example:
public static double GetWeekendDaysCount(DateTime start, DateTime end)
{
if (start == end) return 0;
if (start > end)
{
DateTime temp = start;
start = end;
end = temp;
}
double weekendDays = 0;
var current = start;
// To be super accurate, we can calculate based on Ticks instead of hours
var ticksInADay = (double)TimeSpan.FromDays(1).Ticks;
while (current <= end)
{
if (current.DayOfWeek == DayOfWeek.Saturday ||
current.DayOfWeek == DayOfWeek.Sunday)
{
// If the time is midnight, count it as one day,
// otherwise add a fraction of a day
weekendDays += current.TimeOfDay > TimeSpan.Zero
? current.TimeOfDay.Ticks / ticksInADay
: 1;
}
// Add a day and set the time to midnight by using 'Date'
current = current.AddDays(1).Date;
// Unless we're on the last day, then we want
// to use the TimeOfDay that was specified
if (current == end.Date) current = end;
}
return weekendDays;
}

Date Time and Formula Improvement C#

I got here a scenario where I input an information but the code does does not work. What happen is when I input 12/11/2015, the Address2Panel shows. Which is wrong because there no more date that I can input because Person A is born in 12/11/2015. The logic should Enter addresses for the past 5 years. But it goes wrong if the Birthdate gap is not lesser the 5 years from the current date.
Person A Birthday = 12/11/2015
Person A StartLiving = 12/11/2015 because its the day he/she was born.
Should not display Address2Panel
int CurrentDateInMonths = (((DateTime.Today.Year) * 12) + (DateTime.Today.Month));
static int AlienMonthsAtCurrentAddress = 0;
DateTime myDateTime;
//LivedHere = 12/11/2015
myDateTime = DateTime.Parse(LivedHere.Text);
AlienMonthsAtCurrentAddress = (CurrentDateInMonths - (((Convert.ToInt16(myDateTime.Year)) * 12) + Convert.ToInt16(myDateTime.Month)));
if (AlienMonthsAtCurrentAddress < 60)
{
Address2Panel.Visible = true;//shows the Address2Panel
}
else
{
ClearAddress2Panel();//hides also the Address2Panel
}
Any suggestion how should I improve my formula and date time manipulation?
no need to convert date into months, subtract dates using DateTime.Subtract method :
From MSDN, DateTime.Subtract Method subtracts the specified date and time from this instance.It returns a TimeSpan object which has a property Days
static int AlienMonthsAtCurrentAddress = 0;
try
{
DateTime myDateTime;
myDateTime = DateTime.Parse(LivedHere.Text);
// If you don't wish to subtract from today's date use required date in place of DateTime.Now
TimeSpan span = DateTime.Now.Subtract ( myDateTime );
if (span.Days < 60)
{
Address2Panel.Visible = true;//shows the Address2Panel
}
else
{
ClearAddress2Panel();//hides also the Address2Panel
}
}
catch { }
You could check years, months and days with a separately logic, and then get them all together:
DateTime date = new DateTime(2015,11,12)
DateTime input = getDate()
int years = input.Year - date.Year - 1
years += If(input.Month > date.Month, 1, 0)
years += If(input.Month = date.Month AndAlso input.Day >= date.Day, 1, 0)
This will output the exact number of years between two days (truncating the resulting integer). You'll just have to compare it to 5, in your case

Date algorithm - Add days without considering leap years

The system I'm working on is built and configured in such a way, where users cannot choose leap years when setting up a recurring payment. This results in all the date-math behind the scenes having to ignore leap years. (I didn't choose this, but this is how it was written)
I have to write a method that takes in a DateTime value, and adds days to the date, ignoring leap years, which essentially means ignoring Feb 29th and pretending it doesn't exist.
For example, If I'm adding 365 days to 1/1/2016, that should result in 1/1/2017, not 12/31/2016.
I'm using .NET, so I can make use of DateTime.IsLeapYear, and other helper methods.
This is a work in progress, and here is what I have so far. I started taking a simpler route, and I'm now realizing that it's going to require a more complex algorithm.
public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd)
{
// Nothing to do
if (daysToAdd == 0)
{
return date;
}
// NOTE: This is an invalid approach; using DateTime.AddDays will take leap years into account
DateTime dateWithAddedDays = date.AddDays(daysToAdd);
const int FEB_28_DAY_OF_YEAR = 59;
int daysToSubtractForLeapYearConsideration = 0;
// The year is a leap year, which is under the feb 28 day threshold, and we're adding enough days to push it over the feb 28 day threshold
// This will result in .NET taking into account the feb 29th (the leap year day), but we have to subtract that leap year day since the system doesn't take feb 29th into account
if (DateTime.IsLeapYear(date.Year) && date.DayOfYear < FEB_28_DAY_OF_YEAR && (date.DayOfYear + daysToAdd > FEB_28_DAY_OF_YEAR))
{
daysToSubtractForLeapYearConsideration++;
}
// The resulting date (after the days are added or subtracted) is a leap year, whose day is past the feburary 28 day threshold, and it's not the same year as the date (i.e. it spans across "n" years)
if (DateTime.IsLeapYear(dateWithAddedDays.Year) && dateWithAddedDays.DayOfYear > FEB_28_DAY_OF_YEAR && dateWithAddedDays.Year != date.Year)
{
daysToSubtractForLeapYearConsideration++;
}
// We determined if the original date should be leap year considered, as well as the resulting date/year with the days added. Now see if there are any years in between
// that we should consider
bool isThereAYearRangeThatWeNeedToEvaluateLeapYearsFor = Math.Abs(date.Year - dateWithAddedDays.Year) > 0;
if (isThereAYearRangeThatWeNeedToEvaluateLeapYearsFor)
{
for (int leapYearEvalIndex = Math.Min(date.Year, dateWithAddedDays.Year); leapYearEvalIndex <= Math.Max(date.Year, dateWithAddedDays.Year); leapYearEvalIndex++)
{
bool isYearPartOfTheYearsThatWeveAlreadyChecked = leapYearEvalIndex == date.Year || leapYearEvalIndex == dateWithAddedDays.Year;
if (!isYearPartOfTheYearsThatWeveAlreadyChecked && DateTime.IsLeapYear(leapYearEvalIndex))
{
daysToSubtractForLeapYearConsideration++;
}
}
}
DateTime dateResult = date.AddDays(daysToAdd - daysToSubtractForLeapYearConsideration);
// The system does not allow 2/29 days, hence all this crazy date math
if (dateResult.Month == 2 && dateResult.Day == 29)
{
dateResult = dateResult.AddDays(1);
}
return dateResult;
}
The logic has to take into account negative numbers as well (i.e. subtracting), which the above code fails on.
The above code by no means works, but I wanted to demonstrate that I'm trying to tackle the problem, and not simply asking without having tried anything.
Edit
I've come up with an algorithm pretty close to David's approach. (I wrote it, and then came back to StackOverflow to check responses).
public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd)
{
// Nothing to do
if (daysToAdd == 0)
{
return date;
}
DateTime dateResult = date;
// Are we adding or subtracting
bool areWeAddingDays = daysToAdd > 0;
int daysToAccountForInRegardToLeapYearDates = 0,
absDaysToAdd = Math.Abs(daysToAdd);
for (int i = 1; i <= absDaysToAdd; i++)
{
dateResult = dateResult.AddDays(areWeAddingDays ? 1 : -1);
if (dateResult.Month == 2 && dateResult.Day == 29)
{
daysToAccountForInRegardToLeapYearDates++;
}
}
dateResult = dateResult.AddDays(areWeAddingDays ? daysToAccountForInRegardToLeapYearDates : -daysToAccountForInRegardToLeapYearDates);
return dateResult;
}
Here is an extension method that works. Will also work if you're adding or subtracting enough days to span multiple leap years.
public static DateTime AddDaysWithoutLeapYear(this DateTime input, int days)
{
var output = input;
if (days != 0)
{
var increment = days > 0 ? 1 : -1; //this will be used to increment or decrement the date.
var daysAbs = Math.Abs(days); //get the absolute value of days to add
var daysAdded = 0; // save the number of days added here
while (daysAdded < daysAbs)
{
output = output.AddDays(increment);
if (!(output.Month == 2 && output.Day == 29)) //don't increment the days added if it is a leap year day
{
daysAdded++;
}
}
}
return output;
}
Might need some more testing, but without using the DateTime Add... functions or too much looping, a possible custom implementation:
public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd)
{
int year = date.Year + daysToAdd / 365, month = date.Month - 1, dir = Math.Sign(daysToAdd);
daysToAdd = (daysToAdd % 365) + date.Day;
int[] months = {31,28,31,30,31,30,31,31,30,31,30,31};
while(daysToAdd > months[month] || daysToAdd < 0){
if(dir ==1) daysToAdd -= months[month];
month += dir;
if(month == 12 || month == -1){
year += dir;
month = dir == -1 ? 11 : 0;
}
if(dir ==-1) daysToAdd += months[month]; //for reverse direction, add previous month
}
return new DateTime(year, ++month,daysToAdd);
}

Simplest way to calculate anniversary given a date in the past?

i need a function that calculates if there is an upcoming anniversary for a person given a start date in the past and today's current date. I found this code below but looking at the below code and I have the feeling that there is a much simpler way to calculate this without required this for loop:
private const int ANNIVERSARY_ALERT = 10;
public virtual string UpcomingMilestone
{
get
{
var years = Years() + 2;
for (int year = 0; year < years; year++)
{
int days = year * 365;
int dayDiff = days - NumberOfDays;
if (dayDiff == 0)
{
return year + " year milestone";
}
if (dayDiff < ANNIVERSARY_ALERT && dayDiff > 0)
{
return year + " year milestone in " + dayDiff + " days";
}
}
return string.Empty;
}
}
public virtual int NumberOfDays
{
get
{
TimeSpan ts = DateTime.Today - StartDate.Value;
return (int)ts.TotalDays;
}
}
public virtual int Years()
{
TimeSpan span = DateTime.Now.Subtract(StartDate.Value);
return (int)(span.Days / 365.25); // leap years included
}
Can anyone suggest a way to calculate this above without having to do this loop? This is more for code maintainability versus any performance considerations.
Use the relevant parts of the startdate to compose a new DateTime:
//My birthday, feel free to put this date in your calendars!
var startDate = new DateTime(1976, 2, 29);
//Get the anniversary date for this year
DateTime nextAnniversary;
try
{
nextAnniversary = new DateTime(DateTime.Today.Year, startDate.Month, startDate.Day);
}
catch(ArgumentOutOfRangeException)
{
//DateTime conversion failed, try next day in the year
nextAnniversary = new DateTime(DateTime.Today.Year, startDate.AddDays(1).Month, startDate.AddDays(1).Day);
}
//Check if this year's anniversary has already happened
if(nextAnniversary < DateTime.Today) nextAnniversary = nextAnniversary.AddYears(1);
This should do it. You need to provide TimeSpan range that can be a day or a week, and date which you are matching anniversary to.
public bool Upcomming(DateTime date, TimeSpan range){
var newdate = new Date(DateTime.Now.Year, date.Month, date.Day);
return newdate - DateTime.Now < range;
}
int anniversaryYear = DateTime.Now.Year - StartDate.Year + 1;
DateTime nextAnniversary = StartDate.AddYears(anniversaryYear);
if (nextAnniversary == DateTime.Now)
{
return anniverasryYear + " year milestone";
}
if (DateTime.Now > nextAnniverasry)
{
anniverasryYear++;
nextAnniversary = StartDate.AddYears(anniversaryYear);
}
var daysTillNext = Math.Abs( (nextAnniversary - DateTime.Now).TotalDays );
return string.Format("{0} milestone in {1}", anniversaryYear, daysTillNext);
These methods should be sufficient for your purposes.
public DateTime GetNextAnniversaryDate(DateTime anniversary)
{
var today = DateTime.Today;
var year = anniversary.Month < today.Month ||
(anniversary.Month == today.Month && anniversary.Day < today.Day)
? today.Year + 1 : today.Year;
return anniversary.Month == 2 && anniversary.Day == 29 &&
!DateTime.IsLeapYear(year)
? new DateTime(year, 2, 28)
: new DateTime(year, anniversary.Month, anniversary.Day);
}
public int GetDaysUntilNextAnniversary(DateTime anniversary)
{
var nextDate = GetNextAnniversaryDate(anniversary);
return (int)(nextDate - DateTime.Today).TotalDays;
}
Note that we are specifically electing to celebrate leap-day anniversaries on Feb 28th when the anniversary year is not a leap year. You could change that to March 1 if desired.
Also note that this question assumes that the day of the person in question is the same day as the computer's local clock. This might not be true, for example if the local machine is in a time zone that is ahead of the user's time zone, then it might be one day off. If you want to take that into consideration, then you could use TimeZoneInfo and DateTime.UtcNow. Or you could use Noda Time. More about this on my blog.

Categories