601 lines
32 KiB
C#
601 lines
32 KiB
C#
#if FRAMEWORK20
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Globalization;
|
|
|
|
namespace DevComponents.Schedule.Model
|
|
{
|
|
internal class RecurrenceGenerator : IRecurrenceGenerator
|
|
{
|
|
|
|
#region IRecurrenceGenerator Members
|
|
|
|
/// <summary>
|
|
/// Generates Daily recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
|
/// </summary>
|
|
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
|
/// <param name="recurrence">Recurrence description, must be of Daily recurrence type.</param>
|
|
/// <param name="startDate">Start date for generation.</param>
|
|
/// <param name="endDate">End date for generation.</param>
|
|
public void GenerateDailyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate)
|
|
{
|
|
Appointment app = recurrence.Appointment;
|
|
int appointmentDaysDuration = recurrence.Daily.ExplicitDailyRecurrence ? 0 : (int)Math.Max(0, Math.Ceiling(app.EndTime.Date.Subtract(app.StartTime.Date).TotalDays));
|
|
DateTime recurrenceStartDate = DateTimeHelper.MaxDate(recurrence.RecurrenceStartDate,
|
|
recurrence.Daily.ExplicitDailyRecurrence ? (app.StartTime.AddDays(1).Date) : (DateTimeHelper.IsBeginningOfDay(app.EndTime) ? app.EndTime : app.EndTime.AddDays(recurrence.Daily.RepeatInterval).Date));
|
|
|
|
if (recurrenceStartDate > endDate) return;
|
|
|
|
int repeats = 0;
|
|
|
|
// Check the range first
|
|
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate)
|
|
{
|
|
if (startDate > recurrence.RangeEndDate) return;
|
|
}
|
|
else if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences)
|
|
{
|
|
DateTime rangeStartDate = DateTimeHelper.MaxDate(recurrence.RecurrenceStartDate,
|
|
recurrence.Daily.ExplicitDailyRecurrence ? (app.LocalStartTime.AddDays(1).Date) : (DateTimeHelper.IsBeginningOfDay(app.LocalEndTime) ? app.LocalEndTime : app.LocalEndTime.AddDays(recurrence.Daily.RepeatInterval).Date));
|
|
int totalDays = (int)Math.Ceiling(startDate.Subtract(rangeStartDate).TotalDays);
|
|
|
|
switch (recurrence.Daily.RepeatOnDaysOfWeek)
|
|
{
|
|
case eDailyRecurrenceRepeat.All:
|
|
repeats = Math.Max(0, totalDays / (recurrence.Daily.RepeatInterval + appointmentDaysDuration));
|
|
|
|
if (repeats >= recurrence.RangeNumberOfOccurrences)
|
|
return;
|
|
break;
|
|
|
|
case eDailyRecurrenceRepeat.WeekDays:
|
|
repeats = Math.Max(0, DateTimeHelper.TotalWeekDays(recurrenceStartDate, startDate) / (recurrence.Daily.RepeatInterval + appointmentDaysDuration));
|
|
|
|
// Assume weekdays repeat
|
|
if (repeats > recurrence.RangeNumberOfOccurrences)
|
|
return;
|
|
break;
|
|
|
|
default:
|
|
repeats = Math.Max(0, DateTimeHelper.TotalWeekDays(recurrenceStartDate, startDate));
|
|
repeats = Math.Max(0, totalDays - repeats);
|
|
|
|
// Assume weekend days repeat
|
|
if (repeats > recurrence.RangeNumberOfOccurrences)
|
|
return;
|
|
break;
|
|
}
|
|
|
|
//repeats = 0;
|
|
}
|
|
|
|
DateTime currentDay = recurrenceStartDate; // DateTimeHelper.MaxDate(startDate, recurrenceStartDate);
|
|
|
|
while (currentDay <= endDate)
|
|
{
|
|
if (currentDay >= startDate || currentDay < startDate && IsRecurringOnDay(currentDay, startDate, endDate, app))
|
|
{
|
|
if (!IsRecurringOnDay(currentDay, startDate, endDate, app)) break;
|
|
if (recurrence.Daily.RepeatOnDaysOfWeek == eDailyRecurrenceRepeat.All ||
|
|
(recurrence.Daily.RepeatOnDaysOfWeek == eDailyRecurrenceRepeat.WeekDays && !DateTimeHelper.IsWeekendDay(currentDay)) ||
|
|
(recurrence.Daily.RepeatOnDaysOfWeek == eDailyRecurrenceRepeat.WeekendDays && DateTimeHelper.IsWeekendDay(currentDay)))
|
|
{
|
|
if (!IsIgnoredRecurrence(currentDay, recurrence))
|
|
{
|
|
subsetCollection.Add(CreateRecurringAppointmentInstance(currentDay, app));
|
|
repeats++;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (recurrence.Daily.RepeatOnDaysOfWeek)
|
|
{
|
|
case eDailyRecurrenceRepeat.All:
|
|
currentDay = currentDay.AddDays(recurrence.Daily.RepeatInterval + appointmentDaysDuration);
|
|
break;
|
|
|
|
case eDailyRecurrenceRepeat.WeekDays:
|
|
//currentDay = currentDay.AddDays(currentDay.DayOfWeek == DayOfWeek.Saturday ? 2 : Math.Max(1, recurrence.Daily.RepeatInterval + appointmentDaysDuration));
|
|
currentDay = currentDay.AddDays(Math.Max(1, recurrence.Daily.RepeatInterval + appointmentDaysDuration)); // Changed for consistency
|
|
break;
|
|
|
|
case eDailyRecurrenceRepeat.WeekendDays:
|
|
while (true)
|
|
{
|
|
currentDay = currentDay.AddDays(1);
|
|
|
|
if (DateTimeHelper.IsWeekendDay(currentDay) == true)
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate && currentDay >= recurrence.RangeEndDate ||
|
|
recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && repeats >= recurrence.RangeNumberOfOccurrences)
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static bool IsIgnoredRecurrence(DateTime currentDay, AppointmentRecurrence recurrence)
|
|
{
|
|
if (recurrence.SkippedRecurrences.Count == 0) return false;
|
|
foreach (DateTime date in recurrence.SkippedRecurrences)
|
|
{
|
|
if (date.Date == currentDay.Date)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool IsRecurringOnDay(DateTime currentDay, DateTime rangeStart, DateTime rangeEnd, Appointment rootAppointment)
|
|
{
|
|
TimeSpan duration = rootAppointment.EndTime.Subtract(rootAppointment.StartTime);
|
|
DateTime startTime = rootAppointment.GetValidDateTime(new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, rootAppointment.StartTime.Hour, rootAppointment.StartTime.Minute, 0));
|
|
DateTime endTime = rootAppointment.GetValidDateTime(startTime.Add(duration));
|
|
|
|
DateTime localStartTime = rootAppointment.GetLocalDateTime(rootAppointment.GetUTCDateTime(startTime));
|
|
DateTime localEndTime = rootAppointment.GetLocalDateTime(rootAppointment.GetUTCDateTime(endTime));
|
|
|
|
// If time-zone is not used return false
|
|
//if (startTime == localStartTime) return currentDay >= rangeStart;
|
|
|
|
return DateTimeHelper.TimePeriodsOverlap(localStartTime, localEndTime, rangeStart, rangeEnd);
|
|
}
|
|
|
|
private DateTime GetAppointmentEndTime(DateTime currentDay, Appointment rootAppointment)
|
|
{
|
|
TimeSpan duration = rootAppointment.EndTime.Subtract(rootAppointment.StartTime);
|
|
DateTime startTime = rootAppointment.GetValidDateTime(new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, rootAppointment.StartTime.Hour, rootAppointment.StartTime.Minute, 0));
|
|
DateTime endTime = rootAppointment.GetValidDateTime(startTime.Add(duration));
|
|
|
|
return endTime;
|
|
}
|
|
|
|
private Appointment CreateRecurringAppointmentInstance(DateTime currentDay, Appointment rootAppointment)
|
|
{
|
|
Appointment app = rootAppointment.Copy();
|
|
TimeSpan duration = rootAppointment.EndTime.Subtract(rootAppointment.StartTime);
|
|
DateTime startTime = app.GetValidDateTime(new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, rootAppointment.StartTime.Hour, rootAppointment.StartTime.Minute, 0));
|
|
DateTime endTime = app.GetValidDateTime(startTime.Add(duration));
|
|
|
|
app.StartTime = startTime;
|
|
app.EndTime = endTime;
|
|
app.IsRecurringInstance = true;
|
|
app.RootAppointment = rootAppointment;
|
|
app.Tooltip = rootAppointment.Tooltip;
|
|
if (rootAppointment.Recurrence == null || !rootAppointment.Recurrence.IndependentVisibility)
|
|
app.Visible = rootAppointment.Visible;
|
|
|
|
foreach (Reminder reminder in app.Reminders)
|
|
{
|
|
//reminder.ReminderTime = app.StartTime.Add(reminder.ReminderTime.Subtract(rootAppointment.StartTime));
|
|
reminder.IsActive = true;
|
|
}
|
|
|
|
return app;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates Weekly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
|
/// </summary>
|
|
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
|
/// <param name="recurrence">Recurrence description, must be of Weekly recurrence type.</param>
|
|
/// <param name="startDate">Start date for generation.</param>
|
|
/// <param name="endDate">End date for generation.</param>
|
|
public void GenerateWeeklyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate)
|
|
{
|
|
Appointment app = recurrence.Appointment;
|
|
|
|
DateTime baseStartDate = app.EndTime.Date;
|
|
if (app.EndTime.Date > app.StartTime.Date && app.EndTime.TimeOfDay > app.StartTime.TimeOfDay && baseStartDate != DateTime.MinValue)
|
|
baseStartDate = baseStartDate.AddDays(1);
|
|
DateTime recurrenceStartDate = baseStartDate;
|
|
int repeatInterval = recurrence.Weekly.RepeatInterval;
|
|
|
|
if (RepeatsOnSingleDayOnly(recurrence.Weekly.RepeatOnDaysOfWeek) && !(app.StartTime.Date == startDate.Date && endDate.Date == startDate.Date))
|
|
{
|
|
recurrenceStartDate = baseStartDate.AddMilliseconds(-1).AddDays((repeatInterval) * 7 /*+ (recurrence.Weekly.RepeatInterval > 1 ? (8 - (int)baseStartDate.DayOfWeek) : 1)*/).Date;
|
|
if (recurrenceStartDate.DayOfWeek != GetDayOfWeek(recurrence.Weekly.RepeatOnDaysOfWeek))
|
|
{
|
|
DayOfWeek dayOfWeek = GetDayOfWeek(recurrence.Weekly.RepeatOnDaysOfWeek);
|
|
while (recurrenceStartDate.DayOfWeek != dayOfWeek)
|
|
recurrenceStartDate = recurrenceStartDate.AddDays(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (app.EndTime.Date > app.StartTime.Date && app.EndTime.TimeOfDay < app.StartTime.TimeOfDay)
|
|
recurrenceStartDate = app.StartTime.Date.AddDays(1);
|
|
else
|
|
recurrenceStartDate = app.EndTime.Date.AddDays(1);
|
|
}
|
|
|
|
recurrenceStartDate = DateTimeHelper.MaxDate(recurrence.RecurrenceStartDate, recurrenceStartDate);
|
|
|
|
if (recurrenceStartDate > endDate) return;
|
|
|
|
int totalRecurrences = 0;
|
|
// Check the range first
|
|
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate)
|
|
{
|
|
if (startDate > recurrence.RangeEndDate) return;
|
|
}
|
|
else if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && repeatInterval == 1)
|
|
{
|
|
int totalDays = (int)Math.Ceiling(endDate.Subtract(recurrenceStartDate).TotalDays);
|
|
int occurrencesPerWeek = GetNumberOfDays(recurrence.Weekly.RepeatOnDaysOfWeek);
|
|
if (occurrencesPerWeek == 0)
|
|
throw new ArgumentException("Weekly recurrence must have at least single day selected using RepeatOnDaysOfWeek property.");
|
|
totalRecurrences = DateTimeHelper.TotalNumberOfDays(recurrenceStartDate, endDate, recurrence.Weekly.RepeatOnDaysOfWeek);
|
|
if (totalRecurrences > recurrence.RangeNumberOfOccurrences) return;
|
|
}
|
|
|
|
bool countTotalRecurrences = false;
|
|
if (repeatInterval > 1 && recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences)
|
|
countTotalRecurrences = true;
|
|
|
|
bool spansDays = app.StartTime.Day != app.EndTime.Day && endDate.Subtract(startDate).TotalDays <= 1;
|
|
|
|
DateTime currentDay = recurrenceStartDate;// DateTimeHelper.MaxDate(recurrenceStartDate, startDate);
|
|
while (currentDay <= endDate)
|
|
{
|
|
if ((currentDay >= startDate || spansDays && GetAppointmentEndTime(currentDay, app) >= startDate) && DateTimeHelper.HasDay(currentDay.DayOfWeek, recurrence.Weekly.RepeatOnDaysOfWeek) &&
|
|
!IsIgnoredRecurrence(currentDay, recurrence))
|
|
{
|
|
subsetCollection.Add(CreateRecurringAppointmentInstance(currentDay, app));
|
|
totalRecurrences++;
|
|
}
|
|
else if (countTotalRecurrences && currentDay < startDate && DateTimeHelper.HasDay(currentDay.DayOfWeek, recurrence.Weekly.RepeatOnDaysOfWeek)
|
|
&& !IsIgnoredRecurrence(currentDay, recurrence))
|
|
{
|
|
totalRecurrences++;
|
|
if (totalRecurrences >= recurrence.RangeNumberOfOccurrences)
|
|
return;
|
|
}
|
|
|
|
if (currentDay.DayOfWeek != DayOfWeek.Sunday)
|
|
{
|
|
while (currentDay.DayOfWeek != DayOfWeek.Sunday)
|
|
{
|
|
currentDay = currentDay.AddDays(1);
|
|
if (currentDay > endDate || recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate && currentDay > recurrence.RangeEndDate)
|
|
break;
|
|
if ((currentDay >= startDate || spansDays && GetAppointmentEndTime(currentDay, app) >= startDate) && DateTimeHelper.HasDay(currentDay.DayOfWeek, recurrence.Weekly.RepeatOnDaysOfWeek)
|
|
&& !IsIgnoredRecurrence(currentDay, recurrence))
|
|
{
|
|
subsetCollection.Add(CreateRecurringAppointmentInstance(currentDay, app));
|
|
}
|
|
else if (countTotalRecurrences && currentDay < startDate && DateTimeHelper.HasDay(currentDay.DayOfWeek, recurrence.Weekly.RepeatOnDaysOfWeek)
|
|
&& !IsIgnoredRecurrence(currentDay, recurrence))
|
|
{
|
|
totalRecurrences++;
|
|
if (totalRecurrences >= recurrence.RangeNumberOfOccurrences)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
currentDay = currentDay.AddDays(1 + (repeatInterval - 1) * 7);
|
|
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate && currentDay > recurrence.RangeEndDate ||
|
|
recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && totalRecurrences > recurrence.RangeNumberOfOccurrences)
|
|
break;
|
|
}
|
|
}
|
|
|
|
private DayOfWeek GetDayOfWeek(eDayOfWeekRecurrence d)
|
|
{
|
|
if (d == eDayOfWeekRecurrence.Monday)
|
|
return DayOfWeek.Monday;
|
|
else if (d == eDayOfWeekRecurrence.Tuesday)
|
|
return DayOfWeek.Tuesday;
|
|
else if (d == eDayOfWeekRecurrence.Wednesday)
|
|
return DayOfWeek.Wednesday;
|
|
else if (d == eDayOfWeekRecurrence.Thursday)
|
|
return DayOfWeek.Thursday;
|
|
else if (d == eDayOfWeekRecurrence.Friday)
|
|
return DayOfWeek.Friday;
|
|
else if (d == eDayOfWeekRecurrence.Saturday)
|
|
return DayOfWeek.Saturday;
|
|
else
|
|
return DayOfWeek.Sunday;
|
|
}
|
|
|
|
private bool RepeatsOnSingleDayOnly(eDayOfWeekRecurrence dow)
|
|
{
|
|
return dow == eDayOfWeekRecurrence.Monday || dow == eDayOfWeekRecurrence.Tuesday ||
|
|
dow == eDayOfWeekRecurrence.Wednesday || dow == eDayOfWeekRecurrence.Thursday ||
|
|
dow == eDayOfWeekRecurrence.Friday || dow == eDayOfWeekRecurrence.Saturday || dow == eDayOfWeekRecurrence.Sunday;
|
|
}
|
|
|
|
private int GetNumberOfDays(eDayOfWeekRecurrence daysOfWeek)
|
|
{
|
|
int days = 0;
|
|
if ((daysOfWeek & eDayOfWeekRecurrence.Friday) != 0)
|
|
days++;
|
|
if ((daysOfWeek & eDayOfWeekRecurrence.Monday) != 0)
|
|
days++;
|
|
if ((daysOfWeek & eDayOfWeekRecurrence.Saturday) != 0)
|
|
days++;
|
|
if ((daysOfWeek & eDayOfWeekRecurrence.Sunday) != 0)
|
|
days++;
|
|
if ((daysOfWeek & eDayOfWeekRecurrence.Thursday) != 0)
|
|
days++;
|
|
if ((daysOfWeek & eDayOfWeekRecurrence.Tuesday) != 0)
|
|
days++;
|
|
if ((daysOfWeek & eDayOfWeekRecurrence.Wednesday) != 0)
|
|
days++;
|
|
|
|
return days;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates Monthly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
|
/// </summary>
|
|
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
|
/// <param name="recurrence">Recurrence description, must be of Monthly recurrence type.</param>
|
|
/// <param name="startDate">Start date for generation.</param>
|
|
/// <param name="endDate">End date for generation.</param>
|
|
public void GenerateMonthlyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate)
|
|
{
|
|
Appointment app = recurrence.Appointment;
|
|
DateTime recurrenceStartDate = DateTimeHelper.MaxDate(recurrence.RecurrenceStartDate, app.EndTime/*.AddDays(1).Date*/);
|
|
if (recurrenceStartDate > endDate) return;
|
|
|
|
int totalRecurrences = 0;
|
|
// Check the range first
|
|
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate)
|
|
{
|
|
if (startDate > recurrence.RangeEndDate) return;
|
|
}
|
|
else if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && recurrence.RangeNumberOfOccurrences > 0)
|
|
{
|
|
int count = recurrence.RangeNumberOfOccurrences + 1;
|
|
DateTime testDate = DateTimeHelper.BeginningOfDay(recurrenceStartDate);
|
|
DateTime startDateOnly = DateTimeHelper.BeginningOfDay(startDate);
|
|
while (count > 0 && testDate < startDateOnly)
|
|
{
|
|
int repeatInterval = recurrence.Monthly.RepeatInterval;
|
|
while (repeatInterval > 0)
|
|
{
|
|
testDate = GetNextMonthlyRecurrence(recurrence, testDate);
|
|
repeatInterval--;
|
|
}
|
|
if (testDate < startDate)
|
|
totalRecurrences++;
|
|
count--;
|
|
}
|
|
if (count == 0) return;
|
|
}
|
|
|
|
DateTime currentDate = recurrenceStartDate;// DateTimeHelper.MaxDate(recurrenceStartDate, startDate).AddMonths(-1);
|
|
|
|
do
|
|
{
|
|
int repeatCount = recurrence.Monthly.RepeatInterval;
|
|
do
|
|
{
|
|
repeatCount--;
|
|
currentDate = GetNextMonthlyRecurrence(recurrence, currentDate);
|
|
} while (repeatCount > 0);
|
|
|
|
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate && currentDate > recurrence.RangeEndDate ||
|
|
recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && totalRecurrences >= recurrence.RangeNumberOfOccurrences)
|
|
break;
|
|
|
|
if (currentDate >= startDate && currentDate <= endDate && !IsIgnoredRecurrence(currentDate, recurrence))
|
|
{
|
|
subsetCollection.Add(CreateRecurringAppointmentInstance(currentDate, app));
|
|
totalRecurrences++;
|
|
}
|
|
} while (currentDate <= endDate);
|
|
}
|
|
|
|
private DateTime GetNextMonthlyRecurrence(AppointmentRecurrence recurrence, DateTime startDate)
|
|
{
|
|
MonthlyRecurrenceSettings monthly = recurrence.Monthly;
|
|
return GetNextMonthlyRecurrence(startDate, monthly.RepeatOnRelativeDayInMonth,
|
|
monthly.RelativeDayOfWeek, monthly.RepeatOnDayOfMonth, monthly.RepeatOnMonths);
|
|
}
|
|
|
|
private static void AdjustForRepeatOnMonths(ref DateTime dt, eMonthRecurrence repeatOnMonths)
|
|
{
|
|
while (((int)Math.Pow(2, (dt.Month - 1)) & (int)repeatOnMonths) == 0)
|
|
dt = dt.AddMonths(1);
|
|
}
|
|
private static DateTime GetNextMonthlyRecurrence(DateTime startDate, eRelativeDayInMonth repeatOnRelativeDayInMonth, DayOfWeek relativeDayOfWeek, int repeatOnDayOfMonth, eMonthRecurrence repeatOnMonths)
|
|
{
|
|
Calendar cal = GetCalendar();
|
|
|
|
if (repeatOnRelativeDayInMonth == eRelativeDayInMonth.None)
|
|
{
|
|
DateTime dt = startDate.AddDays(cal.GetDaysInMonth(startDate.Year, startDate.Month) - startDate.Day + 1);
|
|
if (repeatOnMonths != eMonthRecurrence.All) AdjustForRepeatOnMonths(ref dt, repeatOnMonths); // Adjust for repeat on Months
|
|
return dt.AddDays(Math.Min(cal.GetDaysInMonth(dt.Year, dt.Month), repeatOnDayOfMonth) - 1);
|
|
}
|
|
else
|
|
{
|
|
if (repeatOnRelativeDayInMonth == eRelativeDayInMonth.First)
|
|
{
|
|
DateTime dt = startDate.AddDays(cal.GetDaysInMonth(startDate.Year, startDate.Month) - startDate.Day + 1);
|
|
if (repeatOnMonths != eMonthRecurrence.All) AdjustForRepeatOnMonths(ref dt, repeatOnMonths); // Adjust for repeat on Months
|
|
while (dt.DayOfWeek != relativeDayOfWeek)
|
|
dt = dt.AddDays(1);
|
|
return dt;
|
|
}
|
|
else if (repeatOnRelativeDayInMonth == eRelativeDayInMonth.Last)
|
|
{
|
|
DateTime dt = startDate.AddDays(cal.GetDaysInMonth(startDate.Year, startDate.Month) - startDate.Day + 1);
|
|
if (repeatOnMonths != eMonthRecurrence.All) AdjustForRepeatOnMonths(ref dt, repeatOnMonths); // Adjust for repeat on Months
|
|
dt = dt.AddDays(cal.GetDaysInMonth(dt.Year, dt.Month) - 1);
|
|
while (dt.DayOfWeek != relativeDayOfWeek)
|
|
dt = dt.AddDays(-1);
|
|
return dt;
|
|
}
|
|
else
|
|
{
|
|
// Second, third and forth
|
|
int relCount = 2;
|
|
if (repeatOnRelativeDayInMonth == eRelativeDayInMonth.Third)
|
|
relCount = 3;
|
|
else if (repeatOnRelativeDayInMonth == eRelativeDayInMonth.Fourth)
|
|
relCount = 4;
|
|
DateTime dt = startDate.AddDays(cal.GetDaysInMonth(startDate.Year, startDate.Month) - startDate.Day);
|
|
if (repeatOnMonths != eMonthRecurrence.All) AdjustForRepeatOnMonths(ref dt, repeatOnMonths); // Adjust for repeat on Months
|
|
while (relCount > 0)
|
|
{
|
|
dt = dt.AddDays(1);
|
|
if (dt.DayOfWeek == relativeDayOfWeek)
|
|
relCount--;
|
|
}
|
|
return dt;
|
|
}
|
|
}
|
|
throw new InvalidOperationException("Could not find the next relative date for monthly recurrence starting on " + startDate.ToString() + " RelativeDayInMonth=" + repeatOnRelativeDayInMonth.ToString() + " RelativeDayOfWeek=" + relativeDayOfWeek.ToString());
|
|
}
|
|
|
|
private static Calendar GetCalendar()
|
|
{
|
|
return CultureInfo.CurrentCulture.Calendar;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates Yearly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
|
/// </summary>
|
|
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
|
/// <param name="recurrence">Recurrence description, must be of Monthly recurrence type.</param>
|
|
/// <param name="startDate">Start date for generation.</param>
|
|
/// <param name="endDate">End date for generation.</param>
|
|
public void GenerateYearlyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate)
|
|
{
|
|
Appointment app = recurrence.Appointment;
|
|
DateTime recurrenceStartDate = DateTimeHelper.MaxDate(recurrence.RecurrenceStartDate, app.EndTime.AddDays(1).Date);
|
|
if (recurrenceStartDate > endDate) return;
|
|
|
|
if (!(startDate.Month <= recurrence.Yearly.RepeatOnMonth && endDate.Month >= recurrence.Yearly.RepeatOnMonth) && endDate.Subtract(startDate).TotalDays < 28)
|
|
return;
|
|
int totalRecurrences = 0;
|
|
// Check the range first
|
|
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate)
|
|
{
|
|
if (startDate > recurrence.RangeEndDate) return;
|
|
}
|
|
else if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && recurrence.RangeNumberOfOccurrences > 0 && recurrenceStartDate < startDate)
|
|
{
|
|
int count = recurrence.RangeNumberOfOccurrences + 1;
|
|
DateTime testDate = DateTimeHelper.BeginningOfDay(recurrenceStartDate.AddDays(-(recurrenceStartDate.Day - 1))).AddMonths(-1);
|
|
if (testDate < app.EndTime)
|
|
testDate = app.EndTime.Date.AddDays(1);
|
|
DateTime startDateOnly = DateTimeHelper.BeginningOfDay(startDate);
|
|
while (count > 0 && testDate < startDateOnly)
|
|
{
|
|
testDate = GetNextYearlyRecurrence(recurrence, testDate);
|
|
if (testDate >= startDateOnly) break;
|
|
count--;
|
|
totalRecurrences++;
|
|
}
|
|
if (count == 0) return;
|
|
}
|
|
|
|
DateTime currentDate = DateTimeHelper.MaxDate(startDate, recurrenceStartDate).AddDays(-1);
|
|
if (recurrence.Yearly.RepeatInterval > 1)
|
|
currentDate = recurrenceStartDate;
|
|
do
|
|
{
|
|
currentDate = GetNextYearlyRecurrence(recurrence, currentDate);
|
|
|
|
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate && currentDate > recurrence.RangeEndDate ||
|
|
recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && totalRecurrences >= recurrence.RangeNumberOfOccurrences)
|
|
break;
|
|
|
|
if (currentDate >= startDate && currentDate <= endDate && !IsIgnoredRecurrence(currentDate, recurrence))
|
|
{
|
|
subsetCollection.Add(CreateRecurringAppointmentInstance(currentDate, app));
|
|
totalRecurrences++;
|
|
}
|
|
} while (currentDate <= endDate);
|
|
}
|
|
internal static DateTime GetNextYearlyRecurrence(AppointmentRecurrence recurrence, DateTime startDate)
|
|
{
|
|
YearlyRecurrenceSettings yearly = recurrence.Yearly;
|
|
if (yearly.RepeatInterval > 1)
|
|
{
|
|
DateTime currentDate = startDate;
|
|
for (int i = 0; i < yearly.RepeatInterval; i++)
|
|
{
|
|
currentDate = GetNextSingleYearlyRecurrence(recurrence, currentDate);
|
|
}
|
|
return currentDate;
|
|
}
|
|
else
|
|
{
|
|
return GetNextSingleYearlyRecurrence(recurrence, startDate);
|
|
}
|
|
}
|
|
internal static DateTime GetNextSingleYearlyRecurrence(AppointmentRecurrence recurrence, DateTime startDate)
|
|
{
|
|
YearlyRecurrenceSettings yearly = recurrence.Yearly;
|
|
Calendar cal = GetCalendar();
|
|
|
|
if (startDate.Month < yearly.RepeatOnMonth)
|
|
{
|
|
startDate = startDate.AddMonths((yearly.RepeatOnMonth - startDate.Month) - 1);
|
|
return GetNextMonthlyRecurrence(startDate, yearly.RepeatOnRelativeDayInMonth, yearly.RelativeDayOfWeek, yearly.RepeatOnDayOfMonth, eMonthRecurrence.All);
|
|
}
|
|
else if (startDate.Month == yearly.RepeatOnMonth)
|
|
{
|
|
DateTime refDate = startDate.AddDays(-(startDate.Day));
|
|
DateTime ret = GetNextMonthlyRecurrence(refDate, yearly.RepeatOnRelativeDayInMonth, yearly.RelativeDayOfWeek, yearly.RepeatOnDayOfMonth, eMonthRecurrence.All);
|
|
if (ret > startDate) return ret;
|
|
}
|
|
|
|
// Forward to next year and right month
|
|
startDate = startDate.AddDays(cal.GetDaysInYear(startDate.Year) - startDate.DayOfYear).AddMonths(yearly.RepeatOnMonth - 1);
|
|
return GetNextMonthlyRecurrence(startDate, yearly.RepeatOnRelativeDayInMonth, yearly.RelativeDayOfWeek, yearly.RepeatOnDayOfMonth, eMonthRecurrence.All);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
internal interface IRecurrenceGenerator
|
|
{
|
|
/// <summary>
|
|
/// Generates Daily recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
|
/// </summary>
|
|
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
|
/// <param name="recurrence">Recurrence description, must be of Daily recurrence type.</param>
|
|
/// <param name="startDate">Start date for generation.</param>
|
|
/// <param name="endDate">End date for generation.</param>
|
|
void GenerateDailyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate);
|
|
|
|
/// <summary>
|
|
/// Generates Weekly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
|
/// </summary>
|
|
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
|
/// <param name="recurrence">Recurrence description, must be of Weekly recurrence type.</param>
|
|
/// <param name="startDate">Start date for generation.</param>
|
|
/// <param name="endDate">End date for generation.</param>
|
|
void GenerateWeeklyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate);
|
|
|
|
/// <summary>
|
|
/// Generates Monthly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
|
/// </summary>
|
|
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
|
/// <param name="recurrence">Recurrence description, must be of Monthly recurrence type.</param>
|
|
/// <param name="startDate">Start date for generation.</param>
|
|
/// <param name="endDate">End date for generation.</param>
|
|
void GenerateMonthlyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate);
|
|
|
|
/// <
|
|
/// summary>
|
|
/// Generates Yearly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
|
/// </summary>
|
|
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
|
/// <param name="recurrence">Recurrence description, must be of Monthly recurrence type.</param>
|
|
/// <param name="startDate">Start date for generation.</param>
|
|
/// <param name="endDate">End date for generation.</param>
|
|
void GenerateYearlyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate);
|
|
}
|
|
}
|
|
#endif
|
|
|