如何从一组给定的输入中找到 EndDate

How to find the EndDate from a set of given inputs

我正在努力解决日期时间问题,在该问题中,我获得了一组输入,我需要使用这些输入找到 EndDate。我正在尽力解决这个问题,但由于我 运行 没时间了,所以我来到了这里。所以可能已经遇到这个问题的人或有解决方案的人可以告诉我一个。

问题解释:

概念是直播 class 流媒体应用程序的每周时间表。老师安排了从特定开始日期开始的每周 class。

假设开始日期是 2021 年 4 月 18 日,老师每周选择 3 天(星期一、星期二和星期三),每一天都有不同的 class 持续时间(我的意思是,我们有class 当天的开始时间以及 class 的时长和分钟数)。

好的!现在我们将重复将 TDPD(3 小时 30 分钟)添加到开始日期,直到我们达到 50 小时并找到它到达的日期(结束日期)。

到目前为止我尝试了什么?

int totalWeeks = 0;
int totalHoursPerWeek = 3;
int totalHoursAdded = 0;
int maxHours = 50;
for(int i = totalHoursPerWeek; i <= maxHours; i += totalHoursPerWeek)
{
    totalHoursAdded += i;
    totalWeeks++;
}

循环结束后,我就有了总周值。

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

这是两个选项(使用 For 和使用 While 循环),应该可以解决问题:

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();

        }
    }
}

我相信这可以通过一些经过深思熟虑的日期数学来完成,但是下面可能被称为懒惰的出路。这种方法只需要开始日期、class 会面的星期几列表,即星期一、星期二等……,class 的持续时间(以小时和分钟为单位),最后是所需的最大总小时数和分钟数。

根据我的理解,根据上述信息,我们想知道,给定开始日期和 class 会面的星期几……

on what Date will the LAST class be that fulfills the max total hours.

为了简化这一点,一件事会派上用场,那就是知道...

“how many classes are needed to fulfill the MAX requirement”.

换句话说,如果我们知道需要多少 class 才能满足最大要求,那么这应该会让事情变得更容易。

计算满足最大要求所需的 classes 总数,可以通过将最大要求除以 class 持续时间得到。如果除法产生余数,则这意味着需要额外的一 (1) 个 class 来满足最大要求。

因此,如果我们将 class duration 和 max duration 变量都作为 Timespan 对象,那么,我们可以通过它们各自的 Tick 划分 TimeSpan 个对象属性和 return 需要多少 class 才能满足最大要求。这个方法可能看起来像……

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

接下来,我们需要比较日期的 DayOfWeek 和 class 的 DayOfWeek。因此,我们可以做的是创建一个 List<DayOfWeek> … class 正在会话中的 DayOfWeek 个对象的列表。我们将使用此列表来检查并查看特定日期的星期几是否在该列表中。因此,在此示例中,class 遇到的星期几是一个简单的逗号分隔 string。鉴于此 string,代码将解析出日期和 return 正确的 DayOfWeek 对象列表以进行比较。这个方法可能看起来像……

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

这就是我们所需要的。总体思路是这样的……我们首先将 int 变量 curClassCount 设置为零 (0)。此外,我们将创建一个用开始日期初始化的 DateTime 对象 tempDate。最后,我们将创建一个包含 DateTime 个对象 scheduledClasses 的列表,其中将填充 class 个对象的日期。只要 curClassCount 少于所需的 classes 总数,我们将开始一个 while 循环,条件是继续。

在循环的每次迭代中,都会检查 tempDateDayOfWeek 是否是 class 的 DayOfWeek. 之一……如果是是,然后我们将该日期添加到 scheduledClasses 列表并递增 curClassCount。最后将 tempDate 增加一 (1) 天,然后重新开始循环。最终,curClassCount 将等于所需的 classes 的数量。这段代码可能看起来像……

while (curClassCount < totalNumberOfClassesNeeded) {
  if (ClassDaysOfWeek.Contains(tempDate.DayOfWeek)) {
    scheduledClasses.Add(tempDate.Date);
    curClassCount++;
  }
  tempDate = tempDate.AddDays(1);
}

通过将 DateTimePicker, four (4) TextBoxes、Button 和多行 TextBox 放到新的 Winforms .Net Form 上,将所有这些放在一起可能看起来像……

使用下面的代码应该可以完成示例。

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

请注意,关于最长持续时间……因为 TimeSpan 只允许小时 < 23,我们需要将 50 小时分成 2 天 2 小时。我希望这是有道理的。

最后,我根据@John 的回答找到了自己的解决方案。此答案专门针对可变小时和分钟。

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

和模特

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