//===================================================================== /// <summary> /// This is overridden to allow cloning of a PDI object /// </summary> /// <returns>A clone of the object.</returns> public override object Clone() { ObservanceRule o = new ObservanceRule(); o.Clone(this); return(o); }
/// <summary> /// This is overridden to allow proper comparison of observance objects /// </summary> /// <param name="obj">The object to which this instance is compared</param> /// <returns>Returns true if the object equals this instance, false if it does not</returns> public override bool Equals(object obj) { ObservanceRule obr = obj as ObservanceRule; if (obr == null) { return(false); } // The ToString() method returns a text representation of the observance based on all of its settings // so it's a reliable way to tell if two instances are the same. return(this == obr || this.ToString() == obr.ToString()); }
/// <summary> /// This is overridden to allow copying of the additional properties /// </summary> /// <param name="p">The PDI object from which the settings are to be copied</param> protected override void Clone(PDIObject p) { ObservanceRule o = (ObservanceRule)p; this.ClearProperties(); ruleType = o.RuleType; startDate = (StartDateProperty)o.StartDateTime.Clone(); offsetFrom = (TimeZoneOffsetProperty)o.OffsetFrom.Clone(); offsetTo = (TimeZoneOffsetProperty)o.OffsetTo.Clone(); comment = (CommentProperty)o.Comment.Clone(); this.RecurrenceRules.CloneRange(o.RecurrenceRules); this.RecurDates.CloneRange(o.RecurDates); this.TimeZoneNames.CloneRange(o.TimeZoneNames); this.CustomProperties.CloneRange(o.CustomProperties); }
/// <summary> /// Update the prior row with the values from the unbound controls and load the values for the new row /// when the position changes. /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void BindingSource_PositionChanged(object sender, EventArgs e) { ObservanceRule newItem = (ObservanceRule)this.BindingSource.Current; int hours, minutes; // If all deleted, ignore it if(newItem == null) { currentRule = null; return; } // Save changes to the unbound controls to the prior row this.StoreChanges(); // Load the new values into the unbound controls currentRule = newItem; // We'll only edit the first time zone name if(currentRule.TimeZoneNames.Count == 0) currentRule.TimeZoneNames.Add("GMT"); txtTZName.Text = currentRule.TimeZoneNames[0].Value; hours = currentRule.OffsetFrom.TimeSpanValue.Hours; minutes = currentRule.OffsetFrom.TimeSpanValue.Minutes; // If hours are specified, keep minutes positive if(hours != 0 && minutes < 0) minutes *= -1; udcFromHours.Value = (hours < -23) ? -23 : (hours > 23) ? 23 : hours; udcFromMinutes.Value = (minutes < -59) ? -59 : (minutes > 59) ? 59 : minutes; hours = currentRule.OffsetTo.TimeSpanValue.Hours; minutes = currentRule.OffsetTo.TimeSpanValue.Minutes; // If hours are specified, keep minutes positive if(hours != 0 && minutes < 0) minutes *= -1; udcToHours.Value = (hours < -23) ? -23 : (hours > 23) ? 23 : hours; udcToMinutes.Value = (minutes < -59) ? -59 : (minutes > 59) ? 59 : minutes; rcRulesDates.SetValues(currentRule.RecurrenceRules, currentRule.RecurDates); }
/// <summary> /// This is used by the time zone conversion methods to find the correct observance rules to calculate /// the standard time and daylight saving time information. /// </summary> /// <param name="convertDate">The date being converted.</param> /// <param name="timeZoneId">The time zone for the conversion.</param> /// <param name="useLocalTime">True to return information in local time or false to return the /// information in the specified time zone's time.</param> /// <param name="standardRule">This is used to returned the standard rule. It is null if one is not /// found.</param> /// <param name="daylightRule">This is used to returned the daylight rule. It is null if one is not /// found.</param> /// <param name="standardDate">This is used to return the start of standard time for the returned /// standard rule.</param> /// <param name="dstDate">This is used to return the start of daylight saving time for the returned /// daylight rule.</param> private static void FindRules(DateTime convertDate, string timeZoneId, bool useLocalTime, out ObservanceRule standardRule, out ObservanceRule daylightRule, out DateTime standardDate, out DateTime dstDate) { DateTime startDate; DateTimeCollection dates; standardRule = daylightRule = null; standardDate = dstDate = DateTime.MinValue; VTimeZone vtz = timeZones[timeZoneId]; if(vtz == null) return; // In order to calculate the correct time, we need the UTC offset for standard time locally TimeZone tzCurrent = TimeZone.CurrentTimeZone; TimeSpan tsUTC = tzCurrent.GetUtcOffset(tzCurrent.GetDaylightChanges(convertDate.Year).End); // Get the observance rules to use in the conversion foreach(ObservanceRule or in vtz.ObservanceRules) { startDate = or.StartDateTime.TimeZoneDateTime; if(startDate.Year > convertDate.Year) continue; // Find the first standard rule that generates a date for the date/time's year if(standardRule == null && or.RuleType == ObservanceRuleType.Standard) { // Check for an RDATE that matches. foreach(RDateProperty rdt in or.RecurDates) if(rdt.TimeZoneDateTime.Year == convertDate.Year) { standardRule = or; if(useLocalTime) standardDate = rdt.TimeZoneDateTime.Add(or.OffsetFrom.TimeSpanValue.Negate()).ToLocalTime(); else standardDate = rdt.TimeZoneDateTime; break; } // If there wasn't one, check the RRULEs if(standardRule == null) foreach(RRuleProperty rrule in or.RecurrenceRules) { // Set the start date and resolve the recurrence rrule.Recurrence.StartDateTime = startDate; dates = rrule.Recurrence.InstancesBetween(new DateTime(convertDate.Year, 1, 1), new DateTime(convertDate.Year, 12, 31, 23, 59, 59)); // There should only be one... if(dates.Count != 0) { standardRule = or; if(useLocalTime) standardDate = dates[0].Add(or.OffsetTo.TimeSpanValue.Negate()).Add(tsUTC); else standardDate = dates[0]; break; } } // If it doesn't have any RDATEs or RRULEs and the year is on or after the start date year, // use the start date. if(or.RecurDates.Count == 0 && or.RecurrenceRules.Count == 0 && startDate.Year <= convertDate.Year) { standardRule = or; if(useLocalTime) standardDate = startDate.Add(or.OffsetTo.TimeSpanValue.Negate()).Add(tsUTC); else standardDate = startDate; } } // Find the first daylight rule that generates a date for the date/times' year if(daylightRule == null && or.RuleType == ObservanceRuleType.Daylight) { // Check for an RDATE that matches. foreach(RDateProperty rdt in or.RecurDates) if(rdt.TimeZoneDateTime.Year == convertDate.Year) { daylightRule = or; if(useLocalTime) dstDate = rdt.TimeZoneDateTime.Add(or.OffsetFrom.TimeSpanValue.Negate()).ToLocalTime(); else dstDate = rdt.TimeZoneDateTime; break; } // If there wasn't one, check the RRULEs if(daylightRule == null) foreach(RRuleProperty rrule in or.RecurrenceRules) { // Set the start date and resolve the recurrence rrule.Recurrence.StartDateTime = startDate; dates = rrule.Recurrence.InstancesBetween(new DateTime(convertDate.Year, 1, 1), new DateTime(convertDate.Year, 12, 31, 23, 59, 59)); // There should only be one... if(dates.Count != 0) { daylightRule = or; if(useLocalTime) dstDate = dates[0].Add(or.OffsetFrom.TimeSpanValue.Negate()).Add(tsUTC); else dstDate = dates[0]; break; } } // If it doesn't have any RDATEs or RRULEs and the year is on or after the start date year, // use the start date. if(or.RecurDates.Count == 0 && or.RecurrenceRules.Count == 0 && startDate.Year <= convertDate.Year) { daylightRule = or; if(useLocalTime) dstDate = startDate.Add(or.OffsetFrom.TimeSpanValue.Negate()).Add(tsUTC); else dstDate = startDate; } } // Stop when we've got a match for each if(standardRule != null && daylightRule != null) break; } }
//===================================================================== /// <summary> /// This is overridden to allow cloning of a PDI object /// </summary> /// <returns>A clone of the object.</returns> public override object Clone() { ObservanceRule o = new ObservanceRule(); o.Clone(this); return o; }
//===================================================================== /// <summary> /// This is overridden to handle the additional state maintained by the calendar parser /// </summary> /// <param name="fullReset">If true, a full reset is done (i.e. this is the start of a brand new session. /// If false only the line state is reset (it's done parsing a property name or value).</param> protected override void ResetState(bool fullReset) { if(fullReset) { currentState = VCalendarParserState.VCalendar; vEvent = null; vToDo = null; vJournal = null; vAlarm = null; vFreeBusy = null; vTimeZone = null; obsRule = null; priorState.Clear(); beginValue.Clear(); } base.ResetState(fullReset); }
/// <summary> /// This is implemented to handle properties related to observance rule items in VTimeZone objects /// </summary> /// <param name="propertyName">The name of the property.</param> /// <param name="parameters">A string collection containing the parameters and their values. If empty, /// there are no parameters.</param> /// <param name="propertyValue">The value of the property.</param> protected virtual void ObservanceRuleParser(string propertyName, StringCollection parameters, string propertyValue) { StringCollection sc; string[] parts, parms; int idx; // The last entry is always CustomProperty so scan for length minus one for(idx = 0; idx < ntvORule.Length - 1; idx++) if(ntvORule[idx].IsMatch(propertyName)) break; // An opening BEGIN:STANDARD or BEGIN:DAYLIGHT property must have been seen. if(obsRule == null) throw new PDIParserException(this.LineNumber, LR.GetString("ExParseNoBeginProp", "BEGIN:STANDARD/BEGIN:DAYLIGHT", propertyName)); // Handle or create the property switch(ntvORule[idx].EnumValue) { case PropertyType.Begin: // Handle unknown nested objects priorState.Push(currentState); currentState = VCalendarParserState.Custom; CustomObjectParser(propertyName, parameters, propertyValue); break; case PropertyType.End: // For this, the value must be STANDARD or DAYLIGHT depending on the rule type if((obsRule.RuleType == ObservanceRuleType.Standard && String.Compare(propertyValue.Trim(), "STANDARD", StringComparison.OrdinalIgnoreCase) != 0) || (obsRule.RuleType == ObservanceRuleType.Daylight && String.Compare(propertyValue.Trim(), "DAYLIGHT", StringComparison.OrdinalIgnoreCase) != 0)) { throw new PDIParserException(this.LineNumber, LR.GetString("ExParseUnrecognizedTagValue", ntvORule[idx].Name, propertyValue)); } // The rule is added to the collection when created so we don't have to rely on an END tag to // add it. obsRule = null; currentState = priorState.Pop(); break; case PropertyType.StartDateTime: obsRule.StartDateTime.DeserializeParameters(parameters); obsRule.StartDateTime.EncodedValue = propertyValue; break; case PropertyType.TimeZoneOffsetFrom: obsRule.OffsetFrom.DeserializeParameters(parameters); obsRule.OffsetFrom.EncodedValue = propertyValue; break; case PropertyType.TimeZoneOffsetTo: obsRule.OffsetTo.DeserializeParameters(parameters); obsRule.OffsetTo.EncodedValue = propertyValue; break; case PropertyType.Comment: // If this is seen more than once, just add the new stuff to the existing property if(obsRule.Comment.Value != null) { obsRule.Comment.EncodedValue += "\r\n"; obsRule.Comment.EncodedValue += propertyValue; } else { obsRule.Comment.DeserializeParameters(parameters); obsRule.Comment.EncodedValue = propertyValue; } break; case PropertyType.RecurrenceRule: RRuleProperty rr = new RRuleProperty(); rr.DeserializeParameters(parameters); rr.EncodedValue = propertyValue; obsRule.RecurrenceRules.Add(rr); break; case PropertyType.RecurDate: // There may be more than one date in the value. If so, split them into separate ones. This // makes it easier to manage. They'll get written back out as individual properties but // that's okay. parts = propertyValue.Split(',', ';'); // It's important that we retain the same parameters for each one parms = new string[parameters.Count]; parameters.CopyTo(parms, 0); foreach(string s in parts) { sc = new StringCollection(); sc.AddRange(parms); RDateProperty rd = new RDateProperty(); rd.DeserializeParameters(sc); rd.EncodedValue = s; obsRule.RecurDates.Add(rd); } break; case PropertyType.TimeZoneName: TimeZoneNameProperty tzn = new TimeZoneNameProperty(); tzn.DeserializeParameters(parameters); tzn.EncodedValue = propertyValue; obsRule.TimeZoneNames.Add(tzn); break; default: // Anything else is a custom property CustomProperty cust = new CustomProperty(propertyName); cust.DeserializeParameters(parameters); cust.EncodedValue = propertyValue; obsRule.CustomProperties.Add(cust); break; } }
/// <summary> /// This is implemented to handle properties related to VTimeZone items /// </summary> /// <param name="propertyName">The name of the property.</param> /// <param name="parameters">A string collection containing the parameters and their values. If empty, /// there are no parameters.</param> /// <param name="propertyValue">The value of the property.</param> protected virtual void VTimeZoneParser(string propertyName, StringCollection parameters, string propertyValue) { int idx; // The last entry is always CustomProperty so scan for length minus one for(idx = 0; idx < ntvTimeZone.Length - 1; idx++) if(ntvTimeZone[idx].IsMatch(propertyName)) break; // An opening BEGIN:VTIMEZONE property must have been seen if(vTimeZone == null) throw new PDIParserException(this.LineNumber, LR.GetString("ExParseNoBeginProp", "BEGIN:VTIMEZONE", propertyName)); // Handle or create the property switch(ntvTimeZone[idx].EnumValue) { case PropertyType.Begin: // Handle nested objects priorState.Push(currentState); // Is it a STANDARD or DAYLIGHT property? if(String.Compare(propertyValue.Trim(), "STANDARD", StringComparison.OrdinalIgnoreCase) == 0) { obsRule = new ObservanceRule(ObservanceRuleType.Standard); vTimeZone.ObservanceRules.Add(obsRule); currentState = VCalendarParserState.ObservanceRule; } else if(String.Compare(propertyValue.Trim(), "DAYLIGHT", StringComparison.OrdinalIgnoreCase) == 0) { obsRule = new ObservanceRule(ObservanceRuleType.Daylight); vTimeZone.ObservanceRules.Add(obsRule); currentState = VCalendarParserState.ObservanceRule; } else { // Unknown/custom object currentState = VCalendarParserState.Custom; CustomObjectParser(propertyName, parameters, propertyValue); } break; case PropertyType.End: // For this, the value must be VTIMEZONE if(String.Compare(propertyValue.Trim(), "VTIMEZONE", StringComparison.OrdinalIgnoreCase) != 0) throw new PDIParserException(this.LineNumber, LR.GetString("ExParseUnrecognizedTagValue", ntvTimeZone[idx].Name, propertyValue)); // Unlike other objects, we do rely on the end tag to add the time zone object to the // collection as they are shared amongst all instances. VCalendar.TimeZones.Merge(vTimeZone); vTimeZone = null; currentState = priorState.Pop(); break; case PropertyType.TimeZoneId: vTimeZone.TimeZoneId.EncodedValue = propertyValue; break; case PropertyType.TimeZoneUrl: vTimeZone.TimeZoneUrl.EncodedValue = propertyValue; break; case PropertyType.LastModified: vTimeZone.LastModified.DeserializeParameters(parameters); vTimeZone.LastModified.EncodedValue = propertyValue; break; default: // Anything else is a custom property CustomProperty cust = new CustomProperty(propertyName); cust.DeserializeParameters(parameters); cust.EncodedValue = propertyValue; vTimeZone.CustomProperties.Add(cust); break; } }