I made a little parking application in C#. There are some different pricings depending on vehicle type and time zone. Day can be divided into time zones (for example morning, day, evening and night). Now if customer stops parking I want to calculate in which time zones customer has parked and how long.
For example morning time zone starts at 6:00 and ends 12:00, day time zone starts at 12:00 and ends 16:00, evening time zone starts at 16:00 and ends at 23:00 and night time zone starts at 23:00 and ends at 6:00. Customer started parking his car at 00:30 and ends parking at 6:32. Currently I have 4 variables for that: parking start time, parking end time and timezone starting time and timezone ending time.
Second example would be like customer parks 24H, then the parking time has all time zones covered.
How is the simplest way to calculate how many hours and minutes customer parked his car in different time zones?
Regards,
evilone
EDIT:
Got this answer from MSDN and post it here so others can learn from it too.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
DateTime start = new DateTime(2011, 7, 25, 0, 30, 0);
DateTime end = new DateTime(2011, 7, 26, 6, 32, 0);
List<DateTime> listTimeZones = CalculateTotalTime(start, end);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < listTimeZones.Count; i++)
{
sb.AppendLine(String.Format("{0}. {1}: from {2} to {3}",
i + 1,
GetZoneInWords(listTimeZones[i].Hour),
String.Format("{0:dd.MM.yyyy hh:mm}", listTimeZones[i]),
(i + 1) < listTimeZones.Count
? String.Format("{0:dd.MM.yyyy hh:mm}", listTimeZones[i + 1])
: "Parking ended"));
}
MessageBox.Show(sb.ToString());
}
private List<DateTime> CalculateTotalTime(DateTime start, DateTime end)
{
DateTime temp = start;
int hour = start.Hour;
int minute = start.Minute;
int morning = 6;
int day = 12;
int evening = 17;
int night = 23;
List<DateTime> timeZones = new List<DateTime>();
do
{
temp = temp.AddHours(1);
if (temp.Hour == morning || temp.Hour == day ||
temp.Hour == evening || temp.Hour == night)
{
timeZones.Add(temp);
}
} while (temp < end);
return timeZones;
}
private string GetZoneInWords(int time)
{
string timeOfDay = "";
if (time.Equals(6))
timeOfDay = "Morning";
else if (time.Equals(12))
timeOfDay = "Day";
else if (time.Equals(17))
timeOfDay = "Evening";
else if (time.Equals(23))
timeOfDay = "Night";
return timeOfDay + " parking";
}
}
Iterate through all the "time zones" and for each, work out the overlap between that and the customer's parking. For example, as pseudo-code:
private static TimeSpan FindOverlap(ParkingTime parkingTime, TimeZone timeZone)
{
// Handle wraparound zones like 23-6. Note that this doesn't attempt
// to handle *parking* which starts at 11.30pm etc.
if (timeZone.Start > timeZone.End)
{
return FindOverlap(parkingTime,
new TimeZone(timeZone.Start.Date, timeZone.End)
+ FindOverlap(parkingTime,
new TimeZone(timeZone.End, timeZone.Start.Date.AddDays(1));
}
DateTime overlapStart = Max(parkingTime.Start, timeZone.Start);
DateTime overlapEnd = Min(parkingTime.End, timeZone.End);
TimeSpan overlap = overlapEnd - overlapStart;
// If the customer arrived after the end or left before the start,
// the overlap will be negative at this point.
return overlap < TimeSpan.Zero ? TimeSpan.Zero : overlap;
}
private static DateTime Min(DateTime x, DateTime y)
{
return x < y ? x : y;
}
private static DateTime Max(DateTime x, DateTime y)
{
return x > y ? x : y;
}
By the way, I would strongly encourage you to rename your "time zone" concept, given that it already has a well-known (if not well-understood :) meaning.
Perhaps you should call it ParkingInterval? Or ParkingPriceInterval if the difference is really in terms of cost?
Related
I am struggling with a datetime problem where I am given a set of inputs and I need to find the EndDate by using those inputs. I am trying my best to solve this, but since I am running out of time, I landed up here. So someone who already might have faced this problem or someone with a solution could let me know one.
Problem Explanation:
The Concept is a Weekly schedule of a live class streaming application. The teacher has scheduled a weekly class from a specific start date.
Let's say, the start date is from 18th April, 2021 and the teacher selects 3 days per week(Monday, Tuesday & Wednesday) each with different class duration(By duration I mean that, We have the class start time of that day and the length of the class in hours and minutes).
Start Date: 18th April, 2021
Total days per week: 3
Days: Monday, Tuesday, Wednesday
Total duration per day: 3 hrs and 30 minutes
Total duration per week: 10 hours 30 minutes (Aggregated = Mon + Tue + Wed)
Max duration that the user cannot exceed: 50 hrs
Ok! Now we shall repeatedly add the TDPD(3 hrs and 30 minutes) to the start date until we reach 50 hrs and find which date it has landed upon(the end date).
What I have tried so far?
int totalWeeks = 0;
int totalHoursPerWeek = 3;
int totalHoursAdded = 0;
int maxHours = 50;
for(int i = totalHoursPerWeek; i <= maxHours; i += totalHoursPerWeek)
{
totalHoursAdded += i;
totalWeeks++;
}
Once the loop ends, I have the total week value.
DateTime endDate;
if(totalHoursAdded == maxHours)
{
//My problem is solved, as there is no remaining time pending
endDate = currentDate.AddDays(totalWeeks * 7);
}
else
{
// I have some pending hours
int pendingHours = maxHours - totalHoursAdded;
//How do I proceed with this pendingHours? how to add this to the
//specific days per week and find the end date? I am stuck here...
}
I am confident this can be done with some carefully thought-out math with dates, however below may be termed the lazy way out. This approach only needs the starting date, a list of days of the week that the class meets, i.e., Monday, Tuesday etc.…, the duration of the class in hours and minutes, and finally the max total hours and minutes that are required.
From my understanding, given the above info, we want to know, given that start date and the days of the week the class meets…
on what Date will the LAST class be that fulfills the max total hours.
To simplify this, one thing that will come in handy is knowing...
“how many classes are needed to fulfill the MAX requirement”.
In other words, if we know how many classes are needed to fulfill the max requirement, then this should make things easier.
Computing the total number of classes needed to fulfil the max requirement, can be found by dividing the max requirement by the class duration. If the division produces a remainder, then this would mean that one (1) additional class would be needed to fulfil the max requirement.
Therefore, if we have both the class duration and max duration variables as Timespan objects, then, we could divide the TimeSpan objects via their respective Tick properties and return how many classes are needed to fulfil the max requirement. This method may look something like…
private int GetTotalNumberOfClassesNeeded(TimeSpan classDuration, TimeSpan totalDuration) {
double td = totalDuration.Ticks / (double)classDuration.Ticks;
int totalClasses = (int)Math.Truncate(td); // <- get the whole portion
if (Math.Floor(td) != td) {
totalClasses++; // <- there is a fractional part - 1 more class needed
}
return totalClasses;
}
Next, we need to compare the DayOfWeek for a date with the DayOfWeek for the class. Therefore, is what we can do is create a List<DayOfWeek> … a list of DayOfWeek objects that the class is in session. We will use this list to check and see if a particular date’s day of week is IN that list. Therefore, in this example, the days of the week the class meets are a simple comma delimited string. Given this string, the code would parse out the days and return the proper list of DayOfWeek objects to compare with. This method may look something like…
private List<DayOfWeek> GetDaysOfWeekForClasses(string daysOfWeek) {
List<DayOfWeek> classesDOW = new List<DayOfWeek>();
string[] splitArray = daysOfWeek.Split(',');
DayOfWeek dow;
for (int i = 0; i < splitArray.Length; i++) {
switch (splitArray[i].Trim()) {
case "Monday":
dow = DayOfWeek.Monday;
break;
case "Tuesday":
dow = DayOfWeek.Tuesday;
break;
case "Wednesday":
dow = DayOfWeek.Wednesday;
break;
case "Thursday":
dow = DayOfWeek.Thursday;
break;
case "Friday":
dow = DayOfWeek.Friday;
break;
case "Saturday":
dow = DayOfWeek.Saturday;
break;
default:
dow = DayOfWeek.Sunday;
break;
}
classesDOW.Add(dow);
}
return classesDOW;
}
That is pretty much all we need. The general idea is this… we start by setting an int variable curClassCount to zero (0). In addition, we will create a DateTime object tempDate that is initialized with the starting date. Lastly, we will create a list of DateTime objects scheduledClasses which will get filled with the dates of the classes. We will start a while loop with the condition to continue as long as curClassCount is less than the total number of classes needed.
In each iteration of the loop a check is made to see if the tempDate’s DayOfWeek is one of the class’s DayOfWeek. … if it is, then we add that date to the scheduledClasses list and increment curClassCount. Finally increment tempDate by one (1) day, then start the loop over. Eventually, the curClassCount will equal the number of classes needed. This code may look something like…
while (curClassCount < totalNumberOfClassesNeeded) {
if (ClassDaysOfWeek.Contains(tempDate.DayOfWeek)) {
scheduledClasses.Add(tempDate.Date);
curClassCount++;
}
tempDate = tempDate.AddDays(1);
}
Putting all this together by droping a DateTimePicker, four (4) TextBoxes, a Button and a multi-line TextBox onto a new Winforms .Net Form may look something like…
Using the code below should complete the example.
private void Form1_Load(object sender, EventArgs e) {
dateTimePicker1.Value = new DateTime(2021, 4, 18);
TextBoxTotDaysPerWeek.Text = "3";
textBoxClassDays.Text = "Monday, Tuesday, Wednesday";
textBoxClassDuration.Text = "00:03:00:00";
textBoxMaxDuation.Text = "02:02:00:00";
}
private void btnCalculate_Click(object sender, EventArgs e) {
DateTime StartDate = dateTimePicker1.Value;
TimeSpan.TryParse(textBoxClassDuration.Text.Trim(), out TimeSpan ClassDuration);
TimeSpan.TryParse(textBoxMaxDuation.Text.Trim(), out TimeSpan MaxDuration);
int totalNumberOfClassesNeeded = GetTotalNumberOfClassesNeeded(ClassDuration, MaxDuration);
List<DayOfWeek> ClassDaysOfWeek = GetDaysOfWeekForClasses(textBoxClassDays.Text.Trim());
List<DateTime> scheduledClasses = new List<DateTime>();
int curClassCount = 0;
DateTime tempDate = StartDate.Date;
while (curClassCount < totalNumberOfClassesNeeded) {
if (ClassDaysOfWeek.Contains(tempDate.DayOfWeek)) {
scheduledClasses.Add(tempDate.Date);
curClassCount++;
}
tempDate = tempDate.AddDays(1);
}
txtBoxResults.Text = "";
txtBoxResults.Text = "Start Date: " + StartDate.ToShortDateString() +Environment.NewLine;
for (int i = 0; i < scheduledClasses.Count; i++) {
txtBoxResults.Text += "Class # " + (i + 1) + " of " + totalNumberOfClassesNeeded +
" Date: " + scheduledClasses[i].ToShortDateString() + Environment.NewLine;
}
}
A note, on the max duration… since the TimeSpan only allows hours < 23, we need to break 50 hours into 2 days and 2 hours. I hope this makes sense.
These are two options (using For and using While loop), should solve the problem:
using System;
namespace SO.DtProblem
{
class Program
{
static void Main(string[] args)
{
ClaculationOption1(); //Using For loop
ClaculationOption2(); //Using While loop
}
private static void ClaculationOption1()
{
var totalWeeks = 0;
var totalHoursPerWeek = 3;
var durationPerDay = 3;
var maxHours = 50;
var courseStartDate = new DateTime(2021, 4, 12); //Which is a Monday day
totalWeeks = maxHours / totalHoursPerWeek;
var expectedEndDate = courseStartDate.AddDays(totalWeeks * 7);
var pendingHours = maxHours % totalHoursPerWeek;
for (var day = 1; day <= 6; day++)
{
if (pendingHours > 0)
{
expectedEndDate = expectedEndDate.AddDays(1);
if ((expectedEndDate.AddDays(1).DayOfWeek == DayOfWeek.Monday)
|| (expectedEndDate.AddDays(1).DayOfWeek == DayOfWeek.Tuesday)
|| (expectedEndDate.AddDays(1).DayOfWeek == DayOfWeek.Wednesday))
{
if (pendingHours - durationPerDay >= 0)
{
pendingHours = pendingHours - durationPerDay;
}
else
{
pendingHours = 0;
break;
}
}
}
}
Console.Clear();
Console.WriteLine("Option 1 Results");
Console.WriteLine($"Course Start Date : {courseStartDate}");
Console.WriteLine($"Course Start Date Day Name: {courseStartDate.DayOfWeek}");
Console.WriteLine($"Expected End Date : {expectedEndDate}");
Console.WriteLine($"Expected End Date Day Name: {expectedEndDate.DayOfWeek}");
Console.WriteLine("===========================================================");
}
private static void ClaculationOption2()
{
var totalWeeks = 0;
var totalHoursPerWeek = 3;
var durationPerDay = 3;
var maxHours = 50;
var courseStartDate = new DateTime(2021, 4, 12); //Which is a Monday day
totalWeeks = maxHours / totalHoursPerWeek;
var expectedEndDate = courseStartDate.AddDays(totalWeeks * 7);
var pendingHours = maxHours % totalHoursPerWeek;
while (pendingHours > 0)
{
expectedEndDate = expectedEndDate.AddDays(1);
if ((expectedEndDate.AddDays(1).DayOfWeek == DayOfWeek.Monday)
|| (expectedEndDate.AddDays(1).DayOfWeek == DayOfWeek.Tuesday)
|| (expectedEndDate.AddDays(1).DayOfWeek == DayOfWeek.Wednesday))
{
if (pendingHours - durationPerDay >= 0)
{
pendingHours = pendingHours - durationPerDay;
}
else
{
pendingHours = 0;
break;
}
}
}
Console.WriteLine("Option 2 Results");
Console.WriteLine($"Course Start Date : {courseStartDate}");
Console.WriteLine($"Course Start Date Day Name: {courseStartDate.DayOfWeek}");
Console.WriteLine($"Expected End Date : {expectedEndDate}");
Console.WriteLine($"Expected End Date Day Name: {expectedEndDate.DayOfWeek}");
Console.ReadKey();
}
}
}
Finally I got my own solution working on the idea based on #John's answer. This answer is specially for the variable hours and minutes.
private static void CalculateClassDates()
{
DateTime courseStartDateTime = DateTime.Today;
int courseDurationInHours = 60;
int courseDurationInMinutes = 0;
TimeSpan courseMaxDuration = new TimeSpan(courseDurationInHours, courseDurationInMinutes, 0);
List<CourseScheduleDates> courseScheduleDates = new List<CourseScheduleDates>();
List<DaysOfWeek> daysOfWeeks = new List<DaysOfWeek>()
{
new DaysOfWeek(){ DayOfWeek = DayOfWeek.Monday, StartTime = DateTime.Today.AddHours(10).TimeOfDay , TotalHours = 1, TotalMinutes = 15},
new DaysOfWeek(){ DayOfWeek = DayOfWeek.Tuesday, StartTime = DateTime.Today.AddHours(10).TimeOfDay , TotalHours = 1, TotalMinutes = 15},
new DaysOfWeek(){ DayOfWeek = DayOfWeek.Wednesday, StartTime = DateTime.Today.AddHours(10).TimeOfDay , TotalHours = 1, TotalMinutes = 30}
};
int daysToAdd = 0;
TimeSpan singleDuration = new TimeSpan(daysOfWeeks[0].TotalHours, daysOfWeeks[0].TotalMinutes, 0);
TimeSpan additionallyAddedTime = TimeSpan.Zero;
List<DayOfWeek> days = daysOfWeeks.Select(x => x.DayOfWeek).ToList();
while (singleDuration.Ticks <= courseMaxDuration.Ticks)
{
if (days.Contains(courseStartDateTime.AddDays(daysToAdd).DayOfWeek))
{
var dayOfWeek = daysOfWeeks.Where(x => x.DayOfWeek == courseStartDateTime.AddDays(daysToAdd).DayOfWeek).First();
courseScheduleDates.Add(new CourseScheduleDates()
{
ScheduleDate = courseStartDateTime.AddDays(daysToAdd),
StartTime = dayOfWeek.StartTime,
TotalHours = dayOfWeek.TotalHours,
TotalMinutes = dayOfWeek.TotalMinutes
});
additionallyAddedTime = new TimeSpan(dayOfWeek.TotalHours, dayOfWeek.TotalMinutes, 0);
singleDuration = singleDuration.Add(additionallyAddedTime);
}
daysToAdd++;
}
singleDuration = singleDuration.Subtract(additionallyAddedTime);
if (singleDuration.Ticks != courseMaxDuration.Ticks)
{
var timeSpanToAdd = new TimeSpan(courseMaxDuration.Ticks - singleDuration.Ticks);
bool shouldContinue = true;
while (shouldContinue)
{
var currentDate = courseStartDateTime.AddDays(daysToAdd);
if (days.Contains(currentDate.DayOfWeek))
{
var dayOfWeek = daysOfWeeks.Where(x => x.DayOfWeek == courseStartDateTime.DayOfWeek).First();
courseScheduleDates.Add(new CourseScheduleDates()
{
ScheduleDate = courseStartDateTime.AddDays(daysToAdd),
StartTime = dayOfWeek.StartTime,
TotalHours = timeSpanToAdd.Hours,
TotalMinutes = timeSpanToAdd.Minutes
});
shouldContinue = false;
}
}
}
Console.WriteLine("Course Start Date " + courseStartDateTime.ToString("dd MMM, yyyy"));
int classCount = 1;
foreach (var item in courseScheduleDates)
{
DateTime dateTime = new DateTime(item.StartTime.Ticks);
Console.WriteLine("Class " + classCount + " will commence on " + item.ScheduleDate.ToString("dd MMM, yyyy") +
" " + dateTime.ToString("hh:mm tt") + " and will last for " + item.TotalHours + " hrs " + item.TotalMinutes + " mins");
classCount++;
}
Console.ReadLine();
}
And the Models
public class DaysOfWeek
{
public DayOfWeek DayOfWeek { get; set; }
public TimeSpan StartTime { get; set; }
public int TotalHours { get; set; }
public int TotalMinutes { get; set; }
}
public class CourseScheduleDates
{
public DateTime ScheduleDate { get; set; }
public TimeSpan StartTime { get; set; }
public int TotalHours { get; set; }
public int TotalMinutes { get; set; }
}
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;
}
}
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;
}
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.
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.