/// <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 converts the date/time info to local time /// </summary> /// <remarks>This converts the date time instance information from its current time zone identified by /// the <see cref="TimeZoneId"/> property to local time. The time zone ID property will be set to null /// after conversion to indicate that the instance is in local time.</remarks> public void ToLocalTime() { DateTimeInstance dti, dtiEnd; // Already in local time? if (timeZoneID == null) { return; } dti = VCalendar.TimeZoneTimeToLocalTime(startDate, timeZoneID); dtiEnd = VCalendar.TimeZoneTimeToLocalTime(endDate, timeZoneID); timeZoneID = null; startDate = dti.StartDateTime; startIsDST = dti.StartIsDaylightSavingTime; startTZName = dti.StartTimeZoneName; endDate = dtiEnd.EndDateTime; endIsDST = dtiEnd.EndIsDaylightSavingTime; endTZName = dtiEnd.EndTimeZoneName; }
/// <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 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 }