using System;
using System.Collections.Generic;
using System.Linq;
namespace NPP.SmartSchedue.Api.Contracts.Domain.Common
{
///
/// 日期范围类
/// 智能排班系统中的时间区间管理,支持工作日计算、工时统计和时间跨度分析
/// 深度业务思考:提供完整的时间范围操作,支持复杂的排班业务场景
///
public class DateRange
{
#region 属性定义
///
/// 开始日期
///
public DateTime StartDate { get; set; }
///
/// 结束日期
///
public DateTime EndDate { get; set; }
///
/// 时间跨度
/// 计算开始日期和结束日期之间的时间差
///
public TimeSpan Duration => EndDate.Date - StartDate.Date;
///
/// 总天数(包含开始和结束日期)
///
public int TotalDays => Duration.Days + 1;
///
/// 纯粹的天数差(不包含结束日期)
///
public int DurationDays => Duration.Days;
///
/// 工作日天数
/// 排除周末和节假日的实际工作天数
///
public int WorkingDays => CalculateWorkingDays();
///
/// 自然工作日天数
/// 仅排除周末,不考虑节假日
///
public int NaturalWorkingDays => CalculateNaturalWorkingDays();
///
/// 周末天数
///
public int WeekendDays => CalculateWeekendDays();
///
/// 节假日天数
///
public int HolidayDays => CalculateHolidayDays();
///
/// 是否为有效的日期范围
///
public bool IsValid => StartDate <= EndDate;
///
/// 是否为单日范围
///
public bool IsSingleDay => StartDate.Date == EndDate.Date;
///
/// 是否跨越多个月份
///
public bool SpansMultipleMonths => StartDate.Month != EndDate.Month || StartDate.Year != EndDate.Year;
///
/// 是否跨越多个年份
///
public bool SpansMultipleYears => StartDate.Year != EndDate.Year;
#endregion
#region 构造函数
///
/// 默认构造函数
///
public DateRange()
{
StartDate = DateTime.Today;
EndDate = DateTime.Today;
}
///
/// 指定开始和结束日期的构造函数
///
/// 开始日期
/// 结束日期
public DateRange(DateTime startDate, DateTime endDate)
{
StartDate = startDate.Date;
EndDate = endDate.Date;
if (!IsValid)
{
throw new ArgumentException("结束日期不能早于开始日期");
}
}
///
/// 从指定日期开始,持续指定天数的构造函数
///
/// 开始日期
/// 持续天数
public DateRange(DateTime startDate, int days)
{
if (days < 0)
{
throw new ArgumentException("天数不能为负数");
}
StartDate = startDate.Date;
EndDate = startDate.Date.AddDays(days - 1);
}
#endregion
#region 工时计算方法
///
/// 计算标准工时
/// 基于每天8小时的标准工作时间
///
/// 每日标准工时,默认8小时
/// 总标准工时
public decimal CalculateStandardHours(decimal dailyStandardHours = 8.0m)
{
return WorkingDays * dailyStandardHours;
}
///
/// 计算自然日标准工时
/// 基于总天数计算,不考虑工作日
///
/// 每日标准工时,默认8小时
/// 总标准工时
public decimal CalculateNaturalStandardHours(decimal dailyStandardHours = 8.0m)
{
return TotalDays * dailyStandardHours;
}
///
/// 计算工作日工时
/// 仅计算工作日的工时,排除周末和节假日
///
/// 每个工作日的工时,默认8小时
/// 工作日总工时
public decimal CalculateWorkingHours(decimal dailyWorkingHours = 8.0m)
{
return WorkingDays * dailyWorkingHours;
}
///
/// 计算可用工时
/// 考虑具体的工作时间配置
///
/// 工作时间配置
/// 可用总工时
public decimal CalculateAvailableHours(WorkingTimeConfiguration workingTimeConfig = null)
{
if (workingTimeConfig == null)
{
return CalculateWorkingHours();
}
decimal totalHours = 0;
var currentDate = StartDate;
while (currentDate <= EndDate)
{
if (IsWorkingDay(currentDate))
{
totalHours += workingTimeConfig.GetDailyWorkingHours(currentDate);
}
currentDate = currentDate.AddDays(1);
}
return totalHours;
}
#endregion
#region 日期判断方法
///
/// 判断指定日期是否在范围内
///
/// 要判断的日期
/// 是否在范围内
public bool Contains(DateTime date)
{
var checkDate = date.Date;
return checkDate >= StartDate && checkDate <= EndDate;
}
///
/// 判断是否与另一个日期范围重叠
///
/// 另一个日期范围
/// 是否重叠
public bool Overlaps(DateRange other)
{
if (other == null) return false;
return StartDate <= other.EndDate && EndDate >= other.StartDate;
}
///
/// 判断是否完全包含另一个日期范围
///
/// 另一个日期范围
/// 是否完全包含
public bool Contains(DateRange other)
{
if (other == null) return false;
return StartDate <= other.StartDate && EndDate >= other.EndDate;
}
///
/// 判断指定日期是否为工作日
///
/// 要判断的日期
/// 是否为工作日
public bool IsWorkingDay(DateTime date)
{
// 排除周末
if (date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday)
{
return false;
}
// 排除节假日
return !IsHoliday(date);
}
///
/// 判断指定日期是否为节假日
///
/// 要判断的日期
/// 是否为节假日
public bool IsHoliday(DateTime date)
{
// 这里可以集成具体的节假日数据源
// 暂时返回false,实际项目中需要根据具体需求实现
return HolidayProvider.IsHoliday(date);
}
#endregion
#region 范围操作方法
///
/// 获取与另一个日期范围的交集
///
/// 另一个日期范围
/// 交集范围,如果没有交集则返回null
public DateRange Intersect(DateRange other)
{
if (other == null || !Overlaps(other))
{
return null;
}
var intersectStart = StartDate > other.StartDate ? StartDate : other.StartDate;
var intersectEnd = EndDate < other.EndDate ? EndDate : other.EndDate;
return new DateRange(intersectStart, intersectEnd);
}
///
/// 获取与另一个日期范围的并集
///
/// 另一个日期范围
/// 并集范围
public DateRange Union(DateRange other)
{
if (other == null)
{
return new DateRange(StartDate, EndDate);
}
var unionStart = StartDate < other.StartDate ? StartDate : other.StartDate;
var unionEnd = EndDate > other.EndDate ? EndDate : other.EndDate;
return new DateRange(unionStart, unionEnd);
}
///
/// 扩展日期范围
///
/// 向前扩展的天数
/// 向后扩展的天数
/// 扩展后的日期范围
public DateRange Extend(int daysBefore, int daysAfter)
{
return new DateRange(
StartDate.AddDays(-daysBefore),
EndDate.AddDays(daysAfter)
);
}
///
/// 拆分日期范围为指定天数的子范围
///
/// 每个子范围的天数
/// 子范围列表
public List Split(int chunkSize)
{
if (chunkSize <= 0)
{
throw new ArgumentException("块大小必须大于0");
}
var chunks = new List();
var currentStart = StartDate;
while (currentStart <= EndDate)
{
var currentEnd = currentStart.AddDays(chunkSize - 1);
if (currentEnd > EndDate)
{
currentEnd = EndDate;
}
chunks.Add(new DateRange(currentStart, currentEnd));
currentStart = currentEnd.AddDays(1);
}
return chunks;
}
#endregion
#region 日期枚举方法
///
/// 获取范围内的所有日期
///
/// 日期列表
public List GetAllDates()
{
var dates = new List();
var currentDate = StartDate;
while (currentDate <= EndDate)
{
dates.Add(currentDate);
currentDate = currentDate.AddDays(1);
}
return dates;
}
///
/// 获取范围内的所有工作日
///
/// 工作日列表
public List GetWorkingDays()
{
return GetAllDates().Where(IsWorkingDay).ToList();
}
///
/// 获取范围内的所有周末
///
/// 周末日期列表
public List GetWeekends()
{
return GetAllDates().Where(date =>
date.DayOfWeek == DayOfWeek.Saturday ||
date.DayOfWeek == DayOfWeek.Sunday).ToList();
}
///
/// 获取范围内的所有节假日
///
/// 节假日列表
public List GetHolidays()
{
return GetAllDates().Where(IsHoliday).ToList();
}
#endregion
#region 私有计算方法
///
/// 计算工作日天数
///
private int CalculateWorkingDays()
{
return GetWorkingDays().Count;
}
///
/// 计算自然工作日天数(仅排除周末)
///
private int CalculateNaturalWorkingDays()
{
var workDays = 0;
var currentDate = StartDate;
while (currentDate <= EndDate)
{
if (currentDate.DayOfWeek != DayOfWeek.Saturday &&
currentDate.DayOfWeek != DayOfWeek.Sunday)
{
workDays++;
}
currentDate = currentDate.AddDays(1);
}
return workDays;
}
///
/// 计算周末天数
///
private int CalculateWeekendDays()
{
return GetWeekends().Count;
}
///
/// 计算节假日天数
///
private int CalculateHolidayDays()
{
return GetHolidays().Count;
}
#endregion
#region 静态工厂方法
///
/// 创建今天的日期范围
///
public static DateRange Today()
{
return new DateRange(DateTime.Today, DateTime.Today);
}
///
/// 创建本周的日期范围
///
public static DateRange ThisWeek()
{
var today = DateTime.Today;
var startOfWeek = today.AddDays(-(int)today.DayOfWeek);
var endOfWeek = startOfWeek.AddDays(6);
return new DateRange(startOfWeek, endOfWeek);
}
///
/// 创建本月的日期范围
///
public static DateRange ThisMonth()
{
var today = DateTime.Today;
var startOfMonth = new DateTime(today.Year, today.Month, 1);
var endOfMonth = startOfMonth.AddMonths(1).AddDays(-1);
return new DateRange(startOfMonth, endOfMonth);
}
///
/// 创建本年的日期范围
///
public static DateRange ThisYear()
{
var today = DateTime.Today;
var startOfYear = new DateTime(today.Year, 1, 1);
var endOfYear = new DateTime(today.Year, 12, 31);
return new DateRange(startOfYear, endOfYear);
}
///
/// 创建指定月份的日期范围
///
public static DateRange ForMonth(int year, int month)
{
var startOfMonth = new DateTime(year, month, 1);
var endOfMonth = startOfMonth.AddMonths(1).AddDays(-1);
return new DateRange(startOfMonth, endOfMonth);
}
///
/// 创建指定年份的日期范围
///
public static DateRange ForYear(int year)
{
var startOfYear = new DateTime(year, 1, 1);
var endOfYear = new DateTime(year, 12, 31);
return new DateRange(startOfYear, endOfYear);
}
#endregion
#region 重写方法
///
/// 重写ToString方法
///
public override string ToString()
{
if (IsSingleDay)
{
return StartDate.ToString("yyyy-MM-dd");
}
return $"{StartDate:yyyy-MM-dd} ~ {EndDate:yyyy-MM-dd} ({TotalDays}天)";
}
///
/// 重写Equals方法
///
public override bool Equals(object obj)
{
if (obj is DateRange other)
{
return StartDate == other.StartDate && EndDate == other.EndDate;
}
return false;
}
///
/// 重写GetHashCode方法
///
public override int GetHashCode()
{
return HashCode.Combine(StartDate, EndDate);
}
#endregion
}
#region 支持类定义
///
/// 工作时间配置
///
public class WorkingTimeConfiguration
{
///
/// 标准每日工作时间
///
public decimal StandardDailyHours { get; set; } = 8.0m;
///
/// 特殊日期工作时间配置
/// Key: 日期, Value: 工作时间
///
public Dictionary SpecialDayHours { get; set; } = new();
///
/// 获取指定日期的工作时间
///
public decimal GetDailyWorkingHours(DateTime date)
{
return SpecialDayHours.GetValueOrDefault(date.Date, StandardDailyHours);
}
}
///
/// 节假日提供者
///
public static class HolidayProvider
{
///
/// 节假日缓存
///
private static readonly HashSet _holidays = new();
///
/// 判断是否为节假日
///
public static bool IsHoliday(DateTime date)
{
// 这里可以集成具体的节假日数据源
// 例如:国家法定节假日API、企业自定义节假日等
return _holidays.Contains(date.Date);
}
///
/// 添加节假日
///
public static void AddHoliday(DateTime date)
{
_holidays.Add(date.Date);
}
///
/// 批量添加节假日
///
public static void AddHolidays(IEnumerable dates)
{
foreach (var date in dates)
{
_holidays.Add(date.Date);
}
}
///
/// 移除节假日
///
public static void RemoveHoliday(DateTime date)
{
_holidays.Remove(date.Date);
}
///
/// 清空所有节假日
///
public static void ClearHolidays()
{
_holidays.Clear();
}
}
#endregion
}