public void TestMask() { var calendar = new Calendar(); calendar.Mask = 0; Assert.AreEqual(calendar.Monday, false); Assert.AreEqual(calendar.Tuesday, false); Assert.AreEqual(calendar.Wednesday, false); Assert.AreEqual(calendar.Thursday, false); Assert.AreEqual(calendar.Friday, false); Assert.AreEqual(calendar.Saturday, false); Assert.AreEqual(calendar.Sunday, false); calendar.Mask = 1; Assert.AreEqual(calendar.Monday, true); Assert.AreEqual(calendar.Tuesday, false); Assert.AreEqual(calendar.Wednesday, false); Assert.AreEqual(calendar.Thursday, false); Assert.AreEqual(calendar.Friday, false); Assert.AreEqual(calendar.Saturday, false); Assert.AreEqual(calendar.Sunday, false); calendar.Mask = 64; Assert.AreEqual(calendar.Monday, false); Assert.AreEqual(calendar.Tuesday, false); Assert.AreEqual(calendar.Wednesday, false); Assert.AreEqual(calendar.Thursday, false); Assert.AreEqual(calendar.Friday, false); Assert.AreEqual(calendar.Saturday, false); Assert.AreEqual(calendar.Sunday, true); calendar.Mask = 1+4+8+16; Assert.AreEqual(calendar.Monday, true); Assert.AreEqual(calendar.Tuesday, false); Assert.AreEqual(calendar.Wednesday, true); Assert.AreEqual(calendar.Thursday, true); Assert.AreEqual(calendar.Friday, true); Assert.AreEqual(calendar.Saturday, false); Assert.AreEqual(calendar.Sunday, false); calendar.Mask = 0; calendar.Monday = true; Assert.AreEqual(1, calendar.Mask); calendar.Mask = 0; calendar.Sunday = true; Assert.AreEqual(64, calendar.Mask); calendar.Mask = 0; calendar.Monday = true; calendar.Wednesday = true; calendar.Thursday = true; calendar.Friday = true; Assert.AreEqual(1+4+8+16, calendar.Mask); }
/// <summary> /// Copies the week pattern from the given calendar. /// </summary> public static void CopyWeekPatternFrom(this Calendar calendar, Calendar weekPatternSource) { calendar.Monday = weekPatternSource.Monday; calendar.Tuesday = weekPatternSource.Tuesday; calendar.Wednesday = weekPatternSource.Wednesday; calendar.Thursday = weekPatternSource.Thursday; calendar.Friday = weekPatternSource.Friday; calendar.Saturday = weekPatternSource.Saturday; calendar.Sunday = weekPatternSource.Sunday; }
/// <summary> /// Adds the given calendar day and returns one or more new calendar entities representing the new data. /// </summary> /// <returns></returns> public static IEnumerable<Calendar> Add(this Calendar calendar, DateTime day) { if(calendar.CoversDate(day)) { // no work needs to be done. return new Calendar[] { calendar }; } // naively add another calendar entity representing one day. if ((calendar.EndDate - calendar.StartDate).Days <= 7 && calendar.StartDate <= day && calendar.EndDate >= day) { var newCalendar = new Calendar() { EndDate = calendar.EndDate, StartDate = calendar.StartDate, Mask = calendar.Mask, ServiceId = calendar.ServiceId }; newCalendar.Set(day, true); return new Calendar[] { newCalendar }; } return new Calendar[] { calendar, day.CreateCalendar(calendar.ServiceId) }; }
public void TestTryMerge() { // two simple consequitive weeks. var calendar1 = new Calendar() { ServiceId = "0", Mask = 64, StartDate = new System.DateTime(2015, 11, 23), EndDate = new System.DateTime(2015, 11, 29) }; var calendar2 = new Calendar() { ServiceId = "0", Mask = 64, StartDate = new System.DateTime(2015, 11, 30), EndDate = new System.DateTime(2015, 12, 06) }; Calendar merge; Assert.IsTrue(calendar1.TryMerge(calendar2, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(64, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 29), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 06), merge.EndDate); Assert.IsTrue(calendar2.TryMerge(calendar1, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(64, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 29), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 06), merge.EndDate); // two calendars one completely overlapping the other. calendar1 = new Calendar() { ServiceId = "0", Mask = 64, StartDate = new System.DateTime(2015, 11, 23), EndDate = new System.DateTime(2015, 11, 29) }; calendar2 = new Calendar() { ServiceId = "0", Mask = 64, StartDate = new System.DateTime(2015, 11, 23), EndDate = new System.DateTime(2015, 12, 06) }; Assert.IsTrue(calendar1.TryMerge(calendar2, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(64, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 29), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 06), merge.EndDate); Assert.IsTrue(calendar2.TryMerge(calendar1, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(64, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 29), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 06), merge.EndDate); // two calendars one completely overlapping the other. calendar1 = new Calendar() { ServiceId = "0", Mask = 64, StartDate = new System.DateTime(2015, 11, 23), EndDate = new System.DateTime(2015, 11, 29) }; calendar1.TrimDates(); calendar2 = new Calendar() { ServiceId = "0", Mask = 64, StartDate = new System.DateTime(2015, 11, 23), EndDate = new System.DateTime(2015, 12, 06) }; calendar2.TrimDates(); Assert.IsTrue(calendar1.TryMerge(calendar2, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(64, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 29), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 06), merge.EndDate); Assert.IsTrue(calendar2.TryMerge(calendar1, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(64, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 29), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 06), merge.EndDate); // two calendars one completely overlapping the other. calendar1 = new Calendar() { ServiceId = "0", Mask = 64, StartDate = new System.DateTime(2015, 11, 23), EndDate = new System.DateTime(2015, 11, 29) }; calendar1.TrimDates(); calendar2 = new Calendar() { ServiceId = "0", Mask = 1+64, StartDate = new System.DateTime(2015, 11, 23), EndDate = new System.DateTime(2015, 12, 06) }; calendar2.TrimDates(); Assert.IsTrue(calendar1.TryMerge(calendar2, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(1+64, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 23), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 06), merge.EndDate); Assert.IsTrue(calendar2.TryMerge(calendar1, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(1+64, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 23), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 06), merge.EndDate); // two calendars with conflicting masks. calendar1 = new Calendar() { ServiceId = "0", Mask = 2+64, StartDate = new System.DateTime(2015, 11, 23), EndDate = new System.DateTime(2015, 11, 29) }; calendar1.TrimDates(); calendar2 = new Calendar() { ServiceId = "0", Mask = 1+64, StartDate = new System.DateTime(2015, 11, 30), EndDate = new System.DateTime(2015, 12, 06) }; calendar2.TrimDates(); Assert.IsFalse(calendar1.TryMerge(calendar2, out merge)); Assert.IsFalse(calendar2.TryMerge(calendar1, out merge)); // two calendars both spanning more than one week. calendar1 = new Calendar() { ServiceId = "0", Mask = 127, StartDate = new System.DateTime(2015, 11, 16), EndDate = new System.DateTime(2015, 11, 29) }; calendar1.TrimDates(); calendar2 = new Calendar() { ServiceId = "0", Mask = 127, StartDate = new System.DateTime(2015, 11, 30), EndDate = new System.DateTime(2015, 12, 13) }; calendar2.TrimDates(); Assert.IsTrue(calendar1.TryMerge(calendar2, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(127, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 16), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 13), merge.EndDate); Assert.IsTrue(calendar2.TryMerge(calendar1, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(127, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 16), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 13), merge.EndDate); // two calendars first one week with a few don't-case other more than a week. calendar1 = new Calendar() { ServiceId = "0", Mask = 64 + 32 + 16 + 8, StartDate = new System.DateTime(2015, 11, 26), EndDate = new System.DateTime(2015, 11, 29) }; calendar1.TrimDates(); calendar2 = new Calendar() { ServiceId = "0", Mask = 127, StartDate = new System.DateTime(2015, 11, 30), EndDate = new System.DateTime(2015, 12, 13) }; calendar2.TrimDates(); Assert.IsTrue(calendar1.TryMerge(calendar2, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(127, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 26), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 13), merge.EndDate); Assert.IsTrue(calendar2.TryMerge(calendar1, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(127, merge.Mask); Assert.AreEqual(new System.DateTime(2015, 11, 26), merge.StartDate); Assert.AreEqual(new System.DateTime(2015, 12, 13), merge.EndDate); // two calendars first two weeks with a few don't-cares other the week right after. calendar1 = new Calendar() { ServiceId = "0", Mask = 127, StartDate = new System.DateTime(2016, 01, 01), EndDate = new System.DateTime(2016, 01, 10) }; calendar1.TrimDates(); calendar2 = new Calendar() { ServiceId = "0", Mask = 127, StartDate = new System.DateTime(2016, 01, 11), EndDate = new System.DateTime(2016, 01, 17) }; calendar2.TrimDates(); Assert.IsTrue(calendar1.TryMerge(calendar2, out merge)); Assert.AreEqual("0", merge.ServiceId); Assert.AreEqual(127, merge.Mask); Assert.AreEqual(new System.DateTime(2016, 01, 01), merge.StartDate); Assert.AreEqual(new System.DateTime(2016, 01, 17), merge.EndDate); }
/// <summary> /// Tries to merge two calendars together. /// </summary> /// <returns></returns> public static bool TryMerge(this Calendar calendar, Calendar other, out Calendar merge) { if(calendar.ServiceId != other.ServiceId) { throw new InvalidOperationException("Cannot merge calendars with different service id's."); } var calendarMonday = calendar.StartDate.FirstDayOfWeek(); var otherMonday = other.StartDate.FirstDayOfWeek(); if(calendarMonday > otherMonday) { // switch the two, assume that calendar 'before' other. var temp = calendar; calendar = other; other = temp; calendarMonday = calendar.StartDate.FirstDayOfWeek(); otherMonday = other.StartDate.FirstDayOfWeek(); } var otherStartMonday = other.StartDate.FirstDayOfWeek(); var calendarEndSunday = calendar.EndDate.LastDayOfWeek(); if (calendarEndSunday.AddDays(1) != otherStartMonday && calendarMonday != otherMonday) { // cannot merge, differ more than a week. merge = null; return false; } // check if masks match and merge. var mergedMask = new bool[7]; for (var i = 0; i < 7; i++) { var calendarStatus = calendar.GetStatusFor(calendarMonday.AddDays(i)); var otherStatus = other.GetStatusFor(otherMonday.AddDays(i)); if (calendarStatus == null) { mergedMask[i] = otherStatus == null ? false : otherStatus.Value; } else if (otherStatus == null) { mergedMask[i] = calendarStatus == null ? false : calendarStatus.Value; } else if (otherStatus.Value != calendarStatus.Value) { // impossible to merge conflicting statuses. merge = null; return false; } else { mergedMask[i] = calendarStatus.Value; } } merge = new Calendar() { StartDate = calendar.StartDate < other.StartDate ? calendar.StartDate : other.StartDate, EndDate = other.EndDate > calendar.EndDate ? other.EndDate : calendar.EndDate, ServiceId = calendar.ServiceId, Monday = mergedMask[0], Tuesday = mergedMask[1], Wednesday = mergedMask[2], Thursday = mergedMask[3], Friday = mergedMask[4], Saturday = mergedMask[5], Sunday = mergedMask[6], }; merge.TrimDates(); return true; }
/// <summary> /// Subtracts the given calendar day and returns one or more new calendar entities representing the same data. /// </summary> public static IEnumerable<Calendar> Subtract(this Calendar calendar, DateTime day) { if (!calendar.CoversDate(day)) { // no work needs to be done. return new Calendar[] { calendar }; } var firstDayOfWeek = day.FirstDayOfWeek(); var lastDayOfWeek = day.LastDayOfWeek(); if(firstDayOfWeek <= calendar.StartDate && lastDayOfWeek >= calendar.EndDate) { // yay! this is exceptional but let's take advantage of this. calendar[day.DayOfWeek] = false; return new Calendar[] { calendar }; } // possibly split in two or three pieces. if(firstDayOfWeek <= calendar.StartDate) { // two pieces, a first week with the day substracted and the rest. var subtracted = new Calendar() { StartDate = calendar.StartDate, EndDate = lastDayOfWeek }; subtracted.CopyWeekPatternFrom(calendar); subtracted[day.DayOfWeek] = false; var rest = new Calendar() { StartDate = lastDayOfWeek.AddDays(1), EndDate = calendar.EndDate }; rest.CopyWeekPatternFrom(calendar); return new Calendar[] { subtracted, rest }; } else if (lastDayOfWeek >= calendar.EndDate) { // two pieces, a last week with the day substracted and the rest. var rest = new Calendar() { StartDate = calendar.StartDate, EndDate = firstDayOfWeek.AddDays(-1) }; rest.CopyWeekPatternFrom(calendar); var subtracted = new Calendar() { StartDate = firstDayOfWeek, EndDate = calendar.EndDate }; subtracted.CopyWeekPatternFrom(calendar); subtracted[day.DayOfWeek] = false; return new Calendar[] { rest, subtracted }; } else { // three pieces, a first period, a week with the day subtracted and a last period. var rest1 = new Calendar() { StartDate = calendar.StartDate, EndDate = firstDayOfWeek.AddDays(-1) }; rest1.CopyWeekPatternFrom(calendar); var subtracted = new Calendar() { StartDate = firstDayOfWeek, EndDate = lastDayOfWeek }; subtracted.CopyWeekPatternFrom(calendar); subtracted[day.DayOfWeek] = false; var rest2 = new Calendar() { StartDate = lastDayOfWeek.AddDays(1), EndDate = calendar.EndDate }; rest2.CopyWeekPatternFrom(calendar); return new Calendar[] { rest1, subtracted, rest2 }; } }
/// <summary> /// Creates a calendar entity for the day. /// </summary> /// <returns></returns> public static Calendar CreateCalendar(this DateTime day, string serviceId) { var calendar = new Calendar() { StartDate = day, EndDate = day, ServiceId = serviceId }; calendar[day.DayOfWeek] = true; return calendar; }