8
Edit:
Steps:
Start at target day. 
Then move backwards until no events are carried over from another day.
From there, start counting hours, and keep track of carried over hours. 
Day cannot last more than ActualDayLength()
Then, once you know that, work your way back to target and then calculate actual occupied hours.

我有放在日历上的任务:

在此处输入图像描述

现在让我给出一些背景信息:每天在这里“持续”7.5 小时。但是我使用了一个名为 DayHours 的变量(现在是 7.5)。(DayHours 也用于锁定时间,这将在下面描述)。

此日历的目标是为员工安排 7.5 小时的工作日。

我需要的是一种算法,它可以正确地告诉我一天实际占用了多少小时。

这看起来很简单,但实际上是相当递归的。

首先,一些笔记。您会注意到案例经理在 14 小时时可以在 7.5 小时的 2 天中完成,剩下 1 小时。它被延长到 3 天,因为 1. 计划是 5 小时长,以及 2. 直到当天的前置任务完成后才能开始。

还有锁定时间的概念。紫色是锁定时间。这是一个 10 小时的锁定时间块。这意味着,在 12 日,我只能做 (7.5 - 7.5) 小时的工作,而周一也只能做 (7.5 - 2.5) 小时的工作。

我已经有一个函数来计算实际一天的可用时间来解决这个问题:

public decimal GetActualDayLength(DateTime day, Schedule s)
{
    var e = Schedules.GetAllWithElement();
    var t = Timeless(day);
    var locked = from p in e
                 where p.EmployeID == s.EmployeID &&
                 ((p.DateTo.Value.Date) >= t &&
                 Timeless(p.DateFrom.Value) <= t) &&
                 p.IsLocked
                 select p;

    decimal hrs = 0.0M;

    foreach (var c in locked)
    {
        if (c.Hours.Value <= DaysManager.GetDayHours())
            hrs += c.Hours.Value;
        else if (Timeless(c.DateTo.Value) != t)
            hrs += DaysManager.GetDayHours();
        else
        {
            if (c.Hours.Value % DaysManager.GetDayHours() > 0)
                hrs += c.Hours.Value % DaysManager.GetDayHours();
            else
                hrs += DaysManager.GetDayHours();
        }
    }

    return DaysManager.GetDayHours() - hrs;
}

还有携带时间的概念。

这是一个例子:

在此处输入图像描述

现在让我们以 18 号星期四为例(18 号有 1 个案例):

要找到该员工这一天的小时数,我们需要首先查看当天开始、结束或落入当天的任务。

我不知道我在 18 号可以做多少小时,因为那天结束的任务可能有携带时间。所以我去看看执行单元测试的开始日。我也想不通,因为 NWDM 当天完成,它可能需要几个小时。

所以现在我去评估 NWDM。啊,这一天没有结束,所以我知道日程安排需要 5 / 7.5 小时。

所以我继续前进,每天通过的时间增加 7.5 小时。

然后我到了 NWDM 的最后一天。在那之前,我工作了 5 + 7.5 + 7.5 + 7.5 小时,

所以我投入了 27.5 小时,所以我会在 22 日投入 (30 - 27.5 = 2.5h) 来完成它。所以我还有 5 个小时的时间来进行执行单元测试。

这意味着我需要 1.5 小时才能完成它。现在案例是 1 小时。

如果案例是 7.5 - 1.5 或更多,我们说这一天已满并返回 DayHours。

因此,我们完成了。返回值为 1.5 + 1 = 2.5。

该函数应该看起来有点像这样:

public decimal GetHours(IEnumerable<Schedule> s, DateTime today)
{
    DateTime t = Timeless(today);

    decimal hrs = 0;
    foreach (Schedule c in s)
    {
        if (c.Hours.Value <= DaysManager.GetDayHours())
            hrs += c.Hours.Value;
        else if (Timeless(c.DateTo.Value) != t)
            hrs += DaysManager.GetDayHours();
        else
        {
            if (c.Hours.Value % DaysManager.GetDayHours() > 0)
                hrs += c.Hours.Value % DaysManager.GetDayHours();
            else
                hrs += DaysManager.GetDayHours();
        }
    }
    return hrs;
}

要获取在给定日期内开始、结束或落下的事件,我使用:

public IEnumerable<Schedule> GetAllToday(DateTime date, int employeeID, Schedule current)
{
    DateTime t = Timeless(date);
    int sid = current == null ? -1 : current.ScheduleID;

    var e = Schedules.GetAllWithElement();
    return from p in e
           where (((Timeless(p.DateTo.Value) >= t &&
           Timeless(p.DateFrom.Value) <= t &&
           p.EmployeID == employeeID) &&
           (p.IsLocked || (Timeless(p.DateFrom.Value) < t &&
           (sid == -1 ? true : Timeless(p.DateFrom.Value) < current.DateFrom.Value)) ||
           bumpedList.Any(d => d.ScheduleID == p.ScheduleID)) &&
           p.ScheduleID != sid) ||
           ((Timeless(p.DateTo.Value) >= t &&
           (Timeless(p.DateFrom.Value) == t || (Timeless(p.DateFrom.Value) < t &&
           (sid == -1 ? true : Timeless(p.DateFrom.Value) > current.DateFrom.Value))) &&
           p.EmployeID == employeeID) &&
           !p.IsLocked &&
           !bumpedList.Any(d => d.ScheduleID == p.ScheduleID) &&
           p.ScheduleID != sid)) &&
           p.ScheduleID != sid
           select p;
        }

附表有以下相关字段:

DateFrom
DateTo
Hours
EmployeeID

时间表看起来像:

[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Schedule")]
public partial class Schedule : INotifyPropertyChanging, INotifyPropertyChanged
{
    private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
    private int _ScheduleID;
    private System.Nullable<System.DateTime> _DateFrom;
    private System.Nullable<decimal> _Hours;
    private System.Nullable<int> _EmployeID;
    private System.Nullable<int> _RecurringID;
    private System.Nullable<int> _Priority;
    private System.Nullable<System.DateTime> _DateTo;
    private bool _IsLocked;
    private System.Nullable<int> _BumpPriority;
    private EntitySet<Case> _Cases;
    private EntitySet<Project> _Projects;
    private EntitySet<Task> _Tasks;
    private EntitySet<Task> _Tasks1;
    private EntityRef<Employee> _Employee;
    private EntityRef<Recurring> _Recurring;

    #region Extensibility Method Definitions
    partial void OnLoaded();
    partial void OnValidate(System.Data.Linq.ChangeAction action);
    partial void OnCreated();
    partial void OnScheduleIDChanging(int value);
    partial void OnScheduleIDChanged();
    partial void OnDateFromChanging(System.Nullable<System.DateTime> value);
    partial void OnDateFromChanged();
    partial void OnHoursChanging(System.Nullable<decimal> value);
    partial void OnHoursChanged();
    partial void OnEmployeIDChanging(System.Nullable<int> value);
    partial void OnEmployeIDChanged();
    partial void OnRecurringIDChanging(System.Nullable<int> value);
    partial void OnRecurringIDChanged();
    partial void OnPriorityChanging(System.Nullable<int> value);
    partial void OnPriorityChanged();
    partial void OnDateToChanging(System.Nullable<System.DateTime> value);
    partial void OnDateToChanged();
    partial void OnIsLockedChanging(bool value);
    partial void OnIsLockedChanged();
    partial void OnBumpPriorityChanging(System.Nullable<int> value);
    partial void OnBumpPriorityChanged();
    #endregion

    public Schedule()
    {
        this._Cases = new EntitySet<Case>(new Action<Case>(this.attach_Cases), new Action<Case>(this.detach_Cases));
        this._Projects = new EntitySet<Project>(new Action<Project>(this.attach_Projects), new Action<Project>(this.detach_Projects));
        this._Tasks = new EntitySet<Task>(new Action<Task>(this.attach_Tasks), new Action<Task>(this.detach_Tasks));
        this._Tasks1 = new EntitySet<Task>(new Action<Task>(this.attach_Tasks1), new Action<Task>(this.detach_Tasks1));
        this._Employee = default(EntityRef<Employee>);
        this._Recurring = default(EntityRef<Recurring>);
        OnCreated();
    }
}

任何人都可以帮助我开发一种可以做到这一点的算法吗?

4

3 回答 3

1

尽管你的问题很复杂,解释得也不是很清楚,但我会尽力回答。或者更准确地暗示你应该如何分解和解决它(或者我将如何解决它)。

我需要的是一种算法,它可以正确地告诉我一天实际占用了多少小时。

起初我看不出真正的问题,以防万一你有DateTo可用的价值Schedule。除非它等于DateFrom+ Hours。在这种情况下,它并不反映真实DateTo但有些无关紧要的价值。

我假设 anySchedule是由开始时间DateFrom和持续时间定义的HoursDateTo是计算值和高效计算是问题的真正核心。

所以我认为这个功能在任何时间范围内都可以使用时间非常简单。用伪代码说话:

TimeSpan GetAvailableTime(DateRange range)
    var tasks = FindIntersectingTasks(range)

    ' now the algorithm which finds available hours on given collection 
    ' of tasks
    ' firstly - we need to determine relevant ranges which intersect 
    '           with given range
    var occupiedRanges = New List<DateRange>(tasks.Count)
    for each task in tasks
        var intersection = range.Intersect(
            new DateRange(task.DateFrom, task.DateTo)
        )

        if Not intersection.IsEmpty
            occupiedRanges.Add(intersection)
        end 
    end 

    ' secondly - sort ranges by start so we can easily merge them
    ranges.Sort(range => range.DateFrom)
    var mergedOccupiedRanges = new List(DateRange)

    ' thirdly - merge ranges so that we have collection with 
    '           non-overlaping ranges (and also sorted)
    for each occupiedRange in occupiedRanges
        ' range may merge only it there is non-empty intersection
        if occupiedRange.CanMerge(mergedOccupiedRanges.Last)
             var mergedRange = range.Merge(mergedOccupiedRanges.Last)
             mergedOccupiedRanges.RemoveLast()
             mergedOccupiedRanges.Add(mergedRange)
        end
    end 

    ' fourthly - it is simple now to determine available/occupied hours
    var timeAvailable = range.Duration
    for each mergedRange in mergedOccupiedRanges
        timeAvailable -= mergedRange.Duration
    end

    return timeAvailable
end

IEnumerable<Schedule> FindIntersectingTasks(DateRange range)
    return From schedule In allEvents
           Where schedule.DateFrom <= range.To 
               And schedule.DateTo >= range.From
end

您可能需要一些调整,因为DateTime预计正常的 24 小时工作日。

于 2014-02-23T12:51:35.177 回答
0

这不能回答确切的问题,但我建议通过使用一些辅助方法扩展您的类(对象)来简化您的逻辑,例如返回占用天数列表的方法/属性。如果您无法访问这些类(即它们不是来自您的代码库) - 然后创建新类并映射到这些类。此外 - .NET DateTime 类具有一些非常有用的属性和枚举,例如“DayOfWeek”和“TimeOfDay”,它们可能对您有用。

于 2014-12-22T17:22:13.243 回答
0

像这样:

  1. 创建有空闲时间的日期列表。
  2. 对于列表中的每个项目,添加可用于您的任务的最长时间。
  3. 如果您的项目所需的时间达到 0,请停止添加块。
于 2013-04-19T08:48:43.727 回答