假设我有3个时间范围:
07:00 - 18:00
18:00 - 23:00
23:00 - 07:00
和代码:
public class TimeShift
{
public TimeSpan Start { get; set; }
public TimeSpan End { get; set; }
}
List<TimeShift> shifts = new List<TimeShift>();
如何检查列表中的每个项目是否在上述3个范围和多少小时之间?
例如一个TimeShift
,其中:
Start: 07:00
End: 23:30
那意味着16.5小时。
对于上面的例子:
Range 1: 11 hours
Range 2: 5 hours
Range 3: 0.5 hours
答案 0 :(得分:2)
这是一个包含测试的解决方案:
public class TimeSpacCalculator
{
public static TimeSpan GetTimeSpanIntersect(TimeShift input, TimeSpan start, TimeSpan end)
{
// Loopsback input from 23:59 - 00:00
if (input.Start > input.End)
return GetTimeSpanIntersect(new TimeShift(input.Start, TimeSpan.FromHours(24)), start, end) +
GetTimeSpanIntersect(new TimeShift(TimeSpan.FromHours(0), input.End), start, end);
// Loopsback Shift from 23:59 - 00:00
if (start > end)
return GetTimeSpanIntersect(input, new TimeSpan(), end) +
GetTimeSpanIntersect(input, start, TimeSpan.FromHours(24));
if (input.End < start)
return new TimeSpan();
if (input.Start > end)
return new TimeSpan();
var actualStart = input.Start < start
? start
: input.Start;
var actualEnd = input.End > end
? end
: input.End;
return actualEnd - actualStart;
}
}
public class TimeRange : TimeShift
{
public TimeRange(string name, TimeSpan start, TimeSpan end) : base(start, end)
{
Name = name;
}
public string Name { get; set; }
}
public class TimeShift
{
public TimeShift(TimeSpan start, TimeSpan end)
{
Start = start;
End = end;
}
public TimeSpan Start { get; set; }
public TimeSpan End { get; set; }
}
[TestFixture]
internal class TimShiftTests
{
[Test]
[TestCase(7, 23.5, 11, 5, 0.5)]
[TestCase(22, 7.5, 0.5, 1, 8)]
public void Test(double inputStartHours, double inputEndHours, double expectedRange1Hours, double expectedRange2Hours, double expectedRange3Hours )
{
var input = new TimeShift(TimeSpan.FromHours(inputStartHours), TimeSpan.FromHours(inputEndHours));
var ranges = new List<TimeRange>
{
new TimeRange("Range1", TimeSpan.FromHours(7), TimeSpan.FromHours(18)),
new TimeRange("Range2", TimeSpan.FromHours(18), TimeSpan.FromHours(23)),
new TimeRange("Range3", TimeSpan.FromHours(23), TimeSpan.FromHours(7))
};
var result = new Dictionary<string, TimeSpan>();
foreach (var range in ranges)
{
var time = TimeSpacCalculator.GetTimeSpanIntersect(input, range.Start, range.End);
result.Add(range.Name, time);
Console.WriteLine($"{range.Name}: " + time.TotalHours);
}
result["Range1"].Should().Be(TimeSpan.FromHours(expectedRange1Hours));
result["Range2"].Should().Be(TimeSpan.FromHours(expectedRange2Hours));
result["Range3"].Should().Be(TimeSpan.FromHours(expectedRange3Hours));
}
答案 1 :(得分:0)
你使用的是错误的类型。 Start
和End
应为DateTime
,而非TimeSpan
。 End.Subtract(Start)
将提供TimeSpan
作为结果。 TimeSpan
类型具有提供小时,分钟等总数的属性
答案 2 :(得分:0)
我建议在您的类中添加一个方法,该方法表示开始和结束之间的实际差异。例如,称之为Timepan TimeDiff。您需要包含if语句以确认TimeLater小于TimeEnd且TimeEarlier大于TimeStart。那么&#39; TimeDiff = TimeLater - TimeEarlier&#39; 。
TimeLater是所提供范围的结束。 TimeEarlier是提供范围的开始。
如果要计算经过TimeEnd到TimeStart的时间跨度,您只需要执行检查TimeEarlier是否大于TimeLater并且具有计算差异的逻辑。这将是TimeDiff =(TimeEnd - TimeEarlier)+(TimeLater - TimeEnd)
要在所有时间跨度中使用.Subtract()完成时间跨度减法
答案 3 :(得分:0)
我想我找到了解决方案:
private double GetHours(TimeSpan start, TimeSpan end, TimeSpan startTarget, TimeSpan endTarget)
{
double result = 0;
if (startTarget >= start && endTarget <= end)
{
result = (endTarget - startTarget).TotalHours;
}
if ((startTarget >= start && startTarget < end) && endTarget > end)
{
result = (end - startTarget).TotalHours;
}
if (startTarget < start && (endTarget > start && endTarget <= end))
{
result = (endTarget - start).TotalHours;
}
if (startTarget < start && endTarget > end)
{
result = (end - start).TotalHours;
}
return result;
}
答案 4 :(得分:0)
对最近的项目有类似的要求。以下是我解决同一问题的经验。
根据要求,推导出以下类别。
public interface IRange<T> : IEquatable<T> where T : IComparable {
T Maximum { get; }
T Minimum { get; }
}
public sealed class Range<T> : IRange<T>
where T : IComparable {
public Range(T minimum, T maximum) {
Minimum = minimum;
Maximum = maximum;
}
public T Maximum { get; private set; }
public T Minimum { get; private set; }
public override string ToString() {
return string.Format("{{{0} - {1}}}", Minimum, Maximum);
}
public override int GetHashCode() {
return ToString().GetHashCode();
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) {
return false;
}
return obj is Range<T> && Equals((Range<T>)obj);
}
public bool Equals(T other) {
return object.Equals(this.ToString(), other.ToString());
}
}
支持以下扩展方法
public static class Range {
/// <summary>
/// Create an <seealso cref="IRange<T>"/> using the provided minimum and maximum value
/// </summary>
public static IRange<T> Of<T>(T min, T max) where T : IComparable {
return new Range<T>(min, max);
}
/// <summary>
///
/// </summary>
public static bool Contains<T>(this IRange<T> range, T value) where T : IComparable {
return range.Minimum.CompareTo(value) <= 0 && value.CompareTo(range.Maximum) <= 0;
}
/// <summary>
///
/// </summary>
public static bool IsOverlapped<T>(this IRange<T> range, IRange<T> other, bool inclusive = false) where T : IComparable {
return inclusive
? range.Minimum.CompareTo(other.Maximum) <= 0 && other.Minimum.CompareTo(range.Maximum) <= 0
: range.Minimum.CompareTo(other.Maximum) < 0 && other.Minimum.CompareTo(range.Maximum) < 0;
}
/// <summary>
///
/// </summary>
public static IRange<T> GetIntersection<T>(this IRange<T> range, IRange<T> other, bool inclusive = false) where T : IComparable {
var start = new[] { range.Minimum, other.Minimum }.Max();
var end = new[] { range.Maximum, other.Maximum }.Min();
var valid = inclusive ? start.CompareTo(end) < 0 : start.CompareTo(end) <= 0;
return valid ? new Range<T>(start, end) : null;
}
}
这是一个适合您特定要求的测试
[TestClass]
public class TimeShiftTests : MiscUnitTests {
[TestMethod]
public void TimeShiftDurationTest() {
var shifts = new List<string>(){
"07:00 - 18:00",
"18:00 - 23:00",
"23:00 - 07:00"
}.Select(s => ParseShift(s));
var timeShift = "07:00 - 23:30";
var totalExpectedHours = 16.5;
var input = ParseShift(timeShift);
var intersections = shifts
.Select(shift => shift.GetIntersection(input))
.ToArray();
intersections.Length.Should().Be(3);
var actualHours = intersections.Select(range => (range.Maximum - range.Minimum).TotalHours).ToArray();
var totalActualHours = actualHours.Sum();
totalActualHours.Should().Be(totalExpectedHours);
actualHours[0].Should().Be(11);
actualHours[1].Should().Be(5);
actualHours[2].Should().Be(0.5);
}
private IRange<DateTime> ParseShift(string period, string format = "HH:mm") {
var tokens = period
.Split(new[] { "to", "-" }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim().Replace(" ", string.Empty))
.ToArray();
if (tokens.Length != 2) throw new FormatException("time period not well formatted");
var startDate = DateTime.ParseExact(tokens[0], format, CultureInfo.InvariantCulture);
var stopDate = DateTime.ParseExact(tokens[1], format, CultureInfo.InvariantCulture);
var beginTime = startDate.TimeOfDay;
var endTime = stopDate.TimeOfDay;
if (endTime < beginTime) {
stopDate = stopDate.AddDays(1);
}
return Range.Of(startDate, stopDate);
}
}