private void buildOutlookPattern(Event ev, AppointmentItem ai, out RecurrencePattern oPattern) { if (ev.Recurrence == null) { oPattern = null; return; } Dictionary <String, String> ruleBook = explodeRrule(ev.Recurrence); if (ruleBook == null) { throw new ApplicationException("WARNING: The recurrence pattern is not compatible with Outlook. This event cannot be synced."); } log.Fine("Building Outlook recurrence pattern"); oPattern = ai.GetRecurrencePattern(); #region RECURRENCE PATTERN //RRULE:FREQ=WEEKLY;UNTIL=20150906T000000Z;BYDAY=SA switch (ruleBook["FREQ"]) { case "DAILY": { oPattern.RecurrenceType = OlRecurrenceType.olRecursDaily; break; } case "WEEKLY": { oPattern.RecurrenceType = OlRecurrenceType.olRecursWeekly; // Need to work out dayMask from "BY" pattern // Eg "BYDAY=MO,TU,WE,TH,FR" OlDaysOfWeek dowMask = getDOWmask(ruleBook); if (dowMask != 0) { oPattern.DayOfWeekMask = dowMask; } break; } case "MONTHLY": { oPattern.RecurrenceType = OlRecurrenceType.olRecursMonthly; if (ruleBook.ContainsKey("BYSETPOS")) { oPattern.RecurrenceType = OlRecurrenceType.olRecursMonthNth; int gInstance = Convert.ToInt16(ruleBook["BYSETPOS"]); oPattern.Instance = (gInstance == -1) ? 5 : gInstance; oPattern.DayOfWeekMask = getDOWmask(ruleBook); if (oPattern.DayOfWeekMask == (OlDaysOfWeek)127 && gInstance == -1 && (ev.Start.DateTime ?? DateTime.Parse(ev.Start.Date)).Day > 28) { //In Outlook this is simply a monthly recurring oPattern.RecurrenceType = OlRecurrenceType.olRecursMonthly; } } if (ruleBook.ContainsKey("BYDAY")) { if (ruleBook["BYDAY"].StartsWith("-1")) { oPattern.RecurrenceType = OlRecurrenceType.olRecursMonthNth; oPattern.Instance = 5; oPattern.DayOfWeekMask = getDOWmask(ruleBook["BYDAY"].TrimStart("-1".ToCharArray())); } else if ("1,2,3,4".Contains(ruleBook["BYDAY"].Substring(0, 1))) { oPattern.RecurrenceType = OlRecurrenceType.olRecursMonthNth; oPattern.Instance = Convert.ToInt16(ruleBook["BYDAY"].Substring(0, 1)); oPattern.DayOfWeekMask = getDOWmask(ruleBook["BYDAY"].TrimStart(oPattern.Instance.ToString().ToCharArray())); } } break; } case "YEARLY": { oPattern.RecurrenceType = OlRecurrenceType.olRecursYearly; //Google interval is years, Outlook is months if (ruleBook.ContainsKey("INTERVAL") && Convert.ToInt16(ruleBook["INTERVAL"]) > 1) { oPattern.Interval = Convert.ToInt16(ruleBook["INTERVAL"]) * 12; } if (ruleBook.ContainsKey("BYSETPOS")) { oPattern.RecurrenceType = OlRecurrenceType.olRecursYearNth; int gInstance = Convert.ToInt16(ruleBook["BYSETPOS"]); oPattern.Instance = (gInstance == -1) ? 5 : gInstance; oPattern.DayOfWeekMask = getDOWmask(ruleBook); if (ruleBook.ContainsKey("BYMONTH")) { oPattern.MonthOfYear = Convert.ToInt16(ruleBook["BYMONTH"]); } } break; } } #endregion #region RANGE ai = OutlookOgcs.Calendar.Instance.IOutlook.WindowsTimeZone_set(ai, ev); oPattern.PatternStartDate = ev.Start.DateTime ?? DateTime.Parse(ev.Start.Date); if (ruleBook.ContainsKey("INTERVAL") && Convert.ToInt16(ruleBook["INTERVAL"]) > 1 && ruleBook["FREQ"] != "YEARLY") { oPattern.Interval = Convert.ToInt16(ruleBook["INTERVAL"]); } if (ruleBook.ContainsKey("COUNT")) { oPattern.Occurrences = Convert.ToInt16(ruleBook["COUNT"]); } if (ruleBook.ContainsKey("UNTIL")) { if (ruleBook["UNTIL"].StartsWith("4500")) { log.Warn("Outlook can't handle end dates this far in the future. Converting to no end date."); oPattern.NoEndDate = true; } else { DateTime endDate; if (ruleBook["UNTIL"].Length == 8 && !ruleBook["UNTIL"].EndsWith("Z")) { endDate = DateTime.ParseExact(ruleBook["UNTIL"], "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture).Date; } else { endDate = DateTime.ParseExact(ruleBook["UNTIL"], "yyyyMMddTHHmmssZ", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AdjustToUniversal); endDate = endDate.AddHours(TimezoneDB.GetUtcOffset(ev.End.TimeZone)).Date; } if (endDate < oPattern.PatternStartDate) { log.Debug("PatternStartDate: " + oPattern.PatternStartDate.ToString("yyyyMMddHHmmss")); log.Debug("PatternEndDate: " + ruleBook["UNTIL"].ToString()); String summary = GoogleOgcs.Calendar.GetEventSummary(ev, onlyIfNotVerbose: true); Forms.Main.Instance.Console.Update(summary + "The recurring Google event has an end date <i>before</i> the start date, which Outlook doesn't allow.<br/>" + "The synced Outlook recurrence has been changed to a single occurrence.", Console.Markup.warning); oPattern.Occurrences = 1; } else { oPattern.PatternEndDate = endDate; } } } if (!ruleBook.ContainsKey("COUNT") && !ruleBook.ContainsKey("UNTIL")) { oPattern.NoEndDate = true; } #endregion }