根据日期范围查找确切的周数

Find the exact week count based on date range

我需要计算员工的总分配周数和总替补周数。

一名员工在某个时间段内被分配到不同的项目中,该时间段总是从星期一开始,总是在星期五结束。周一至周五将被视为 1 周。如果员工在分配的状态周项目中工作,并且该项目持续时间属于替补状态周项目,则分配状态的任何重叠周都应被视为 1 周,因此该周将不计入总替补周数。

以下为相关场景

"AssignedHistory":[ 
   {"Project":1,"status":"Assigned","Startdate":"01/03/2022", "Enddate":"01/07/2022" }, 
   {"Project":2,"status":"Assigned","Startdate":"01/10/2022", "Enddate":"01/14/2022" }, 
   {"Project":3,"status":"Assigned", "Startdate":"01/10/2022", "Enddate":"01/21/2022" }, 
   {"Project":4,"status":"Bench","Startdate":"01/17/2022", "Enddate":"01/21/2022" },
   {"Project":5,"status":"Bench","Startdate":"02/07/2022", "Enddate":"02/11/2022" }    
]

在这里我需要找到员工的总分配周数和总替补周数,预期结果应该是:

Total assigned week:3
Total benched week :1

由于从 1 月 10 日到 1 月 14 日,2 个项目的 1 周分配状态重叠,而且项目 4 的工作周与 1 月 17 日到 1 月 21 日的项目 3 的分配状态重叠。

这就是我正在尝试的方式

var assignedweek = AssignedHistory.Where(x => new []{"Assigned"}.Contains(x.status)).ToList();
var benchedweek = AssignedHistory.Where(x => new []{"Bench"}.Contains(x.status)).ToList();
 
int AssignedWeekCount = assignedweek
    .SelectMany(a => {
        DateTime firstSunday = a.Startdate.AddDays(-(int)a.Startdate.DayOfWeek);
        DateTime lastSunday = a.Enddate.AddDays(-(int)a.Enddate.DayOfWeek); 
        int weeks = (lastSunday - firstSunday).Days / 7 + 1;

        // Enumerate one Sunday per week
        return Enumerable.Range(0, weeks).Select(i => firstSunday.AddDays(7 * i));
    })
    .Distinct()
    .Count();
    
    int BenchWeekCount = benchedweek
    .SelectMany(a => {
        DateTime firstSunday = a.Startdate.AddDays(-(int)a.Startdate.DayOfWeek);
        DateTime lastSunday = a.Enddate.AddDays(-(int)a.Enddate.DayOfWeek); 
        int weeks = (lastSunday - firstSunday).Days / 7 + 1;

        // Enumerate one Sunday per week
        return Enumerable.Range(0, weeks).Select(i => firstSunday.AddDays(7 * i));
    })
    .Distinct()
    .Count();

但是如果替补周与分配的周有重叠,它会给出不正确的替补周计数。我无法从替补周中删除重叠的一周。

任何建议将不胜感激。

我认为这会有所帮助。这是计算重叠周数的公式,其中指定周与替补周重叠。

此公式将计算有多少工作周与指定工作周重叠,您可以从工作周计数中减去。

 var overlappingWeeks = benchedweek.Where(b => assignedweek.Any(
         a => a.Startdate < b.Enddate && a.Enddate > b.Startdate)
     )
     .Select(b => b.Startdate)
     .Distinct()
     .Count();

ETA:我忘记了同一周可能有多个替补周,我的公式会把它们都算进去!所以我添加了 Select 和一个 Distinct.

让我知道这在您的场景中是如何工作的:我是凭记忆做的,所以还没有经过测试。

您创建这样的周结构可能会很有趣:

public class Week
    {
        public int WeekId { get; private set; }
        private DateTime FirstDayOfWeek {get; set; }

        private const int numDayInWeek = 7;


        public Week(Week anotherWeek): this(anotherWeek.FirstDayOfWeek)
        {

        }

        public Week(DateTime date)
        {
            FirstDayOfWeek = GetFirstDateOfWeek(date);
            var numWeekInYear = GetIndexWeekInYearForFirstDayOfWeek(date);
            WeekId = GenerateWeekId(numWeekInYear, FirstDayOfWeek.Year);
        }

        private int GetIndexWeekInYearForFirstDayOfWeek(DateTime date)
        {
            return (GetFirstDateOfWeek(date).DayOfYear - 1) / numDayInWeek + 1;
        }

        private DateTime GetFirstDateOfWeek(DateTime date)
        {
            return date.AddDays(-(int)date.DayOfWeek);
        }

        private int GenerateWeekId(int numWeekInYear, int year)
        {
            return year * 100 + numWeekInYear;
        }

        public static List<Week> operator -(Week last, Week first)
        {
            List<Week> ret = new List<Week>();
            if (last.WeekId < first.WeekId)
                return ret;            
            for(Week cursor = first; cursor <= last; cursor ++)
                ret.Add(cursor);
            return ret;
        }

        public static Week operator++(Week current)
        {
            return new Week(current.FirstDayOfWeek.AddDays(numDayInWeek));
        }

        public static bool operator >= (Week left, Week right)
        {
            return left.FirstDayOfWeek >= right.FirstDayOfWeek;
        }

        public static bool operator <= (Week left, Week right)
        {
            return left.FirstDayOfWeek <= right.FirstDayOfWeek;
        }

        public override int GetHashCode()
        {
            return WeekId;
        }
        public override bool Equals(object obj)
        {
            return Equals(obj as Week);
        }

        public bool Equals(Week obj)
        {
            return obj != null && obj.WeekId == this.WeekId;
        }
    }
   

并像这样使用这个结构:

internal class ProjectDataCalculator
        {
            public List<Week> GetProjectDataWeeks(ProjectData projectData)
            {
                return new Week(projectData.EndDate) - new Week(projectData.StartDate);
            }
    
            public ProjectReport GetProjectReport(List<ProjectData> projectDatas)
            {
                ProjectReport report = new ProjectReport();
                foreach (var projectData in projectDatas)
                {
                    foreach (var week in GetProjectDataWeeks(projectData))
                    {
                        DefineWeekAssigned(report, projectData.Assigned, week);
                    }
                }
                return report;
            }
    
            private void DefineWeekAssigned(ProjectReport report, ProjectDataStatus assigned, Week week)
            {
                if(report.WeeksStatus.ContainsKey(week))
                {
                    if (assigned == ProjectDataStatus.Assigned)
                        report.WeeksStatus[week] = assigned;
                } else
                {
                    report.WeeksStatus[week] = assigned;
                }
            }
        }
    
        internal class ProjectReport {
            public Dictionary<Week, ProjectDataStatus> WeeksStatus { get; set; } = new Dictionary<Week, ProjectDataStatus>();
        }

enum ProjectDataStatus
    {
        Assigned,
        Bench
    }
    internal class ProjectData
    {
        public int Project { get; set; }
        public ProjectDataStatus Assigned { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
    }

使用 .Except(AssignedWeeks 中间结果) 应该可以解决问题。尝试:

var assignedweek = AssignedHistory.Where(x => new []{"Assigned"}.Contains(x.status)).ToList();
var benchedweek = AssignedHistory.Where(x => new []{"Bench"}.Contains(x.status)).ToList();
 
var AssignedWeeks = assignedweek
    .SelectMany(a => {
        DateTime firstSunday = a.Startdate.AddDays(-(int)a.Startdate.DayOfWeek);
        DateTime lastSunday = a.Enddate.AddDays(-(int)a.Enddate.DayOfWeek); 
        int weeks = (lastSunday - firstSunday).Days / 7 + 1;

        // Enumerate one Sunday per week
        return Enumerable.Range(0, weeks).Select(i => firstSunday.AddDays(7 * i));
    })
    .Distinct()
    .ToList();

int AssignedWeekCount = AssignedWeeks.Count();
    
int BenchWeekCount = benchedweek
    .SelectMany(a => {
        DateTime firstSunday = a.Startdate.AddDays(-(int)a.Startdate.DayOfWeek);
        DateTime lastSunday = a.Enddate.AddDays(-(int)a.Enddate.DayOfWeek); 
        int weeks = (lastSunday - firstSunday).Days / 7 + 1;

        // Enumerate one Sunday per week
        return Enumerable.Range(0, weeks).Select(i => firstSunday.AddDays(7 * i));
    })
    .Distinct()
    .Except(AssignedWeeks) // Exclude weeks already counted as assigned
    .Count();