首页 > 解决方案 > 根据初始开始时间计算下一次开始时间

问题描述

考虑以下对象:

new TimeObject 
{
    StartTime = new DateTime(2019, 1, 1, 0, 0 , 0),
    DurationInMinutes = 20,
    RepeatFrequencyType = RepeatFrequencyType.Month
    //This would mean repeat every 1 month.
    RepeatFrequency = 1,
}

我需要编写代码,在 2019 年 1 月 1 日在屏幕上显示一条消息,然后每个月在同一时间重复。现在这显示在网站上,因此您可以在必须显示的消息中途加载页面。所以为了解决这个问题,我想到了一个两步的过程。首先是找出下一个开始时间是什么(如果用户在显示消息时加载,这可能是过去),然后第 2 步是确定我是否应该显示消息或需要多长时间展示它。第2步很容易解决,但第1步有些麻烦。因此,这是我的解决方案,它适用于我设置的单元测试。

请注意,我在这里仅显示第 1 步的代码,这是我需要帮助的部分。我解释了全貌,以便您更好地理解问题。

private DateTime GetNextMonthStartDate()
{
    var currentDate = DateTime.UtcNow;

    //If this is the first time it is running then we just return the initial start time.
    if (currentDate < StartTime.AddMinutes(DurationInMinutes)) return StartTime;
    //If we happen to run this when there is 0 minutes left, then return next month'start time.
    if (currentDate == StartTime.AddMinutes(DurationInMinutes)) return StartTime.AddMonths(RepeatFrequency);

    var dayOfTheMonth = StartTime.Day;
    var previousDateOfTheMonth = currentDate.AddMinutes(-DurationInMinutes);
    //As not every month has same number of days, if we are on one which has less days then we just run it on the last day of that month.
    var lastDay = DateTime.DaysInMonth(currentDate.Year, currentDate.Month);
    if (dayOfTheMonth > lastDay) dayOfTheMonth = lastDay;

    //If on the same day
    if (currentDate.Day == dayOfTheMonth)
    {
        var nextStartDate = new DateTime(currentDate.Year, currentDate.Month, currentDate.Day, StartTime.Hour, StartTime.Minute, StartTime.Second);
        var endDate = nextStartDate.AddMinutes(DurationInMinutes);

        //If the event is still lasting or has not started  then return current date and start time else return next month start time.
        return currentDate < endDate ? nextStartDate : nextStartDate.AddMonths(RepeatFrequency);
    }

    //If the event is still running but it started in previous day, we return start date of that previous day.
    if (currentDate.Day != previousDateOfTheMonth.Day && previousDateOfTheMonth.Day == dayOfTheMonth)
    {
        return new DateTime(previousDateOfTheMonth.Year, previousDateOfTheMonth.Month, previousDateOfTheMonth.Day, StartTime.Hour, StartTime.Minute, StartTime.Second);
    }

    //Subtract next day of the month (based on the current year and month and start date) from the current date
    var nextDayOfTheMonthDate = new DateTime(currentDate.Year, currentDate.Month, StartTime.Day);
    var currentDateWithoutTime = new DateTime(currentDate.Year, currentDate.Month, currentDate.Day);
    var daysUntilDayOfTheMonth = nextDayOfTheMonthDate.Subtract(currentDateWithoutTime).TotalDays;

    //If days is less than 0 it means it has passed, so we will recalculate from the next month.
    if (daysUntilDayOfTheMonth < 0)
    {
        daysUntilDayOfTheMonth = nextDayOfTheMonthDate.AddMonths(RepeatFrequency).Subtract(currentDateWithoutTime).TotalDays;
    }

    //Get the next day, month and year by adding days from current time. This will ensure things like switching into next year won't cause a problem.
    var nextDate = currentDate.AddDays(daysUntilDayOfTheMonth);

    //return date time with nextDate year, month and day with startDate time.
    return new DateTime(nextDate.Year, nextDate.Month, nextDate.Day, StartTime.Hour, StartTime.Minute, StartTime.Second);
}

正如您所看到的,它感觉有些复杂,现在我需要为年频率、日、小时等...出去?

标签: c#asp.net-core-mvc

解决方案


如果我正确理解了这个问题,那么您将尝试添加RepeatFrequencyStartTime直到达到DateTime大于当前日期的值。

如果是这种情况,我认为您可以使用一个循环来增加nextTimeby RepeatFrequencyuntil nextTime > DateTime.UtcNow

首先,我假设您有enum以下内容:

enum RepeatFrequencyType
{
    Minutes,
    Hours,
    Days,
    Weeks,
    Months,
    Years,
    FirstWeekdayOfMonth
}

如果是这样,我认为这个逻辑可以解决问题:

class TimeObject
{
    public DateTime StartTime { get; set; }
    public int DurationInMinutes { get; set; }
    public RepeatFrequencyType RepeatFrequencyType { get; set; }
    public int RepeatFrequency { get; set; }

    public DateTime NextStartTime()
    {
        var currentTime = DateTime.UtcNow;

        // Grab the StartTime and add the duration
        var nextTime = StartTime.AddMinutes(DurationInMinutes);

        // Continue to increment it until it's greater than the current time
        while (currentTime >= nextTime)
        {
            switch (RepeatFrequencyType)
            {
                case RepeatFrequencyType.Minutes:
                    nextTime = nextTime.AddMinutes(RepeatFrequency);
                    break;
                case RepeatFrequencyType.Hours:
                    nextTime = nextTime.AddHours(RepeatFrequency);
                    break;
                case RepeatFrequencyType.Days:
                    nextTime = nextTime.AddDays(RepeatFrequency);
                    break;
                case RepeatFrequencyType.Weeks:
                    nextTime = nextTime.AddDays(RepeatFrequency * 7);
                    break;
                case RepeatFrequencyType.Months:
                    nextTime = nextTime.AddMonths(RepeatFrequency);
                    break;
                case RepeatFrequencyType.Years:
                    nextTime = nextTime.AddYears(RepeatFrequency);
                    break;
                case RepeatFrequencyType.FirstWeekdayOfMonth:
                    nextTime = GetNextFirstWeekdayOfMonth(nextTime.AddMonths(RepeatFrequency));
                    break;
                default:
                    throw new Exception("Unknown value for RepeatFrequency specified.");
            }
        }

        // Remove the added duration from the return value
        return nextTime.AddMinutes(-DurationInMinutes);
    }

    private DateTime GetNextFirstWeekdayOfMonth(DateTime date)
    {
        // Start at the first day of the month
        var firstWeekday = new DateTime(date.Year, date.Month, 1);

        // While the first day is not a weekday, add a day
        while (firstWeekday.DayOfWeek == DayOfWeek.Saturday ||
               firstWeekday.DayOfWeek == DayOfWeek.Saturday)
        {
            firstWeekday.AddDays(1);
        }

        // If the specified date is greater than the first weekday,
        // return the first weekday of the next month.
        if (date > firstWeekday)
        {
            firstWeekday = GetNextFirstWeekdayOfMonth(date.AddMonths(1));
        }

        return firstWeekday;
    }
}

推荐阅读