Saturday, August 29, 2009

Calculate the next time a job scheduled for multiple days should be processed

This is my research for a component in a real world project.

Scenario:
User creates a new job to process at 3 pm on Monday and Friday. *Note: I'm using System.Threads.Timers for async job scheduling, so multiple jobs can be configured.

Solution:
First here's the UI I created for my research:

Current Date: it defaults to DateTime.Now, but can be changed. This is so I could create several different testing scenarios
Time of Day: The poorly named field is actually "TimeToExecute". This defaults to the current time and can be changed to any time.

Source notes:

btnWeekly_Click method is what you would call when you call Timer.Change(TimeSpan, TimeSpan). In a real job scheduler when you create the Timer you would want to call the "btnWeekly_Click" method (obviously not an event handler in real code) to calculate the first parameter of Timer.Change() function.

Note: My DayOfWeek enum is a bit flag (marked with [Flags], which means you can use bit operations and have multiple days selected. System.DayOfWeek is not a bit flagged enum. I'll create a separate blog post about this subject.

Any questions about implementation or about the code comment or email me.

Source:

private void btnWeekly_Click(object sender, EventArgs e) {
    DayOfWeek days = new DayOfWeek();
    //this is from the schedule object

    DateTime now = DateTime.Parse(txtCurrentDateTime.Text);
    TimeSpan target = DateTime.Parse(txtTimeOfDay.Text).TimeOfDay;
    TimeSpan currentBest = new TimeSpan(20, 0, 0, 0);
    //set a random amount of time that can't possibly be
    //reached in a legitmite configuration, to initalize comparison
    //this will not be necessary in the real code, just for simulation

    //This is just for the simulator, Remove when moved
    for (int i = 0; i < daysOfWeek.CheckedItems.Count; i++) {
        switch (daysOfWeek.CheckedItems[i].ToString()) {
            case "Sunday":
                days = days | DayOfWeek.Sunday;
                break;
            case "Monday":
                days = days | DayOfWeek.Monday;
                break;
            case "Tuesday":
                days = days | DayOfWeek.Tuesday;
                break;
            case "Wednesday":
                days = days | DayOfWeek.Wednesday;
                break;
            case "Thursday":
                days = days | DayOfWeek.Thursday;
                break;
            case "Friday":
                days = days | DayOfWeek.Friday;
                break;
            case "Saturday":
                days = days | DayOfWeek.Saturday;
                break;
        }
    }
    //end of remove



    for (int j = 0; j < 7; j++) {
        if (daySelected(days, now)) {
            TimeSpan tmp = getNextOccurence(now.TimeOfDay, target, j, false);
            if (TimeSpan.Compare(tmp, currentBest) < 0)
                currentBest = tmp;
        }
        now = now.AddDays(1);
    }
    //DEBUGGING
    txtResults.Text += "Best time = " + currentBest.TotalMinutes.ToString() + Environment.NewLine;
}
private TimeSpan getNextOccurence(TimeSpan checkTime, TimeSpan targetTime, int dayCount, bool isDaily) {
    TimeSpan ts = new TimeSpan();
    if (TimeSpan.Compare(checkTime, targetTime) < 0) {
        //checkTime is earlier than targetTime
        if (isDaily)
            dayCount = 0;
        ts = targetTime.Subtract(checkTime);
        ts = ts.Add(new TimeSpan(dayCount, 0, 0, 0));
        //DEBUGGING
        txtResults.Text += "dayCount = " + dayCount.ToString() +
            " difference in minutes = " + ts.TotalMinutes.ToString() + Environment.NewLine;

    } else {
        //checkTime is later than targetTime
        if (isDaily)
            dayCount = 6;
        ts = new TimeSpan(6 - dayCount, 0, 0, 0);
        ts = ts.Add(new TimeSpan(24, 0, 0).Subtract(checkTime.Subtract(targetTime)));
        //DEBUGGING
        txtResults.Text += "dayCount = " + dayCount.ToString() +
            " difference in minutes = " + ts.TotalMinutes.ToString() + Environment.NewLine;

    }
    return ts;
}
private bool daySelected(DayOfWeek daysSelected, DateTime toTest) {
    DayOfWeek dayOfWeekToTest = mapDayToDayOfWeekEnum(toTest);
    return (daysSelected & dayOfWeekToTest) == dayOfWeekToTest;
}
private DayOfWeek mapDayToDayOfWeekEnum(DateTime test) {
    switch (test.DayOfWeek) {
        case System.DayOfWeek.Friday:
            return DayOfWeek.Friday;
            break;
        case System.DayOfWeek.Monday:
            return DayOfWeek.Monday;
            break;
        case System.DayOfWeek.Saturday:
            return DayOfWeek.Saturday;
            break;
        case System.DayOfWeek.Sunday:
            return DayOfWeek.Sunday;
            break;
        case System.DayOfWeek.Thursday:
            return DayOfWeek.Thursday;
            break;
        case System.DayOfWeek.Tuesday:
            return DayOfWeek.Tuesday;
            break;
        case System.DayOfWeek.Wednesday:
            return DayOfWeek.Wednesday;
            break;
    }
    return DayOfWeek.Wednesday; //this will not be reached
}#
endregion
}
[Flags]
public enum DayOfWeek {
    Sunday = 1,
        Monday = 2,
        Tuesday = 4,
        Wednesday = 8,
        Thursday = 16,
        Friday = 32,
        Saturday = 64
}


No comments:

Post a Comment

There was an error in this gadget