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; }
}
The problem is that I want to set some fixed values for 3 types of charts:
First is a week chart so it need to have Sunday to Saturday values on X axis.
Second is month so it have to set the days of the current month
The last one its a year chart that need to show months from 1 to 12 or jan to dec.
I did watch a lot of tutorials but any of those set points like I want and most of them teach how to set X and Y points, but I want a fixed X point and get the Y point from the DB.
To get these fixed ranges :
I have set up the chart like so:
private void rb_range_CheckedChanged(object sender, EventArgs e)
{
Chart chart = chart8;
Series s = chart.Series[0];
s.ChartType = SeriesChartType.Line;
s.XValueType = ChartValueType.DateTime;
s.YValueType = ChartValueType.Double;
Axis ax = chart.ChartAreas[0].AxisX;
Axis ay = chart.ChartAreas[0].AxisY;
//ax.IsMarginVisible = true; // max or may be necessary
ax.Interval = 1;
if (rb_week.Checked)
{
setValues('w', 123);
ax.IntervalType = DateTimeIntervalType.Days;
ax.LabelStyle.Format = "dddd";
}
else if (rb_month.Checked)
{
setValues('m', 123);
ax.IntervalType = DateTimeIntervalType.Days;
ax.LabelStyle.Format = "dd";
}
else if (rb_year.Checked)
{
setValues('y', 123);
ax.IntervalType = DateTimeIntervalType.Months;
ax.LabelStyle.Format = "MMMM";
}
s.Points.Clear();
foreach (var dp in points) s.Points.Add(dp);
// after the points are added or bound to you may want to..
// set the minimum&maximum, but if the data fit you don't have to!
ax.Minimum = points.Min(x => x.XValue);
ax.Maximum = points.Max(x => x.XValue);
}
A few Notes :
It is important to select or bind only those dates that should go into the chart! If you make mistakes here the limits will be off!
If your data are dense enough, that is, if they inclused the 1st and last day of the interval they refer to, you can omit setting the Minimum and Maximum on the x-axis; in that case also include ax.IsMarginVisible = false; to avoid points from the neighboring ranges showing up.
If you data are sparse you may need to determine the Minimum and Maximum values differently than simply picking the first and last x-values. Instead you should pick the correct DateTime values. Note that you need to pick real DateTimes and convert them to double with the ToOADate() function, as the axis properties expect value units.
You can study the code I used to create my data for hints on how to get the Date of the 1st and last day of a given week, month or year..
Note how I use DateTime.DaysInMonth to get the correct number of days in a given month
If you chose Column as ChartType the 1st and last columns may get cut. For this case you can expand the range by adding half a unit to the Maximum and subtracting the same from the Minimum. You may also need to add one such amount to the IntervalOffset.
Here is how I set up the points:
List<DataPoint> points = new List<DataPoint>();
void setValues(char time, int rand)
{
Random rnd = new Random(rand); // random data values
points = new List<DataPoint>();
DateTime dn = DateTime.Now;
DateTime dw = new DateTime(dn.Year, dn.Month, dn.Day % 7 + 1); //my weeks start on monday
DateTime dm = new DateTime(dn.Year, dn.Month, 1);
DateTime dy = new DateTime(dn.Year, 1, 1);
if (time == 'w') for (int i = 0; i < 7; i++)
points.Add(new DataPoint(dw.AddDays(i).ToOADate(), rnd.Next(100) + 50));
if (time == 'm') for (int i = 0; i < DateTime.DaysInMonth(dn.Year, dn.Month); i++)
points.Add(new DataPoint(dm.AddDays(i).ToOADate(), rnd.Next(100) + 50));
if (time == 'y') for (int i = 0; i < 12; i++)
points.Add(new DataPoint(dy.AddMonths(i).ToOADate(), rnd.Next(100) + 50));
}
I need help about assigning day automatically. I couldn't find correct way.
Now problem is that I have fitness program. And I will add new user. When I add I will add program too. Fitness program has limit.
For example. 8 seans. Then user chooses every Saturday and Sunday.
16.03.2019, 17.03.2019, 23.03.2019, 24.03.2019,
30.03.2019, 31.03.2019, 06.04.2019, 07.04.2019
The dates will be assign automatically due to his chosen. uye.DAYS choosed days of week. For example '0,6' and uye.SURE means limit of seans
if(uy.UYELIK== "PLATES")
{
DateTime date = DateTime.Now.Date;
System.TimeSpan duration = new System.TimeSpan(1, 0, 0, 0);
for (int i = 0; i < uye.SURE; i++)
{
date = date.Add(duration);
var list = uye.DAYS.Split(',');
for(int j = 0; j < list.Length; j++)
{
if ((int)date.DayOfWeek == Convert.ToInt32(list[j]))
{
HR_FITNESS_USER_PLATES_PROGRAM program = new HR_FITNESS_USER_PLATES_PROGRAM();
program.REF_HOCA = uye.HOCA;program.SEANS_LIMIT = uye.SURE;program.SEANS_TIME = date;program.REF_UYELIK = uy.ID;program.SICIL = uye.SICIL.ToString();
db.HR_FITNESS_USER_PLATES_PROGRAM.Add(program);db.SaveChanges();
}
}
}
}
This code is not correct. Here I can't increase day if not one of them. How can I do it?
As I understand, you need to insert a weekend fitness program for a member. Here is a simple psuedocode for that,
var startDate = DateTime.Today; //today
int currentDayOfWeek = (int)startDate.DayOfWeek; //today
//saturday of this week, disregarding if today is
//saturday or sunday, it is up to you to enhance this.
DateTime thisSaturday = startDate.AddDays(6 - currentDayOfWeek);
var sessions = 8;
for (int i = 0; i<sessions; i++)
{
SaveToDb(thisSaturday);
thisSaturday = thisSaturday.AddDays(7); //next week's weekend
}
private void SaveToDb(DateTime saturday)
{
DateTime sunday = saturday.AddDays(1);
//insert data for saturday and sunday
}
I would like to display a week of dates with the default being today's date on a drop down list. How can I do this?
I was also told to "use class DateTime.Now, and convert the data value into a string".
Any help is appreciated!
this works for me on my asp.net project
DropDownList1.Items.Add(DateTime.Now.ToString());
and this one on my combobox
comboBox1.Items.Add(DateTime.Now);
i'm not entirely sure about your question. is the following you want to do?
for (int i = 0; i < 7; i++)
{
DropDownList1.Items.Add(DateTime.Now.AddDays(-i).ToString());
}
Try This:
//Get Start And End
int delta = Convert.ToInt32(DateTime.Now.DayOfWeek);
delta = delta == 0 ? delta + 7 : delta;
DateTime moday = DateTime.Now.AddDays(1 - delta);
DateTime sunday = DateTime.Now.AddDays(7 - delta);
//Get Date Range
List<DateTime> allDates = new List<DateTime>();
//Add To Your List
for (DateTime i = moday; i <= sunday; i = i.AddDays(1))
{
DropDownList1.Items.Add(i.Date.DayOfWeek);
}
//Select Today Name
DropDownList1.SelectedItem = DateTime.Today.Date.DayOfWeek;
Edited
For This Format(mm/dd/yyy)
//Add To Your List
for (DateTime i = moday; i <= sunday; i = i.AddDays(1))
{
comboBox1.Items.Add(i.Date.ToShortDateString());
}
//Select Today Date(dd/mm/yyy)
comboBox1.SelectedItem = DateTime.Today.ToShortDateString();
you can put it on the load event
protected void Page_Load(object sender, EventArgs e)
{
for (int i = 0; i < 7; i++)
{
DropDownList1.Items.Add(DateTime.Now.AddDays(-i).ToString());
}
}
I have a Microsoft Chart Controls within my winforms app.
I currently play the X and y values within a loop. I also had the X-axis format set as
ChartAreas[0].AxisX.LabelStyle.Format={"00:00:00"}
This worked fine as a time format, however I noticed once my time values went above 60 seconds, (i.e. 00:00:60), rather than the scale moving up to 1 minute (i.e. 00:01:00) it goes to 61 (i.e. 00:00:61) right up to 99 before it goes to one minute (00:00:99) then (00:01:00)
Is there a way to fix this please?
I suspect that LabelStyle.Format property is used in a similar way as in string.Format(mySringFormat,objToFormat).
Hence, given that your underlying X objects type is double, it will just print a colon-separated double (e.g. 4321 will be 00:43:21).
AFAIK, there isn't an easy way to print a double value like a time value using just a string format.
If you can change the code filling the chart, I suggest you to pass DateTime's for the X values, and then you will be able to use custom DateTime formatting, e.g.
"HH:mm:ss", or others
EDIT:
As per your comment:
// create a base date at the beginning of the method that fills the chart.
// Today is just an example, you can use whatever you want
// as the date part is hidden using the format = "HH:mm:ss"
DateTime baseDate = DateTime.Today;
var x = baseDate.AddSeconds((double)value1);
var y = (double)value2;
series.Points.addXY(x, y);
EDIT 2:
Here's a complete example, it should be easy to apply this logic to your code:
private void PopulateChart()
{
int elements = 100;
// creates 100 random X points
Random r = new Random();
List<double> xValues = new List<double>();
double currentX = 0;
for (int i = 0; i < elements; i++)
{
xValues.Add(currentX);
currentX = currentX + r.Next(1, 100);
}
// creates 100 random Y values
List<double> yValues = new List<double>();
for (int i = 0; i < elements; i++)
{
yValues.Add(r.Next(0, 20));
}
// remove all previous series
chart1.Series.Clear();
var series = chart1.Series.Add("MySeries");
series.ChartType = SeriesChartType.Line;
series.XValueType = ChartValueType.Auto;
DateTime baseDate = DateTime.Today;
for (int i = 0; i < xValues.Count; i++)
{
var xDate = baseDate.AddSeconds(xValues[i]);
var yValue = yValues[i];
series.Points.AddXY(xDate, yValue);
}
// show an X label every 3 Minute
chart1.ChartAreas[0].AxisX.Interval = 3.0;
chart1.ChartAreas[0].AxisX.IntervalType = DateTimeIntervalType.Minutes;
chart1.ChartAreas[0].AxisX.LabelStyle.Format = "HH:mm:ss";
}