根据日期范围查找确切的周数
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();
我需要计算员工的总分配周数和总替补周数。
一名员工在某个时间段内被分配到不同的项目中,该时间段总是从星期一开始,总是在星期五结束。周一至周五将被视为 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();