Timespan contains leapyear - c#

I have 2 dates, a start (1/1/15) an end (31/12/16)
I need to calculate an amount per day from a total amount (20,000) and a annual amount based on 365 days,
I'm using Timespan to get the days between start and end dates, but in this case it returns 731 (365 + 366) as 2006 is a leap year,
but what I need is to get 730 without the leap day, is there any way of doing this
Thanks
Aj

Perhaps there is a more efficient approach but this works as expected:
public static int DaysDiffMinusLeapYears(DateTime dt1, DateTime dt2)
{
DateTime startDate = dt1 <= dt2 ? dt1.Date : dt2.Date;
DateTime endDate = dt1 <= dt2 ? dt2.Date : dt1.Date;
int days = (endDate - startDate).Days + 1;
int daysDiff = Enumerable.Range(0, days)
.Select(d => startDate.AddDays(d))
.Count(day => day.Day != 29 || day.Month != 2);
return daysDiff;
}
Your sample:
int days = DaysDiffMinusLeapYears(new DateTime(15, 1, 1), new DateTime(16,12,31));
Result: 730

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;
}

Calculating days left until the next specified day

In a scenario where you would need to calculate the next 'Billing date' if the DAY (2nd, 25th, etc) is known, how can you calculate the number of days left until the next bill payment?
Explanation:
Tom's bill gets generated on the 4th of every month
What's the best way/logic to calculate the days left until the next bill? For example, if today is the 28th of this month, the result would be 6 days left
What we know:
Bill Generation Date is known
Today's Date is known
What I've done so far:
int billingDay = 4; //The day the bill gets generated every month
DateTime today = DateTime.Today; //Today's date
How would I continue this to calculate the next billing date?
P.S: Sorry if this sounds lame, I just couldn't wrap my head around it :)
I think this works:
private int GetNumDaysToNextBillingDate(int billingDayOfMonth)
{
DateTime today = DateTime.Today;
if (today.Day <= billingDayOfMonth)
{
return (new DateTime(today.Year, today.Month, billingDayOfMonth) - today).Days;
}
else
{
var oneMonthFromToday = today.AddMonths(1);
var billingDateNextMonth =
new DateTime(oneMonthFromToday.Year,
oneMonthFromToday.Month, billingDayOfMonth);
return (billingDateNextMonth - today).Days;
}
}
How about:
int billingDay = 4;
DateTime today = DateTime.UtcNow;
DateTime billing = today.Day >= billingDay
? new DateTime(today.AddMonths(1).Year, today.AddMonths(1).Month, billingDay)
: new DateTime(today.Year, today.Month, billingDay);
TimeSpan left = billing - today;
This uses a loop but is less prone to error as it takes into account month and year changes:
int DaysUntilBilling(int billingDay, DateTime referenceDate)
{
int count = 0;
while (referenceDate.AddDays(count).Day != billingDay)
{
count++;
};
return count;
}
You of course don't need to pass a DateTime in as an argument if you are always using today's date, but this helps to test that that for different inputs, you get the desired output:
int billingDay = 4;
DaysUntilBilling(billingDay, DateTime.Now); //26 (today is 9th Aug 2016)
DaysUntilBilling(billingDay, new DateTime(2016, 09, 03); //1
DaysUntilBilling(billingDay, new DateTime(2016, 09, 04); //0
DaysUntilBilling(billingDay, new DateTime(2016, 08, 05); //30
DaysUntilBilling(billingDay, new DateTime(2016, 12, 19); //16
This link might help you :
https://msdn.microsoft.com/en-us/library/system.datetime.daysinmonth(v=vs.110).aspx
What you can do is something like this:
int daysUntilBill = 0;
int billingDay = 4;
DateTime today = DateTime.Today;
if (billingDay > today.Day) {
daysUntilBill = billingDay - today.Day;
} else {
int daysLeftInMonth = DateTime.DaysInMonth(today.Year, today.Month) - today.Day;
daysUntilBill = billingDay + daysLeftInMonth;
}
or slightly more concise
int daysUntilBill = (billingDay >= today.Day)
? billingDay - today.Day
: billingDay + DateTime.DaysInMonth(today.Year, today.Month) - today.Day;
This properly handles the year ending too, since it doesn't try to wrap around.
First you need to determine if the current date is on or before the billing day and if it is just subtract the current day of the month. Otherwise you have to determine the next billing date in the following month.
public int DaysToNextBill(int billingDay)
{
var today = DateTime.Today;
if(today.Day <= billingDay)
return billingDay - today.Day;
var nextMonth = today.AddMonth(1);
var nextBillingDate = new DateTime(nextMonth.Year, nextMonth.Month, billingDay)
return (nextBillingDate - today).Days;
}
The only thing left to deal with is if billingDay is greater than the number of days in the current or following month.

How to Find 5th or ending date of a particular day in a month based on date of the same month

I have been trying to find the 5th week date of a day in a month like 5th week Monday date, 5th week Tue date, Wed... and so on based on the date from the same month. This date could belong to any week of same month.
I tried like
DateTime MonthEventDate=05/01/2016; //Date format in dd/MM/yyyy format
DayOfWeek EventDay="Sunday"; //For Example i want to find 5th Sunday in January Month, but days too will change everytime based on user selection
string SelectedWeek="5"; //Here i'm getting the week in which i need to find the given date i.e, 5th Monday or Tuesday & so on
if (SelectedWeek == "5")
{
//Here i tried to add number of days to my initial day to find 5th day date, but every time its returning next month value
MonthEventDate = MonthEventDate.AddDays((EventDay < MonthEventDate.DayOfWeek ? 31 : 28) + EventDay - MonthEventDate.DayOfWeek);
}
I know the logic is wrong but i want to get date of 5th day of the week, and if that day is not present, return 0. Looking for some guidance
Note: Here Month will change based on User Input, so how to return Date of fifth day, if it exist in the given month
This should give you the 5th day (if there is one) ...
DateTime dayInMonth = DateTime.Now;
DayOfWeek dayToFind = DayOfWeek.Friday;
var fifth= Enumerable.Range(1, DateTime.DaysInMonth(dayInMonth.Year, dayInMonth.Month))
.Select(day => new DateTime(dayInMonth.Year, dayInMonth.Month, day))
.Where(day => day.DayOfWeek == dayToFind)
.Skip(4)
.FirstOrDefault();
Extending #Soner Gönül's answer. In input you put required day of week, required month and required year. On output you get date of same day of week in fifth weeks, or null
public DateTime? FifthDay(DayOfWeek dayOfWeek, byte monthNum, int year)
{
if (monthNum > 12 || monthNum < 1)
{
throw new Exception("Month value should be between 1 and 12");
}
var searchDate = new DateTime(year, monthNum, 1);
var weekDay = searchDate.DayOfWeek;
if (weekDay == dayOfWeek)
{
searchDate = searchDate.AddDays(28);
}
for (DateTime d = searchDate; d < d.AddDays(7); d = d.AddDays(1))
{
if (d.DayOfWeek == dayOfWeek)
{
searchDate = searchDate.AddDays(28);
break;
}
}
if (searchDate.Month == monthNum)
return searchDate;
return null;
}
You can use a simple math like this (no validations included)
static DateTime? FindDate(int year, int month, DayOfWeek dayOfWeek, int weekOfMonth = 5)
{
var baseDate = new DateTime(year, month, 1);
int firstDateOffset = ((int)dayOfWeek - (int)baseDate.DayOfWeek + 7) % 7;
var date = baseDate.AddDays(firstDateOffset + 7 * (weekOfMonth - 1));
return date.Month == month ? date : (DateTime?)null;
}
I think the code is self explanatory. The only trick that probably needs explanation is the line
int firstDateOffset = ((int)dayOfWeek - (int)baseDate.DayOfWeek + 7) % 7;
which handles the case when let say the month starts in Friday and you asked for Monday, and is a short equivalent of
int firstDateOffset = (int)dayOfWeek - (int)baseDate.DayOfWeek;
if (firstDateOffset < 0) firstDateOffset += 7;
Usage in your example
var monthEventDate = FindDate(2016, 1, DayOfWeek.Sunday, 5);
You can refer this demo code
int requiredDay = 5; //Day number i.e 0-6(Sunday to SaturDay)
DateTime day = new DateTime(2016, 1, 1); //Month,year,Date
if (DateTime.DaysInMonth(day.Year, day.Month) > 28)
{
//Get the first day name of the month
int firstMonthDay = (int)day.DayOfWeek;
int offset=0;
//Number of days from the first day for the required day
if (firstMonthDay <= requiredDay)
offset = requiredDay - firstMonthDay;
else
offset = 7 - firstMonthDay + requiredDay;
//
DateTime firstoccurence = day.AddDays((double)offset);
DateTime fifthOccurence = firstoccurence.AddDays(28);
if (fifthOccurence.Month == firstoccurence.Month)
MessageBox.Show(fifthOccurence.ToString());
else
MessageBox.Show("No 5th Occurence for this day");
}

How to do I invert the week returned from Calendar.GetWeekOfYear back to a DateTime? [duplicate]

This question already has answers here:
Calculate date from week number
(26 answers)
Closed 9 years ago.
The problem is there does not seem to be a built-in way to get a DateTime from a previously calculated "week number".
What I want to do is basically have this work for all times and all cultural calendars.
DateTime dt1 = new DateTime.Now;
int week = cal.GetWeekOfYear(dt);
DateTime dt2 = GetDateTimeFromYearAndWeek(dt.Year, week);
if (dt1 < dt2 || dt2 > dt2.AddDays(7.0))
{
throw new Exception("new datetime limits do not span existing datetime;
}
I think one of the issues is that for some cultures, the cal.GetWeekOfYear(dt) call will return the correct week number for a different year from dt.Year. That means you can't use it in the call to my fictious GetDateTimeFromYearAndWeek call.
My own answer is three-fold. First, I came up with a wrapper to Calendar.GetWeekOfYear that returns the "year" of the week, since this year may not be the same as the year of the DateTime object.
public static void GetWeek(DateTime dt, CultureInfo ci, out int week, out int year)
{
year = dt.Year;
week = ci.Calendar.GetWeekOfYear(dt, ci.DateTimeFormat.CalendarWeekRule, ci.DateTimeFormat.FirstDayOfWeek);
int prevweek = ci.Calendar.GetWeekOfYear(dt.AddDays(-7.0), ci.DateTimeFormat.CalendarWeekRule, ci.DateTimeFormat.FirstDayOfWeek);
if (prevweek + 1 == week) {
// year of prevweek should be correct
year = dt.AddDays(-7.0).Year;
} else {
// stay here
year = dt.Year;
}
}
Next, here is the meat of the answer. This inverts the year and weekOfYear back into a DateTime object. Note that I used the middle of the year, because this seems to avoid the problems with the singularities of the new year (where 52 or 53 may or not wrap around to 1). I also synchronize the date by finding a date that is indeed the first day of the week, avoiding problems with negative offsets comparing two DayOfWeek values.
public static DateTime FirstDateOfWeek(int year, int weekOfYear, CultureInfo ci)
{
DateTime jul1 = new DateTime(year, 7, 1);
while (jul1.DayOfWeek != ci.DateTimeFormat.FirstDayOfWeek)
{
jul1 = jul1.AddDays(1.0);
}
int refWeek = ci.Calendar.GetWeekOfYear(jul1, ci.DateTimeFormat.CalendarWeekRule, ci.DateTimeFormat.FirstDayOfWeek);
int weekOffset = weekOfYear - refWeek;
return jul1.AddDays(7 * weekOffset );
}
And finally to all those who doubt it, here is my unit test that cycles through lots of dates and cultures to make sure it works on all of them.
public static void TestDates()
{
foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures).Where((x)=>!x.IsNeutralCulture && x.Calendar.AlgorithmType == CalendarAlgorithmType.SolarCalendar))
{
for (int year = 2010; year < 2040; year++)
{
// first try a bunch of hours in this year
// convert from date -> week -> date
for (int hour = 0; hour < 356 * 24; hour+= 6)
{
DateTime dt = new DateTime(year, 1, 1).AddHours(hour);
int ww;
int wyear;
Gener8.SerialNumber.GetWeek(dt, ci, out ww, out wyear);
if (wyear != year)
{
//Console.WriteLine("{0} warning: {1} {2} {3}", ci.Name, dt, year, wyear);
}
DateTime dt1 = Gener8.SerialNumber.FirstDateOfWeek(wyear, ww, ci);
DateTime dt2 = Gener8.SerialNumber.FirstDateOfWeek(wyear, ww, ci).AddDays(7.0);
if (dt < dt1 || dt > dt2)
{
Console.WriteLine("{3} Bad date {0} not between {1} and {2}", dt, dt1, dt2, ci.Name);
}
}
// next try a bunch of weeks in this year
// convert from week -> date -> week
for (int week = 1; week < 54; week++)
{
DateTime dt0 = FirstDateOfWeek(year, week, ci);
int ww0;
int wyear0;
GetWeek(dt0, ci, out ww0, out wyear0);
DateTime dt1 = dt0.AddDays(6.9);
int ww1;
int wyear1;
GetWeek(dt1, ci, out ww1, out wyear1);
if ((dt0.Year == year && ww0 != week) ||
(dt1.Year == year && ww1 != week))
{
Console.WriteLine("{4} Bad date {0} ww0={1} ww1={2}, week={3}", dt0, ww0, ww1, week, ci.Name);
}
}
}
}
}

Categories