/// <summary> /// This is called by the containing form to apply a new time zone to the RDATE/EXDATE property date/time /// values in the control. /// </summary> /// <param name="oldTZ">The old time zone's ID</param> /// <param name="newTZ">The new time zone's ID</param> public void ApplyTimeZone(string oldTZ, string newTZ) { DateTimeInstance dti; int idx = 0; // This only applies to RDATE/EXDATE properties with a time foreach (RDateProperty rdt in rDates) { if (rdt.ValueLocation == ValLocValue.DateTime) { if (oldTZ == null) { dti = VCalendar.LocalTimeToTimeZoneTime(rdt.TimeZoneDateTime, newTZ); } else { dti = VCalendar.TimeZoneToTimeZone(rdt.TimeZoneDateTime, oldTZ, newTZ); } rdt.TimeZoneDateTime = dti.StartDateTime; lbRDates.Items[idx] = dti.StartDateTime.ToString("G"); } idx++; } }
/// <summary> /// Convert the selected date/time to local time and back and to the selected time zone and back to test /// the time zone conversion code. /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> protected void btnApplySrc_Click(object sender, EventArgs e) { DateTime dt; VTimeZone vtzSource = VCalendar.TimeZones[cboSourceTimeZone.SelectedItem.Text]; VTimeZone vtzDest = VCalendar.TimeZones[cboDestTimeZone.SelectedItem.Text]; // Show information for the selected time zones lblTimeZoneInfo.Text = String.Format("<strong>From Time Zone:</strong>\r\n\r\n" + "{0}\r\n<strong>To Time Zone:</strong>\r\n\r\n{1}", vtzSource.ToString(), vtzDest.ToString()); // Do the conversions. Each is round tripped to make sure it gets the expected results. Note that // there will be some anomalies when round tripping times that are near the standard time/DST shift // as these times are somewhat ambiguous and their meaning can vary depending on which side of the // shift you are on and the direction of the conversion. if (!DateTime.TryParse(txtSourceDate.Text, CultureInfo.CurrentCulture, DateTimeStyles.None, out dt)) { lblLocalTime.Text = "Invalid date/time format"; lblLocalBackToSource.Text = lblDestTime.Text = lblDestBackToSource.Text = String.Empty; return; } DateTimeInstance dti = VCalendar.TimeZoneTimeToLocalTime(dt, vtzSource.TimeZoneId.Value); lblLocalTime.Text = String.Format("{0} {1}", dti.StartDateTime, dti.StartTimeZoneName); dti = VCalendar.LocalTimeToTimeZoneTime(dti.StartDateTime, vtzSource.TimeZoneId.Value); lblLocalBackToSource.Text = String.Format("{0} {1}", dti.StartDateTime, dti.StartTimeZoneName); dti = VCalendar.TimeZoneToTimeZone(dt, vtzSource.TimeZoneId.Value, vtzDest.TimeZoneId.Value); lblDestTime.Text = String.Format("{0} {1}", dti.StartDateTime, dti.StartTimeZoneName); dti = VCalendar.TimeZoneToTimeZone(dti.StartDateTime, vtzDest.TimeZoneId.Value, vtzSource.TimeZoneId.Value); lblDestBackToSource.Text = String.Format("{0} {1}", dti.StartDateTime, dti.StartTimeZoneName); }
/// <summary> /// This is called by the containing form to apply a new time zone to the date/time values in the control /// </summary> /// <param name="oldTZ">The old time zone's ID</param> /// <param name="newTZ">The new time zone's ID</param> public void ApplyTimeZone(string oldTZ, string newTZ) { DateTimeInstance dti; if (oldTZ == null) { if (dtpStartDate.Checked) { dti = VCalendar.LocalTimeToTimeZoneTime(dtpStartDate.Value, newTZ); dtpStartDate.Value = dti.StartDateTime; } if (dtpEndDate.Checked) { dti = VCalendar.LocalTimeToTimeZoneTime(dtpEndDate.Value, newTZ); dtpEndDate.Value = dti.StartDateTime; } } else { if (dtpStartDate.Checked) { dti = VCalendar.TimeZoneToTimeZone(dtpStartDate.Value, oldTZ, newTZ); dtpStartDate.Value = dti.StartDateTime; } if (dtpEndDate.Checked) { dti = VCalendar.TimeZoneToTimeZone(dtpEndDate.Value, oldTZ, newTZ); dtpEndDate.Value = dti.StartDateTime; } } }
/// <summary> /// This converts the date/time info to the specified time zone /// </summary> /// <param name="tzid">The time zone to which to convert this instance</param> /// <remarks>This converts the date time instance information from its current time zone identified by /// the <see cref="TimeZoneId"/> property to the specified time zone. The time zone ID property will be /// set to the specified time zone after conversion.</remarks> public void ToTimeZone(string tzid) { DateTimeInstance dti, dtiEnd; // Already in the specified time zone? if (timeZoneID == tzid) { return; } // Convert from local time? if (timeZoneID == null) { dti = VCalendar.LocalTimeToTimeZoneTime(startDate, tzid); dtiEnd = VCalendar.LocalTimeToTimeZoneTime(endDate, tzid); } else { dti = VCalendar.TimeZoneToTimeZone(startDate, timeZoneID, tzid); dtiEnd = VCalendar.TimeZoneToTimeZone(endDate, timeZoneID, tzid); } timeZoneID = dti.TimeZoneId; startDate = dti.StartDateTime; startIsDST = dti.StartIsDaylightSavingTime; startTZName = dti.StartTimeZoneName; endDate = dtiEnd.EndDateTime; endIsDST = dtiEnd.EndIsDaylightSavingTime; endTZName = dtiEnd.EndTimeZoneName; }
/// <summary> /// This method can be used to quickly determine the end date of an item regardless of whether it uses /// recurrence or not. /// </summary> /// <param name="inLocalTime">If true, the date/time is returned expressed in local time. If false, it /// is returned expressed in the time zone of the object as specified by the <see cref="TimeZoneId"/> /// property. If no time zone ID has been specified or it cannot be found, local time is used.</param> /// <returns>For non-recurring items, it returns the starting date plus the duration. For recurring /// items, it scans the recurrence rules' <see cref="PDI.Recurrence.RecurUntil"/> property values and the /// recurrence dates' <see cref="RDateProperty.DateTimeValue"/> or it's <see cref="RDateProperty.PeriodValue"/>'s /// <see cref="PDI.Period.EndDateTime"/> property values to find the highest date/time and returns that /// plus the duration. Note that it will not expand the recurrence rules. Exception rules and exception /// dates are ignored. This is useful for finding out whether or not an item may generate a date in a /// given range without actually expanding it completely.</returns> public DateTime LastInstance(bool inLocalTime) { DateTime endDate, dt = this.StartDateTime.DateTimeValue; TimeSpan ts = this.InstanceDuration.TimeSpan; if (this.IsRecurring) { foreach (RRuleProperty r in this.RecurrenceRules) { if (r.Recurrence.RecurUntil > dt) { dt = r.Recurrence.RecurUntil; if (dt != DateTime.MaxValue) { dt = dt.Add(ts); } } } foreach (RDateProperty rd in this.RecurDates) { if (rd.ValueLocation != ValLocValue.Period) { // If it's not a period, use the component's duration. If it's only a date, assume it's // the whole day. The spec is not clear on this so I'm making a best guess. if (rd.ValueLocation == ValLocValue.DateTime) { endDate = rd.DateTimeValue.Add(ts); } else { endDate = rd.DateTimeValue.Add(new TimeSpan(TimeSpan.TicksPerDay)); } } else { endDate = rd.PeriodValue.EndDateTime; } if (endDate > dt) { dt = endDate; } } } else if (dt != DateTime.MaxValue) { dt = dt.Add(ts); } return(inLocalTime ? dt : VCalendar.LocalTimeToTimeZoneTime(dt, this.TimeZoneId).StartDateTime); }
/// <summary> /// This is called by the containing form to apply a new time zone to the date/time values in the control /// </summary> /// <param name="oldTZ">The old time zone's ID</param> /// <param name="newTZ">The new time zone's ID</param> public void ApplyTimeZone(string oldTZ, string newTZ) { DateTimeInstance dti; if (dtpTrigger.Checked) { if (oldTZ == null) { dti = VCalendar.LocalTimeToTimeZoneTime(dtpTrigger.Value, newTZ); } else { dti = VCalendar.TimeZoneToTimeZone(dtpTrigger.Value, oldTZ, newTZ); } dtpTrigger.Value = dti.StartDateTime; this.StoreChanges(); } }
/// <summary> /// Convert the selected date/time to local time and back and to the selected time zone and back to test /// the time zone conversion code. /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void UpdateTimes(object sender, EventArgs e) { // Wait until both have a value selected if (cboSourceTimeZone.SelectedIndex == -1 || cboDestTimeZone.SelectedIndex == -1) { return; } VTimeZone vtzSource = VCalendar.TimeZones[cboSourceTimeZone.SelectedIndex]; VTimeZone vtzDest = VCalendar.TimeZones[cboDestTimeZone.SelectedIndex]; // Show information for the selected time zones txtTimeZoneInfo.Clear(); txtTimeZoneInfo.AppendText("From Time Zone:\r\n"); txtTimeZoneInfo.AppendText(vtzSource.ToString()); txtTimeZoneInfo.AppendText("\r\n"); txtTimeZoneInfo.AppendText("To Time Zone:\r\n"); txtTimeZoneInfo.AppendText(vtzDest.ToString()); // Do the conversions. Each is round tripped to make sure it gets the expected results. Note that // there will be some anomalies when round tripping times that are near the standard time/DST shift // as these times are somewhat ambiguous and their meaning can vary depending on which side of the // shift you are on and the direction of the conversion. DateTime dt = dtpSourceDate.Value; DateTimeInstance dti = VCalendar.TimeZoneTimeToLocalTime(dt, vtzSource.TimeZoneId.Value); lblLocalTime.Text = String.Format("{0} {1}", dti.StartDateTime, dti.StartTimeZoneName); dti = VCalendar.LocalTimeToTimeZoneTime(dti.StartDateTime, vtzSource.TimeZoneId.Value); lblLocalBackToSource.Text = String.Format("{0} {1}", dti.StartDateTime, dti.StartTimeZoneName); dti = VCalendar.TimeZoneToTimeZone(dt, vtzSource.TimeZoneId.Value, vtzDest.TimeZoneId.Value); lblDestTime.Text = String.Format("{0} {1}", dti.StartDateTime, dti.StartTimeZoneName); dti = VCalendar.TimeZoneToTimeZone(dti.StartDateTime, vtzDest.TimeZoneId.Value, vtzSource.TimeZoneId.Value); lblDestBackToSource.Text = String.Format("{0} {1}", dti.StartDateTime, dti.StartTimeZoneName); }
/// <summary> /// This is used to apply the time zone selection to all date/time values in the dialog box /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event parameters</param> private void btnApplyTZ_Click(object sender, EventArgs e) { DateTimeInstance dti; string sourceTZ, destTZ; if (cboTimeZone.SelectedIndex == timeZoneIdx) { MessageBox.Show("The time zone has not changed", "No Change", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } if (MessageBox.Show(String.Format("Do you want to convert all times from the '{0}' time zone to " + "the '{1}' time zone?", cboTimeZone.Items[timeZoneIdx], cboTimeZone.Items[cboTimeZone.SelectedIndex]), "Change Time Zone", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { return; } // Get the time zone IDs if (timeZoneIdx == 0) { sourceTZ = null; } else { sourceTZ = (string)cboTimeZone.Items[timeZoneIdx]; } destTZ = (string)cboTimeZone.Items[cboTimeZone.SelectedIndex]; // Convert the times if (sourceTZ == null) { if (dtpStartDate.Checked) { dti = VCalendar.LocalTimeToTimeZoneTime(dtpStartDate.Value, destTZ); dtpStartDate.Value = dti.StartDateTime; } if (dtpEndDate.Checked) { dti = VCalendar.LocalTimeToTimeZoneTime(dtpEndDate.Value, destTZ); dtpEndDate.Value = dti.StartDateTime; } } else { if (dtpStartDate.Checked) { dti = VCalendar.TimeZoneToTimeZone(dtpStartDate.Value, sourceTZ, destTZ); dtpStartDate.Value = dti.StartDateTime; } if (dtpEndDate.Checked) { dti = VCalendar.TimeZoneToTimeZone(dtpEndDate.Value, sourceTZ, destTZ); dtpEndDate.Value = dti.StartDateTime; } } ucFreeBusy.ApplyTimeZone(sourceTZ, destTZ); timeZoneIdx = cboTimeZone.SelectedIndex; }
/// <summary> /// This method is used to return all recurring instances between the two specified date/times based on /// the current settings. /// </summary> /// <param name="fromDate">The minimum date/time on or after which instances should occur. This will /// include an instance if it starts before the date/time but overlaps it when its duration is added to /// its start time.</param> /// <param name="toDate">The maximum date/time on or before which instances should occur. This will /// include an instance if it starts on or before the specified date/time regardless of its duration.</param> /// <param name="inLocalTime">If true, the date/time parameters are assumed to be in local time and the /// returned date/times are expressed in local time. If false, the date/time parameters are assumed to /// be in the time zone of the object and the returned date/times are expressed in the time zone of the /// object as specified by the <see cref="TimeZoneId"/> property. If no time zone ID has been specified /// or it cannot be found, local time is used.</param> /// <returns>Returns a <see cref="DateTimeInstanceCollection"/> containing <see cref="DateTimeInstance" /> /// objects that represent all instances found between the two specified date/times. Instances may have /// a different duration if created from an <c>RDATE</c> property.</returns> /// <exception cref="ArgumentException">This is thrown if a start date has not been specified (it equals /// <c>DateTime.MinValue</c>) or the duration is negative.</exception> /// <seealso cref="AllInstances"/> /// <seealso cref="OccursOn"/> public DateTimeInstanceCollection InstancesBetween(DateTime fromDate, DateTime toDate, bool inLocalTime) { DateTimeCollection recurDates; Period p; DateTime endDate, tempDate1 = DateTime.MaxValue, tempDate2; int idx, count; string timeZoneID = this.TimeZoneId; PeriodCollection periods = new PeriodCollection(); DateTime startDate = this.StartDateTime.TimeZoneDateTime; Duration dur = this.InstanceDuration; if (startDate == DateTime.MinValue) { throw new ArgumentException(LR.GetString("ExNoComponentStartDate")); } if (dur.Ticks < 0) { throw new ArgumentException(LR.GetString("ExRONegativeDuration")); } // Convert fromDate and toDate to time zone time if inLocalTime is true. Recurrences are always // resolved in the time of the object. if (inLocalTime && timeZoneID != null) { fromDate = VCalendar.LocalTimeToTimeZoneTime(fromDate, timeZoneID).StartDateTime; toDate = VCalendar.LocalTimeToTimeZoneTime(toDate, timeZoneID).StartDateTime; } // There might be instances that overlap the requested range so we'll adjust the From date/time by // the duration to catch them. if (dur.Ticks > 1) { fromDate = fromDate.Add(new TimeSpan(0 - dur.Ticks + 1)); } // As per the spec, the start date/time is always included in the set but only if it (or it's // duration) is within the requested range. However, if it is recurring and the custom Exclude Start // property is set to true, it is not added. p = new Period(startDate, dur); if (((p.StartDateTime >= fromDate && p.StartDateTime <= toDate) || (p.EndDateTime >= fromDate && p.EndDateTime <= toDate)) && (!this.IsRecurring || !this.ExcludeStartDateTime)) { periods.Add(p); } // If it isn't recurring or starts after the requested end date, just return the collection as it is if (this.IsRecurring && startDate <= toDate) { // Expand each recurrence rule foreach (RRuleProperty rr in this.RecurrenceRules) { // If used, RecurUntil is stored in Universal Time which is converted to local time. If a // time zone ID is specified, convert it to that time zone temporarily to make sure things // are calculated correctly. if (rr.Recurrence.MaximumOccurrences == 0 && timeZoneID != null) { tempDate1 = rr.Recurrence.RecurUntil; rr.Recurrence.RecurUntil = VCalendar.LocalTimeToTimeZoneTime(tempDate1, timeZoneID).StartDateTime; } rr.Recurrence.StartDateTime = startDate; recurDates = rr.Recurrence.InstancesBetween(fromDate, toDate); if (rr.Recurrence.MaximumOccurrences == 0 && timeZoneID != null) { rr.Recurrence.RecurUntil = tempDate1; } foreach (DateTime dt in recurDates) { periods.Add(new Period(dt, dur)); } } // Add on recurrence dates within the range foreach (RDateProperty rd in this.RecurDates) { if (rd.ValueLocation != ValLocValue.Period) { // If it's not a period, use the component's duration. If it's only a date, assume it's // the whole day. The spec is not clear on this so I'm making a best guess. if (rd.ValueLocation == ValLocValue.DateTime) { endDate = rd.TimeZoneDateTime.Add(dur.TimeSpan); } else { endDate = rd.TimeZoneDateTime.Add(new TimeSpan(TimeSpan.TicksPerDay)); } if ((rd.TimeZoneDateTime >= fromDate && rd.TimeZoneDateTime <= toDate) || (endDate >= fromDate && endDate <= toDate)) { periods.Add(new Period(rd.TimeZoneDateTime, endDate)); } } else { // As with Recurrence.RecurUntil, the period values are in Universal Time so convert them // to the time zone time for proper comparison. tempDate1 = rd.PeriodValue.StartDateTime; tempDate2 = rd.PeriodValue.EndDateTime; if (timeZoneID != null) { tempDate1 = VCalendar.LocalTimeToTimeZoneTime(tempDate1, timeZoneID).StartDateTime; } if (timeZoneID != null) { tempDate2 = VCalendar.LocalTimeToTimeZoneTime(tempDate2, timeZoneID).StartDateTime; } if ((tempDate1 >= fromDate && tempDate1 <= toDate) || (tempDate2 >= fromDate && tempDate2 <= toDate)) { periods.Add(new Period(tempDate1, tempDate2)); } } } // Expand exception rules and filter out those instances count = periods.Count; foreach (RRuleProperty er in this.ExceptionRules) { // Same as above if (er.Recurrence.MaximumOccurrences == 0 && timeZoneID != null) { tempDate1 = er.Recurrence.RecurUntil; er.Recurrence.RecurUntil = VCalendar.LocalTimeToTimeZoneTime(tempDate1, timeZoneID).StartDateTime; } er.Recurrence.StartDateTime = startDate; recurDates = er.Recurrence.InstancesBetween(fromDate, toDate); if (er.Recurrence.MaximumOccurrences == 0 && timeZoneID != null) { er.Recurrence.RecurUntil = tempDate1; } foreach (DateTime dt in recurDates) { for (idx = 0; idx < count; idx++) { if (periods[idx].StartDateTime == dt) { periods.RemoveAt(idx); idx--; count--; } } } } // Filter out any exception dates foreach (ExDateProperty ed in this.ExceptionDates) { DateTime dt = ed.TimeZoneDateTime; // If it's only a date, assume it's the whole day and remove all instances on that day // regardless of the time. The spec is not clear on this so I'm making a best guess. if (ed.ValueLocation == ValLocValue.DateTime) { if (dt >= fromDate && dt <= toDate) { for (idx = 0; idx < count; idx++) { if (periods[idx].StartDateTime == dt) { periods.RemoveAt(idx); idx--; count--; } } } } else if (dt >= fromDate.Date && dt <= toDate.Date) { for (idx = 0; idx < count; idx++) { if (periods[idx].StartDateTime.Date == dt) { periods.RemoveAt(idx); idx--; count--; } } } } // Sort the periods and remove duplicates periods.Sort(true); for (idx = 1; idx < count; idx++) { if (periods[idx] == periods[idx - 1]) { periods.RemoveAt(idx); idx--; count--; } } } // Now convert the periods to DateTimeInstances that include the necessary time zone information DateTimeInstanceCollection dtic = new DateTimeInstanceCollection(); DateTimeInstance dti, dtiEnd; // Always in local time if there is no time zone ID if (timeZoneID == null) { inLocalTime = true; } foreach (Period pd in periods) { if (inLocalTime) { dti = VCalendar.TimeZoneTimeToLocalTime(pd.StartDateTime, timeZoneID); dtiEnd = VCalendar.TimeZoneTimeToLocalTime(pd.EndDateTime, timeZoneID); } else { dti = VCalendar.TimeZoneTimeInfo(pd.StartDateTime, timeZoneID); dtiEnd = VCalendar.TimeZoneTimeInfo(pd.EndDateTime, timeZoneID); } dti.Duration = pd.Duration; dti.EndDateTime = dtiEnd.EndDateTime; dti.EndIsDaylightSavingTime = dtiEnd.EndIsDaylightSavingTime; dti.EndTimeZoneName = dtiEnd.EndTimeZoneName; // If it already contains the entry and it is in DST, bump it forward an hour to account for the // time adjustment. This will happen on hourly, minutely, and secondly recurrence patterns. By // moving duplicates forward an hour, we retain the expected number of occurrences. if (!dtic.Contains(dti)) { dtic.Add(dti); } else if (dti.StartIsDaylightSavingTime) { dti.StartDateTime = dti.StartDateTime.AddHours(1); dti.EndDateTime = dti.EndDateTime.AddHours(1); dtic.Add(dti); } } return(dtic); // And finally, we are done }