【问题标题】:c# check if a timespan range is between timespan range and how many hoursc# 检查时间跨度范围是否在时间跨度范围和多少小时之间
【发布时间】:2016-12-01 23:43:25
【问题描述】:

假设我有 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  

【问题讨论】:

  • .TotalHoursTimeSpan 上可用。但是,对于轮班,我建议使用DateTime 而不是TimeSpan。或者,DateTime StartTimeTimeSpan Duration 的组合

标签: c# datetime timespan date-range


【解决方案1】:

这是一个包含测试的解决方案:

计算

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));
}

【讨论】:

  • 好答案。最近不得不做类似的事情。看看我是如何解决的。 stackoverflow.com/a/40923461/5233410
  • 如果我的班次从 22:00 开始,到第二天 07:30 结束,则结果将全部为 0。我该如何解决?
  • @active92 修复了 GetTimeSpanIntersect 并更新了 Test 以使用 TestCases 并包含您的测试用例。
【解决方案2】:

您使用了错误的类型。 StartEnd 应该是 DateTime,而不是 TimeSpanEnd.Subtract(Start) 将提供 TimeSpan 作为其结果。 TimeSpan 类型的属性将提供总小时数、分钟数等。

TimeSpan Properties

【讨论】:

    【解决方案3】:

    我建议向您的类添加一个方法,该方法表示开始和结束之间的实际差异。例如,将其称为时间跨度 TimeDiff。您需要包含一个 if 语句来确认 TimeLater 小于 TimeEnd 并且 TimeEarlier 大于 TimeStart。然后是 'TimeDiff = TimeLater - TimeEarlier' 。

    TimeLater 是所提供范围的结束。 TimeEarlier 是所提供范围的开始。

    如果您想计算从 TimeEnd 到 TimeStart 的时间跨度,您只需执行检查 TimeEarlier 是否大于 TimeLater 并具有计算差异的逻辑。这将是 TimeDiff = (TimeEnd - TimeEarlier) + (TimeLater - TimeEnd)

    要完成时间跨度减法,请在所有时间跨度中使用 .Subtract()

    【讨论】:

      【解决方案4】:

      对最近的一个项目有类似的要求。这是我解决相同问题的经验。

      根据需求,派生了以下类。

      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&lt;T&gt;"/> 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);
          }
      }
      

      【讨论】:

        【解决方案5】:

        我想我找到了解决办法:

        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;
        }
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-22
        • 1970-01-01
        • 1970-01-01
        • 2013-09-26
        相关资源
        最近更新 更多