Click here to Skip to main content
15,886,724 members
Articles / Programming Languages / C#

Recurring Date Generator with Pattern Coding

Rate me:
Please Sign up or sign in to vote.
4.95/5 (51 votes)
4 Sep 2007CPOL4 min read 146.2K   6.9K   98  
Create recurring dates using a user-defined pattern. Create recurring dates from a coded value that defines what the pattern should be.
using System;

namespace RecurrenceGenerator
{
    #region Class-specific Enums
    /// <summary>
    /// The Regeneration type. 
    /// </summary>
    public enum WeeklyRegenType { NotSet = -1, OnEveryXWeeks};
#endregion //Class-specific Enums

    #region Class-specific Structs
    public struct SelectedDayOfWeekValues
    {
        public bool Sunday;
        public bool Monday;
        public bool Tuesday;
        public bool Wednesday;
        public bool Thursday;
        public bool Friday;
        public bool Saturday;
    }
#endregion //Class-specific Structs

    public class WeeklyRecurrenceSettings : RecurrenceSettings
    {
        #region Constructors
        /// <summary>
        /// Get dates by Start date only. This is for no ending date values.
        /// </summary>
        /// <param name="startDate"></param>
        public WeeklyRecurrenceSettings(DateTime startDate) : base(startDate) { }
        /// <summary>
        /// Get dates by Start and End date boundries.
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        public WeeklyRecurrenceSettings(DateTime startDate, DateTime endDate) : base(startDate, endDate) { }
        /// <summary>
        /// Get dates by Start date and number of occurrences.
        /// </summary>
        /// <param name="startDate"></param>
        /// <param name="numberOfOccurrences"></param>
        public WeeklyRecurrenceSettings(DateTime startDate, int numberOfOccurrences) : base(startDate, numberOfOccurrences) { }
        #endregion

        #region Private Fields
        
        WeeklyRegenType regenType = WeeklyRegenType.OnEveryXWeeks;
        SelectedDayOfWeekValues selectedDays;
        int regenEveryXWeeks;
        bool getNextDateValue;
        DateTime nextDateValue;
#endregion //Private Fields

        #region Public GetValues
        /// <summary>
        /// Get day values. This overload is for every x-weeks.
        /// </summary>
        /// <param name="regenEveryXDays">Interval of weeks. Every x-weeks.</param>
        /// <returns>RecurrenceValues</returns>
        public RecurrenceValues GetValues(int regenEveryXWeeks, SelectedDayOfWeekValues selectedDays)
        {
            this.regenEveryXWeeks = regenEveryXWeeks;
            regenType = WeeklyRegenType.OnEveryXWeeks;
            this.selectedDays = selectedDays;
            return GetValues();
        }
#endregion //Public GetValues

        internal static RecurrenceInfo GetFriendlyRecurrenceInfo(string seriesInfo)
        {
            RecurrenceInfo info = new RecurrenceInfo();
            EndDateType endType = EndDateType.NotDefined;
            DateTime endDateValue = DateTime.MinValue;
            int noOccurrences;

            // Exit if not a Weekly seriesInfo type
            if (!seriesInfo.StartsWith("W"))
                return null;

            info.SetSeriesInfo(seriesInfo);
            info.SetRecurrenceType(RecurrenceType.Weekly);
            
            // FORMATTING DEFINITIONS
            //  Y = Yearly Designator
            //  |       0 = Start Date (8 chars)
            //  |       |        1 = End Date (8 chars)
            //  |       |        |        2 = Number of occurrences (4 chars)
            //  |       |        |        |      3 = Regeneration Type (1 char)
            //  |       |        |        |      |    4 = End date type (1 char)
            //  |       |        |        |      |    |      5 = Regenerate on Sunday
            //  |       |        |        |      |    |      |       6 = Regenerate on Monday
            //  |       |        |        |      |    |      |       |       7 = Regenerate on Tuesday
            //  |       |        |        |      |    |      |       |       |       8 = Regenerate on Wednesday
            //  |       |        |        |      |    |      |       |       |       |      9 = Regenerate on Thursday
            //  |       |        |        |      |    |      |       |       |       |      |      10 = Regenerate on Friday
            //  |       |        |        |      |    |      |       |       |       |      |       |      11 = Regenerate on Saturday
            //  |       |        |        |      |    |      |       |       |       |      |       |      |        12  Regen Every x weeks
            //  |       |        |        |      |    |      |       |       |       |      |       |      |         |
            // [0]    [1-8]    [9-16]  [17-20]  [21] [22]  [23]    [24]    [25]    [26]   [27]     [28]   [29]      [30-32]
            //  W   20071231  20171231   0000    1     1     T       T      F        F      F       F      F        000 
            string startDate = seriesInfo.Substring(1, 8);

            DateTime dtStartDate = new DateTime(int.Parse(startDate.Substring(0, 4)), int.Parse(startDate.Substring(4, 2)), int.Parse(startDate.Substring(6, 2)));
            string endDate = seriesInfo.Substring(9, 8);
            SelectedDayOfWeekValues selectedDays = new SelectedDayOfWeekValues();
            string occurrences = seriesInfo.Substring(17, 4);
            string weeklyRegenType = seriesInfo.Substring(21, 1);
            string endDateType = seriesInfo.Substring(22, 1);
            int regenXWeeks = int.Parse(seriesInfo.Substring(30, 3));

            endType = (EndDateType)(int.Parse(endDateType));
            noOccurrences = int.Parse(occurrences);

            selectedDays.Sunday = (seriesInfo.Substring(23, 1) == "Y") ? true : false;
            selectedDays.Monday = (seriesInfo.Substring(24, 1) == "Y") ? true : false;
            selectedDays.Tuesday = (seriesInfo.Substring(25, 1) == "Y") ? true : false;
            selectedDays.Wednesday = (seriesInfo.Substring(26, 1) == "Y") ? true : false;
            selectedDays.Thursday = (seriesInfo.Substring(27, 1) == "Y") ? true : false;
            selectedDays.Friday = (seriesInfo.Substring(28, 1) == "Y") ? true : false;
            selectedDays.Saturday = (seriesInfo.Substring(29, 1) == "Y") ? true : false;

            endType = (EndDateType)(int.Parse(endDateType));
            noOccurrences = int.Parse(occurrences);

            // Get the EndDate before any modifications on it are performed
            if (endType == EndDateType.SpecificDate)
                endDateValue = new DateTime(int.Parse(endDate.Substring(0, 4)), int.Parse(endDate.Substring(4, 2)), int.Parse(endDate.Substring(6, 2)));

            info.SetEndDateType(endType);
            // Determine the Constructor by the type of End Date.
            // All constructors start with a Start date at a minimum.
            // Determine the Constructor by the type of End Date.
            // All constructors start with a Start date at a minimum.
            switch (endType)
            {
                case EndDateType.NumberOfOccurrences:
                    info.SetStartDate(dtStartDate);
                    info.SetNumberOfOccurrences(noOccurrences);
                    break;

                case EndDateType.SpecificDate:
                    info.SetStartDate(dtStartDate);
                    info.SetEndDate(endDateValue);
                    break;

                case EndDateType.NoEndDate:
                    info.SetStartDate(dtStartDate);
                    break;
            }

            info.SetWeeklyRegenType((WeeklyRegenType)(int.Parse(weeklyRegenType)));
            // Determine the Type of dates to get, specific, custom, etc.
            switch (info.WeeklyRegenType)
            {
                case WeeklyRegenType.OnEveryXWeeks:
                    info.SetSelectedDayOfWeekValues(selectedDays);
                    info.SetRegenEveryXWeeks(regenXWeeks);
                    break;

                case WeeklyRegenType.NotSet:

                    break;
            }

            return info;


        }


        #region Internal GetValues
        /// <summary>
        ///     Final overloaded function that gets the Recurrence Values. 
        ///     This is called from the RecurrenceHelper staic methods only.
        /// </summary>
        /// <returns>
        ///     A RecurrenceGenerator.RecurrenceValues value...
        /// </returns>
        internal override RecurrenceValues GetValues()
        {
            return GetRecurrenceValues();
        }
        internal override RecurrenceValues GetValues(DateTime startDate, DateTime endDate)
        {
            base.StartDate = startDate;
            base.EndDate = endDate;
            // Change the end type to End Date as this original series info
            // may have been set to number of occurrences.
            base.endDateType = EndDateType.SpecificDate;
            return GetRecurrenceValues();
        }

        internal override RecurrenceValues GetValues(DateTime startDate, int numberOfOccurrences)
        {
            base.NumberOfOccurrences = numberOfOccurrences;
            base.StartDate = startDate;
            // Change the end type to number of occurrences. 
            // This must be set because the original starting Series Info may
            // be set to have an End Date type.
            base.endDateType = EndDateType.NumberOfOccurrences;
            return GetRecurrenceValues();
        }
        RecurrenceValues GetRecurrenceValues()
        {
            RecurrenceValues values = null;
            switch (RegenType)
            {
                case WeeklyRegenType.OnEveryXWeeks:
                    values = GetEveryXWeeksValues();
                    break;

            }
            if (values.Values.Count > 0)
            {
                values.SetStartDate(values.Values[0]);

                // Get the end date if not open-ended
                if (base.TypeOfEndDate != EndDateType.NoEndDate)
                    values.SetEndDate(values.Values[values.Values.Count - 1]);
            }
            // Set the Series information that's used to get the next date
            // values for no ending dates.
            values.SetSeriesInfo(GetSeriesInfo());

            return values;
        }
#endregion //Internal GetValues

        #region Internal Procedures

        internal static string GetPatternDefinition(string seriesInfo)
        {
            string returnValue = string.Empty;
            returnValue =
            " WEEKLY FORMATTING DEFINITIONS \r\n" +
            "  Y = Yearly Designator \r\n" +
            "  |       0 = Start Date (8 chars) \r\n" +
            "  |       |        1 = End Date (8 chars) \r\n" +
            "  |       |        |        2 = Number of occurrences (4 chars) \r\n" +
            "  |       |        |        |      3 = Regeneration Type (1 char) \r\n" +
            "  |       |        |        |      |    4 = End date type (1 char) \r\n" +
            "  |       |        |        |      |    |      5 = Regenerate on Sunday \r\n" +
            "  |       |        |        |      |    |      |       6 = Regenerate on Monday \r\n" +
            "  |       |        |        |      |    |      |       |       7 = Regenerate on Tuesday \r\n" +
            "  |       |        |        |      |    |      |       |       |       8 = Regenerate on Wednesday \r\n" +
            "  |       |        |        |      |    |      |       |       |       |      9 = Regenerate on Thursday \r\n" +
            "  |       |        |        |      |    |      |       |       |       |      |      10 = Regenerate on Friday \r\n" +
            "  |       |        |        |      |    |      |       |       |       |      |       |      11 = Regenerate on Saturday \r\n" +
            "  |       |        |        |      |    |      |       |       |       |      |       |      |        12  Regen Every x weeks \r\n" +
            "  |       |        |        |      |    |      |       |       |       |      |       |      |         | \r\n" +
            " [0]    [1-8]    [9-16]  [17-20]  [21] [22]  [23]    [24]    [25]    [26]   [27]     [28]   [29]      [30-32] \r\n" +
            string.Format("  W   {0}  {1}   {2}    {3}     {4}     {5}       {6}       {7}      {8}       {9}       {10}      {11}         {12}\r\n", seriesInfo.Substring(1, 8), seriesInfo.Substring(9, 8), seriesInfo.Substring(17, 4), seriesInfo.Substring(21, 1), seriesInfo.Substring(22, 1), seriesInfo.Substring(23, 1), seriesInfo.Substring(24, 1), seriesInfo.Substring(25, 1), seriesInfo.Substring(26, 1), seriesInfo.Substring(27, 1), seriesInfo.Substring(28, 1), seriesInfo.Substring(29, 1), seriesInfo.Substring(30, 3));
            return returnValue;
        }
        /// <summary>
        /// Set the values in preperation for getting the Next date in the series.
        /// </summary>
        /// <param name="regenEveryXWeeks">Value to regenerate the dates every x-weeks</param>
        /// <param name="selectedDays">Struct of days selected for the week.</param>
        void SetValues(int regenEveryXWeeks, SelectedDayOfWeekValues selectedDays)
        {
            this.regenEveryXWeeks = regenEveryXWeeks;
            regenType = WeeklyRegenType.OnEveryXWeeks;
            this.selectedDays = selectedDays;
        }

        /// <summary>
        ///     Get the next date
        /// </summary>
        /// <param name="currentDate" type="System.DateTime">
        ///     <para>
        ///         
        ///     </para>
        /// </param>
        /// <returns>
        ///     A System.DateTime value...
        /// </returns>
        internal override DateTime GetNextDate(DateTime currentDate)
        {
            getNextDateValue = true;
            nextDateValue = currentDate;
            // Now get the values and return the last date found.
            RecurrenceValues values = GetValues();
            return values.EndDate;
        }

        internal static WeeklyRecurrenceSettings GetRecurrenceSettings(string seriesInfo)
        {
            return GetRecurrenceSettings(seriesInfo, -1, DateTime.MinValue);
        }
        internal static WeeklyRecurrenceSettings GetRecurrenceSettings(DateTime modifiedStartDate, string seriesInfo)
        {
            return GetRecurrenceSettings(seriesInfo, -1, modifiedStartDate, DateTime.MinValue);
        }

        internal static WeeklyRecurrenceSettings GetRecurrenceSettings(string seriesInfo, int modifiedOccurrencesValue)
        {
            return GetRecurrenceSettings(seriesInfo, modifiedOccurrencesValue, DateTime.MinValue);
        }
        internal static WeeklyRecurrenceSettings GetRecurrenceSettings(string seriesInfo, DateTime modifiedStartDate, int modifiedOccurrencesValue)
        {
            return GetRecurrenceSettings(seriesInfo, modifiedOccurrencesValue, modifiedStartDate, DateTime.MinValue);
        }

        internal static WeeklyRecurrenceSettings GetRecurrenceSettings(string seriesInfo, DateTime modifiedEndDate)
        {
            return GetRecurrenceSettings(seriesInfo, -1, modifiedEndDate);
        }
        internal static WeeklyRecurrenceSettings GetRecurrenceSettings(string seriesInfo, DateTime modifiedStartDate, DateTime modifiedEndDate)
        {
            return GetRecurrenceSettings(seriesInfo, -1, modifiedStartDate, modifiedEndDate);
        }

        static WeeklyRecurrenceSettings GetRecurrenceSettings(string seriesInfo, int modifiedOccurrencesValue, DateTime modifiedStartDate, DateTime modifiedEndDate)
        {
            WeeklyRecurrenceSettings settings = null;
            RecurrenceInfo info = WeeklyRecurrenceSettings.GetFriendlyRecurrenceInfo(seriesInfo);

            // Check to see if this is to modify the SeriesInfo and run as endtype for occurrences
            if (modifiedOccurrencesValue != -1)
            {
                info.SetEndDateType(EndDateType.NumberOfOccurrences);
                info.SetNumberOfOccurrences(modifiedOccurrencesValue);
            }
            // Check to see if this is to modify the EndDate and run as endType for EndDate
            if (modifiedEndDate != DateTime.MinValue)
            {
                info.SetEndDateType(EndDateType.SpecificDate);
                info.SetEndDate(modifiedEndDate);
            }

            // Determine the Constructor by the type of End Date.
            // All constructors start with a Start date at a minimum.
            switch (info.EndDateType)
            {
                case EndDateType.NumberOfOccurrences:
                    settings = new WeeklyRecurrenceSettings(modifiedStartDate, info.NumberOfOccurrences);
                    break;

                case EndDateType.SpecificDate:
                    settings = new WeeklyRecurrenceSettings(modifiedStartDate, info.EndDate.Value);
                    break;

                case EndDateType.NoEndDate:
                    settings = new WeeklyRecurrenceSettings(modifiedStartDate);
                    break;
            }


            // Determine the Type of dates to get, specific, custom, etc.
            switch (info.WeeklyRegenType)
            {
                case WeeklyRegenType.OnEveryXWeeks:
                    settings.SetValues(info.WeeklyRegenEveryXWeeks, info.WeeklySelectedDays);
                    break;

                case WeeklyRegenType.NotSet:

                    break;
            }

            return settings;

        }

        static WeeklyRecurrenceSettings GetRecurrenceSettings(string seriesInfo, int modifiedOccurrencesValue,DateTime modifiedEndDate)
        {
            RecurrenceInfo info = WeeklyRecurrenceSettings.GetFriendlyRecurrenceInfo(seriesInfo);

            return GetRecurrenceSettings(seriesInfo, modifiedOccurrencesValue, info.StartDate, modifiedEndDate);
        }
        
#endregion //Internal Procedures
        
        #region Private Procedures
        /// <summary>
        /// Get the Series information that's used to get dates at a later
        /// date. This is passed into the RecurrenceHelper to get date values.
        /// Most likely used for non-ending dates.
        /// </summary>
        /// <returns></returns>
        string GetSeriesInfo()
        {
            string info = string.Empty;
            string endDate = "ZZZZZZZZ"; // Default for no ending date.
            string occurrences = base.NumberOfOccurrences.ToString("0000");
            string weeklyRegenType = ((int)regenType).ToString("0");
            string endDateType = ((int)base.TypeOfEndDate).ToString("0");
            string regenXWeeks = regenEveryXWeeks.ToString("000");
            string daySelectedSunday = selectedDays.Sunday ? "Y" : "N";
            string daySelectedMonday = selectedDays.Monday ? "Y" : "N";
            string daySelectedTuesday = selectedDays.Tuesday ? "Y" : "N";
            string daySelectedWednesday = selectedDays.Wednesday ? "Y" : "N";
            string daySelectedThursday = selectedDays.Thursday ? "Y" : "N";
            string daySelectedFriday = selectedDays.Friday ? "Y" : "N";
            string daySelectedSaturday = selectedDays.Saturday ? "Y" : "N";

            // End Date may be null if no ending date.
            if (base.EndDate.HasValue)
                endDate = base.EndDate.Value.ToString("yyyyMMdd");

            // FORMATTING DEFINITIONS
            //  Y = Yearly Designator
            //  |       0 = Start Date (8 chars)
            //  |       |        1 = End Date (8 chars)
            //  |       |        |        2 = Number of occurrences (4 chars)
            //  |       |        |        |      3 = Regeneration Type (1 char)
            //  |       |        |        |      |    4 = End date type (1 char)
            //  |       |        |        |      |    |      5 = Regenerate on Sunday
            //  |       |        |        |      |    |      |       6 = Regenerate on Monday
            //  |       |        |        |      |    |      |       |       7 = Regenerate on Tuesday
            //  |       |        |        |      |    |      |       |       |       8 = Regenerate on Wednesday
            //  |       |        |        |      |    |      |       |       |       |      9 = Regenerate on Thursday
            //  |       |        |        |      |    |      |       |       |       |      |      10 = Regenerate on Friday
            //  |       |        |        |      |    |      |       |       |       |      |       |      11 = Regenerate on Saturday
            //  |       |        |        |      |    |      |       |       |       |      |       |      |        12  Regen Every x weeks
            //  |       |        |        |      |    |      |       |       |       |      |       |      |         |
            // [0]    [1-8]    [9-16]  [17-20]  [21] [22]  [23]    [24]    [25]    [26]   [27]     [28]   [29]      [30]
            //  W   20071231  01082017   0000    1     1     T       T      F        F      F       F      F        000 
            info = string.Format("W{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}{11}{12}", base.StartDate.ToString("yyyyMMdd"), endDate, occurrences, weeklyRegenType, endDateType, daySelectedSunday, daySelectedMonday, daySelectedTuesday, daySelectedWednesday, daySelectedThursday, daySelectedFriday, daySelectedSaturday, regenXWeeks);
            return info;
        }

        RecurrenceValues GetEveryXWeeksValues()
        {
            RecurrenceValues values = new RecurrenceValues();
            DateTime dt = base.StartDate.AddDays(-1); // Backup a day so the first instance of GetNextDay will increment to the next day.

            if (getNextDateValue)
            {
                do
                {
                    dt = GetNextDay(dt);
                    values.AddDateValue(dt);
                    if (values.Values[values.Values.Count - 1] > nextDateValue)
                        break;
                } while (dt <= nextDateValue.AddDays((regenEveryXWeeks * 7) + 7)); // Ensure the last date if greater than what's needed for the next date in the cycle
            }
            else
            {
                switch (base.TypeOfEndDate)
                {
                    case EndDateType.NoEndDate:
                        throw new Exception("The ability to create recurring dates with no End date is not currently available.");

                    case EndDateType.NumberOfOccurrences:

                        for (int i = 0; i < base.NumberOfOccurrences; i++)
                        {
                            dt = GetNextDay(dt);
                            values.AddDateValue(dt);
                        }
                        break;

                    case EndDateType.SpecificDate:
                        do
                        {
                            dt = GetNextDay(dt);
                            // Handle for dates past the end date
                            if (dt > base.EndDate)
                                break;

                            values.AddDateValue(dt);
                        } while (dt <= base.EndDate);
                        break;

                    default:
                        throw new ArgumentNullException("TypeOfEndDate", "The TypeOfEndDate property has not been set.");
                }
            }

            return values;
        }

        DateTime GetNextDay(DateTime input)
        {
            DateTime? returnDate = null;

            // Get the return date by incrementing the date
            // and checking the value against the selected days
            // of the week.
            do
            {
                input = input.AddDays(1);
                switch (input.DayOfWeek)
                {
                    case DayOfWeek.Sunday:
                        if (selectedDays.Sunday)
                            returnDate = input;
                        break;
                    case DayOfWeek.Monday:
                        if (selectedDays.Monday)
                            returnDate = input;
                        break;
                    case DayOfWeek.Tuesday:
                        if (selectedDays.Tuesday)
                            returnDate = input;
                        break;
                    case DayOfWeek.Wednesday:
                        if (selectedDays.Wednesday)
                            returnDate = input;
                        break;
                    case DayOfWeek.Thursday:
                        if (selectedDays.Thursday)
                            returnDate = input;
                        break;
                    case DayOfWeek.Friday:
                        if (selectedDays.Friday)
                            returnDate = input;
                        break;
                    case DayOfWeek.Saturday:
                        if (selectedDays.Saturday)
                            returnDate = input;
                        else
                        {
                            // Increment by weeks if regenXWeeks has a value 
                            // greater than 1 which is default.
                            // But only increment if we've gone over
                            // at least 7 days already.
                            if (regenEveryXWeeks > 1 )
                                input = input.AddDays((regenEveryXWeeks -1) * 7);
                        }
                        break;
                }
            } while (!returnDate.HasValue);
            return returnDate.Value;
        }
#endregion //Private Procedures

        #region Public Fields
        /// <summary>
        /// What is the interval to generate dates. This is used to skip weeks in the cycle.
        /// </summary>
        public int RegenEveryXWeeks
        {
            get
            {
                return regenEveryXWeeks;
            }
        }

        public SelectedDayOfWeekValues SelectedDays
        {
            get
            {
                return selectedDays;
            }
        }

        /// <summary>
        /// What is the regeneration type such as Specific day of month, custom date, etc.
        /// </summary>
        public WeeklyRegenType RegenType
        {
            get
            {
                return regenType;
            }
        }

#endregion //Public Fields

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) BOCA Software Technologies, Inc.
United States United States
.NET Developer in Garner, North Carolina. Specializing in WinForms development in C#, VB.Net.

CEO/Founder BOCA Software Technologies, Inc.

Comments and Discussions