/// <param name="dynamicTimeZone">can be set to null if not available</param> public override void SetOriginalTimeZone(TimeZoneInfo staticTimeZone, TimeZoneInfo dynamicTimeZone, int effectiveYear) { this.TimeZoneDescription = staticTimeZone.DisplayName; // Outlook 2003 RTM writes this for single appointments as well if (this.File.WriterCompatibilityMode >= WriterCompatibilityMode.Outlook2003SP3) { TimeZoneInfo.AdjustmentRule effectiveRule = AdjustmentRuleUtils.GetFirstRule(staticTimeZone); TimeZoneInfo definitionTimezone = (dynamicTimeZone == null) ? staticTimeZone : dynamicTimeZone; this.TimeZoneDefinitionStartDisplay = TimeZoneDefinitionStructure.FromTimeZoneInfo(definitionTimezone, effectiveRule, effectiveYear, false); // We do not set TimeZoneDefinitionEndDisplay, so Outlook will use TimeZoneDefinitionStartDisplay. } }
/// <param name="dynamicTimeZone">can be set to null if not available</param> public override void SetOriginalTimeZone(TimeZoneInfo staticTimeZone, TimeZoneInfo dynamicTimeZone, int effectiveYear) { this.TimeZoneStructure = TimeZoneStructure.FromTimeZoneInfo(staticTimeZone); this.TimeZoneDescription = staticTimeZone.DisplayName; if (this.File.WriterCompatibilityMode >= WriterCompatibilityMode.Outlook2003SP3) { TimeZoneInfo.AdjustmentRule effectiveRule = AdjustmentRuleUtils.GetFirstRule(staticTimeZone); TimeZoneInfo definitionTimezone = (dynamicTimeZone == null) ? staticTimeZone : dynamicTimeZone; TimeZoneDefinitionStructure timeZoneDefinitionStartDisplay = TimeZoneDefinitionStructure.FromTimeZoneInfo(definitionTimezone, effectiveRule, effectiveYear, false); this.TimeZoneDefinitionStartDisplay = timeZoneDefinitionStartDisplay; // We do not set TimeZoneDefinitionEndDisplay, so Outlook will use TimeZoneDefinitionStartDisplay. // Only Outlook 2007 uses PidLidAppointmentTimeZoneDefinitionRecur if (this.File.WriterCompatibilityMode >= WriterCompatibilityMode.Outlook2007RTM && this.File.WriterCompatibilityMode < WriterCompatibilityMode.Outlook2010RTM) { TimeZoneDefinitionStructure timeZoneDefinitionRecurStructure = TimeZoneDefinitionStructure.FromTimeZoneInfo(definitionTimezone, effectiveRule, effectiveYear, true); this.TimeZoneDefinitionRecurStructure = timeZoneDefinitionRecurStructure; } } }
/// Note about how Outlook selects the effective adjustment rule: /// For backward compatibility reasons, the effective rule is always the static timezone /// rule used by the OS. /// It's possible however that there is no dynamic rule that matches the static rule, /// If that's the case, Outlook will replace the dynamic rule of the current year /// with the static rule, which will be marked as the effective rule. /// <param name="effectiveRule">null if there are no daylight savings</param> public static TimeZoneDefinitionStructure FromTimeZoneInfo(TimeZoneInfo timezone, TimeZoneInfo.AdjustmentRule effectiveRule, int effectiveRuleYear, bool isRecurringTimeZoneRule) { TimeZoneInfo.AdjustmentRule[] adjustmentRules = timezone.GetAdjustmentRules(); if (effectiveRule != null) { if ((effectiveRule.DaylightTransitionStart.IsFixedDateRule || effectiveRule.DaylightTransitionEnd.IsFixedDateRule) && effectiveRule.DateStart.Year < effectiveRule.DateEnd.Year) { // a rule can either be fixed-date (wYear is not zero) or floating-date (wYear is zero) // a fixed-date rule cannot span multiple years. throw new NotImplementedException("Effective DST rule cannot be fixed-date and span multiple years"); } if (adjustmentRules.Length == 0) { // effective rule != null means timezone rule is defined, but the timezone has no such rule throw new ArgumentException("Time zone does not match effective rule"); } } TimeZoneRuleFlags effectiveRuleFlags = TimeZoneRuleFlags.EffectiveTimeZoneRule; if (isRecurringTimeZoneRule) { effectiveRuleFlags |= TimeZoneRuleFlags.RecurringTimeZoneRule; } TimeZoneRuleStructure[] tzRules; if (adjustmentRules.Length == 0) { // at this point effectiveRule must be null TimeZoneRuleStructure ruleStructure = new TimeZoneRuleStructure(); ruleStructure.SetBias(timezone.BaseUtcOffset, new TimeSpan()); ruleStructure.wYear = 1601; // That's what Outlook 2007 SP3 uses ruleStructure.TZRuleFlags = effectiveRuleFlags; tzRules = new TimeZoneRuleStructure[] { ruleStructure }; } else { List <TimeZoneRuleStructure> rules = new List <TimeZoneRuleStructure>(); // First we will create a list of the dynamic rules: // we have an effective rule and at least one dynamic rule int firstRuleYear = adjustmentRules[0].DateStart.Year; if (firstRuleYear > 1) { TimeZoneRuleStructure firstRule = new TimeZoneRuleStructure(); firstRule.SetBias(timezone.BaseUtcOffset, adjustmentRules[0].DaylightDelta); firstRule.wYear = (ushort)(firstRuleYear - 1); rules.Add(firstRule); } for (int index = 0; index < adjustmentRules.Length; index++) { TimeZoneInfo.AdjustmentRule adjustmentRule = adjustmentRules[index]; TimeZoneRuleStructure rule = new TimeZoneRuleStructure(); rule.wYear = (ushort)adjustmentRule.DateStart.Year;; rule.SetBias(timezone.BaseUtcOffset, adjustmentRule.DaylightDelta); rule.stStandardDate = AdjustmentRuleHelper.GetStandardDate(adjustmentRule); rule.stDaylightDate = AdjustmentRuleHelper.GetDaylightDate(adjustmentRule); rules.Add(rule); } int lastRuleYear = adjustmentRules[adjustmentRules.Length - 1].DateEnd.Year; if (lastRuleYear < 9999) { TimeZoneRuleStructure lastRule = new TimeZoneRuleStructure(); lastRule.SetBias(timezone.BaseUtcOffset, adjustmentRules[adjustmentRules.Length - 1].DaylightDelta); lastRule.wYear = (ushort)(lastRuleYear + 1); rules.Add(lastRule); } // Outlook will try to find a rule that match the static rule, and will use the last rule that fits // now we will replace a dynamic rule with the appropriate static rule // find the index for the rule int effectiveRuleIndex = 0; for (int index = 0; index < rules.Count; index++) { if (effectiveRuleYear >= rules[index].wYear) { effectiveRuleIndex = index; } } // prepare the effective rule structure TimeZoneRuleStructure ruleStructure = new TimeZoneRuleStructure(); ruleStructure.wYear = rules[effectiveRuleIndex].wYear; ruleStructure.TZRuleFlags = effectiveRuleFlags; if (effectiveRule == null) { // We should have a no-daylight-savings rule ruleStructure.SetBias(timezone.BaseUtcOffset, new TimeSpan()); } else { ruleStructure.SetBias(timezone.BaseUtcOffset, effectiveRule.DaylightDelta); ruleStructure.stStandardDate = AdjustmentRuleHelper.GetStandardDate(effectiveRule); ruleStructure.stDaylightDate = AdjustmentRuleHelper.GetDaylightDate(effectiveRule); } rules[effectiveRuleIndex] = ruleStructure; tzRules = rules.ToArray(); } TimeZoneDefinitionStructure structure = new TimeZoneDefinitionStructure(); structure.KeyName = timezone.Id; structure.TZRules = tzRules; return(structure); }