public void CalendarGetGapTest() { // simmulation of some reservations var periods = new TimePeriodCollection { new DayRangeCollection(2011, 3, 7, 2), new DayRangeCollection(2011, 3, 16, 2) }; // the overall search range var limits = new CalendarTimeRange(new DateTime(2011, 3, 4), new DateTime(2011, 3, 21)); var days = new DayRangeCollection(limits.Start, limits.Duration.Days + 1); // limits의 내부이고, 주말인 DayRange를 추가합니다. var excludeDays = days.GetDays().Where(day => limits.HasInside(day) && day.DayOfWeek.IsWeekEnd()); periods.AddAll(excludeDays.Cast <ITimePeriod>()); var gapCalculator = new TimeGapCalculator <TimeRange>(new TimeCalendar()); var gaps = gapCalculator.GetGaps(periods, limits); gaps.Count.Should().Be(4); gaps[0].IsSamePeriod(new TimeRange(new DateTime(2011, 3, 4), DurationUtil.Days(1))).Should().Be.True(); gaps[1].IsSamePeriod(new TimeRange(new DateTime(2011, 3, 9), DurationUtil.Days(3))).Should().Be.True(); gaps[2].IsSamePeriod(new TimeRange(new DateTime(2011, 3, 14), DurationUtil.Days(2))).Should().Be.True(); gaps[3].IsSamePeriod(new TimeRange(new DateTime(2011, 3, 18), DurationUtil.Days(1))).Should().Be.True(); }
public void SortByEndTest() { DateTime now = ClockProxy.Clock.Now; SchoolDay schoolDay = new SchoolDay(now); TimePeriodCollection timePeriods = new TimePeriodCollection(); timePeriods.AddAll(schoolDay); timePeriods.SortByEnd(ListSortDirection.Descending); Assert.Equal(timePeriods[0], schoolDay.Lesson4); Assert.Equal(timePeriods[1], schoolDay.Break3); Assert.Equal(timePeriods[2], schoolDay.Lesson3); Assert.Equal(timePeriods[3], schoolDay.Break2); Assert.Equal(timePeriods[4], schoolDay.Lesson2); Assert.Equal(timePeriods[5], schoolDay.Break1); Assert.Equal(timePeriods[6], schoolDay.Lesson1); timePeriods.SortByEnd(); Assert.Equal(timePeriods[0], schoolDay.Lesson1); Assert.Equal(timePeriods[1], schoolDay.Break1); Assert.Equal(timePeriods[2], schoolDay.Lesson2); Assert.Equal(timePeriods[3], schoolDay.Break2); Assert.Equal(timePeriods[4], schoolDay.Lesson3); Assert.Equal(timePeriods[5], schoolDay.Break3); Assert.Equal(timePeriods[6], schoolDay.Lesson4); } // SortByEndTest
} // CalculateNextStateChange // ---------------------------------------------------------------------- private ITimePeriodCollection GetWeekExcludePeriods(DateTime moment) { ITimePeriodCollection weekExcludePeriods = new TimePeriodCollection(); weekExcludePeriods.AddAll(holidays); Week calendarWeek = new Week(moment); foreach (Day calendarDay in calendarWeek.GetDays()) { ScheduleDay <T> day = week[calendarDay.DayOfWeek]; weekExcludePeriods.AddAll(day.GetExcludePeriods(calendarDay.Start)); } return(new TimePeriodCombiner <T>().CombinePeriods(weekExcludePeriods)); } // GetWeekExcludePeriods
public void GetGapTest() { DateTime now = ClockProxy.Clock.Now; SchoolDay schoolDay = new SchoolDay(now); TimePeriodCollection excludePeriods = new TimePeriodCollection(); TimeGapCalculator <TimeRange> gapCalculator = new TimeGapCalculator <TimeRange>(); excludePeriods.AddAll(schoolDay); Assert.Equal(0, gapCalculator.GetGaps(excludePeriods).Count); Assert.Equal(0, gapCalculator.GetGaps(excludePeriods, schoolDay).Count); excludePeriods.Clear(); excludePeriods.Add(schoolDay.Lesson1); excludePeriods.Add(schoolDay.Lesson2); excludePeriods.Add(schoolDay.Lesson3); excludePeriods.Add(schoolDay.Lesson4); ITimePeriodCollection gaps2 = gapCalculator.GetGaps(excludePeriods); Assert.Equal(3, gaps2.Count); Assert.True(gaps2[0].IsSamePeriod(schoolDay.Break1)); Assert.True(gaps2[1].IsSamePeriod(schoolDay.Break2)); Assert.True(gaps2[2].IsSamePeriod(schoolDay.Break3)); TimeRange testRange3 = new TimeRange(schoolDay.Lesson1.Start, schoolDay.Lesson4.End); ITimePeriodCollection gaps3 = gapCalculator.GetGaps(excludePeriods, testRange3); Assert.Equal(3, gaps3.Count); Assert.True(gaps3[0].IsSamePeriod(schoolDay.Break1)); Assert.True(gaps3[1].IsSamePeriod(schoolDay.Break2)); Assert.True(gaps3[2].IsSamePeriod(schoolDay.Break3)); TimeRange testRange4 = new TimeRange(schoolDay.Start.AddHours(-1), schoolDay.End.AddHours(1)); ITimePeriodCollection gaps4 = gapCalculator.GetGaps(excludePeriods, testRange4); Assert.Equal(5, gaps4.Count); Assert.True(gaps4[0].IsSamePeriod(new TimeRange(testRange4.Start, schoolDay.Start))); Assert.True(gaps4[1].IsSamePeriod(schoolDay.Break1)); Assert.True(gaps4[2].IsSamePeriod(schoolDay.Break2)); Assert.True(gaps4[3].IsSamePeriod(schoolDay.Break3)); Assert.True(gaps4[4].IsSamePeriod(new TimeRange(testRange4.End, testRange3.End))); excludePeriods.Clear(); excludePeriods.Add(schoolDay.Lesson1); ITimePeriodCollection gaps8 = gapCalculator.GetGaps(excludePeriods, schoolDay.Lesson1); Assert.Equal(0, gaps8.Count); TimeRange testRange9 = new TimeRange(schoolDay.Lesson1.Start.Subtract(new TimeSpan(1)), schoolDay.Lesson1.End.Add(new TimeSpan(1))); ITimePeriodCollection gaps9 = gapCalculator.GetGaps(excludePeriods, testRange9); Assert.Equal(2, gaps9.Count); Assert.Equal <TimeSpan>(gaps9[0].Duration, new TimeSpan(1)); Assert.Equal <TimeSpan>(gaps9[1].Duration, new TimeSpan(1)); } // GetGapsTest
public void CountTest() { var timePeriods = new TimePeriodCollection(); timePeriods.Count.Should().Be(0); timePeriods.AddAll(_timeRangeTestData.AllPeriods); timePeriods.Count.Should().Be(_timeRangeTestData.AllPeriods.Count); timePeriods.Clear(); timePeriods.Count.Should().Be(0); }
public void CountTest() { TimePeriodCollection timePeriods = new TimePeriodCollection(); Assert.Equal(0, timePeriods.Count); timePeriods.AddAll(timeRangeTestData.AllPeriods); Assert.Equal <int>(timePeriods.Count, timeRangeTestData.AllPeriods.Count); timePeriods.Clear(); Assert.Equal(0, timePeriods.Count); } // CountTest
public void ItemIndexTest() { var timePeriods = new TimePeriodCollection(); var schoolDay = new SchoolDay(); timePeriods.AddAll(schoolDay); Assert.AreEqual(timePeriods[0], schoolDay.Lesson1); Assert.AreEqual(timePeriods[1], schoolDay.Break1); Assert.AreEqual(timePeriods[2], schoolDay.Lesson2); Assert.AreEqual(timePeriods[3], schoolDay.Break2); Assert.AreEqual(timePeriods[4], schoolDay.Lesson3); Assert.AreEqual(timePeriods[5], schoolDay.Break3); Assert.AreEqual(timePeriods[6], schoolDay.Lesson4); }
public void ClearTest() { TimePeriodCollection timePeriods = new TimePeriodCollection(); Assert.Equal(0, timePeriods.Count); timePeriods.Clear(); Assert.Equal(0, timePeriods.Count); timePeriods.AddAll(new SchoolDay()); Assert.Equal(7, timePeriods.Count); timePeriods.Clear(); Assert.Equal(0, timePeriods.Count); } // ClearTest
public void ClearTest() { var timePeriods = new TimePeriodCollection(); timePeriods.Count.Should().Be(0); timePeriods.Clear(); timePeriods.Count.Should().Be(0); timePeriods.AddAll(new SchoolDay()); timePeriods.Count.Should().Be(7); timePeriods.Clear(); timePeriods.Count.Should().Be(0); }
public void AddAllTest() { DateTime now = ClockProxy.Clock.Now; SchoolDay schoolDay = new SchoolDay(now); TimePeriodCollection timePeriods = new TimePeriodCollection(); Assert.Equal(0, timePeriods.Count); timePeriods.AddAll(schoolDay); Assert.Equal(timePeriods.Count, schoolDay.Count); timePeriods.Clear(); Assert.Equal(0, timePeriods.Count); } // AddAllTest
public void AddAllTest() { var now = ClockProxy.Clock.Now; var schoolDay = new SchoolDay(now); var timePeriods = new TimePeriodCollection(); timePeriods.Count.Should().Be(0); timePeriods.AddAll(schoolDay); timePeriods.Count.Should().Be(schoolDay.Count); timePeriods.Clear(); timePeriods.Count.Should().Be(0); }
public void ItemIndexTest() { TimePeriodCollection timePeriods = new TimePeriodCollection(); SchoolDay schoolDay = new SchoolDay(); timePeriods.AddAll(schoolDay); Assert.Equal(timePeriods[0], schoolDay.Lesson1); Assert.Equal(timePeriods[1], schoolDay.Break1); Assert.Equal(timePeriods[2], schoolDay.Lesson2); Assert.Equal(timePeriods[3], schoolDay.Break2); Assert.Equal(timePeriods[4], schoolDay.Lesson3); Assert.Equal(timePeriods[5], schoolDay.Break3); Assert.Equal(timePeriods[6], schoolDay.Lesson4); } // ItemIndexTest
public void SortByEndTest() { DateTime now = ClockProxy.Clock.Now; SchoolDay schoolDay = new SchoolDay(now); TimePeriodCollection timePeriods = new TimePeriodCollection(); timePeriods.AddAll(schoolDay); timePeriods.SortByEnd(); timePeriods[0].Should().Be(schoolDay.Lesson4); timePeriods[1].Should().Be(schoolDay.Break3); timePeriods[2].Should().Be(schoolDay.Lesson3); timePeriods[3].Should().Be(schoolDay.Break2); timePeriods[4].Should().Be(schoolDay.Lesson2); timePeriods[5].Should().Be(schoolDay.Break1); timePeriods[6].Should().Be(schoolDay.Lesson1); }
public void IndexOfTest() { DateTime now = ClockProxy.Clock.Now; SchoolDay schoolDay = new SchoolDay(now); TimePeriodCollection timePeriods = new TimePeriodCollection(); Assert.AreEqual(timePeriods.IndexOf(new TimeRange()), -1); Assert.AreEqual(timePeriods.IndexOf(new TimeBlock()), -1); timePeriods.AddAll(schoolDay); Assert.AreEqual(timePeriods.IndexOf(schoolDay.Lesson1), 0); Assert.AreEqual(timePeriods.IndexOf(schoolDay.Break1), 1); Assert.AreEqual(timePeriods.IndexOf(schoolDay.Lesson2), 2); Assert.AreEqual(timePeriods.IndexOf(schoolDay.Break2), 3); Assert.AreEqual(timePeriods.IndexOf(schoolDay.Lesson3), 4); Assert.AreEqual(timePeriods.IndexOf(schoolDay.Break3), 5); Assert.AreEqual(timePeriods.IndexOf(schoolDay.Lesson4), 6); timePeriods.Remove(schoolDay.Lesson1); Assert.AreEqual(timePeriods.IndexOf(schoolDay.Lesson1), -1); } // IndexOfTest
// notUseTime holds disabled (grayed out) time slots. italki uses it to ensure that a session must be booked at least in 24 hours in advance. //private ITimePeriodCollection ProcessNotUseTime(string html, DateTime firstDay) //{ // var arrText = FindText(html, "var notUseTime=", ";"); // // I am not sure how many days may come in notUseTime, i.e. whether it is one- or two-dimentional. We force it to be 2D. // if (!arrText.StartsWith("[[") && !arrText.EndsWith("]]")) // { // arrText = "[" + arrText + "]"; // } // return ProcessDayArray(arrText, firstDay); //} private ITimePeriodCollection ProcessDayArray(string arrText, DateTime firstDay) { var periods = new TimePeriodCollection(); var days = JArray.Parse(arrText); foreach (var day in days) { // The first item is the day number. The following items are half-hour times. var dayNumber = (int)day.First(); var slots = day .Skip(1) .Select(i => { var slot = (int)i; var start = firstDay.AddDays(dayNumber).AddMinutes(slot * ItalkiTimeSlotDuration.TotalMinutes); var end = start + ItalkiTimeSlotDuration; return(new TimeRange(start, end)); }); periods.AddAll(slots); } return(CombinePeriods(periods)); }
public void IndexOfTest() { DateTime now = ClockProxy.Clock.Now; SchoolDay schoolDay = new SchoolDay(now); TimePeriodCollection timePeriods = new TimePeriodCollection(); Assert.Equal <int>(timePeriods.IndexOf(new TimeRange()), -1); Assert.Equal <int>(timePeriods.IndexOf(new TimeBlock()), -1); timePeriods.AddAll(schoolDay); Assert.Equal(0, timePeriods.IndexOf(schoolDay.Lesson1)); Assert.Equal(1, timePeriods.IndexOf(schoolDay.Break1)); Assert.Equal(2, timePeriods.IndexOf(schoolDay.Lesson2)); Assert.Equal(3, timePeriods.IndexOf(schoolDay.Break2)); Assert.Equal(4, timePeriods.IndexOf(schoolDay.Lesson3)); Assert.Equal(5, timePeriods.IndexOf(schoolDay.Break3)); Assert.Equal(6, timePeriods.IndexOf(schoolDay.Lesson4)); timePeriods.Remove(schoolDay.Lesson1); Assert.Equal <int>(timePeriods.IndexOf(schoolDay.Lesson1), -1); } // IndexOfTest
/// <summary> /// Get vacant time slots from Italki, shredded as sessions /// </summary> /// <returns></returns> public static async Task <IEnumerable <SessionDto> > GetOfferedSchedules(DateTime startDate, DateTime endDate, bool forceRealTime) { // Load all the HTML pages simultaneously. var tasks = ItalkiTeachers.Select(i => GetTeacherPeriods(i.UserId, forceRealTime)); var teacherPeriodsArray = await Task.WhenAll(tasks); // italki does not allow booking in less than 24 hour. var threshold = GetBookingTimeThreshold(); var allPeriods = teacherPeriodsArray // Flatten all lists .SelectMany(i => i) // Filter .Where(i => (i.Start < endDate) && (i.End > startDate) && // There may be 30-minute "left over" slots available. We filter them out. Our sessions are 60 minutes. (i.Duration >= SessionDuration) && (i.End >= threshold + SessionDuration) ) ; // Merge all periods for display. ITimePeriodCollection periodCollection = new TimePeriodCollection(); var periodCombiner = new TimePeriodCombiner <TeacherTimeRange>(); periodCollection.AddAll(allPeriods); periodCollection = periodCombiner.CombinePeriods(periodCollection); var offeredSessions = periodCollection .Select(i => new SessionDto { Start = i.Start >= threshold ? i.Start : threshold, End = i.End, }); return(offeredSessions); }
public void IndexOfTest() { var now = ClockProxy.Clock.Now; var schoolDay = new SchoolDay(now); var timePeriods = new TimePeriodCollection(); timePeriods.IndexOf(new TimeRange()).Should().Be(-1); timePeriods.IndexOf(new TimeBlock()).Should().Be(-1); timePeriods.AddAll(schoolDay); timePeriods.IndexOf(schoolDay.Lesson1).Should().Be(0); timePeriods.IndexOf(schoolDay.Break1).Should().Be(1); timePeriods.IndexOf(schoolDay.Lesson2).Should().Be(2); timePeriods.IndexOf(schoolDay.Break2).Should().Be(3); timePeriods.IndexOf(schoolDay.Lesson3).Should().Be(4); timePeriods.IndexOf(schoolDay.Break3).Should().Be(5); timePeriods.IndexOf(schoolDay.Lesson4).Should().Be(6); timePeriods.Remove(schoolDay.Lesson1); timePeriods.IndexOf(schoolDay.Lesson1).Should().Be(-1); timePeriods.IndexOf(schoolDay.Break1).Should().Be(0); }
public void GetGapTest() { var now = ClockProxy.Clock.Now; var schoolDay = new SchoolDay(now); var gapCalculator = new TimeGapCalculator <TimeRange>(); var excludePeriods = new TimePeriodCollection(); excludePeriods.AddAll(schoolDay); gapCalculator.GetGaps(excludePeriods).Count.Should().Be(0); gapCalculator.GetGaps(excludePeriods, schoolDay).Count.Should().Be(0); excludePeriods.Clear(); excludePeriods.Add(schoolDay.Lesson1); excludePeriods.Add(schoolDay.Lesson2); excludePeriods.Add(schoolDay.Lesson3); excludePeriods.Add(schoolDay.Lesson4); var gaps2 = gapCalculator.GetGaps(excludePeriods); gaps2.Count.Should().Be(3); gaps2[0].IsSamePeriod(schoolDay.Break1).Should().Be.True(); gaps2[1].IsSamePeriod(schoolDay.Break2).Should().Be.True(); gaps2[2].IsSamePeriod(schoolDay.Break3).Should().Be.True(); var testRange3 = new TimeRange(schoolDay.Lesson1.Start, schoolDay.Lesson4.End); var gaps3 = gapCalculator.GetGaps(excludePeriods, testRange3); gaps3.Count.Should().Be(3); gaps3[0].IsSamePeriod(schoolDay.Break1).Should().Be.True(); gaps3[1].IsSamePeriod(schoolDay.Break2).Should().Be.True(); gaps3[2].IsSamePeriod(schoolDay.Break3).Should().Be.True(); var testRange4 = new TimeRange(schoolDay.Start.AddHours(-1), schoolDay.End.AddHours(1)); var gaps4 = gapCalculator.GetGaps(excludePeriods, testRange4); gaps4.Count.Should().Be(5); gaps4[0].IsSamePeriod(new TimeRange(testRange4.Start, schoolDay.Start)).Should().Be.True(); gaps4[1].IsSamePeriod(schoolDay.Break1).Should().Be.True(); gaps4[2].IsSamePeriod(schoolDay.Break2).Should().Be.True(); gaps4[3].IsSamePeriod(schoolDay.Break3).Should().Be.True(); gaps4[4].IsSamePeriod(new TimeRange(testRange4.End, schoolDay.End)).Should().Be.True(); excludePeriods.Clear(); excludePeriods.Add(schoolDay.Lesson1); var gaps8 = gapCalculator.GetGaps(excludePeriods, schoolDay.Lesson1); gaps8.Count.Should().Be(0); excludePeriods.Clear(); excludePeriods.Add(schoolDay.Lesson1); var testRange9 = new TimeRange(schoolDay.Lesson1.Start.Subtract(new TimeSpan(1)), schoolDay.Lesson1.End.Add(new TimeSpan(1))); var gaps9 = gapCalculator.GetGaps(excludePeriods, testRange9); gaps9.Count.Should().Be(2); gaps9[0].Duration.Should().Be(TimeSpan.FromTicks(1)); gaps9[1].Duration.Should().Be(TimeSpan.FromTicks(1)); }
/// <summary> /// De-tangles a list of overlapping Verbrauch entries where entries can be subsets of other entries. /// </summary> /// <example> /// [---v1---) /// [-----v2------) /// [---------v3---------) /// is transformed into /// [--v1---) /// ........[-v2--) /// ..............[--v3--) /// </example> /// <param name="input"></param> /// <returns></returns> public static List <Verbrauch> Detangle(IEnumerable <Verbrauch> input) { //var filteredInput = KeepShortestSlices(input); HashSet <Verbrauch> resultSet = new HashSet <Verbrauch>(); var groups = input.OrderBy(v => (v.Startdatum, v.Wertermittlungsverfahren, v.Obiskennzahl, v.Einheit)).GroupBy(v => new Tuple <Wertermittlungsverfahren, string, Mengeneinheit> ( v.Wertermittlungsverfahren, v.Obiskennzahl, v.Einheit )); foreach (var vGroup in groups) { HashSet <Verbrauch> subResult = new HashSet <Verbrauch>(); // find pairs (x,y) where x.end == y.start: // |----x----|--------y--------| //var adjacentVerbrauchs = from x in vGroup join y in vGroup on x.enddatum equals y.startdatum select new { x, y }; var adjacentVerbrauchs = from x in vGroup join y in vGroup on x.Enddatum equals y.Startdatum select new { x, y }; foreach (var av in adjacentVerbrauchs) { subResult.Add(av.x); subResult.Add(av.y); } // |----x----|--------y--------| // |-------------z-------------| // ==> delete z from result where z.start == x.start and z.end == y.end //var fullyRedundantVerbrauchs = from av in adjacentVerbrauchs join z in vGroup on new { av.x.startdatum, av.y.enddatum } equals new { z.startdatum, z.enddatum } select new { av, z }; var fullyRedundantVerbrauchs = from av in adjacentVerbrauchs join z in vGroup on new { av.x.Startdatum, av.y.Enddatum } equals new { z.Startdatum, z.Enddatum } select new { av, z }; foreach (var frv in fullyRedundantVerbrauchs) { if (frv.av.x.Wert + frv.av.y.Wert != frv.z.Wert) { throw new ArgumentException($"Inconsistent data detected: {JsonConvert.SerializeObject(frv.av.x)} + {JsonConvert.SerializeObject(frv.av.y)} ≠ {JsonConvert.SerializeObject(frv.z)}"); } subResult.Remove(frv.z); } // |----------x----------|---y---| // |---------------z-------------| // or // |---y---|----------x----------| // |---------------z-------------| // or // |---x1--|-----y------|---x2---| // |--------------z--------------| // y and z are given. find x such that x.value == y.value+z.value //subResult.UnionWith(vGroup); subResult.UnionWith(vGroup); foreach (Verbrauch z in new HashSet <Verbrauch>(subResult)) { var ys = subResult.Where(y => z.Contains(y) && !z.Equals(y)); if (ys.Count() > 0) { TimePeriodSubtractor <TimeRange> tps = new TimePeriodSubtractor <TimeRange>(); TimePeriodCollection source = new TimePeriodCollection { z.GetTimeRange() }; TimePeriodCollection subtract = new TimePeriodCollection(); subtract.AddAll(ys.Select(y => y.GetTimeRange())); ITimePeriodCollection subtractionResult = tps.SubtractPeriods(source, subtract); var xs = new HashSet <Verbrauch>(); foreach (var tr in subtractionResult) { Verbrauch v = new Verbrauch() { Einheit = z.Einheit, Wertermittlungsverfahren = z.Wertermittlungsverfahren, Obiskennzahl = z.Obiskennzahl, Startdatum = tr.Start, Enddatum = tr.End }; xs.Add(v); } var totalXWert = z.Wert - ys.Select(y => y.Wert).Sum(); var totalXDuration = xs.Select(x => x.GetDuration().TotalSeconds).Sum(); foreach (var x in xs) { x.Wert = (totalXWert * (decimal)x.GetDuration().TotalSeconds) / ((decimal)totalXDuration); } subResult.Remove(z); subResult.UnionWith(xs); } } resultSet.UnionWith(subResult); } List <Verbrauch> result = new List <Verbrauch>(resultSet); result.Sort(new VerbrauchDateTimeComparer()); return(result); }
/// <summary> /// <paramref name="start"/>시각으로부터 <paramref name="offset"/> 만큼 떨어진 시각을 구합니다. /// </summary> /// <param name="start">기준 시각</param> /// <param name="offset">기간</param> /// <param name="seekDirection">검색 방향 (이전|이후)</param> /// <param name="seekBoundaryMode">검색 값 포함 여부</param> /// <param name="remaining">짜투리 기간</param> /// <returns>기준 시각으로터 오프셋만큼 떨어진 시각</returns> protected DateTime? CalculateEnd(DateTime start, TimeSpan offset, SeekDirection seekDirection, SeekBoundaryMode seekBoundaryMode, out TimeSpan? remaining) { if(IsDebugEnabled) log.Debug("기준시각으로부터 오프셋만큼 떨어진 시각을 구합니다... " + @"start=[{0}], offset=[{1}], seekDirection=[{2}], seekBoundaryMode=[{3}]", start, offset, seekDirection, seekBoundaryMode); Guard.Assert(offset >= TimeSpan.Zero, "offset 값은 항상 0 이상이어야 합니다. offset=[{0}]", offset); remaining = offset; // search periods ITimePeriodCollection searchPeriods = new TimePeriodCollection(IncludePeriods); if(searchPeriods.Count == 0) searchPeriods.Add(TimeRange.Anytime); // available periods ITimePeriodCollection availablePeriods = new TimePeriodCollection(); if(ExcludePeriods.Count == 0) { availablePeriods.AddAll(searchPeriods); } else { // 예외 기간을 제외합니다. // var gapCalculator = new TimeGapCalculator<TimeRange>(); var query = searchPeriods #if !SILVERLIGHT .AsParallel() .AsOrdered() #endif .SelectMany(searchPeriod => ExcludePeriods.HasOverlapPeriods(searchPeriod) ? gapCalculator.GetGaps(ExcludePeriods, searchPeriod) // 예외 기간과 검색 기간에서 겹치지 않는 기간을 계산하여, 추려냅니다. : new TimePeriodCollection { searchPeriod } // 예외 기간과 겹쳐진 부분이 없다면, 기간 전체를 추가합니다. ); availablePeriods.AddAll(query); } // 유효한 Period가 없다면 중단합니다. if(availablePeriods.Count == 0) return null; // 기간중에 중복되는 부분의 없도록 유효한 기간을 결합합니다. if(availablePeriods.Count > 1) { var periodCombiner = new TimePeriodCombiner<TimeRange>(); availablePeriods = periodCombiner.CombinePeriods(availablePeriods); } // 첫 시작 기간을 찾습니다. // DateTime seekMoment; var startPeriod = (seekDirection == SeekDirection.Forward) ? FindNextPeriod(start, availablePeriods, out seekMoment) : FindPreviousPeriod(start, availablePeriods, out seekMoment); // 첫 시작 기간이 없다면 중단합니다. if(startPeriod == null) return null; // 오프셋 값이 0 이라면, 바로 다음 값이므로 seekMoment를 반환합니다. if(offset == TimeSpan.Zero) return seekMoment; if(seekDirection == SeekDirection.Forward) { for(var i = availablePeriods.IndexOf(startPeriod); i < availablePeriods.Count; i++) { var gap = availablePeriods[i]; var gapRemaining = gap.End - seekMoment; if(IsDebugEnabled) log.Debug("Seek Forward... gap=[{0}], gapRemaining=[{1}], remaining=[{2}], seekMoment=[{3}]", gap, gapRemaining, remaining, seekMoment); var isTargetPeriod = (seekBoundaryMode == SeekBoundaryMode.Fill) ? gapRemaining >= remaining : gapRemaining > remaining; if(isTargetPeriod) { var end = seekMoment + remaining.Value; remaining = null; return end; } remaining = remaining - gapRemaining; if(i == availablePeriods.Count - 1) return null; seekMoment = availablePeriods[i + 1].Start; // next period } } else { for(var i = availablePeriods.IndexOf(startPeriod); i >= 0; i--) { var gap = availablePeriods[i]; var gapRemaining = seekMoment - gap.Start; if(IsDebugEnabled) log.Debug("Seek Backward... gap=[{0}], gapRemaining=[{1}], remaining=[{2}], seekMoment=[{3}]", gap, gapRemaining, remaining, seekMoment); var isTargetPeriod = (seekBoundaryMode == SeekBoundaryMode.Fill) ? gapRemaining >= remaining : gapRemaining > remaining; if(isTargetPeriod) { var end = seekMoment - remaining.Value; remaining = null; return end; } remaining = remaining - gapRemaining; if(i == 0) return null; seekMoment = availablePeriods[i - 1].End; // previous period } } return null; }
public void GetGapTest() { var now = ClockProxy.Clock.Now; var schoolDay = new SchoolDay(now); var gapCalculator = new TimeGapCalculator<TimeRange>(); var excludePeriods = new TimePeriodCollection(); excludePeriods.AddAll(schoolDay); gapCalculator.GetGaps(excludePeriods).Count.Should().Be(0); gapCalculator.GetGaps(excludePeriods, schoolDay).Count.Should().Be(0); excludePeriods.Clear(); excludePeriods.Add(schoolDay.Lesson1); excludePeriods.Add(schoolDay.Lesson2); excludePeriods.Add(schoolDay.Lesson3); excludePeriods.Add(schoolDay.Lesson4); var gaps2 = gapCalculator.GetGaps(excludePeriods); gaps2.Count.Should().Be(3); gaps2[0].IsSamePeriod(schoolDay.Break1).Should().Be.True(); gaps2[1].IsSamePeriod(schoolDay.Break2).Should().Be.True(); gaps2[2].IsSamePeriod(schoolDay.Break3).Should().Be.True(); var testRange3 = new TimeRange(schoolDay.Lesson1.Start, schoolDay.Lesson4.End); var gaps3 = gapCalculator.GetGaps(excludePeriods, testRange3); gaps3.Count.Should().Be(3); gaps3[0].IsSamePeriod(schoolDay.Break1).Should().Be.True(); gaps3[1].IsSamePeriod(schoolDay.Break2).Should().Be.True(); gaps3[2].IsSamePeriod(schoolDay.Break3).Should().Be.True(); var testRange4 = new TimeRange(schoolDay.Start.AddHours(-1), schoolDay.End.AddHours(1)); var gaps4 = gapCalculator.GetGaps(excludePeriods, testRange4); gaps4.Count.Should().Be(5); gaps4[0].IsSamePeriod(new TimeRange(testRange4.Start, schoolDay.Start)).Should().Be.True(); gaps4[1].IsSamePeriod(schoolDay.Break1).Should().Be.True(); gaps4[2].IsSamePeriod(schoolDay.Break2).Should().Be.True(); gaps4[3].IsSamePeriod(schoolDay.Break3).Should().Be.True(); gaps4[4].IsSamePeriod(new TimeRange(testRange4.End, schoolDay.End)).Should().Be.True(); excludePeriods.Clear(); excludePeriods.Add(schoolDay.Lesson1); var gaps8 = gapCalculator.GetGaps(excludePeriods, schoolDay.Lesson1); gaps8.Count.Should().Be(0); excludePeriods.Clear(); excludePeriods.Add(schoolDay.Lesson1); var testRange9 = new TimeRange(schoolDay.Lesson1.Start.Subtract(new TimeSpan(1)), schoolDay.Lesson1.End.Add(new TimeSpan(1))); var gaps9 = gapCalculator.GetGaps(excludePeriods, testRange9); gaps9.Count.Should().Be(2); gaps9[0].Duration.Should().Be(TimeSpan.FromTicks(1)); gaps9[1].Duration.Should().Be(TimeSpan.FromTicks(1)); }
public void GetFreeBusyRequestTimePeriodIntersectorTest() { //取得會議室資訊 var calendarAttributes = "fullName,email"; var searchMeetingRooms = new SearchCalendarResourcesRequest(calendarAttributes); ZmailRequest.ApiRequest = searchMeetingRooms; var zResquestRoom = ZmailDispatcher.SendRequest(ZmailRequest); var respRoom = zResquestRoom.ApiResponse as SearchCalendarResourcesResponse; var crList = respRoom?.CalendarResourceList; if (crList != null) { var crEmails = crList.Select(cr => cr.AttributesList.Where(ar => ar.Key.Equals("email")).Select(ar => ar.Value).FirstOrDefault()); var sdate = DateTime.Now; var edate = sdate.AddDays(7); //多人請用逗號隔開 var crEmailStrings = string.Join(",", crEmails.ToArray()); var searchNames = "[email protected],[email protected]," + crEmailStrings; ZmailRequest.ApiRequest = new GetFreeBusyRequest(sdate, edate, searchNames); var zResquest = ZmailDispatcher.SendRequest(ZmailRequest); var resp = zResquest.ApiResponse as GetFreeBusyResponse; var wkHours = resp?.Workinghours; if (wkHours != null) { var periods = new Dictionary <string, TimePeriodCollection>(); var users = wkHours.Users.Where(u => !crEmails.Contains(u.id)); //找出所有人的共同時間 var periodIntersector = new TimePeriodIntersector <TimeRange>(); var resultPeriods = new TimePeriodCollection(); var idx = 0; foreach (var user in users) { Console.WriteLine(user.id); Console.WriteLine("Free"); var userPeriods = new TimePeriodCollection(); foreach (var f in user.Fs) { Console.WriteLine($" {f.s} - {f.e}"); userPeriods.Add(new TimeRange(f.s, f.e)); } periods.Add(user.id, userPeriods); if (idx == 0) { resultPeriods.AddAll(user.Fs.Select(f => new TimeRange(f.s, f.e))); } else { resultPeriods.AddAll(user.Fs.Select(f => new TimeRange(f.s, f.e))); if (resultPeriods.Any()) { var intersectedPeriods = periodIntersector.IntersectPeriods(resultPeriods, false); resultPeriods.Clear(); resultPeriods.AddAll(intersectedPeriods.Select(p => new TimeRange(p.Start, p.End))); } } } Console.WriteLine("...共同的時間..."); foreach (var intersectedPeriod in resultPeriods) { Console.WriteLine("所有人共同的的時間 Period: " + intersectedPeriod); } //對應到會議室 var crs = wkHours.Users.Where(u => crEmails.Contains(u.id)); foreach (var cr in crList) { var email = cr.AttributesList.Where(ar => ar.Key.Equals("email")) .Select(ar => ar.Value) .FirstOrDefault(); var fullName = cr.AttributesList.Where(ar => ar.Key.Equals("fullName")) .Select(ar => ar.Value) .FirstOrDefault(); Console.WriteLine($"{fullName}:{email}"); var crHour = wkHours.Users.FirstOrDefault(u => u.id.Equals(email)); if (crHour != null) { var crPeriods = new TimePeriodCollection(); crPeriods.AddAll(resultPeriods.Select(p => new TimeRange(p.Start, p.End))); crPeriods.AddAll(crHour.Fs.Select(f => new TimeRange(f.s, f.e))); var intersectedPeriods = periodIntersector.IntersectPeriods(crPeriods, false); Console.WriteLine($"{fullName}:{email}...共同的時間..."); foreach (var intersectedPeriod in intersectedPeriods) { Console.WriteLine(" Period: " + intersectedPeriod); } } } } } }
public void SortByTest() { DateTime now = ClockProxy.Clock.Now; SchoolDay schoolDay = new SchoolDay(now); TimePeriodCollection timePeriods = new TimePeriodCollection(); // start timePeriods.Add(schoolDay.Lesson4); timePeriods.Add(schoolDay.Break3); timePeriods.Add(schoolDay.Lesson3); timePeriods.Add(schoolDay.Break2); timePeriods.Add(schoolDay.Lesson2); timePeriods.Add(schoolDay.Break1); timePeriods.Add(schoolDay.Lesson1); timePeriods.SortBy(TimePeriodStartComparer.Comparer); Assert.Equal(timePeriods[0], schoolDay.Lesson1); Assert.Equal(timePeriods[1], schoolDay.Break1); Assert.Equal(timePeriods[2], schoolDay.Lesson2); Assert.Equal(timePeriods[3], schoolDay.Break2); Assert.Equal(timePeriods[4], schoolDay.Lesson3); Assert.Equal(timePeriods[5], schoolDay.Break3); Assert.Equal(timePeriods[6], schoolDay.Lesson4); timePeriods.SortReverseBy(TimePeriodStartComparer.Comparer); Assert.Equal(timePeriods[0], schoolDay.Lesson4); Assert.Equal(timePeriods[1], schoolDay.Break3); Assert.Equal(timePeriods[2], schoolDay.Lesson3); Assert.Equal(timePeriods[3], schoolDay.Break2); Assert.Equal(timePeriods[4], schoolDay.Lesson2); Assert.Equal(timePeriods[5], schoolDay.Break1); Assert.Equal(timePeriods[6], schoolDay.Lesson1); // end timePeriods.Clear(); timePeriods.AddAll(schoolDay); timePeriods.SortReverseBy(TimePeriodEndComparer.Comparer); Assert.Equal(timePeriods[0], schoolDay.Lesson4); Assert.Equal(timePeriods[1], schoolDay.Break3); Assert.Equal(timePeriods[2], schoolDay.Lesson3); Assert.Equal(timePeriods[3], schoolDay.Break2); Assert.Equal(timePeriods[4], schoolDay.Lesson2); Assert.Equal(timePeriods[5], schoolDay.Break1); Assert.Equal(timePeriods[6], schoolDay.Lesson1); timePeriods.SortBy(TimePeriodEndComparer.Comparer); Assert.Equal(timePeriods[0], schoolDay.Lesson1); Assert.Equal(timePeriods[1], schoolDay.Break1); Assert.Equal(timePeriods[2], schoolDay.Lesson2); Assert.Equal(timePeriods[3], schoolDay.Break2); Assert.Equal(timePeriods[4], schoolDay.Lesson3); Assert.Equal(timePeriods[5], schoolDay.Break3); Assert.Equal(timePeriods[6], schoolDay.Lesson4); // duration timePeriods.Clear(); TimeSpan oneHour = new TimeSpan(1, 0, 0); TimeSpan twoHours = new TimeSpan(2, 0, 0); TimeSpan threeHours = new TimeSpan(3, 0, 0); TimeSpan fourHours = new TimeSpan(4, 0, 0); timePeriods.Add(new TimeRange(now, oneHour)); timePeriods.Add(new TimeRange(now, twoHours)); timePeriods.Add(new TimeRange(now, threeHours)); timePeriods.Add(new TimeRange(now, fourHours)); timePeriods.SortReverseBy(TimePeriodDurationComparer.Comparer); Assert.Equal(fourHours, timePeriods[0].Duration); Assert.Equal(threeHours, timePeriods[1].Duration); Assert.Equal(twoHours, timePeriods[2].Duration); Assert.Equal(oneHour, timePeriods[3].Duration); timePeriods.SortBy(TimePeriodDurationComparer.Comparer); Assert.Equal(oneHour, timePeriods[0].Duration); Assert.Equal(twoHours, timePeriods[1].Duration); Assert.Equal(threeHours, timePeriods[2].Duration); Assert.Equal(fourHours, timePeriods[3].Duration); } // SortByTest
/// <summary> /// <paramref name="start"/>시각으로부터 <paramref name="offset"/> 만큼 떨어진 시각을 구합니다. /// </summary> /// <param name="start">기준 시각</param> /// <param name="offset">기간</param> /// <param name="seekDirection">검색 방향 (이전|이후)</param> /// <param name="seekBoundaryMode">검색 값 포함 여부</param> /// <param name="remaining">짜투리 기간</param> /// <returns>기준 시각으로터 오프셋만큼 떨어진 시각</returns> protected DateTime?CalculateEnd(DateTime start, TimeSpan offset, SeekDirection seekDirection, SeekBoundaryMode seekBoundaryMode, out TimeSpan?remaining) { if (IsDebugEnabled) { log.Debug("기준시각으로부터 오프셋만큼 떨어진 시각을 구합니다... " + @"start=[{0}], offset=[{1}], seekDirection=[{2}], seekBoundaryMode=[{3}]", start, offset, seekDirection, seekBoundaryMode); } Guard.Assert(offset >= TimeSpan.Zero, "offset 값은 항상 0 이상이어야 합니다. offset=[{0}]", offset); remaining = offset; // search periods ITimePeriodCollection searchPeriods = new TimePeriodCollection(IncludePeriods); if (searchPeriods.Count == 0) { searchPeriods.Add(TimeRange.Anytime); } // available periods ITimePeriodCollection availablePeriods = new TimePeriodCollection(); if (ExcludePeriods.Count == 0) { availablePeriods.AddAll(searchPeriods); } else { // 예외 기간을 제외합니다. // var gapCalculator = new TimeGapCalculator <TimeRange>(); var query = searchPeriods #if !SILVERLIGHT .AsParallel() .AsOrdered() #endif .SelectMany(searchPeriod => ExcludePeriods.HasOverlapPeriods(searchPeriod) ? gapCalculator.GetGaps(ExcludePeriods, searchPeriod) // 예외 기간과 검색 기간에서 겹치지 않는 기간을 계산하여, 추려냅니다. : new TimePeriodCollection { searchPeriod } // 예외 기간과 겹쳐진 부분이 없다면, 기간 전체를 추가합니다. ); availablePeriods.AddAll(query); } // 유효한 Period가 없다면 중단합니다. if (availablePeriods.Count == 0) { return(null); } // 기간중에 중복되는 부분의 없도록 유효한 기간을 결합합니다. if (availablePeriods.Count > 1) { var periodCombiner = new TimePeriodCombiner <TimeRange>(); availablePeriods = periodCombiner.CombinePeriods(availablePeriods); } // 첫 시작 기간을 찾습니다. // DateTime seekMoment; var startPeriod = (seekDirection == SeekDirection.Forward) ? FindNextPeriod(start, availablePeriods, out seekMoment) : FindPreviousPeriod(start, availablePeriods, out seekMoment); // 첫 시작 기간이 없다면 중단합니다. if (startPeriod == null) { return(null); } // 오프셋 값이 0 이라면, 바로 다음 값이므로 seekMoment를 반환합니다. if (offset == TimeSpan.Zero) { return(seekMoment); } if (seekDirection == SeekDirection.Forward) { for (var i = availablePeriods.IndexOf(startPeriod); i < availablePeriods.Count; i++) { var gap = availablePeriods[i]; var gapRemaining = gap.End - seekMoment; if (IsDebugEnabled) { log.Debug("Seek Forward... gap=[{0}], gapRemaining=[{1}], remaining=[{2}], seekMoment=[{3}]", gap, gapRemaining, remaining, seekMoment); } var isTargetPeriod = (seekBoundaryMode == SeekBoundaryMode.Fill) ? gapRemaining >= remaining : gapRemaining > remaining; if (isTargetPeriod) { var end = seekMoment + remaining.Value; remaining = null; return(end); } remaining = remaining - gapRemaining; if (i == availablePeriods.Count - 1) { return(null); } seekMoment = availablePeriods[i + 1].Start; // next period } } else { for (var i = availablePeriods.IndexOf(startPeriod); i >= 0; i--) { var gap = availablePeriods[i]; var gapRemaining = seekMoment - gap.Start; if (IsDebugEnabled) { log.Debug("Seek Backward... gap=[{0}], gapRemaining=[{1}], remaining=[{2}], seekMoment=[{3}]", gap, gapRemaining, remaining, seekMoment); } var isTargetPeriod = (seekBoundaryMode == SeekBoundaryMode.Fill) ? gapRemaining >= remaining : gapRemaining > remaining; if (isTargetPeriod) { var end = seekMoment - remaining.Value; remaining = null; return(end); } remaining = remaining - gapRemaining; if (i == 0) { return(null); } seekMoment = availablePeriods[i - 1].End; // previous period } } return(null); }
public void CalendarGetGapTest() { // simmulation of some reservations var periods = new TimePeriodCollection { new DayRangeCollection(2011, 3, 7, 2), new DayRangeCollection(2011, 3, 16, 2) }; // the overall search range var limits = new CalendarTimeRange(new DateTime(2011, 3, 4), new DateTime(2011, 3, 21)); var days = new DayRangeCollection(limits.Start, limits.Duration.Days + 1); // limits의 내부이고, 주말인 DayRange를 추가합니다. var excludeDays = days.GetDays().Where(day => limits.HasInside(day) && day.DayOfWeek.IsWeekEnd()); periods.AddAll(excludeDays.Cast<ITimePeriod>()); var gapCalculator = new TimeGapCalculator<TimeRange>(new TimeCalendar()); var gaps = gapCalculator.GetGaps(periods, limits); gaps.Count.Should().Be(4); gaps[0].IsSamePeriod(new TimeRange(new DateTime(2011, 3, 4), DurationUtil.Days(1))).Should().Be.True(); gaps[1].IsSamePeriod(new TimeRange(new DateTime(2011, 3, 9), DurationUtil.Days(3))).Should().Be.True(); gaps[2].IsSamePeriod(new TimeRange(new DateTime(2011, 3, 14), DurationUtil.Days(2))).Should().Be.True(); gaps[3].IsSamePeriod(new TimeRange(new DateTime(2011, 3, 18), DurationUtil.Days(1))).Should().Be.True(); }