先看一下运行的结果(支持同天,跨天,跨任意多天等情况,同时还可自定义休息时间段):
2022-7-13 11:00到2022-7-15 19:00的工时计算结果:
2022-7-13 11:00到2022-7-17 19:00的工时计算结果:
以下是C#程序代码实现的逻辑(你可以任意调整BreakTimeRange
中的休息时间段):
namespace ConsoleApp3
{
internal class Program
{
private static void Main(string[] args)
{
var start = new WorkingDate(new MyDate(2022, 7, 13, 11, 0));
var end = new WorkingDate(new MyDate(2022, 7, 17, 19, 0));
var calc = new WorkingHoursCalculator(start, end);
calc.CalculateWorkingHours();
Console.WriteLine($"{calc.Start.DateAsString}到{calc.End.DateAsString}的工时计算结果如下:");
Console.WriteLine($"总工时:{calc.TotalWorkingMinutes}分钟(包含休息时间)");
Console.WriteLine($"实际工时:{calc.ActualWorkingMinutes}分钟(不包含休息时间)");
Console.WriteLine($"休息时长:{calc.TotalBreakMinutes}分钟");
Console.WriteLine("=======以下是休息明细=======");
Console.WriteLine();
foreach (var item in calc.WorkingTimeItems)
{
Console.WriteLine($"从{item.StartAsString}到{item.EndAsString}(工时:{item.ActualWorkingMinutes}分钟,休息{item.TotalBreakMinutes}分钟)");
foreach (var breakTimeItem in item.BreakTimeItems)
{
Console.WriteLine($"{breakTimeItem.BreakTime.Name}时间段休息:{breakTimeItem.BreakMinutes}分钟");
}
Console.WriteLine();
}
Console.ReadKey();
}
}
public record MyDate(int Year, int Month, int Day, int Hour, int Minute);
public record MyTime(int Hour, int Minute)
{
/// <summary>
/// 转换得到的总的分钟数
/// </summary>
public int Minutes => Hour * 60 + Minute;
}
public record BreakTime(string Name, MyTime Start, MyTime End)
{
public int BreakMinutes => End.Minutes - Start.Minutes;
public int BreakStart => Start.Minutes;
public int BreakEnd => End.Minutes;
}
public class BreakTimeRange
{
public static List<BreakTime> BreakTimes => new()
{
new BreakTime("00:00-08:30",new MyTime(0, 0), new MyTime(8, 30)),
new BreakTime("12:00-13:00",new MyTime(12, 0), new MyTime(13, 0)),
new BreakTime("17:00-18:00",new MyTime(17, 30), new MyTime(18, 30)),
new BreakTime("20:30-24:00",new MyTime(20, 30), new MyTime(24, 0))
};
}
public class WorkingDate
{
public WorkingDate(MyDate date)
{
_myDate = date;
Year = _myDate.Year;
Month = _myDate.Month;
Day = _myDate.Day;
Hour = _myDate.Hour;
Minute = _myDate.Minute;
}
private readonly MyDate _myDate;
public int Year { get; }
public int Month { get; }
public int Day { get; }
public int Hour { get; }
public int Minute { get; }
public DateTime Date
{
get
{
var hour = _myDate.Hour;
var day = _myDate.Day;
if (_myDate.Hour == 24)
{
hour = 0;
day += 1;
}
return new DateTime(_myDate.Year, _myDate.Month, day, hour, _myDate.Minute, 0);
}
}
public int TotalMinutes => _myDate.Hour * 60 + _myDate.Minute;
public string DateAsString => $"{_myDate.Year}-{_myDate.Month}-{_myDate.Day} {_myDate.Hour:00}:{_myDate.Minute:00}";
}
public static class WorkingDateExtension
{
public static WorkingDate ConvertToEndOfDay(this WorkingDate workingDate)
{
return new WorkingDate(new MyDate(workingDate.Year, workingDate.Month, workingDate.Day, 24,
0));
}
public static WorkingDate ConvertToStartOfDay(this WorkingDate workingDate)
{
return new WorkingDate(new MyDate(workingDate.Year, workingDate.Month, workingDate.Day, 0,
0));
}
public static WorkingDate AddDay(this WorkingDate workingDate)
{
return new WorkingDate(new MyDate(workingDate.Year, workingDate.Month, workingDate.Day + 1, workingDate.Hour,
workingDate.Minute));
}
}
public class BreakTimeItem
{
public BreakTime BreakTime { get; set; }
public int BreakMinutes { get; set; }
}
public class WorkingTimeItem
{
public WorkingTimeItem(WorkingDate start, WorkingDate end)
{
Start = start;
End = end;
BreakTimeItems = new List<BreakTimeItem>();
}
public WorkingDate Start { get; set; }
public WorkingDate End { get; set; }
public string DayAsString => Start.Date.ToString("yyyy-MM-dd");
public string StartAsString => Start.Date.ToString("yyyy-MM-dd HH:mm");
public string EndAsString => End.Date.ToString("yyyy-MM-dd HH:mm");
/// <summary>
/// 当天工时(分钟),不包含休息时间
/// </summary>
public int ActualWorkingMinutes
{
get
{
return End.TotalMinutes - Start.TotalMinutes - BreakTimeItems.Sum(x => x.BreakMinutes);
}
}
/// <summary>
/// 当天工时(分钟),包含休息时间
/// </summary>
public int TotalWorkingMinutes => End.TotalMinutes - Start.TotalMinutes;
/// <summary>
/// 当天总的休息时长
/// </summary>
public int TotalBreakMinutes => BreakTimeItems.Sum(x => x.BreakMinutes);
/// <summary>
/// 休息明细
/// </summary>
public List<BreakTimeItem> BreakTimeItems { get; private set; }
/// <summary>
/// 添加休息时间段
/// </summary>
/// <param name="item"></param>
public void AddBreakTimeItem(BreakTimeItem item)
{
BreakTimeItems.Add(item);
}
}
/// <summary>
/// 工时计算器
/// </summary>
public class WorkingHoursCalculator
{
/// <summary>
///
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
public WorkingHoursCalculator(WorkingDate start, WorkingDate end)
{
Start = start;
End = end;
}
/// <summary>
/// 开始的天
/// </summary>
public int DayOfStart { get; private set; }
/// <summary>
/// 结束的天
/// </summary>
public int DayOfEnd { get; private set; }
/// <summary>
/// 跨天数
/// </summary>
public int CrossDays { get; private set; }
/// <summary>
/// 两个时期间是否跨天
/// </summary>
public bool IsCrossDay => DayOfEnd != DayOfStart;
/// <summary>
/// 所有天的总工时(包含休息时间)
/// </summary>
public double TotalWorkingMinutes => WorkingTimeItems.Sum(x => x.TotalWorkingMinutes);
/// <summary>
/// 所有天的总实际工时(不包含工作时间)
/// </summary>
public double ActualWorkingMinutes => WorkingTimeItems.Sum(x => x.ActualWorkingMinutes);
/// <summary>
/// 所有天的总休息时长
/// </summary>
public double TotalBreakMinutes => WorkingTimeItems.Sum(x => x.TotalBreakMinutes);
public List<WorkingTimeItem> WorkingTimeItems { get; set; }
public WorkingDate Start { get; set; }
public WorkingDate End { get; set; }
public void CalculateWorkingHours()
{
if (Start.Date > End.Date) throw new ArgumentException("开始时间和结束时间输入错误");
DayOfStart = Start.Date.Day;
DayOfEnd = End.Date.Day;
CrossDays = (End.Date - Start.Date).Days;
WorkingTimeItems = new List<WorkingTimeItem>();
if (!IsCrossDay)
{
FindWorkingMinutes(Start, End);
}
else
{
// 跨天时
// 第一天,开始时间到24点
FindWorkingMinutes(Start, Start.ConvertToEndOfDay());
var days = CrossDays;
var current = Start;
while (days > 0)
{
if (days == 1)
{
FindWorkingMinutes(End.ConvertToStartOfDay(), End);
break;
}
current = current.AddDay();
FindWorkingMinutes(current.ConvertToStartOfDay(), current.ConvertToEndOfDay());
days--;
}
}
}
public void FindWorkingMinutes(WorkingDate start, WorkingDate end)
{
var wti = new WorkingTimeItem(start, end);
var aStart = start.TotalMinutes;
var aEnd = end.TotalMinutes;
foreach (var breakTime in BreakTimeRange.BreakTimes.Where(breakTime => aStart <= breakTime.BreakEnd && aEnd >= breakTime.BreakStart))
{
if (aStart <= breakTime.BreakStart && aEnd >= breakTime.BreakEnd)
{
wti.AddBreakTimeItem(new BreakTimeItem
{
BreakTime = breakTime,
BreakMinutes = breakTime.BreakMinutes
});
continue;
}
if (aStart <= breakTime.BreakStart && aEnd <= breakTime.BreakEnd)
{
wti.AddBreakTimeItem(new BreakTimeItem
{
BreakTime = breakTime,
BreakMinutes = aEnd - breakTime.BreakStart
});
continue;
}
if (aStart >= breakTime.BreakStart && aEnd >= breakTime.BreakEnd)
{
wti.AddBreakTimeItem(new BreakTimeItem
{
BreakTime = breakTime,
BreakMinutes = breakTime.BreakEnd - aStart
});
continue;
}
if (aStart >= breakTime.BreakStart && aEnd <= breakTime.BreakEnd)
{
wti.AddBreakTimeItem(new BreakTimeItem
{
BreakTime = breakTime,
BreakMinutes = aEnd - aStart
});
}
}
WorkingTimeItems.Add(wti);
}
}
}