public void Scheduling() { #region Scheduling var nz = TimeSpan.FromHours(12); var availability = new TimeRange[] { new TimeRange(new DateTimeOffset(2013, 8, 13, 9, 0, 0, nz), TimeSpan.FromHours(3)), // 0900 - 1200 new TimeRange(new DateTimeOffset(2013, 8, 13, 14, 0, 0, nz), TimeSpan.FromHours(3)), // 1400 - 1700 }; var busy = new TimeRange[] { new TimeRange(new DateTimeOffset(2013, 8, 13, 9, 0, 0, nz), TimeSpan.FromMinutes(30)), // 0900 - 0930 new TimeRange(new DateTimeOffset(2013, 8, 13, 9, 0, 0, nz), TimeSpan.FromHours(1)), // 0900 - 1000 new TimeRange(new DateTimeOffset(2013, 8, 13, 11, 0, 0, nz), TimeSpan.FromHours(1)), // 1100 - 1200 new TimeRange(new DateTimeOffset(2013, 8, 13, 11, 0, 0, nz), TimeSpan.FromHours(2)), // 1100 - 1300 new TimeRange(new DateTimeOffset(2013, 8, 13, 16, 0, 0, nz), TimeSpan.FromMinutes(30)), // 1600 - 1630 }; // Get the free times and then find a 2 hour slot. var when = availability .SelectMany(a => a.Subtract(busy)) .SelectMany(a => a.Divide(TimeSpan.FromHours(2))) .First(); Console.WriteLine(when); // Produces: 13 Aug 2013 2:00:00 p.m. +12:00 for 02:00:00 #endregion }
public void DividingEndOfTime() { var now = DateTimeOffset.MaxValue - TimeSpan.FromHours(1); var availableTime = new TimeRange(now, now.AddHours(1)); var freeSlots = availableTime .Divide(TimeSpan.FromMinutes(15)) .ToArray(); Assert.AreEqual(4, freeSlots.Length); Assert.IsTrue(freeSlots.All(s => s.Duration == TimeSpan.FromMinutes(15))); }
public void ContainmentEndofTime() { var now = DateTimeOffset.Now; var eot = DateTimeOffset.MaxValue; var start = now - TimeSpan.FromSeconds(10); var tick = TimeSpan.FromTicks(1); var range = new TimeRange(start); Assert.IsFalse(range.Contains(start - tick)); Assert.IsTrue(range.Contains(start)); Assert.IsTrue(range.Contains(start + tick)); Assert.IsTrue(range.Contains(eot - tick)); Assert.IsTrue(range.Contains(eot)); }
public void Containment() { var now = DateTimeOffset.Now; var start = now - TimeSpan.FromSeconds(10); var end = now + TimeSpan.FromSeconds(10); var tick = TimeSpan.FromTicks(1); var range = new TimeRange(start, end); Assert.IsFalse(range.Contains(start - tick)); Assert.IsTrue(range.Contains(start)); Assert.IsTrue(range.Contains(start + tick)); Assert.IsTrue(range.Contains(end - tick)); Assert.IsFalse(range.Contains(end)); Assert.IsFalse(range.Contains(end + tick)); }
void Snippets() { #region Dividing var now = new DateTimeOffset(2013, 8, 13, 13, 0, 0, TimeSpan.FromHours(12)); var availableTime = new TimeRange(now, now.AddHours(1)); var freeSlots = availableTime.Divide(TimeSpan.FromMinutes(15)); foreach (var freeSlot in freeSlots) { Console.WriteLine(freeSlot); } // Produces 4 15 minute time ranges. // // 13 Aug 2013 1:00:00 p.m. +12:00 for 00:15:00 // 13 Aug 2013 1:15:00 p.m. +12:00 for 00:15:00 // 13 Aug 2013 1:30:00 p.m. +12:00 for 00:15:00 // 13 Aug 2013 1:45:00 p.m. +12:00 for 00:15:00 #endregion }
void Snippets2() { #region SubtractCollection var nz = TimeSpan.FromHours(12); var availability = new TimeRange(new DateTimeOffset(2013, 8, 13, 9, 0, 0, nz), TimeSpan.FromHours(3)); // 0900 - 1200 var busy = new TimeRange[] { new TimeRange(new DateTimeOffset(2013, 8, 13, 9, 0, 0, nz), TimeSpan.FromMinutes(30)), // 0900 - 0930 new TimeRange(new DateTimeOffset(2013, 8, 13, 9, 0, 0, nz), TimeSpan.FromHours(1)), // 0900 - 1000 new TimeRange(new DateTimeOffset(2013, 8, 13, 11, 15, 0, nz), TimeSpan.FromMinutes(15)), // 1115 - 1130 }; var free = availability.Subtract(busy); foreach (var t in free) Console.WriteLine(t); // Produces the following free time // 13 Aug 2013 10:00:00 a.m. +12:00 for 01:15:00 // 13 Aug 2013 11:30:00 a.m. +12:00 for 00:30:00 #endregion }
// TODO: maybe rename to Remove /// <summary> /// Generates an ordered sequence of time ranges that excludes the specified <see cref="TimeRange"/> /// from this <see cref="TimeRange"/>. /// </summary> /// <param name="other"> /// The <see cref="TimeRange"/> to exclude. /// </param> /// <returns> /// An ordered sequence of time range. The sequence can contain 0, 1 or 2 time ranges. /// </returns> /// <remarks> /// This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required /// to perform the action. The operation represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method /// directly or by using <c>foreach</c>. /// </remarks> /// <example> /// The following example determines the free time for a resource.. /// /// <code title="Subtracting a Time Range" source="SepiaExamples\TimeRangeExample.cs" region="SubtractCollection" language="C#" /> /// </example> public IEnumerable<TimeRange> Subtract(TimeRange other) { if (other.StartsOn <= this.StartsOn && other.EndsOn >= this.EndsOn) yield break; if (other.EndsOn <= this.StartsOn || this.EndsOn <= other.StartsOn) { yield return this; yield break; } if (other.StartsOn <= this.StartsOn && other.EndsOn < this.EndsOn) { yield return new TimeRange(other.EndsOn, this.EndsOn); yield break; } if (other.StartsOn < this.EndsOn && this.StartsOn != other.StartsOn) yield return new TimeRange(this.StartsOn, other.StartsOn); if (other.endsOn < this.EndsOn && other.EndsOn != this.EndsOn) yield return new TimeRange(other.EndsOn, this.EndsOn); }
/// <summary> /// Determines if the specified <see cref="TimeRange"/> overlaps this time range. /// </summary> /// <param name="that">The time to check for intersection.</param> /// <returns> /// <b>true</b>, if <paramref name="that"/> intersects this time range; otherwise, <b>false</b>. /// </returns> /// <seealso href="http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap"/> public bool Intersects(TimeRange that) { return (this.StartsOn < that.EndsOn) && (that.StartsOn < this.EndsOn); }
// TODO: maybe rename to split /// <summary> /// Generates an ordered sequence of time ranges starting at this <see cref="StartsOn"/> /// incrementing by the specified <see cref="TimeSpan"/> until the <see cref="EndsOn"/> is reached. /// </summary> /// <param name="step"> /// The <see cref="TimeSpan"/> to increment by. /// </param> /// <returns> /// An ordered sequence of time range. /// </returns> /// <exception cref="ArgumentException"> /// <paramref name="step"/> is less than or equal <see cref="TimeSpan.Zero"/>. /// </exception> /// <remarks> /// This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required /// to perform the action. The operation represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method /// directly or by using <c>foreach</c>. /// <para> /// The sequence can, in theory, be infinite because each item is generated as needed. The length of the sequence can be /// controlled by using a <see cref="System.Linq"/> limiter; such as <see cref="System.Linq.Enumerable.Take{T}"/>. /// </para> /// <para> /// The sequence is terminated on Overflow. /// </para> /// <para> /// When <paramref name="step"/> does not divide evenly into the <see cref="Duration"/>, then the "last" time range is not returned. /// In other words, only time ranges with a <see cref="Duration"/> equal to <paramref name="step"/> is returned. /// </para> /// </remarks> /// <example> /// The following example divides a <see cref="TimeRange"/> into 15 minutes slots. /// /// <code title="Dividing a Time Range" source="SepiaExamples\TimeRangeExample.cs" region="Dividing" language="C#" /> /// </example> public IEnumerable<TimeRange> Divide(TimeSpan step) { Guard.Require(step > TimeSpan.Zero, "step", "Cannot step by a zero or negative number."); var next = startsOn; while (next < endsOn) { var value = new TimeRange(next, step); if (value.Duration != step || value.endsOn > endsOn) break; yield return value; // Overflow ends the sequence. checked { try { next += step; } catch (OverflowException) { break; } } } }
public void DividingNone() { var now = new DateTimeOffset(2013, 8, 13, 13, 0, 0, TimeSpan.FromHours(12)); var availableTime = new TimeRange(now, now.AddMinutes(14)); var freeSlots = availableTime .Divide(TimeSpan.FromMinutes(15)) .ToArray(); Assert.AreEqual(0, freeSlots.Length); }
public void SubtractingAgain() { var nz = TimeSpan.FromHours(12); var duration = TimeSpan.FromMinutes(1); var a = new DateTimeOffset(2013, 8, 13, 9, 0, 0, nz); var b = a + duration; var c = b + duration; var d = c + duration; var e = d + duration; var f = e + duration; var g = f + duration; var h = g + duration; var availability = new TimeRange(c, f); var free = availability.Subtract(new TimeRange(a, b)).ToArray(); Assert.AreEqual(1, free.Length); Assert.AreEqual(availability, free[0]); free = availability.Subtract(new TimeRange(b, c)).ToArray(); Assert.AreEqual(1, free.Length); Assert.AreEqual(availability, free[0]); free = availability.Subtract(new TimeRange(f, g)).ToArray(); Assert.AreEqual(1, free.Length); Assert.AreEqual(availability, free[0]); free = availability.Subtract(new TimeRange(g, h)).ToArray(); Assert.AreEqual(1, free.Length); Assert.AreEqual(availability, free[0]); free = availability.Subtract(new TimeRange(b, g)).ToArray(); Assert.AreEqual(0, free.Length); free = availability.Subtract(new TimeRange(d, e)).ToArray(); Assert.AreEqual(2, free.Length); Assert.AreEqual(new TimeRange(c, d), free[0]); Assert.AreEqual(new TimeRange(e, f), free[1]); free = availability.Subtract(new TimeRange(b, d)).ToArray(); // fails Assert.AreEqual(1, free.Length); Assert.AreEqual(new TimeRange(d, f), free[0]); free = availability.Subtract(new TimeRange(e, g)).ToArray(); Assert.AreEqual(1, free.Length); Assert.AreEqual(new TimeRange(c, e), free[0]); free = availability.Subtract(availability).ToArray(); Assert.AreEqual(0, free.Length); free = availability.Subtract(new TimeRange(c, d)).ToArray(); Assert.AreEqual(1, free.Length); Assert.AreEqual(new TimeRange(d, f), free[0]); free = availability.Subtract(new TimeRange(e, f)).ToArray(); Assert.AreEqual(1, free.Length); Assert.AreEqual(new TimeRange(c, e), free[0]); }
public void Subtracting() { var now = new DateTimeOffset(2013, 8, 13, 13, 0, 0, TimeSpan.FromHours(12)); var tick = new TimeSpan(1); var time = new TimeRange(now, now.AddHours(1)); var before = new TimeRange(now - TimeSpan.FromHours(1), time.StartsOn); var after = new TimeRange(time.EndsOn, TimeSpan.FromHours(1)); var mid = new TimeRange(now.AddMinutes(15), TimeSpan.FromMinutes(30)); // No intersection Assert.AreEqual(1, time.Subtract(before).Count()); Assert.AreEqual(time, time.Subtract(before).First()); Assert.AreEqual(1, time.Subtract(after).Count()); Assert.AreEqual(time, time.Subtract(after).First()); // Identity Assert.AreEqual(0, time.Subtract(time).Count()); // Subsumes Assert.AreEqual(0, time.Subtract(new TimeRange(before.StartsOn, after.EndsOn)).Count()); // Middle Assert.AreEqual(2, time.Subtract(mid).Count()); Assert.IsTrue(time.Subtract(mid).Any(t => t.StartsOn == now && t.Duration == TimeSpan.FromMinutes(15))); Assert.IsTrue(time.Subtract(mid).Any(t => t.StartsOn == now.AddMinutes(45) && t.Duration == TimeSpan.FromMinutes(15))); }
public void ParsingIso8061() { var nz = TimeSpan.FromHours(12); var utc = TimeSpan.Zero; var expectUtc = new TimeRange(new DateTimeOffset(2013, 8, 13, 9, 0, 0, utc), TimeSpan.FromMinutes(30)); var expectNz = new TimeRange(new DateTimeOffset(2013, 8, 13, 9, 0, 0, nz), TimeSpan.FromMinutes(30)); Assert.AreEqual(expectUtc, TimeRange.ParseIso8061("20130813T090000Z/20130813T093000Z")); Assert.AreEqual(expectNz, TimeRange.ParseIso8061("20130813T090000+1200/20130813T093000+1200")); Assert.AreEqual(expectUtc, TimeRange.ParseIso8061("20130813T090000Z/PT30M")); Assert.AreEqual(expectNz, TimeRange.ParseIso8061("20130813T090000+1200/PT30M")); Assert.AreEqual(expectUtc, TimeRange.ParseIso8061("2013-08-13T09:00:00Z/2013-08-13T09:30:00Z")); Assert.AreEqual(expectNz, TimeRange.ParseIso8061("2013-08-13T09:00:00+1200/2013-08-13T09:30:00+1200")); Assert.AreEqual(expectUtc, TimeRange.ParseIso8061("2013-08-13T09:00:00Z/PT30M")); Assert.AreEqual(expectNz, TimeRange.ParseIso8061("2013-08-13T09:00:00+1200/PT30M")); }
public void DividingUneven() { var now = new DateTimeOffset(2013, 8, 13, 13, 0, 0, TimeSpan.FromHours(12)); var availableTime = new TimeRange(now, now.AddMinutes(59)); var freeSlots = availableTime .Divide(TimeSpan.FromMinutes(15)) .ToArray(); Assert.AreEqual(3, freeSlots.Length); Assert.IsTrue(freeSlots.All(s => s.Duration == TimeSpan.FromMinutes(15))); }