using System.Collections.Generic;
public static void Main()
foreach(var year in Enumerable.Range(2017, 9))
var existingPlanStartDate = new DateTime(year, 1, 1);
var existingPlanEndDate = existingPlanStartDate.AddMonths(12).AddSeconds(-1);
var existingPlanLength = (existingPlanEndDate - existingPlanStartDate).TotalSeconds;
var existingPlan = new SamplePlan() { StartDate = existingPlanStartDate, EndDate = existingPlanEndDate };
var eventStart = existingPlanStartDate;
while (eventStart.AddDays(7) <= existingPlanEndDate)
existingPlan.AddToEvents(eventStart, eventStart.AddDays(7));
eventStart = eventStart.AddMonths(1);
Console.WriteLine("Existing Plan: {0}", existingPlan);
var newPlanStartDate = existingPlan.EndDate.Date.AddDays(1);
var newPlanEndDate = newPlanStartDate.AddSeconds(existingPlanLength);
var leapDayDifference = newPlanStartDate.LeapDayCount(newPlanEndDate) - existingPlan.StartDate.LeapDayCount(existingPlan.EndDate);
newPlanEndDate = newPlanEndDate.AddDays(leapDayDifference);
var newPlan = new SamplePlan() { StartDate = newPlanStartDate, EndDate = newPlanEndDate };
Console.WriteLine("New Plan: {0}", newPlan);
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
private List<SamplePlanEvent> _events = new List<SamplePlanEvent>();
public IEnumerable<SamplePlanEvent> Events { get { return (_events ?? new List<SamplePlanEvent>()).AsEnumerable(); } }
public void AddToEvents(DateTime start, DateTime end)
AddToEvents(new SamplePlanEvent() { Start = start, End = end });
public void AddToEvents(SamplePlanEvent newEvent)
AddToEvents(new SamplePlanEvent[] { newEvent });
public void AddToEvents(IEnumerable<SamplePlanEvent> newEvents)
_events = new List<SamplePlanEvent>();
newEvents.ToList().ForEach(x => CheckEvent(x));
_events.AddRange(newEvents);
private void CheckEvent(SamplePlanEvent samplePlanEvent)
if (samplePlanEvent.Start < StartDate)
throw new ArgumentException("Start of event cannot be before start of plan", nameof(samplePlanEvent));
if (samplePlanEvent.End > EndDate)
throw new ArgumentException("End of event cannot be after end of plan", nameof(samplePlanEvent));
if (samplePlanEvent.Start >= samplePlanEvent.End)
throw new ArgumentException("End of event cannot be before the start", nameof(samplePlanEvent));
public override string ToString()
var text = string.Format("{0} to {1}: ({2:0} days)", StartDate.ToString(Extensions.DATETIME_FORMAT), EndDate.ToString(Extensions.DATETIME_FORMAT), (EndDate - StartDate).TotalDays);
if (Events != null && Events.Any())
text += Environment.NewLine + string.Join(Environment.NewLine, Events.OrderBy(x => x.Start).Select((x, y) => x.ToString()));
public class SamplePlanEvent
public DateTime Start { get; set; }
public DateTime End { get; set; }
public override string ToString() => string.Format("{0} to {1}", Start.ToString(Extensions.DATETIME_FORMAT), End.ToString(Extensions.DATETIME_FORMAT));
public static class Extensions
public static int LeapDayCount(this DateTime dateFrom, DateTime dateTo)
var start = (dateFrom <= dateTo ? dateFrom : dateTo).Date;
var end = (dateFrom > dateTo ? dateFrom : dateTo).Date;
var days = (end - start).Days + 1;
return Enumerable.Range(0, days).Select(x => start.AddDays(x)).Count(x => x.Month == 2 && x.Day == 29);
public const string DATETIME_FORMAT = "dd-MMM-yy HH:mm:ss";