/** * Applies BYDAY rules specified in this Recur instance to the specified date list. If no BYDAY rules are specified * the date list is returned unmodified. * @param dates * @return */ static private List <DateTime> GetDayVariants(List <DateTime> dates, RecurrencePattern pattern, bool?expand) { if (expand == null || pattern.ByDay.Count == 0) { return(dates); } if (expand.HasValue && expand.Value) { // Expand behavior List <DateTime> weekDayDates = new List <DateTime>(); for (int i = 0; i < dates.Count; i++) { DateTime date = dates[i]; for (int j = 0; j < pattern.ByDay.Count; j++) { weekDayDates.AddRange(GetAbsWeekDays(date, pattern.ByDay[j], pattern, expand)); } } return(weekDayDates); } else { // Limit behavior for (int i = dates.Count - 1; i >= 0; i--) { DateTime date = dates[i]; for (int j = 0; j < pattern.ByDay.Count; j++) { WeekDay weekDay = pattern.ByDay[j]; if (weekDay.DayOfWeek.Equals(date.DayOfWeek)) { // If no offset is specified, simply test the day of week! // FIXME: test with offset... if (date.DayOfWeek.Equals(weekDay.DayOfWeek)) { goto Next; } } } dates.RemoveAt(i); Next :; } return(dates); } }
/** * Returns a list of applicable dates corresponding to the specified week day in accordance with the frequency * specified by this recurrence rule. * @param date * @param weekDay * @return */ static private List <DateTime> GetAbsWeekDays(DateTime date, WeekDay weekDay, RecurrencePattern pattern, bool?expand) { List <DateTime> days = new List <DateTime>(); DayOfWeek dayOfWeek = weekDay.DayOfWeek; if (pattern.Frequency == FrequencyType.Daily) { if (date.DayOfWeek == dayOfWeek) { days.Add(date); } } else if (pattern.Frequency == FrequencyType.Weekly || pattern.ByWeekNo.Count > 0) { // Rewind to the first day of the week while (date.DayOfWeek != pattern.FirstDayOfWeek) { date = date.AddDays(-1); } // Step forward until we're on the day of week we're interested in while (date.DayOfWeek != dayOfWeek) { date = date.AddDays(1); } days.Add(date); } else if (pattern.Frequency == FrequencyType.Monthly || pattern.ByMonth.Count > 0) { int month = date.Month; // construct a list of possible month days.. date = date.AddDays(-date.Day + 1); while (date.DayOfWeek != dayOfWeek) { date = date.AddDays(1); } while (date.Month == month) { days.Add(date); date = date.AddDays(7); } } else if (pattern.Frequency == FrequencyType.Yearly) { int year = date.Year; // construct a list of possible year days.. date = date.AddDays(-date.DayOfYear + 1); while (date.DayOfWeek != dayOfWeek) { date = date.AddDays(1); } while (date.Year == year) { days.Add(date); date = date.AddDays(7); } } return(GetOffsetDates(days, weekDay.Offset)); }
public object Deserialize(TextReader tr) { string value = tr.ReadToEnd(); // Instantiate the data type RecurrencePattern r = new RecurrencePattern(); if (r != null) { Match match = Regex.Match(value, @"FREQ=(SECONDLY|MINUTELY|HOURLY|DAILY|WEEKLY|MONTHLY|YEARLY);?(.*)", RegexOptions.IgnoreCase); if (match.Success) { // Parse the frequency type r.Frequency = (FrequencyType)Enum.Parse(typeof(FrequencyType), match.Groups[1].Value, true); // NOTE: fixed a bug where the group 2 match // resulted in an empty string, which caused // an error. if (match.Groups[2].Success && match.Groups[2].Length > 0) { string[] keywordPairs = match.Groups[2].Value.Split(';'); foreach (string keywordPair in keywordPairs) { string[] keyValues = keywordPair.Split('='); string keyword = keyValues[0]; string keyValue = keyValues[1]; switch (keyword.ToUpper()) { case "UNTIL": { try { r.Until = DateTime.Parse(keyValue, System.Globalization.CultureInfo.InvariantCulture); } catch (Exception ex) { Log.e(ex); } } break; case "COUNT": r.Count = Convert.ToInt32(keyValue); break; case "INTERVAL": r.Interval = Convert.ToInt32(keyValue); break; case "BYSECOND": AddInt32Values(r.BySecond, keyValue); break; case "BYMINUTE": AddInt32Values(r.ByMinute, keyValue); break; case "BYHOUR": AddInt32Values(r.ByHour, keyValue); break; case "BYDAY": { string[] days = keyValue.Split(','); foreach (string day in days) { WeekDay wd = new WeekDay(); Match match2 = Regex.Match(value, @"(\+|-)?(\d{1,2})?(\w{2})"); if (match2.Success) { if (match2.Groups[2].Success) { wd.Offset = Convert.ToInt32(match2.Groups[2].Value); if (match2.Groups[1].Success && match2.Groups[1].Value.Contains("-")) { wd.Offset *= -1; } } wd.DayOfWeek = GetDayOfWeek(match2.Groups[3].Value); r.ByDay.Add(wd); } } } break; case "BYMONTHDAY": AddInt32Values(r.ByMonthDay, keyValue); break; case "BYYEARDAY": AddInt32Values(r.ByYearDay, keyValue); break; case "BYWEEKNO": AddInt32Values(r.ByWeekNo, keyValue); break; case "BYMONTH": AddInt32Values(r.ByMonth, keyValue); break; case "BYSETPOS": AddInt32Values(r.BySetPosition, keyValue); break; case "WKST": r.FirstDayOfWeek = GetDayOfWeek(keyValue); break; } } } } // // This matches strings such as: // // "Every 6 minutes" // "Every 3 days" // else if ((match = Regex.Match(value, @"every\s+(?<Interval>other|\d+)?\w{0,2}\s*(?<Freq>second|minute|hour|day|week|month|year)s?,?\s*(?<More>.+)", RegexOptions.IgnoreCase)).Success) { if (match.Groups["Interval"].Success) { int interval; if (!int.TryParse(match.Groups["Interval"].Value, out interval)) { r.Interval = 2; // "other" } else { r.Interval = interval; } } else { r.Interval = 1; } switch (match.Groups["Freq"].Value.ToLower()) { case "second": r.Frequency = FrequencyType.Secondly; break; case "minute": r.Frequency = FrequencyType.Minutely; break; case "hour": r.Frequency = FrequencyType.Hourly; break; case "day": r.Frequency = FrequencyType.Daily; break; case "week": r.Frequency = FrequencyType.Weekly; break; case "month": r.Frequency = FrequencyType.Monthly; break; case "year": r.Frequency = FrequencyType.Yearly; break; } string[] values = match.Groups["More"].Value.Split(','); foreach (string item in values) { if ((match = Regex.Match(item, @"(?<Num>\d+)\w\w\s+(?<Type>second|minute|hour|day|week|month)", RegexOptions.IgnoreCase)).Success || (match = Regex.Match(item, @"(?<Type>second|minute|hour|day|week|month)\s+(?<Num>\d+)", RegexOptions.IgnoreCase)).Success) { int num; if (int.TryParse(match.Groups["Num"].Value, out num)) { switch (match.Groups["Type"].Value.ToLower()) { case "second": r.BySecond.Add(num); break; case "minute": r.ByMinute.Add(num); break; case "hour": r.ByHour.Add(num); break; case "day": switch (r.Frequency) { case FrequencyType.Yearly: r.ByYearDay.Add(num); break; case FrequencyType.Monthly: r.ByMonthDay.Add(num); break; } break; case "week": r.ByWeekNo.Add(num); break; case "month": r.ByMonth.Add(num); break; } } } else if ((match = Regex.Match(item, @"(?<Num>\d+\w{0,2})?(\w|\s)+?(?<First>first)?(?<Last>last)?\s*((?<Day>sunday|monday|tuesday|wednesday|thursday|friday|saturday)\s*(and|or)?\s*)+", RegexOptions.IgnoreCase)).Success) { int num = int.MinValue; if (match.Groups["Num"].Success) { if (int.TryParse(match.Groups["Num"].Value, out num)) { if (match.Groups["Last"].Success) { // Make number negative num *= -1; } } } else if (match.Groups["Last"].Success) { num = -1; } else if (match.Groups["First"].Success) { num = 1; } foreach (Capture capture in match.Groups["Day"].Captures) { WeekDay wd = new WeekDay(); wd.Offset = num; wd.DayOfWeek = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), capture.Value, true); r.ByDay.Add(wd); } } else if ((match = Regex.Match(item, @"at\s+(?<Hour>\d{1,2})(:(?<Minute>\d{2})((:|\.)(?<Second>\d{2}))?)?\s*(?<Meridian>(a|p)m?)?", RegexOptions.IgnoreCase)).Success) { int hour, minute, second; if (int.TryParse(match.Groups["Hour"].Value, out hour)) { // Adjust for PM if (match.Groups["Meridian"].Success && match.Groups["Meridian"].Value.ToUpper().StartsWith("P")) { hour += 12; } r.ByHour.Add(hour); if (match.Groups["Minute"].Success && int.TryParse(match.Groups["Minute"].Value, out minute)) { r.ByMinute.Add(minute); if (match.Groups["Second"].Success && int.TryParse(match.Groups["Second"].Value, out second)) { r.BySecond.Add(second); } } } } else if ((match = Regex.Match(item, @"^\s*until\s+(?<DateTime>.+)$", RegexOptions.IgnoreCase)).Success) { DateTime dt = DateTime.Parse(match.Groups["DateTime"].Value); DateTime.SpecifyKind(dt, DateTimeKind.Utc); r.Until = dt; } else if ((match = Regex.Match(item, @"^\s*for\s+(?<Count>\d+)\s+occurrences\s*$", RegexOptions.IgnoreCase)).Success) { int count; if (!int.TryParse(match.Groups["Count"].Value, out count)) { return(false); } else { r.Count = count; } } } } else { // Couldn't parse the object, return null! r = null; } if (r != null) { CheckMutuallyExclusive("COUNT", "UNTIL", r.Count, r.Until); CheckRange("INTERVAL", r.Interval, 0, int.MaxValue); CheckRange("COUNT", r.Count, 0, int.MaxValue); CheckRange("BYSECOND", r.BySecond, 0, 59); CheckRange("BYMINUTE", r.ByMinute, 0, 59); CheckRange("BYHOUR", r.ByHour, 0, 23); CheckRange("BYMONTHDAY", r.ByMonthDay, -31, 31); CheckRange("BYYEARDAY", r.ByYearDay, -366, 366); CheckRange("BYWEEKNO", r.ByWeekNo, -53, 53); CheckRange("BYMONTH", r.ByMonth, 1, 12); CheckRange("BYSETPOS", r.BySetPosition, -366, 366); } } return(r); }