diff --git a/net-core/Ical.Net/Ical.Net.Collections/Constants.cs b/net-core/Ical.Net/Ical.Net.Collections/Constants.cs new file mode 100644 index 00000000..c65b02d9 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Constants.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ical.Net.Collections +{ + public class ObjectEventArgs : + EventArgs + { + public T First { get; set; } + public TU Second { get; set; } + + public ObjectEventArgs(T first, TU second) + { + First = first; + Second = second; + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/Enumerators/GroupedListEnumerator.cs b/net-core/Ical.Net/Ical.Net.Collections/Enumerators/GroupedListEnumerator.cs new file mode 100644 index 00000000..7f5b0a8a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Enumerators/GroupedListEnumerator.cs @@ -0,0 +1,99 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Ical.Net.Collections.Enumerators +{ + public class GroupedListEnumerator : + IEnumerator + { + private readonly HashSet> _lists; + private IEnumerator> _listsEnumerator; + private IEnumerator _listEnumerator; + + public GroupedListEnumerator(HashSet> lists) + { + _lists = lists; + } + + public virtual TType Current => _listEnumerator == null + ? default(TType) + :_listEnumerator.Current; + + public virtual void Dispose() + { + Reset(); + } + + private void DisposeListEnumerator() + { + if (_listEnumerator == null) + { + return; + } + _listEnumerator.Dispose(); + _listEnumerator = null; + } + + object IEnumerator.Current => _listEnumerator == null + ? default(TType) + : _listEnumerator.Current; + + private bool MoveNextList() + { + if (_listsEnumerator == null) + { + _listsEnumerator = _lists.GetEnumerator(); + } + + if (_listsEnumerator == null) + { + return false; + } + + if (!_listsEnumerator.MoveNext()) + { + return false; + } + + DisposeListEnumerator(); + if (_listsEnumerator.Current == null) + { + return false; + } + + _listEnumerator = _listsEnumerator.Current.GetEnumerator(); + return true; + } + + public virtual bool MoveNext() + { + if (_listEnumerator != null) + { + if (_listEnumerator.MoveNext()) + { + return true; + } + DisposeListEnumerator(); + if (MoveNextList()) + return MoveNext(); + } + else + { + if (MoveNextList()) + return MoveNext(); + } + return false; + } + + public virtual void Reset() + { + if (_listsEnumerator == null) + { + return; + } + + _listsEnumerator.Dispose(); + _listsEnumerator = null; + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/GroupedList.cs b/net-core/Ical.Net/Ical.Net.Collections/GroupedList.cs new file mode 100644 index 00000000..925908e2 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/GroupedList.cs @@ -0,0 +1,257 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.Collections.Enumerators; +using Ical.Net.Collections.Interfaces; + +namespace Ical.Net.Collections +{ + /// A list of objects that are keyed. + public class GroupedList : + IGroupedList + where TItem : class, IGroupedObject + { + private readonly HashSet> _lists = new HashSet>(); + private readonly Dictionary> _dictionary = new Dictionary>(); + + private HashSet EnsureList(TGroup group) + { + if (group == null) + { + return null; + } + + if (_dictionary.ContainsKey(group)) + { + return _dictionary[group]; + } + + var list = new HashSet(); + _dictionary[group] = list; + + _lists.Add(list); + return list; + } + + private HashSet ListForIndex(int index, out int relativeIndex) + { + foreach (var list in _lists.Where(list => list.Count > index)) + { + relativeIndex = index; + return list; + } + relativeIndex = -1; + return null; + } + + public event EventHandler> ItemAdded; + + protected void OnItemAdded(TItem obj, int index) + { + ItemAdded?.Invoke(this, new ObjectEventArgs(obj, index)); + } + + public virtual void Add(TItem item) + { + if (item == null) + { + return; + } + + // Add a new list if necessary + var group = item.Group; + var list = EnsureList(group); + var index = list.Count; + list.Add(item); + OnItemAdded(item, index); + } + + public virtual int IndexOf(TItem item) + { + return 0; + //var group = item.Group; + //if (!_dictionary.ContainsKey(group)) + //{ + // return -1; + //} + + //// Get the list associated with this object's group + //var list = _dictionary[group]; + + //// Find the object within the list. + //var index = list.IndexOf(item); + + //// Return the index within the overall KeyedList + //if (index >= 0) + // return index; + //return -1; + } + + public virtual void Clear(TGroup group) + { + if (!_dictionary.ContainsKey(group)) + { + return; + } + + // Clear the list (note that this also clears the list in the _Lists object). + _dictionary[group].Clear(); + } + + public virtual void Clear() + { + _dictionary.Clear(); + _lists.Clear(); + } + + public virtual bool ContainsKey(TGroup group) => _dictionary.ContainsKey(group); + + public virtual int Count => _lists.Sum(list => list.Count); + + public virtual int CountOf(TGroup group) + { + return _dictionary.ContainsKey(group) + ? _dictionary[group].Count + : 0; + } + + public virtual IEnumerable Values() => _dictionary.Values.SelectMany(i => i); + + public virtual IEnumerable AllOf(TGroup group) + { + return _dictionary.ContainsKey(group) + ? (IEnumerable) _dictionary[group] + : new TItem[0]; + } + + public virtual bool Remove(TItem obj) + { + var group = obj.Group; + if (!_dictionary.ContainsKey(group)) + { + return false; + } + + return _dictionary[obj.Group].Remove(obj); + + //var items = _dictionary[group]; + //var index = items.IndexOf(obj); + + //if (index < 0) + //{ + // return false; + //} + + //items.RemoveAt(index); + //return true; + } + + public virtual bool Remove(TGroup group) + { + if (!_dictionary.ContainsKey(group)) + { + return false; + } + + return _dictionary.Remove(group); + + //var list = _dictionary[group]; + //for (var i = list.Count - 1; i >= 0; i--) + //{ + // list.RemoveAt(i); + //} + //return true; + } + + public virtual bool Contains(TItem item) + { + var group = item.Group; + return _dictionary.ContainsKey(group) && _dictionary[group].Contains(item); + } + + public virtual void CopyTo(TItem[] array, int arrayIndex) + { + _dictionary.SelectMany(kvp => kvp.Value).ToArray().CopyTo(array, arrayIndex); + } + + public virtual bool IsReadOnly => false; + + public virtual void Insert(int index, TItem item) + { + if (!_dictionary.ContainsKey(item.Group)) + { + _dictionary[item.Group] = new HashSet {item}; + return; + } + + _dictionary[item.Group].Add(item); + //int relativeIndex; + //var list = ListForIndex(index, out relativeIndex); + //if (list == null) + //{ + // return; + //} + + //list.Insert(relativeIndex, item); + OnItemAdded(item, index); + } + + public virtual void RemoveAt(int index) + { + if (true) + { + return; + } + //int relativeIndex; + //var list = ListForIndex(index, out relativeIndex); + //if (list == null) + //{ + // return; + //} + //var item = list[relativeIndex]; + //list.RemoveAt(relativeIndex); + } + + public virtual TItem this[int index] + { + get + { + return default(TItem); + //int relativeIndex; + //var list = ListForIndex(index, out relativeIndex); + //return list?[relativeIndex]; + } + set + { + if (true) + { + return; + } + //int relativeIndex; + //var list = ListForIndex(index, out relativeIndex); + //if (list == null) + //{ + // return; + //} + + //// Remove the item at that index and replace it + //var item = list[relativeIndex]; + //list.RemoveAt(relativeIndex); + //list.Insert(relativeIndex, value); + + //OnItemAdded(item, index); + } + } + + public IEnumerator GetEnumerator() + { + return new GroupedListEnumerator(_lists); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new GroupedListEnumerator(_lists); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/GroupedValueList.cs b/net-core/Ical.Net/Ical.Net.Collections/GroupedValueList.cs new file mode 100644 index 00000000..501ca84c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/GroupedValueList.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.Collections.Interfaces; +using Ical.Net.Collections.Proxies; + +namespace Ical.Net.Collections +{ + public class GroupedValueList : + GroupedList + where TInterface : class, IGroupedObject, IValueObject + where TItem : new() + { + public virtual void Set(TGroup group, TValueType value) + { + Set(group, new[] { value }); + } + + public virtual void Set(TGroup group, IEnumerable values) + { + if (ContainsKey(group)) + { + var items = AllOf(group); + if (items != null) + { + // Add a value to the first matching item in the list + items.First().SetValue(values); + return; + } + } + + // No matching item was found, add a new item to the list + var obj = Activator.CreateInstance(typeof(TItem)) as TInterface; + obj.Group = group; + Add(obj); + obj.SetValue(values); + } + + public virtual TType Get(TGroup group) + { + var firstItem = AllOf(group).FirstOrDefault(); + if (firstItem?.Values != null) + { + return firstItem + .Values + .OfType() + .FirstOrDefault(); + } + return default(TType); + } + + public virtual IList GetMany(TGroup group) + { + return new GroupedValueListProxy(this, group); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/Ical.Net.Collections.csproj b/net-core/Ical.Net/Ical.Net.Collections/Ical.Net.Collections.csproj new file mode 100644 index 00000000..4f8972e6 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Ical.Net.Collections.csproj @@ -0,0 +1,16 @@ + + + netstandard1.4 + + + + + + + + + + + + + \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IGroupedCollection.cs b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IGroupedCollection.cs new file mode 100644 index 00000000..ee06f9cc --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IGroupedCollection.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; + +namespace Ical.Net.Collections.Interfaces +{ + //Isn't this effectively a Dictionary>? + public interface IGroupedCollection : + ICollection + where TItem : class, IGroupedObject + { + /// + /// Fired after an item is added to the collection. + /// + event EventHandler> ItemAdded; + + /// + /// Removes all items with the matching group from the collection. + /// + /// True if the object was removed, false otherwise. + bool Remove(TGroup group); + + /// + /// Clears all items matching the specified group. + /// + void Clear(TGroup group); + + /// + /// Returns true if the list contains at least one + /// object with a matching group, false otherwise. + /// + bool ContainsKey(TGroup group); + + /// + /// Returns the number of objects in the list + /// with a matching group. + /// + int CountOf(TGroup group); + + /// + /// Returns a list of objects that + /// match the specified group. + /// + IEnumerable AllOf(TGroup group); + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IGroupedList.cs b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IGroupedList.cs new file mode 100644 index 00000000..34147ffd --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IGroupedList.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace Ical.Net.Collections.Interfaces +{ + public interface IGroupedList : + IGroupedCollection, + IList + where TItem : class, IGroupedObject + { + /// + /// Returns the index of the given item + /// within the list, or -1 if the item + /// is not found in the list. + /// + new int IndexOf(TItem obj); + + /// + /// Gets the object at the specified index. + /// + new TItem this[int index] { get; } + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IGroupedObject.cs b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IGroupedObject.cs new file mode 100644 index 00000000..a7d082d4 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IGroupedObject.cs @@ -0,0 +1,7 @@ +namespace Ical.Net.Collections.Interfaces +{ + public interface IGroupedObject + { + TGroup Group { get; set; } + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IMultiLinkedList.cs b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IMultiLinkedList.cs new file mode 100644 index 00000000..c83dbfe4 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IMultiLinkedList.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Ical.Net.Collections.Interfaces +{ + public interface IMultiLinkedList : + IList + { + int StartIndex { get; } + int ExclusiveEnd { get; } + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IValueObject.cs b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IValueObject.cs new file mode 100644 index 00000000..d6c61e9d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/IValueObject.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Ical.Net.Collections.Interfaces +{ + public interface IValueObject + { + IEnumerable Values { get; } + + bool ContainsValue(T value); + void SetValue(T value); + void SetValue(IEnumerable values); + void AddValue(T value); + void RemoveValue(T value); + int ValueCount { get; } + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/Interfaces/Proxies/IGroupedCollectionProxy.cs b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/Proxies/IGroupedCollectionProxy.cs new file mode 100644 index 00000000..9d06eecb --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Interfaces/Proxies/IGroupedCollectionProxy.cs @@ -0,0 +1,11 @@ +namespace Ical.Net.Collections.Interfaces.Proxies +{ + public interface IGroupedCollectionProxy : + IGroupedCollection + where TOriginal : class, IGroupedObject + where TNew : class, TOriginal + { + IGroupedCollection RealObject { get; } + void SetProxiedObject(IGroupedCollection realObject); + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/MultiLinkedList.cs b/net-core/Ical.Net/Ical.Net.Collections/MultiLinkedList.cs new file mode 100644 index 00000000..3b04669a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/MultiLinkedList.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Ical.Net.Collections +{ + public class MultiLinkedList : + List + { + public virtual int StartIndex => 0; + public virtual int ExclusiveEnd => Count > 0 + ? StartIndex + Count + : StartIndex; + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/Proxies/GroupedCollectionProxy.cs b/net-core/Ical.Net/Ical.Net.Collections/Proxies/GroupedCollectionProxy.cs new file mode 100644 index 00000000..b1fd9381 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Proxies/GroupedCollectionProxy.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.Collections.Interfaces; +using Ical.Net.Collections.Interfaces.Proxies; + +namespace Ical.Net.Collections.Proxies +{ + /// + /// A proxy for a keyed list. + /// + + public class GroupedCollectionProxy : + IGroupedCollectionProxy + where TOriginal : class, IGroupedObject + where TNew : class, TOriginal + { + private readonly Func _predicate; + + public GroupedCollectionProxy(IGroupedCollection realObject, Func predicate = null) + { + _predicate = predicate ?? (o => true); + SetProxiedObject(realObject); + } + + public virtual event EventHandler> ItemAdded; + public virtual event EventHandler> ItemRemoved; + + protected void OnItemAdded(TNew item, int index) + { + ItemAdded?.Invoke(this, new ObjectEventArgs(item, index)); + } + + protected void OnItemRemoved(TNew item, int index) + { + ItemRemoved?.Invoke(this, new ObjectEventArgs(item, index)); + } + + public virtual bool Remove(TGroup group) + { + return RealObject.Remove(group); + } + + public virtual void Clear(TGroup group) + { + RealObject.Clear(group); + } + + public virtual bool ContainsKey(TGroup group) + { + return RealObject.ContainsKey(group); + } + + public virtual int CountOf(TGroup group) + { + return RealObject.OfType().Count(); + } + + public virtual IEnumerable AllOf(TGroup group) + { + return RealObject + .AllOf(group) + .OfType() + .Where(_predicate); + } + + public virtual void Add(TNew item) + { + RealObject.Add(item); + } + + public virtual void Clear() + { + // Only clear items of this type + // that match the predicate. + + var items = RealObject + .OfType() + .ToArray(); + + foreach (var item in items) + { + RealObject.Remove(item); + } + } + + public virtual bool Contains(TNew item) => RealObject.Contains(item); + + public virtual void CopyTo(TNew[] array, int arrayIndex) + { + var i = 0; + foreach (var item in this) + { + array[arrayIndex + (i++)] = item; + } + } + + public virtual int Count => RealObject + .OfType() + .Count(); + + public virtual bool IsReadOnly => false; + + public virtual bool Remove(TNew item) + { + return RealObject.Remove(item); + } + + public virtual IEnumerator GetEnumerator() + { + return RealObject + .OfType() + .GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return RealObject + .OfType() + .GetEnumerator(); + } + + public IGroupedCollection RealObject { get; private set; } + + public virtual void SetProxiedObject(IGroupedCollection realObject) + { + RealObject = realObject; + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/Proxies/GroupedValueListProxy.cs b/net-core/Ical.Net/Ical.Net.Collections/Proxies/GroupedValueListProxy.cs new file mode 100644 index 00000000..2b35b42f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/Proxies/GroupedValueListProxy.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.Collections.Interfaces; + +namespace Ical.Net.Collections.Proxies +{ + /// + /// A proxy for a keyed list. + /// + public class GroupedValueListProxy : IList + where TInterface : class, IGroupedObject, IValueObject + where TItem : new() + { + private readonly GroupedValueList _realObject; + private readonly TGroup _group; + private TInterface _container; + + public GroupedValueListProxy(GroupedValueList realObject, TGroup group) + { + _realObject = realObject; + _group = group; + } + + private TInterface EnsureContainer() + { + if (_container != null) + { + return _container; + } + + // Find an item that matches our group + _container = Items.FirstOrDefault(); + + // If no item is found, create a new object and add it to the list + if (!Equals(_container, default(TInterface))) + { + return _container; + } + var container = new TItem(); + if (!(container is TInterface)) + { + throw new Exception("Could not create a container for the value - the container is not of type " + typeof(TInterface).Name); + } + + _container = (TInterface)(object)container; + _container.Group = _group; + _realObject.Add(_container); + return _container; + } + + private void IterateValues(Func, int, int, bool> action) + { + var i = 0; + foreach (var obj in _realObject) + { + // Get the number of items of the target value i this object + var count = obj.Values?.OfType().Count() ?? 0; + + // Perform some action on this item + if (!action(obj, i, count)) + return; + + i += count; + } + } + + private IEnumerator GetEnumeratorInternal() + { + return Items + .Where(o => o.ValueCount > 0) + .SelectMany(o => o.Values.OfType()) + .GetEnumerator(); + } + + public virtual void Add(TNewValue item) + { + // Add the value to the object + if (item is TOriginalValue) + { + var value = (TOriginalValue)(object)item; + EnsureContainer().AddValue(value); + } + } + + public virtual void Clear() + { + var items = Items.Where(o => o.Values != null); + + foreach (var original in items) + { + // Clear all values from each matching object + original.SetValue(default(TOriginalValue)); + } + } + + public virtual bool Contains(TNewValue item) => Items.Any(o => o.ContainsValue((TOriginalValue)(object)item)); + + public virtual void CopyTo(TNewValue[] array, int arrayIndex) + { + Items + .Where(o => o.Values != null) + .SelectMany(o => o.Values) + .ToArray() + .CopyTo(array, arrayIndex); + } + + public virtual int Count => Items.Sum(o => o.ValueCount); + + public virtual bool IsReadOnly => false; + + public virtual bool Remove(TNewValue item) + { + if (!(item is TOriginalValue)) + { + return false; + } + + var value = (TOriginalValue)(object)item; + var container = Items.FirstOrDefault(o => o.ContainsValue(value)); + + if (container == null) + { + return false; + } + + container.RemoveValue(value); + return true; + } + + public virtual IEnumerator GetEnumerator() => GetEnumeratorInternal(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumeratorInternal(); + + public virtual int IndexOf(TNewValue item) + { + return 0; + //var index = -1; + + //if (!(item is TOriginalValue)) + //{ + // return index; + //} + + //var value = (TOriginalValue)(object)item; + //IterateValues((o, i, count) => + //{ + // if (o.Values != null && o.Values.Contains(value)) + // { + // var list = o.Values.ToList(); + // index = i + list.IndexOf(value); + // return false; + // } + // return true; + //}); + + //return index; + } + + public virtual void Insert(int index, TNewValue item) + { + IterateValues((o, i, count) => + { + var value = (TOriginalValue)(object)item; + + // Determine if this index is found within this object + if (index < i || index >= count) + { + return true; + } + + // Convert the items to a list + var items = o.Values.ToList(); + // Insert the item at the relative index within the list + items.Insert(index - i, value); + // Set the new list + o.SetValue(items); + return false; + }); + } + + public virtual void RemoveAt(int index) + { + //IterateValues((o, i, count) => + //{ + // // Determine if this index is found within this object + // if (index >= i && index < count) + // { + // // Convert the items to a list + // var items = o.Values.ToList(); + // // Remove the item at the relative index within the list + // items.RemoveAt(index - i); + // // Set the new list + // o.SetValue(items); + // return false; + // } + // return true; + //}); + } + + public virtual TNewValue this[int index] + { + get + { + if (index >= 0 && index < Count) + { + return this + .Skip(index) + .FirstOrDefault(); + } + return default(TNewValue); + } + set + { + if (true) + { + return; + } + //if (index >= 0 && index < Count) + //{ + // if (!Equals(value, default(TNewValue))) + // { + // Insert(index, value); + // index++; + // } + // RemoveAt(index); + //} + } + } + + public virtual IEnumerable Items + { + get + { + return _group == null + ? _realObject + : _realObject.AllOf(_group); + } + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.Collections/app.config b/net-core/Ical.Net/Ical.Net.Collections/app.config new file mode 100644 index 00000000..0dff63a0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.Collections/app.config @@ -0,0 +1,3 @@ + + + diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/AlarmTest.cs b/net-core/Ical.Net/Ical.Net.UnitTests/AlarmTest.cs new file mode 100644 index 00000000..9751fc1a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/AlarmTest.cs @@ -0,0 +1,167 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; + +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class AlarmTest + { + private const string _tzid = "US-Eastern"; + + public void TestAlarm(string calendarString, List dates, CalDateTime start, CalDateTime end) + { + var iCal = Calendar.LoadFromStream(new StringReader(calendarString))[0]; + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); + + // Poll all alarms that occurred between Start and End + var alarms = evt.PollAlarms(start, end); + + foreach (var alarm in alarms) + Assert.IsTrue(dates.Contains(alarm.DateTime), "Alarm triggers at " + alarm.Period.StartTime + ", but it should not."); + Assert.IsTrue(dates.Count == alarms.Count, "There were " + alarms.Count + " alarm occurrences; there should have been " + dates.Count + "."); + } + + [Test, Category("Alarm")] + public void Alarm1() + { + var dateTimes = new List(); + dateTimes.AddRange(new[] + { + new CalDateTime(2006, 7, 18, 9, 30, 0, _tzid) + }); + + var content = IcsFiles.ALARM1; + TestAlarm(content, dateTimes, new CalDateTime(2006, 7, 1, _tzid), new CalDateTime(2006, 9, 1, _tzid)); + } + + [Test, Category("Alarm")] + public void Alarm2() + { + var dateTimes = new List(); + dateTimes.AddRange(new[] + { + new CalDateTime(2006, 7, 18, 9, 30, 0, _tzid), + new CalDateTime(2006, 7, 20, 9, 30, 0, _tzid), + new CalDateTime(2006, 7, 22, 9, 30, 0, _tzid), + new CalDateTime(2006, 7, 24, 9, 30, 0, _tzid), + new CalDateTime(2006, 7, 26, 9, 30, 0, _tzid), + new CalDateTime(2006, 7, 28, 9, 30, 0, _tzid), + new CalDateTime(2006, 7, 30, 9, 30, 0, _tzid), + new CalDateTime(2006, 8, 1, 9, 30, 0, _tzid), + new CalDateTime(2006, 8, 3, 9, 30, 0, _tzid), + new CalDateTime(2006, 8, 5, 9, 30, 0, _tzid) + }); + + var content = IcsFiles.ALARM2; + TestAlarm(content, dateTimes, new CalDateTime(2006, 7, 1, _tzid), new CalDateTime(2006, 9, 1, _tzid)); + } + + [Test, Category("Alarm")] + public void Alarm3() + { + var dateTimes = new List(); + dateTimes.AddRange(new[] + { + new CalDateTime(1998, 2, 11, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 11, 9, 0, 0, _tzid), + new CalDateTime(1998, 11, 11, 9, 0, 0, _tzid), + new CalDateTime(1999, 8, 11, 9, 0, 0, _tzid), + new CalDateTime(2000, 10, 11, 9, 0, 0, _tzid) + }); + + var content = IcsFiles.ALARM3; + TestAlarm(content, dateTimes, new CalDateTime(1997, 1, 1, _tzid), new CalDateTime(2000, 12, 31, _tzid)); + } + + [Test, Category("Alarm")] + public void Alarm4() + { + var dateTimes = new List(); + dateTimes.AddRange(new[] + { + new CalDateTime(1998, 2, 11, 9, 0, 0, _tzid), + new CalDateTime(1998, 2, 11, 11, 0, 0, _tzid), + new CalDateTime(1998, 2, 11, 13, 0, 0, _tzid), + new CalDateTime(1998, 2, 11, 15, 0, 0, _tzid), + new CalDateTime(1998, 3, 11, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 11, 11, 0, 0, _tzid), + new CalDateTime(1998, 3, 11, 13, 0, 0, _tzid), + new CalDateTime(1998, 3, 11, 15, 0, 0, _tzid), + new CalDateTime(1998, 11, 11, 9, 0, 0, _tzid), + new CalDateTime(1998, 11, 11, 11, 0, 0, _tzid), + new CalDateTime(1998, 11, 11, 13, 0, 0, _tzid), + new CalDateTime(1998, 11, 11, 15, 0, 0, _tzid), + new CalDateTime(1999, 8, 11, 9, 0, 0, _tzid), + new CalDateTime(1999, 8, 11, 11, 0, 0, _tzid), + new CalDateTime(1999, 8, 11, 13, 0, 0, _tzid), + new CalDateTime(1999, 8, 11, 15, 0, 0, _tzid), + new CalDateTime(2000, 10, 11, 9, 0, 0, _tzid), + new CalDateTime(2000, 10, 11, 11, 0, 0, _tzid), + new CalDateTime(2000, 10, 11, 13, 0, 0, _tzid), + new CalDateTime(2000, 10, 11, 15, 0, 0, _tzid) + }); + + var content = IcsFiles.ALARM4; + TestAlarm(content, dateTimes, new CalDateTime(1997, 1, 1, _tzid), new CalDateTime(2000, 12, 31, _tzid)); + } + + [Test, Category("Alarm")] + public void Alarm5() + { + var dateTimes = new List(); + dateTimes.AddRange(new[] + { + new CalDateTime(1998, 1, 2, 8, 0, 0, _tzid) + }); + + var content = IcsFiles.ALARM5; + TestAlarm(content, dateTimes, new CalDateTime(1997, 7, 1, _tzid), new CalDateTime(2000, 12, 31, _tzid)); + } + + [Test, Category("Alarm")] + public void Alarm6() + { + var dateTimes = new List(); + dateTimes.AddRange(new[] + { + new CalDateTime(1998, 1, 2, 8, 0, 0, _tzid), + new CalDateTime(1998, 1, 5, 8, 0, 0, _tzid), + new CalDateTime(1998, 1, 8, 8, 0, 0, _tzid), + new CalDateTime(1998, 1, 11, 8, 0, 0, _tzid), + new CalDateTime(1998, 1, 14, 8, 0, 0, _tzid), + new CalDateTime(1998, 1, 17, 8, 0, 0, _tzid) + }); + + var content = IcsFiles.ALARM6; + TestAlarm(content, dateTimes, new CalDateTime(1997, 7, 1, _tzid), new CalDateTime(2000, 12, 31, _tzid)); + } + + [Test, Category("Alarm")] + public void Alarm7() + { + var dateTimes = new List(); + dateTimes.AddRange(new[] + { + new CalDateTime(2006, 7, 18, 10, 30, 0, _tzid), + new CalDateTime(2006, 7, 20, 10, 30, 0, _tzid), + new CalDateTime(2006, 7, 22, 10, 30, 0, _tzid), + new CalDateTime(2006, 7, 24, 10, 30, 0, _tzid), + new CalDateTime(2006, 7, 26, 10, 30, 0, _tzid), + new CalDateTime(2006, 7, 28, 10, 30, 0, _tzid), + new CalDateTime(2006, 7, 30, 10, 30, 0, _tzid), + new CalDateTime(2006, 8, 1, 10, 30, 0, _tzid), + new CalDateTime(2006, 8, 3, 10, 30, 0, _tzid), + new CalDateTime(2006, 8, 5, 10, 30, 0, _tzid) + }); + + var content = IcsFiles.ALARM7; + TestAlarm(content, dateTimes, new CalDateTime(2006, 7, 1, _tzid), new CalDateTime(2006, 9, 1, _tzid)); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/AttendeeTest.cs b/net-core/Ical.Net/Ical.Net.UnitTests/AttendeeTest.cs new file mode 100644 index 00000000..014180ae --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/AttendeeTest.cs @@ -0,0 +1,96 @@ +using Ical.Net.DataTypes; +using Ical.Net.Serialization.iCalendar.Serializers; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class AttendeeTest + { + internal static CalendarEvent VEventFactory() + { + return new CalendarEvent + { + Summary = "Testing", + Start = new CalDateTime(2010, 3, 25), + End = new CalDateTime(2010, 3, 26) + }; + } + + private static readonly IList _attendees = new List + { + new Attendee("MAILTO:james@example.com") + { + CommonName = "James James", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Tentative + }, + new Attendee("MAILTO:mary@example.com") + { + CommonName = "Mary Mary", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Accepted + } + }.AsReadOnly(); + + + /// + /// Ensures that attendees can be properly added to an event. + /// + [Test, Category("Attendee")] + public void Add1Attendee() + { + var evt = VEventFactory(); + Assert.AreEqual(0, evt.Attendees.Count); + + evt.Attendees.Add(_attendees[0]); + Assert.AreEqual(1, evt.Attendees.Count); + + //the properties below had been set to null during the Attendees.Add operation in NuGet version 2.1.4 + Assert.AreEqual(ParticipationRole.RequiredParticipant, evt.Attendees[0].Role); + Assert.AreEqual(EventParticipationStatus.Tentative, evt.Attendees[0].ParticipationStatus); + } + + [Test, Category("Attendee")] + public void Add2Attendees() + { + var evt = VEventFactory(); + Assert.AreEqual(0, evt.Attendees.Count); + + evt.Attendees.Add(_attendees[0]); + evt.Attendees.Add(_attendees[1]); + Assert.AreEqual(2, evt.Attendees.Count); + Assert.AreEqual(ParticipationRole.RequiredParticipant, evt.Attendees[1].Role); + + var cal = new Calendar(); + cal.Events.Add(evt); + var serializer = new CalendarSerializer(); + Console.Write(serializer.SerializeToString(cal)); + } + + /// + /// Ensures that attendees can be properly removed from an event. + /// + [Test, Category("Attendee")] + public void Remove1Attendee() + { + var evt = VEventFactory(); + Assert.AreEqual(0, evt.Attendees.Count); + + var attendee = _attendees.First(); + evt.Attendees.Add(attendee); + Assert.AreEqual(1, evt.Attendees.Count); + + evt.Attendees.Remove(attendee); + Assert.AreEqual(0, evt.Attendees.Count); + + evt.Attendees.Remove(_attendees.Last()); + Assert.AreEqual(0, evt.Attendees.Count); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/CalendarEventTest.cs b/net-core/Ical.Net/Ical.Net.UnitTests/CalendarEventTest.cs new file mode 100644 index 00000000..cd1dbae8 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/CalendarEventTest.cs @@ -0,0 +1,449 @@ +using Ical.Net.DataTypes; +using Ical.Net.ExtensionMethods; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Ical.Net.Serialization.iCalendar.Serializers; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class CalendarEventTest + { + private static readonly DateTime _now = DateTime.UtcNow; + private static readonly DateTime _later = _now.AddHours(1); + private static readonly string _uid = Guid.NewGuid().ToString(); + + /// + /// Ensures that events can be properly added to a calendar. + /// + [Test, Category("Event")] + public void Add1() + { + Calendar cal = new Calendar(); + + var evt = new CalendarEvent + { + Summary = "Testing", + Start = new CalDateTime(2010, 3, 25), + End = new CalDateTime(2010, 3, 26) + }; + + cal.Events.Add(evt); + Assert.AreEqual(1, cal.Children.Count); + Assert.AreSame(evt, cal.Children[0]); + } + + /// + /// Ensures that events can be properly removed from a calendar. + /// + [Test, Category("Event")] + public void Remove1() + { + Calendar cal = new Calendar(); + + var evt = new CalendarEvent + { + Summary = "Testing", + Start = new CalDateTime(2010, 3, 25), + End = new CalDateTime(2010, 3, 26) + }; + + cal.Events.Add(evt); + Assert.AreEqual(1, cal.Children.Count); + Assert.AreSame(evt, cal.Children[0]); + + cal.RemoveChild(evt); + Assert.AreEqual(0, cal.Children.Count); + Assert.AreEqual(0, cal.Events.Count); + } + + /// + /// Ensures that events can be properly removed from a calendar. + /// + [Test, Category("Event")] + public void Remove2() + { + Calendar cal = new Calendar(); + + var evt = new CalendarEvent + { + Summary = "Testing", + Start = new CalDateTime(2010, 3, 25), + End = new CalDateTime(2010, 3, 26) + }; + + cal.Events.Add(evt); + Assert.AreEqual(1, cal.Children.Count); + Assert.AreSame(evt, cal.Children[0]); + + cal.Events.Remove(evt); + Assert.AreEqual(0, cal.Children.Count); + Assert.AreEqual(0, cal.Events.Count); + } + + /// + /// Ensures that event DTSTAMP is set. + /// + [Test, Category("Event")] + public void EnsureDTSTAMPisNotNull() + { + Calendar cal = new Calendar(); + + // Do not set DTSTAMP manually + var evt = new CalendarEvent + { + Summary = "Testing", + Start = new CalDateTime(2010, 3, 25), + End = new CalDateTime(2010, 3, 26) + }; + + cal.Events.Add(evt); + Assert.IsNotNull(evt.DtStamp); + } + + /// + /// Ensures that automatically set DTSTAMP property is of kind UTC. + /// + [Test, Category("Event")] + public void EnsureDTSTAMPisOfTypeUTC() + { + Calendar cal = new Calendar(); + + var evt = new CalendarEvent + { + Summary = "Testing", + Start = new CalDateTime(2010, 3, 25), + End = new CalDateTime(2010, 3, 26) + }; + + cal.Events.Add(evt); + Assert.IsTrue(evt.DtStamp.IsUniversalTime, "DTSTAMP should always be of type UTC."); + } + + /// + /// Ensures that correct set DTSTAMP property is being serialized with kind UTC. + /// + [Test, Category("Deserialization")] + public void EnsureCorrectSetDTSTAMPisSerializedAsKindUTC() + { + var ical = new Ical.Net.Calendar(); + var evt = new Ical.Net.CalendarEvent(); + evt.DtStamp = new CalDateTime(new DateTime(2016, 8, 17, 2, 30, 0, DateTimeKind.Utc)); + ical.Events.Add(evt); + + var serializer = new Ical.Net.Serialization.iCalendar.Serializers.CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(ical); + + var lines = serializedCalendar.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); + var result = lines.First(s => s.StartsWith("DTSTAMP")); + Assert.AreEqual("DTSTAMP:20160817T023000Z", result); + } + + /// + /// Ensures that automatically set DTSTAMP property is being serialized with kind UTC. + /// + [Test, Category("Deserialization")] + public void EnsureAutomaticallySetDTSTAMPisSerializedAsKindUTC() + { + var ical = new Ical.Net.Calendar(); + var evt = new Ical.Net.CalendarEvent(); + ical.Events.Add(evt); + + var serializer = new Ical.Net.Serialization.iCalendar.Serializers.CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(ical); + + var lines = serializedCalendar.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); + var result = lines.First(s => s.StartsWith("DTSTAMP")); + Assert.AreEqual($"DTSTAMP:{evt.DtStamp.Year}{evt.DtStamp.Month:00}{evt.DtStamp.Day:00}T{evt.DtStamp.Hour:00}{evt.DtStamp.Minute:00}{evt.DtStamp.Second:00}Z", result); + } + + [Test] + public void EventWithExDateShouldNotBeEqualToSameEventWithoutExDate() + { + const string icalNoException = @"BEGIN:VCALENDAR +PRODID:-//Telerik Inc.//NONSGML RadScheduler//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +BEGIN:VTIMEZONE +TZID:UTC +BEGIN:STANDARD +TZNAME:UTC +TZOFFSETTO:+0000 +TZOFFSETFROM:+0000 +DTSTART:16010101T000000 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=UTC:20161020T170000 +DTEND;TZID=UTC:20161020T230000 +UID:694f818f-6d67-4307-9c4d-0b5211686ff0 +IMPORTANCE:None +RRULE:FREQ=DAILY +END:VEVENT +END:VCALENDAR"; + + const string icalWithException = @"BEGIN:VCALENDAR +PRODID:-//Telerik Inc.//NONSGML RadScheduler//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +BEGIN:VTIMEZONE +TZID:UTC +BEGIN:STANDARD +TZNAME:UTC +TZOFFSETTO:+0000 +TZOFFSETFROM:+0000 +DTSTART:16010101T000000 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=UTC:20161020T170000 +DTEND;TZID=UTC:20161020T230000 +UID:694f818f-6d67-4307-9c4d-0b5211686ff0 +IMPORTANCE:None +RRULE:FREQ=DAILY +EXDATE;TZID=UTC:20161020T170000 +END:VEVENT +END:VCALENDAR"; + + var noException = Calendar.LoadFromStream(new StringReader(icalNoException)).First().Events.First(); + var withException = Calendar.LoadFromStream(new StringReader(icalWithException)).First().Events.First(); + + Assert.AreNotEqual(noException, withException); + Assert.AreNotEqual(noException.GetHashCode(), withException.GetHashCode()); + } + + private static CalendarEvent GetSimpleEvent() => new CalendarEvent + { + DtStart = new CalDateTime(_now), + DtEnd = new CalDateTime(_later), + }; + + [Test] + public void RrulesAreSignificantTests() + { + var rrule = new RecurrencePattern(FrequencyType.Daily, 1); + var testRrule = GetSimpleEvent(); + testRrule.RecurrenceRules = new List { rrule }; + + var simpleEvent = GetSimpleEvent(); + Assert.AreNotEqual(simpleEvent, testRrule); + Assert.AreNotEqual(simpleEvent.GetHashCode(), testRrule.GetHashCode()); + + var testRdate = GetSimpleEvent(); + testRdate.RecurrenceDates = new List { new PeriodList { new Period(new CalDateTime(_now)) } }; + Assert.AreNotEqual(simpleEvent, testRdate); + Assert.AreNotEqual(simpleEvent.GetHashCode(), testRdate.GetHashCode()); + } + + private static List GetSimpleRecurrenceList() + => new List { new RecurrencePattern(FrequencyType.Daily, 1) { Count = 5 } }; + private static List GetExceptionDates() + => new List { new PeriodList { new Period(new CalDateTime(_now.AddDays(1).Date)) } }; + + [Test] + public void EventWithRecurrenceAndExceptionComparison() + { + var vEvent = GetSimpleEvent(); + vEvent.RecurrenceRules = GetSimpleRecurrenceList(); + vEvent.ExceptionDates = GetExceptionDates(); + + var calendar = new Calendar(); + calendar.Events.Add(vEvent); + + var vEvent2 = GetSimpleEvent(); + vEvent2.RecurrenceRules = GetSimpleRecurrenceList(); + vEvent2.ExceptionDates = GetExceptionDates(); + + var cal2 = new Calendar(); + cal2.Events.Add(vEvent2); + + var eventA = calendar.Events.First(); + var eventB = cal2.Events.First(); + + Assert.AreEqual(eventA.RecurrenceRules.First(), eventB.RecurrenceRules.First()); + Assert.AreEqual(eventA.RecurrenceRules.First().GetHashCode(), eventB.RecurrenceRules.First().GetHashCode()); + Assert.AreEqual(eventA.ExceptionDates.First(), eventB.ExceptionDates.First()); + Assert.AreEqual(eventA.ExceptionDates.First().GetHashCode(), eventB.ExceptionDates.First().GetHashCode()); + Assert.AreEqual(eventA.GetHashCode(), eventB.GetHashCode()); + Assert.AreEqual(eventA, eventB); + Assert.AreEqual(calendar, cal2); + } + + [Test] + public void AddingExdateToEventShouldNotBeEqualToOriginal() + { + //Create a calendar with an event with a recurrence rule + //Serialize to string, and deserialize + //Change the original calendar.Event to have an ExDate + //Serialize to string, and deserialize + //Event and Calendar hash codes and equality should NOT be the same + var serializer = new CalendarSerializer(); + + var vEvent = GetSimpleEvent(); + vEvent.RecurrenceRules = GetSimpleRecurrenceList(); + var cal1 = new Calendar(); + cal1.Events.Add(vEvent); + var serialized = serializer.SerializeToString(cal1); + var deserializedNoExDate = Calendar.LoadFromStream(new StringReader(serialized)).First(); + Assert.AreEqual(cal1, deserializedNoExDate); + + vEvent.ExceptionDates = GetExceptionDates(); + serialized = serializer.SerializeToString(cal1); + var deserializedWithExDate = Calendar.LoadFromStream(new StringReader(serialized)).First(); + + Assert.AreNotEqual(deserializedNoExDate.Events.First(), deserializedWithExDate.Events.First()); + Assert.AreNotEqual(deserializedNoExDate.Events.First().GetHashCode(), deserializedWithExDate.Events.First().GetHashCode()); + Assert.AreNotEqual(deserializedNoExDate, deserializedWithExDate); + } + + [Test] + public void ChangingRrulesShouldNotBeEqualToOriginalEvent() + { + var eventA = GetSimpleEvent(); + eventA.RecurrenceRules = GetSimpleRecurrenceList(); + + var eventB = GetSimpleEvent(); + eventB.RecurrenceRules = GetSimpleRecurrenceList(); + Assert.IsFalse(ReferenceEquals(eventA, eventB)); + Assert.AreEqual(eventA, eventB); + + var foreverDailyRule = new RecurrencePattern(FrequencyType.Daily, 1); + eventB.RecurrenceRules = new List { foreverDailyRule }; + + Assert.AreNotEqual(eventA, eventB); + Assert.AreNotEqual(eventA.GetHashCode(), eventB.GetHashCode()); + } + + [Test] + public void EventsDifferingByDtStampAreEqual() + { + const string eventA = @"BEGIN:VCALENDAR +PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN +VERSION:2.0 +BEGIN:VEVENT +ATTACH;FMTTYPE=application/json;VALUE=BINARY;ENCODING=BASE64:eyJzdWJqZWN0I + joiSFAgQ29hdGVyIGFuZCBDdXR0ZXIgQ2xlYW51cCIsInVuaXF1ZUlkZW50aWZpZXIiOiIwND + EwNzI1NGRjNWM5MDk0YWY3MWEwZTE5N2U2NWE1NTdkZmJjYjg0IiwiaWNhbFN0cmluZyI6IiI + sImxhYm9yRG93bnRpbWVzIjpbXSwiZGlzYWJsZWRFcXVpcG1lbnQiOlt7ImRpc2FibGVkRXF1 + aXBtZW50SW5zdGFuY2VOYW1lcyI6WyJEaWdpdGFsIFByaW50XFxIUCAyOCIsIkRpZ2l0YWwgU + HJpbnRcXEhQIDQ0Il0sImZ1bGxUaW1lRXF1aXZhbGVudHNDb3VudCI6MC4wfV0sIm1vZGVzTm + 90QWxsb3dlZCI6W10sInJhd01hdGVyaWFsc05vdEFsbG93ZWQiOltdLCJsYWJvckFsbG9jYXR + pb25zIjpbXX0= +DTEND;TZID=UTC:20150615T055000 +DTSTAMP:20161011T195316Z +DTSTART;TZID=UTC:20150615T054000 +EXDATE;TZID=UTC:20151023T054000 +IMPORTANCE:None +RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA +UID:04107254dc5c9094af71a0e197e65a557dfbcb84 +END:VEVENT +END:VCALENDAR"; + + const string eventB = @"BEGIN:VCALENDAR +PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN +VERSION:2.0 +BEGIN:VEVENT +ATTACH;FMTTYPE=application/json;VALUE=BINARY;ENCODING=BASE64:eyJzdWJqZWN0I + joiSFAgQ29hdGVyIGFuZCBDdXR0ZXIgQ2xlYW51cCIsInVuaXF1ZUlkZW50aWZpZXIiOiIwND + EwNzI1NGRjNWM5MDk0YWY3MWEwZTE5N2U2NWE1NTdkZmJjYjg0IiwiaWNhbFN0cmluZyI6IiI + sImxhYm9yRG93bnRpbWVzIjpbXSwiZGlzYWJsZWRFcXVpcG1lbnQiOlt7ImRpc2FibGVkRXF1 + aXBtZW50SW5zdGFuY2VOYW1lcyI6WyJEaWdpdGFsIFByaW50XFxIUCAyOCIsIkRpZ2l0YWwgU + HJpbnRcXEhQIDQ0Il0sImZ1bGxUaW1lRXF1aXZhbGVudHNDb3VudCI6MC4wfV0sIm1vZGVzTm + 90QWxsb3dlZCI6W10sInJhd01hdGVyaWFsc05vdEFsbG93ZWQiOltdLCJsYWJvckFsbG9jYXR + pb25zIjpbXX0= +DTEND;TZID=UTC:20150615T055000 +DTSTAMP:20161024T201419Z +DTSTART;TZID=UTC:20150615T054000 +EXDATE;TZID=UTC:20151023T054000 +IMPORTANCE:None +RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR,SA +UID:04107254dc5c9094af71a0e197e65a557dfbcb84 +END:VEVENT +END:VCALENDAR"; + + var calendarA = Calendar.LoadFromStream(new StringReader(eventA)).First(); + var calendarB = Calendar.LoadFromStream(new StringReader(eventB)).First(); + + Assert.AreEqual(calendarA.Events.First().GetHashCode(), calendarB.Events.First().GetHashCode()); + Assert.AreEqual(calendarA.Events.First(), calendarB.Events.First()); + Assert.AreEqual(calendarA.GetHashCode(), calendarB.GetHashCode()); + Assert.AreEqual(calendarA, calendarB); + } + + [Test] + public void EventResourcesCanBeZeroedOut() + { + var e = GetSimpleEvent(); + var resources = new[] { "Foo", "Bar", "Baz" }; + + e.Resources = new List(resources); + CollectionAssert.AreEquivalent(e.Resources, resources); + + var newResources = new[] { "Hello", "Goodbye" }; + e.Resources = new List(newResources); + CollectionAssert.AreEquivalent(e.Resources, newResources); + Assert.IsFalse(e.Resources.Any(r => resources.Contains(r))); + + e.Resources = null; + //See https://github.com/rianjs/ical.net/issues/208 -- this should be changed later so the collection is really null + Assert.AreEqual(0, e.Resources?.Count); + } + + [Test] + public void HourMinuteSecondOffsetParsingTest() + { + const string ical = +@"BEGIN:VCALENDAR +PRODID:-//1&1 Mail & Media GmbH/GMX Kalender Server 3.10.0//NONSGML//DE +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VTIMEZONE +TZID:Europe/Brussels +TZURL:http://tzurl.org/zoneinfo/Europe/Brussels +X-LIC-LOCATION:Europe/Brussels +BEGIN:DAYLIGHT +TZOFFSETFROM:-001730 +TZOFFSETTO:-001730 +TZNAME:CEST +DTSTART:19810329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+001730 +TZOFFSETTO:+001730 +TZNAME:BMT +DTSTART:18800101T000000 +RDATE:18800101T000000 +END:STANDARD +END:VTIMEZONE +END:VCALENDAR"; + + using (var reader = new StringReader(ical)) + { + var timezones = Calendar.LoadFromStream(reader) + .First() + .TimeZones.First() + .Children.Cast(); + + var positiveOffset = timezones + .Skip(1).Take(1).First() + .Properties.First().Value as UtcOffset; + var expectedPositive = TimeSpan.FromMinutes(17.5); + Assert.AreEqual(expectedPositive, positiveOffset?.Offset); + + var negativeOffset = timezones + .First() + .Properties.First().Value as UtcOffset; + + var expectedNegative = TimeSpan.FromMinutes(-17.5); + Assert.AreEqual(expectedNegative, negativeOffset?.Offset); + } + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/CalendarPropertiesTest.cs b/net-core/Ical.Net/Ical.Net.UnitTests/CalendarPropertiesTest.cs new file mode 100644 index 00000000..05806fb2 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/CalendarPropertiesTest.cs @@ -0,0 +1,53 @@ +using System; +using System.Linq; +using Ical.Net.DataTypes; +using Ical.Net.General; +using Ical.Net.Serialization.iCalendar.Serializers; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class CalendarPropertiesTest + { + [Test] + public void AddPropertyShouldNotIncludePropertyNameInValue() + { + const string propName = "X-WR-CALNAME"; + const string propValue = "Testname"; + + var iCal = new Calendar(); + iCal.AddProperty(propName, propValue); + + var result = new CalendarSerializer().SerializeToString(iCal); + + var lines = result.Split(new [] { SerializationConstants.LineBreak }, StringSplitOptions.None); + var propLine = lines.FirstOrDefault(x => x.StartsWith("X-WR-CALNAME:")); + Assert.AreEqual($"{propName}:{propValue}", propLine); + } + + [Test] + [Ignore("Calendar properties aren't being properly serialized")] + public void PropertySerialization_Tests() + { + const string formatted = +@"FMTTYPE=text/html:\n\n\n\n\n\n\n\n\n

This is some HTML formatted text.

\n\n\n"; + + var start = DateTime.Now; + var end = start.AddHours(1); + var @event = new CalendarEvent + { + Start = new CalDateTime(start), + End = new CalDateTime(end), + Description = "This is a description", + }; + var property = new CalendarProperty("X-ALT-DESC", formatted); + @event.AddProperty(property); + var calendar = new Calendar(); + calendar.Events.Add(@event); + + var serialized = new CalendarSerializer().SerializeToString(calendar); + Assert.IsTrue(serialized.Contains("X-ALT-DESC;")); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM1.ics new file mode 100644 index 00000000..565f0094 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM1.ics @@ -0,0 +1,37 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:20060718T100000 +DTEND;TZID=US-Eastern:20060718T110000 +BEGIN:VALARM +TRIGGER:-PT30M +ACTION:DISPLAY +DESCRIPTION:Breakfast meeting with executive\nteam at 8:30 AM EST. +END:VALARM +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM2.ics new file mode 100644 index 00000000..15612984 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM2.ics @@ -0,0 +1,38 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:20060718T100000 +DTEND;TZID=US-Eastern:20060718T110000 +RRULE:FREQ=DAILY;COUNT=10;INTERVAL=2 +BEGIN:VALARM +TRIGGER:-PT30M +ACTION:DISPLAY +DESCRIPTION:Breakfast meeting with executive\nteam at 8:30 AM EST. +END:VALARM +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM3.ics new file mode 100644 index 00000000..c363ff94 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM3.ics @@ -0,0 +1,39 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +DTEND;TZID=US-Eastern:19970902T100000 +EXDATE;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13 +BEGIN:VALARM +TRIGGER:-P2D +ACTION:DISPLAY +DESCRIPTION:Friday the 13th is coming soon!! +END:VALARM +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM4.ics new file mode 100644 index 00000000..e20baa61 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM4.ics @@ -0,0 +1,41 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +DTEND;TZID=US-Eastern:19970902T100000 +EXDATE;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13 +BEGIN:VALARM +TRIGGER:-P2D +DURATION:PT2H +REPEAT:3 +ACTION:DISPLAY +DESCRIPTION:Friday the 13th is coming soon!! +END:VALARM +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM5.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM5.ics new file mode 100644 index 00000000..7f53962c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM5.ics @@ -0,0 +1,39 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +DTEND;TZID=US-Eastern:19970902T100000 +EXDATE;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13 +BEGIN:VALARM +TRIGGER;VALUE=DATE-TIME:19980102T130000Z +ACTION:DISPLAY +DESCRIPTION:Friday the 13th is coming soon!! +END:VALARM +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM6.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM6.ics new file mode 100644 index 00000000..6773cc29 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM6.ics @@ -0,0 +1,41 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +DTEND;TZID=US-Eastern:19970902T100000 +EXDATE;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13 +BEGIN:VALARM +TRIGGER;VALUE=DATE-TIME:19980102T130000Z +DURATION:P3D +REPEAT:5 +ACTION:DISPLAY +DESCRIPTION:Friday the 13th is coming soon!! +END:VALARM +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM7.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM7.ics new file mode 100644 index 00000000..10ffb9fb --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Alarm/ALARM7.ics @@ -0,0 +1,38 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:20060718T100000 +DURATION:PT1H +RRULE:FREQ=DAILY;COUNT=10;INTERVAL=2 +BEGIN:VALARM +TRIGGER;RELATED=END:-PT30M +ACTION:DISPLAY +DESCRIPTION:Breakfast meeting with executive\nteam at 8:30 AM EST. +END:VALARM +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Journal/JOURNAL1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Journal/JOURNAL1.ics new file mode 100644 index 00000000..a113ca68 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Journal/JOURNAL1.ics @@ -0,0 +1,23 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ABC Corporation//NONSGML My Product//EN +BEGIN:VJOURNAL +DTSTAMP:19970324T120000Z +UID:uid5@host1.com +ORGANIZER:MAILTO:jsmith@host.com +STATUS:DRAFT +CLASS:PUBLIC +CATEGORY:Project Report, XYZ, Weekly Meeting +DESCRIPTION:Project xyz Review Meeting Minutes\n + Agenda\n1. Review of project version 1.0 requirements.\n2. + Definition of project processes.\n3. Review of project schedule.\n + Participants: John Smith, Jane Doe, Jim Dandy\n-It was + decided that the requirements need to be signed off by + product marketing.\n-Project processes were accepted.\n + -Project schedule needs to account for scheduled holidays + and employee vacation time. Check with HR for specific + dates.\n-New schedule will be distributed by Friday.\n- + Next weeks meeting is cancelled. No meeting until 3/23. +SUMMARY:Project xyz Review Meeting +END:VJOURNAL +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Journal/JOURNAL2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Journal/JOURNAL2.ics new file mode 100644 index 00000000..03866e05 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Journal/JOURNAL2.ics @@ -0,0 +1,24 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ABC Corporation//NONSGML My Product//EN +BEGIN:VJOURNAL +DTSTAMP:19970324T120000Z +UID:uid5@host1.com +ORGANIZER;SENT-BY="MAILTO:jane_doe@host.com";CN=JohnSmith;DIR="ldap://host.com:6666/ + o=3DDC%20Associates,c=3DUS??(cn=3DJohn%20Smith)":MAILTO:jsmith@host.com +STATUS:FINAL +CLASS:PRIVATE +CATEGORY:Project Report, XYZ, Weekly Meeting +DESCRIPTION:Project xyz Review Meeting Minutes\n + Agenda\n1. Review of project version 1.0 requirements.\n2. + Definition of project processes.\n3. Review of project schedule.\n + Participants: John Smith, Jane Doe, Jim Dandy\n-It was + decided that the requirements need to be signed off by + product marketing.\n-Project processes were accepted.\n + -Project schedule needs to account for scheduled holidays + and employee vacation time. Check with HR for specific + dates.\n-New schedule will be distributed by Friday.\n- + Next weeks meeting is cancelled. No meeting until 3/23. +SUMMARY:Project xyz Review Meeting +END:VJOURNAL +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug1741093.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug1741093.ics new file mode 100644 index 00000000..975f1e88 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug1741093.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20070621T161236Z +LAST-MODIFIED:20070621T161655Z +DTSTAMP:20070621T161655Z +UID:fcb6b7aa-772b-45ae-b607-fd7ad59f810b +SUMMARY:Work +RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,TU,WE,TH,FR +DTSTART;TZID=US-Eastern:20070621T080000 +DTEND;TZID=US-Eastern:20070621T170000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2912657.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2912657.ics new file mode 100644 index 00000000..6b05fea3 Binary files /dev/null and b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2912657.ics differ diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2916581.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2916581.ics new file mode 100644 index 00000000..98ff24c6 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2916581.ics @@ -0,0 +1,31 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Microsoft Corporation//Windows Calendar 1.0//EN +CALSCALE:GREGORIAN +METHOD:PUBLISH +BEGIN:VTIMEZONE +TZID:大阪、札幌、東京 +BEGIN:STANDARD +DTSTART:20000101T000000 +TZNAME:東京 (標準時) +TZOFFSETFROM:+1000 +TZOFFSETTO:+0900 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTAMP:20091217T220735Z +DTSTART;TZID=大阪、札幌、東京:20091225T110000 +DTEND;TZID=大阪、札幌、東京:20091225T113000 +RRULE:FREQ=WEEKLY +SUMMARY:Friday Job +UID:55BEC619-0C7B-48C0-8A17-2F358EA69DDD +END:VEVENT +BEGIN:VEVENT +DTSTAMP:20091217T220758Z +DTSTART;TZID=大阪、札幌、東京:20091226T110000 +DTEND;TZID=大阪、札幌、東京:20091226T113000 +RRULE:FREQ=WEEKLY +SUMMARY:Saturday Job +UID:AD43DEDA-7DE4-4921-BCC2-A66DDFADA41D +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2959692.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2959692.ics new file mode 100644 index 00000000..27a7ecc0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2959692.ics @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +PRODID:-//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN VERSION:2.0 +METHOD:PUBLISH +BEGIN:VTIMEZONE +TZID:Sarajewo, Skopie, Warszawa, Zagrzeb +BEGIN:STANDARD +DTSTART:20071028T030000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:Standard Time +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:20070325T020000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3 +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:Daylight Savings Time +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +ORGANIZER:MAILTO:majk@majk.com +DTSTART;TZID="Sarajewo, Skopie, Warszawa, Zagrzeb":20080103T170000 +DTEND;TZID="Sarajewo, Skopie, Warszawa, Zagrzeb":20080103T173000 +RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=TH;WKST=MO +TRANSP:OPAQUE +SEQUENCE:0 +UID:040000008200E00074C5B7101A82E00800000000203BC100BE99CA0100000000000000001000000086169DE46654E94FAC27686E5FB73EB8 +DTSTAMP:20100120T094747Z +DESCRIPTION:NIP 6311400348 NZOZ DUO-DENT \nKOD 180103\; 1 x poj. 10l\nGodz. 12-18\n +SUMMARY:NIP +PRIORITY:5 +X-MICROSOFT-CDO-IMPORTANCE:1 +CLASS:PUBLIC +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2966236.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2966236.ics new file mode 100644 index 00000000..5fed0310 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug2966236.ics @@ -0,0 +1,26 @@ +BEGIN:VCALENDAR +PRODID:-//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN +VERSION:2.0 +METHOD:PUBLISH +BEGIN:VTIMEZONE +TZID:Beijing +BEGIN:STANDARD +DTSTART:16010101T000000 +TZOFFSETFROM:+0800 +TZOFFSETTO:+0800 +TZNAME:Standard Time +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID="Beijing":20100119T080000 +DTEND;TZID="Beijing":20100119T083000 +RRULE:FREQ=DAILY;INTERVAL=7;WKST=SU +TRANSP:OPAQUE +SEQUENCE:0 +UID:040000008200E00074C5B7101A82E00800000000B0A18ABE4599CA0100000000000000001000000056A09E09285A0344B8A705F9CED24211 +DTSTAMP:20100119T122656Z +PRIORITY:5 +X-MICROSOFT-CDO-IMPORTANCE:1 +CLASS:PUBLIC +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug3007244.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug3007244.ics new file mode 100644 index 00000000..4f463cf1 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Bug3007244.ics @@ -0,0 +1,13 @@ +BEGIN:VCALENDAR +PRODID:-//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN +VERSION:2.0 +BEGIN:VEVENT +DTSTART;VALUE=DATE:20100523 +DTEND;VALUE=DATE:20100902 +RRULE:FREQ=YEARLY +SUMMARY:Test +UID:B438227F-6E9B-433A-A282-C5D0A7BEBA80 +SEQUENCE:3 +DTSTAMP:20050930T234204Z +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/ByMonth1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/ByMonth1.ics new file mode 100644 index 00000000..c4f791bb --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/ByMonth1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1 +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/ByMonth2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/ByMonth2.ics new file mode 100644 index 00000000..3e888988 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/ByMonth2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=YEARLY;UNTIL=20000131T150000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/ByMonthDay1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/ByMonthDay1.ics new file mode 100644 index 00000000..d65698e2 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/ByMonthDay1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970928T090000 +RRULE:FREQ=MONTHLY;BYMONTHDAY=-3 +DTEND:19970928T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Daily1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Daily1.ics new file mode 100644 index 00000000..7413ffa0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Daily1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=DAILY;INTERVAL=2 +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyByDay1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyByDay1.ics new file mode 100644 index 00000000..fa9e7bab --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyByDay1.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +BEGIN:VEVENT +DTEND:20070910T080000 +DTSTART:20070910T070000 +RRULE:FREQ=DAILY;BYDAY=MO,TH +SUMMARY:Every week on Monday and Thursday. +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyByHourMinute1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyByHourMinute1.ics new file mode 100644 index 00000000..20e7ef4b --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyByHourMinute1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40 +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyCount1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyCount1.ics new file mode 100644 index 00000000..346bdd10 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyCount1.ics @@ -0,0 +1,35 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +RRULE:FREQ=DAILY;COUNT=10;INTERVAL=2 +DTSTART;TZID=US-Eastern:20060718T100000 +DTEND;TZID=US-Eastern:20060718T110000 +PRIORITY:2 +SEQUENCE:3 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyCount2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyCount2.ics new file mode 100644 index 00000000..2dbd3e36 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyCount2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5 +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyInterval1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyInterval1.ics new file mode 100644 index 00000000..6420f426 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyInterval1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20070405T013153Z +DTEND;TZID=US-Eastern:20070410T070000 +DTSTAMP:20070405T013153Z +DTSTART;TZID=US-Eastern:20070409T070000 +DURATION:P1D +RRULE:FREQ=DAILY;INTERVAL=3 +SUMMARY:Every third day +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyInterval2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyInterval2.ics new file mode 100644 index 00000000..924029a1 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyInterval2.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +BEGIN:VEVENT +DTEND:20070410T070000 +DTSTART:20070409T070000 +RRULE:FREQ=DAILY;INTERVAL=2 +SUMMARY:Every 2 days +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyUntil1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyUntil1.ics new file mode 100644 index 00000000..622dbf03 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/DailyUntil1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=DAILY;UNTIL=19971224T000000Z +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Empty1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Empty1.ics new file mode 100644 index 00000000..4a04f739 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Empty1.ics @@ -0,0 +1,8 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +BEGIN:VEVENT +DTEND:20090930T060000 +DTSTART:20090927T053000 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Hourly1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Hourly1.ics new file mode 100644 index 00000000..77d39e62 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Hourly1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20070621T161236Z +LAST-MODIFIED:20070621T161655Z +DTSTAMP:20070621T161655Z +UID:fcb6b7aa-772b-45ae-b607-fd7ad59f810b +SUMMARY:Work +RRULE:FREQ=HOURLY +DTSTART;TZID=US-Eastern:20070621T080000 +DTEND;TZID=US-Eastern:20070621T170000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/HourlyInterval1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/HourlyInterval1.ics new file mode 100644 index 00000000..9fff80b5 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/HourlyInterval1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20070405T013153Z +DTEND;TZID=US-Eastern:20070410T070000 +DTSTAMP:20070405T013153Z +DTSTART;TZID=US-Eastern:20070409T070000 +DURATION:P1D +RRULE:FREQ=HOURLY;INTERVAL=18 +SUMMARY:Every 18 hours +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/HourlyInterval2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/HourlyInterval2.ics new file mode 100644 index 00000000..8046c238 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/HourlyInterval2.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +BEGIN:VEVENT +DTEND:20070410T070000 +DTSTART:20070409T070000 +RRULE:FREQ=HOURLY;INTERVAL=4 +SUMMARY:Every 4 hours +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/HourlyUntil1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/HourlyUntil1.ics new file mode 100644 index 00000000..5b1bceb8 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/HourlyUntil1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T190000Z +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Minutely1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Minutely1.ics new file mode 100644 index 00000000..79196d2d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Minutely1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20070621T161236Z +LAST-MODIFIED:20070621T161655Z +DTSTAMP:20070621T161655Z +UID:fcb6b7aa-772b-45ae-b607-fd7ad59f810b +SUMMARY:Work +RRULE:FREQ=MINUTELY +DTSTART;TZID=US-Eastern:20070621T080000 +DTEND;TZID=US-Eastern:20070621T170000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyByHour1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyByHour1.ics new file mode 100644 index 00000000..9e7db31c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyByHour1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16 +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount1.ics new file mode 100644 index 00000000..ad8edd3c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6 +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount2.ics new file mode 100644 index 00000000..ff0d1afc --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4 +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount3.ics new file mode 100644 index 00000000..0c2cd1fe --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount3.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:20100827T110000 +DTEND;TZID=US-Eastern:20100827T110300 +RRULE:FREQ=MINUTELY;COUNT=10;BYHOUR=11,12 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount4.ics new file mode 100644 index 00000000..c901df63 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyCount4.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:20100827T110000 +DTEND;TZID=US-Eastern:20100827T110300 +RRULE:FREQ=MINUTELY;INTERVAL=7;COUNT=10;BYHOUR=11,12 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyInterval1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyInterval1.ics new file mode 100644 index 00000000..a2a311e0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MinutelyInterval1.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +BEGIN:VEVENT +DTEND:20070410T070000 +DTSTART:20070409T070000 +RRULE:FREQ=MINUTELY;INTERVAL=30 +SUMMARY:Every 30 minutes +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Monthly1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Monthly1.ics new file mode 100644 index 00000000..f259f11d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Monthly1.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +BEGIN:VEVENT +DTEND:20070910T080000 +DTSTART:20070910T070000 +RRULE:FREQ=MONTHLY +SUMMARY:Every month. +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyByDay1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyByDay1.ics new file mode 100644 index 00000000..09992272 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyByDay1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyByMonthDay1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyByMonthDay1.ics new file mode 100644 index 00000000..b8594a69 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyByMonthDay1.ics @@ -0,0 +1,34 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +EXDATE;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13 +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyByMonthDay2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyByMonthDay2.ics new file mode 100644 index 00000000..162a5dcb --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyByMonthDay2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970913T090000 +RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13 +DTEND:19970913T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyBySetPos1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyBySetPos1.ics new file mode 100644 index 00000000..ca6a887e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyBySetPos1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970904T090000 +RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3 +DTEND:19970904T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyBySetPos2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyBySetPos2.ics new file mode 100644 index 00000000..32331903 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyBySetPos2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970929T090000 +RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2 +DTEND:19970929T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByDay1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByDay1.ics new file mode 100644 index 00000000..ce37183d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByDay1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970905T090000 +RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR +DTEND:19970905T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByDay2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByDay2.ics new file mode 100644 index 00000000..74681223 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByDay2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970907T090000 +RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU +DTEND:19970907T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByDay3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByDay3.ics new file mode 100644 index 00000000..a306460c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByDay3.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970922T090000 +RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO +DTEND:19970922T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByMonthDay1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByMonthDay1.ics new file mode 100644 index 00000000..e30d3447 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByMonthDay1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15 +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByMonthDay2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByMonthDay2.ics new file mode 100644 index 00000000..88347d0e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByMonthDay2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970930T090000 +RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1 +DTEND:19970930T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByMonthDay3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByMonthDay3.ics new file mode 100644 index 00000000..7bcf5274 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyCountByMonthDay3.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970910T090000 +RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15 +DTEND:19970910T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyInterval1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyInterval1.ics new file mode 100644 index 00000000..55fccabd --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyInterval1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20070405T013153Z +DTEND;TZID=US-Eastern:20070410T070000 +DTSTAMP:20070405T013153Z +DTSTART;TZID=US-Eastern:20070409T070000 +DURATION:P1D +RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=MO,TU;BYMONTHDAY=8,9,10,11,12,13,14 +SUMMARY:Every other month, on Monday and Tuesday on the 2nd week of the month. +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyUntilByDay1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyUntilByDay1.ics new file mode 100644 index 00000000..818db421 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/MonthlyUntilByDay1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970905T090000 +RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR +DTEND:19970905T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Secondly1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Secondly1.ics new file mode 100644 index 00000000..236a2d48 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Secondly1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20070621T161236Z +LAST-MODIFIED:20070621T161655Z +DTSTAMP:20070621T161655Z +UID:fcb6b7aa-772b-45ae-b607-fd7ad59f810b +SUMMARY:Work +RRULE:FREQ=SECONDLY +DTSTART;TZID=US-Eastern:20070621T080000 +DTEND;TZID=US-Eastern:20070621T170000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCount1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCount1.ics new file mode 100644 index 00000000..5510b671 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCount1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;COUNT=10 +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst1.ics new file mode 100644 index 00000000..1f60daaa --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst2.ics new file mode 100644 index 00000000..e0e66ac1 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst3.ics new file mode 100644 index 00000000..2560f2e3 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst3.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970805T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO +DTEND:19970805T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst4.ics new file mode 100644 index 00000000..2ffdad51 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyCountWkst4.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970805T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU +DTEND:19970805T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyInterval1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyInterval1.ics new file mode 100644 index 00000000..23b13b7b --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyInterval1.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +BEGIN:VEVENT +DTEND:20070910T080000 +DTSTART:20070910T070000 +RRULE:FREQ=WEEKLY;INTERVAL=2 +SUMMARY:Every other week. +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyUntil1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyUntil1.ics new file mode 100644 index 00000000..a7abc7a5 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyUntil1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyUntilWkst1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyUntilWkst1.ics new file mode 100644 index 00000000..037f83dd --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyUntilWkst1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyUntilWkst2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyUntilWkst2.ics new file mode 100644 index 00000000..9b484504 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyUntilWkst2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyWeekStartsLastYear.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyWeekStartsLastYear.ics new file mode 100644 index 00000000..f1b2fe7d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyWeekStartsLastYear.ics @@ -0,0 +1,10 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +BEGIN:VEVENT +DTSTART:20120101T070000 +RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR +SUMMARY:Every weekday. +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyWkst1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyWkst1.ics new file mode 100644 index 00000000..d087ffd5 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/WeeklyWkst1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU +DTEND:19970902T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Yearly1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Yearly1.ics new file mode 100644 index 00000000..da4e0a0f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/Yearly1.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +BEGIN:VEVENT +DTEND:20070910T080000 +DTSTART:20070910T070000 +RRULE:FREQ=YEARLY +SUMMARY:Every month. +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByDay1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByDay1.ics new file mode 100644 index 00000000..189bb19a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByDay1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970519T090000 +RRULE:FREQ=YEARLY;BYDAY=20MO +DTEND:19970519T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonth1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonth1.ics new file mode 100644 index 00000000..2c21b10c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonth1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970610T090000 +RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7 +DTEND:19970610T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonth2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonth2.ics new file mode 100644 index 00000000..85a9a996 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonth2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970313T090000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH +DTEND:19970313T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonth3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonth3.ics new file mode 100644 index 00000000..75c248ff --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonth3.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970605T090000 +RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8 +DTEND:19970605T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonthDay1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonthDay1.ics new file mode 100644 index 00000000..21921767 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByMonthDay1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19961105T090000 +RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8 +DTEND:19961105T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyBySetPos1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyBySetPos1.ics new file mode 100644 index 00000000..6b113b4f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyBySetPos1.ics @@ -0,0 +1,13 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +BEGIN:VEVENT +CREATED:20090920T181406Z +DTEND:20090927T060000 +DTSTAMP:20090920T181406Z +DTSTART:20090927T053000 +RRULE:FREQ=YEARLY;COUNT=10;BYDAY=SA,SU;BYMONTH=9;BYSETPOS=-1 +SEQUENCE:0 +UID:61ac9d49-9186-4577-979d-962cd43a3dfd +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo1.ics new file mode 100644 index 00000000..c53e27dc --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970512T090000 +RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO +DTEND:19970512T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo2.ics new file mode 100644 index 00000000..02d3cd33 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo2.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970512T090000 +RRULE:FREQ=YEARLY;BYWEEKNO=20 +DTEND:19970512T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo3.ics new file mode 100644 index 00000000..8f09b7e4 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo3.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:20020101T100000 +DTEND;TZID=US-Eastern:20020101T103000 +RRULE:FREQ=YEARLY;BYWEEKNO=1 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo4.ics new file mode 100644 index 00000000..498810ca --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo4.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970512T090000 +RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO,TU,WE,TH,FR,SA,SU +DTEND:19970512T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo5.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo5.ics new file mode 100644 index 00000000..a821f61f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyByWeekNo5.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:20020101T100000 +DTEND;TZID=US-Eastern:20020101T103000 +RRULE:FREQ=YEARLY;BYWEEKNO=1;BYDAY=MO,TU,WE,TH,FR,SA,SU +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyComplex1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyComplex1.ics new file mode 100644 index 00000000..ed7c0c0f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyComplex1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test Semi-Yearly January Sundays at 8:30 9:30 +RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30 +DTSTART;TZID=US-Eastern:20050718T100000 +DTEND;TZID=US-Eastern:20050718T110000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyCountByMonth1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyCountByMonth1.ics new file mode 100644 index 00000000..febb9db7 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyCountByMonth1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970310T090000 +RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3 +DTEND:19970310T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyCountByYearDay1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyCountByYearDay1.ics new file mode 100644 index 00000000..3d32c55b --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyCountByYearDay1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART;TZID=US-Eastern:19970101T090000 +RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200 +DTEND:19970101T100000 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyInterval1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyInterval1.ics new file mode 100644 index 00000000..7d43ec43 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Recurrence/YearlyInterval1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20050405T013153Z +DTEND;TZID=US-Eastern:20050410T070000 +DTSTAMP:20050405T013153Z +DTSTART;TZID=US-Eastern:20050409T070000 +DURATION:P1D +RRULE:FREQ=YEARLY;INTERVAL=2;BYDAY=MO,TU;BYMONTHDAY=8,9,10,11,12,13,14 +SUMMARY:Every other year, on Monday and Tuesday on the 2nd week of each month. +UID:baff9b6a-a161-4057-bd9c-d76359dc90b1 +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attachment3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attachment3.ics new file mode 100644 index 00000000..62308d05 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attachment3.ics @@ -0,0 +1,52 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +ATTACH;VALUE=BINARY;ENCODING=BASE64: + VGhpcyBpcyBhIHRlc3QgdG8gdHJ5IG91dCBiYXNlNjQgZW5jb2Rpbmcgd2l0aG91dCBiZW + luZyB0b28gbGFyZ2UuDQpUaGlzIGlzIGEgdGVzdCB0byB0cnkgb3V0IGJhc2U2NCBlbmNv + ZGluZyB3aXRob3V0IGJlaW5nIHRvbyBsYXJnZS4NClRoaXMgaXMgYSB0ZXN0IHRvIHRyeS + BvdXQgYmFzZTY0IGVuY29kaW5nIHdpdGhvdXQgYmVpbmcgdG9vIGxhcmdlLg0KVGhpcyBp + cyBhIHRlc3QgdG8gdHJ5IG91dCBiYXNlNjQgZW5jb2Rpbmcgd2l0aG91dCBiZWluZyB0b2 + 8gbGFyZ2UuDQpUaGlzIGlzIGEgdGVzdCB0byB0cnkgb3V0IGJhc2U2NCBlbmNvZGluZyB3 + aXRob3V0IGJlaW5nIHRvbyBsYXJnZS4NClRoaXMgaXMgYSB0ZXN0IHRvIHRyeSBvdXQgYm + FzZTY0IGVuY29kaW5nIHdpdGhvdXQgYmVpbmcgdG9vIGxhcmdlLg0KVGhpcyBpcyBhIHRl + c3QgdG8gdHJ5IG91dCBiYXNlNjQgZW5jb2Rpbmcgd2l0aG91dCBiZWluZyB0b28gbGFyZ2 + UuDQpUaGlzIGlzIGEgdGVzdCB0byB0cnkgb3V0IGJhc2U2NCBlbmNvZGluZyB3aXRob3V0 + IGJlaW5nIHRvbyBsYXJnZS4NClRoaXMgaXMgYSB0ZXN0IHRvIHRyeSBvdXQgYmFzZTY0IG + VuY29kaW5nIHdpdGhvdXQgYmVpbmcgdG9vIGxhcmdlLg0KVGhpcyBpcyBhIHRlc3QgdG8g + dHJ5IG91dCBiYXNlNjQgZW5jb2Rpbmcgd2l0aG91dCBiZWluZyB0b28gbGFyZ2UuDQpUaG + lzIGlzIGEgdGVzdCB0byB0cnkgb3V0IGJhc2U2NCBlbmNvZGluZyB3aXRob3V0IGJlaW5n + IHRvbyBsYXJnZS4NClRoaXMgaXMgYSB0ZXN0IHRvIHRyeSBvdXQgYmFzZTY0IGVuY29kaW + 5nIHdpdGhvdXQgYmVpbmcgdG9vIGxhcmdlLg== +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +CATEGORIES:Category +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR +DTEND:19970902T100000 +LOCATION:Daywest +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attachment4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attachment4.ics new file mode 100644 index 00000000..553d651d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attachment4.ics @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +ATTACH:http://ical.mac.com/ical/US32Holidays.ics +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +CATEGORIES:Category +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR +DTEND:19970902T100000 +LOCATION:Daywest +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attendee1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attendee1.ics new file mode 100644 index 00000000..c5b7c3ba --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attendee1.ics @@ -0,0 +1,16 @@ +BEGIN:VCALENDAR +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +VERSION:2.0 +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART:20060718T100000 +DTEND:20060718T110000 +LOCATION:Daywest +ATTENDEE;MEMBER="mailto:DEV-GROUP@example.com":mailto:joecool@example.com +ATTENDEE;DELEGATED-FROM="mailto:immud@example.com":mailto:ildoit@example.com +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attendee2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attendee2.ics new file mode 100644 index 00000000..48e78d36 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Attendee2.ics @@ -0,0 +1,17 @@ +BEGIN:VCALENDAR +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +VERSION:2.0 +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART:20060718T100000 +DTEND:20060718T110000 +LOCATION:Daywest +ATTENDEE;MEMBER="mailto:DEV-GROUP@example.com","mailto:ANOTHER-GROUP@example.com";MEMBER="mailto:THIRD-GROUP@example.com":mailto:joecool@example.com +NOTE:The above use of MEMBER is not technically valid according to RFC 5545; however, since the parser + should still be able to handle it, we test for it. +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Bug2033495.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Bug2033495.ics new file mode 100644 index 00000000..da8cd393 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Bug2033495.ics @@ -0,0 +1,10 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-LOTUS-CHILD_UID:XXX +BEGIN:VEVENT +SUMMARY:Event +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Bug2148092.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Bug2148092.ics new file mode 100644 index 00000000..03066311 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Bug2148092.ics @@ -0,0 +1,194 @@ +BEGIN:VCALENDAR +VERSION + :2.0 +PRODID + :-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +BEGIN:VTODO +UID + :1c903d42-fd22-11da-9097-d8074ea454d0 +SUMMARY + :Stda garaget +DESCRIPTION + :NOGA!!! +LOCATION + :Garaget +PRIORITY + :5 +STATUS + :IN-PROCESS +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :30 +X-MOZILLA-LASTALARMACK + :20060616T135845 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +DTSTART + :20060616T130000 +DUE + :20060616T135900 +DTSTAMP + :20060616T102229Z +LAST-MODIFIED + :20060616T115845Z +PERCENT-COMPLETE + :50 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT30M +END:VALARM +END:VTODO +BEGIN:VEVENT +UID + :27dedb92-69a4-11db-8fae-b9231e60cc13 +SUMMARY + :God Jul +DESCRIPTION + :Tomten kommer! +STATUS + :TENTATIVE +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +DTSTART + :20061224T000000 +DTEND + :20061224T230000 +DTSTAMP + :20061101T122553Z +LAST-MODIFIED + :20061101T122737Z +END:VEVENT +BEGIN:VEVENT +UID + :403f9454-6fc8-11db-b5e3-c1ad1b64708e +SUMMARY + :Mte kontoret +DESCRIPTION + :Skall trffa liselotte och diskutera vr ekonomi. Och se ver vra + verenskommelser.\n +LOCATION + :Kontoret +STATUS + :TENTATIVE +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +DTSTART + ;VALUE=DATE + :20061031 +DTEND + ;VALUE=DATE + :20061101 +DTSTAMP + :20061109T075916Z +LAST-MODIFIED + :20061110T073618Z +END:VEVENT +BEGIN:VEVENT +UID + :eff4c625-6902-11db-b2df-fecd9e34a9d8 +SUMMARY + :Swedish chars and letters +DESCRIPTION + :This is Swedish chars: and +LOCATION + :Kontoret +STATUS + :TENTATIVE +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +DTSTART + :20061101T090000 +DTEND + :20061101T100000 +DTSTAMP + :20061031T171124Z +LAST-MODIFIED + :20061110T073743Z +END:VEVENT +BEGIN:VEVENT +UID + :02d7e71d-6903-11db-bb22-d4dd07f6c8b9 +SUMMARY + :Sova +DESCRIPTION + :God natt +LOCATION + :Sngen +STATUS + :TENTATIVE +CLASS + :PUBLIC +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +DTSTART + :20061101T100000 +DTEND + :20061101T110000 +DTSTAMP + :20061031T171210Z +LAST-MODIFIED + :20061110T073800Z +END:VEVENT +BEGIN:VEVENT +UID + :5083e6eb-6954-11db-aa33-e45ec9209be7 +SUMMARY + :Nu mste jag g hem +DESCRIPTION + :Hej +LOCATION + :sova +STATUS + :TENTATIVE +CLASS + :PUBLIC +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +DTSTART + :20061109T230000 +DTEND + :20061111T000000 +DTSTAMP + :20061101T025415Z +LAST-MODIFIED + :20061110T074001Z +END:VEVENT +BEGIN:VEVENT +UID + :a4df60b2-708e-11db-8d7b-f7ec1dceb054 +SUMMARY + :Mamma fyller r +DESCRIPTION + :Kom ihg hennes fdelsedag! +LOCATION + :Vsters +STATUS + :TENTATIVE +CLASS + :PUBLIC +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +DTSTART + ;VALUE=DATE + :20061109 +DTEND + ;VALUE=DATE + :20061110 +DTSTAMP + :20061110T073918Z +LAST-MODIFIED + :20061110T074017Z +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Bug2938007.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Bug2938007.ics new file mode 100644 index 00000000..e2186d82 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Bug2938007.ics @@ -0,0 +1,12 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +BEGIN:VEVENT +DTSTAMP:20100123T152945Z +DTSTART;TZID=大阪、札幌、東京:20100117T000000 +DTEND;TZID=大阪、札幌、東京:20100117T003000 +RRULE:FREQ=WEEKLY;COUNT=3 +SUMMARY:WeeklyTest +UID:4F1AD425-E0AE-4480-8393-CA754C5870DE +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Calendar1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Calendar1.ics new file mode 100644 index 00000000..8cf39636 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Calendar1.ics @@ -0,0 +1,515 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +X-WR-CALNAME:Cabin +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Furnace filter - Vacuum out or replace +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTODO +UID:9ebd7d36-1e73-11db-8c03-f85d3350626a +SUMMARY:Fill wood pile +DESCRIPTION:The wood pile needs to be filled before winter comes. +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTART:20060601T135657 +DUE:20061101T135657 +DTSTAMP:20060728T195657Z +LAST-MODIFIED:20060728T195958Z +END:VTODO +BEGIN:VTODO +UID:0aa8fdaa-1e73-11db-8377-fc48e342e6ce +SUMMARY:Replace vacuum with bagless +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T195520Z +LAST-MODIFIED:20060728T200007Z +END:VTODO +BEGIN:VTODO +UID:c210a4d6-1e73-11db-82a4-ba30ce79b81f +SUMMARY:Mr. Clean Magic Eraser +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T200028Z +LAST-MODIFIED:20060728T200113Z +END:VTODO +BEGIN:VTODO +UID:41f9c9c6-1e74-11db-8565-d0a0991e371a +SUMMARY:Bring snowmobiles for winter +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +X-MOZILLA-RECUR-DEFAULT-UNITS:years +RRULE:FREQ=YEARLY;BYMONTH=12 +DTSTART:20061201T140313 +DUE:20070201T140313 +DTSTAMP:20060728T200313Z +END:VTODO +BEGIN:VTODO +UID:763909f6-1e74-11db-885f-8af00c4d1a66 +SUMMARY:Schedule propane tank to be filled +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +X-MOZILLA-RECUR-DEFAULT-UNITS:months +X-MOZILLA-RECUR-DEFAULT-INTERVAL:6 +RRULE:FREQ=MONTHLY;INTERVAL=6 +DTSTART:20060301T140400 +DUE:20060501T140400 +DTSTAMP:20060728T200423Z +LAST-MODIFIED:20060728T200553Z +END:VTODO +BEGIN:VTODO +UID:b01c5256-1e74-11db-8c56-de0cafc4da53 +SUMMARY:New cordless phone w/answering machine +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T200555Z +END:VTODO +BEGIN:VTODO +UID:0cc4a5e6-1e75-11db-9f41-dec204a0f75c +SUMMARY:Tupperware that can fit into armoire +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T200934Z +END:VTODO +BEGIN:VTODO +UID:160b4746-1e75-11db-996c-f474ebe371c9 +SUMMARY:Get the chimney swept - find out how often +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T201002Z +END:VTODO +BEGIN:VTODO +UID:4836c236-1e75-11db-835f-a024e2a6131f +SUMMARY:Refinish the floor +DESCRIPTION:The floor needs to be sanded and restained. We need to bid + this out to companies and compare prices. +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +X-MOZILLA-RECUR-DEFAULT-UNITS:years +X-MOZILLA-RECUR-DEFAULT-INTERVAL:7 +RRULE:FREQ=YEARLY;INTERVAL=7;BYMONTH=4 +DTSTART:20060401T141018 +DUE:20060904T161017 +DTSTAMP:20060728T201018Z +LAST-MODIFIED:20060728T201417Z +END:VTODO +BEGIN:VTODO +UID:2df60496-1e73-11db-ba96-e3cfe6793b5f +SUMMARY:Wash Windows +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +X-MOZILLA-RECUR-DEFAULT-UNITS:years +RRULE:FREQ=YEARLY;BYMONTH=3 +DTSTART:20060301T135500 +DUE:20061031T155400 +DTSTAMP:20060728T195539Z +LAST-MODIFIED:20060728T201543Z +END:VTODO +BEGIN:VTODO +UID:fc07ec26-1e74-11db-a9c8-e35827dc6341 +SUMMARY:Tupperware under the beds - for sheets +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T200909Z +LAST-MODIFIED:20060728T201605Z +END:VTODO +BEGIN:VTODO +UID:eb9920f6-1e74-11db-a889-da41d19945ea +SUMMARY:Put cabin phone # on do-not-call list +STATUS:COMPLETED +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T200742Z +LAST-MODIFIED:20060728T201612Z +COMPLETED:20060728T141612 +END:VTODO +BEGIN:VTODO +UID:9468a6a6-1e75-11db-9faf-9808ab9bd289 +SUMMARY:Restain cabin exterior +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +X-MOZILLA-RECUR-DEFAULT-UNITS:years +X-MOZILLA-RECUR-DEFAULT-INTERVAL:7 +RRULE:FREQ=YEARLY;INTERVAL=7;BYMONTH=4 +DTSTART:20070401T141100 +DUE:20081001T131100 +DTSTAMP:20060728T201143Z +LAST-MODIFIED:20060728T201626Z +END:VTODO +BEGIN:VTODO +UID:04837316-1e76-11db-84d2-a2ee86282ad7 +SUMMARY:Leather Work Gloves +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T201634Z +END:VTODO +BEGIN:VTODO +UID:1f041b96-1e76-11db-9ba1-9fa157c32b9e +SUMMARY:7 copies of gate key +DESCRIPTION:We need a copy for each person (1 for each brother/sister\, + and 1 for Mom) +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T201658Z +END:VTODO +BEGIN:VTODO +UID:26eb47d6-1e76-11db-a601-bab4767cbef2 +SUMMARY:Snowmobile covers +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T201743Z +END:VTODO +BEGIN:VTODO +UID:2f609086-1e76-11db-8ea0-a4f547033177 +SUMMARY:Helmets for kids +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T201755Z +END:VTODO +BEGIN:VTODO +UID:37da6ec6-1e76-11db-beaa-f568664ff9e6 +SUMMARY:Snap-on covers for adults +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T201810Z +END:VTODO +BEGIN:VTODO +UID:43a5e946-1e76-11db-b119-bf59ef08cb02 +SUMMARY:Paper towels +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T201824Z +END:VTODO +BEGIN:VTODO +UID:4b221e66-1e76-11db-9cb7-e2e0a5db81d3 +SUMMARY:Paper plates / paper cups +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T201843Z +END:VTODO +BEGIN:VTODO +UID:605d7c56-1e76-11db-8611-dbe65fb454ce +SUMMARY:One set of Cailfornia King sheets +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T201916Z +END:VTODO +BEGIN:VTODO +UID:80c877a6-1e76-11db-881d-feced7d399fb +SUMMARY:Have septic tank inspected and pumped out +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +X-MOZILLA-RECUR-DEFAULT-UNITS:years +X-MOZILLA-RECUR-DEFAULT-INTERVAL:5 +RRULE:FREQ=YEARLY;INTERVAL=5;BYMONTH=3 +DTSTART:20060301T141900 +DUE:20061001T141900 +DTSTAMP:20060728T201932Z +END:VTODO +BEGIN:VTODO +UID:02185126-1e74-11db-bb94-aa9a788c3292 +SUMMARY:Bring 4-wheelers for summer +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +X-MOZILLA-RECUR-DEFAULT-UNITS:years +RRULE:FREQ=YEARLY;BYMONTH=4 +DTSTART:20060401T140100 +DUE:20060601T140100 +DTSTAMP:20060728T200114Z +LAST-MODIFIED:20060728T202139Z +END:VTODO +BEGIN:VTODO +UID:90b0ca56-1e76-11db-a041-d53e937a279f +SUMMARY:Tupperware for kitchen storage +DESCRIPTION:To hold sugar\, flour\, rice\, cereals\, etc. +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T202030Z +LAST-MODIFIED:20060728T202212Z +END:VTODO +BEGIN:VTODO +UID:c88e0e26-1e76-11db-8581-cbfbe398acf8 +SUMMARY:Take inventory of tools +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T202214Z +END:VTODO +BEGIN:VTODO +UID:d1a24256-1e76-11db-9a1e-d406b43b689f +SUMMARY:Swiffer broom/vacuum +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T202226Z +END:VTODO +BEGIN:VTODO +UID:100cde11-2335-11db-a9ad-c869095da4a2 +SUMMARY:Batteries of all types +DESCRIPTION:AAA\, AA\, C\, D\nPlease do not take the batteries out of + their cases unless you're using them immediately\, as they will lose + their charge (even if they're only touching each other). In other + words\, don't dump them all into a big battery-jar. +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060803T211327Z +END:VTODO +BEGIN:VEVENT +UID:32241400-2eda-11db-8fdc-a2e0f940a8ab +SUMMARY:Wendi's Parents +LOCATION:Cabin +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTART:20060901T120000 +DTEND:20060904T180000 +DTSTAMP:20060818T165348Z +END:VEVENT +BEGIN:VEVENT +UID:5c6d8613-2249-11db-8b3c-c1263126b9eb +SUMMARY:Doug & Brittney +LOCATION:Cabin +STATUS:CANCELLED +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-UNITS:days +X-MOZILLA-ALARM-DEFAULT-LENGTH:3 +X-MOZILLA-LASTALARMACK:20060829T181811 +X-MOZILLA-RECUR-DEFAULT-INTERVAL:0 +DTSTART:20060823T180000 +DTEND:20060827T180000 +DTSTAMP:20060802T170624Z +LAST-MODIFIED:20060830T001811Z +BEGIN:VALARM +TRIGGER;VALUE=DURATION:-P3D +END:VALARM +END:VEVENT +BEGIN:VEVENT +UID:3d3a8c5f-2340-11db-8436-e0dc2f2b91f7 +SUMMARY:Family Trip for Dad's Birthday +DESCRIPTION:Mom\,\nNate + 3\,\nTraci + 1\,\nMel + 4 +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-UNITS:days +X-MOZILLA-ALARM-DEFAULT-LENGTH:3 +X-MOZILLA-LASTALARMACK:20061005T163705 +DTSTART:20061006T120000 +DTEND:20061008T180000 +DTSTAMP:20060803T223416Z +LAST-MODIFIED:20061005T223705Z +BEGIN:VALARM +TRIGGER;VALUE=DURATION:-P3D +END:VALARM +END:VEVENT +BEGIN:VEVENT +UID:51c1d3fa-5a29-11db-86d3-d8bf82ec9163 +SUMMARY:Nate/Matt +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTART;VALUE=DATE:20061013 +DTEND;VALUE=DATE:20061016 +DTSTAMP:20061012T193834Z +END:VEVENT +BEGIN:VEVENT +UID:30eab49e-8bbe-11db-9ab3-ac825aaa2cdb +SUMMARY:Matt +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTART;VALUE=DATE:20061215 +DTEND;VALUE=DATE:20061218 +DTSTAMP:20061214T215749Z +END:VEVENT +BEGIN:VTODO +UID:b68e8356-1e73-11db-9641-96363766f8bb +SUMMARY:Cordless drill +DESCRIPTION:15+ volt +LOCATION:Cabin +CATEGORIES:Shopping List +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T200013Z +LAST-MODIFIED:20061214T220303Z +END:VTODO +BEGIN:VTODO +UID:e4b8b282-8bbe-11db-b6fb-e5b80f3f2951 +SUMMARY:Corded Drill +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20061214T220304Z +END:VTODO +BEGIN:VEVENT +UID:66747330-968e-11db-bbf1-e29fd8058dfb +SUMMARY:Matt +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTART;VALUE=DATE:20061229 +DTEND;VALUE=DATE:20070101 +DTSTAMP:20061228T161337Z +END:VEVENT +BEGIN:VEVENT +UID:24d77905-a4b3-11db-b94a-c92640379392 +SUMMARY:Doug & Brittney +LOCATION:Cabin +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:1 +DTSTART;VALUE=DATE:20070215 +DTEND;VALUE=DATE:20070220 +DTSTAMP:20070115T161137Z +END:VEVENT +BEGIN:VTODO +UID:56aa5ca6-1e76-11db-8ada-f202f84e3383 +SUMMARY:Hand Towels / Traci's stock +DESCRIPTION:Ask Traci about this one +CATEGORIES:Shopping List +STATUS:COMPLETED +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +X-MOZILLA-RECUR-DEFAULT-INTERVAL:0 +DTSTAMP:20060728T201856Z +LAST-MODIFIED:20070115T161531Z +COMPLETED:20070115T091531 +END:VTODO +BEGIN:VEVENT +UID:58c63a8d-0ec1-11dc-ad88-c2de7fcdbd9f +SUMMARY:Nate & Wendi +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTART;VALUE=DATE:20070601 +DTEND;VALUE=DATE:20070604 +DTSTAMP:20070530T152032Z +END:VEVENT +BEGIN:VEVENT +UID:6e869e4c-0ec1-11dc-9802-f3dcf4a22afe +SUMMARY:Mom\, Heather & Girls Camp +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTART;VALUE=DATE:20070604 +DTEND;VALUE=DATE:20070609 +DTSTAMP:20070530T152103Z +END:VEVENT +BEGIN:VEVENT +UID:d9a4fb7f-33ea-11dc-8a9f-e786f11cc591 +SUMMARY:Mandi (Wendi's Sister) +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTART;VALUE=DATE:20070716 +DTEND;VALUE=DATE:20070720 +DTSTAMP:20070716T222052Z +END:VEVENT +BEGIN:VEVENT +UID:e74e2e77-33ea-11dc-8d42-f59290ddbe6a +SUMMARY:Matt (Wendi's Brother) +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTART;VALUE=DATE:20070720 +DTEND;VALUE=DATE:20070724 +DTSTAMP:20070716T222118Z +END:VEVENT +BEGIN:VEVENT +UID:03034be6-19d2-11dc-baf4-ed7e086b3d03 +SUMMARY:Wendi's Parents +STATUS:TENTATIVE +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTART;VALUE=DATE:20070622 +DTEND;VALUE=DATE:20070625 +DTSTAMP:20070613T171738Z +LAST-MODIFIED:20070716T222234Z +END:VEVENT +BEGIN:VEVENT +UID:{e06d4078-6fe5-c84c-b886-9862069853a3} +SUMMARY:Nate & Wendi +CLASS:PRIVATE +DTSTAMP:20070723T160806Z +LAST-MODIFIED:20070723T160806Z +DTSTART;VALUE=DATE:20070810 +DTEND;VALUE=DATE:20070813 +END:VEVENT +BEGIN:VEVENT +DTSTAMP:20070731T203424Z +UID:{f110f89c-5486-7746-b1db-2bf17fdd47e5} +SUMMARY:Day Family - Staining Cabin +CLASS:PRIVATE +LAST-MODIFIED:20070813T153356Z +DTSTART;VALUE=DATE:20070821 +DTEND;VALUE=DATE:20070824 +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CalendarParameters2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CalendarParameters2.ics new file mode 100644 index 00000000..9627d6ba --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CalendarParameters2.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +X-WR-CALNAME:Cabin +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Furnace filter - Vacuum out or replace +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +DTSTAMP:20060728T195437Z +END:VTODO +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive1.ics new file mode 100644 index 00000000..64ad8265 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive1.ics @@ -0,0 +1,18 @@ +BEGIN:vCalendar +VERSION:2.0 +METHOD:PUBLISH +BEGIN:vEvent +DTSTART:20070606T213000Z +DTEND:20070606T224500Z +UID:1032340930 +DTSTAMP:20070526T025824Z +DESCRIPTION:Thank you for your interest in Microsoft Events. We would like to remind you of the following event: MSDN Webcast: Building Microsoft Windows Communication Foundation and Windows Workflow Foundation Applications with Microsoft Visual Studio Codename "Orcas" (Level 300). \n\nPlease review the information below.\n\nEvent Code: 1032340930\nEvent Name: MSDN Webcast: Building Microsoft Windows Communication Foundation and Windows Workflow Foundation Applications with Microsoft Visual Studio Codename "Orcas" (Level 300)\nStart Date: 6/6/2007\nStart Time: 2:30 PM (GMT-08:00) Pacific Time (US & Canada)\nEnd Date: 6/6/2007\nEnd Time: 3:45 PM (GMT-08:00) Pacific Time (US & Canada)\n\nPlease click on the following link for more information regarding this Event https://msevents.microsoft.com/cui/r.aspx?r=1290603960&c=en-US&t=2. We look forward to seeing you at the Event! +SUMMARY:Live Webcast - MSDN Webcast: Building Microsoft Windows Communication Foundation and Windows Workflow Foundation Applications with Microsoft Visual Studio Codename "Orcas" (Level 300) +PRIORITY:3 +BEGIN:vAlarm +TRIGGER:P0DT0H15M +ACTION:DISPLAY +DESCRIPTION:Reminder +END:vAlarm +END:vEvent +END:vCalendar diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive2.ics new file mode 100644 index 00000000..fe7ad2df --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive2.ics @@ -0,0 +1,18 @@ +BEGIN:vCalendar +VERSION:2.0 +METHOD:PUBLISH +BEGIN:vEvent +DTSTART:20070612T160000Z +DTEND:20070612T170000Z +UID:1032340889 +DTSTAMP:20070526T025936Z +DESCRIPTION:Thank you for your interest in Microsoft Events. We would like to remind you of the following event: MSDN Webcast: Services in ASP.NET AJAX Client Libraries (Level 200). \n\nPlease review the information below.\n\nEvent Code: 1032340889\nEvent Name: MSDN Webcast: Services in ASP.NET AJAX Client Libraries (Level 200)\nStart Date: 6/12/2007\nStart Time: 9:00 AM (GMT-08:00) Pacific Time (US & Canada)\nEnd Date: 6/12/2007\nEnd Time: 10:00 AM (GMT-08:00) Pacific Time (US & Canada)\n\nPlease click on the following link for more information regarding this Event https://msevents.microsoft.com/cui/r.aspx?r=1290603983&c=en-US&t=4. We look forward to seeing you at the Event! +SUMMARY:Live Webcast - MSDN Webcast: Services in ASP.NET AJAX Client Libraries (Level 200) +PRIORITY:3 +BEGIN:vAlarm +TRIGGER:P0DT0H15M +ACTION:DISPLAY +DESCRIPTION:Reminder +END:vAlarm +END:vEvent +END:vCalendar diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive3.ics new file mode 100644 index 00000000..d1e79edb --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive3.ics @@ -0,0 +1,18 @@ +BEGIN:vCalendar +VERSION:2.0 +METHOD:PUBLISH +BEGIN:vEvent +DTSTART:20070604T204500Z +DTEND:20070604T220000Z +UID:1032340718 +DTSTAMP:20070526T030009Z +DESCRIPTION:Thank you for your interest in Microsoft Events. We would like to remind you of the following event: MSDN Webcast: A Lap Around SilverLight (Level 300). \n\nPlease review the information below.\n\nEvent Code: 1032340718\nEvent Name: MSDN Webcast: A Lap Around SilverLight (Level 300)\nStart Date: 6/4/2007\nStart Time: 1:45 PM (GMT-08:00) Pacific Time (US & Canada)\nEnd Date: 6/4/2007\nEnd Time: 3:00 PM (GMT-08:00) Pacific Time (US & Canada)\n\nPlease click on the following link for more information regarding this Event https://msevents.microsoft.com/cui/r.aspx?r=1290603991&c=en-US&t=2. We look forward to seeing you at the Event! +SUMMARY:Live Webcast - MSDN Webcast: A Lap Around SilverLight (Level 300) +PRIORITY:3 +BEGIN:vAlarm +TRIGGER:P0DT0H15M +ACTION:DISPLAY +DESCRIPTION:Reminder +END:vAlarm +END:vEvent +END:vCalendar diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive4.ics new file mode 100644 index 00000000..6bd8e162 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/CaseInsensitive4.ics @@ -0,0 +1,13 @@ +BEGIN:VCALENDAR +Version:2.5 +X-WR-CALNAME:GKCC +BEGIN:VEVENT +UID:fadc65bd4f5d746ce4a581f072ca66ea +DTSTART;VALUE=DATE:20090117T200000 +SUMMARY:Apple Hill Chamber Players +DTEND;VALUE=DATE:20090117T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1232164801#eventid2377 +CREATED:20080716T070640Z +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Categories1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Categories1.ics new file mode 100644 index 00000000..26cb68c7 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Categories1.ics @@ -0,0 +1,39 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +CATEGORIES:One,Two,Three +CATEGORIES:Four,Five,Six +CATEGORIES;ENCODING=BASE64:U2V2ZW4= +CATEGORIES:A string of text with nothing less than a comma\, semicolon\; and a newline\n. +DTSTART;TZID=US-Eastern:20060902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=20061224T000000Z;WKST=SU;BYDAY=MO,WE,FR +EXDATE:20060914T050000/PT2H +DTEND;TZID=US-Eastern:20060902T100000 +LOCATION:Daywest +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/DateTime1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/DateTime1.ics new file mode 100644 index 00000000..cda2ee79 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/DateTime1.ics @@ -0,0 +1,116 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:Test Calendar +X-WR-TIMEZONE:America/Toronto +X-WR-CALDESC:Calendar used for testing embedding and the Google API +BEGIN:VTIMEZONE +TZID:America/Toronto +X-LIC-LOCATION:America/Toronto +BEGIN:DAYLIGHT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=America/Toronto:20090826T063000 +DTEND;TZID=America/Toronto:20090826T103000 +DTSTAMP:20091111T124521Z +UID:glk3in708vjsb4l66g5t1od1as@google.com +RECURRENCE-ID;TZID=America/Toronto:20090826T063000 +CREATED:20090817T143146Z +DESCRIPTION: +LAST-MODIFIED:20090826T203825Z +LOCATION:MDCL +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Repeater Event +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=America/Toronto:20090819T063000 +DTEND;TZID=America/Toronto:20090819T103000 +RRULE:FREQ=WEEKLY;BYDAY=WE;WKST=SU +DTSTAMP:20091111T124521Z +UID:glk3in708vjsb4l66g5t1od1as@google.com +CREATED:20090817T143146Z +DESCRIPTION: +LAST-MODIFIED:20090826T203825Z +LOCATION:MDCL +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Repeater Event +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=America/Toronto:20090827T083000 +DTEND;TZID=America/Toronto:20090827T093000 +DTSTAMP:20091111T124521Z +UID:nc2o66s0u36iesitl2l0b8inn8@google.com +RECURRENCE-ID;TZID=America/Toronto:20090824T083000 +CREATED:00001231T000000Z +DESCRIPTION: +LAST-MODIFIED:20090826T203615Z +LOCATION:Tims +SEQUENCE:2 +STATUS:CONFIRMED +SUMMARY:Breakfast at Tims +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=America/Toronto:20090821T090000 +DTEND;TZID=America/Toronto:20090821T100000 +DTSTAMP:20091111T124521Z +UID:nc2o66s0u36iesitl2l0b8inn8@google.com +RECURRENCE-ID;TZID=America/Toronto:20090819T083000 +CREATED:00001231T000000Z +DESCRIPTION: +LAST-MODIFIED:20090821T141922Z +LOCATION:Tims +SEQUENCE:2 +STATUS:CONFIRMED +SUMMARY:Breakfast at Tims +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=America/Toronto:20090819T083000 +DTEND;TZID=America/Toronto:20090819T093000 +RRULE:FREQ=WEEKLY;BYDAY=MO;WKST=SU +DTSTAMP:20091111T124521Z +UID:nc2o66s0u36iesitl2l0b8inn8@google.com +CREATED:00001231T000000Z +DESCRIPTION: +LAST-MODIFIED:20090820T002154Z +LOCATION:Tims +SEQUENCE:2 +STATUS:CONFIRMED +SUMMARY:Breakfast at Tims +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART:20090817T110000Z +DTEND:20090817T170000Z +DTSTAMP:20091111T124521Z +UID:ppqqomfff9i293gvdrkjdg2e9c@google.com +CREATED:20090817T140527Z +DESCRIPTION: +LAST-MODIFIED:20090817T140530Z +LOCATION:Tiffany's +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Breakfast at Tiffany's +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/DateTime2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/DateTime2.ics new file mode 100644 index 00000000..7860d9ba --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/DateTime2.ics @@ -0,0 +1,9 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +BEGIN:VEVENT +SUMMARY:Testing +DTSTART:19970412 +DTEND:19970412T020000 +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Duration1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Duration1.ics new file mode 100644 index 00000000..6d60c471 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Duration1.ics @@ -0,0 +1,32 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//test.org/Calendar//EN +BEGIN:VTIMEZONE +TZID:Europe/Paris +X-LIC-LOCATION:Europe/Paris +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:CEST +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:CET +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20090202T114737Z +LAST-MODIFIED:20090202T114820Z +DTSTAMP:20090322T170116Z +UID:edb7a48a-d846-47f8-bad2-9ea3f29bcda5 +SUMMARY:Vacances de la Toussaint +DTSTART;TZID=Europe/Paris;VALUE=DATE:20081025 +DTEND;TZID=Europe/Paris;VALUE=DATE:20081106 +TRANSP:TRANSPARENT +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines1.ics new file mode 100644 index 00000000..c5b25188 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines1.ics @@ -0,0 +1,33 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +BEGIN:VEVENT +CREATED:20060713T165752Z +LAST-MODIFIED:20060718T205134Z +DTSTAMP:20060718T205134Z +UID:990859596 +SUMMARY:Degree Evaluation +CLASS:PUBLIC +DTSTART:20060112T133000 +DTEND:20060112T140000 +CATEGORIES:School +X-MOZILLA-ALARM-DEFAULT-LENGTH:30 +LOCATION:WSU +END:VEVENT + +BEGIN:VEVENT +CREATED:20060713T165752Z +LAST-MODIFIED:20060718T205134Z +DTSTAMP:20060718T205134Z +UID:965775320 +SUMMARY:IT Planning Mtg. +CLASS:PUBLIC +DTSTART:20060116T090000 +DTEND:20060116T110000 +CATEGORIES:Business +X-MOZILLA-ALARM-DEFAULT-LENGTH:1 +LOCATION:Daywest +X-MOZILLA-ALARM-DEFAULT-UNITS:hours +END:VEVENT +END:VCALENDAR + diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines2.ics new file mode 100644 index 00000000..320857c4 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines2.ics @@ -0,0 +1,65 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +BEGIN:VEVENT +CREATED:20060713T165752Z +LAST-MODIFIED:20060718T205134Z +DTSTAMP:20060718T205134Z +UID:990859596 +SUMMARY:Degree Evaluation +CLASS:PUBLIC +DTSTART:20060112T133000 +DTEND:20060112T140000 +CATEGORIES:School +X-MOZILLA-ALARM-DEFAULT-LENGTH:30 +LOCATION:WSU +END:VEVENT + +BEGIN:VEVENT +CREATED:20060713T165752Z +LAST-MODIFIED:20060718T205134Z +DTSTAMP:20060718T205134Z +UID:965775320 +SUMMARY:IT Planning Mtg. +CLASS:PUBLIC +DTSTART:20060116T090000 +DTEND:20060116T110000 +CATEGORIES:Business +X-MOZILLA-ALARM-DEFAULT-LENGTH:1 +LOCATION:Daywest +X-MOZILLA-ALARM-DEFAULT-UNITS:hours +END:VEVENT +END:VCALENDAR + +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +BEGIN:VEVENT +CREATED:20060713T165752Z +LAST-MODIFIED:20060718T205134Z +DTSTAMP:20060718T205134Z +UID:990859598 +SUMMARY:Degree Evaluation +CLASS:PUBLIC +DTSTART:20060112T133000 +DTEND:20060112T140000 +CATEGORIES:School +X-MOZILLA-ALARM-DEFAULT-LENGTH:30 +LOCATION:WSU +END:VEVENT + +BEGIN:VEVENT +CREATED:20060713T165752Z +LAST-MODIFIED:20060718T205134Z +DTSTAMP:20060718T205134Z +UID:965775321 +SUMMARY:IT Planning Mtg. +CLASS:PUBLIC +DTSTART:20060116T090000 +DTEND:20060116T110000 +CATEGORIES:Business +X-MOZILLA-ALARM-DEFAULT-LENGTH:1 +LOCATION:Daywest +X-MOZILLA-ALARM-DEFAULT-UNITS:hours +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines3.ics new file mode 100644 index 00000000..31f2d9e2 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines3.ics @@ -0,0 +1,16 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +X-WR-CALNAME:Cabin + +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Furnace filter - Vacuum out or replace +LOCATION:Cabin +STATUS:NEEDS-ACTION +CLASS:PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH:0 +DTSTAMP:20060728T195437Z +END:VTODO + +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines4.ics new file mode 100644 index 00000000..95a0e749 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/EmptyLines4.ics @@ -0,0 +1,311 @@ +BEGIN:VCALENDAR +X-WR-CALNAME:GKCC + +BEGIN:VEVENT +UID:fadc65bd4f5d746ce4a581f072ca66ea +DTSTART;VALUE=DATE:20090117T200000 +SUMMARY:Apple Hill Chamber Players +DTEND;VALUE=DATE:20090117T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1232164801#eventid2377 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:8a435f4360e7e7b3ba5cc38d65791b55 +DTSTART;VALUE=DATE:20090120T193000 +SUMMARY:Hairspray +DTEND;VALUE=DATE:20090120T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1232424001#eventid2378 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:816d755f77c4373e2099fed63bf1dc2c +DTSTART;VALUE=DATE:20090121T193000 +SUMMARY:The Birth of Broadaway: 1866 to 1900 by Frank Behrens +DTEND;VALUE=DATE:20090121T213000 +LOCATION:Historical Society of Cheshire County, 246 Main Street, Keene, NH 03431 +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1232514001#eventid2618 +CREATED:20090102T112428Z +END:VEVENT + + +BEGIN:VEVENT +UID:5e209d3362c2e4936083436459fc5622 +DTSTART;VALUE=DATE:20090122T180000 +SUMMARY:Relay For Life Kickoff Celebration +DTEND;VALUE=DATE:20090122T200000 +LOCATION:Dragon Palace Restaurant in Troy +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1232600401#eventid2617 +CREATED:20081229T081706Z +END:VEVENT + + +BEGIN:VEVENT +UID:e43fdedc4362510d074a57e36aa7fc66 +DTSTART;VALUE=DATE:20090124T200000 +SUMMARY:Sweet Honey in the Rock +DTEND;VALUE=DATE:20090124T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1232769601#eventid2379 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:f469350a54fc34bfeb898b2e6101f276 +DTSTART;VALUE=DATE:20090127T073000 +SUMMARY:2009 NH Industry Forecast +DTEND;VALUE=DATE:20090127T093000 +LOCATION:SERESC Conference Center, Bedford NH +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1233032401#eventid2615 +CREATED:20081216T103808Z +END:VEVENT + + +BEGIN:VEVENT +UID:d4da3e845fc51404fab62d71f59a5f51 +DTSTART;VALUE=DATE:20090131T200000 +SUMMARY:Best of North Shore Comedy +DTEND;VALUE=DATE:20090131T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1233374401#eventid2380 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:6d6d1dc09bb36044ce2558f3a6175388 +DTSTART;VALUE=DATE:20090131T140000 +SUMMARY:Play Reading: The Devil's Disciple by Bernard Shaw, read by the Hourglass Thea +DTEND;VALUE=DATE:20090131T150000 +LOCATION:Historical Society of Cheshire County +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1233378001#eventid2619 +CREATED:20090102T112433Z +END:VEVENT + + +BEGIN:VEVENT +UID:a1d39d09fa5f2ba6574368f2dbefd63c +DTSTART;VALUE=DATE:20090201T160000 +SUMMARY:Keene Chamber Orchestra +DTEND;VALUE=DATE:20090201T180000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1233460801#eventid2351 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:e66fed7e85b3fa5ccefedad28a6acce3 +DTSTART;VALUE=DATE:20090203T170000 +SUMMARY:Granite State College Open House +DTEND;VALUE=DATE:20090203T190000 +LOCATION:Claremont, Concord, Portsmouth and Conway Centers +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1233637201#eventid2614 +CREATED:20081216T082614Z +END:VEVENT + + +BEGIN:VEVENT +UID:9e0a3152c60ec761e304c98165abc74d +DTSTART;VALUE=DATE:20090206T200000 +SUMMARY:Sam Bush +DTEND;VALUE=DATE:20090206T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1233892801#eventid2337 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:d739ef1573b5b0139f804c346807ab67 +DTSTART;VALUE=DATE:20090207T100000 +SUMMARY:Keene Ice & Snow Festival +DTEND;VALUE=DATE:20090207T160000 +LOCATION:Downtown Keene +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1234062000#eventid2536 +CREATED:20081030T090713Z +END:VEVENT + + +BEGIN:VEVENT +UID:063488b481c6b2f02eaf52c2bd96bb4f +DTSTART;VALUE=DATE:20090212T200000 +SUMMARY:The Keene Lions Club is pleased to announce its 57th Annual Musical, Rodgers & Hammerstein +DTEND;VALUE=DATE:20090214T223000 +LOCATION:The Colonial Theatre, Main Street, Keene, NH +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1234414801#eventid2608 +CREATED:20081202T143647Z +END:VEVENT + + +BEGIN:VEVENT +UID:4f71aec437b771a63eb25ad815d68040 +DTSTART;VALUE=DATE:20090213T200000 +SUMMARY:Keene Lions Club: Carousel +DTEND;VALUE=DATE:20090213T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1234497601#eventid2352 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:6bd343b5580d078af50225e4de41a8c2 +DTSTART;VALUE=DATE:20090219T073000 +SUMMARY:Breakfast With The Best +DTEND;VALUE=DATE:20090219T093000 +LOCATION:Radisson Hotel/Center of NH, Manchester NH +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1235019601#eventid2616 +CREATED:20081216T115609Z +END:VEVENT + + +BEGIN:VEVENT +UID:ae9312c2e7ebebe6e97b331098a5c312 +DTSTART;VALUE=DATE:20090221T190000 +SUMMARY:Messiah Sing +DTEND;VALUE=DATE:20090221T200000 +LOCATION:23 Central Sq, Keene, NH +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1235192401#eventid2544 +CREATED:20081106T085139Z +END:VEVENT + + +BEGIN:VEVENT +UID:d7a5f25afb63ef876c88ebfeffceb30a +DTSTART;VALUE=DATE:20090306T193000 +SUMMARY:San Jose Taiko +DTEND;VALUE=DATE:20090306T213000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1236312001#eventid2338 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:84390394314ceb21aa2cf91ae1d3c9e3 +DTSTART;VALUE=DATE:20090314T200000 +SUMMARY:Footloose +DTEND;VALUE=DATE:20090314T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1237003201#eventid2339 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:747d4ff966ac2414db989bedec60e0ee +DTSTART;VALUE=DATE:20090321T200000 +SUMMARY:NY Gilbert and Sullivan Players: The Mikado +DTEND;VALUE=DATE:20090321T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1237608001#eventid2340 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:3b32e26150b70486c207a57d18d35df1 +DTSTART;VALUE=DATE:20090327 +SUMMARY:Made In NH Try It & Buy It Expo +DTEND;VALUE=DATE:20090329 +LOCATION:NH National Guard Armory, Manchester +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1238130001#eventid2611 +CREATED:20081211T085213Z +END:VEVENT + + +BEGIN:VEVENT +UID:0173559dc610ddfa861be18cc831248e +DTSTART;VALUE=DATE:20090328T200000 +SUMMARY:Trinity Irish Dance Company +DTEND;VALUE=DATE:20090328T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1238212801#eventid2341 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:2126bc6788f122f8e4c33a84f45ccde0 +DTSTART;VALUE=DATE:20090405T140000 +SUMMARY:Moscow Cats Theatre +DTEND;VALUE=DATE:20090405T160000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1238904001#eventid2342 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:df502f225a0b75412c2666b270988338 +DTSTART;VALUE=DATE:20090418T200000 +SUMMARY:Second City +DTEND;VALUE=DATE:20090418T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1240027201#eventid2344 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:3ac7931dc57785b0ee68e475c06dbc0c +DTSTART;VALUE=DATE:20090425T160000 +SUMMARY:Russian National Ballet: Cinderella +DTEND;VALUE=DATE:20090425T180000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1240632001#eventid2343 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:06a3311386ad1642baf8fb37b7042b4d +DTSTART;VALUE=DATE:20090502T190000 +SUMMARY:6th Annual Keene-tucky Derby Benefit for Cedarcrest Center +DTEND;VALUE=DATE:20090502T210000 +LOCATION:Keene Country Club +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1241236801#eventid2233 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:94e6927ad54c52491a5241417b71de51 +DTSTART;VALUE=DATE:20090502T200000 +SUMMARY:Get the Led Out: The Ultimate Led Zeppelin Tribute +DTEND;VALUE=DATE:20090502T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1241236801#eventid2345 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:0161a213a96c5b4b235b81c875574273 +DTSTART;VALUE=DATE:20090509T200000 +SUMMARY:Buddy Holly 50th Anniversary Family Reunion +DTEND;VALUE=DATE:20090509T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1241841601#eventid2346 +CREATED:20080716T070640Z +END:VEVENT + + +BEGIN:VEVENT +UID:ab4cddefd58dd15af91ad531e92d53ba +DTSTART;VALUE=DATE:20090510T160000 +SUMMARY:Keene Chamber Orchestra +DTEND;VALUE=DATE:20090510T180000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1241928001#eventid2353 +CREATED:20080716T070640Z +END:VEVENT + +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Encoding1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Encoding1.ics new file mode 100644 index 00000000..d363cb35 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Encoding1.ics @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID;ENCODING=BASE64:dXVpZDExNTMxNzA0MzA0MDY= +SUMMARY:Test event +CATEGORIES:Category +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR +DTEND:19970902T100000 +SEQUENCE;ENCODING=BASE64:MQ== +LOCATION:Daywest +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Encoding2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Encoding2.ics new file mode 100644 index 00000000..62308d05 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Encoding2.ics @@ -0,0 +1,52 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +ATTACH;VALUE=BINARY;ENCODING=BASE64: + VGhpcyBpcyBhIHRlc3QgdG8gdHJ5IG91dCBiYXNlNjQgZW5jb2Rpbmcgd2l0aG91dCBiZW + luZyB0b28gbGFyZ2UuDQpUaGlzIGlzIGEgdGVzdCB0byB0cnkgb3V0IGJhc2U2NCBlbmNv + ZGluZyB3aXRob3V0IGJlaW5nIHRvbyBsYXJnZS4NClRoaXMgaXMgYSB0ZXN0IHRvIHRyeS + BvdXQgYmFzZTY0IGVuY29kaW5nIHdpdGhvdXQgYmVpbmcgdG9vIGxhcmdlLg0KVGhpcyBp + cyBhIHRlc3QgdG8gdHJ5IG91dCBiYXNlNjQgZW5jb2Rpbmcgd2l0aG91dCBiZWluZyB0b2 + 8gbGFyZ2UuDQpUaGlzIGlzIGEgdGVzdCB0byB0cnkgb3V0IGJhc2U2NCBlbmNvZGluZyB3 + aXRob3V0IGJlaW5nIHRvbyBsYXJnZS4NClRoaXMgaXMgYSB0ZXN0IHRvIHRyeSBvdXQgYm + FzZTY0IGVuY29kaW5nIHdpdGhvdXQgYmVpbmcgdG9vIGxhcmdlLg0KVGhpcyBpcyBhIHRl + c3QgdG8gdHJ5IG91dCBiYXNlNjQgZW5jb2Rpbmcgd2l0aG91dCBiZWluZyB0b28gbGFyZ2 + UuDQpUaGlzIGlzIGEgdGVzdCB0byB0cnkgb3V0IGJhc2U2NCBlbmNvZGluZyB3aXRob3V0 + IGJlaW5nIHRvbyBsYXJnZS4NClRoaXMgaXMgYSB0ZXN0IHRvIHRyeSBvdXQgYmFzZTY0IG + VuY29kaW5nIHdpdGhvdXQgYmVpbmcgdG9vIGxhcmdlLg0KVGhpcyBpcyBhIHRlc3QgdG8g + dHJ5IG91dCBiYXNlNjQgZW5jb2Rpbmcgd2l0aG91dCBiZWluZyB0b28gbGFyZ2UuDQpUaG + lzIGlzIGEgdGVzdCB0byB0cnkgb3V0IGJhc2U2NCBlbmNvZGluZyB3aXRob3V0IGJlaW5n + IHRvbyBsYXJnZS4NClRoaXMgaXMgYSB0ZXN0IHRvIHRyeSBvdXQgYmFzZTY0IGVuY29kaW + 5nIHdpdGhvdXQgYmVpbmcgdG9vIGxhcmdlLg== +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +CATEGORIES:Category +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR +DTEND:19970902T100000 +LOCATION:Daywest +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Encoding3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Encoding3.ics new file mode 100644 index 00000000..d363cb35 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Encoding3.ics @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID;ENCODING=BASE64:dXVpZDExNTMxNzA0MzA0MDY= +SUMMARY:Test event +CATEGORIES:Category +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR +DTEND:19970902T100000 +SEQUENCE;ENCODING=BASE64:MQ== +LOCATION:Daywest +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event1.ics new file mode 100644 index 00000000..3c93d491 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event1.ics @@ -0,0 +1,14 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +DTSTART:20060718T100000 +DTEND:20060718T110000 +LOCATION:Daywest +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event2.ics new file mode 100644 index 00000000..052e106e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event2.ics @@ -0,0 +1,97 @@ +BEGIN:VCALENDAR +VERSION;X-BLA=1:2.0 +PRODID + :-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :990859596 +SUMMARY + :Degree Evaluation +CLASS + :PUBLIC +DTSTART + :20060112T133000 +DTEND + :20060112T140000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :30 +LOCATION + :WSU +CATEGORIES + :School +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :965775320 +SUMMARY + :IT Planning Mtg. +CLASS + :PUBLIC +DTSTART + :20060116T090000 +DTEND + :20060116T110000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +LOCATION + :Daywest +CATEGORIES + :Business +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :979830820 +SUMMARY + :Teaching Sunday School +CLASS + :PUBLIC +DTSTART + :20060122T130000 +DTEND + :20060122T140000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :2 +CATEGORIES + :Church +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :911322954 +SUMMARY + :Theraclin Training @ Crestwood (Attend in person) +CLASS + :PUBLIC +DTSTART + :20060123T140000 +DTEND + :20060123T160000 +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event3.ics new file mode 100644 index 00000000..8d807804 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event3.ics @@ -0,0 +1,3090 @@ +BEGIN:VCALENDAR +VERSION + :2.0 +PRODID + :-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :990859596 +SUMMARY + :Degree Evaluation +CLASS + :PUBLIC +DTSTART + :20060112T133000 +DTEND + :20060112T140000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :30 +LOCATION + :WSU +CATEGORIES + :School +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :965775320 +SUMMARY + :IT Planning Mtg. +CLASS + :PUBLIC +DTSTART + :20060116T090000 +DTEND + :20060116T110000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +LOCATION + :Daywest +CATEGORIES + :Business +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :979830820 +SUMMARY + :Teaching Sunday School +CLASS + :PUBLIC +DTSTART + :20060122T130000 +DTEND + :20060122T140000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :2 +CATEGORIES + :Church +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :911322954 +SUMMARY + :Theraclin Training @ Crestwood (Attend in person) +CLASS + :PUBLIC +DTSTART + :20060123T140000 +DTEND + :20060123T160000 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :929450481 +SUMMARY + :Meet with Greg Wood @ CSI +CLASS + :PUBLIC +DTSTART + :20060124T153000 +DTEND + :20060124T170000 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :932815999 +SUMMARY + :Dentist Appt. +CLASS + :PUBLIC +DTSTART + :20060125T083000 +DTEND + :20060125T093000 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :915166509 +SUMMARY + :Theraclin Training @ Crestwood (Attend in person) +CLASS + :PUBLIC +DTSTART + :20060124T090000 +DTEND + :20060124T113000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :943920440 +SUMMARY + :Buy CS Book @ WSU +CLASS + :PUBLIC +DTSTART + :20060123T120000 +DTEND + :20060123T123000 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :907538096 +SUMMARY + :Phone Meeting with Unitime (Michael Hanthorn) +CLASS + :PUBLIC +DTSTART + :20060130T100000 +DTEND + :20060130T103000 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :999351685 +SUMMARY + :Contact CSI to figure out a time to work +CLASS + :PUBLIC +DTSTART + ;VALUE=DATE + :20060208 +DTEND + ;VALUE=DATE + :20060209 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :30 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :937726387 +SUMMARY + :Contact DeWayne to determine a specific time frame to work at CSI +CLASS + :PUBLIC +DTSTART + ;VALUE=DATE + :20060213 +DTEND + ;VALUE=DATE + :20060214 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :2 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :947279220 +SUMMARY + :Accu-Med Demo +CLASS + :PUBLIC +DTSTART + :20060215T120000 +DTEND + :20060215T130000 +LOCATION + :Daywest +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :939390335 +SUMMARY + :Theraclin conference call +CLASS + :PUBLIC +DTSTART + :20060222T140000 +DTEND + :20060222T143000 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :903578853 +SUMMARY + :Calendar Systems +CLASS + :PUBLIC +DTSTART + :20060227T160000 +DTEND + :20060227T180000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :30 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :964034331 +SUMMARY + :Don't go through Riverdale +CLASS + :PUBLIC +DTSTART + ;VALUE=DATE + :20060306 +DTEND + ;VALUE=DATE + :20060307 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :928644641 +SUMMARY + :Greg leaves for Houston +CLASS + :PUBLIC +DTSTART + :20060301T170000 +DTEND + :20060301T180000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +CATEGORIES + :CSI +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :947901527 +SUMMARY + :HelpDesk Training +CLASS + :PUBLIC +DTSTART + :20060306T093000 +DTEND + :20060306T113000 +LOCATION + :Highland +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :906000955 +SUMMARY + :HelpDesk Training +CLASS + :PUBLIC +DTSTART + :20060306T143000 +DTEND + :20060306T163000 +LOCATION + :AG - Salt Lake +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :942259956 +SUMMARY + :Meet with Doctor @ U of U Hospital +CLASS + :PUBLIC +DTSTART + :20060306T130000 +DTEND + :20060306T140000 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :978012213 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060222T130000 +DTEND + :20060222T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131606 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174735Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :919100698 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060220T130000 +DTEND + :20060220T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131610 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174947Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :960550565 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060215T130000 +DTEND + :20060215T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131611 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174859Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :957531279 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060227T130000 +DTEND + :20060227T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131613 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174858Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :908475710 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060208T130000 +DTEND + :20060208T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131614 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174858Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :916344672 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060206T130000 +DTEND + :20060206T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131616 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174857Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :942420870 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060201T130000 +DTEND + :20060201T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131617 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174851Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :912942220 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060130T130000 +DTEND + :20060130T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131618 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174842Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :977647084 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060125T130000 +DTEND + :20060125T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131621 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174841Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :933760467 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060123T130000 +DTEND + :20060123T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131622 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174841Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :985324211 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060213T130000 +DTEND + :20060213T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131623 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174840Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :995604576 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060301T130000 +DTEND + :20060301T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060307T131624 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174840Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :957076524 +SUMMARY + :IT Staff Mtg +CLASS + :PUBLIC +DTSTART + :20060201T090000 +DTEND + :20060201T100000 +X-MOZILLA-LASTALARMACK + :20060307T131625 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT15M +X-MOZ-LASTACK + :20060713T174830Z +DESCRIPTION + :Mozilla Alarm: IT Staff Mtg +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :999079598 +SUMMARY + :IT Staff Mtg +CLASS + :PUBLIC +DTSTART + :20060215T090000 +DTEND + :20060215T100000 +X-MOZILLA-LASTALARMACK + :20060307T131625 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT15M +X-MOZ-LASTACK + :20060713T174817Z +DESCRIPTION + :Mozilla Alarm: IT Staff Mtg +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :930528168 +SUMMARY + :IT Staff Mtg +CLASS + :PUBLIC +DTSTART + :20060301T090000 +DTEND + :20060301T100000 +X-MOZILLA-LASTALARMACK + :20060307T131625 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT15M +X-MOZ-LASTACK + :20060713T174816Z +DESCRIPTION + :Mozilla Alarm: IT Staff Mtg +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :976319250 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060308T130000 +DTEND + :20060308T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060308T123141 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174816Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :9e591e5a-af9f-11da-82f1-96800a937a3d +SUMMARY + :Pick up Contract from Calendar Systems +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060309T121500 +DTEND + :20060309T122500 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :8a502c06-b2ba-11da-a067-8572141b4651 +SUMMARY + :Unitime Tech Support +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060312T083000 +DTEND + :20060312T113000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +LOCATION + :Daywest +DESCRIPTION + :Jo Wojcik will contact me. I am supposed to e-mail him when I have + successfully accessed Unitime again after the reinstall of SQL Server. +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :950513896 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060313T130000 +DTEND + :20060313T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060316T121820 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174816Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :935816782 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060315T130000 +DTEND + :20060315T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060316T121820 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174815Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :927784597 +SUMMARY + :IT Staff Mtg +CLASS + :PUBLIC +DTSTART + :20060315T090000 +DTEND + :20060315T100000 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-LASTALARMACK + :20060316T121820 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT15M +X-MOZ-LASTACK + :20060713T174815Z +DESCRIPTION + :Mozilla Alarm: IT Staff Mtg +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :953939377 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060320T130000 +DTEND + :20060320T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060321T133419 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174814Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :23346626-b91a-11da-b18e-9ebdb1c6cb22 +SUMMARY + :Take Math Test +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060322T100000 +DTEND + :20060322T120000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :908774111 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060322T130000 +DTEND + :20060322T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060328T151334 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174807Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :901840929 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060327T130000 +DTEND + :20060327T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060328T151335 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174741Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :b0d7bfcc-b521-11da-b285-ff1d5aa59907 +SUMMARY + :Follow-up with Unitime for support +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060327T083000 +DTEND + :20060327T103000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :936944113 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060329T130000 +DTEND + :20060329T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060406T130359 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174740Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :982210282 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060403T130000 +DTEND + :20060403T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060406T130400 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174740Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :994358423 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060405T130000 +DTEND + :20060405T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060406T130400 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174739Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :962381288 +SUMMARY + :IT Staff Mtg +CLASS + :PUBLIC +DTSTART + :20060329T090000 +DTEND + :20060329T100000 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-LASTALARMACK + :20060406T130401 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT15M +X-MOZ-LASTACK + :20060713T174726Z +DESCRIPTION + :Mozilla Alarm: IT Staff Mtg +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :46c9cbe6-c5a0-11da-8e7e-bc1141f6e70c +SUMMARY + :Meet with Greg & Troy +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060109T160000 +DTEND + :20060109T173000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +LOCATION + :Calendar Systems +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :908848554 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060410T130000 +DTEND + :20060410T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060419T162417 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174656Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :912150002 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060412T130000 +DTEND + :20060412T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060419T162419 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174655Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :973314307 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060417T130000 +DTEND + :20060417T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060419T162420 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174654Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :999620763 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060419T130000 +DTEND + :20060419T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060419T162420 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174653Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :916657940 +SUMMARY + :IT Staff Mtg +CLASS + :PUBLIC +DTSTART + :20060412T090000 +DTEND + :20060412T100000 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-LASTALARMACK + :20060419T162420 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT15M +X-MOZ-LASTACK + :20060713T174652Z +DESCRIPTION + :Mozilla Alarm: IT Staff Mtg +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :81344a87-d16a-11da-b310-f5ae7fc94a53 +SUMMARY + :St. George HelpDesk Training & Applesys planning +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060502T070000 +DTEND + :20060503T200000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +LOCATION + :St. George +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :d687631c-d16b-11da-82c1-e6da71f966be +SUMMARY + :American Fork Training - Applesys research +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060428T070000 +DTEND + :20060428T180000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +LOCATION + :American Fork +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :947113591 +SUMMARY + :American Fork Training - Applesys research +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060501T140000Z +DTEND + :20060501T220000Z +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +LOCATION + :American Fork +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :08fadbd6-d16f-11da-a006-e247a5641153 +SUMMARY + :Ogden training - Applesys research +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060510T080000 +DTEND + :20060510T170000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :2c9a1536-d170-11da-9de4-9ab513ed8962 +SUMMARY + :Salt Lake Training - Applesys research +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060517T140000Z +DTEND + :20060517T230000Z +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :993267165 +SUMMARY + :Vernal training - Applesys research +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060509T140000Z +DTEND + :20060509T230000Z +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +LOCATION + :Vernal +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :54c675f1-cff3-11da-8619-b3c20a2ea3c4 +SUMMARY + :Implementation of Unitime +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + ;VALUE=DATE + :20060424 +DTEND + ;VALUE=DATE + :20060425 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :2 +LOCATION + :Crestwood +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060424T081334 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT2H +X-MOZ-LASTACK + :20060713T174651Z +DESCRIPTION + :Mozilla Alarm: Implementation of Unitime +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :936825862 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060424T130000 +DTEND + :20060424T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060427T161049 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174651Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :965267223 +SUMMARY + :Allergy Shots +CLASS + :PUBLIC +DTSTART + :20060426T130000 +DTEND + :20060426T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060427T161050 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T174650Z +DESCRIPTION + :Mozilla Alarm: Allergy Shots +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :961046507 +SUMMARY + :IT Staff Mtg +CLASS + :PUBLIC +DTSTART + :20060426T090000 +DTEND + :20060426T100000 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-LASTALARMACK + :20060427T161050 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT15M +X-MOZ-LASTACK + :20060713T174650Z +DESCRIPTION + :Mozilla Alarm: IT Staff Mtg +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :ffbd0fb0-d63b-11da-85ef-f3a1118ab604 +SUMMARY + :Tinting car windows +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060504T110000 +DTEND + :20060504T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :e98bae69-db9b-11da-b5b8-9b8205ee8541 +SUMMARY + :Bountiful Training +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060505T090000 +DTEND + :20060505T150000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :f7ce8507-e45e-11da-9f90-b735cddc4e3d +SUMMARY + :Training with Brandi Rich +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060516T140000 +DTEND + :20060516T160000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :26a07e3a-d170-11da-a72f-edbb80eb8422 +SUMMARY + :Salt Lake Training - Applesys research +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060516T080000 +DTEND + :20060516T140000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :b8f6f886-eff8-11da-b298-db30d58d7624 +SUMMARY + :Teacher Development Meeting +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060531T190000 +DTEND + :20060531T200000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +LOCATION + :Church +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :108217d4-efe0-11da-81bc-bdbc15b2917e +SUMMARY + :Unitime payroll run for both Crestwood and Highland +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060605T080000 +DTEND + :20060605T170000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :5 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +X-MOZILLA-LASTALARMACK + :20060531T092808 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-P5D +X-MOZ-LASTACK + :20060713T174649Z +DESCRIPTION + :Mozilla Alarm: Unitime payroll run for both Crestwood and Highland +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :09896070-f121-11da-b398-f34782a9549a +SUMMARY + :Ethan's Baby Blessing +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060604T123000 +DTEND + :20060604T133000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +LOCATION + :Clinton 5th Ward +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :28a73ce0-f121-11da-86fb-bfea598ba91d +SUMMARY + :Picnic +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060604T134500 +DTEND + :20060604T163000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +END:VEVENT +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :f42ac2ca-f23e-11da-9a05-f7db4ed74351 +SUMMARY + :Autorewards - email after card activation +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :CSI +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :0113301e-f23f-11da-b59b-88f7cee54add +SUMMARY + :Autorewards - F&i list to activate cards +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :CSI +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :110672c4-f23f-11da-94de-b42514eaea8c +SUMMARY + :Flag resolved items as \"didn't fix this ticket\" when resolved + tickets are re-opened +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :1887836c-f23f-11da-861c-ffe7561910bf +SUMMARY + :Admin Report - # of status chg by cat/subcat +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :b140a8ba-f23f-11da-be64-f82bece622c1 +SUMMARY + :Admin Report - # of tickets per week/month/year +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :d2b89b3d-f23f-11da-a39e-fef6e66f3622 +SUMMARY + :Admin Report - #issues by cat/priority (top 10 expandible) +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :d8283ce4-f23f-11da-bf7b-b771a8d1a99c +SUMMARY + :Admin Report - avg time start to finish w/drilldown +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :6b250b01-f240-11da-aacf-c4afbc65988a +SUMMARY + :Admin Reports - all should be filtered by admin user +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :80787ebd-f240-11da-a579-f3c7068297a3 +SUMMARY + :Set est. comp time for ticket +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :883c672f-f240-11da-a4fe-82b0f70b29da +SUMMARY + :Add e-mail addresses inside of Active Directory +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :8f2d72fd-f240-11da-87ca-b3d566ac4ea8 +SUMMARY + :Add history & manual changes to PTO Accrual Program +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :98a4d863-f240-11da-9fb2-c3ef9fa64f2e +SUMMARY + :Schedule Charge Creation screens\, modified form\, remove option + for Include Created charges +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :9c90bdbe-f240-11da-b958-ff21a75ba193 +SUMMARY + :Rewrite UR Reply Form +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :a0908048-f240-11da-bd73-fcbc181a2510 +SUMMARY + :Plan screens - Remove from list for Branches +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :a6ea4b00-f240-11da-8e6f-881661694c1a +SUMMARY + :Evaluate Applesys needs +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :accce69e-f240-11da-9eab-f3e4e796eb23 +SUMMARY + :Create a Daily Labor Report for Unitime +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :b2d8c3c5-f240-11da-b469-a6561f6d23ff +SUMMARY + :Attach Plan of Care to the top of all notes +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VTODO +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :b88f0f84-f240-11da-9ece-ff39b13fee56 +SUMMARY + :Inservice Groups and Subgroups +STATUS + :NEEDS-ACTION +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +CATEGORIES + :Work +END:VTODO +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :e81b2212-f26a-11da-8732-a6a5d600e2ed +SUMMARY + :Boy's Birthday Party +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060605T170000 +DTEND + :20060605T200000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +LOCATION + :Mom's House +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :f7e30940-f1e7-11da-9781-aa6f22c6ff9e +SUMMARY + :St. George Trip +STATUS + :CONFIRMED +CLASS + :PUBLIC +DTSTART + :20060607T060000 +DTEND + :20060611T180000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +LOCATION + :Townhouse in St. George +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +X-MOZILLA-LASTALARMACK + :20060612T065245 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-P1D +X-MOZ-LASTACK + :20060713T174647Z +DESCRIPTION + :Mozilla Alarm: St. George Trip +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :5c41c2b0-f500-11da-ab95-c53917c633d6 +SUMMARY + :Baptisms for the Dead +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060616T191500 +DTEND + :20060616T213000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :2 +LOCATION + :Bountiful Temple +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +DESCRIPTION + :Be there by 6:45 p.m. +X-MOZILLA-LASTALARMACK + :20060615T125318 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-P2D +X-MOZ-LASTACK + :20060713T174634Z +DESCRIPTION + :Mozilla Alarm: Baptisms for the Dead +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :15eef6e0-fd8b-11da-9651-d32da72b98b9 +SUMMARY + :Brooke's Baby Shower +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060620T150000 +DTEND + :20060620T154500 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +LOCATION + :Daywest +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :decc25f0-f4fc-11da-a41f-b2cffd8c99e0 +SUMMARY + :Slagowski Family Party +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + ;VALUE=DATE + :20060624 +DTEND + ;VALUE=DATE + :20060625 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :3 +LOCATION + :Mom's House +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +X-MOZILLA-LASTALARMACK + :20060622T132441 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-P3D +X-MOZ-LASTACK + :20060713T174003Z +DESCRIPTION + :Mozilla Alarm: Slagowski Family Party +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :0528c6a3-fbe3-11da-9fa5-96d06b1c5802 +SUMMARY + :Installing CSI Programs +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060620T160000 +DTEND + :20060620T193000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :2 +LOCATION + :CSI +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +X-MOZILLA-LASTALARMACK + :20060622T132442 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-P2D +X-MOZ-LASTACK + :20060713T170947Z +DESCRIPTION + :Mozilla Alarm: Installing CSI Programs +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :08451702-0225-11db-be6d-c19c1a2171d1 +SUMMARY + :Fixing Unitime Implementation +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060623T110000 +DTEND + :20060623T123000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +LOCATION + :Daywest +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060623T102602 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T170947Z +DESCRIPTION + :Mozilla Alarm: Fixing Unitime Implementation +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :f4a36100-f4fc-11da-aab0-d6c37d34ed88 +SUMMARY + :Day Family Party +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060701T120000 +DTEND + :20060701T150000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :3 +LOCATION + :Park South of Kent's House +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +X-MOZILLA-LASTALARMACK + :20060630T101046 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-P3D +X-MOZ-LASTACK + :20060713T170946Z +DESCRIPTION + :Mozilla Alarm: Day Family Party +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :a78ddbb0-0535-11db-a4d3-d5f2dbee7d0e +SUMMARY + :Games +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060629T180000 +DTEND + :20060629T213000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +LOCATION + :Mom's House +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +X-MOZILLA-LASTALARMACK + :20060630T101054 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-P1D +X-MOZ-LASTACK + :20060713T170946Z +DESCRIPTION + :Mozilla Alarm: Games +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :12556a2c-0c78-11db-9d57-f4622a3e9cd8 +SUMMARY + :Talk with Sharon from Premiere +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060706T100000 +DTEND + :20060706T103000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +LOCATION + :Daywest +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :hours +X-MOZILLA-LASTALARMACK + :20060706T095431 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT1H +X-MOZ-LASTACK + :20060713T170946Z +DESCRIPTION + :Mozilla Alarm: Talk with Sharon from Premiere +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :6b30abc3-fa12-11da-bba1-9f0c8cbcbc20 +SUMMARY + :Dentist Appointment +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060712T080000 +DTEND + :20060712T093000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :3 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +X-MOZILLA-LASTALARMACK + :20060710T083503 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-P3D +X-MOZ-LASTACK + :20060713T165759Z +DESCRIPTION + :Mozilla Alarm: Dentist Appointment +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060713T165752Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :a9af0379-102e-11db-a3e9-f803f95f5838 +SUMMARY + :Daywest Meeting About Unitime +STATUS + :TENTATIVE +CLASS + :PRIVATE +DTSTART + :20060712T103000 +DTEND + :20060712T113000 +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +X-MOZILLA-LASTALARMACK + :20060712T160652 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-P1D +X-MOZ-LASTACK + :20060713T165758Z +DESCRIPTION + :Mozilla Alarm: Daywest Meeting About Unitime +ACTION + :DISPLAY +END:VALARM +END:VEVENT +BEGIN:VEVENT +CREATED + :20060714T150853Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :uuid1152889866393 +SUMMARY + :Code Review with Scott Wood +DTSTART + ;TZID=/mozilla.org/20050126_1/America/Boise + :20060720T173000 +DTEND + ;TZID=/mozilla.org/20050126_1/America/Boise + :20060720T193000 +LOCATION + :Calendar Systems +END:VEVENT +BEGIN:VEVENT +CREATED + :20060714T163818Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :uuid1152895128940 +SUMMARY + :Grooming for Sadie +DTSTART + ;TZID=/mozilla.org/20050126_1/America/Boise + :20060717T080000 +DTEND + ;TZID=/mozilla.org/20050126_1/America/Boise + :20060717T083000 +LOCATION + :ERZ +END:VEVENT +BEGIN:VEVENT +CREATED + :20060717T155852Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :uuid1153151978239 +SUMMARY + :Unitime Support Call - Ryan +PRIORITY + :0 +CLASS + :PUBLIC +DTSTART + ;TZID=/mozilla.org/20050126_1/America/Boise + :20060717T143000 +DTEND + ;TZID=/mozilla.org/20050126_1/America/Boise + :20060717T163000 +LOCATION + :Daywest +END:VEVENT +BEGIN:VEVENT +CREATED + :20060717T210517Z +LAST-MODIFIED + :20060718T205134Z +DTSTAMP + :20060718T205134Z +UID + :uuid1153170430406 +SUMMARY + :Ryan Follow-up Call +PRIORITY + :0 +STATUS + :CANCELLED +CLASS + :PUBLIC +DTSTART + ;TZID=/mozilla.org/20050126_1/America/Boise + :20060718T100000 +DTEND + ;TZID=/mozilla.org/20050126_1/America/Boise + :20060718T110000 +LOCATION + :Daywest +END:VEVENT +BEGIN:VTIMEZONE +TZID + :/mozilla.org/20050126_1/America/Boise +X-LIC-LOCATION + :America/Boise +BEGIN:STANDARD +TZOFFSETFROM + :-060000 +TZOFFSETTO + :-070000 +TZNAME + :MST +DTSTART + :19701025T020000 +RRULE + :FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETFROM + :-070000 +TZOFFSETTO + :-060000 +TZNAME + :MDT +DTSTART + :19700405T020000 +RRULE + :FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=4 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID + :945ea47d-11f5-11db-9a1e-e8a86add7ab5 +SUMMARY + :Allergy Shots +LOCATION + :McKay Dee Hospital +STATUS + :TENTATIVE +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +RRULE + :FREQ=WEEKLY;INTERVAL=1;BYDAY=TH +DTSTART + :20060713T130000 +DTEND + :20060713T131500 +DTSTAMP + :20060718T205134Z +LAST-MODIFIED + :20060725T182907Z +END:VEVENT +BEGIN:VEVENT +UID + :93553c32-1c0b-11db-9d80-8337f42dd8e4 +SUMMARY + :Calendar Systems Bug Fix +LOCATION + :CSI +STATUS + :TENTATIVE +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-UNITS + :days +X-MOZILLA-ALARM-DEFAULT-LENGTH + :1 +DTSTART + :20060727T160000 +DTEND + :20060727T170000 +DTSTAMP + :20060725T182919Z +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-P1D +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event4.ics new file mode 100644 index 00000000..8bdde860 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Event4.ics @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +ATTACH:http://www.applegatehomecare.com/Calendars/cabin.ics +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +CATEGORIES:Category +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR +DTEND:19970902T100000 +LOCATION:Daywest +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/GeographicLocation1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/GeographicLocation1.ics new file mode 100644 index 00000000..f8b3452b --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/GeographicLocation1.ics @@ -0,0 +1,36 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VEVENT +CREATED:20060717T210517Z +LAST-MODIFIED:20060717T210718Z +DTSTAMP:20060717T210718Z +UID:uuid1153170430406 +SUMMARY:Test event +CATEGORIES:Category +GEO:37.386013;-122.082932 +DTSTART;TZID=US-Eastern:19970902T090000 +RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR +DTEND:19970902T100000 +LOCATION:Daywest +END:VEVENT +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Google1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Google1.ics new file mode 100644 index 00000000..748b8eb0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Google1.ics @@ -0,0 +1,42 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:Test Calendar +X-WR-TIMEZONE:Europe/Berlin +BEGIN:VTIMEZONE +TZID:Europe/Berlin +X-LIC-LOCATION:Europe/Berlin +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:CEST +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:CET +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=Europe/Berlin:20061211T070000 +DURATION:PT3600S +RRULE:FREQ=DAILY;WKST=MO +DTSTAMP:20061223T162148Z +UID:594oeajmftl3r9qlkb476rpr3c@google.com +CLASS:PUBLIC +CREATED:20061215T212453Z +DESCRIPTION: +LAST-MODIFIED:20061215T212453Z +LOCATION: +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Zähne putzen +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language1.ics new file mode 100644 index 00000000..048a1903 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language1.ics @@ -0,0 +1,695 @@ +BEGIN:VCALENDAR +VERSION:2.0 +X-WR-CALNAME:Barça 2006 - 2007 +PRODID:-//Apple Computer\, Inc//iCal 2.0//EN +X-WR-RELCALID:2EBACFB1-8102-4CEF-BB39-024F9F179B0A +X-WR-TIMEZONE:Europe/Madrid +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALDESC:Todos los partidos del Barça desde princios de la temporada + 2006 – 2007.\n\nEl Barça\, esta temporada está jugando o ha jugado:\n\n + - Supercopa de España (campeón)\n- Supercopa de Europa (eliminado)\n- Tr + ofeo Joan Gamper (campeón)\n- Liga (en juego)\n- Champions League\n- Cop + a del Rey\n- Copa de Catalunya\n\n======================\n\nAll the game + s of the 2006-2007 season.\n\n +BEGIN:VTIMEZONE +TZID:Europe/Madrid +LAST-MODIFIED:20060831T085543Z +BEGIN:DAYLIGHT +DTSTART:20060326T010000 +TZOFFSETTO:+0200 +TZOFFSETFROM:+0000 +TZNAME:CEST +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:20061029T030000 +TZOFFSETTO:+0100 +TZOFFSETFROM:+0200 +TZNAME:CET +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:20070325T030000 +TZOFFSETTO:+0200 +TZOFFSETFROM:+0100 +TZNAME:CEST +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:20071028T030000 +TZOFFSETTO:+0100 +TZOFFSETFROM:+0200 +TZNAME:CET +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:San Mamés\, Bilbao +DTSTAMP:20060731T103716Z +UID:97199ED3-78FF-43FD-A77D-A524A668853F-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061001T170000 +SUMMARY:Athletic Bilbao - Barça +DESCRIPTION:Liga\nJornada 5\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060825T123839Z +UID:B3284440-9E23-4DEF-89BE-94E8740BD2F3-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060822T220000 +SUMMARY:Barça 4 - 0 Bayern Munich +DESCRIPTION:Trofeu Joan Gamper\n\nGoles:\n1 - 0\, min. 29\, Ronaldinho\n + 2 - 0\, min. 32\, Eto'o\n3 - 0\, min. 39\, Eto'o\n4 - 0\, min. 50\, Savi + ola +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T105715Z +UID:2888A37B-9ED4-411D-ADD4-D73B67825FEE-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061112T170000 +SUMMARY:Barça - Zaragoza +DESCRIPTION:Liga\nJornada 10\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T102726Z +UID:44725A49-4A14-4FE7-AFC1-6993D7417BD0-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:1 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060924T170000 +SUMMARY:Barça - Valencia +DESCRIPTION:Liga\nJornada 4\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H +LOCATION:Georgi Asparoukhov\, Sofia\, Bulgaria +DTSTAMP:20060830T084132Z +UID:3B4D5094-F9CE-4640-BFFC-52C06B7542F2-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:7 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061122T110000 +SUMMARY:Levski Sofia - Barça +DESCRIPTION:Champions League\nFase de Grupos\, Jornada 5\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Stade Louis II\, Mónaco +DTSTAMP:20060830T081312Z +UID:50F41DD6-0288-47D0-B1AA-43F8865CB641-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:9 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060825T204500 +SUMMARY:Barça 0 - 3 Sevilla +DESCRIPTION:Supercopa de Europa\n\n\nGoles:\n0 - 1\, min. 7\, Renato\n0 + - 2\, min. 44\, Kanouté\n0 - 3\, min. 89\, Maresca\, penalti\n\nELIMINAD + O +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T103707Z +UID:33AA1688-2062-4D11-B881-BCF5F2CCDB2F-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061015T170000 +SUMMARY:Barça - Sevilla +DESCRIPTION:Liga\nJornada 6\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Coliseum Alfonso Pérez\, Getafe\, Madrid +DTSTAMP:20060731T112004Z +UID:1252CF7F-FBBC-4BA4-A1AC-F7660B22DC9E-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070107T170000 +SUMMARY:Getafe - Barça +DESCRIPTION:Liga\nJornada 17\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Manuel Ruiz de Lopera\, Sevilla +DTSTAMP:20060731T111649Z +UID:3C4706A1-337B-4441-8E82-BF5C8230A232-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061217T170000 +SUMMARY:Betis - Barça +DESCRIPTION:Liga\nJornada 15\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T115943Z +UID:4E9A904F-5D90-45F4-A66D-9F8979F0AE8C-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:6 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070225T170000 +SUMMARY:Barça - Athletic Bilbao +DESCRIPTION:Liga\nJornada 24\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T111223Z +UID:7BD61A24-E706-4125-9919-CCC1EE057DF9-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070429T170000 +SUMMARY:Barça - Levante +DESCRIPTION:Liga\nJornada 32\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T113250Z +UID:AE59DE18-C03F-41D2-83F4-863268C5775C-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070211T170000 +SUMMARY:Barça - Racing +DESCRIPTION:Liga\nJornada 22\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060821T123620Z +UID:6A0C61A2-751B-40F6-A8C3-536A06D8DEBB-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:12 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060820T220000 +SUMMARY:Barça 3 - 0 Espanyol +DESCRIPTION:Supercopa de España\nVuelta\n\nGoles:\n1 - 0\, min. 2\, Xavi + \n2 - 0\, min. 12\, Deco\n3 - 0\, min. 61\, Deco\n\nCAMPEÓN +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:La Romareda\, Zaragoza +DTSTAMP:20060731T114411Z +UID:370C7639-572F-47CE-9D4B-06CDE1A92691-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070408T170000 +SUMMARY:Zaragoza - Barça +DESCRIPTION:Liga\nJornada 29\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Riazor\, A Coruña +DTSTAMP:20060731T104429Z +UID:77B7BFE2-3B9C-4AC0-B9A8-6631EEEFC3A8-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061105T170000 +SUMMARY:Deportivo - Barça +DESCRIPTION:Liga\nJornada 9\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +LOCATION:Giants Stadium\, New York\, EEUU +DTSTAMP:20060814T143022Z +UID:121922D9-2D9A-49BF-934C-905D3A63208D-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:12 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060813T013000 +SUMMARY:New York Red Bulls 1 - 4 Barça +DTEND;TZID=Europe/Madrid:20060813T031500 +DESCRIPTION:Amistoso\n\nGoles\n0 - 1\, min. 12\, Ronaldinho\, penalti\n1 + - 1\, min. 38\, Djorkaeff\n1 - 2\, min. 50\, Ronaldinho\n1 - 3\, min. 6 + 0\, Messi\n1 - 4\, min. 86\, Saviola +END:VEVENT +BEGIN:VEVENT +LOCATION:Reliant Stadium\, Houston\, EEUU +DTSTAMP:20060810T123757Z +UID:29EE7C39-1A93-46B4-A37D-4A990944E5B1-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:12 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060810T043000 +SUMMARY:Club América 4 - 4 Barça +DTEND;TZID=Europe/Madrid:20060810T061500 +DESCRIPTION:Amistoso\n\nGoles\n1 - 0\, min. 3\, Cuevas\n1 - 1\, min. 5\, + Márquez\n2 - 1\, min. 30\, Cuevas\n3 - 1\, min. 44\, Cuevas\n4 - 1\, mi + n. 64\, Oleguer\, propia puerta\n4 - 2\, min. 84\, Saviola\n4 - 3\, min. + 88\, Ronaldinho\n4 - 4\, min. 91\, Eto'o +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T104241Z +UID:20FDF59A-8A7D-4612-809C-1B1A0FE9D103-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070128T170000 +SUMMARY:Barça - Celta +DESCRIPTION:Liga\nJornada 20 +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Anoeta\, Donostia/San Sebastián +DTSTAMP:20060731T114803Z +UID:4BD48B6D-9349-4866-869B-A270CBA40343-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070506T170000 +SUMMARY:R. Sociedad - Barça +DESCRIPTION:Liga\nJornada 33\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +LOCATION:Memorial Coliseum\, Los Angeles\, EEUU +DTSTAMP:20060810T123503Z +UID:344BC4B1-35A6-4ED0-B16A-B2480E4CF1A4-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:15 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060807T033000 +SUMMARY:Chivas Guadalajara 1 - 1 Barça +DTEND;TZID=Europe/Madrid:20060807T051500 +DESCRIPTION:Amistoso\n\nGoles\n\n0 - 1\, min. 74\, Gudjohnsen\n1 - 1\, m + in. 83\, Diego Martínez +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T112716Z +UID:6D1E20B3-7055-43C6-AB56-3BF2AD352846-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070121T170000 +SUMMARY:Barça - Gimnàstic +DESCRIPTION:Liga\nJornada 19\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060830T082916Z +UID:2959B82E-E2A6-4E3B-BD88-3961B6F1D769-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061031T204500 +SUMMARY:Barça - Chelsea +DESCRIPTION:Champions League\nFase de Grupos\, Jornada 4\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Vicente Calderón\, Madrid +DTSTAMP:20060731T115142Z +UID:A8B687C7-A514-4629-BF7E-091380298009-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070520T170000 +SUMMARY:At. Madrid - Barça +DESCRIPTION:Liga\nJornada 35\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Weserstadion\, Bremen\, Alemania +DTSTAMP:20060830T082257Z +UID:E0C2F235-ABC1-4AE4-B3CB-C8571E07D109-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060927T204500 +SUMMARY:Werder Bremen - Barça +DESCRIPTION:Champions League\nFase de Grupos\, Jornada 2\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Son Moix\, Mallorca +DTSTAMP:20060731T110730Z +UID:63FBA7FF-0722-4856-837C-6BF378BE0A82-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061119T170000 +SUMMARY:Mallorca - Barça +DESCRIPTION:Liga\nJornada 11\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Stamford Bridge\, Londres\, Reino Unido +DTSTAMP:20060830T082625Z +UID:1777356F-B859-41E3-A5A9-958C28CC702F-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061018T204500 +SUMMARY:Chelsea - Barça +DESCRIPTION:Champions League\nFase de Grupos\, Jornada 3\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T115508Z +UID:1072D399-62FB-40E2-827A-D564E19A3F59-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070610T170000 +SUMMARY:Barça - Espanyol +DESCRIPTION:Liga\nJornada 37\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T102122Z +UID:99D890CC-6351-4227-ACDC-84A16EB6C079-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060910T170000 +SUMMARY:Barça - Osasuna +DESCRIPTION:Liga\nJornada 2\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T120210Z +UID:DFE72F1F-FEA1-4271-975E-32138E649A7D-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070527T170000 +SUMMARY:Barça - Getafe +DESCRIPTION:Liga\nJornada 36\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T111056Z +UID:23AF28FC-23CA-4BD5-945D-BC63703605D3-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:6 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061126T170000 +SUMMARY:Barça - Villarreal +DESCRIPTION:Liga\nJornada 12\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Mestalla\, Valencia +DTSTAMP:20060731T113410Z +UID:53B4F561-5674-482A-AC22-31AC8A729601-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070218T170000 +SUMMARY:Valencia - Barça +DESCRIPTION:Liga\nJornada 23\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Lluís Companys\, Barcelona +DTSTAMP:20060731T112125Z +UID:F14287BC-9A6B-441B-9A27-1B11907A1CCF-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070114T170000 +SUMMARY:Espanyol - Barça +DESCRIPTION:Liga\nJornada 18\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T114522Z +UID:291F59CC-4690-4E5A-995A-12A60B9A27D5-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070415T170000 +SUMMARY:Barça - Mallorca +DESCRIPTION:Liga\nJornada 30\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060830T082230Z +UID:9BCB074A-9183-4CE8-B11E-7B38A12EDDE8-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:11 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060912T204500 +SUMMARY:Barça - Levski Sofia +DTEND;TZID=Europe/Madrid:20060912T223000 +DESCRIPTION:Champions League\nFase de Grupos\, Jornada 1\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:El Madrigal\, Villarreal\, Castellón +DTSTAMP:20060731T114629Z +UID:05D4293D-78E3-412A-9C26-410ED31A1014-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070422T170000 +SUMMARY:Villarreal - Barça +DESCRIPTION:Liga\nJornada 31\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +LOCATION:Idraettens Hus Vest\, Stadion Allé 70\, 8000 Ärhus C\, Dinamarc + a\n +DTSTAMP:20060731T091249Z +UID:9D5D90B4-06CA-4780-B522-35E6A0BA1DA6-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:17 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060728T220000 +SUMMARY:AGF Aarthus 0 - 3 Barça\n +DTEND;TZID=Europe/Madrid:20060728T234500 +DESCRIPTION:Amistoso\n\nGoles:\n0 - 1\, min. 18\, Giovanni\n0 - 2\, min. + 38\, Ezquerro\n0 - 3\, min. 60\, Schultz propia puerta +END:VEVENT +BEGIN:VEVENT +LOCATION:Estadio Universitario de Nuevo León\, Monterrey\, México +DTSTAMP:20060810T123515Z +UID:3B66ED8E-76BF-471B-B96E-0CDAE837BC78-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:12 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060804T030000 +SUMMARY:Club Tigres 0 - 3 Barça +DTEND;TZID=Europe/Madrid:20060804T044500 +DESCRIPTION:Amistoso\n\nGoles\n0 - 1\, min. 7\, Balderas\, propia puerta + \n0 - 2\, min. 70\, Ronaldinho\n0 - 3\, min. 77\, Gudjohnsen +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Lluís Companys\, Barcelona +DTSTAMP:20060818T233028Z +UID:C7051BE7-AE26-466E-85A4-86FE38E93560-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:9 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060817T220000 +SUMMARY:Espanyol 0 - 1 Barça +DESCRIPTION:Supercopa de España\nIda\n\nGoles:\n0 - 1\, min. 43\, Giuly +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Nou Estadi\, Tarragona +DTSTAMP:20060731T113026Z +UID:CB729FCF-6F6E-4BD7-A9B3-3A0744E44E4B-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070617T170000 +SUMMARY:Gimnàstic - Barça +DESCRIPTION:Liga\nJornada 38\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T104503Z +UID:AE9EF976-60FC-4F26-89D9-7BC8B881C118-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061029T170000 +SUMMARY:Barça - Recreativo de Huelva +DESCRIPTION:Liga\nJornada 8\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T111824Z +UID:EB10A7E1-ECED-4A24-97C4-3F76B5B07DE5-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061220T170000 +SUMMARY:Barça - At. Madrid +DESCRIPTION:Liga\nJornada 16\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Ciutat de Valencia\, Valencia +DTSTAMP:20060731T111050Z +UID:DAD6FF1B-CFD9-48B8-AEB8-868F60C3EAF7-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061203T170000 +SUMMARY:Levante - Barça +DESCRIPTION:Liga\nJornada 13\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060830T084427Z +UID:C4758AAE-6478-48BE-8D43-38CAB85F4677-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061205T204500 +SUMMARY:Barça - Werder Bremen +DESCRIPTION:Champions League\nFase de Grupos Jornada 6\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:El Sadar\, Pamplona\, Navarra +DTSTAMP:20060731T113136Z +UID:8D73104F-5050-4996-8787-96C801AD8801-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070204T170000 +SUMMARY:Osasuna - Barça +DESCRIPTION:Liga\nJornada 21\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Nuevo Colombino\, Huelva +DTSTAMP:20060731T104053Z +UID:E53E51B9-3B2C-4CEE-8CE3-026C5AD5354F-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070318T170000 +SUMMARY:Recreativo de Huelva - Barça +DESCRIPTION:Liga\nJornada 27\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +LOCATION:Balaídos\, Vigo +DTSTAMP:20060830T081242Z +UID:956B1612-8052-4036-B5C0-B5733C67AF8A-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:11 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060827T170000 +SUMMARY:Celta 2 - 3 Barça +DTEND;TZID=Europe/Madrid:20060827T184500 +DESCRIPTION:Liga\nJornada 1\n\nGoles\n1 - 0\, min. 41\, Baiano\n1 - 1\, + min. 55\, Eto'o\n1 - 2\, min. 59\, Messi\n2 - 2\, min. 64\, López\n2 - 3 + \, min. 87\, Gudjohnsen +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T114238Z +UID:2B848DBE-06A7-4751-A912-712083A2064E-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070401T170000 +SUMMARY:Barça - Deportivo +DESCRIPTION:Liga\nJornada 28\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Santiago Bernabeu\, Madrid +DTSTAMP:20060731T103743Z +UID:BB8FCD09-2EF1-4DAD-85E8-B8C37AE1BFB7-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061022T170000 +SUMMARY:R. Madrid - Barça +DESCRIPTION:Liga\nJornada 7\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Ramón Sánchez Pizjuán\, Sevilla +DTSTAMP:20060731T113711Z +UID:6479E9BD-3C96-4862-80EF-72DBFD1035EA-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070304T170000 +SUMMARY:Sevilla - Barça +DESCRIPTION:Liga\nJornada 25\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T120138Z +UID:A5E15535-3A62-4745-A225-3FCEB617C636-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070513T170000 +SUMMARY:Barça - Betis +DESCRIPTION:Liga\nJornada 34\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T114149Z +UID:B683A522-7513-4D33-AD1A-CAB1EAECC0D6-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070311T170000 +SUMMARY:Barça - R. Madrid +DESCRIPTION:Liga\nJornada 26\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:El Sardinero\, Santander +DTSTAMP:20060731T102312Z +UID:28E93356-68EE-4CFA-8B39-9DA7BF619CF7-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060917T170000 +SUMMARY:Racing - Barça +DESCRIPTION:Liga\nJornada 3\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T111405Z +UID:705144C2-5757-4856-807B-4F7195CA3B15-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061210T170000 +SUMMARY:Barça - Real Sociedad +DESCRIPTION:Liga\nJornada 14\n\nGoles:\nN/A +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language2.ics new file mode 100644 index 00000000..2f083765 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language2.ics @@ -0,0 +1,194 @@ +BEGIN:VCALENDAR +VERSION + :2.0 +PRODID + :-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +BEGIN:VTODO +UID + :1c903d42-fd22-11da-9097-d8074ea454d0 +SUMMARY + :Städa garaget +DESCRIPTION + :NOGA!!! +LOCATION + :Garaget +PRIORITY + :5 +STATUS + :IN-PROCESS +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :30 +X-MOZILLA-LASTALARMACK + :20060616T135845 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +DTSTART + :20060616T130000 +DUE + :20060616T135900 +DTSTAMP + :20060616T102229Z +LAST-MODIFIED + :20060616T115845Z +PERCENT-COMPLETE + :50 +BEGIN:VALARM +TRIGGER + ;VALUE=DURATION + :-PT30M +END:VALARM +END:VTODO +BEGIN:VEVENT +UID + :27dedb92-69a4-11db-8fae-b9231e60cc13 +SUMMARY + :God Jul +DESCRIPTION + :Tomten kommer! +STATUS + :TENTATIVE +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +DTSTART + :20061224T000000 +DTEND + :20061224T230000 +DTSTAMP + :20061101T122553Z +LAST-MODIFIED + :20061101T122737Z +END:VEVENT +BEGIN:VEVENT +UID + :403f9454-6fc8-11db-b5e3-c1ad1b64708e +SUMMARY + :Möte kontoret +DESCRIPTION + :Skall träffa liselotte och diskutera vår ekonomi. Och se över våra + överenskommelser.\n +LOCATION + :Kontoret +STATUS + :TENTATIVE +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +DTSTART + ;VALUE=DATE + :20061031 +DTEND + ;VALUE=DATE + :20061101 +DTSTAMP + :20061109T075916Z +LAST-MODIFIED + :20061110T073618Z +END:VEVENT +BEGIN:VEVENT +UID + :eff4c625-6902-11db-b2df-fecd9e34a9d8 +SUMMARY + :Swedish chars and letters +DESCRIPTION + :This is Swedish chars: åäö and ÅÄÖ +LOCATION + :Kontoret +STATUS + :TENTATIVE +CLASS + :PRIVATE +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +DTSTART + :20061101T090000 +DTEND + :20061101T100000 +DTSTAMP + :20061031T171124Z +LAST-MODIFIED + :20061110T073743Z +END:VEVENT +BEGIN:VEVENT +UID + :02d7e71d-6903-11db-bb22-d4dd07f6c8b9 +SUMMARY + :Sova +DESCRIPTION + :God natt +LOCATION + :Sängen +STATUS + :TENTATIVE +CLASS + :PUBLIC +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +DTSTART + :20061101T100000 +DTEND + :20061101T110000 +DTSTAMP + :20061031T171210Z +LAST-MODIFIED + :20061110T073800Z +END:VEVENT +BEGIN:VEVENT +UID + :5083e6eb-6954-11db-aa33-e45ec9209be7 +SUMMARY + :Nu måste jag gå hem +DESCRIPTION + :Hej +LOCATION + :sova +STATUS + :TENTATIVE +CLASS + :PUBLIC +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +X-MOZILLA-RECUR-DEFAULT-INTERVAL + :0 +DTSTART + :20061109T230000 +DTEND + :20061111T000000 +DTSTAMP + :20061101T025415Z +LAST-MODIFIED + :20061110T074001Z +END:VEVENT +BEGIN:VEVENT +UID + :a4df60b2-708e-11db-8d7b-f7ec1dceb054 +SUMMARY + :Mamma fyller år +DESCRIPTION + :Kom ihåg hennes födelsedag! +LOCATION + :Västerås +STATUS + :TENTATIVE +CLASS + :PUBLIC +X-MOZILLA-ALARM-DEFAULT-LENGTH + :0 +DTSTART + ;VALUE=DATE + :20061109 +DTEND + ;VALUE=DATE + :20061110 +DTSTAMP + :20061110T073918Z +LAST-MODIFIED + :20061110T074017Z +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language3.ics new file mode 100644 index 00000000..b1a2f04b --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language3.ics @@ -0,0 +1,111 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VEVENT +CREATED:20070606T164109Z +LAST-MODIFIED:20100101T201701Z +DTSTAMP:20070606T164109Z +UID:8147ef1b-8301-4ea8-b712-90a394f647a3 +SUMMARY:Новый год +RRULE:FREQ=YEARLY;INTERVAL=1 +DTSTART;VALUE=DATE:20100101 +DTEND;VALUE=DATE:20100102 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:6 +END:VEVENT +BEGIN:VEVENT +CREATED:20100101T200818Z +LAST-MODIFIED:20100101T201704Z +DTSTAMP:20100101T200818Z +UID:b3408b45-b00a-4e23-b40a-57760a2e6bd1 +SUMMARY:Новогодние каникулы +RRULE:FREQ=YEARLY;INTERVAL=1 +DTSTART;VALUE=DATE:20100102 +DTEND;VALUE=DATE:20100106 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:6 +END:VEVENT +BEGIN:VEVENT +CREATED:20070606T164109Z +LAST-MODIFIED:20100101T201707Z +DTSTAMP:20070606T164109Z +UID:49660752-db14-42f7-bfa4-bd264111e513 +SUMMARY:Рождество Христово +RRULE:FREQ=YEARLY;INTERVAL=1 +DTSTART;VALUE=DATE:20100107 +DTEND;VALUE=DATE:20100108 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:4 +END:VEVENT +BEGIN:VEVENT +CREATED:20070606T164109Z +LAST-MODIFIED:20100101T201710Z +DTSTAMP:20070606T164109Z +UID:14cd5330-c5d4-4580-a2d5-05986920c48e +SUMMARY:День защитника Отечества +RRULE:FREQ=YEARLY;INTERVAL=1 +DTSTART;VALUE=DATE:20100223 +DTEND;VALUE=DATE:20100224 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:4 +END:VEVENT +BEGIN:VEVENT +CREATED:20070606T164109Z +LAST-MODIFIED:20100101T201712Z +DTSTAMP:20070606T164109Z +UID:4c8df85c-86ac-46cf-b271-877c02166e9e +SUMMARY:Международный женский день +RRULE:FREQ=YEARLY;INTERVAL=1 +DTSTART;VALUE=DATE:20100308 +DTEND;VALUE=DATE:20100309 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:4 +END:VEVENT +BEGIN:VEVENT +CREATED:20070606T164109Z +LAST-MODIFIED:20100101T201715Z +DTSTAMP:20070606T164109Z +UID:71c1345a-04fe-480f-a134-f5b9bed0f807 +SUMMARY:Праздник весны и труда +RRULE:FREQ=YEARLY;INTERVAL=1 +DTSTART;VALUE=DATE:20100501 +DTEND;VALUE=DATE:20100502 +X-MOZ-GENERATION:5 +END:VEVENT +BEGIN:VEVENT +CREATED:20070606T164109Z +LAST-MODIFIED:20100101T201717Z +DTSTAMP:20070606T164109Z +UID:593ebbe1-cce3-4b01-9979-3578b3b3b7ec +SUMMARY:День Победы +RRULE:FREQ=YEARLY;INTERVAL=1 +DTSTART;VALUE=DATE:20100509 +DTEND;VALUE=DATE:20100510 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:4 +END:VEVENT +BEGIN:VEVENT +CREATED:20070606T164109Z +LAST-MODIFIED:20100101T201719Z +DTSTAMP:20070606T164109Z +UID:fc0d48b2-41d8-4993-83eb-6e9fdbd4741a +SUMMARY:День России +RRULE:FREQ=YEARLY;INTERVAL=1 +DTSTART;VALUE=DATE:20100612 +DTEND;VALUE=DATE:20100613 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:4 +END:VEVENT +BEGIN:VEVENT +CREATED:20070606T164109Z +LAST-MODIFIED:20100101T201722Z +DTSTAMP:20070606T164109Z +UID:8fb6d2cc-728c-4e99-99fc-83757d05f359 +SUMMARY:День народного единства +RRULE:FREQ=YEARLY;INTERVAL=1 +DTSTART;VALUE=DATE:20101104 +DTEND;VALUE=DATE:20101105 +TRANSP:TRANSPARENT +X-MOZ-GENERATION:5 +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language4.ics new file mode 100644 index 00000000..048a1903 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Language4.ics @@ -0,0 +1,695 @@ +BEGIN:VCALENDAR +VERSION:2.0 +X-WR-CALNAME:Barça 2006 - 2007 +PRODID:-//Apple Computer\, Inc//iCal 2.0//EN +X-WR-RELCALID:2EBACFB1-8102-4CEF-BB39-024F9F179B0A +X-WR-TIMEZONE:Europe/Madrid +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALDESC:Todos los partidos del Barça desde princios de la temporada + 2006 – 2007.\n\nEl Barça\, esta temporada está jugando o ha jugado:\n\n + - Supercopa de España (campeón)\n- Supercopa de Europa (eliminado)\n- Tr + ofeo Joan Gamper (campeón)\n- Liga (en juego)\n- Champions League\n- Cop + a del Rey\n- Copa de Catalunya\n\n======================\n\nAll the game + s of the 2006-2007 season.\n\n +BEGIN:VTIMEZONE +TZID:Europe/Madrid +LAST-MODIFIED:20060831T085543Z +BEGIN:DAYLIGHT +DTSTART:20060326T010000 +TZOFFSETTO:+0200 +TZOFFSETFROM:+0000 +TZNAME:CEST +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:20061029T030000 +TZOFFSETTO:+0100 +TZOFFSETFROM:+0200 +TZNAME:CET +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:20070325T030000 +TZOFFSETTO:+0200 +TZOFFSETFROM:+0100 +TZNAME:CEST +END:DAYLIGHT +BEGIN:STANDARD +DTSTART:20071028T030000 +TZOFFSETTO:+0100 +TZOFFSETFROM:+0200 +TZNAME:CET +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:San Mamés\, Bilbao +DTSTAMP:20060731T103716Z +UID:97199ED3-78FF-43FD-A77D-A524A668853F-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061001T170000 +SUMMARY:Athletic Bilbao - Barça +DESCRIPTION:Liga\nJornada 5\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060825T123839Z +UID:B3284440-9E23-4DEF-89BE-94E8740BD2F3-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060822T220000 +SUMMARY:Barça 4 - 0 Bayern Munich +DESCRIPTION:Trofeu Joan Gamper\n\nGoles:\n1 - 0\, min. 29\, Ronaldinho\n + 2 - 0\, min. 32\, Eto'o\n3 - 0\, min. 39\, Eto'o\n4 - 0\, min. 50\, Savi + ola +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T105715Z +UID:2888A37B-9ED4-411D-ADD4-D73B67825FEE-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061112T170000 +SUMMARY:Barça - Zaragoza +DESCRIPTION:Liga\nJornada 10\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T102726Z +UID:44725A49-4A14-4FE7-AFC1-6993D7417BD0-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:1 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060924T170000 +SUMMARY:Barça - Valencia +DESCRIPTION:Liga\nJornada 4\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H +LOCATION:Georgi Asparoukhov\, Sofia\, Bulgaria +DTSTAMP:20060830T084132Z +UID:3B4D5094-F9CE-4640-BFFC-52C06B7542F2-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:7 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061122T110000 +SUMMARY:Levski Sofia - Barça +DESCRIPTION:Champions League\nFase de Grupos\, Jornada 5\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Stade Louis II\, Mónaco +DTSTAMP:20060830T081312Z +UID:50F41DD6-0288-47D0-B1AA-43F8865CB641-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:9 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060825T204500 +SUMMARY:Barça 0 - 3 Sevilla +DESCRIPTION:Supercopa de Europa\n\n\nGoles:\n0 - 1\, min. 7\, Renato\n0 + - 2\, min. 44\, Kanouté\n0 - 3\, min. 89\, Maresca\, penalti\n\nELIMINAD + O +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T103707Z +UID:33AA1688-2062-4D11-B881-BCF5F2CCDB2F-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061015T170000 +SUMMARY:Barça - Sevilla +DESCRIPTION:Liga\nJornada 6\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Coliseum Alfonso Pérez\, Getafe\, Madrid +DTSTAMP:20060731T112004Z +UID:1252CF7F-FBBC-4BA4-A1AC-F7660B22DC9E-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070107T170000 +SUMMARY:Getafe - Barça +DESCRIPTION:Liga\nJornada 17\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Manuel Ruiz de Lopera\, Sevilla +DTSTAMP:20060731T111649Z +UID:3C4706A1-337B-4441-8E82-BF5C8230A232-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061217T170000 +SUMMARY:Betis - Barça +DESCRIPTION:Liga\nJornada 15\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T115943Z +UID:4E9A904F-5D90-45F4-A66D-9F8979F0AE8C-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:6 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070225T170000 +SUMMARY:Barça - Athletic Bilbao +DESCRIPTION:Liga\nJornada 24\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T111223Z +UID:7BD61A24-E706-4125-9919-CCC1EE057DF9-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070429T170000 +SUMMARY:Barça - Levante +DESCRIPTION:Liga\nJornada 32\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T113250Z +UID:AE59DE18-C03F-41D2-83F4-863268C5775C-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070211T170000 +SUMMARY:Barça - Racing +DESCRIPTION:Liga\nJornada 22\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060821T123620Z +UID:6A0C61A2-751B-40F6-A8C3-536A06D8DEBB-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:12 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060820T220000 +SUMMARY:Barça 3 - 0 Espanyol +DESCRIPTION:Supercopa de España\nVuelta\n\nGoles:\n1 - 0\, min. 2\, Xavi + \n2 - 0\, min. 12\, Deco\n3 - 0\, min. 61\, Deco\n\nCAMPEÓN +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:La Romareda\, Zaragoza +DTSTAMP:20060731T114411Z +UID:370C7639-572F-47CE-9D4B-06CDE1A92691-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070408T170000 +SUMMARY:Zaragoza - Barça +DESCRIPTION:Liga\nJornada 29\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Riazor\, A Coruña +DTSTAMP:20060731T104429Z +UID:77B7BFE2-3B9C-4AC0-B9A8-6631EEEFC3A8-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061105T170000 +SUMMARY:Deportivo - Barça +DESCRIPTION:Liga\nJornada 9\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +LOCATION:Giants Stadium\, New York\, EEUU +DTSTAMP:20060814T143022Z +UID:121922D9-2D9A-49BF-934C-905D3A63208D-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:12 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060813T013000 +SUMMARY:New York Red Bulls 1 - 4 Barça +DTEND;TZID=Europe/Madrid:20060813T031500 +DESCRIPTION:Amistoso\n\nGoles\n0 - 1\, min. 12\, Ronaldinho\, penalti\n1 + - 1\, min. 38\, Djorkaeff\n1 - 2\, min. 50\, Ronaldinho\n1 - 3\, min. 6 + 0\, Messi\n1 - 4\, min. 86\, Saviola +END:VEVENT +BEGIN:VEVENT +LOCATION:Reliant Stadium\, Houston\, EEUU +DTSTAMP:20060810T123757Z +UID:29EE7C39-1A93-46B4-A37D-4A990944E5B1-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:12 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060810T043000 +SUMMARY:Club América 4 - 4 Barça +DTEND;TZID=Europe/Madrid:20060810T061500 +DESCRIPTION:Amistoso\n\nGoles\n1 - 0\, min. 3\, Cuevas\n1 - 1\, min. 5\, + Márquez\n2 - 1\, min. 30\, Cuevas\n3 - 1\, min. 44\, Cuevas\n4 - 1\, mi + n. 64\, Oleguer\, propia puerta\n4 - 2\, min. 84\, Saviola\n4 - 3\, min. + 88\, Ronaldinho\n4 - 4\, min. 91\, Eto'o +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T104241Z +UID:20FDF59A-8A7D-4612-809C-1B1A0FE9D103-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070128T170000 +SUMMARY:Barça - Celta +DESCRIPTION:Liga\nJornada 20 +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Anoeta\, Donostia/San Sebastián +DTSTAMP:20060731T114803Z +UID:4BD48B6D-9349-4866-869B-A270CBA40343-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070506T170000 +SUMMARY:R. Sociedad - Barça +DESCRIPTION:Liga\nJornada 33\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +LOCATION:Memorial Coliseum\, Los Angeles\, EEUU +DTSTAMP:20060810T123503Z +UID:344BC4B1-35A6-4ED0-B16A-B2480E4CF1A4-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:15 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060807T033000 +SUMMARY:Chivas Guadalajara 1 - 1 Barça +DTEND;TZID=Europe/Madrid:20060807T051500 +DESCRIPTION:Amistoso\n\nGoles\n\n0 - 1\, min. 74\, Gudjohnsen\n1 - 1\, m + in. 83\, Diego Martínez +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T112716Z +UID:6D1E20B3-7055-43C6-AB56-3BF2AD352846-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070121T170000 +SUMMARY:Barça - Gimnàstic +DESCRIPTION:Liga\nJornada 19\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060830T082916Z +UID:2959B82E-E2A6-4E3B-BD88-3961B6F1D769-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061031T204500 +SUMMARY:Barça - Chelsea +DESCRIPTION:Champions League\nFase de Grupos\, Jornada 4\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Vicente Calderón\, Madrid +DTSTAMP:20060731T115142Z +UID:A8B687C7-A514-4629-BF7E-091380298009-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070520T170000 +SUMMARY:At. Madrid - Barça +DESCRIPTION:Liga\nJornada 35\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Weserstadion\, Bremen\, Alemania +DTSTAMP:20060830T082257Z +UID:E0C2F235-ABC1-4AE4-B3CB-C8571E07D109-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060927T204500 +SUMMARY:Werder Bremen - Barça +DESCRIPTION:Champions League\nFase de Grupos\, Jornada 2\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Son Moix\, Mallorca +DTSTAMP:20060731T110730Z +UID:63FBA7FF-0722-4856-837C-6BF378BE0A82-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061119T170000 +SUMMARY:Mallorca - Barça +DESCRIPTION:Liga\nJornada 11\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Stamford Bridge\, Londres\, Reino Unido +DTSTAMP:20060830T082625Z +UID:1777356F-B859-41E3-A5A9-958C28CC702F-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061018T204500 +SUMMARY:Chelsea - Barça +DESCRIPTION:Champions League\nFase de Grupos\, Jornada 3\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T115508Z +UID:1072D399-62FB-40E2-827A-D564E19A3F59-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070610T170000 +SUMMARY:Barça - Espanyol +DESCRIPTION:Liga\nJornada 37\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T102122Z +UID:99D890CC-6351-4227-ACDC-84A16EB6C079-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060910T170000 +SUMMARY:Barça - Osasuna +DESCRIPTION:Liga\nJornada 2\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T120210Z +UID:DFE72F1F-FEA1-4271-975E-32138E649A7D-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070527T170000 +SUMMARY:Barça - Getafe +DESCRIPTION:Liga\nJornada 36\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T111056Z +UID:23AF28FC-23CA-4BD5-945D-BC63703605D3-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:6 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061126T170000 +SUMMARY:Barça - Villarreal +DESCRIPTION:Liga\nJornada 12\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Mestalla\, Valencia +DTSTAMP:20060731T113410Z +UID:53B4F561-5674-482A-AC22-31AC8A729601-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070218T170000 +SUMMARY:Valencia - Barça +DESCRIPTION:Liga\nJornada 23\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Lluís Companys\, Barcelona +DTSTAMP:20060731T112125Z +UID:F14287BC-9A6B-441B-9A27-1B11907A1CCF-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070114T170000 +SUMMARY:Espanyol - Barça +DESCRIPTION:Liga\nJornada 18\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T114522Z +UID:291F59CC-4690-4E5A-995A-12A60B9A27D5-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070415T170000 +SUMMARY:Barça - Mallorca +DESCRIPTION:Liga\nJornada 30\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060830T082230Z +UID:9BCB074A-9183-4CE8-B11E-7B38A12EDDE8-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:11 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060912T204500 +SUMMARY:Barça - Levski Sofia +DTEND;TZID=Europe/Madrid:20060912T223000 +DESCRIPTION:Champions League\nFase de Grupos\, Jornada 1\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:El Madrigal\, Villarreal\, Castellón +DTSTAMP:20060731T114629Z +UID:05D4293D-78E3-412A-9C26-410ED31A1014-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070422T170000 +SUMMARY:Villarreal - Barça +DESCRIPTION:Liga\nJornada 31\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +LOCATION:Idraettens Hus Vest\, Stadion Allé 70\, 8000 Ärhus C\, Dinamarc + a\n +DTSTAMP:20060731T091249Z +UID:9D5D90B4-06CA-4780-B522-35E6A0BA1DA6-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:17 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060728T220000 +SUMMARY:AGF Aarthus 0 - 3 Barça\n +DTEND;TZID=Europe/Madrid:20060728T234500 +DESCRIPTION:Amistoso\n\nGoles:\n0 - 1\, min. 18\, Giovanni\n0 - 2\, min. + 38\, Ezquerro\n0 - 3\, min. 60\, Schultz propia puerta +END:VEVENT +BEGIN:VEVENT +LOCATION:Estadio Universitario de Nuevo León\, Monterrey\, México +DTSTAMP:20060810T123515Z +UID:3B66ED8E-76BF-471B-B96E-0CDAE837BC78-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:12 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060804T030000 +SUMMARY:Club Tigres 0 - 3 Barça +DTEND;TZID=Europe/Madrid:20060804T044500 +DESCRIPTION:Amistoso\n\nGoles\n0 - 1\, min. 7\, Balderas\, propia puerta + \n0 - 2\, min. 70\, Ronaldinho\n0 - 3\, min. 77\, Gudjohnsen +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Lluís Companys\, Barcelona +DTSTAMP:20060818T233028Z +UID:C7051BE7-AE26-466E-85A4-86FE38E93560-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:9 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060817T220000 +SUMMARY:Espanyol 0 - 1 Barça +DESCRIPTION:Supercopa de España\nIda\n\nGoles:\n0 - 1\, min. 43\, Giuly +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Nou Estadi\, Tarragona +DTSTAMP:20060731T113026Z +UID:CB729FCF-6F6E-4BD7-A9B3-3A0744E44E4B-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070617T170000 +SUMMARY:Gimnàstic - Barça +DESCRIPTION:Liga\nJornada 38\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T104503Z +UID:AE9EF976-60FC-4F26-89D9-7BC8B881C118-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061029T170000 +SUMMARY:Barça - Recreativo de Huelva +DESCRIPTION:Liga\nJornada 8\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T111824Z +UID:EB10A7E1-ECED-4A24-97C4-3F76B5B07DE5-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061220T170000 +SUMMARY:Barça - At. Madrid +DESCRIPTION:Liga\nJornada 16\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Ciutat de Valencia\, Valencia +DTSTAMP:20060731T111050Z +UID:DAD6FF1B-CFD9-48B8-AEB8-868F60C3EAF7-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061203T170000 +SUMMARY:Levante - Barça +DESCRIPTION:Liga\nJornada 13\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060830T084427Z +UID:C4758AAE-6478-48BE-8D43-38CAB85F4677-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:3 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061205T204500 +SUMMARY:Barça - Werder Bremen +DESCRIPTION:Champions League\nFase de Grupos Jornada 6\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:El Sadar\, Pamplona\, Navarra +DTSTAMP:20060731T113136Z +UID:8D73104F-5050-4996-8787-96C801AD8801-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070204T170000 +SUMMARY:Osasuna - Barça +DESCRIPTION:Liga\nJornada 21\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Nuevo Colombino\, Huelva +DTSTAMP:20060731T104053Z +UID:E53E51B9-3B2C-4CEE-8CE3-026C5AD5354F-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070318T170000 +SUMMARY:Recreativo de Huelva - Barça +DESCRIPTION:Liga\nJornada 27\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +LOCATION:Balaídos\, Vigo +DTSTAMP:20060830T081242Z +UID:956B1612-8052-4036-B5C0-B5733C67AF8A-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:11 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060827T170000 +SUMMARY:Celta 2 - 3 Barça +DTEND;TZID=Europe/Madrid:20060827T184500 +DESCRIPTION:Liga\nJornada 1\n\nGoles\n1 - 0\, min. 41\, Baiano\n1 - 1\, + min. 55\, Eto'o\n1 - 2\, min. 59\, Messi\n2 - 2\, min. 64\, López\n2 - 3 + \, min. 87\, Gudjohnsen +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T114238Z +UID:2B848DBE-06A7-4751-A912-712083A2064E-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070401T170000 +SUMMARY:Barça - Deportivo +DESCRIPTION:Liga\nJornada 28\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Santiago Bernabeu\, Madrid +DTSTAMP:20060731T103743Z +UID:BB8FCD09-2EF1-4DAD-85E8-B8C37AE1BFB7-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061022T170000 +SUMMARY:R. Madrid - Barça +DESCRIPTION:Liga\nJornada 7\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Ramón Sánchez Pizjuán\, Sevilla +DTSTAMP:20060731T113711Z +UID:6479E9BD-3C96-4862-80EF-72DBFD1035EA-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070304T170000 +SUMMARY:Sevilla - Barça +DESCRIPTION:Liga\nJornada 25\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T120138Z +UID:A5E15535-3A62-4745-A225-3FCEB617C636-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070513T170000 +SUMMARY:Barça - Betis +DESCRIPTION:Liga\nJornada 34\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T114149Z +UID:B683A522-7513-4D33-AD1A-CAB1EAECC0D6-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:5 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20070311T170000 +SUMMARY:Barça - R. Madrid +DESCRIPTION:Liga\nJornada 26\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:El Sardinero\, Santander +DTSTAMP:20060731T102312Z +UID:28E93356-68EE-4CFA-8B39-9DA7BF619CF7-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20060917T170000 +SUMMARY:Racing - Barça +DESCRIPTION:Liga\nJornada 3\n\nGoles:\nN/A +END:VEVENT +BEGIN:VEVENT +DURATION:PT1H45M +LOCATION:Camp Nou\, Barcelona +DTSTAMP:20060731T111405Z +UID:705144C2-5757-4856-807B-4F7195CA3B15-0D0ECF56-3A6B-459A-B908-4D534E8 + 8799A +SEQUENCE:4 +URL;VALUE=URI:http://www.fcbarcelona.com +DTSTART;TZID=Europe/Madrid:20061210T170000 +SUMMARY:Barça - Real Sociedad +DESCRIPTION:Liga\nJornada 14\n\nGoles:\nN/A +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Outlook2007LineFolds.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Outlook2007LineFolds.ics new file mode 100644 index 00000000..fac1b58c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Outlook2007LineFolds.ics @@ -0,0 +1,50 @@ +BEGIN:VCALENDAR +PRODID:-//Microsoft Corporation//Outlook 12.0 MIMEDIR//EN +VERSION:2.0 +METHOD:PUBLISH +X-CALSTART:20090621T000000 +X-CALEND:20090622T000000 +X-WR-RELCALID:{0000002E-6380-7FD2-FED7-97EAE70D6611} +X-WR-CALNAME:Parse Error Calendar +BEGIN:VEVENT +ATTENDEE;CN=some.attendee@event.com;RSVP=TRUE:mailto:some.attendee@event.co + m +ATTENDEE;CN=event@calendardemo.net;RSVP=TRUE:mailto:event@calendardemo.net +ATTENDEE;CN="4th Floor Meeting Room";CUTYPE=RESOURCE;ROLE=NON-PARTICIPANT;R + SVP=TRUE:mailto:4th.floor.meeting.room@somewhere.com +CLASS:PUBLIC +CREATED:20090621T201527Z +DESCRIPTION:\n +DTEND;VALUE=DATE:20090622 +DTSTAMP:20090621T201612Z +DTSTART;VALUE=DATE:20090621 +LAST-MODIFIED:20090621T201618Z +LOCATION:The Exceptionally Long Named Meeting Room Whose Name Wraps Over Se + veral Lines When Exported From Leading Calendar and Office Software App + lication Microsoft Office 2007 +ORGANIZER;CN="Event Organizer":mailto:some.attendee@somewhere.com +PRIORITY:5 +SEQUENCE:0 +SUMMARY;LANGUAGE=en-gb:Example Calendar Export that Blows Up DDay.iCal +TRANSP:TRANSPARENT +UID:040000008200E00074C5B7101A82E00800000000900AD080B5F2C901000000000000000 + 010000000B29680BF9E5DC246B5EDDE228038E71F +X-ALT-DESC;FMTTYPE=text/html:\n\n\n\n\n\n\n\n\n

\n\n\n +X-MICROSOFT-CDO-BUSYSTATUS:FREE +X-MICROSOFT-CDO-IMPORTANCE:1 +X-MICROSOFT-DISALLOW-COUNTER:FALSE +X-MS-OLK-ALLOWEXTERNCHECK:TRUE +X-MS-OLK-APPTSEQTIME:20090621T201612Z +X-MS-OLK-AUTOFILLLOCATION:TRUE +X-MS-OLK-CONFTYPE:0 +BEGIN:VALARM +TRIGGER:-PT1080M +ACTION:DISPLAY +DESCRIPTION:Reminder +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/PARSE17.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/PARSE17.ics new file mode 100644 index 00000000..97ccca8f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/PARSE17.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +BEGIN:VEVENT +DTSTART:1234 +DTEND:5678 +SUMMARY:Event +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Parameter1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Parameter1.ics new file mode 100644 index 00000000..97f3f0a3 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Parameter1.ics @@ -0,0 +1,13 @@ +BEGIN:VCALENDAR +VERSION:2.0 +X-WR-CALNAME:GKCC +BEGIN:VEVENT +UID:fadc65bd4f5d746ce4a581f072ca66ea +DTSTART;VALUE=DATE;VALUE=OTHER:20090117T200000 +SUMMARY:Apple Hill Chamber Players +DTEND;VALUE=DATE:20090117T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1232164801#eventid2377 +CREATED:20080716T070640Z +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Parameter2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Parameter2.ics new file mode 100644 index 00000000..3a3942f7 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Parameter2.ics @@ -0,0 +1,29 @@ +BEGIN:VCALENDAR +PRODID:1234 +CALSCALE:GREGORIAN +METHOD:PUBLISH +BEGIN:VEVENT +DTSTAMP:20090920T000000 +LAST-MODIFIED:20090920T000000 +CREATED:20090920T000000 +DTSTART:20090901T000000 +DTEND:20090901T000000 +UID: +SUMMARY: Test +CATEGORIES: +STATUS:CONFIRMED +PARTSTAT:ACCEPTED +END:VEVENT +BEGIN:VEVENT +DTSTAMP:20090920T000000 +LAST-MODIFIED:20090920T000000 +CREATED:20090920T000000 +DTSTART:20090902T000000 +DTEND:20090902T000000 +UID: +SUMMARY: Test +CATEGORIES: +STATUS:CONFIRMED +PARTSTAT:ACCEPTED +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Parse1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Parse1.ics new file mode 100644 index 00000000..2a02f78f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Parse1.ics @@ -0,0 +1,41 @@ +BEGIN:VCALENDAR +VERSION:2.0 +X-WR-CALNAME:Frost Free Library | January 05, 2009 - February 04, 2009 +PRODID:-//strange bird labs//Drupal iCal API//EN +BEGIN:VEVENT +DTSTART;VALUE=DATE-TIME:20090106T203000Z +DTEND;VALUE=DATE-TIME:20090106T203000Z +SUMMARY;ENCODING=QUOTED-PRINTABLE:Library Tea +DESCRIPTION;ENCODING=QUOTED-PRINTABLE:

Normal 0 false false false Mic= +rosoftInternetExplorer4

=0D=0A
+URL;VALUE=URI:http://www.frostfree.org/node/505 +UID:http://www.frostfree.org/node/505 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE-TIME:20090108T153000Z +DTEND;VALUE=DATE-TIME:20090108T163000Z +SUMMARY;ENCODING=QUOTED-PRINTABLE:Story Time +DESCRIPTION;ENCODING=QUOTED-PRINTABLE:

Normal 0 false false false Mic= +rosoftInternetExplorer4

=0D=0A
+URL;VALUE=URI:http://www.frostfree.org/node/506 +UID:http://www.frostfree.org/node/506 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE-TIME:20090115T153000Z +DTEND;VALUE=DATE-TIME:20090115T163000Z +SUMMARY;ENCODING=QUOTED-PRINTABLE:Story Time +DESCRIPTION;ENCODING=QUOTED-PRINTABLE:

Normal 0 false false false Mic= +rosoftInternetExplorer4

=0D=0A
+URL;VALUE=URI:http://www.frostfree.org/node/508 +UID:http://www.frostfree.org/node/508 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE-TIME:20090115T153000Z +DTEND;VALUE=DATE-TIME:20090115T163000Z +SUMMARY;ENCODING=QUOTED-PRINTABLE:Clone of Story Time +DESCRIPTION;ENCODING=QUOTED-PRINTABLE:

Normal 0 false false false Mic= +rosoftInternetExplorer4

=0D=0A
+URL;VALUE=URI:http://www.frostfree.org/node/509 +UID:http://www.frostfree.org/node/509 +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/ProdID1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/ProdID1.ics new file mode 100644 index 00000000..04bda46f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/ProdID1.ics @@ -0,0 +1,510 @@ +BEGIN:VCALENDAR +PRODID:-//Apple Computer, Inc//iCal 2.0//EN +VERSION:2.0 +METHOD:PUBLISH +X-WR-CALNAME:Pat McCurdy +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080701 +DTEND;VALUE=DATE:20080701 +SUMMARY:Regent Street Retreat +LOCATION:Madison +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080702 +DTEND;VALUE=DATE:20080702 +SUMMARY:Perl's Country Inn +LOCATION:St. Peter\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080703 +DTEND;VALUE=DATE:20080703 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080705 +DTEND;VALUE=DATE:20080705 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080707 +DTEND;VALUE=DATE:20080707 +SUMMARY:Beat Kitchen +LOCATION:Chicago +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080708 +DTEND;VALUE=DATE:20080708 +SUMMARY:Regent Street Retreat +LOCATION:Madison +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080709 +DTEND;VALUE=DATE:20080709 +SUMMARY:Oak Creek Community Center +LOCATION:7:00 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080710 +DTEND;VALUE=DATE:20080710 +SUMMARY:Blues Bar +LOCATION:Mt Prospect\, IL +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080711 +DTEND;VALUE=DATE:20080711 +SUMMARY:Westfire Grill +LOCATION:Rochester\, MN +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080712 +DTEND;VALUE=DATE:20080712 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080713 +DTEND;VALUE=DATE:20080713 +SUMMARY:Club Paragon +LOCATION:Milwaukee +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080714 +DTEND;VALUE=DATE:20080714 +SUMMARY:Beat Kitchen +LOCATION:Chicago +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080715 +DTEND;VALUE=DATE:20080715 +SUMMARY:Regent Street Retreat +LOCATION:Madison +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080716 +DTEND;VALUE=DATE:20080716 +SUMMARY:Drifters +LOCATION:Paddock Lake\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080717 +DTEND;VALUE=DATE:20080717 +SUMMARY:Festa Italiana +LOCATION:Milwaukee +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080718 +DTEND;VALUE=DATE:20080718 +SUMMARY:Govnor's Pub Brew Daze +LOCATION:Lake in the Hills\, IL +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080719 +DTEND;VALUE=DATE:20080719 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080720 +DTEND;VALUE=DATE:20080720 +SUMMARY:Festa Italiana +LOCATION:Milwaukee +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080721 +DTEND;VALUE=DATE:20080721 +SUMMARY:Beat Kitchen +LOCATION:Chicago +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080722 +DTEND;VALUE=DATE:20080722 +SUMMARY:Regent Street Retreat +LOCATION:Madison +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080723 +DTEND;VALUE=DATE:20080723 +SUMMARY:Rotary Park +LOCATION:Mequon\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080724 +DTEND;VALUE=DATE:20080724 +SUMMARY:Mill Creek +LOCATION:Appleton\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080725 +DTEND;VALUE=DATE:20080725 +SUMMARY:St. Therese festival +LOCATION:Milwaukee +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080726 +DTEND;VALUE=DATE:20080726 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080727 +DTEND;VALUE=DATE:20080727 +SUMMARY:Coins Sports Bar +LOCATION:Kenosha\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080728 +DTEND;VALUE=DATE:20080728 +SUMMARY:Beat Kitchen +LOCATION:Chicago +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080729 +DTEND;VALUE=DATE:20080729 +SUMMARY:Regent Street Retreat +LOCATION:Madison +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080730 +DTEND;VALUE=DATE:20080730 +SUMMARY:Bar Louie +LOCATION:Milwaukee (Water St) +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080731 +DTEND;VALUE=DATE:20080731 +SUMMARY:Sheboygan Brat Days +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080801 +DTEND;VALUE=DATE:20080801 +SUMMARY:UW-Madison Memorial Union Terrace +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080802 +DTEND;VALUE=DATE:20080802 +SUMMARY:Wisconsin State Fair +LOCATION:Charcoal Grill Roadhouse\n1:30pmLumpy's Sports Bar and Grill\nSalem\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080803 +DTEND;VALUE=DATE:20080803 +SUMMARY:Sweet Applewood Festival +LOCATION:Cudahy\, WI\n5:30pm +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080804 +DTEND;VALUE=DATE:20080804 +SUMMARY:Wisconsin State Fair +LOCATION:Charcoal Grill Roadhouse\n6:00pm +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080805 +DTEND;VALUE=DATE:20080805 +SUMMARY:Wisconsin State Fair +LOCATION:Charcoal Grill Roadhouse\n6:00pm +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080806 +DTEND;VALUE=DATE:20080806 +SUMMARY:Wisconsin State Fair +LOCATION:Charcoal Grill Roadhouse\n6:00pm +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080808 +DTEND;VALUE=DATE:20080808 +SUMMARY:Claddagh Pub +LOCATION:Middleton\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080809 +DTEND;VALUE=DATE:20080809 +SUMMARY:Kiel Community Picnic +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080810 +DTEND;VALUE=DATE:20080810 +SUMMARY:Navy Pier Beer Garden +LOCATION:Chicago\n6:00pm +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080811 +DTEND;VALUE=DATE:20080811 +SUMMARY:Beat Kitchen +LOCATION:Chicago +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080812 +DTEND;VALUE=DATE:20080812 +SUMMARY:Regent Street Retreat +LOCATION:Madison +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080813 +DTEND;VALUE=DATE:20080813 +SUMMARY:Perl's Country Inn +LOCATION:St. Peter\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080815 +DTEND;VALUE=DATE:20080815 +SUMMARY:Cubby Bear +LOCATION:Lincolnshire\, IL +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080816 +DTEND;VALUE=DATE:20080816 +SUMMARY:Alpine Inn +LOCATION:La Crosse\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080817 +DTEND;VALUE=DATE:20080817 +SUMMARY:St. Rita's Festival +LOCATION:Racine\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080818 +DTEND;VALUE=DATE:20080818 +SUMMARY:Beat Kitchen +LOCATION:Chicago +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080819 +DTEND;VALUE=DATE:20080819 +SUMMARY:Regent Street Retreat +LOCATION:Madison +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080820 +DTEND;VALUE=DATE:20080820 +SUMMARY:St. Brendan's Irish Inn +LOCATION:Green Bay +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080821 +DTEND;VALUE=DATE:20080821 +SUMMARY:Mill Creek +LOCATION:Appleton\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080822 +DTEND;VALUE=DATE:20080822 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080824 +DTEND;VALUE=DATE:20080824 +SUMMARY:The Looking Glass +LOCATION:Janesville\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080825 +DTEND;VALUE=DATE:20080825 +SUMMARY:Beat Kitchen +LOCATION:Chicago +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080826 +DTEND;VALUE=DATE:20080826 +SUMMARY:Regent Street Retreat +LOCATION:Madison +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080827 +DTEND;VALUE=DATE:20080827 +SUMMARY:Bar Louie +LOCATION:Milwaukee (Water St) +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080829 +DTEND;VALUE=DATE:20080829 +SUMMARY:Lakeland College +LOCATION:Sheboygan\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080830 +DTEND;VALUE=DATE:20080830 +SUMMARY:St. Francis Days +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080831 +DTEND;VALUE=DATE:20080831 +SUMMARY:Sandee's Cool Runnings +LOCATION:Sheboygan\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080901 +DTEND;VALUE=DATE:20080901 +SUMMARY:Oak Creek Lions Festival +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080903 +DTEND;VALUE=DATE:20080903 +SUMMARY:Edgewood College +LOCATION:Madison\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080904 +DTEND;VALUE=DATE:20080904 +SUMMARY:UWM Gasthaus +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080906 +DTEND;VALUE=DATE:20080906 +SUMMARY:Alpine Inn +LOCATION:La Crosse\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080907 +DTEND;VALUE=DATE:20080907 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080910 +DTEND;VALUE=DATE:20080910 +SUMMARY:St. Brendan's Irish Inn +LOCATION:Green Bay +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080912 +DTEND;VALUE=DATE:20080912 +SUMMARY:O'Gara's +LOCATION:St. Paul\, MN +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080913 +DTEND;VALUE=DATE:20080913 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080914 +DTEND;VALUE=DATE:20080914 +SUMMARY:Coins Sports Bar +LOCATION:Kenosha\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080917 +DTEND;VALUE=DATE:20080917 +SUMMARY:Bar Louie +LOCATION:Milwaukee (Water St) +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080918 +DTEND;VALUE=DATE:20080918 +SUMMARY:Mill Creek +LOCATION:Appleton\, WI +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080919 +DTEND;VALUE=DATE:20080919 +SUMMARY:Cubby Bear +LOCATION:Lincolnshire\, IL +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080920 +DTEND;VALUE=DATE:20080920 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080922 +DTEND;VALUE=DATE:20080922 +SUMMARY:No show - on vacation +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080923 +DTEND;VALUE=DATE:20080923 +SUMMARY:No show - on vacation +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080924 +DTEND;VALUE=DATE:20080924 +SUMMARY:No show - on vacation +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080925 +DTEND;VALUE=DATE:20080925 +SUMMARY:No show - on vacation +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080926 +DTEND;VALUE=DATE:20080926 +SUMMARY:No show - on vacation +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080927 +DTEND;VALUE=DATE:20080927 +SUMMARY:No show - on vacation +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080928 +DTEND;VALUE=DATE:20080928 +SUMMARY:No show - on vacation +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20081001 +DTEND;VALUE=DATE:20081001 +SUMMARY:Wisconsin Athletic Club +LOCATION:West Allis +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20081002 +DTEND;VALUE=DATE:20081002 +SUMMARY:La Crosse Oktoberfest +LOCATION:7:00 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20081003 +DTEND;VALUE=DATE:20081003 +SUMMARY:O'Gara's +LOCATION:St. Paul\, MN +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20081008 +DTEND;VALUE=DATE:20081008 +SUMMARY:St. Brendan's Irish Inn +LOCATION:Green Bay +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20081018 +DTEND;VALUE=DATE:20081018 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20081022 +DTEND;VALUE=DATE:20081022 +SUMMARY:Private event +LOCATION: +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20081025 +DTEND;VALUE=DATE:20081025 +SUMMARY:Shank Hall +LOCATION:Milwaukee +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20081029 +DTEND;VALUE=DATE:20081029 +SUMMARY:Bar Louie +LOCATION:Milwaukee (Water St) +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/ProdID2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/ProdID2.ics new file mode 100644 index 00000000..1c6bfd7c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/ProdID2.ics @@ -0,0 +1,287 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//CSV To iCal Convertor//Manas Tungare, manastungare.com//EN +CALSCALE:GREGORIAN +X-WR-TIMEZONE;VALUE=TEXT:US/Eastern +X-WR-CALNAME;VALUE=TEXT:UNC Basketball +BEGIN:VEVENT +SUMMARY:UNC vs. St. Augustines (exhibition) +DESCRIPTION:UNC vs. St. Augustines (exhibition) FALSE +DTSTAMP:20061101T000000 +DTSTART:20061101T073000 +DTEND:20061101T083000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Pfeiffer (exhibition - Time TBA) +DESCRIPTION:UNC vs. Pfeiffer (exhibition - Time TBA) FALSE +DTSTAMP:20061111T000000 +DTSTART:20061111T120000 +DTEND:20061111T130000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:NIT Tip-Off: UNC vs. Sacred Heart (Charlotte - ESPNU) +DESCRIPTION:NIT Tip-Off: UNC vs. Sacred Heart (Charlotte - ESPNU) FALSE +DTSTAMP:20061114T000000 +DTSTART:20061114T070000 +DTEND:20061114T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:NIT Tip-Off: UNC vs. Iona or Winthrop (Charlotte - ESPN) +DESCRIPTION:NIT Tip-Off: UNC vs. Iona or Winthrop (Charlotte - ESPN) FALSE +DTSTAMP:20061115T000000 +DTSTART:20061115T070000 +DTEND:20061115T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Gardner-Webb (Chapel Hill - Fox Sports South/NESN) +DESCRIPTION:UNC vs. Gardner-Webb (Chapel Hill - Fox Sports South/NESN) FALSE +DTSTAMP:20061119T000000 +DTSTART:20061119T010000 +DTEND:20061119T020000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:NIT Semifinals (Madison Square Garden - ESPN2) +DESCRIPTION:NIT Semifinals (Madison Square Garden - ESPN2) FALSE +DTSTAMP:20061122T000000 +DTSTART:20061122T070000 +DTEND:20061122T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:NIT Final/Consolation (Madison Square Garden - ESPN2/ESPN) +DESCRIPTION:NIT Final/Consolation (Madison Square Garden - ESPN2/ESPN) FALSE +DTSTAMP:20061124T000000 +DTSTART:20061124T070000 +DTEND:20061124T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Ohio State (Chapel Hill - ESPN) +DESCRIPTION:UNC vs. Ohio State (Chapel Hill - ESPN) FALSE +DTSTAMP:20061129T000000 +DTSTART:20061129T090000 +DTEND:20061129T100000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Kentucky (Chapel Hill - CBS) +DESCRIPTION:UNC vs. Kentucky (Chapel Hill - CBS) FALSE +DTSTAMP:20061202T000000 +DTSTART:20061202T120000 +DTEND:20061202T130000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. High Point (Chapel Hill - TV TBA) +DESCRIPTION:UNC vs. High Point (Chapel Hill - TV TBA) FALSE +DTSTAMP:20061209T000000 +DTSTART:20061209T070000 +DTEND:20061209T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. UNC Asheville (Chapel Hill - Fox Sports Net) +DESCRIPTION:UNC vs. UNC Asheville (Chapel Hill - Fox Sports Net) FALSE +DTSTAMP:20061216T000000 +DTSTART:20061216T020000 +DTEND:20061216T030000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Florida Atlantic (Chapel Hill - ESPNU) +DESCRIPTION:UNC vs. Florida Atlantic (Chapel Hill - ESPNU) FALSE +DTSTAMP:20061219T000000 +DTSTART:20061219T070000 +DTEND:20061219T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC @ St. Louis (Savvis Center - ESPN2 or ESPNU) +DESCRIPTION:UNC @ St. Louis (Savvis Center - ESPN2 or ESPNU) FALSE +DTSTAMP:20061222T000000 +DTSTART:20061222T070000 +DTEND:20061222T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Rutgers (Chapel Hill - ESPN2) +DESCRIPTION:UNC vs. Rutgers (Chapel Hill - ESPN2) FALSE +DTSTAMP:20061228T000000 +DTSTART:20061228T070000 +DTEND:20061228T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Dayton (Chapel Hill - Fox Sports Net) +DESCRIPTION:UNC vs. Dayton (Chapel Hill - Fox Sports Net) FALSE +DTSTAMP:20061231T000000 +DTSTART:20061231T030000 +DTEND:20061231T040000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Pennsylvania (Chapel Hill - ESPN) +DESCRIPTION:UNC vs. Pennsylvania (Chapel Hill - ESPN) FALSE +DTSTAMP:20070103T000000 +DTSTART:20070103T080000 +DTEND:20070103T090000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Fla. State (Chapel Hill - Fox Sports Net) +DESCRIPTION:UNC vs. Fla. State (Chapel Hill - Fox Sports Net) FALSE +DTSTAMP:20070107T000000 +DTSTART:20070107T070000 +DTEND:20070107T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Virginia (Chapel Hill - Raycom/LF) +DESCRIPTION:UNC vs. Virginia (Chapel Hill - Raycom/LF) FALSE +DTSTAMP:20070110T000000 +DTSTART:20070110T090000 +DTEND:20070110T100000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Ga. Tech (Chapel Hill - ESPN) +DESCRIPTION:UNC vs. Ga. Tech (Chapel Hill - ESPN) FALSE +DTSTAMP:20070120T000000 +DTSTART:20070120T090000 +DTEND:20070120T100000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC @ Wake Forest (Winston-Salem - ESPN) +DESCRIPTION:UNC @ Wake Forest (Winston-Salem - ESPN) FALSE +DTSTAMP:20070124T000000 +DTSTART:20070124T070000 +DTEND:20070124T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Miami (Chapel Hill - ESPN) +DESCRIPTION:UNC vs. Miami (Chapel Hill - ESPN) FALSE +DTSTAMP:20070131T000000 +DTSTART:20070131T070000 +DTEND:20070131T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC @ NC State (Raleigh - ABC) +DESCRIPTION:UNC @ NC State (Raleigh - ABC) FALSE +DTSTAMP:20070203T000000 +DTSTART:20070203T033000 +DTEND:20070203T043000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC @ Duke (Durham +DESCRIPTION:UNC @ Duke (Durham FALSE +DTSTAMP:19691231T165959 +DTSTART:19691231T165959 +DTEND:19691231T165959 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Wake Forest (Chapel Hill - Raycom/LF) +DESCRIPTION:UNC vs. Wake Forest (Chapel Hill - Raycom/LF) FALSE +DTSTAMP:20070210T000000 +DTSTART:20070210T013000 +DTEND:20070210T023000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Va. Tech (Chapel Hill - Raycom/LF) +DESCRIPTION:UNC vs. Va. Tech (Chapel Hill - Raycom/LF) FALSE +DTSTAMP:20070213T000000 +DTSTART:20070213T080000 +DTEND:20070213T090000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. NC State (Chapel Hill - Raycom/LF) +DESCRIPTION:UNC vs. NC State (Chapel Hill - Raycom/LF) FALSE +DTSTAMP:20070221T000000 +DTSTART:20070221T090000 +DTEND:20070221T100000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:UNC vs. Duke (Chapel Hill - CBS) +DESCRIPTION:UNC vs. Duke (Chapel Hill - CBS) FALSE +DTSTAMP:20070304T000000 +DTSTART:20070304T040000 +DTEND:20070304T050000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Tournament (#8/#9 - Raycom/LF) +DESCRIPTION:ACC Tournament (#8/#9 - Raycom/LF) FALSE +DTSTAMP:20070308T000000 +DTSTART:20070308T120000 +DTEND:20070308T130000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Tournament (#5/#12 - Raycom/LF) +DESCRIPTION:ACC Tournament (#5/#12 - Raycom/LF) FALSE +DTSTAMP:20070308T000000 +DTSTART:20070308T023000 +DTEND:20070308T033000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Tournament (#7/#10 - ESPN2) +DESCRIPTION:ACC Tournament (#7/#10 - ESPN2) FALSE +DTSTAMP:20070308T000000 +DTSTART:20070308T070000 +DTEND:20070308T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Tournament (#6/#11 - Raycom/LF) +DESCRIPTION:ACC Tournament (#6/#11 - Raycom/LF) FALSE +DTSTAMP:20070308T000000 +DTSTART:20070308T093000 +DTEND:20070308T103000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Tournament (#1 vs. 8/9 - Raycom/LF/ESPN2) +DESCRIPTION:ACC Tournament (#1 vs. 8/9 - Raycom/LF/ESPN2) FALSE +DTSTAMP:20070309T000000 +DTSTART:20070309T120000 +DTEND:20070309T130000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Tournament (#4 vs. 5/12 - Raycom/LF/ESPN2) +DESCRIPTION:ACC Tournament (#4 vs. 5/12 - Raycom/LF/ESPN2) FALSE +DTSTAMP:20070309T000000 +DTSTART:20070309T023000 +DTEND:20070309T033000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Tournament (#2 vs. 7/10 - Raycom/LF/ESPN2) +DESCRIPTION:ACC Tournament (#2 vs. 7/10 - Raycom/LF/ESPN2) FALSE +DTSTAMP:20070309T000000 +DTSTART:20070309T070000 +DTEND:20070309T080000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Tournament (#3 vs. 6/11 - Raycom/LF/ESPN2) +DESCRIPTION:ACC Tournament (#3 vs. 6/11 - Raycom/LF/ESPN2) FALSE +DTSTAMP:20070309T000000 +DTSTART:20070309T093000 +DTEND:20070309T103000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Semifinals (Raycom/LF/ESPN2) +DESCRIPTION:ACC Semifinals (Raycom/LF/ESPN2) FALSE +DTSTAMP:20070310T000000 +DTSTART:20070310T013000 +DTEND:20070310T023000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Semifinals (Raycom/LF/ESPN) +DESCRIPTION:ACC Semifinals (Raycom/LF/ESPN) FALSE +DTSTAMP:20070310T000000 +DTSTART:20070310T040000 +DTEND:20070310T050000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:ACC Championship (Raycom/LF/ESPN) +DESCRIPTION:ACC Championship (Raycom/LF/ESPN) FALSE +DTSTAMP:20070311T000000 +DTSTART:20070311T010000 +DTEND:20070311T020000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:Final Four (Atlanta - CBS) +DESCRIPTION:Final Four (Atlanta - CBS) FALSE +DTSTAMP:20070331T000000 +DTSTART:20070331T063000 +DTEND:20070331T073000 +END:VEVENT +BEGIN:VEVENT +SUMMARY:National Championship (CBS) +DESCRIPTION:National Championship (CBS) FALSE +DTSTAMP:20070402T000000 +DTSTART:20070402T090000 +DTEND:20070402T100000 +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Property1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Property1.ics new file mode 100644 index 00000000..852905fe --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Property1.ics @@ -0,0 +1,14 @@ +BEGIN:VCALENDAR +VERSION:2.0 +VERSION:2.1 +X-WR-CALNAME:GKCC +BEGIN:VEVENT +UID:fadc65bd4f5d746ce4a581f072ca66ea +DTSTART;VALUE=DATE:20090117T200000 +SUMMARY:Apple Hill Chamber Players +DTEND;VALUE=DATE:20090117T220000 +LOCATION:The Colonial Theatre +URL;VALUE=URI:http://www.keenechamber.com/events.html?date=1232164801#eventid2377 +CREATED:20080716T070640Z +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/RecurrenceDates1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/RecurrenceDates1.ics new file mode 100644 index 00000000..63a39742 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/RecurrenceDates1.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//hacksw/handcal//NONSGML v1.0//EN +BEGIN:VEVENT +RDATE:19970714T123000Z +RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z, + 19960404T010000Z/PT3H +RDATE;VALUE=DATE:19970101,19970120,19970217,19970421, + 19970526,19970704,19970901,19971014,19971128,19971129,19971225 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/RequestStatus1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/RequestStatus1.ics new file mode 100644 index 00000000..9c1af00c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/RequestStatus1.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//hacksw/handcal//NONSGML v1.0//EN +BEGIN:VEVENT +REQUEST-STATUS:2.0;Success +REQUEST-STATUS:3.1;Invalid property value;DTSTART:96-Apr-01 +REQUEST-STATUS:2.8; Success\, repeating event ignored. Scheduled + as a single event.;RRULE:FREQ=WEEKLY\;INTERVAL=2 +REQUEST-STATUS:4.1;Event conflict. Date/time is busy. +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/TimeZone1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/TimeZone1.ics new file mode 100644 index 00000000..0a321f47 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/TimeZone1.ics @@ -0,0 +1,22 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +BEGIN:STANDARD +DTSTART:19971026T020000 +RDATE:19971026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19971026T020000 +RDATE:19970406T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/TimeZone2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/TimeZone2.ics new file mode 100644 index 00000000..0a321f47 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/TimeZone2.ics @@ -0,0 +1,22 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +BEGIN:STANDARD +DTSTART:19971026T020000 +RDATE:19971026T020000 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19971026T020000 +RDATE:19970406T020000 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/TimeZone3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/TimeZone3.ics new file mode 100644 index 00000000..07036093 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/TimeZone3.ics @@ -0,0 +1,39 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTIMEZONE +TZID:/mozilla.org/20070129_1/Europe/Berlin +X-LIC-LOCATION:Europe/Berlin +BEGIN:DAYLIGHT +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +TZNAME:CEST +DTSTART:19700329T020000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +TZNAME:CET +DTSTART:19701025T030000 +RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10 +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20080121T170855Z +LAST-MODIFIED:20080121T170935Z +DTSTAMP:20080121T170940Z +UID:949ee027-7b13-4792-863b-73d9061b60f0 +SUMMARY:Neujahr +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTHDAY=1;BYMONTH=1 +DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Berlin:20080101 +DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Berlin:20080102 +TRANSP:TRANSPARENT +BEGIN:VALARM +TRIGGER;VALUE=DURATION:-PT1H +X-MOZ-LASTACK:20080121T170935Z +DESCRIPTION:Mozilla Alarm: Neujahr +ACTION:DISPLAY +END:VALARM +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo1.ics new file mode 100644 index 00000000..dc0ba63a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo1.ics @@ -0,0 +1,31 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:NEEDS-ACTION +CLASS:PRIVATE +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo2.ics new file mode 100644 index 00000000..b56620c8 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo2.ics @@ -0,0 +1,32 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:NEEDS-ACTION +CLASS:PRIVATE +DTSTAMP:20060728T195437Z +DTSTART;TZID=US-Eastern:20060728T090000 +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo3.ics new file mode 100644 index 00000000..b986e8e7 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo3.ics @@ -0,0 +1,31 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:CANCELLED +CLASS:PRIVATE +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo4.ics new file mode 100644 index 00000000..1f352ed2 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo4.ics @@ -0,0 +1,32 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:COMPLETED +CLASS:PRIVATE +DTSTART;TZID=US-Eastern:20060728T090000 +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo5.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo5.ics new file mode 100644 index 00000000..e7612c8f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo5.ics @@ -0,0 +1,34 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:COMPLETED +COMPLETED;TZID=US-Eastern:20060730T090000 +CLASS:PRIVATE +DTSTART;TZID=US-Eastern:20060728T090000 +RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo6.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo6.ics new file mode 100644 index 00000000..62dcbf54 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo6.ics @@ -0,0 +1,34 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:COMPLETED +COMPLETED;TZID=US-Eastern:20060804T080000 +CLASS:PRIVATE +DTSTART;TZID=US-Eastern:20060728T090000 +RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo7.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo7.ics new file mode 100644 index 00000000..ecf77c8e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Todo7.ics @@ -0,0 +1,34 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:COMPLETED +COMPLETED;TZID=US-Eastern:20060804T100000 +CLASS:PRIVATE +DTSTART;TZID=US-Eastern:20060728T090000 +RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Transparency1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Transparency1.ics new file mode 100644 index 00000000..75c191e1 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Transparency1.ics @@ -0,0 +1,14 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//test.org/Calendar//EN +BEGIN:VEVENT +CREATED:20090202T114737Z +LAST-MODIFIED:20090202T114820Z +DTSTAMP:20090322T170116Z +UID:edb7a48a-d846-47f8-bad2-9ea3f29bcda5 +SUMMARY:Vacances de la Toussaint +DTSTART;VALUE=DATE:20081025 +DTEND;VALUE=DATE:20081106 +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Transparency2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Transparency2.ics new file mode 100644 index 00000000..5f48d569 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Transparency2.ics @@ -0,0 +1,14 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//test.org/Calendar//EN +BEGIN:VEVENT +CREATED:20090202T114737Z +LAST-MODIFIED:20090202T114820Z +DTSTAMP:20090322T170116Z +UID:edb7a48a-d846-47f8-bad2-9ea3f29bcda5 +SUMMARY:Vacances de la Toussaint +DTSTART;VALUE=DATE:20081025 +DTEND;VALUE=DATE:20081106 +TRANSP:TRANSPARENT +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Trigger1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Trigger1.ics new file mode 100644 index 00000000..b7dc270c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/Trigger1.ics @@ -0,0 +1,421 @@ +BEGIN:VCALENDAR +PRODID:-//Sun/Calendar Server//EN +METHOD:PUBLISH +VERSION:2.0 +X-NSCP-WCAP-ERRNO:0 +X-NSCP-CALPROPS-LAST-MODIFIED:20100902T174822Z +X-NSCP-CALPROPS-CREATED:20100902T174822Z +X-NSCP-CALPROPS-READ:999 +X-NSCP-CALPROPS-WRITE:999 +X-NSCP-CALPROPS-RELATIVE-CALID:luis.sanchez@example.com +X-NSCP-CALPROPS-NAME:Luis Sanchez +X-NSCP-CALPROPS-LANGUAGE:en +X-NSCP-CALPROPS-PRIMARY-OWNER:luis.sanchez@example.com +X-NSCP-CALPROPS-ACCESS-CONTROL-ENTRY:@@o^a^r^g +X-NSCP-CALPROPS-ACCESS-CONTROL-ENTRY:@@o^c^wdeic^g +X-NSCP-CALPROPS-ACCESS-CONTROL-ENTRY:@^a^rsf^g +X-NSCP-CALPROPS-ACCESS-CONTROL-ENTRY:@^p^r^g +X-NSCP-CALPROPS-RESOURCE:0 +X-S1CS-CALPROPS-ALLOW-DOUBLEBOOKING:1 +BEGIN:VEVENT +UID:00000000000000000000000000000000420a9a4c17370000f304000065180000 +RECURRENCE-ID:20100924T070000Z +DTSTAMP:20100922T135320Z +SUMMARY:Bb1 +DTSTART:20100924T070000Z +DTEND:20100924T080000Z +CREATED:20100922T135306Z +LAST-MODIFIED:20100922T135320Z +PRIORITY:0 +SEQUENCE:3 +CLASS:PUBLIC +ORGANIZER;CN="Luis Sanchez" + ;X-NSCP-ORGANIZER-UID=luis.sanchez@example.com + ;X-S1CS-EMAIL=luss@example.com + :luis.sanchez@example.com +STATUS:CONFIRMED +TRANSP:OPAQUE +X-NSCP-ORIGINAL-DTSTART:20100924T070000Z +X-NSCP-LANGUAGE:es +BEGIN:VALARM +ACTION:EMAIL +TRIGGER;VALUE=DATE-TIME:20100924T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +ATTENDEE:MAILTO:luss@example.com +END:VALARM +BEGIN:VALARM +ACTION:X-SUNW-FLASHING +TRIGGER;VALUE=DATE-TIME:20100924T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +END:VALARM +X-NSCP-DTSTART-TZID:America/Denver +X-NSCP-TOMBSTONE:0 +X-NSCP-ONGOING:0 +X-NSCP-ORGANIZER-EMAIL:luss@example.com +X-NSCP-GSE-COMPONENT-STATE;X-NSCP-GSE-COMMENT="PUBLISH-COMPLETED":65538 +REQUEST-STATUS:2.0;Success. Store successful. +END:VEVENT +BEGIN:VEVENT +UID:00000000000000000000000000000000420a9a4c17370000f304000065180000 +RECURRENCE-ID:20100927T070000Z +DTSTAMP:20100922T135320Z +SUMMARY:Bb1 +DTSTART:20100927T070000Z +DTEND:20100927T080000Z +CREATED:20100922T135306Z +LAST-MODIFIED:20100922T135320Z +PRIORITY:0 +SEQUENCE:3 +CLASS:PUBLIC +ORGANIZER;CN="Luis Sanchez" + ;X-NSCP-ORGANIZER-UID=luis.sanchez@example.com + ;X-S1CS-EMAIL=luss@example.com + :luis.sanchez@example.com +STATUS:CONFIRMED +TRANSP:OPAQUE +X-NSCP-ORIGINAL-DTSTART:20100927T070000Z +X-NSCP-LANGUAGE:es +BEGIN:VALARM +ACTION:EMAIL +TRIGGER;VALUE=DATE-TIME:20100927T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +ATTENDEE:MAILTO:luss@example.com +END:VALARM +BEGIN:VALARM +ACTION:X-SUNW-FLASHING +TRIGGER;VALUE=DATE-TIME:20100927T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +END:VALARM +X-NSCP-DTSTART-TZID:America/Denver +X-NSCP-TOMBSTONE:0 +X-NSCP-ONGOING:0 +X-NSCP-ORGANIZER-EMAIL:luss@example.com +X-NSCP-GSE-COMPONENT-STATE;X-NSCP-GSE-COMMENT="PUBLISH-COMPLETED":65538 +REQUEST-STATUS:2.0;Success. Store successful. +END:VEVENT +BEGIN:VEVENT +UID:00000000000000000000000000000000420a9a4c17370000f304000065180000 +RECURRENCE-ID:20101015T070000Z +DTSTAMP:20100922T135320Z +SUMMARY:Bb1 +DTSTART:20101015T070000Z +DTEND:20101015T080000Z +CREATED:20100922T135306Z +LAST-MODIFIED:20100922T135320Z +PRIORITY:0 +SEQUENCE:3 +CLASS:PUBLIC +ORGANIZER;CN="Luis Sanchez" + ;X-NSCP-ORGANIZER-UID=luis.sanchez@example.com + ;X-S1CS-EMAIL=luss@example.com + :luis.sanchez@example.com +STATUS:CONFIRMED +TRANSP:OPAQUE +X-NSCP-ORIGINAL-DTSTART:20101015T070000Z +X-NSCP-LANGUAGE:es +BEGIN:VALARM +ACTION:EMAIL +TRIGGER;VALUE=DATE-TIME:20101015T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +ATTENDEE:MAILTO:luss@example.com +END:VALARM +BEGIN:VALARM +ACTION:X-SUNW-FLASHING +TRIGGER;VALUE=DATE-TIME:20101015T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +END:VALARM +X-NSCP-DTSTART-TZID:America/Denver +X-NSCP-TOMBSTONE:0 +X-NSCP-ONGOING:0 +X-NSCP-ORGANIZER-EMAIL:luss@example.com +X-NSCP-GSE-COMPONENT-STATE;X-NSCP-GSE-COMMENT="PUBLISH-COMPLETED":65538 +REQUEST-STATUS:2.0;Success. Store successful. +END:VEVENT +BEGIN:VEVENT +UID:00000000000000000000000000000000420a9a4c17370000f304000065180000 +RECURRENCE-ID:20101021T070000Z +DTSTAMP:20100922T135320Z +SUMMARY:Bb1 +DTSTART:20101021T070000Z +DTEND:20101021T080000Z +CREATED:20100922T135306Z +LAST-MODIFIED:20100922T135320Z +PRIORITY:0 +SEQUENCE:3 +CLASS:PUBLIC +ORGANIZER;CN="Luis Sanchez" + ;X-NSCP-ORGANIZER-UID=luis.sanchez@example.com + ;X-S1CS-EMAIL=luss@example.com + :luis.sanchez@example.com +STATUS:CONFIRMED +TRANSP:OPAQUE +X-NSCP-ORIGINAL-DTSTART:20101021T070000Z +X-NSCP-LANGUAGE:es +BEGIN:VALARM +ACTION:EMAIL +TRIGGER;VALUE=DATE-TIME:20101021T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +ATTENDEE:MAILTO:luss@example.com +END:VALARM +BEGIN:VALARM +ACTION:X-SUNW-FLASHING +TRIGGER;VALUE=DATE-TIME:20101021T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +END:VALARM +X-NSCP-DTSTART-TZID:America/Denver +X-NSCP-TOMBSTONE:0 +X-NSCP-ONGOING:0 +X-NSCP-ORGANIZER-EMAIL:luss@example.com +X-NSCP-GSE-COMPONENT-STATE;X-NSCP-GSE-COMMENT="PUBLISH-COMPLETED":65538 +REQUEST-STATUS:2.0;Success. Store successful. +END:VEVENT +BEGIN:VEVENT +UID:00000000000000000000000000000000420a9a4c17370000f304000065180000 +RECURRENCE-ID:20100930T070000Z +DTSTAMP:20100922T135320Z +SUMMARY:Bb1 +DTSTART:20100930T070000Z +DTEND:20100930T080000Z +CREATED:20100922T135306Z +LAST-MODIFIED:20100922T135320Z +PRIORITY:0 +SEQUENCE:3 +CLASS:PUBLIC +ORGANIZER;CN="Luis Sanchez" + ;X-NSCP-ORGANIZER-UID=luis.sanchez@example.com + ;X-S1CS-EMAIL=luss@example.com + :luis.sanchez@example.com +STATUS:CONFIRMED +TRANSP:OPAQUE +X-NSCP-ORIGINAL-DTSTART:20100930T070000Z +X-NSCP-LANGUAGE:es +BEGIN:VALARM +ACTION:EMAIL +TRIGGER;VALUE=DATE-TIME:20100930T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +ATTENDEE:MAILTO:luss@example.com +END:VALARM +BEGIN:VALARM +ACTION:X-SUNW-FLASHING +TRIGGER;VALUE=DATE-TIME:20100930T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +END:VALARM +X-NSCP-DTSTART-TZID:America/Denver +X-NSCP-TOMBSTONE:0 +X-NSCP-ONGOING:0 +X-NSCP-ORGANIZER-EMAIL:luss@example.com +X-NSCP-GSE-COMPONENT-STATE;X-NSCP-GSE-COMMENT="PUBLISH-COMPLETED":65538 +REQUEST-STATUS:2.0;Success. Store successful. +END:VEVENT +BEGIN:VEVENT +UID:00000000000000000000000000000000420a9a4c17370000f304000065180000 +RECURRENCE-ID:20101012T070000Z +DTSTAMP:20100922T135320Z +SUMMARY:Bb1 +DTSTART:20101012T070000Z +DTEND:20101012T080000Z +CREATED:20100922T135306Z +LAST-MODIFIED:20100922T135320Z +PRIORITY:0 +SEQUENCE:3 +CLASS:PUBLIC +ORGANIZER;CN="Luis Sanchez" + ;X-NSCP-ORGANIZER-UID=luis.sanchez@example.com + ;X-S1CS-EMAIL=luss@example.com + :luis.sanchez@example.com +STATUS:CONFIRMED +TRANSP:OPAQUE +X-NSCP-ORIGINAL-DTSTART:20101012T070000Z +X-NSCP-LANGUAGE:es +BEGIN:VALARM +ACTION:EMAIL +TRIGGER;VALUE=DATE-TIME:20101012T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +ATTENDEE:MAILTO:luss@example.com +END:VALARM +BEGIN:VALARM +ACTION:X-SUNW-FLASHING +TRIGGER;VALUE=DATE-TIME:20101012T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +END:VALARM +X-NSCP-DTSTART-TZID:America/Denver +X-NSCP-TOMBSTONE:0 +X-NSCP-ONGOING:0 +X-NSCP-ORGANIZER-EMAIL:luss@example.com +X-NSCP-GSE-COMPONENT-STATE;X-NSCP-GSE-COMMENT="PUBLISH-COMPLETED":65538 +REQUEST-STATUS:2.0;Success. Store successful. +END:VEVENT +BEGIN:VEVENT +UID:00000000000000000000000000000000420a9a4c17370000f304000065180000 +RECURRENCE-ID:20101003T070000Z +DTSTAMP:20100922T135320Z +SUMMARY:Bb1 +DTSTART:20101003T070000Z +DTEND:20101003T080000Z +CREATED:20100922T135306Z +LAST-MODIFIED:20100922T135320Z +PRIORITY:0 +SEQUENCE:3 +CLASS:PUBLIC +ORGANIZER;CN="Luis Sanchez" + ;X-NSCP-ORGANIZER-UID=luis.sanchez@example.com + ;X-S1CS-EMAIL=luss@example.com + :luis.sanchez@example.com +STATUS:CONFIRMED +TRANSP:OPAQUE +X-NSCP-ORIGINAL-DTSTART:20101003T070000Z +X-NSCP-LANGUAGE:es +BEGIN:VALARM +ACTION:EMAIL +TRIGGER;VALUE=DATE-TIME:20101003T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +ATTENDEE:MAILTO:luss@example.com +END:VALARM +BEGIN:VALARM +ACTION:X-SUNW-FLASHING +TRIGGER;VALUE=DATE-TIME:20101003T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +END:VALARM +X-NSCP-DTSTART-TZID:America/Denver +X-NSCP-TOMBSTONE:0 +X-NSCP-ONGOING:0 +X-NSCP-ORGANIZER-EMAIL:luss@example.com +X-NSCP-GSE-COMPONENT-STATE;X-NSCP-GSE-COMMENT="PUBLISH-COMPLETED":65538 +REQUEST-STATUS:2.0;Success. Store successful. +END:VEVENT +BEGIN:VEVENT +UID:00000000000000000000000000000000420a9a4c17370000f304000065180000 +RECURRENCE-ID:20101018T070000Z +DTSTAMP:20100922T135320Z +SUMMARY:Bb1 +DTSTART:20101018T070000Z +DTEND:20101018T080000Z +CREATED:20100922T135306Z +LAST-MODIFIED:20100922T135320Z +PRIORITY:0 +SEQUENCE:3 +CLASS:PUBLIC +ORGANIZER;CN="Luis Sanchez" + ;X-NSCP-ORGANIZER-UID=luis.sanchez@example.com + ;X-S1CS-EMAIL=luss@example.com + :luis.sanchez@example.com +STATUS:CONFIRMED +TRANSP:OPAQUE +X-NSCP-ORIGINAL-DTSTART:20101018T070000Z +X-NSCP-LANGUAGE:es +BEGIN:VALARM +ACTION:EMAIL +TRIGGER;VALUE=DATE-TIME:20101018T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +ATTENDEE:MAILTO:luss@example.com +END:VALARM +BEGIN:VALARM +ACTION:X-SUNW-FLASHING +TRIGGER;VALUE=DATE-TIME:20101018T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +END:VALARM +X-NSCP-DTSTART-TZID:America/Denver +X-NSCP-TOMBSTONE:0 +X-NSCP-ONGOING:0 +X-NSCP-ORGANIZER-EMAIL:luss@example.com +X-NSCP-GSE-COMPONENT-STATE;X-NSCP-GSE-COMMENT="PUBLISH-COMPLETED":65538 +REQUEST-STATUS:2.0;Success. Store successful. +END:VEVENT +BEGIN:VEVENT +UID:00000000000000000000000000000000420a9a4c17370000f304000065180000 +RECURRENCE-ID:20101006T070000Z +DTSTAMP:20100922T135320Z +SUMMARY:Bb1 +DTSTART:20101006T070000Z +DTEND:20101006T080000Z +CREATED:20100922T135306Z +LAST-MODIFIED:20100922T135320Z +PRIORITY:0 +SEQUENCE:3 +CLASS:PUBLIC +ORGANIZER;CN="Luis Sanchez" + ;X-NSCP-ORGANIZER-UID=luis.sanchez@example.com + ;X-S1CS-EMAIL=luss@example.com + :luis.sanchez@example.com +STATUS:CONFIRMED +TRANSP:OPAQUE +X-NSCP-ORIGINAL-DTSTART:20101006T070000Z +X-NSCP-LANGUAGE:es +BEGIN:VALARM +ACTION:EMAIL +TRIGGER;VALUE=DATE-TIME:20101006T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +ATTENDEE:MAILTO:luss@example.com +END:VALARM +BEGIN:VALARM +ACTION:X-SUNW-FLASHING +TRIGGER;VALUE=DATE-TIME:20101006T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +END:VALARM +X-NSCP-DTSTART-TZID:America/Denver +X-NSCP-TOMBSTONE:0 +X-NSCP-ONGOING:0 +X-NSCP-ORGANIZER-EMAIL:luss@example.com +X-NSCP-GSE-COMPONENT-STATE;X-NSCP-GSE-COMMENT="PUBLISH-COMPLETED":65538 +REQUEST-STATUS:2.0;Success. Store successful. +END:VEVENT +BEGIN:VEVENT +UID:00000000000000000000000000000000420a9a4c17370000f304000065180000 +RECURRENCE-ID:20101009T070000Z +DTSTAMP:20100922T135320Z +SUMMARY:Bb1 +DTSTART:20101009T070000Z +DTEND:20101009T080000Z +CREATED:20100922T135306Z +LAST-MODIFIED:20100922T135320Z +PRIORITY:0 +SEQUENCE:3 +CLASS:PUBLIC +ORGANIZER;CN="Luis Sanchez" + ;X-NSCP-ORGANIZER-UID=luis.sanchez@example.com + ;X-S1CS-EMAIL=luss@example.com + :luis.sanchez@example.com +STATUS:CONFIRMED +TRANSP:OPAQUE +X-NSCP-ORIGINAL-DTSTART:20101009T070000Z +X-NSCP-LANGUAGE:es +BEGIN:VALARM +ACTION:EMAIL +TRIGGER;VALUE=DATE-TIME:20101009T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +ATTENDEE:MAILTO:luss@example.com +END:VALARM +BEGIN:VALARM +ACTION:X-SUNW-FLASHING +TRIGGER;VALUE=DATE-TIME:20101009T081500Z +SUMMARY:Reminder: Bb1 +DESCRIPTION:Reminder: Bb1 +END:VALARM +X-NSCP-DTSTART-TZID:America/Denver +X-NSCP-TOMBSTONE:0 +X-NSCP-ONGOING:0 +X-NSCP-ORGANIZER-EMAIL:luss@example.com +X-NSCP-GSE-COMPONENT-STATE;X-NSCP-GSE-COMMENT="PUBLISH-COMPLETED":65538 +REQUEST-STATUS:2.0;Success. Store successful. +END:VEVENT +X-NSCP-WCAP-ERRNO:0 +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/USHolidays.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/USHolidays.ics new file mode 100644 index 00000000..c9e0802b --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/USHolidays.ics @@ -0,0 +1,315 @@ +BEGIN:VCALENDAR +VERSION:2.0 +X-WR-CALNAME:US Holidays +PRODID:-//Apple Computer\, Inc//iCal 2.0//EN +X-WR-RELCALID:8F2E90BA-42BA-49C4-8D14-4D4AD8C03A6A +X-WR-TIMEZONE:US/Pacific +CALSCALE:GREGORIAN +METHOD:PUBLISH +BEGIN:VEVENT +DTSTART;VALUE=DATE:20021111 +DTEND;VALUE=DATE:20021112 +SUMMARY:Veteran's Day +UID:133EA24B-078D-4933-8E91-A904FA12037B +DTSTAMP:20050930T224854Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=11 +SEQUENCE:1 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20080323 +DTEND;VALUE=DATE:20080324 +SUMMARY:Easter +UID:B438227F-6E9B-433A-A282-C5D0A7BEBA80 +SEQUENCE:3 +DTSTAMP:20050930T234204Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20060116 +DTEND;VALUE=DATE:20060117 +SUMMARY:Martin Luther King\, Jr. Day +UID:70E24990-753D-4DFC-9053-CB7B4627D0CC +DTSTAMP:20060315T010408Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=1;BYDAY=3MO +SEQUENCE:2 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020415 +DTEND;VALUE=DATE:20020416 +SUMMARY:Tax Day +UID:4A2526B1-DCBB-451E-BB96-878895CA4946 +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=4 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020202 +SUMMARY:Groundhog Day +UID:30366B52-A177-45E6-9C51-70391627BD07 +SEQUENCE:3 +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2 +DURATION:P1D +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020505 +SUMMARY:Cinco de Mayo +UID:1002175C-4916-46B7-97D6-A36922D243EF +SEQUENCE:3 +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=5 +DURATION:P1D +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20070408 +DTEND;VALUE=DATE:20070409 +SUMMARY:Easter +UID:131C076C-6836-4EBA-993A-7CF4880481A5 +SEQUENCE:3 +DTSTAMP:20050930T234143Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020214 +DTEND;VALUE=DATE:20020215 +SUMMARY:Valentine's Day +UID:7694F7CA-1367-47AB-AE20-48054DFB46F9 +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020212 +DTEND;VALUE=DATE:20020213 +SUMMARY:Lincoln's Birthday +UID:00B33BAF-30A0-4C2A-A93A-8236318F8002 +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20021128 +DTEND;VALUE=DATE:20021129 +SUMMARY:Thanksgiving +UID:B1221932-07B5-44DC-902C-FB43EEFFC2C4 +DTSTAMP:20050930T224857Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=11;BYDAY=4TH +SEQUENCE:1 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20030420 +DTEND;VALUE=DATE:20030421 +SUMMARY:Easter +UID:22834865-F6D6-4D27-8477-6722B1F0663D +DTSTAMP:20041015T171054Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20021014 +DTEND;VALUE=DATE:20021015 +SUMMARY:Columbus Day +UID:DDACFF0E-F511-4130-9A32-B161E3899265 +DTSTAMP:20050930T224841Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=10;BYDAY=2MO +SEQUENCE:1 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20041102 +DTEND;VALUE=DATE:20041103 +SUMMARY:Election Day +UID:7068B764-161E-4869-889F-EAC808CD01A2 +RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYMONTHDAY=2,3,4,5,6,7,8 +SEQUENCE:2 +DTSTAMP:20041015T171232Z +DESCRIPTION:Every four years on the first Tuesday after the first Monday + in November +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20040411 +DTEND;VALUE=DATE:20040412 +SUMMARY:Easter +UID:FA46C0F2-D708-4723-8381-DD0B99C4F92D +SEQUENCE:2 +DTSTAMP:20041015T171054Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020704 +DTEND;VALUE=DATE:20020705 +SUMMARY:Independence Day +UID:4D036C0C-48BB-4717-A102-133FC28AD7AF +DTSTAMP:20050930T224803Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=7 +SEQUENCE:1 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020317 +DTEND;VALUE=DATE:20020318 +SUMMARY:St. Patrick's Day +UID:EBC57F03-F2E3-40EB-A1AF-2B0A6E00A72F +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=3 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020401 +SUMMARY:April Fool's Day +UID:7D44FD61-C9EB-416A-BB02-DB78BA88ECCD +SEQUENCE:3 +DTSTAMP:20050930T224649Z +RRULE:FREQ=YEARLY;INTERVAL=1 +DURATION:P1D +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020222 +DTEND;VALUE=DATE:20020223 +SUMMARY:Washington's Birthday +UID:85A26634-913F-464D-AB57-CDBFF9B046D9 +DTSTAMP:20050930T224632Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2 +SEQUENCE:1 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020422 +DTEND;VALUE=DATE:20020423 +SUMMARY:Earth Day +UID:16DBBB89-88C5-426E-9885-AF8D0BA1FBED +SEQUENCE:4 +DTSTAMP:20050930T233002Z +RRULE:FREQ=YEARLY;INTERVAL=1 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020529 +DTEND;VALUE=DATE:20020530 +SUMMARY:John F. Kennedy's Birthday +UID:1B1D5326-A053-40D8-A7E6-950718188862 +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=5 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020407 +DTEND;VALUE=DATE:20020408 +SUMMARY:Daylight Saving Time Begins +UID:7616272F-EF23-48C6-8936-6B170DBAF221 +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=4;BYDAY=1SU +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020616 +DTEND;VALUE=DATE:20020617 +SUMMARY:Father's Day +UID:47E0B303-7718-4F26-BA03-AD248764FA7B +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=6;BYDAY=3SU +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020527 +DTEND;VALUE=DATE:20020528 +SUMMARY:Memorial Day +UID:4EA36692-8E63-4705-9B01-4721A4823ED6 +DTSTAMP:20050930T224747Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=5;BYDAY=-1MO +SEQUENCE:1 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020331 +DTEND;VALUE=DATE:20020401 +SUMMARY:Easter +UID:261D52C3-6415-4DAF-838B-A18F9A5E6480 +DTSTAMP:20041015T171054Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20100404 +SUMMARY:Easter +UID:7A3005A8-1AF2-4E29-9EBB-F568628B68C0 +DTSTAMP:20060203T023643Z +SEQUENCE:1 +DURATION:P1D +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20021027 +DTEND;VALUE=DATE:20021028 +SUMMARY:Daylight Saving Time Ends +UID:AB66D61E-611C-4ADA-9A44-468EBD5B8A80 +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=10;BYDAY=-1SU +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20050327 +DTEND;VALUE=DATE:20050328 +SUMMARY:Easter +UID:93AF6778-6B8A-447C-B16F-5EECFDABDF2F +SEQUENCE:3 +DTSTAMP:20041015T171054Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20060101 +DTEND;VALUE=DATE:20060102 +SUMMARY:New Year's Day +UID:02AB18AF-4AB4-4D7E-9593-36027101F945 +DTSTAMP:20060315T010538Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=1 +SEQUENCE:2 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020902 +DTEND;VALUE=DATE:20020903 +SUMMARY:Labor Day +UID:D5A701A3-4552-4FE5-AA16-8248793DF333 +DTSTAMP:20050930T224817Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=9;BYDAY=1MO +SEQUENCE:1 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020614 +SUMMARY:Flag Day +UID:85631789-1E4C-4BC2-B8EE-8F82E15B1E43 +SEQUENCE:3 +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=6 +DURATION:P1D +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20060416 +DTEND;VALUE=DATE:20060417 +SUMMARY:Easter +UID:E2E9853B-9DA3-4968-882E-E064C6758484 +SEQUENCE:2 +DTSTAMP:20041015T171054Z +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20061031 +DTEND;VALUE=DATE:20061101 +SUMMARY:Halloween +UID:6EBAF33D-C37D-4288-8CDA-F3D0037B7639 +SEQUENCE:4 +DTSTAMP:20060203T022636Z +RRULE:FREQ=YEARLY;INTERVAL=1 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020218 +DTEND;VALUE=DATE:20020219 +SUMMARY:President's Day +UID:6613C392-F3A0-4C71-B6FF-6CFC0247DAB7 +DTSTAMP:20050930T233908Z +SEQUENCE:5 +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=2;BYDAY=3MO +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20021225 +DTEND;VALUE=DATE:20021226 +SUMMARY:Christmas +UID:A9378BE8-50F0-4E9A-9C0E-2C348FC86DAE +DTSTAMP:20050930T231146Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=12 +SEQUENCE:2 +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20090412 +SUMMARY:Easter +UID:3214701E-0EB1-45A7-A8D0-0C834EDA2193 +DTSTAMP:20060203T023620Z +SEQUENCE:1 +DURATION:P1D +END:VEVENT +BEGIN:VEVENT +DTSTART;VALUE=DATE:20020512 +DTEND;VALUE=DATE:20020513 +SUMMARY:Mother's Day +UID:ED5D5811-96FB-4FD1-9AF6-31B0D0311327 +DTSTAMP:20041015T171054Z +RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=5;BYDAY=2SU +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/XProperty1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/XProperty1.ics new file mode 100644 index 00000000..c490d5be --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/XProperty1.ics @@ -0,0 +1,18 @@ +BEGIN:vCalendar +VERSION:2.0 +METHOD:PUBLISH +BEGIN:vEvent +DTSTART:20070604T204500Z +DTEND:20070604T220000Z +UID:1032340718 +DTSTAMP:20070526T030009Z +X-MS-OLK-WKHRDAYS:MO,TU,WE,TH,FR +SUMMARY:Test X-MS-OLK-WKHRDAYS +PRIORITY:3 +BEGIN:vAlarm +TRIGGER:P0DT0H15M +ACTION:DISPLAY +DESCRIPTION:Reminder +END:vAlarm +END:vEvent +END:vCalendar diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/XProperty2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/XProperty2.ics new file mode 100644 index 00000000..bca0b7c1 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Serialization/XProperty2.ics @@ -0,0 +1,39 @@ +BEGIN:VCALENDAR +PRODID:-//Microsoft Corporation//Outlook 12.0 MIMEDIR//EN +VERSION:2.0 +METHOD:PUBLISH +X-CALSTART:20080117T233000Z +X-CALEND:20080118T023000Z +X-WR-RELCALID:{00000018-5612-0D3D-61BA-4310B52A6BD9} +X-WR-CALNAME:Jake Ginnivan +X-PRIMARY-CALENDAR:TRUE +X-OWNER;CN="Jake Ginnivan":mailto:someemail@bla.au +X-MS-OLK-WKHRSTART;TZID="W. Australia Standard Time":080000 +X-MS-OLK-WKHREND;TZID="W. Australia Standard Time":170000 +X-MS-OLK-WKHRDAYS:MO,TU,WE,TH,FR +BEGIN:VTIMEZONE +TZID:W. Australia Standard Time +BEGIN:STANDARD +DTSTART:16010325T030000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 +TZOFFSETFROM:+0900 +TZOFFSETTO:+0800 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:16011028T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:+0800 +TZOFFSETTO:+0900 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +DTEND:20080118T023000Z +DTSTAMP:20080118T064934Z +DTSTART:20080117T233000Z +SEQUENCE:0 +SUMMARY;LANGUAGE=en-au:Busy +TRANSP:OPAQUE +UID:7WBi8EjfdkCM+BY+z9tdhA== +X-MICROSOFT-CDO-BUSYSTATUS:BUSY +END:VEVENT +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo1.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo1.ics new file mode 100644 index 00000000..dc0ba63a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo1.ics @@ -0,0 +1,31 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:NEEDS-ACTION +CLASS:PRIVATE +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo2.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo2.ics new file mode 100644 index 00000000..b56620c8 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo2.ics @@ -0,0 +1,32 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:NEEDS-ACTION +CLASS:PRIVATE +DTSTAMP:20060728T195437Z +DTSTART;TZID=US-Eastern:20060728T090000 +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo3.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo3.ics new file mode 100644 index 00000000..b986e8e7 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo3.ics @@ -0,0 +1,31 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:CANCELLED +CLASS:PRIVATE +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo4.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo4.ics new file mode 100644 index 00000000..1f352ed2 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo4.ics @@ -0,0 +1,32 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:COMPLETED +CLASS:PRIVATE +DTSTART;TZID=US-Eastern:20060728T090000 +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo5.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo5.ics new file mode 100644 index 00000000..e7612c8f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo5.ics @@ -0,0 +1,34 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:COMPLETED +COMPLETED;TZID=US-Eastern:20060730T090000 +CLASS:PRIVATE +DTSTART;TZID=US-Eastern:20060728T090000 +RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo6.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo6.ics new file mode 100644 index 00000000..62dcbf54 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo6.ics @@ -0,0 +1,34 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:COMPLETED +COMPLETED;TZID=US-Eastern:20060804T080000 +CLASS:PRIVATE +DTSTART;TZID=US-Eastern:20060728T090000 +RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo7.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo7.ics new file mode 100644 index 00000000..ecf77c8e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo7.ics @@ -0,0 +1,34 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:COMPLETED +COMPLETED;TZID=US-Eastern:20060804T100000 +CLASS:PRIVATE +DTSTART;TZID=US-Eastern:20060728T090000 +RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo8.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo8.ics new file mode 100644 index 00000000..5ca7cfee --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo8.ics @@ -0,0 +1,34 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:COMPLETED +COMPLETED;TZID=US-Eastern:20070105T100000 +CLASS:PRIVATE +DTSTART;TZID=US-Eastern:20060728T090000 +RRULE:FREQ=MONTHLY;BYDAY=1FR;UNTIL=20071207T160000Z +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo9.ics b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo9.ics new file mode 100644 index 00000000..8be11951 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Calendars/Todo/Todo9.ics @@ -0,0 +1,34 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +BEGIN:VTODO +UID:fed50a1c-1e72-11db-a465-aae271be3660 +SUMMARY:Test Todo +LOCATION:Test +STATUS:COMPLETED +COMPLETED;TZID=US-Eastern:20060825T100000 +CLASS:PRIVATE +DTSTART;TZID=US-Eastern:20060728T090000 +RRULE:FREQ=WEEKLY;INTERVAL=3;UNTIL=20061207T160000Z +DTSTAMP:20060728T195437Z +END:VTODO +BEGIN:VTIMEZONE +TZID:US-Eastern +LAST-MODIFIED:19870101T000000Z +TZURL:http://zones.stds_r_us.net/tz/US-Eastern +BEGIN:STANDARD +DTSTART:19671029T020000 +RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19870405T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +END:VCALENDAR diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/CollectionHelpersTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/CollectionHelpersTests.cs new file mode 100644 index 00000000..2657fb64 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/CollectionHelpersTests.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.DataTypes; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + internal class CollectionHelpersTests + { + private static readonly DateTime _now = DateTime.UtcNow; + private static readonly DateTime _later = _now.AddHours(1); + private static readonly string _uid = Guid.NewGuid().ToString(); + + private static List GetSimpleRecurrenceList() + => new List { new RecurrencePattern(FrequencyType.Daily, 1) { Count = 5 } }; + private static List GetExceptionDates() + => new List { new PeriodList { new Period(new CalDateTime(_now.AddDays(1).Date)) } }; + + [Test] + public void ExDateTests() + { + Assert.AreEqual(GetExceptionDates(), GetExceptionDates()); + Assert.AreNotEqual(GetExceptionDates(), null); + Assert.AreNotEqual(null, GetExceptionDates()); + + var changedPeriod = GetExceptionDates(); + changedPeriod.First().First().StartTime = new CalDateTime(_now.AddHours(-1)); + + Assert.AreNotEqual(GetExceptionDates(), changedPeriod); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/ComponentTest.cs b/net-core/Ical.Net/Ical.Net.UnitTests/ComponentTest.cs new file mode 100644 index 00000000..5e229c8e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/ComponentTest.cs @@ -0,0 +1,19 @@ +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class ComponentTest + { + [Test, Category("Component")] + public void UniqueComponent1() + { + var iCal = new Calendar(); + var evt = iCal.Create(); + + Assert.IsNotNull(evt.Uid); + Assert.IsNull(evt.Created); // We don't want this to be set automatically + Assert.IsNotNull(evt.DtStamp); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/ConcurrentDeserializationTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/ConcurrentDeserializationTests.cs new file mode 100644 index 00000000..3be5486e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/ConcurrentDeserializationTests.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + public class ConcurrentDeserializationTests + { + [Test] + public void ConcurrentDeserialization_Test() + { + // https://github.com/rianjs/ical.net/issues/40 + var calendars = new List + { + IcsFiles.DailyCount2, + IcsFiles.DailyInterval2, + IcsFiles.DailyByDay1, + IcsFiles.RecurrenceDates1, + IcsFiles.DailyByHourMinute1, + }; + + var deserializedCalendars = calendars.AsParallel().SelectMany(c => + { + CalendarCollection calendar; + using (var reader = new StringReader(c ?? string.Empty)) + { + calendar = Calendar.LoadFromStream(reader); + } + return calendar; + }); + + var materialized = deserializedCalendars.ToList(); + Assert.AreEqual(5, materialized.Count); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/CopyTest.cs b/net-core/Ical.Net/Ical.Net.UnitTests/CopyTest.cs new file mode 100644 index 00000000..ffc875d0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/CopyTest.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using Ical.Net.DataTypes; +using Ical.Net.Serialization.iCalendar.Serializers; +using NUnit.Framework; +using NUnit.Framework.Interfaces; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class CopyTest + { + [Test, TestCaseSource(nameof(CopyCalendarTest_TestCases)), Category("Copy tests")] + public void CopyCalendarTest(string calendarString) + { + var iCal1 = Calendar.LoadFromStream(new StringReader(calendarString))[0]; + var iCal2 = iCal1.Copy(); + SerializationTests.CompareCalendars(iCal1, iCal2); + } + + public static IEnumerable CopyCalendarTest_TestCases() + { + yield return new TestCaseData(IcsFiles.Attachment3).SetName("Attachment3"); + yield return new TestCaseData(IcsFiles.Bug2148092).SetName("Bug2148092"); + yield return new TestCaseData(IcsFiles.CaseInsensitive1).SetName("CaseInsensitive1"); + yield return new TestCaseData(IcsFiles.CaseInsensitive2).SetName("CaseInsensitive2"); + yield return new TestCaseData(IcsFiles.CaseInsensitive3).SetName("CaseInsensitive3"); + yield return new TestCaseData(IcsFiles.Categories1).SetName("Categories1"); + yield return new TestCaseData(IcsFiles.Duration1).SetName("Duration1"); + yield return new TestCaseData(IcsFiles.Encoding1).SetName("Encoding1"); + yield return new TestCaseData(IcsFiles.Event1).SetName("Event1"); + yield return new TestCaseData(IcsFiles.Event2).SetName("Event2"); + yield return new TestCaseData(IcsFiles.Event3).SetName("Event3"); + yield return new TestCaseData(IcsFiles.Event4).SetName("Event4"); + yield return new TestCaseData(IcsFiles.GeographicLocation1).SetName("GeographicLocation1"); + yield return new TestCaseData(IcsFiles.Language1).SetName("Language1"); + yield return new TestCaseData(IcsFiles.Language2).SetName("Language2"); + yield return new TestCaseData(IcsFiles.Language3).SetName("Language3"); + yield return new TestCaseData(IcsFiles.TimeZone1).SetName("TimeZone1"); + yield return new TestCaseData(IcsFiles.TimeZone2).SetName("TimeZone2"); + yield return new TestCaseData(IcsFiles.TimeZone3).SetName("TimeZone3"); + yield return new TestCaseData(IcsFiles.XProperty1).SetName("XProperty1"); + yield return new TestCaseData(IcsFiles.XProperty2).SetName("XProperty2"); + } + + private static readonly DateTime _now = DateTime.Now; + private static readonly DateTime _later = _now.AddHours(1); + + private static CalendarEvent GetSimpleEvent() => new CalendarEvent + { + DtStart = new CalDateTime(_now), + DtEnd = new CalDateTime(_later), + Duration = TimeSpan.FromHours(1), + }; + + private static string SerializeEvent(CalendarEvent e) => new CalendarSerializer().SerializeToString(new Calendar { Events = { e } }); + + [Test] + public void EventUid_Tests() + { + var e = GetSimpleEvent(); + e.Uid = "Hello"; + var copy = e.Copy(); + Assert.AreEqual(e.Uid, copy.Uid); + //Assert.IsFalse(ReferenceEquals(e.Uid, copy.Uid)); + + copy.Uid = "Goodbye"; + //Assert.AreNotEqual(e.Uid, copy.Uid); + + const string uidPattern = "UID:"; + var serializedOrig = SerializeEvent(e); + Assert.AreEqual(1, Regex.Matches(serializedOrig, uidPattern).Count); + + var serializedCopy = SerializeEvent(copy); + Assert.AreEqual(1, Regex.Matches(serializedCopy, uidPattern).Count); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/DateTimeSerializerTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/DateTimeSerializerTests.cs new file mode 100644 index 00000000..d1b6d3d6 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/DateTimeSerializerTests.cs @@ -0,0 +1,46 @@ +using System; +using System.Linq; +using Ical.Net.DataTypes; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; +using NUnit.Framework; + +namespace Ical.Net.UnitTests.Serialization.iCalendar.Serializers.DataTypes +{ + [TestFixture] + public class DateTimeSerializerTests + { + [Test, Category("Deserialization")] + public void TZIDPropertyMustNotBeAppliedToUtcDateTime() + { + var ical = new Ical.Net.Calendar(); + var evt = new Ical.Net.CalendarEvent(); + evt.DtStamp = new CalDateTime(new DateTime(2016, 8, 17, 2, 30, 0, DateTimeKind.Utc)); + ical.Events.Add(evt); + + var serializer = new Ical.Net.Serialization.iCalendar.Serializers.CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(ical); + + var lines = serializedCalendar.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries).ToList(); + var result = lines.First(s => s.StartsWith("DTSTAMP")); + Assert.AreEqual("DTSTAMP:20160817T023000Z", result); + } + + [Test, Category("Deserialization")] + public void TZIDPropertyShouldBeAppliedForLocalTimezones() + { + // see http://www.ietf.org/rfc/rfc2445.txt p.36 + var result = DateTimeSerializer() + .SerializeToString( + new CalDateTime(new DateTime(1997, 7, 14, 13, 30, 0, DateTimeKind.Local), "US-Eastern")); + + // TZID is applied elsewhere - just make sure this doesn't have 'Z' appended. + Assert.AreEqual("19970714T133000", result); + } + + + private static DateTimeSerializer DateTimeSerializer() + { + return new DateTimeSerializer(); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/DeserializationTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/DeserializationTests.cs new file mode 100644 index 00000000..be6d6941 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/DeserializationTests.cs @@ -0,0 +1,569 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using antlr; +using Ical.Net.DataTypes; +using Ical.Net.General; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Serialization; +using Ical.Net.Serialization.iCalendar.Serializers; +using Ical.Net.Serialization.iCalendar.Serializers.Other; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class DeserializationTests + { + + [Test, Category("Deserialization")] + public void Attendee1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Attendee1))[0]; + Assert.AreEqual(1, iCal.Events.Count); + + var evt = iCal.Events.First(); + // Ensure there are 2 attendees + Assert.AreEqual(2, evt.Attendees.Count); + + var attendee1 = evt.Attendees[0]; + var attendee2 = evt.Attendees[1]; + + // Values + Assert.AreEqual(new Uri("mailto:joecool@example.com"), attendee1.Value); + Assert.AreEqual(new Uri("mailto:ildoit@example.com"), attendee2.Value); + + // MEMBERS + Assert.AreEqual(1, attendee1.Members.Count); + Assert.AreEqual(0, attendee2.Members.Count); + Assert.AreEqual(new Uri("mailto:DEV-GROUP@example.com"), attendee1.Members[0]); + + // DELEGATED-FROM + Assert.AreEqual(0, attendee1.DelegatedFrom.Count); + Assert.AreEqual(1, attendee2.DelegatedFrom.Count); + Assert.AreEqual(new Uri("mailto:immud@example.com"), attendee2.DelegatedFrom[0]); + + // DELEGATED-TO + Assert.AreEqual(0, attendee1.DelegatedTo.Count); + Assert.AreEqual(0, attendee2.DelegatedTo.Count); + } + + /// + /// Tests that multiple parameters of the + /// same name are correctly aggregated into + /// a single list. + /// + [Test, Category("Deserialization")] + public void Attendee2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Attendee2))[0]; + Assert.AreEqual(1, iCal.Events.Count); + + var evt = iCal.Events.First(); + // Ensure there is 1 attendee + Assert.AreEqual(1, evt.Attendees.Count); + + var attendee1 = evt.Attendees[0]; + + // Values + Assert.AreEqual(new Uri("mailto:joecool@example.com"), attendee1.Value); + + // MEMBERS + Assert.AreEqual(3, attendee1.Members.Count); + Assert.AreEqual(new Uri("mailto:DEV-GROUP@example.com"), attendee1.Members[0]); + Assert.AreEqual(new Uri("mailto:ANOTHER-GROUP@example.com"), attendee1.Members[1]); + Assert.AreEqual(new Uri("mailto:THIRD-GROUP@example.com"), attendee1.Members[2]); + } + + /// + /// Tests that Lotus Notes-style properties are properly handled. + /// https://sourceforge.net/tracker/?func=detail&aid=2033495&group_id=187422&atid=921236 + /// Sourceforge bug #2033495 + /// + [Test, Category("Deserialization")] + public void Bug2033495() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Bug2033495))[0]; + Assert.AreEqual(1, iCal.Events.Count); + Assert.AreEqual(iCal.Properties["X-LOTUS-CHILD_UID"].Value, "XXX"); + } + + /// + /// Tests bug #2938007 - involving the HasTime property in IDateTime values. + /// See https://sourceforge.net/tracker/?func=detail&aid=2938007&group_id=187422&atid=921236 + /// + [Test, Category("Deserialization")] + public void Bug2938007() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Bug2938007))[0]; + Assert.AreEqual(1, iCal.Events.Count); + + var evt = iCal.Events.First(); + Assert.AreEqual(true, evt.Start.HasTime); + Assert.AreEqual(true, evt.End.HasTime); + + foreach (var o in evt.GetOccurrences(new CalDateTime(2010, 1, 17, 0, 0, 0), new CalDateTime(2010, 2, 1, 0, 0, 0))) + { + Assert.AreEqual(true, o.Period.StartTime.HasTime); + Assert.AreEqual(true, o.Period.EndTime.HasTime); + } + } + + /// + /// Tests bug #3177278 - Serialize closes stream + /// See https://sourceforge.net/tracker/?func=detail&aid=3177278&group_id=187422&atid=921236 + /// + [Test, Category("Deserialization")] + public void Bug3177278() + { + var calendar = new Calendar(); + var serializer = new CalendarSerializer(); + + var ms = new MemoryStream(); + serializer.Serialize(calendar, ms, Encoding.UTF8); + + Assert.IsTrue(ms.CanWrite); + } + + /// + /// Tests that a mixed-case VERSION property is loaded properly + /// + [Test, Category("Deserialization")] + public void CaseInsensitive4() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.CaseInsensitive4))[0]; + Assert.AreEqual("2.5", iCal.Version); + } + + [Test, Category("Deserialization")] + public void Categories1_2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Categories1))[0]; + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); + + var items = new List(); + items.AddRange(new[] + { + "One", "Two", "Three", + "Four", "Five", "Six", + "Seven", "A string of text with nothing less than a comma, semicolon; and a newline\n." + }); + + var found = new Dictionary(); + foreach (var s in evt.Categories.Where(s => items.Contains(s))) + { + found[s] = true; + } + + foreach (string item in items) + Assert.IsTrue(found.ContainsKey(item), "Event should contain CATEGORY '" + item + "', but it was not found."); + } + + [Test, Category("Deserialization")] + public void EmptyLines1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.EmptyLines1))[0]; + Assert.AreEqual(2, iCal.Events.Count, "iCalendar should have 2 events"); + } + + [Test, Category("Deserialization")] + public void EmptyLines2() + { + var calendars = Calendar.LoadFromStream(new StringReader(IcsFiles.EmptyLines2)); + Assert.AreEqual(2, calendars.Count); + Assert.AreEqual(2, calendars[0].Events.Count, "iCalendar should have 2 events"); + Assert.AreEqual(2, calendars[1].Events.Count, "iCalendar should have 2 events"); + } + + /// + /// Verifies that blank lines between components are allowed + /// (as occurs with some applications/parsers - i.e. KOrganizer) + /// + [Test, Category("Deserialization")] + public void EmptyLines3() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.EmptyLines3))[0]; + Assert.AreEqual(1, iCal.Todos.Count, "iCalendar should have 1 todo"); + } + + /// + /// Similar to PARSE4 and PARSE5 tests. + /// + [Test, Category("Deserialization")] + public void EmptyLines4() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.EmptyLines4))[0]; + Assert.AreEqual(28, iCal.Events.Count); + } + + [Test] + public void Encoding2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Encoding2))[0]; + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); + + Assert.AreEqual( +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.\r\n" + +"This is a test to try out base64 encoding without being too large.", + evt.Attachments[0].ToString(), + "Attached value does not match."); + } + + [Test] + public void Encoding3() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Encoding3))[0]; + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); + + Assert.AreEqual("uuid1153170430406", evt.Uid, "UID should be 'uuid1153170430406'; it is " + evt.Uid); + Assert.AreEqual(1, evt.Sequence, "SEQUENCE should be 1; it is " + evt.Sequence); + } + + [Test, Category("Deserialization")] + public void Event8() + { + var sr = new StringReader(@"BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Apple Computer\, Inc//iCal 1.0//EN +CALSCALE:GREGORIAN +BEGIN:VEVENT +CREATED:20070404T211714Z +DTEND:20070407T010000Z +DTSTAMP:20070404T211714Z +DTSTART:20070406T230000Z +DURATION:PT2H +RRULE:FREQ=WEEKLY;UNTIL=20070801T070000Z;BYDAY=FR +SUMMARY:Friday Meetings +DTSTAMP:20040103T033800Z +SEQUENCE:1 +UID:fd940618-45e2-4d19-b118-37fd7a8e3906 +END:VEVENT +BEGIN:VEVENT +CREATED:20070404T204310Z +DTEND:20070416T030000Z +DTSTAMP:20070404T204310Z +DTSTART:20070414T200000Z +DURATION:P1DT7H +RRULE:FREQ=DAILY;COUNT=12;BYDAY=SA,SU +SUMMARY:Weekend Yea! +DTSTAMP:20040103T033800Z +SEQUENCE:1 +UID:ebfbd3e3-cc1e-4a64-98eb-ced2598b3908 +END:VEVENT +END:VCALENDAR +"); + var iCal = Calendar.LoadFromStream(sr)[0]; + Assert.IsTrue(iCal.Events.Count == 2, "There should be 2 events in the parsed calendar"); + Assert.IsNotNull(iCal.Events["fd940618-45e2-4d19-b118-37fd7a8e3906"], "Event fd940618-45e2-4d19-b118-37fd7a8e3906 should exist in the calendar"); + Assert.IsNotNull(iCal.Events["ebfbd3e3-cc1e-4a64-98eb-ced2598b3908"], "Event ebfbd3e3-cc1e-4a64-98eb-ced2598b3908 should exist in the calendar"); + } + + [Test] + public void GeographicLocation1_2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.GeographicLocation1))[0]; + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); + + Assert.AreEqual(37.386013, evt.GeographicLocation.Latitude, "Latitude should be 37.386013; it is not."); + Assert.AreEqual(-122.082932, evt.GeographicLocation.Longitude, "Longitude should be -122.082932; it is not."); + } + + [Test, Category("Deserialization")] + public void Google1() + { + var tzId = "Europe/Berlin"; + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Google1))[0]; + var evt = iCal.Events["594oeajmftl3r9qlkb476rpr3c@google.com"]; + Assert.IsNotNull(evt); + + IDateTime dtStart = new CalDateTime(2006, 12, 18, tzId); + IDateTime dtEnd = new CalDateTime(2006, 12, 23, tzId); + var occurrences = iCal.GetOccurrences(dtStart, dtEnd).OrderBy(o => o.Period.StartTime).ToList(); + + var dateTimes = new[] + { + new CalDateTime(2006, 12, 18, 7, 0, 0, tzId), + new CalDateTime(2006, 12, 19, 7, 0, 0, tzId), + new CalDateTime(2006, 12, 20, 7, 0, 0, tzId), + new CalDateTime(2006, 12, 21, 7, 0, 0, tzId), + new CalDateTime(2006, 12, 22, 7, 0, 0, tzId) + }; + + for (var i = 0; i < dateTimes.Length; i++) + Assert.AreEqual(dateTimes[i], occurrences[i].Period.StartTime, "Event should occur at " + dateTimes[i]); + + Assert.AreEqual(dateTimes.Length, occurrences.Count, "There should be exactly " + dateTimes.Length + " occurrences; there were " + occurrences.Count); + } + + /// + /// Tests that valid RDATE properties are parsed correctly. + /// + [Test, Category("Deserialization")] + public void RecurrenceDates1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.RecurrenceDates1))[0]; + Assert.AreEqual(1, iCal.Events.Count); + Assert.AreEqual(3, iCal.Events.First().RecurrenceDates.Count); + + Assert.AreEqual((CalDateTime)new DateTime(1997, 7, 14, 12, 30, 0, DateTimeKind.Utc), iCal.Events.First().RecurrenceDates[0][0].StartTime); + Assert.AreEqual((CalDateTime)new DateTime(1996, 4, 3, 2, 0, 0, DateTimeKind.Utc), iCal.Events.First().RecurrenceDates[1][0].StartTime); + Assert.AreEqual((CalDateTime)new DateTime(1996, 4, 3, 4, 0, 0, DateTimeKind.Utc), iCal.Events.First().RecurrenceDates[1][0].EndTime); + Assert.AreEqual(new CalDateTime(1997, 1, 1), iCal.Events.First().RecurrenceDates[2][0].StartTime); + Assert.AreEqual(new CalDateTime(1997, 1, 20), iCal.Events.First().RecurrenceDates[2][1].StartTime); + Assert.AreEqual(new CalDateTime(1997, 2, 17), iCal.Events.First().RecurrenceDates[2][2].StartTime); + Assert.AreEqual(new CalDateTime(1997, 4, 21), iCal.Events.First().RecurrenceDates[2][3].StartTime); + Assert.AreEqual(new CalDateTime(1997, 5, 26), iCal.Events.First().RecurrenceDates[2][4].StartTime); + Assert.AreEqual(new CalDateTime(1997, 7, 4), iCal.Events.First().RecurrenceDates[2][5].StartTime); + Assert.AreEqual(new CalDateTime(1997, 9, 1), iCal.Events.First().RecurrenceDates[2][6].StartTime); + Assert.AreEqual(new CalDateTime(1997, 10, 14), iCal.Events.First().RecurrenceDates[2][7].StartTime); + Assert.AreEqual(new CalDateTime(1997, 11, 28), iCal.Events.First().RecurrenceDates[2][8].StartTime); + Assert.AreEqual(new CalDateTime(1997, 11, 29), iCal.Events.First().RecurrenceDates[2][9].StartTime); + Assert.AreEqual(new CalDateTime(1997, 12, 25), iCal.Events.First().RecurrenceDates[2][10].StartTime); + } + + /// + /// Tests that valid REQUEST-STATUS properties are parsed correctly. + /// + [Test, Category("Deserialization")] + public void RequestStatus1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.RequestStatus1))[0]; + Assert.AreEqual(1, iCal.Events.Count); + Assert.AreEqual(4, iCal.Events.First().RequestStatuses.Count); + + var rs = iCal.Events.First().RequestStatuses[0]; + Assert.AreEqual(2, rs.StatusCode.Primary); + Assert.AreEqual(0, rs.StatusCode.Secondary); + Assert.AreEqual("Success", rs.Description); + Assert.IsNull(rs.ExtraData); + + rs = iCal.Events.First().RequestStatuses[1]; + Assert.AreEqual(3, rs.StatusCode.Primary); + Assert.AreEqual(1, rs.StatusCode.Secondary); + Assert.AreEqual("Invalid property value", rs.Description); + Assert.AreEqual("DTSTART:96-Apr-01", rs.ExtraData); + + rs = iCal.Events.First().RequestStatuses[2]; + Assert.AreEqual(2, rs.StatusCode.Primary); + Assert.AreEqual(8, rs.StatusCode.Secondary); + Assert.AreEqual(" Success, repeating event ignored. Scheduled as a single event.", rs.Description); + Assert.AreEqual("RRULE:FREQ=WEEKLY;INTERVAL=2", rs.ExtraData); + + rs = iCal.Events.First().RequestStatuses[3]; + Assert.AreEqual(4, rs.StatusCode.Primary); + Assert.AreEqual(1, rs.StatusCode.Secondary); + Assert.AreEqual("Event conflict. Date/time is busy.", rs.Description); + Assert.IsNull(rs.ExtraData); + } + + /// + /// Tests that string escaping works with Text elements. + /// + [Test, Category("Deserialization")] + public void String2() + { + var serializer = new StringSerializer(); + var value = @"test\with\;characters"; + var unescaped = (string)serializer.Deserialize(new StringReader(value)); + + Assert.AreEqual(@"test\with;characters", unescaped, "String unescaping was incorrect."); + + value = @"C:\Path\To\My\New\Information"; + unescaped = (string)serializer.Deserialize(new StringReader(value)); + Assert.AreEqual("C:\\Path\\To\\My\new\\Information", unescaped, "String unescaping was incorrect."); + + value = @"\""This\r\nis\Na\, test\""\;\\;,"; + unescaped = (string)serializer.Deserialize(new StringReader(value)); + + Assert.AreEqual("\"This\\r\nis\na, test\";\\;,", unescaped, "String unescaping was incorrect."); + } + + [Test, Category("Deserialization")] + public void Transparency2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Transparency2))[0]; + + Assert.AreEqual(1, iCal.Events.Count); + var evt = iCal.Events.First(); + + Assert.AreEqual(TransparencyType.Transparent, evt.Transparency); + } + + /// + /// Tests that DateTime values that are out-of-range are still parsed correctly + /// and set to the closest representable date/time in .NET. + /// + [Test, Category("Deserialization")] + public void DateTime1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.DateTime1))[0]; + Assert.AreEqual(6, iCal.Events.Count); + + var evt = iCal.Events["nc2o66s0u36iesitl2l0b8inn8@google.com"]; + Assert.IsNotNull(evt); + + // The "Created" date is out-of-bounds. It should be coerced to the + // closest representable date/time. + Assert.AreEqual(DateTime.MinValue, evt.Created.Value); + } + + [Test, Category("Deserialization")] + public void Language4() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Language4))[0]; + Assert.IsNotNull(iCal); + } + + [Test, Category("Deserialization")] + public void Outlook2007_LineFolds1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Outlook2007LineFolds))[0]; + var events = iCal.GetOccurrences(new CalDateTime(2009, 06, 20), new CalDateTime(2009, 06, 22)); + Assert.AreEqual(1, events.Count); + } + + [Test, Category("Deserialization")] + public void Outlook2007_LineFolds2() + { + var longName = "The Exceptionally Long Named Meeting Room Whose Name Wraps Over Several Lines When Exported From Leading Calendar and Office Software Application Microsoft Office 2007"; + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Outlook2007LineFolds))[0]; + var events = iCal.GetOccurrences(new CalDateTime(2009, 06, 20), new CalDateTime(2009, 06, 22)).OrderBy(o => o.Period.StartTime).ToList(); + Assert.AreEqual(longName, ((CalendarEvent)events[0].Source).Location); + } + + /// + /// Tests that multiple parameters are allowed in iCalObjects + /// + [Test, Category("Deserialization")] + public void Parameter1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Parameter1))[0]; + + var evt = iCal.Events.First(); + IList parms = evt.Properties["DTSTART"].Parameters.AllOf("VALUE").ToList(); + Assert.AreEqual(2, parms.Count); + Assert.AreEqual("DATE", parms[0].Values.First()); + Assert.AreEqual("OTHER", parms[1].Values.First()); + } + + /// + /// Tests that empty parameters are allowed in iCalObjects + /// + [Test, Category("Deserialization")] + public void Parameter2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Parameter2))[0]; + Assert.AreEqual(2, iCal.Events.Count); + } + + /// + /// Tests a calendar that should fail to properly parse. + /// + [Test, Category("Deserialization")] + public void Parse1() + { + try + { + var content = IcsFiles.Parse1; + var iCal = Calendar.LoadFromStream(new StringReader(content))[0]; + Assert.IsNotNull(iCal); + } + catch (Exception e) + { + Assert.IsInstanceOf(e); + } + } + + /// + /// Tests that multiple properties are allowed in iCalObjects + /// + [Test, Category("Deserialization")] + public void Property1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Property1))[0]; + + IList props = iCal.Properties.AllOf("VERSION").ToList(); + Assert.AreEqual(2, props.Count); + + for (var i = 0; i < props.Count; i++) + Assert.AreEqual("2." + i, props[i].Value); + } + + /// + /// Tests that line/column numbers are correctly tracked for + /// parsed (deserialized) calendars. + /// + [Test, Category("Deserialization")] + public void LineColumns1() + { + var ctx = new SerializationContext(); + + var settings = ctx.GetService(typeof(ISerializationSettings)) as ISerializationSettings; + settings.EnsureAccurateLineNumbers = true; + + var serializer = new CalendarSerializer + { + SerializationContext = ctx + }; + + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.EmptyLines1), serializer)[0]; + + Assert.AreEqual(2, iCal.Events.Count); + Assert.AreEqual(4, iCal.Events.First().Line); + Assert.AreEqual(18, iCal.Events[1].Line); + Assert.AreEqual(5, iCal.Events.First().Properties["CREATED"].Line); + Assert.AreEqual(6, iCal.Events.First().Properties["LAST-MODIFIED"].Line); + Assert.AreEqual(7, iCal.Events.First().Properties["DTSTAMP"].Line); + Assert.AreEqual(8, iCal.Events.First().Properties["UID"].Line); + Assert.AreEqual(9, iCal.Events.First().Properties["SUMMARY"].Line); + Assert.AreEqual(10, iCal.Events.First().Properties["CLASS"].Line); + Assert.AreEqual(11, iCal.Events.First().Properties["DTSTART"].Line); + Assert.AreEqual(12, iCal.Events.First().Properties["DTEND"].Line); + Assert.AreEqual(13, iCal.Events.First().Properties["CATEGORIES"].Line); + Assert.AreEqual(14, iCal.Events.First().Properties["X-MOZILLA-ALARM-DEFAULT-LENGTH"].Line); + Assert.AreEqual(15, iCal.Events.First().Properties["LOCATION"].Line); + } + + /// + /// Tests that line/column numbers are correctly tracked for + /// parsed (deserialized) calendars. + /// + [Test, Category("Deserialization")] + public void LineColumns2() + { + var ctx = new SerializationContext(); + + var settings = ctx.GetService(typeof(ISerializationSettings)) as ISerializationSettings; + settings.EnsureAccurateLineNumbers = true; + + var serializer = new CalendarSerializer + { + SerializationContext = ctx + }; + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Calendar1), serializer)[0]; + + Assert.IsNotNull(iCal.Todos["2df60496-1e73-11db-ba96-e3cfe6793b5f"]); + Assert.IsNotNull(iCal.Todos["4836c236-1e75-11db-835f-a024e2a6131f"]); + Assert.AreEqual(110, iCal.Todos["4836c236-1e75-11db-835f-a024e2a6131f"].Properties["LOCATION"].Line); + Assert.AreEqual(123, iCal.Todos["2df60496-1e73-11db-ba96-e3cfe6793b5f"].Properties["UID"].Line); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/DocumentationExamples.cs b/net-core/Ical.Net/Ical.Net.UnitTests/DocumentationExamples.cs new file mode 100644 index 00000000..2973cf86 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/DocumentationExamples.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using Ical.Net.DataTypes; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + public class DocumentationExamples + { + [Test] + public void Daily_Test() + { + // The first instance of an event taking place on July 1, 2016 between 07:00 and 08:00. + // We want it to recur through the end of July. + var vEvent = new CalendarEvent + { + DtStart = new CalDateTime(DateTime.Parse("2016-07-01T07:00")), + DtEnd = new CalDateTime(DateTime.Parse("2016-07-01T08:00")), + }; + + //Recur daily through the end of the day, July 31, 2016 + var recurrenceRule = new RecurrencePattern(FrequencyType.Daily, 1) + { + Until = DateTime.Parse("2016-07-31T11:59:59") + }; + + vEvent.RecurrenceRules = new List {recurrenceRule}; + var calendar = new Calendar(); + calendar.Events.Add(vEvent); + + + // Count the occurrences between July 20, and Aug 5 -- there should be 12: + // July 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 + var searchStart = DateTime.Parse("2016-07-20"); + var searchEnd = DateTime.Parse("2016-08-05"); + var occurrences = calendar.GetOccurrences(searchStart, searchEnd); + Assert.AreEqual(12, occurrences.Count); + } + + [Test] + public void EveryOtherTuesdayUntilTheEndOfTheYear_Test() + { + // An event taking place between 07:00 and 08:00, beginning July 5 (a Tuesday) + var vEvent = new CalendarEvent + { + DtStart = new CalDateTime(DateTime.Parse("2016-07-05T07:00")), + DtEnd = new CalDateTime(DateTime.Parse("2016-07-05T08:00")), + }; + + // Recurring every other Tuesday until Dec 31 + var rrule = new RecurrencePattern(FrequencyType.Weekly, 2) + { + Until = DateTime.Parse("2016-12-31T11:59:59") + }; + vEvent.RecurrenceRules = new List { rrule }; + + // Count every other Tuesday between July 1 and Dec 31. + // The first Tuesday is July 5. There should be 13 in total + var searchStart = DateTime.Parse("2010-01-01"); + var searchEnd = DateTime.Parse("2016-12-31"); + var tuesdays = vEvent.GetOccurrences(searchStart, searchEnd); + + Assert.AreEqual(13, tuesdays.Count); + } + + [Test] + public void FourthThursdayOfNovember_Tests() + { + // (The number of US thanksgivings between 2000 and 2016) + // An event taking place between 07:00 and 19:00, beginning July 5 (a Tuesday) + var vEvent = new CalendarEvent + { + DtStart = new CalDateTime(DateTime.Parse("2000-11-23T07:00")), + DtEnd = new CalDateTime(DateTime.Parse("2000-11-23T19:00")), + }; + + // Recurring every other Tuesday until Dec 31 + var rrule = new RecurrencePattern(FrequencyType.Yearly, 1) + { + Frequency = FrequencyType.Yearly, + Interval = 1, + ByMonth = new List { 11 }, + ByDay = new List { new WeekDay { DayOfWeek = DayOfWeek.Thursday, Offset = 4 } }, + Until = DateTime.MaxValue + }; + vEvent.RecurrenceRules = new List { rrule }; + + var searchStart = DateTime.Parse("2000-01-01"); + var searchEnd = DateTime.Parse("2017-01-01"); + var usThanksgivings = vEvent.GetOccurrences(searchStart, searchEnd); + + Assert.AreEqual(17, usThanksgivings.Count); + foreach (var thanksgiving in usThanksgivings) + { + Assert.IsTrue(thanksgiving.Period.StartTime.DayOfWeek == DayOfWeek.Thursday); + } + } + + [Test] + public void DailyExceptSunday_Test() + { + //An event that happens daily through 2016, except for Sundays + var vEvent = new CalendarEvent + { + DtStart = new CalDateTime(DateTime.Parse("2016-01-01T07:00")), + DtEnd = new CalDateTime(DateTime.Parse("2016-12-31T08:00")), + RecurrenceRules = new List { new RecurrencePattern(FrequencyType.Daily, 1)}, + }; + + //Define the exceptions: Sunday + var exceptionRule = new RecurrencePattern(FrequencyType.Weekly, 1) + { + ByDay = new List { new WeekDay(DayOfWeek.Sunday) } + }; + vEvent.ExceptionRules = new List {exceptionRule}; + + var calendar = new Calendar(); + calendar.Events.Add(vEvent); + + // We are essentially counting all the days that aren't Sunday in 2016, so there should be 314 + var searchStart = DateTime.Parse("2015-12-31"); + var searchEnd = DateTime.Parse("2017-01-01"); + var occurrences = calendar.GetOccurrences(searchStart, searchEnd); + Assert.AreEqual(314, occurrences.Count); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/EqualityAndHashingTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/EqualityAndHashingTests.cs new file mode 100644 index 00000000..8dc92608 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/EqualityAndHashingTests.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Ical.Net.DataTypes; +using Ical.Net.Serialization.iCalendar.Serializers; +using Ical.Net.UnitTests.ExtensionMethods; +using NUnit.Framework; +using NUnit.Framework.Interfaces; + +namespace Ical.Net.UnitTests +{ + public class EqualityAndHashingTests + { + private const string _someTz = "America/Los_Angeles"; + private static readonly DateTime _nowTime = DateTime.Parse("2016-07-16T16:47:02.9310521-04:00"); + private static readonly DateTime _later = _nowTime.AddHours(1); + + [Test, TestCaseSource(nameof(CalDateTime_TestCases))] + public void CalDateTime_Tests(CalDateTime incomingDt, CalDateTime expectedDt) + { + Assert.AreEqual(incomingDt.Value, expectedDt.Value); + Assert.AreEqual(incomingDt.GetHashCode(), expectedDt.GetHashCode()); + Assert.AreEqual(incomingDt.TzId, expectedDt.TzId); + Assert.IsTrue(incomingDt.Equals(expectedDt)); + } + + public static IEnumerable CalDateTime_TestCases() + { + var nowCalDt = new CalDateTime(_nowTime); + yield return new TestCaseData(nowCalDt, new CalDateTime(_nowTime)).SetName("Now, no time zone"); + + var nowCalDtWithTz = new CalDateTime(_nowTime, _someTz); + yield return new TestCaseData(nowCalDtWithTz, new CalDateTime(_nowTime, _someTz)).SetName("Now, with time zone"); + } + + [Test] + public void RecurrencePatternTests() + { + var patternA = GetSimpleRecurrencePattern(); + var patternB = GetSimpleRecurrencePattern(); + + Assert.AreEqual(patternA, patternB); + Assert.AreEqual(patternA.GetHashCode(), patternB.GetHashCode()); + } + + [Test, TestCaseSource(nameof(Event_TestCases))] + public void Event_Tests(CalendarEvent incoming, CalendarEvent expected) + { + Assert.AreEqual(incoming.DtStart, expected.DtStart); + Assert.AreEqual(incoming.DtEnd, expected.DtEnd); + Assert.AreEqual(incoming.Location, expected.Location); + Assert.AreEqual(incoming.Status, expected.Status); + Assert.AreEqual(incoming.IsActive(), expected.IsActive()); + Assert.AreEqual(incoming.Duration, expected.Duration); + Assert.AreEqual(incoming.Transparency, expected.Transparency); + Assert.AreEqual(incoming.GetHashCode(), expected.GetHashCode()); + Assert.IsTrue(incoming.Equals(expected)); + } + + private static RecurrencePattern GetSimpleRecurrencePattern() => new RecurrencePattern(FrequencyType.Daily, 1) + { + Count = 5 + }; + + private static CalendarEvent GetSimpleEvent() => new CalendarEvent + { + DtStart = new CalDateTime(_nowTime), + DtEnd = new CalDateTime(_later), + Duration = TimeSpan.FromHours(1), + }; + + private static string SerializeEvent(CalendarEvent e) => new CalendarSerializer().SerializeToString(new Calendar { Events = { e } }); + + + public static IEnumerable Event_TestCases() + { + var outgoing = GetSimpleEvent(); + var expected = GetSimpleEvent(); + yield return new TestCaseData(outgoing, expected).SetName("Events with start, end, and duration"); + + var fiveA = GetSimpleRecurrencePattern(); + var fiveB = GetSimpleRecurrencePattern(); + + outgoing = GetSimpleEvent(); + expected = GetSimpleEvent(); + outgoing.RecurrenceRules = new List { fiveA }; + expected.RecurrenceRules = new List { fiveB }; + yield return new TestCaseData(outgoing, expected).SetName("Events with start, end, duration, and one recurrence rule"); + } + + [Test] + public void Calendar_Tests() + { + var rruleA = new RecurrencePattern(FrequencyType.Daily, 1) + { + Count = 5 + }; + + var e = new CalendarEvent + { + DtStart = new CalDateTime(_nowTime), + DtEnd = new CalDateTime(_later), + Duration = TimeSpan.FromHours(1), + RecurrenceRules = new List { rruleA }, + }; + + var actualCalendar = new Calendar(); + actualCalendar.Events.Add(e); + + //Work around referential equality... + var rruleB = new RecurrencePattern(FrequencyType.Daily, 1) + { + Count = 5 + }; + + var expectedCalendar = new Calendar(); + expectedCalendar.Events.Add(new CalendarEvent + { + DtStart = new CalDateTime(_nowTime), + DtEnd = new CalDateTime(_later), + Duration = TimeSpan.FromHours(1), + RecurrenceRules = new List { rruleB }, + }); + + Assert.AreEqual(actualCalendar.GetHashCode(), expectedCalendar.GetHashCode()); + Assert.IsTrue(actualCalendar.Equals(expectedCalendar)); + } + + [Test, TestCaseSource(nameof(VTimeZone_TestCases))] + public void VTimeZone_Tests(VTimeZone actual, VTimeZone expected) + { + Assert.AreEqual(actual.Url, expected.Url); + Assert.AreEqual(actual.TzId, expected.TzId); + Assert.AreEqual(actual, expected); + Assert.AreEqual(actual.GetHashCode(), expected.GetHashCode()); + } + + public static IEnumerable VTimeZone_TestCases() + { + var first = new VTimeZone + { + TzId = "New Zealand Standard Time" + }; + + var second = new VTimeZone("New Zealand Standard Time"); + yield return new TestCaseData(first, second); + + first.Url = new Uri("http://example.com/"); + second.Url = new Uri("http://example.com"); + yield return new TestCaseData(first, second); + } + + [Test, TestCaseSource(nameof(Attendees_TestCases))] + public void Attendees_Tests(Attendee actual, Attendee expected) + { + Assert.AreEqual(expected.GetHashCode(), actual.GetHashCode()); + Assert.AreEqual(expected, actual); + } + + public static IEnumerable Attendees_TestCases() + { + var tentative1 = new Attendee("MAILTO:james@example.com") + { + CommonName = "James Tentative", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Tentative + }; + var tentative2 = new Attendee("MAILTO:james@example.com") + { + CommonName = "James Tentative", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Tentative + }; + yield return new TestCaseData(tentative1, tentative2).SetName("Simple attendee test case"); + + var complex1 = new Attendee("MAILTO:mary@example.com") + { + CommonName = "Mary Accepted", + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Accepted, + SentBy = new Uri("mailto:someone@example.com"), + DirectoryEntry = new Uri("ldap://example.com:6666/o=eDABC Industries,c=3DUS??(cn=3DBMary Accepted)"), + Type = "CuType", + Members = new List { "Group A", "Group B"}, + Role = ParticipationRole.Chair, + DelegatedTo = new List { "Peon A", "Peon B"}, + DelegatedFrom = new List { "Bigwig A", "Bigwig B"} + }; + var complex2 = new Attendee("MAILTO:mary@example.com") + { + CommonName = "Mary Accepted", + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Accepted, + SentBy = new Uri("mailto:someone@example.com"), + DirectoryEntry = new Uri("ldap://example.com:6666/o=eDABC Industries,c=3DUS??(cn=3DBMary Accepted)"), + Type = "CuType", + Members = new List { "Group A", "Group B" }, + Role = ParticipationRole.Chair, + DelegatedTo = new List { "Peon A", "Peon B" }, + DelegatedFrom = new List { "Bigwig A", "Bigwig B" } + }; + yield return new TestCaseData(complex1, complex2).SetName("Complex attendee test"); + } + + [Test, TestCaseSource(nameof(CalendarCollection_TestCases))] + public void CalendarCollection_Tests(string rawCalendar) + { + var a = Calendar.LoadFromStream(new StringReader(IcsFiles.USHolidays)); + var b = Calendar.LoadFromStream(new StringReader(IcsFiles.USHolidays)); + + Assert.IsNotNull(a); + Assert.IsNotNull(b); + Assert.AreEqual(a.GetHashCode(), b.GetHashCode()); + Assert.AreEqual(a, b); + } + + public static IEnumerable CalendarCollection_TestCases() + { + yield return new TestCaseData(IcsFiles.Google1).SetName("Google calendar test case"); + yield return new TestCaseData(IcsFiles.Parse1).SetName("Weird file parse test case"); + yield return new TestCaseData(IcsFiles.USHolidays).SetName("US Holidays (quite large)"); + } + + [Test] + public void Resources_Tests() + { + var origContents = new[] { "Foo", "Bar" }; + var e = GetSimpleEvent(); + e.Resources = new List(origContents); + Assert.IsTrue(e.Resources.Count == 2); + + e.Resources.Add("Baz"); + Assert.IsTrue(e.Resources.Count == 3); + var serialized = SerializeEvent(e); + Assert.IsTrue(serialized.Contains("Baz")); + + e.Resources.Remove("Baz"); + Assert.IsTrue(e.Resources.Count == 2); + serialized = SerializeEvent(e); + Assert.IsFalse(serialized.Contains("Baz")); + + e.Resources.Add("Hello"); + Assert.IsTrue(e.Resources.Contains("Hello")); + serialized = SerializeEvent(e); + Assert.IsTrue(serialized.Contains("Hello")); + + e.Resources.Clear(); + e.Resources.AddRange(origContents); + CollectionAssert.AreEquivalent(e.Resources, origContents); + serialized = SerializeEvent(e); + Assert.IsTrue(serialized.Contains("Foo")); + Assert.IsTrue(serialized.Contains("Bar")); + Assert.IsFalse(serialized.Contains("Baz")); + Assert.IsFalse(serialized.Contains("Hello")); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/FreeBusyTest.cs b/net-core/Ical.Net/Ical.Net.UnitTests/FreeBusyTest.cs new file mode 100644 index 00000000..1ca4eec0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/FreeBusyTest.cs @@ -0,0 +1,29 @@ +using Ical.Net.DataTypes; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class FreeBusyTest + { + /// + /// Ensures that GetFreeBusyStatus() return the correct status. + /// + [Test, Category("FreeBusy")] + public void GetFreeBusyStatus1() + { + Calendar cal = new Calendar(); + + CalendarEvent evt = cal.Create(); + evt.Summary = "Test event"; + evt.Start = new CalDateTime(2010, 10, 1, 8, 0, 0); + evt.End = new CalDateTime(2010, 10, 1, 9, 0, 0); + + var freeBusy = cal.GetFreeBusy(new CalDateTime(2010, 10, 1, 0, 0, 0), new CalDateTime(2010, 10, 7, 11, 59, 59)); + Assert.AreEqual(FreeBusyStatus.Free, freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 7, 59, 59))); + Assert.AreEqual(FreeBusyStatus.Busy, freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 8, 0, 0))); + Assert.AreEqual(FreeBusyStatus.Busy, freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 8, 59, 59))); + Assert.AreEqual(FreeBusyStatus.Free, freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 9, 0, 0))); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/GetOccurrenceTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/GetOccurrenceTests.cs new file mode 100644 index 00000000..cfd18089 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/GetOccurrenceTests.cs @@ -0,0 +1,163 @@ +using System; +using System.IO; +using System.Linq; +using Ical.Net.DataTypes; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + internal class GetOccurrenceTests + { + public static CalendarCollection GetCalendars(string incoming) => Calendar.LoadFromStream(new StringReader(incoming)); + + [Test] + public void WrongDurationTest() + { + var firstStart = new CalDateTime(DateTime.Parse("2016-01-01")); + var firstEnd = new CalDateTime(DateTime.Parse("2016-01-05")); + var vEvent = new CalendarEvent {DtStart = firstStart, DtEnd = firstEnd,}; + + var secondStart = new CalDateTime(DateTime.Parse("2016-03-01")); + var secondEnd = new CalDateTime(DateTime.Parse("2016-03-05")); + var vEvent2 = new CalendarEvent {DtStart = secondStart, DtEnd = secondEnd,}; + + var calendar = new Calendar(); + calendar.Events.Add(vEvent); + calendar.Events.Add(vEvent2); + + var searchStart = DateTime.Parse("2015-12-29"); + var searchEnd = DateTime.Parse("2017-02-10"); + var occurrences = calendar.GetOccurrences(searchStart, searchEnd).OrderBy(o => o.Period.StartTime).ToList(); + + var firstOccurrence = occurrences.First(); + var firstStartCopy = firstStart.Copy(); + var firstEndCopy = firstEnd.Copy(); + Assert.AreEqual(firstStartCopy, firstOccurrence.Period.StartTime); + Assert.AreEqual(firstEndCopy, firstOccurrence.Period.EndTime); + + var secondOccurrence = occurrences.Last(); + var secondStartCopy = secondStart.Copy(); + var secondEndCopy = secondEnd.Copy(); + Assert.AreEqual(secondStartCopy, secondOccurrence.Period.StartTime); + Assert.AreEqual(secondEndCopy, secondOccurrence.Period.EndTime); + } + + [Test] + public void EnumerationChangedException() + { + const string ical = @"BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:name +X-WR-TIMEZONE:America/New_York +BEGIN:VTIMEZONE +TZID:America/New_York +X-LIC-LOCATION:America/New_York +BEGIN:DAYLIGHT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +END:STANDARD +END:VTIMEZONE + +BEGIN:VEVENT +DTSTART;TZID=America/New_York:20161011T170000 +DTEND;TZID=America/New_York:20161011T180000 +DTSTAMP:20160930T115710Z +UID:blablabla +RECURRENCE-ID;TZID=America/New_York:20161011T170000 +CREATED:20160830T144559Z +DESCRIPTION: +LAST-MODIFIED:20160928T142659Z +LOCATION:Location1 +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Summary1 +TRANSP:OPAQUE +END:VEVENT + +END:VCALENDAR"; + + var calendar = GetCalendars(ical); + var date = new DateTime(2016, 10, 11); + var occurrences = calendar[0].GetOccurrences(date); + + //We really want to make sure this doesn't explode + Assert.AreEqual(1, occurrences.Count); + } + + [Test] + public void GetOccurrencesShouldEnumerate() + { + const string ical = + @"BEGIN:VCALENDAR +PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:W. Europe Standard Time +BEGIN:STANDARD +DTSTART:16010101T030000 +RRULE:FREQ=YEARLY;BYDAY=SU;BYMONTH=10;BYSETPOS=-1 +TZNAME:Mitteleuropäische Zeit +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:00010101T020000 +RRULE:FREQ=YEARLY;BYDAY=SU;BYMONTH=3;BYSETPOS=-1 +TZNAME:Mitteleuropäische Sommerzeit +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +BACKGROUND:BUSY +DESCRIPTION:Backup Daten +DTEND;TZID=W. Europe Standard Time:20150305T043000 +DTSTAMP:20161122T120652Z +DTSTART;TZID=W. Europe Standard Time:20150305T000100 +RESOURCES:server +RRULE:FREQ=WEEKLY;BYDAY=MO +SUMMARY:Server +UID:a30ed847-8000-4c53-9e58-99c8f9cf7c4b +X-LIGHTSOUT-ACTION:START=WakeUp\;END=Reboot\,Force +X-LIGHTSOUT-MODE:TimeSpan +X-MICROSOFT-CDO-BUSYSTATUS:BUSY +END:VEVENT +BEGIN:VEVENT +BACKGROUND:BUSY +DESCRIPTION:Backup Daten +DTEND;TZID=W. Europe Standard Time:20161128T043000 +DTSTAMP:20161122T120652Z +DTSTART;TZID=W. Europe Standard Time:20161128T000100 +RECURRENCE-ID:20161128T000100 +RESOURCES:server +SEQUENCE:0 +SUMMARY:Server +UID:a30ed847-8000-4c53-9e58-99c8f9cf7c4b +X-LIGHTSOUT-ACTION:START=WakeUp\;END=Reboot\,Force +X-LIGHTSOUT-MODE:TimeSpan +X-MICROSOFT-CDO-BUSYSTATUS:BUSY +END:VEVENT +END:VCALENDAR +"; + + var collection = Calendar.LoadFromStream(new StringReader(ical)); + var startCheck = new DateTime(2016, 11, 11); + var occurrences = collection.GetOccurrences(startCheck, startCheck.AddMonths(1)); + + Assert.IsTrue(occurrences.Count == 4); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/Ical.Net.UnitTests.csproj b/net-core/Ical.Net/Ical.Net.UnitTests/Ical.Net.UnitTests.csproj new file mode 100644 index 00000000..eb0e17b2 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/Ical.Net.UnitTests.csproj @@ -0,0 +1,21 @@ + + + netstandard1.6 + + + library + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/IcsFiles.cs b/net-core/Ical.Net/Ical.Net.UnitTests/IcsFiles.cs new file mode 100644 index 00000000..2113bca8 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/IcsFiles.cs @@ -0,0 +1,162 @@ +using System.IO; +using System.Reflection; + +namespace Ical.Net.UnitTests +{ + internal class IcsFiles + { + private static readonly Assembly _assembly = typeof(IcsFiles).GetTypeInfo().Assembly; + + internal static string ReadStream(string manifestResource) + { + using (var stream = _assembly.GetManifestResourceStream(manifestResource)) + { + return new StreamReader(stream).ReadToEnd(); + } + } + + internal static string Alarm1 => ReadStream("Ical.Net.UnitTests.Calendars.Alarm.ALARM1.ics"); + internal static string ALARM1 => ReadStream("Ical.Net.UnitTests.Calendars.Alarm.ALARM1.ics"); + internal static string ALARM2 => ReadStream("Ical.Net.UnitTests.Calendars.Alarm.ALARM2.ics"); + internal static string ALARM3 => ReadStream("Ical.Net.UnitTests.Calendars.Alarm.ALARM3.ics"); + internal static string ALARM4 => ReadStream("Ical.Net.UnitTests.Calendars.Alarm.ALARM4.ics"); + internal static string ALARM5 => ReadStream("Ical.Net.UnitTests.Calendars.Alarm.ALARM5.ics"); + internal static string ALARM6 => ReadStream("Ical.Net.UnitTests.Calendars.Alarm.ALARM6.ics"); + internal static string ALARM7 => ReadStream("Ical.Net.UnitTests.Calendars.Alarm.ALARM7.ics"); + internal static string Attachment3 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Attachment3.ics"); + internal static string Attachment4 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Attachment4.ics"); + internal static string Attendee1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Attendee1.ics"); + internal static string Attendee2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Attendee2.ics"); + internal static string Bug1741093 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Bug1741093.ics"); + internal static string Bug2033495 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Bug2033495.ics"); + internal static string Bug2148092 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Bug2148092.ics"); + internal static string Bug2912657 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Bug2912657.ics"); + internal static string Bug2916581 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Bug2916581.ics"); + internal static string Bug2938007 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Bug2938007.ics"); + internal static string Bug2959692 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Bug2959692.ics"); + internal static string Bug2966236 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Bug2966236.ics"); + internal static string Bug3007244 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Bug3007244.ics"); + internal static string ByMonth1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.ByMonth1.ics"); + internal static string ByMonth2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.ByMonth2.ics"); + internal static string ByMonthDay1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.ByMonthDay1.ics"); + internal static string Calendar1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Calendar1.ics"); + internal static string CalendarParameters2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.CalendarParameters2.ics"); + internal static string CaseInsensitive1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.CaseInsensitive1.ics"); + internal static string CaseInsensitive2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.CaseInsensitive2.ics"); + internal static string CaseInsensitive3 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.CaseInsensitive3.ics"); + internal static string CaseInsensitive4 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.CaseInsensitive4.ics"); + internal static string Categories1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Categories1.ics"); + internal static string Daily1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Daily1.ics"); + internal static string DailyByDay1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.DailyByDay1.ics"); + internal static string DailyByHourMinute1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.DailyByHourMinute1.ics"); + internal static string DailyCount1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.DailyCount1.ics"); + internal static string DailyCount2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.DailyCount2.ics"); + internal static string DailyInterval1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.DailyInterval1.ics"); + internal static string DailyInterval2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.DailyInterval2.ics"); + internal static string DailyUntil1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.DailyUntil1.ics"); + internal static string DateTime1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.DateTime1.ics"); + internal static string DateTime2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.DateTime2.ics"); + internal static string Duration1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Duration1.ics"); + internal static string Empty1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Empty1.ics"); + internal static string EmptyLines1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.EmptyLines1.ics"); + internal static string EmptyLines2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.EmptyLines2.ics"); + internal static string EmptyLines3 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.EmptyLines3.ics"); + internal static string EmptyLines4 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.EmptyLines4.ics"); + internal static string Encoding1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Encoding1.ics"); + internal static string Encoding2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Encoding2.ics"); + internal static string Encoding3 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Encoding3.ics"); + internal static string Event1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Event1.ics"); + internal static string Event2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Event2.ics"); + internal static string Event3 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Event3.ics"); + internal static string Event4 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Event4.ics"); + internal static string GeographicLocation1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.GeographicLocation1.ics"); + internal static string Google1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Google1.ics"); + internal static string Hourly1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Hourly1.ics"); + internal static string HourlyInterval1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.HourlyInterval1.ics"); + internal static string HourlyInterval2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.HourlyInterval2.ics"); + internal static string HourlyUntil1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.HourlyUntil1.ics"); + internal static string JOURNAL1 => ReadStream("Ical.Net.UnitTests.Calendars.Journal.JOURNAL1.ics"); + internal static string JOURNAL2 => ReadStream("Ical.Net.UnitTests.Calendars.Journal.JOURNAL2.ics"); + internal static string Language1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Language1.ics"); + internal static string Language2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Language2.ics"); + internal static string Language3 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Language3.ics"); + internal static string Language4 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Language4.ics"); + internal static string Minutely1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Minutely1.ics"); + internal static string MinutelyByHour1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MinutelyByHour1.ics"); + internal static string MinutelyCount1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MinutelyCount1.ics"); + internal static string MinutelyCount2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MinutelyCount2.ics"); + internal static string MinutelyCount3 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MinutelyCount3.ics"); + internal static string MinutelyCount4 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MinutelyCount4.ics"); + internal static string MinutelyInterval1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MinutelyInterval1.ics"); + internal static string Monthly1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Monthly1.ics"); + internal static string MonthlyByDay1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyByDay1.ics"); + internal static string MonthlyByMonthDay1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyByMonthDay1.ics"); + internal static string MonthlyByMonthDay2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyByMonthDay2.ics"); + internal static string MonthlyBySetPos1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyBySetPos1.ics"); + internal static string MonthlyBySetPos2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyBySetPos2.ics"); + internal static string MonthlyCountByDay1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyCountByDay1.ics"); + internal static string MonthlyCountByDay2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyCountByDay2.ics"); + internal static string MonthlyCountByDay3 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyCountByDay3.ics"); + internal static string MonthlyCountByMonthDay1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyCountByMonthDay1.ics"); + internal static string MonthlyCountByMonthDay2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyCountByMonthDay2.ics"); + internal static string MonthlyCountByMonthDay3 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyCountByMonthDay3.ics"); + internal static string MonthlyInterval1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyInterval1.ics"); + internal static string MonthlyUntilByDay1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.MonthlyUntilByDay1.ics"); + internal static string Outlook2007LineFolds => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Outlook2007LineFolds.ics"); + internal static string Parameter1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Parameter1.ics"); + internal static string Parameter2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Parameter2.ics"); + internal static string Parse1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Parse1.ics"); + internal static string PARSE17 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.PARSE17.ics"); + internal static string ProdID1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.ProdID1.ics"); + internal static string ProdID2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.ProdID2.ics"); + internal static string Property1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Property1.ics"); + internal static string RecurrenceDates1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.RecurrenceDates1.ics"); + internal static string RequestStatus1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.RequestStatus1.ics"); + internal static string Secondly1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Secondly1.ics"); + internal static string TimeZone1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.TimeZone1.ics"); + internal static string TimeZone2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.TimeZone2.ics"); + internal static string TimeZone3 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.TimeZone3.ics"); + internal static string Todo1 => ReadStream("Ical.Net.UnitTests.Calendars.Todo.Todo1.ics"); + internal static string Todo2 => ReadStream("Ical.Net.UnitTests.Calendars.Todo.Todo2.ics"); + internal static string Todo3 => ReadStream("Ical.Net.UnitTests.Calendars.Todo.Todo3.ics"); + internal static string Todo4 => ReadStream("Ical.Net.UnitTests.Calendars.Todo.Todo4.ics"); + internal static string Todo5 => ReadStream("Ical.Net.UnitTests.Calendars.Todo.Todo5.ics"); + internal static string Todo6 => ReadStream("Ical.Net.UnitTests.Calendars.Todo.Todo6.ics"); + internal static string Todo7 => ReadStream("Ical.Net.UnitTests.Calendars.Todo.Todo7.ics"); + internal static string Todo8 => ReadStream("Ical.Net.UnitTests.Calendars.Todo.Todo8.ics"); + internal static string Todo9 => ReadStream("Ical.Net.UnitTests.Calendars.Todo.Todo9.ics"); + internal static string Transparency1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Transparency1.ics"); + internal static string Transparency2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Transparency2.ics"); + internal static string Trigger1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.Trigger1.ics"); + internal static string USHolidays => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.USHolidays.ics"); + internal static string WeeklyCount1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyCount1.ics"); + internal static string WeeklyCountWkst1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyCountWkst1.ics"); + internal static string WeeklyCountWkst2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyCountWkst2.ics"); + internal static string WeeklyCountWkst3 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyCountWkst3.ics"); + internal static string WeeklyCountWkst4 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyCountWkst4.ics"); + internal static string WeeklyInterval1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyInterval1.ics"); + internal static string WeeklyUntil1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyUntil1.ics"); + internal static string WeeklyUntilWkst1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyUntilWkst1.ics"); + internal static string WeeklyUntilWkst2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyUntilWkst2.ics"); + internal static string WeeklyWeekStartsLastYear => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyWeekStartsLastYear.ics"); + internal static string WeeklyWkst1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.WeeklyWkst1.ics"); + internal static string XProperty1 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.XProperty1.ics"); + internal static string XProperty2 => ReadStream("Ical.Net.UnitTests.Calendars.Serialization.XProperty2.ics"); + internal static string Yearly1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.Yearly1.ics"); + internal static string YearlyByDay1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyByDay1.ics"); + internal static string YearlyByMonth1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyByMonth1.ics"); + internal static string YearlyByMonth2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyByMonth2.ics"); + internal static string YearlyByMonth3 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyByMonth3.ics"); + internal static string YearlyByMonthDay1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyByMonthDay1.ics"); + internal static string YearlyBySetPos1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyBySetPos1.ics"); + internal static string YearlyByWeekNo1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyByWeekNo1.ics"); + internal static string YearlyByWeekNo2 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyByWeekNo2.ics"); + internal static string YearlyByWeekNo3 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyByWeekNo3.ics"); + internal static string YearlyByWeekNo4 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyByWeekNo4.ics"); + internal static string YearlyByWeekNo5 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyByWeekNo5.ics"); + internal static string YearlyComplex1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyComplex1.ics"); + internal static string YearlyCountByMonth1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyCountByMonth1.ics"); + internal static string YearlyCountByYearDay1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyCountByYearDay1.ics"); + internal static string YearlyInterval1 => ReadStream("Ical.Net.UnitTests.Calendars.Recurrence.YearlyInterval1.ics"); + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/JournalTest.cs b/net-core/Ical.Net/Ical.Net.UnitTests/JournalTest.cs new file mode 100644 index 00000000..3c445ab7 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/JournalTest.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; +using System.Linq; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class JournalTest + { + [Test, Category("Journal")] + public void Journal1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.JOURNAL1))[0]; + ProgramTest.TestCal(iCal); + Assert.AreEqual(1, iCal.Journals.Count); + var j = iCal.Journals[0]; + + Assert.IsNotNull(j, "Journal entry was null"); + Assert.AreEqual(JournalStatus.Draft, j.Status, "Journal entry should have been in DRAFT status, but it was in " + j.Status + " status."); + Assert.AreEqual("PUBLIC", j.Class, "Journal class should have been PUBLIC, but was " + j.Class + "."); + Assert.IsNull(j.Start); + } + + [Test, Category("Journal")] + public void Journal2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.JOURNAL2))[0]; + ProgramTest.TestCal(iCal); + Assert.AreEqual(1, iCal.Journals.Count); + var j = iCal.Journals.First(); + + Assert.IsNotNull(j, "Journal entry was null"); + Assert.AreEqual(JournalStatus.Final, j.Status, "Journal entry should have been in FINAL status, but it was in " + j.Status + " status."); + Assert.AreEqual("PRIVATE", j.Class, "Journal class should have been PRIVATE, but was " + j.Class + "."); + Assert.AreEqual("JohnSmith", j.Organizer.CommonName, "Organizer common name should have been JohnSmith, but was " + j.Organizer.CommonName); + Assert.IsTrue( + string.Equals( + j.Organizer.SentBy.OriginalString, + "mailto:jane_doe@host.com", + StringComparison.OrdinalIgnoreCase), + "Organizer should have had been SENT-BY 'mailto:jane_doe@host.com'; it was sent by '" + j.Organizer.SentBy + "'"); + Assert.IsTrue( + string.Equals( + j.Organizer.DirectoryEntry.OriginalString, + "ldap://host.com:6666/o=3DDC%20Associates,c=3DUS??(cn=3DJohn%20Smith)", + StringComparison.OrdinalIgnoreCase), + "Organizer's directory entry should have been 'ldap://host.com:6666/o=3DDC%20Associates,c=3DUS??(cn=3DJohn%20Smith)', but it was '" + j.Organizer.DirectoryEntry + "'"); + Assert.AreEqual( + "MAILTO:jsmith@host.com", + j.Organizer.Value.OriginalString); + Assert.AreEqual( + "jsmith", + j.Organizer.Value.UserInfo); + Assert.AreEqual( + "host.com", + j.Organizer.Value.Host); + Assert.IsNull(j.Start); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/ListExtensions.cs b/net-core/Ical.Net/Ical.Net.UnitTests/ListExtensions.cs new file mode 100644 index 00000000..254f4993 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/ListExtensions.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Ical.Net.UnitTests.ExtensionMethods +{ + internal static class ListExtensions + { + public static void AddRange(this IList list, IEnumerable values) + { + if (values != null) + { + foreach (var item in values) + { + list.Add(item); + } + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/ProgramTest.cs b/net-core/Ical.Net/Ical.Net.UnitTests/ProgramTest.cs new file mode 100644 index 00000000..be7b1f47 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/ProgramTest.cs @@ -0,0 +1,184 @@ +using System; +using System.IO; +using System.Linq; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; + +using Ical.Net.Utility; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class ProgramTest + { + [Test] + public void LoadAndDisplayCalendar() + { + // The following code loads and displays an iCalendar + // with US Holidays for 2006. + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.USHolidays))[0]; + Assert.IsNotNull(iCal, "iCalendar did not load."); + } + + private const string _tzid = "US-Eastern"; + + public static void TestCal(Calendar cal) + { + Assert.IsNotNull(cal, "The iCalendar was not loaded"); + if (cal.Events.Count > 0) + Assert.IsTrue(cal.Events.Count == 1, "Calendar should contain 1 event; however, the iCalendar loaded " + cal.Events.Count + " events"); + else if (cal.Todos.Count > 0) + Assert.IsTrue(cal.Todos.Count == 1, "Calendar should contain 1 todo; however, the iCalendar loaded " + cal.Todos.Count + " todos"); + } + + /// + /// The following test is an aggregate of MonthlyCountByMonthDay3() and MonthlyByDay1() in the + /// class. + /// + [Test] + public void Merge1() + { + var iCal1 = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyCountByMonthDay3))[0]; + var iCal2 = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyByDay1))[0]; + + // Change the UID of the 2nd event to make sure it's different + iCal2.Events[iCal1.Events[0].Uid].Uid = "1234567890"; + iCal1.MergeWith(iCal2); + + var evt1 = iCal1.Events.First(); + var evt2 = iCal1.Events.Skip(1).First(); + + // Get occurrences for the first event + var occurrences = evt1.GetOccurrences( + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(2000, 1, 1, _tzid)).OrderBy(o => o.Period.StartTime).ToList(); + + var dateTimes = new[] + { + new CalDateTime(1997, 9, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 11, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 13, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 15, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 10, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 11, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 12, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 13, 9, 0, 0, _tzid) + }; + + var timeZones = new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + }; + + for (var i = 0; i < dateTimes.Length; i++) + { + IDateTime dt = dateTimes[i]; + var start = occurrences[i].Period.StartTime; + Assert.AreEqual(dt, start); + + var expectedZone = DateUtil.GetZone(dt.TimeZoneName); + var actualZone = DateUtil.GetZone(timeZones[i]); + + //Assert.AreEqual(); + + //Normalize the time zones and then compare equality + Assert.AreEqual(expectedZone, actualZone); + + //Assert.IsTrue(dt.TimeZoneName == TimeZones[i], "Event " + dt + " should occur in the " + TimeZones[i] + " timezone"); + } + + Assert.IsTrue(occurrences.Count == dateTimes.Length, "There should be exactly " + dateTimes.Length + " occurrences; there were " + occurrences.Count); + + // Get occurrences for the 2nd event + occurrences = evt2.GetOccurrences( + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 4, 1, _tzid)).OrderBy(o => o.Period.StartTime).ToList(); + + var dateTimes1 = new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 9, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 23, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 4, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 11, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 18, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 25, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 6, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 13, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 20, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 27, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 3, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 10, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 17, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 24, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 31, 9, 0, 0, _tzid) + }; + + var timeZones1 = new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + }; + + for (var i = 0; i < dateTimes1.Length; i++) + { + IDateTime dt = dateTimes1[i]; + var start = occurrences[i].Period.StartTime; + Assert.AreEqual(dt, start); + Assert.IsTrue(dt.TimeZoneName == timeZones1[i], "Event " + dt + " should occur in the " + timeZones1[i] + " timezone"); + } + + Assert.AreEqual(dateTimes1.Length, occurrences.Count, "There should be exactly " + dateTimes1.Length + " occurrences; there were " + occurrences.Count); + } + + [Test] + public void SystemTimeZone3() + { + // Per Jon Udell's test, we should be able to get all + // system time zones on the machine and ensure they + // are properly translated. + var zones = TimeZoneInfo.GetSystemTimeZones(); + foreach (var zone in zones) + { + try + { + TimeZoneInfo.FindSystemTimeZoneById(zone.Id); + } + catch (Exception) + { + Assert.Fail("Not found: " + zone.StandardName); + } + } + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/RecurrenceTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/RecurrenceTests.cs new file mode 100644 index 00000000..3fa61077 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/RecurrenceTests.cs @@ -0,0 +1,3132 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using Ical.Net.DataTypes; +using Ical.Net.Evaluation; +using Ical.Net.Exceptions; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Evaluation; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; +using Ical.Net.Utility; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class RecurrenceTests + { + private const string _tzid = "US-Eastern"; + + private void EventOccurrenceTest( + Calendar cal, + IDateTime fromDate, + IDateTime toDate, + IDateTime[] dateTimes, + string[] timeZones, + int eventIndex + ) + { + var evt = cal.Events.Skip(eventIndex).First(); + fromDate.AssociatedObject = cal; + toDate.AssociatedObject = cal; + + var occurrences = evt.GetOccurrences(fromDate, toDate) + .OrderBy(o => o.Period.StartTime) + .ToList(); + + Assert.AreEqual( + dateTimes.Length, + occurrences.Count, + "There should be exactly " + dateTimes.Length + " occurrences; there were " + occurrences.Count); + + if (evt.RecurrenceRules.Count > 0) + { + Assert.AreEqual(1, evt.RecurrenceRules.Count); + } + + for (var i = 0; i < dateTimes.Length; i++) + { + // Associate each incoming date/time with the calendar. + dateTimes[i].AssociatedObject = cal; + + var dt = dateTimes[i]; + Assert.AreEqual(dt, occurrences[i].Period.StartTime, "Event should occur on " + dt); + if (timeZones != null) + Assert.AreEqual(timeZones[i], dt.TimeZoneName, "Event " + dt + " should occur in the " + timeZones[i] + " timezone"); + } + } + + private void EventOccurrenceTest( + Calendar cal, + IDateTime fromDate, + IDateTime toDate, + IDateTime[] dateTimes, + string[] timeZones + ) + { + EventOccurrenceTest(cal, fromDate, toDate, dateTimes, timeZones, 0); + } + + /// + /// See Page 45 of RFC 2445 - RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30 + /// + [Test, Category("Recurrence")] + public void YearlyComplex1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyComplex1))[0]; + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); + var occurrences = evt.GetOccurrences( + new CalDateTime(2006, 1, 1, _tzid), + new CalDateTime(2011, 1, 1, _tzid)).OrderBy(o => o.Period.StartTime).ToList(); + + IDateTime dt = new CalDateTime(2007, 1, 1, 8, 30, 0, _tzid); + var i = 0; + + while (dt.Year < 2011) + { + if (dt.GreaterThan(evt.Start) && + (dt.Year % 2 == 1) && // Every-other year from 2005 + (dt.Month == 1) && + (dt.DayOfWeek == DayOfWeek.Sunday)) + { + var dt1 = dt.AddHours(1); + Assert.AreEqual(dt, occurrences[i].Period.StartTime, "Event should occur at " + dt); + Assert.AreEqual(dt1, occurrences[i + 1].Period.StartTime, "Event should occur at " + dt); + i += 2; + } + + dt = dt.AddDays(1); + } + } + + /// + /// See Page 118 of RFC 2445 - RRULE:FREQ=DAILY;COUNT=10;INTERVAL=2 + /// + [Test, Category("Recurrence")] + public void DailyCount1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.DailyCount1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2006, 7, 1, _tzid), + new CalDateTime(2006, 9, 1, _tzid), + new[] + { + new CalDateTime(2006, 07, 18, 10, 00, 00, _tzid), + new CalDateTime(2006, 07, 20, 10, 00, 00, _tzid), + new CalDateTime(2006, 07, 22, 10, 00, 00, _tzid), + new CalDateTime(2006, 07, 24, 10, 00, 00, _tzid), + new CalDateTime(2006, 07, 26, 10, 00, 00, _tzid), + new CalDateTime(2006, 07, 28, 10, 00, 00, _tzid), + new CalDateTime(2006, 07, 30, 10, 00, 00, _tzid), + new CalDateTime(2006, 08, 01, 10, 00, 00, _tzid), + new CalDateTime(2006, 08, 03, 10, 00, 00, _tzid), + new CalDateTime(2006, 08, 05, 10, 00, 00, _tzid) + }, + null + ); + } + + /// + /// See Page 118 of RFC 2445 - RRULE:FREQ=DAILY;UNTIL=19971224T000000Z + /// + [Test, Category("Recurrence")] + public void DailyUntil1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.DailyUntil1))[0]; + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); + + var occurrences = evt.GetOccurrences( + new CalDateTime(1997, 9, 1, _tzid), + new CalDateTime(1998, 1, 1, _tzid)).OrderBy(o => o.Period.StartTime).ToList(); + + IDateTime dt = new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid); + var i = 0; + while (dt.Year < 1998) + { + if (dt.GreaterThanOrEqual(evt.Start) && + dt.LessThan(new CalDateTime(1997, 12, 24, 0, 0, 0, _tzid))) + { + Assert.AreEqual(dt, occurrences[i].Period.StartTime, "Event should occur at " + dt); + Assert.IsTrue( + (dt.LessThan(new CalDateTime(1997, 10, 26, _tzid)) && dt.TimeZoneName == "US-Eastern") || + (dt.GreaterThan(new CalDateTime(1997, 10, 26, _tzid)) && dt.TimeZoneName == "US-Eastern"), + "Event " + dt + " doesn't occur in the correct time zone (including Daylight & Standard time zones)"); + i++; + } + + dt = dt.AddDays(1); + } + } + + /// + /// See Page 118 of RFC 2445 - RRULE:FREQ=DAILY;INTERVAL=2 + /// + [Test, Category("Recurrence")] + public void Daily1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Daily1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1997, 9, 1, _tzid), + new CalDateTime(1997, 12, 4, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 4, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 6, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 8, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 18, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 20, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 22, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 24, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 26, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 28, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 4, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 6, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 8, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 18, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 20, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 22, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 24, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 26, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 28, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 1, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 3, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 5, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 7, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 9, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 11, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 13, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 15, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 17, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 19, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 21, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 23, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 25, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 27, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 29, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 1, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 3, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 119 of RFC 2445 - RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5 + /// + [Test, Category("Recurrence")] + public void DailyCount2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.DailyCount2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1997, 9, 1, _tzid), + new CalDateTime(1998, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 22, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 12, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 119 of RFC 2445 - RRULE:FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1 + /// + [Test, Category("Recurrence")] + public void ByMonth1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.ByMonth1))[0]; + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); + + var occurrences = evt.GetOccurrences( + new CalDateTime(1998, 1, 1, _tzid), + new CalDateTime(2000, 12, 31, _tzid)).OrderBy(o => o.Period.StartTime).ToList(); + + IDateTime dt = new CalDateTime(1998, 1, 1, 9, 0, 0, _tzid); + var i = 0; + while (dt.Year < 2001) + { + if (dt.GreaterThanOrEqual(evt.Start) && + dt.Month == 1 && + dt.LessThanOrEqual(new CalDateTime(2000, 1, 31, 9, 0, 0, _tzid))) + { + Assert.AreEqual(dt, occurrences[i].Period.StartTime, "Event should occur at " + dt); + i++; + } + + dt = dt.AddDays(1); + } + } + + /// + /// See Page 119 of RFC 2445 - RRULE:FREQ=YEARLY;UNTIL=20000131T150000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA + /// + /// The example was slightly modified to fix a suspected flaw in the design of + /// the example RRULEs. UNTIL is always UTC time, but it expected the actual + /// time to correspond to other time zones. Odd. + /// + /// + [Test, Category("Recurrence")] + public void ByMonth2() + { + var iCal1 = Calendar.LoadFromStream(new StringReader(IcsFiles.ByMonth1))[0]; + var iCal2 = Calendar.LoadFromStream(new StringReader(IcsFiles.ByMonth2))[0]; + ProgramTest.TestCal(iCal1); + ProgramTest.TestCal(iCal2); + CalendarEvent evt1 = (CalendarEvent)iCal1.Events.First(); + CalendarEvent evt2 = (CalendarEvent)iCal2.Events.First(); + + var evt1Occurrences = evt1.GetOccurrences(new CalDateTime(1997, 9, 1), new CalDateTime(2000, 12, 31)).OrderBy(o => o.Period.StartTime).ToList(); + var evt2Occurrences = evt2.GetOccurrences(new CalDateTime(1997, 9, 1), new CalDateTime(2000, 12, 31)).OrderBy(o => o.Period.StartTime).ToList(); + Assert.IsTrue(evt1Occurrences.Count == evt2Occurrences.Count, "ByMonth1 does not match ByMonth2 as it should"); + for (var i = 0; i < evt1Occurrences.Count; i++) + Assert.AreEqual(evt1Occurrences[i].Period, evt2Occurrences[i].Period, "PERIOD " + i + " from ByMonth1 (" + evt1Occurrences[i] + ") does not match PERIOD " + i + " from ByMonth2 (" + evt2Occurrences[i] + ")"); + } + + /// + /// See Page 119 of RFC 2445 - RRULE:FREQ=WEEKLY;COUNT=10 + /// + [Test, Category("Recurrence")] + public void WeeklyCount1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyCount1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1997, 9, 1, _tzid), + new CalDateTime(1998, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 9, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 23, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 7, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 21, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 28, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 4, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 119 of RFC 2445 - RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z + /// + [Test, Category("Recurrence")] + public void WeeklyUntil1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyUntil1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1997, 9, 1, _tzid), + new CalDateTime(1999, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 9, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 23, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 7, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 21, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 28, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 4, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 11, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 18, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 25, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 9, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 23, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 119 of RFC 2445 - RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU + /// + [Test, Category("Recurrence")] + public void WeeklyWkst1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyWkst1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1997, 9, 1, _tzid), + new CalDateTime(1998, 1, 31, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 28, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 11, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 25, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 9, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 23, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 6, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 20, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 119 of RFC 2445 - RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH + /// + [Test, Category("Recurrence")] + public void WeeklyUntilWkst1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyUntilWkst1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1997, 9, 1, _tzid), + new CalDateTime(1999, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 4, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 9, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 11, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 18, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 23, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 25, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 2, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 120 of RFC 2445 - RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH + /// + [Test, Category("Recurrence")] + public void WeeklyCountWkst1() + { + var iCal1 = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyUntilWkst1))[0]; + var iCal2 = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyCountWkst1))[0]; + ProgramTest.TestCal(iCal1); + ProgramTest.TestCal(iCal2); + var evt1 = iCal1.Events.First(); + var evt2 = iCal2.Events.First(); + + var evt1Occ = evt1.GetOccurrences(new CalDateTime(1997, 9, 1), new CalDateTime(1999, 1, 1)).OrderBy(o => o.Period.StartTime).ToList(); + var evt2Occ = evt2.GetOccurrences(new CalDateTime(1997, 9, 1), new CalDateTime(1999, 1, 1)).OrderBy(o => o.Period.StartTime).ToList(); + Assert.AreEqual(evt1Occ.Count, evt2Occ.Count, "WeeklyCountWkst1() does not match WeeklyUntilWkst1() as it should"); + for (var i = 0; i < evt1Occ.Count; i++) + Assert.AreEqual(evt1Occ[i].Period, evt2Occ[i].Period, "PERIOD " + i + " from WeeklyUntilWkst1 (" + evt1Occ[i].Period + ") does not match PERIOD " + i + " from WeeklyCountWkst1 (" + evt2Occ[i].Period + ")"); + } + + /// + /// See Page 120 of RFC 2445 - RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR + /// + [Test, Category("Recurrence")] + public void WeeklyUntilWkst2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyUntilWkst2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 3, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 5, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 15, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 17, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 19, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 29, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 1, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 3, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 13, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 15, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 17, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 27, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 29, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 31, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 24, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 26, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 28, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 8, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 22, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// Tests to ensure FREQUENCY=WEEKLY with INTERVAL=2 works when starting evaluation from an "off" week + /// + [Test, Category("Recurrence")] + public void WeeklyUntilWkst2_1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyUntilWkst2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1997, 9, 9, _tzid), + new CalDateTime(1999, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 15, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 17, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 19, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 29, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 1, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 3, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 13, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 15, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 17, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 27, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 29, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 31, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 24, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 26, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 28, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 8, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 22, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 120 of RFC 2445 - RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH + /// + [Test, Category("Recurrence")] + public void WeeklyCountWkst2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyCountWkst2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 4, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 18, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 16, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 120 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR + /// + [Test, Category("Recurrence")] + public void MonthlyCountByDay1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyCountByDay1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 5, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 3, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 7, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 5, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 2, 9, 0, 0, _tzid), + new CalDateTime(1998, 2, 6, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 6, 9, 0, 0, _tzid), + new CalDateTime(1998, 4, 3, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 1, 9, 0, 0, _tzid), + new CalDateTime(1998, 6, 5, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 120 of RFC 2445 - RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR + /// + [Test, Category("Recurrence")] + public void MonthlyUntilByDay1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyUntilByDay1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 5, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 3, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 7, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 5, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 120 of RFC 2445 - RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU + /// + [Test, Category("Recurrence")] + public void MonthlyCountByDay2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyCountByDay2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 7, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 28, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 30, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 4, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 25, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 1, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 29, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 3, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 31, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 121 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO + /// + [Test, Category("Recurrence")] + public void MonthlyCountByDay3() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyCountByDay3))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 22, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 20, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 17, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 22, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 19, 9, 0, 0, _tzid), + new CalDateTime(1998, 2, 16, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 121 of RFC 2445 - RRULE:FREQ=MONTHLY;BYMONTHDAY=-3 + /// + [Test, Category("Recurrence")] + public void ByMonthDay1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.ByMonthDay1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 3, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 28, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 29, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 28, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 29, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 29, 9, 0, 0, _tzid), + new CalDateTime(1998, 2, 26, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 121 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15 + /// + [Test, Category("Recurrence")] + public void MonthlyCountByMonthDay1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyCountByMonthDay1))[0]; + + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 3, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 15, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 15, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 15, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 15, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 2, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 15, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 121 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1 + /// + [Test, Category("Recurrence")] + public void MonthlyCountByMonthDay2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyCountByMonthDay2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 3, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 1, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 31, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 1, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 1, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 31, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 1, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 31, 9, 0, 0, _tzid), + new CalDateTime(1998, 2, 1, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 121 of RFC 2445 - RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15 + /// + [Test, Category("Recurrence")] + public void MonthlyCountByMonthDay3() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyCountByMonthDay3))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(2000, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 11, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 13, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 15, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 10, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 11, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 12, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 13, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 122 of RFC 2445 - RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU + /// + [Test, Category("Recurrence")] + public void MonthlyByDay1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyByDay1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 4, 1, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 9, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 23, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 4, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 11, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 18, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 25, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 6, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 13, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 20, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 27, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 3, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 10, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 17, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 24, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 31, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 122 of RFC 2445 - RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7 + /// + [Test, Category("Recurrence")] + public void YearlyByMonth1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyByMonth1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(2002, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 6, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 7, 10, 9, 0, 0, _tzid), + new CalDateTime(1998, 6, 10, 9, 0, 0, _tzid), + new CalDateTime(1998, 7, 10, 9, 0, 0, _tzid), + new CalDateTime(1999, 6, 10, 9, 0, 0, _tzid), + new CalDateTime(1999, 7, 10, 9, 0, 0, _tzid), + new CalDateTime(2000, 6, 10, 9, 0, 0, _tzid), + new CalDateTime(2000, 7, 10, 9, 0, 0, _tzid), + new CalDateTime(2001, 6, 10, 9, 0, 0, _tzid), + new CalDateTime(2001, 7, 10, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 122 of RFC 2445 - RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3 + /// + [Test, Category("Recurrence")] + public void YearlyCountByMonth1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyCountByMonth1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(2003, 4, 1, _tzid), + new[] + { + new CalDateTime(1997, 3, 10, 9, 0, 0, _tzid), + new CalDateTime(1999, 1, 10, 9, 0, 0, _tzid), + new CalDateTime(1999, 2, 10, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 10, 9, 0, 0, _tzid), + new CalDateTime(2001, 1, 10, 9, 0, 0, _tzid), + new CalDateTime(2001, 2, 10, 9, 0, 0, _tzid), + new CalDateTime(2001, 3, 10, 9, 0, 0, _tzid), + new CalDateTime(2003, 1, 10, 9, 0, 0, _tzid), + new CalDateTime(2003, 2, 10, 9, 0, 0, _tzid), + new CalDateTime(2003, 3, 10, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 122 of RFC 2445 - RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200 + /// + [Test, Category("Recurrence")] + public void YearlyCountByYearDay1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyCountByYearDay1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(2007, 1, 1, _tzid), + new[] + { + new CalDateTime(1997, 1, 1, 9, 0, 0, _tzid), + new CalDateTime(1997, 4, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 7, 19, 9, 0, 0, _tzid), + new CalDateTime(2000, 1, 1, 9, 0, 0, _tzid), + new CalDateTime(2000, 4, 9, 9, 0, 0, _tzid), + new CalDateTime(2000, 7, 18, 9, 0, 0, _tzid), + new CalDateTime(2003, 1, 1, 9, 0, 0, _tzid), + new CalDateTime(2003, 4, 10, 9, 0, 0, _tzid), + new CalDateTime(2003, 7, 19, 9, 0, 0, _tzid), + new CalDateTime(2006, 1, 1, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 123 of RFC 2445 - RRULE:FREQ=YEARLY;BYDAY=20MO + /// + [Test, Category("Recurrence")] + public void YearlyByDay1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyByDay1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 12, 31, _tzid), + new[] + { + new CalDateTime(1997, 5, 19, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 18, 9, 0, 0, _tzid), + new CalDateTime(1999, 5, 17, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 123 of RFC 2445 - RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO + /// + [Test, Category("Recurrence")] + public void YearlyByWeekNo1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyByWeekNo1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 12, 31, _tzid), + new[] + { + new CalDateTime(1997, 5, 12, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 11, 9, 0, 0, _tzid), + new CalDateTime(1999, 5, 17, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// DTSTART;TZID=US-Eastern:19970512T090000 + /// RRULE:FREQ=YEARLY;BYWEEKNO=20 + /// Includes Monday in week 20 (since 19970512 is a Monday) + /// of each year. + /// See http://lists.calconnect.org/pipermail/caldeveloper-l/2010-April/000042.html + /// and related threads for a fairly in-depth discussion about this topic. + /// + [Test, Category("Recurrence")] + public void YearlyByWeekNo2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyByWeekNo2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 12, 31, _tzid), + new[] + { + new CalDateTime(1997, 5, 12, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 11, 9, 0, 0, _tzid), + new CalDateTime(1999, 5, 17, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// DTSTART;TZID=US-Eastern:20020101T100000 + /// RRULE:FREQ=YEARLY;BYWEEKNO=1 + /// Ensures that 20021230 part of week 1 in 2002. + /// See http://lists.calconnect.org/pipermail/caldeveloper-l/2010-April/000042.html + /// and related threads for a fairly in-depth discussion about this topic. + /// + [Test, Category("Recurrence")] + public void YearlyByWeekNo3() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyByWeekNo3))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2001, 1, 1, _tzid), + new CalDateTime(2003, 1, 31, _tzid), + new[] + { + new CalDateTime(2002, 1, 1, 10, 0, 0, _tzid), + new CalDateTime(2002, 12, 31, 10, 0, 0, _tzid) + }, + null + ); + } + + /// + /// RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO,TU,WE,TH,FR,SA,SU + /// Includes every day in week 20. + /// See http://lists.calconnect.org/pipermail/caldeveloper-l/2010-April/000042.html + /// and related threads for a fairly in-depth discussion about this topic. + /// + [Test, Category("Recurrence")] + public void YearlyByWeekNo4() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyByWeekNo4))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 12, 31, _tzid), + new[] + { + new CalDateTime(1997, 5, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 5, 13, 9, 0, 0, _tzid), + new CalDateTime(1997, 5, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 5, 15, 9, 0, 0, _tzid), + new CalDateTime(1997, 5, 16, 9, 0, 0, _tzid), + new CalDateTime(1997, 5, 17, 9, 0, 0, _tzid), + new CalDateTime(1997, 5, 18, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 11, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 12, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 13, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 14, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 15, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 16, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 17, 9, 0, 0, _tzid), + new CalDateTime(1999, 5, 17, 9, 0, 0, _tzid), + new CalDateTime(1999, 5, 18, 9, 0, 0, _tzid), + new CalDateTime(1999, 5, 19, 9, 0, 0, _tzid), + new CalDateTime(1999, 5, 20, 9, 0, 0, _tzid), + new CalDateTime(1999, 5, 21, 9, 0, 0, _tzid), + new CalDateTime(1999, 5, 22, 9, 0, 0, _tzid), + new CalDateTime(1999, 5, 23, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// DTSTART;TZID=US-Eastern:20020101T100000 + /// RRULE:FREQ=YEARLY;BYWEEKNO=1;BYDAY=MO,TU,WE,TH,FR,SA,SU + /// Ensures that 20021230 and 20021231 are in week 1. + /// Also ensures 20011231 is NOT in the result. + /// See http://lists.calconnect.org/pipermail/caldeveloper-l/2010-April/000042.html + /// and related threads for a fairly in-depth discussion about this topic. + /// + [Test, Category("Recurrence")] + public void YearlyByWeekNo5() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyByWeekNo5))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2001, 1, 1, _tzid), + new CalDateTime(2003, 1, 31, _tzid), + new[] + { + new CalDateTime(2002, 1, 1, 10, 0, 0, _tzid), + new CalDateTime(2002, 1, 2, 10, 0, 0, _tzid), + new CalDateTime(2002, 1, 3, 10, 0, 0, _tzid), + new CalDateTime(2002, 1, 4, 10, 0, 0, _tzid), + new CalDateTime(2002, 1, 5, 10, 0, 0, _tzid), + new CalDateTime(2002, 1, 6, 10, 0, 0, _tzid), + new CalDateTime(2002, 12, 30, 10, 0, 0, _tzid), + new CalDateTime(2002, 12, 31, 10, 0, 0, _tzid), + new CalDateTime(2003, 1, 1, 10, 0, 0, _tzid), + new CalDateTime(2003, 1, 2, 10, 0, 0, _tzid), + new CalDateTime(2003, 1, 3, 10, 0, 0, _tzid), + new CalDateTime(2003, 1, 4, 10, 0, 0, _tzid), + new CalDateTime(2003, 1, 5, 10, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 123 of RFC 2445 - RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH + /// + [Test, Category("Recurrence")] + public void YearlyByMonth2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyByMonth2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 12, 31, _tzid), + new[] + { + new CalDateTime(1997, 3, 13, 9, 0, 0, _tzid), + new CalDateTime(1997, 3, 20, 9, 0, 0, _tzid), + new CalDateTime(1997, 3, 27, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 5, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 12, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 19, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 26, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 4, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 11, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 18, 9, 0, 0, _tzid), + new CalDateTime(1999, 3, 25, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 123 of RFC 2445 - RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8 + /// + [Test, Category("Recurrence")] + public void YearlyByMonth3() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyByMonth3))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 12, 31, _tzid), + new[] + { + new CalDateTime(1997, 6, 5, 9, 0, 0, _tzid), + new CalDateTime(1997, 6, 12, 9, 0, 0, _tzid), + new CalDateTime(1997, 6, 19, 9, 0, 0, _tzid), + new CalDateTime(1997, 6, 26, 9, 0, 0, _tzid), + new CalDateTime(1997, 7, 3, 9, 0, 0, _tzid), + new CalDateTime(1997, 7, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 7, 17, 9, 0, 0, _tzid), + new CalDateTime(1997, 7, 24, 9, 0, 0, _tzid), + new CalDateTime(1997, 7, 31, 9, 0, 0, _tzid), + new CalDateTime(1997, 8, 7, 9, 0, 0, _tzid), + new CalDateTime(1997, 8, 14, 9, 0, 0, _tzid), + new CalDateTime(1997, 8, 21, 9, 0, 0, _tzid), + new CalDateTime(1997, 8, 28, 9, 0, 0, _tzid), + new CalDateTime(1998, 6, 4, 9, 0, 0, _tzid), + new CalDateTime(1998, 6, 11, 9, 0, 0, _tzid), + new CalDateTime(1998, 6, 18, 9, 0, 0, _tzid), + new CalDateTime(1998, 6, 25, 9, 0, 0, _tzid), + new CalDateTime(1998, 7, 2, 9, 0, 0, _tzid), + new CalDateTime(1998, 7, 9, 9, 0, 0, _tzid), + new CalDateTime(1998, 7, 16, 9, 0, 0, _tzid), + new CalDateTime(1998, 7, 23, 9, 0, 0, _tzid), + new CalDateTime(1998, 7, 30, 9, 0, 0, _tzid), + new CalDateTime(1998, 8, 6, 9, 0, 0, _tzid), + new CalDateTime(1998, 8, 13, 9, 0, 0, _tzid), + new CalDateTime(1998, 8, 20, 9, 0, 0, _tzid), + new CalDateTime(1998, 8, 27, 9, 0, 0, _tzid), + new CalDateTime(1999, 6, 3, 9, 0, 0, _tzid), + new CalDateTime(1999, 6, 10, 9, 0, 0, _tzid), + new CalDateTime(1999, 6, 17, 9, 0, 0, _tzid), + new CalDateTime(1999, 6, 24, 9, 0, 0, _tzid), + new CalDateTime(1999, 7, 1, 9, 0, 0, _tzid), + new CalDateTime(1999, 7, 8, 9, 0, 0, _tzid), + new CalDateTime(1999, 7, 15, 9, 0, 0, _tzid), + new CalDateTime(1999, 7, 22, 9, 0, 0, _tzid), + new CalDateTime(1999, 7, 29, 9, 0, 0, _tzid), + new CalDateTime(1999, 8, 5, 9, 0, 0, _tzid), + new CalDateTime(1999, 8, 12, 9, 0, 0, _tzid), + new CalDateTime(1999, 8, 19, 9, 0, 0, _tzid), + new CalDateTime(1999, 8, 26, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 123 of RFC 2445: + /// EXDATE;TZID=US-Eastern:19970902T090000 + /// RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13 + /// + [Test, Category("Recurrence")] + public void MonthlyByMonthDay1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyByMonthDay1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(2000, 12, 31, _tzid), + new[] + { + new CalDateTime(1998, 2, 13, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 13, 9, 0, 0, _tzid), + new CalDateTime(1998, 11, 13, 9, 0, 0, _tzid), + new CalDateTime(1999, 8, 13, 9, 0, 0, _tzid), + new CalDateTime(2000, 10, 13, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 124 of RFC 2445 - RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13 + /// + [Test, Category("Recurrence")] + public void MonthlyByMonthDay2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyByMonthDay2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 6, 30, _tzid), + new[] + { + new CalDateTime(1997, 9, 13, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 11, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 8, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 13, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 10, 9, 0, 0, _tzid), + new CalDateTime(1998, 2, 7, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 7, 9, 0, 0, _tzid), + new CalDateTime(1998, 4, 11, 9, 0, 0, _tzid), + new CalDateTime(1998, 5, 9, 9, 0, 0, _tzid), + new CalDateTime(1998, 6, 13, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 124 of RFC 2445 - RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8 + /// + [Test, Category("Recurrence")] + public void YearlyByMonthDay1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyByMonthDay1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(2004, 12, 31, _tzid), + new[] + { + new CalDateTime(1996, 11, 5, 9, 0, 0, _tzid), + new CalDateTime(2000, 11, 7, 9, 0, 0, _tzid), + new CalDateTime(2004, 11, 2, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 124 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3 + /// + [Test, Category("Recurrence")] + public void MonthlyBySetPos1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyBySetPos1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(2004, 12, 31, _tzid), + new[] + { + new CalDateTime(1997, 9, 4, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 7, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 6, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 124 of RFC 2445 - RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2 + /// + [Test, Category("Recurrence")] + public void MonthlyBySetPos2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyBySetPos2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 3, 31, _tzid), + new[] + { + new CalDateTime(1997, 9, 29, 9, 0, 0, _tzid), + new CalDateTime(1997, 10, 30, 9, 0, 0, _tzid), + new CalDateTime(1997, 11, 27, 9, 0, 0, _tzid), + new CalDateTime(1997, 12, 30, 9, 0, 0, _tzid), + new CalDateTime(1998, 1, 29, 9, 0, 0, _tzid), + new CalDateTime(1998, 2, 26, 9, 0, 0, _tzid), + new CalDateTime(1998, 3, 30, 9, 0, 0, _tzid) + }, + new[] + { + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern", + "US-Eastern" + } + ); + } + + /// + /// See Page 125 of RFC 2445 - RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z + /// FIXME: The UNTIL time on this item has been altered to 19970902T190000Z to + /// match the local EDT time occurrence of 3:00pm. Is the RFC example incorrect? + /// + [Test, Category("Recurrence")] + public void HourlyUntil1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.HourlyUntil1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 3, 31, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 12, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 15, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 125 of RFC 2445 - RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6 + /// + [Test, Category("Recurrence")] + public void MinutelyCount1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MinutelyCount1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1997, 9, 2, _tzid), + new CalDateTime(1997, 9, 3, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 9, 15, 0, _tzid), + new CalDateTime(1997, 9, 2, 9, 30, 0, _tzid), + new CalDateTime(1997, 9, 2, 9, 45, 0, _tzid), + new CalDateTime(1997, 9, 2, 10, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 10, 15, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 125 of RFC 2445 - RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4 + /// + [Test, Category("Recurrence")] + public void MinutelyCount2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MinutelyCount2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 12, 31, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 10, 30, 0, _tzid), + new CalDateTime(1997, 9, 2, 12, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 13, 30, 0, _tzid) + }, + null + ); + } + + /// + /// See https://sourceforge.net/projects/dday-ical/forums/forum/656447/topic/3827441 + /// + [Test, Category("Recurrence")] + public void MinutelyCount3() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MinutelyCount3))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2010, 8, 27, _tzid), + new CalDateTime(2010, 8, 28, _tzid), + new[] + { + new CalDateTime(2010, 8, 27, 11, 0, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 1, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 2, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 3, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 4, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 5, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 6, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 7, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 8, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 9, 0, _tzid) + }, + null + ); + } + + /// + /// See https://sourceforge.net/projects/dday-ical/forums/forum/656447/topic/3827441 + /// + [Test, Category("Recurrence")] + public void MinutelyCount4() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MinutelyCount4))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2010, 8, 27, _tzid), + new CalDateTime(2010, 8, 28, _tzid), + new[] + { + new CalDateTime(2010, 8, 27, 11, 0, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 7, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 14, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 21, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 28, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 35, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 42, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 49, 0, _tzid), + new CalDateTime(2010, 8, 27, 11, 56, 0, _tzid), + new CalDateTime(2010, 8, 27, 12, 3, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 125 of RFC 2445 - RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40 + /// + [Test, Category("Recurrence")] + public void DailyByHourMinute1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.DailyByHourMinute1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1997, 9, 2, _tzid), + new CalDateTime(1997, 9, 4, _tzid), + new[] + { + new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 9, 20, 0, _tzid), + new CalDateTime(1997, 9, 2, 9, 40, 0, _tzid), + new CalDateTime(1997, 9, 2, 10, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 10, 20, 0, _tzid), + new CalDateTime(1997, 9, 2, 10, 40, 0, _tzid), + new CalDateTime(1997, 9, 2, 11, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 11, 20, 0, _tzid), + new CalDateTime(1997, 9, 2, 11, 40, 0, _tzid), + new CalDateTime(1997, 9, 2, 12, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 12, 20, 0, _tzid), + new CalDateTime(1997, 9, 2, 12, 40, 0, _tzid), + new CalDateTime(1997, 9, 2, 13, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 13, 20, 0, _tzid), + new CalDateTime(1997, 9, 2, 13, 40, 0, _tzid), + new CalDateTime(1997, 9, 2, 14, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 14, 20, 0, _tzid), + new CalDateTime(1997, 9, 2, 14, 40, 0, _tzid), + new CalDateTime(1997, 9, 2, 15, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 15, 20, 0, _tzid), + new CalDateTime(1997, 9, 2, 15, 40, 0, _tzid), + new CalDateTime(1997, 9, 2, 16, 0, 0, _tzid), + new CalDateTime(1997, 9, 2, 16, 20, 0, _tzid), + new CalDateTime(1997, 9, 2, 16, 40, 0, _tzid), + new CalDateTime(1997, 9, 3, 9, 0, 0, _tzid), + new CalDateTime(1997, 9, 3, 9, 20, 0, _tzid), + new CalDateTime(1997, 9, 3, 9, 40, 0, _tzid), + new CalDateTime(1997, 9, 3, 10, 0, 0, _tzid), + new CalDateTime(1997, 9, 3, 10, 20, 0, _tzid), + new CalDateTime(1997, 9, 3, 10, 40, 0, _tzid), + new CalDateTime(1997, 9, 3, 11, 0, 0, _tzid), + new CalDateTime(1997, 9, 3, 11, 20, 0, _tzid), + new CalDateTime(1997, 9, 3, 11, 40, 0, _tzid), + new CalDateTime(1997, 9, 3, 12, 0, 0, _tzid), + new CalDateTime(1997, 9, 3, 12, 20, 0, _tzid), + new CalDateTime(1997, 9, 3, 12, 40, 0, _tzid), + new CalDateTime(1997, 9, 3, 13, 0, 0, _tzid), + new CalDateTime(1997, 9, 3, 13, 20, 0, _tzid), + new CalDateTime(1997, 9, 3, 13, 40, 0, _tzid), + new CalDateTime(1997, 9, 3, 14, 0, 0, _tzid), + new CalDateTime(1997, 9, 3, 14, 20, 0, _tzid), + new CalDateTime(1997, 9, 3, 14, 40, 0, _tzid), + new CalDateTime(1997, 9, 3, 15, 0, 0, _tzid), + new CalDateTime(1997, 9, 3, 15, 20, 0, _tzid), + new CalDateTime(1997, 9, 3, 15, 40, 0, _tzid), + new CalDateTime(1997, 9, 3, 16, 0, 0, _tzid), + new CalDateTime(1997, 9, 3, 16, 20, 0, _tzid), + new CalDateTime(1997, 9, 3, 16, 40, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 125 of RFC 2445 - RRULE:FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16 + /// + [Test, Category("Recurrence")] + public void MinutelyByHour1() + { + var iCal1 = Calendar.LoadFromStream(new StringReader(IcsFiles.DailyByHourMinute1))[0]; + var iCal2 = Calendar.LoadFromStream(new StringReader(IcsFiles.MinutelyByHour1))[0]; + ProgramTest.TestCal(iCal1); + ProgramTest.TestCal(iCal2); + var evt1 = iCal1.Events.First(); + var evt2 = iCal2.Events.First(); + + var evt1Occ = evt1.GetOccurrences(new CalDateTime(1997, 9, 1, _tzid), new CalDateTime(1997, 9, 3, _tzid)).OrderBy(o => o.Period.StartTime).ToList(); + var evt2Occ = evt2.GetOccurrences(new CalDateTime(1997, 9, 1, _tzid), new CalDateTime(1997, 9, 3, _tzid)).OrderBy(o => o.Period.StartTime).ToList(); + Assert.IsTrue(evt1Occ.Count == evt2Occ.Count, "MinutelyByHour1() does not match DailyByHourMinute1() as it should"); + for (var i = 0; i < evt1Occ.Count; i++) + Assert.AreEqual(evt1Occ[i].Period, evt2Occ[i].Period, "PERIOD " + i + " from DailyByHourMinute1 (" + evt1Occ[i].Period + ") does not match PERIOD " + i + " from MinutelyByHour1 (" + evt2Occ[i].Period + ")"); + } + + /// + /// See Page 125 of RFC 2445 - RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO + /// + [Test, Category("Recurrence")] + public void WeeklyCountWkst3() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyCountWkst3))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 12, 31, _tzid), + new[] + { + new CalDateTime(1997, 8, 5, 9, 0, 0, _tzid), + new CalDateTime(1997, 8, 10, 9, 0, 0, _tzid), + new CalDateTime(1997, 8, 19, 9, 0, 0, _tzid), + new CalDateTime(1997, 8, 24, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// See Page 125 of RFC 2445 - RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU + /// This is the same as WeeklyCountWkst3, except WKST is SU, which changes the results. + /// + [Test, Category("Recurrence")] + public void WeeklyCountWkst4() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyCountWkst4))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1998, 12, 31, _tzid), + new[] + { + new CalDateTime(1997, 8, 5, 9, 0, 0, _tzid), + new CalDateTime(1997, 8, 17, 9, 0, 0, _tzid), + new CalDateTime(1997, 8, 19, 9, 0, 0, _tzid), + new CalDateTime(1997, 8, 31, 9, 0, 0, _tzid) + }, + null + ); + } + + /// + /// Tests WEEKLY Frequencies to ensure that those with an INTERVAL > 1 + /// are correctly handled. See Bug #1741093 - WEEKLY frequency eval behaves strangely. + /// + [Test, Category("Recurrence")] + public void Bug1741093() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Bug1741093))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 7, 1, _tzid), + new CalDateTime(2007, 8, 1, _tzid), + new[] + { + new CalDateTime(2007, 7, 2, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 3, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 4, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 5, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 6, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 16, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 17, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 18, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 19, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 20, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 30, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 31, 8, 0, 0, _tzid) + }, + null + ); + } + + /// + /// Ensures that, by default, SECONDLY recurrence rules are not allowed. + /// + [Test, Category("Recurrence")] + public void Secondly1() + { + var evt = new AutoResetEvent(false); + + try + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Secondly1))[0]; + var occurrences = iCal.GetOccurrences(new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid), new CalDateTime(2007, 7, 21, 8, 0, 0, _tzid)); + } + catch (EvaluationEngineException) + { + evt.Set(); + } + + Assert.IsTrue(evt.WaitOne(2000), "Evaluation engine should have failed."); + } + + /// + /// Ensures that the proper behavior occurs when the evaluation + /// mode is set to adjust automatically for SECONDLY evaluation + /// + [Test, Category("Recurrence")] + public void Secondly1_1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Secondly1))[0]; + iCal.RecurrenceEvaluationMode = RecurrenceEvaluationModeType.AdjustAutomatically; + + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 10, 1, _tzid), // End period is exclusive, not inclusive. + new[] + { + new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 1, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 2, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 3, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 4, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 5, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 6, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 7, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 8, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 9, 0, _tzid), + new CalDateTime(2007, 6, 21, 8, 10, 0, _tzid) + }, + null + ); + } + + /// + /// Ensures that if configured, MINUTELY recurrence rules are not allowed. + /// + [Test, Category("Recurrence")/*, ExpectedException(typeof(EvaluationEngineException))*/] + public void Minutely1() + { + try + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Minutely1))[0]; + iCal.RecurrenceRestriction = RecurrenceRestrictionType.RestrictMinutely; + var occurrences = iCal.GetOccurrences( + new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid), + new CalDateTime(2007, 7, 21, 8, 0, 0, _tzid)); + } + catch (Exception e) + { + Assert.IsInstanceOf(e); + } + } + + /// + /// Ensures that the proper behavior occurs when the evaluation + /// mode is set to adjust automatically for MINUTELY evaluation + /// + [Test, Category("Recurrence")] + public void Minutely1_1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Minutely1))[0]; + iCal.RecurrenceRestriction = RecurrenceRestrictionType.RestrictMinutely; + iCal.RecurrenceEvaluationMode = RecurrenceEvaluationModeType.AdjustAutomatically; + + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid), + new CalDateTime(2007, 6, 21, 12, 0, 1, _tzid), // End period is exclusive, not inclusive. + new[] + { + new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid), + new CalDateTime(2007, 6, 21, 9, 0, 0, _tzid), + new CalDateTime(2007, 6, 21, 10, 0, 0, _tzid), + new CalDateTime(2007, 6, 21, 11, 0, 0, _tzid), + new CalDateTime(2007, 6, 21, 12, 0, 0, _tzid) + }, + null + ); + } + + /// + /// Ensures that if configured, HOURLY recurrence rules are not allowed. + /// + [Test, Category("Recurrence")/*, ExpectedException(typeof(EvaluationEngineException))*/] + public void Hourly1() + { + try + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Hourly1))[0]; + iCal.RecurrenceRestriction = RecurrenceRestrictionType.RestrictHourly; + var occurrences = iCal.GetOccurrences(new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid), new CalDateTime(2007, 7, 21, 8, 0, 0, _tzid)); + } + catch (Exception e) + { + Assert.IsInstanceOf(e); + } + + } + + /// + /// Ensures that the proper behavior occurs when the evaluation + /// mode is set to adjust automatically for HOURLY evaluation + /// + [Test, Category("Recurrence")] + public void Hourly1_1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Hourly1))[0]; + iCal.RecurrenceRestriction = RecurrenceRestrictionType.RestrictHourly; + iCal.RecurrenceEvaluationMode = RecurrenceEvaluationModeType.AdjustAutomatically; + + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid), + new CalDateTime(2007, 6, 25, 8, 0, 1, _tzid), // End period is exclusive, not inclusive. + new[] + { + new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid), + new CalDateTime(2007, 6, 22, 8, 0, 0, _tzid), + new CalDateTime(2007, 6, 23, 8, 0, 0, _tzid), + new CalDateTime(2007, 6, 24, 8, 0, 0, _tzid), + new CalDateTime(2007, 6, 25, 8, 0, 0, _tzid) + }, + null + ); + } + + /// + /// Ensures that "off-month" calculation works correctly + /// + [Test, Category("Recurrence")] + public void MonthlyInterval1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MonthlyInterval1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2008, 1, 1, 7, 0, 0, _tzid), + new CalDateTime(2008, 2, 29, 7, 0, 0, _tzid), + new[] + { + new CalDateTime(2008, 2, 11, 7, 0, 0, _tzid), + new CalDateTime(2008, 2, 12, 7, 0, 0, _tzid) + }, + null + ); + } + + /// + /// Ensures that "off-year" calculation works correctly + /// + [Test, Category("Recurrence")] + public void YearlyInterval1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyInterval1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2006, 1, 1, 7, 0, 0, _tzid), + new CalDateTime(2007, 1, 31, 7, 0, 0, _tzid), + new[] + { + new CalDateTime(2007, 1, 8, 7, 0, 0, _tzid), + new CalDateTime(2007, 1, 9, 7, 0, 0, _tzid) + }, + null + ); + } + + /// + /// Ensures that "off-day" calcuation works correctly + /// + [Test, Category("Recurrence")] + public void DailyInterval1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.DailyInterval1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 4, 11, 7, 0, 0, _tzid), + new CalDateTime(2007, 4, 16, 7, 0, 0, _tzid), + new[] + { + new CalDateTime(2007, 4, 12, 7, 0, 0, _tzid), + new CalDateTime(2007, 4, 15, 7, 0, 0, _tzid) + }, + null + ); + } + + /// + /// Ensures that "off-hour" calculation works correctly + /// + [Test, Category("Recurrence")] + public void HourlyInterval1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.HourlyInterval1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 4, 9, 10, 0, 0, _tzid), + new CalDateTime(2007, 4, 10, 20, 0, 0, _tzid), + new[] + { + // NOTE: this instance is included in the result set because it ends + // after the start of the evaluation period. + // See bug #3007244. + // https://sourceforge.net/tracker/?func=detail&aid=3007244&group_id=187422&atid=921236 + new CalDateTime(2007, 4, 9, 7, 0, 0, _tzid), + new CalDateTime(2007, 4, 10, 1, 0, 0, _tzid), + new CalDateTime(2007, 4, 10, 19, 0, 0, _tzid) + }, + null + ); + } + + /// + /// Ensures that the following recurrence functions properly. + /// The desired result is "The last Weekend-day of September for the next 10 years." + /// This specifically tests the BYSETPOS=-1 to accomplish this. + /// + [Test, Category("Recurrence")] + public void YearlyBySetPos1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.YearlyBySetPos1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2009, 1, 1, 0, 0, 0, _tzid), + new CalDateTime(2020, 1, 1, 0, 0, 0, _tzid), + new[] + { + new CalDateTime(2009, 9, 27, 5, 30, 0), + new CalDateTime(2010, 9, 26, 5, 30, 0), + new CalDateTime(2011, 9, 25, 5, 30, 0), + new CalDateTime(2012, 9, 30, 5, 30, 0), + new CalDateTime(2013, 9, 29, 5, 30, 0), + new CalDateTime(2014, 9, 28, 5, 30, 0), + new CalDateTime(2015, 9, 27, 5, 30, 0), + new CalDateTime(2016, 9, 25, 5, 30, 0), + new CalDateTime(2017, 9, 30, 5, 30, 0), + new CalDateTime(2018, 9, 30, 5, 30, 0) + }, + null + ); + } + + /// + /// Ensures that GetOccurrences() always returns a single occurrence + /// for a non-recurring event. + /// + [Test, Category("Recurrence")] + public void Empty1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Empty1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2009, 1, 1, 0, 0, 0, _tzid), + new CalDateTime(2010, 1, 1, 0, 0, 0, _tzid), + new[] + { + new CalDateTime(2009, 9, 27, 5, 30, 0) + }, + null + ); + } + + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for an HOURLY frequency. + /// + [Test, Category("Recurrence")] + public void HourlyInterval2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.HourlyInterval2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 4, 9, 7, 0, 0), + new CalDateTime(2007, 4, 10, 23, 0, 1), // End time is exclusive, not inclusive + new[] + { + new CalDateTime(2007, 4, 9, 7, 0, 0), + new CalDateTime(2007, 4, 9, 11, 0, 0), + new CalDateTime(2007, 4, 9, 15, 0, 0), + new CalDateTime(2007, 4, 9, 19, 0, 0), + new CalDateTime(2007, 4, 9, 23, 0, 0), + new CalDateTime(2007, 4, 10, 3, 0, 0), + new CalDateTime(2007, 4, 10, 7, 0, 0), + new CalDateTime(2007, 4, 10, 11, 0, 0), + new CalDateTime(2007, 4, 10, 15, 0, 0), + new CalDateTime(2007, 4, 10, 19, 0, 0), + new CalDateTime(2007, 4, 10, 23, 0, 0) + }, + null + ); + } + + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for an MINUTELY frequency. + /// + [Test, Category("Recurrence")] + public void MinutelyInterval1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.MinutelyInterval1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 4, 9, 7, 0, 0), + new CalDateTime(2007, 4, 9, 12, 0, 1), // End time is exclusive, not inclusive + new[] + { + new CalDateTime(2007, 4, 9, 7, 0, 0), + new CalDateTime(2007, 4, 9, 7, 30, 0), + new CalDateTime(2007, 4, 9, 8, 0, 0), + new CalDateTime(2007, 4, 9, 8, 30, 0), + new CalDateTime(2007, 4, 9, 9, 0, 0), + new CalDateTime(2007, 4, 9, 9, 30, 0), + new CalDateTime(2007, 4, 9, 10, 0, 0), + new CalDateTime(2007, 4, 9, 10, 30, 0), + new CalDateTime(2007, 4, 9, 11, 0, 0), + new CalDateTime(2007, 4, 9, 11, 30, 0), + new CalDateTime(2007, 4, 9, 12, 0, 0) + }, + null + ); + } + + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for an DAILY frequency with an INTERVAL. + /// + [Test, Category("Recurrence")] + public void DailyInterval2() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.DailyInterval2))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 4, 9, 7, 0, 0), + new CalDateTime(2007, 4, 27, 7, 0, 1), // End time is exclusive, not inclusive + new[] + { + new CalDateTime(2007, 4, 9, 7, 0, 0), + new CalDateTime(2007, 4, 11, 7, 0, 0), + new CalDateTime(2007, 4, 13, 7, 0, 0), + new CalDateTime(2007, 4, 15, 7, 0, 0), + new CalDateTime(2007, 4, 17, 7, 0, 0), + new CalDateTime(2007, 4, 19, 7, 0, 0), + new CalDateTime(2007, 4, 21, 7, 0, 0), + new CalDateTime(2007, 4, 23, 7, 0, 0), + new CalDateTime(2007, 4, 25, 7, 0, 0), + new CalDateTime(2007, 4, 27, 7, 0, 0) + }, + null + ); + } + + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for an DAILY frequency with a BYDAY value. + /// + [Test, Category("Recurrence")] + public void DailyByDay1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.DailyByDay1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 9, 10, 7, 0, 0), + new CalDateTime(2007, 9, 27, 7, 0, 1), // End time is exclusive, not inclusive + new[] + { + new CalDateTime(2007, 9, 10, 7, 0, 0), + new CalDateTime(2007, 9, 13, 7, 0, 0), + new CalDateTime(2007, 9, 17, 7, 0, 0), + new CalDateTime(2007, 9, 20, 7, 0, 0), + new CalDateTime(2007, 9, 24, 7, 0, 0), + new CalDateTime(2007, 9, 27, 7, 0, 0) + }, + null + ); + } + + /// + /// Ensures that DateUtil.AddWeeks works properly when week number is for previous year for selected date. + /// + [Test, Category("Recurrence")] + public void WeeklyWeekStartsLastYear() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyWeekStartsLastYear))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2012, 1, 1, 7, 0, 0), + new CalDateTime(2012, 1, 15, 11, 59, 59), + new[] + { + new CalDateTime(2012, 1, 2, 7, 0, 0), + new CalDateTime(2012, 1, 3, 7, 0, 0), + new CalDateTime(2012, 1, 4, 7, 0, 0), + new CalDateTime(2012, 1, 5, 7, 0, 0), + new CalDateTime(2012, 1, 6, 7, 0, 0), + new CalDateTime(2012, 1, 9, 7, 0, 0), + new CalDateTime(2012, 1, 10, 7, 0, 0), + new CalDateTime(2012, 1, 11, 7, 0, 0), + new CalDateTime(2012, 1, 12, 7, 0, 0), + new CalDateTime(2012, 1, 13, 7, 0, 0) + }, + null + ); + } + + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for a WEEKLY frequency with an INTERVAL. + /// + [Test, Category("Recurrence")] + public void WeeklyInterval1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.WeeklyInterval1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 9, 10, 7, 0, 0), + new CalDateTime(2007, 12, 31, 11, 59, 59), + new[] + { + new CalDateTime(2007, 9, 10, 7, 0, 0), + new CalDateTime(2007, 9, 24, 7, 0, 0), + new CalDateTime(2007, 10, 8, 7, 0, 0), + new CalDateTime(2007, 10, 22, 7, 0, 0), + new CalDateTime(2007, 11, 5, 7, 0, 0), + new CalDateTime(2007, 11, 19, 7, 0, 0), + new CalDateTime(2007, 12, 3, 7, 0, 0), + new CalDateTime(2007, 12, 17, 7, 0, 0), + new CalDateTime(2007, 12, 31, 7, 0, 0) + }, + null + ); + } + + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for a MONTHLY frequency. + /// + [Test, Category("Recurrence")] + public void Monthly1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Monthly1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 9, 10, 7, 0, 0), + new CalDateTime(2008, 9, 10, 7, 0, 1), // Period end is exclusive, not inclusive + new[] + { + new CalDateTime(2007, 9, 10, 7, 0, 0), + new CalDateTime(2007, 10, 10, 7, 0, 0), + new CalDateTime(2007, 11, 10, 7, 0, 0), + new CalDateTime(2007, 12, 10, 7, 0, 0), + new CalDateTime(2008, 1, 10, 7, 0, 0), + new CalDateTime(2008, 2, 10, 7, 0, 0), + new CalDateTime(2008, 3, 10, 7, 0, 0), + new CalDateTime(2008, 4, 10, 7, 0, 0), + new CalDateTime(2008, 5, 10, 7, 0, 0), + new CalDateTime(2008, 6, 10, 7, 0, 0), + new CalDateTime(2008, 7, 10, 7, 0, 0), + new CalDateTime(2008, 8, 10, 7, 0, 0), + new CalDateTime(2008, 9, 10, 7, 0, 0) + }, + null + ); + } + + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for a YEARLY frequency. + /// + [Test, Category("Recurrence")] + public void Yearly1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Yearly1))[0]; + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 9, 10, 7, 0, 0), + new CalDateTime(2020, 9, 10, 7, 0, 1), // Period end is exclusive, not inclusive + new[] + { + new CalDateTime(2007, 9, 10, 7, 0, 0), + new CalDateTime(2008, 9, 10, 7, 0, 0), + new CalDateTime(2009, 9, 10, 7, 0, 0), + new CalDateTime(2010, 9, 10, 7, 0, 0), + new CalDateTime(2011, 9, 10, 7, 0, 0), + new CalDateTime(2012, 9, 10, 7, 0, 0), + new CalDateTime(2013, 9, 10, 7, 0, 0), + new CalDateTime(2014, 9, 10, 7, 0, 0), + new CalDateTime(2015, 9, 10, 7, 0, 0), + new CalDateTime(2016, 9, 10, 7, 0, 0), + new CalDateTime(2017, 9, 10, 7, 0, 0), + new CalDateTime(2018, 9, 10, 7, 0, 0), + new CalDateTime(2019, 9, 10, 7, 0, 0), + new CalDateTime(2020, 9, 10, 7, 0, 0) + }, + null + ); + } + + /// + /// Tests a bug with WEEKLY recurrence values used with UNTIL. + /// https://sourceforge.net/tracker/index.php?func=detail&aid=2912657&group_id=187422&atid=921236 + /// Sourceforge.net bug #2912657 + /// + [Test, Category("Recurrence")] + public void Bug2912657() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Bug2912657))[0]; + var localTzid = iCal.TimeZones[0].TzId; + + // Daily recurrence + EventOccurrenceTest( + iCal, + new CalDateTime(2009, 12, 4, 0, 0, 0, localTzid), + new CalDateTime(2009, 12, 12, 0, 0, 0, localTzid), + new[] + { + new CalDateTime(2009, 12, 4, 2, 00, 00, localTzid), + new CalDateTime(2009, 12, 5, 2, 00, 00, localTzid), + new CalDateTime(2009, 12, 6, 2, 00, 00, localTzid), + new CalDateTime(2009, 12, 7, 2, 00, 00, localTzid), + new CalDateTime(2009, 12, 8, 2, 00, 00, localTzid), + new CalDateTime(2009, 12, 9, 2, 00, 00, localTzid), + new CalDateTime(2009, 12, 10, 2, 00, 00, localTzid) + }, + null, + 0 + ); + + // Weekly with UNTIL value + EventOccurrenceTest( + iCal, + new CalDateTime(2009, 12, 4, localTzid), + new CalDateTime(2009, 12, 10, localTzid), + new[] + { + new CalDateTime(2009, 12, 4, 2, 00, 00, localTzid) + }, + null, + 1 + ); + + // Weekly with COUNT=2 + EventOccurrenceTest( + iCal, + new CalDateTime(2009, 12, 4, localTzid), + new CalDateTime(2009, 12, 12, localTzid), + new[] + { + new CalDateTime(2009, 12, 4, 2, 00, 00, localTzid), + new CalDateTime(2009, 12, 11, 2, 00, 00, localTzid) + }, + null, + 2 + ); + } + + /// + /// Tests a bug with WEEKLY recurrence values that cross year boundaries. + /// https://sourceforge.net/tracker/?func=detail&aid=2916581&group_id=187422&atid=921236 + /// Sourceforge.net bug #2916581 + /// + [Test, Category("Recurrence")] + public void Bug2916581() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Bug2916581))[0]; + var localTzid = iCal.TimeZones[0].TzId; + + // Weekly across year boundary + EventOccurrenceTest( + iCal, + new CalDateTime(2009, 12, 25, 0, 0, 0, localTzid), + new CalDateTime(2010, 1, 3, 0, 0, 0, localTzid), + new[] + { + new CalDateTime(2009, 12, 25, 11, 00, 00, localTzid), + new CalDateTime(2010, 1, 1, 11, 00, 00, localTzid) + }, + null, + 0 + ); + + // Weekly across year boundary + EventOccurrenceTest( + iCal, + new CalDateTime(2009, 12, 25, 0, 0, 0, localTzid), + new CalDateTime(2010, 1, 3, 0, 0, 0, localTzid), + new[] + { + new CalDateTime(2009, 12, 26, 11, 00, 00, localTzid), + new CalDateTime(2010, 1, 2, 11, 00, 00, localTzid) + }, + null, + 1 + ); + } + + /// + /// Tests a bug with WEEKLY recurrence values + /// https://sourceforge.net/tracker/?func=detail&aid=2959692&group_id=187422&atid=921236 + /// Sourceforge.net bug #2959692 + /// + [Test, Category("Recurrence")] + public void Bug2959692() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Bug2959692))[0]; + var localTzid = iCal.TimeZones[0].TzId; + + EventOccurrenceTest( + iCal, + new CalDateTime(2008, 1, 1, 0, 0, 0, localTzid), + new CalDateTime(2008, 4, 1, 0, 0, 0, localTzid), + new[] + { + new CalDateTime(2008, 1, 3, 17, 00, 00, localTzid), + new CalDateTime(2008, 1, 17, 17, 00, 00, localTzid), + new CalDateTime(2008, 1, 31, 17, 00, 00, localTzid), + new CalDateTime(2008, 2, 14, 17, 00, 00, localTzid), + new CalDateTime(2008, 2, 28, 17, 00, 00, localTzid), + new CalDateTime(2008, 3, 13, 17, 00, 00, localTzid), + new CalDateTime(2008, 3, 27, 17, 00, 00, localTzid) + }, + null, + 0 + ); + } + + /// + /// Tests a bug with DAILY recurrence values + /// https://sourceforge.net/tracker/?func=detail&aid=2966236&group_id=187422&atid=921236 + /// Sourceforge.net bug #2966236 + /// + [Test, Category("Recurrence")] + public void Bug2966236() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Bug2966236))[0]; + var localTzid = iCal.TimeZones[0].TzId; + + EventOccurrenceTest( + iCal, + new CalDateTime(2010, 1, 1, 0, 0, 0, localTzid), + new CalDateTime(2010, 3, 1, 0, 0, 0, localTzid), + new[] + { + new CalDateTime(2010, 1, 19, 8, 00, 00, localTzid), + new CalDateTime(2010, 1, 26, 8, 00, 00, localTzid), + new CalDateTime(2010, 2, 2, 8, 00, 00, localTzid), + new CalDateTime(2010, 2, 9, 8, 00, 00, localTzid), + new CalDateTime(2010, 2, 16, 8, 00, 00, localTzid), + new CalDateTime(2010, 2, 23, 8, 00, 00, localTzid) + }, + null, + 0 + ); + + EventOccurrenceTest( + iCal, + new CalDateTime(2010, 2, 1, 0, 0, 0, localTzid), + new CalDateTime(2010, 3, 1, 0, 0, 0, localTzid), + new[] + { + new CalDateTime(2010, 2, 2, 8, 00, 00, localTzid), + new CalDateTime(2010, 2, 9, 8, 00, 00, localTzid), + new CalDateTime(2010, 2, 16, 8, 00, 00, localTzid), + new CalDateTime(2010, 2, 23, 8, 00, 00, localTzid) + }, + null, + 0 + ); + } + + /// + /// Tests a bug with events that span a very long period of time. (i.e. weeks, months, etc.) + /// https://sourceforge.net/tracker/?func=detail&aid=3007244&group_id=187422&atid=921236 + /// Sourceforge.net bug #3007244 + /// + [Test, Category("Recurrence")] + public void Bug3007244() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Bug3007244))[0]; + + EventOccurrenceTest( + cal: iCal, + fromDate: new CalDateTime(2010, 7, 18, 0, 0, 0), + toDate: new CalDateTime(2010, 7, 26, 0, 0, 0), + dateTimes: new[] { new CalDateTime(2010, 05, 23, 0, 0, 0), }, + timeZones: null, + eventIndex: 0 + ); + + EventOccurrenceTest( + cal: iCal, + fromDate: new CalDateTime(2011, 7, 18, 0, 0, 0), + toDate: new CalDateTime(2011, 7, 26, 0, 0, 0), + dateTimes: new[] { new CalDateTime(2011, 05, 23, 0, 0, 0), }, + timeZones: null, + eventIndex: 0 + ); + } + + /// + /// Tests bug #3119920 - missing weekly occurences + /// See https://sourceforge.net/tracker/?func=detail&aid=3119920&group_id=187422&atid=921236 + /// + [Test, Category("Recurrence")] + public void Bug3119920() + { + using (var sr = new StringReader("FREQ=WEEKLY;UNTIL=20251126T120000;INTERVAL=1;BYDAY=MO")) + { + var start = DateTime.Parse("2010-11-27 9:00:00"); + var serializer = new RecurrencePatternSerializer(); + var rp = (RecurrencePattern)serializer.Deserialize(sr); + var rpe = new RecurrencePatternEvaluator(rp); + var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, rp.Until, false); + + var period = recurringPeriods.ElementAt(recurringPeriods.Count() - 1); + + Assert.AreEqual(new CalDateTime(2025, 11, 24, 9, 0, 0), period.StartTime); + } + } + + /// + /// Tests bug #3178652 - 29th day of February in recurrence problems + /// See https://sourceforge.net/tracker/?func=detail&aid=3178652&group_id=187422&atid=921236 + /// + [Test, Category("Recurrence")] + public void Bug3178652() + { + var evt = new CalendarEvent + { + Start = new CalDateTime(2011, 1, 29, 11, 0, 0), + Duration = TimeSpan.FromHours(1.5), + Summary = "29th February Test" + }; + + var pattern = new RecurrencePattern { + Frequency = FrequencyType.Monthly, + Until = new DateTime(2011, 12, 25, 0, 0, 0, DateTimeKind.Utc), + FirstDayOfWeek = DayOfWeek.Sunday, + ByMonthDay = new List(new[] { 29 }) + }; + + evt.RecurrenceRules.Add(pattern); + + var occurrences = evt.GetOccurrences(new DateTime(2011, 1, 1), new DateTime(2012, 1, 1)); + Assert.AreEqual(10, occurrences.Count, "There should be 10 occurrences of this event, one for each month except February and December."); + } + + /// + /// Tests bug #3292737 - Google Repeating Task Until Time Bug + /// See https://sourceforge.net/tracker/?func=detail&aid=3292737&group_id=187422&atid=921236 + /// + [Test, Category("Recurrence")] + public void Bug3292737() + { + using (var sr = new StringReader("FREQ=WEEKLY;UNTIL=20251126")) + { + var serializer = new RecurrencePatternSerializer(); + var rp = (RecurrencePattern)serializer.Deserialize(sr); + + Assert.IsNotNull(rp); + Assert.AreEqual(new DateTime(2025, 11, 26), rp.Until); + } + } + + /// + /// Tests the iCal holidays downloaded from apple.com + /// + [Test, Category("Recurrence")] + public void UsHolidays() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.USHolidays))[0]; + Assert.IsNotNull(iCal, "iCalendar was not loaded."); + var items = new Dictionary + { + { "Christmas", new CalDateTime(2006, 12, 25)}, + {"Thanksgiving", new CalDateTime(2006, 11, 23)}, + {"Veteran's Day", new CalDateTime(2006, 11, 11)}, + {"Halloween", new CalDateTime(2006, 10, 31)}, + {"Daylight Saving Time Ends", new CalDateTime(2006, 10, 29)}, + {"Columbus Day", new CalDateTime(2006, 10, 9)}, + {"Labor Day", new CalDateTime(2006, 9, 4)}, + {"Independence Day", new CalDateTime(2006, 7, 4)}, + {"Father's Day", new CalDateTime(2006, 6, 18)}, + {"Flag Day", new CalDateTime(2006, 6, 14)}, + {"John F. Kennedy's Birthday", new CalDateTime(2006, 5, 29)}, + {"Memorial Day", new CalDateTime(2006, 5, 29)}, + {"Mother's Day", new CalDateTime(2006, 5, 14)}, + {"Cinco de Mayo", new CalDateTime(2006, 5, 5)}, + {"Earth Day", new CalDateTime(2006, 4, 22)}, + {"Easter", new CalDateTime(2006, 4, 16)}, + {"Tax Day", new CalDateTime(2006, 4, 15)}, + {"Daylight Saving Time Begins", new CalDateTime(2006, 4, 2)}, + {"April Fool's Day", new CalDateTime(2006, 4, 1)}, + {"St. Patrick's Day", new CalDateTime(2006, 3, 17)}, + {"Washington's Birthday", new CalDateTime(2006, 2, 22)}, + {"President's Day", new CalDateTime(2006, 2, 20)}, + {"Valentine's Day", new CalDateTime(2006, 2, 14)}, + {"Lincoln's Birthday", new CalDateTime(2006, 2, 12)}, + {"Groundhog Day", new CalDateTime(2006, 2, 2)}, + {"Martin Luther King, Jr. Day", new CalDateTime(2006, 1, 16)}, + { "New Year's Day", new CalDateTime(2006, 1, 1)}, + }; + + var occurrences = iCal.GetOccurrences( + new CalDateTime(2006, 1, 1), + new CalDateTime(2006, 12, 31)); + + Assert.AreEqual(items.Count, occurrences.Count, "The number of holidays did not evaluate correctly."); + foreach (var o in occurrences) + { + var evt = o.Source as CalendarEvent; + Assert.IsNotNull(evt); + Assert.IsTrue(items.ContainsKey(evt.Summary), "Holiday text '" + evt.Summary + "' did not match known holidays."); + Assert.AreEqual(items[evt.Summary], o.Period.StartTime, "Date/time of holiday '" + evt.Summary + "' did not match."); + } + } + + /// + /// Ensures that the StartTime and EndTime of periods have + /// HasTime set to true if the beginning time had HasTime set + /// to false. + /// + [Test, Category("Recurrence")] + public void Evaluate1() + { + Calendar cal = new Calendar(); + CalendarEvent evt = cal.Create(); + evt.Summary = "Event summary"; + + // Start at midnight, UTC time + evt.Start = new CalDateTime(DateTime.SpecifyKind(DateTime.Today, DateTimeKind.Utc)); + + evt.RecurrenceRules.Add(new RecurrencePattern("FREQ=MINUTELY;INTERVAL=10;COUNT=5")); + var occurrences = evt.GetOccurrences(CalDateTime.Today.AddDays(1), CalDateTime.Today.AddDays(2)); + + foreach (var o in occurrences) + Assert.IsTrue(o.Period.StartTime.HasTime, "All recurrences of this event should have a time set."); + } + + [Test, Category("Recurrence")] + public void RecurrencePattern1() + { + // NOTE: evaluators are not generally meant to be used directly like this. + // However, this does make a good test to ensure they behave as they should. + RecurrencePattern pattern = new RecurrencePattern("FREQ=SECONDLY;INTERVAL=10"); + pattern.RestrictionType = RecurrenceRestrictionType.NoRestriction; + + var us = new CultureInfo("en-US"); + + var startDate = new CalDateTime(DateTime.Parse("3/30/08 11:59:40 PM", us)); + var fromDate = new CalDateTime(DateTime.Parse("3/30/08 11:59:40 PM", us)); + var toDate = new CalDateTime(DateTime.Parse("3/31/08 12:00:11 AM", us)); + + var evaluator = pattern.GetService(typeof(IEvaluator)) as IEvaluator; + Assert.IsNotNull(evaluator); + + var occurrences = evaluator.Evaluate( + startDate, + DateUtil.SimpleDateTimeToMatch(fromDate, startDate), + DateUtil.SimpleDateTimeToMatch(toDate, startDate), + false) + .OrderBy(o => o.StartTime) + .ToList(); + Assert.AreEqual(4, occurrences.Count); + Assert.AreEqual(new CalDateTime(DateTime.Parse("03/30/08 11:59:40 PM", us)), occurrences[0].StartTime); + Assert.AreEqual(new CalDateTime(DateTime.Parse("03/30/08 11:59:50 PM", us)), occurrences[1].StartTime); + Assert.AreEqual(new CalDateTime(DateTime.Parse("03/31/08 12:00:00 AM", us)), occurrences[2].StartTime); + Assert.AreEqual(new CalDateTime(DateTime.Parse("03/31/08 12:00:10 AM", us)), occurrences[3].StartTime); + } + + [Test, Category("Recurrence")] + public void RecurrencePattern2() + { + // NOTE: evaluators are generally not meant to be used directly like this. + // However, this does make a good test to ensure they behave as they should. + var pattern = new RecurrencePattern("FREQ=MINUTELY;INTERVAL=1"); + + var us = new CultureInfo("en-US"); + + var startDate = new CalDateTime(DateTime.Parse("3/31/2008 12:00:10 AM", us)); + var fromDate = new CalDateTime(DateTime.Parse("4/1/2008 10:08:10 AM", us)); + var toDate = new CalDateTime(DateTime.Parse("4/1/2008 10:43:23 AM", us)); + + var evaluator = pattern.GetService(typeof(IEvaluator)) as IEvaluator; + Assert.IsNotNull(evaluator); + + var occurrences = evaluator.Evaluate( + startDate, + DateUtil.SimpleDateTimeToMatch(fromDate, startDate), + DateUtil.SimpleDateTimeToMatch(toDate, startDate), + false); + Assert.AreNotEqual(0, occurrences.Count); + } + + [Test, Category("Recurrence")] + public void GetOccurrences1() + { + Calendar cal = new Calendar(); + CalendarEvent evt = cal.Create(); + evt.Start = new CalDateTime(2009, 11, 18, 5, 0, 0); + evt.End = new CalDateTime(2009, 11, 18, 5, 10, 0); + evt.RecurrenceRules.Add(new RecurrencePattern(FrequencyType.Daily)); + evt.Summary = "xxxxxxxxxxxxx"; + + var previousDateAndTime = new CalDateTime(2009, 11, 17, 0, 15, 0); + var previousDateOnly = new CalDateTime(2009, 11, 17, 23, 15, 0); + var laterDateOnly = new CalDateTime(2009, 11, 19, 3, 15, 0); + var laterDateAndTime = new CalDateTime(2009, 11, 19, 11, 0, 0); + var end = new CalDateTime(2009, 11, 23, 0, 0, 0); + + var occurrences = evt.GetOccurrences(previousDateAndTime, end); + Assert.AreEqual(5, occurrences.Count); + + occurrences = evt.GetOccurrences(previousDateOnly, end); + Assert.AreEqual(5, occurrences.Count); + + occurrences = evt.GetOccurrences(laterDateOnly, end); + Assert.AreEqual(4, occurrences.Count); + + occurrences = evt.GetOccurrences(laterDateAndTime, end); + Assert.AreEqual(3, occurrences.Count); + + // Add ByHour "9" and "12" + evt.RecurrenceRules[0].ByHour.Add(9); + evt.RecurrenceRules[0].ByHour.Add(12); + + // Clear the evaluation so we can calculate recurrences again. + evt.ClearEvaluation(); + + occurrences = evt.GetOccurrences(previousDateAndTime, end); + Assert.AreEqual(11, occurrences.Count); + + occurrences = evt.GetOccurrences(previousDateOnly, end); + Assert.AreEqual(11, occurrences.Count); + + occurrences = evt.GetOccurrences(laterDateOnly, end); + Assert.AreEqual(8, occurrences.Count); + + occurrences = evt.GetOccurrences(laterDateAndTime, end); + Assert.AreEqual(7, occurrences.Count); + } + + [Test, Category("Recurrence")] + public void Test1() + { + Calendar cal = new Calendar(); + CalendarEvent evt = cal.Create(); + evt.Summary = "Event summary"; + evt.Start = new CalDateTime(DateTime.SpecifyKind(DateTime.Today, DateTimeKind.Utc)); + + RecurrencePattern recur = new RecurrencePattern(); + evt.RecurrenceRules.Add(recur); + + try + { + var occurrences = evt.GetOccurrences(DateTime.Today.AddDays(1), DateTime.Today.AddDays(2)); + Assert.Fail("An exception should be thrown when evaluating a recurrence with no specified FREQUENCY"); + } + catch { } + } + + [Test, Category("Recurrence")] + public void Test2() + { + Calendar cal = new Calendar(); + CalendarEvent evt = cal.Create(); + evt.Summary = "Event summary"; + evt.Start = new CalDateTime(DateTime.SpecifyKind(DateTime.Today, DateTimeKind.Utc)); + + RecurrencePattern recur = new RecurrencePattern(); + recur.Frequency = FrequencyType.Daily; + recur.Count = 3; + recur.ByDay.Add(new WeekDay(DayOfWeek.Monday)); + recur.ByDay.Add(new WeekDay(DayOfWeek.Wednesday)); + recur.ByDay.Add(new WeekDay(DayOfWeek.Friday)); + evt.RecurrenceRules.Add(recur); + + var serializer = new RecurrencePatternSerializer(); + Assert.IsTrue(string.Compare(serializer.SerializeToString(recur), "FREQ=DAILY;COUNT=3;BYDAY=MO,WE,FR", StringComparison.Ordinal) == 0, + "Serialized recurrence string is incorrect"); + } + + [Test, Category("Recurrence")] + public void Test4() + { + RecurrencePattern rpattern = new RecurrencePattern(); + rpattern.ByDay.Add(new WeekDay(DayOfWeek.Saturday)); + rpattern.ByDay.Add(new WeekDay(DayOfWeek.Sunday)); + + rpattern.Frequency = FrequencyType.Weekly; + + IDateTime evtStart = new CalDateTime(2006, 12, 1); + IDateTime evtEnd = new CalDateTime(2007, 1, 1); + + var evaluator = rpattern.GetService(typeof(IEvaluator)) as IEvaluator; + Assert.IsNotNull(evaluator); + + // Add the exception dates + var periods = evaluator.Evaluate( + evtStart, + DateUtil.GetSimpleDateTimeData(evtStart), + DateUtil.SimpleDateTimeToMatch(evtEnd, evtStart), + false) + .OrderBy(p => p.StartTime) + .ToList(); + Assert.AreEqual(10, periods.Count); + Assert.AreEqual(2, periods[0].StartTime.Day); + Assert.AreEqual(3, periods[1].StartTime.Day); + Assert.AreEqual(9, periods[2].StartTime.Day); + Assert.AreEqual(10, periods[3].StartTime.Day); + Assert.AreEqual(16, periods[4].StartTime.Day); + Assert.AreEqual(17, periods[5].StartTime.Day); + Assert.AreEqual(23, periods[6].StartTime.Day); + Assert.AreEqual(24, periods[7].StartTime.Day); + Assert.AreEqual(30, periods[8].StartTime.Day); + Assert.AreEqual(31, periods[9].StartTime.Day); + } + + [Test, Category("Recurrence")] + public void ExDateShouldFilterOutAllPeriods() + { + //One-day event starting Aug 23 (inclusive), ending Aug 24 (exclusive), repeating daily until Aug 24 (exclusive). + //I.e. an event that occupies all of Aug 23, and no more, with zero recurrences. + //Then exclude Aug 23 and Aug 24 from the set of recurrences. + const string ical = @"BEGIN:VCALENDAR +BEGIN:VEVENT +DTSTART;VALUE=DATE:20120823 +DTEND;VALUE=DATE:20120824 +RRULE:FREQ=DAILY;UNTIL=20120824 +EXDATE;VALUE=DATE:20120824 +EXDATE;VALUE=DATE:20120823 +DTSTAMP:20131031T111655Z +CREATED:20120621T142631Z +TRANSP:TRANSPARENT +END:VEVENT +END:VCALENDAR"; + var collection = Calendar.LoadFromStream(new StringReader(ical)); + var firstEvent = collection.First().Events.First(); + var startSearch = new CalDateTime(2010, 1, 1, _tzid); + var endSearch = new CalDateTime(2016, 12, 31, _tzid); + + var occurrences = firstEvent.GetOccurrences(startSearch, endSearch).Select(o => o.Period as Period).ToList(); + Assert.IsTrue(occurrences.Count == 0); + } + + [Test, Category("Recurrence")] + public void RDateShouldBeUnionedWithRecurrenceSet() + { + //Issues #118 and #107 on Github + const string ical = +@"BEGIN:VCALENDAR +PRODID:-//ddaysoftware.com//NONSGML DDay.iCal 1.0//EN +VERSION:2.0 +BEGIN:VEVENT +DTSTART;TZID=US-Eastern:20160829T080000 +DTEND;TZID=US-Eastern:20160829T090000 +EXDATE;TZID=US-Eastern:20160830T080000 +EXDATE;TZID=US-Eastern:20160831T080000 +RDATE;TZID=US-Eastern:20160830T100000 +RDATE;TZID=US-Eastern:20160831T100000 +RRULE:FREQ=DAILY +UID:abab717c-1786-4efc-87dd-6859c2b48eb6 +END:VEVENT +END:VCALENDAR"; + + var collection = Calendar.LoadFromStream(new StringReader(ical)); + var firstEvent = collection.First().Events.First(); + var startSearch = new CalDateTime(DateTime.Parse("2015-08-28T07:00:00"), _tzid); + var endSearch = new CalDateTime(DateTime.Parse("2016-08-28T07:00:00").AddDays(7), _tzid); + + var occurrences = firstEvent.GetOccurrences(startSearch, endSearch) + .Select(o => o.Period as Period) + .OrderBy(p => p.StartTime) + .ToList(); + + var firstExpectedOccurrence = new CalDateTime(DateTime.Parse("2016-08-29T08:00:00"), _tzid); + Assert.AreEqual(firstExpectedOccurrence, occurrences.First().StartTime); + + var firstExpectedRDate = new CalDateTime(DateTime.Parse("2016-08-30T10:00:00"), _tzid); + Assert.IsTrue(occurrences[1].StartTime.Equals(firstExpectedRDate)); + + var secondExpectedRDate = new CalDateTime(DateTime.Parse("2016-08-31T10:00:00"), _tzid); + Assert.IsTrue(occurrences[2].StartTime.Equals(secondExpectedRDate)); + } + + [Test] + public void OccurrenceMustBeCompletelyContainedWithinSearchRange() + { + //https://github.com/rianjs/ical.net/issues/121 + + const string ical = @"BEGIN:VCALENDAR +PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN +VERSION:2.0 +BEGIN:VEVENT +SUMMARY:This is an event +DTEND;TZID=UTC:20160801T080000 +DTSTAMP:20160905T142724Z +DTSTART;TZID=UTC:20160801T070000 +RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=WE;UNTIL=20160831T070000 +UID:abab717c-1786-4efc-87dd-6859c2b48eb6 +END:VEVENT +END:VCALENDAR"; + + var rrule = new RecurrencePattern(FrequencyType.Weekly, interval: 1) + { + Until = DateTime.Parse("2016-08-31T07:00:00"), + ByDay = new List { new WeekDay(DayOfWeek.Wednesday)}, + }; + + var start = DateTime.Parse("2016-08-01T07:00:00"); + var end = start.AddHours(1); + var e = new CalendarEvent() + { + DtStart = new CalDateTime(start, "UTC"), + DtEnd = new CalDateTime(end, "UTC"), + RecurrenceRules = new List { rrule }, + Summary = "This is an event", + Uid = "abab717c-1786-4efc-87dd-6859c2b48eb6", + }; + + var collection = Calendar.LoadFromStream(new StringReader(ical)); + var firstEvent = collection.First().Events.First(); + var calendar = new Calendar(); + calendar.Events.Add(e); + + Assert.AreEqual(e, firstEvent); + + var startSearch = new CalDateTime(DateTime.Parse("2016-07-01T00:00:00"), "UTC"); + var endSearch = new CalDateTime(DateTime.Parse("2016-08-31T07:00:00"), "UTC"); + + var lastExpected = new CalDateTime(DateTime.Parse("2016-08-31T07:00:00"), "UTC"); + var occurrences = firstEvent.GetOccurrences(startSearch, endSearch) + .Select(o => o.Period as Period) + .OrderBy(p => p.StartTime) + .ToList(); + + Assert.IsFalse(occurrences.Last().StartTime.Equals(lastExpected)); + + //Create 1 second of overlap + endSearch = new CalDateTime(endSearch.Value.AddSeconds(1), "UTC"); + occurrences = firstEvent.GetOccurrences(startSearch, endSearch) + .Select(o => o.Period as Period) + .OrderBy(p => p.StartTime) + .ToList(); + + Assert.IsTrue(occurrences.Last().StartTime.Equals(lastExpected)); + } + + [Test, Ignore("Turn on in v3")] + public void EventsWithShareUidsShouldGenerateASingleRecurrenceSet() + { + //https://github.com/rianjs/ical.net/issues/120 + const string ical = +@"BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:Calendar 2 +X-WR-TIMEZONE:Europe/Bucharest +BEGIN:VEVENT +DTSTART;TZID=Europe/Bucharest:20160829T110000 +DTEND;TZID=Europe/Bucharest:20160829T163000 +RRULE:FREQ=DAILY +DTSTAMP:20160901T104339Z +UID:gknfcr66sb7rpangtprsthmpn8@google.com +CREATED:20160901T104300Z +DESCRIPTION: +LAST-MODIFIED:20160901T104311Z +LOCATION: +SEQUENCE:1 +STATUS:CONFIRMED +SUMMARY:testRScuAD +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=Europe/Bucharest:20160901T163000 +DTEND;TZID=Europe/Bucharest:20160901T220000 +DTSTAMP:20160901T104339Z +UID:gknfcr66sb7rpangtprsthmpn8@google.com +RECURRENCE-ID;TZID=Europe/Bucharest:20160901T110000 +CREATED:20160901T104300Z +DESCRIPTION: +LAST-MODIFIED:20160901T104314Z +LOCATION: +SEQUENCE:2 +STATUS:CONFIRMED +SUMMARY:testRScuAD +TRANSP:OPAQUE +END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=Europe/Bucharest:20160903T070000 +DTEND;TZID=Europe/Bucharest:20160903T123000 +DTSTAMP:20160901T104339Z +UID:gknfcr66sb7rpangtprsthmpn8@google.com +RECURRENCE-ID;TZID=Europe/Bucharest:20160903T110000 +CREATED:20160901T104300Z +DESCRIPTION: +LAST-MODIFIED:20160901T104315Z +LOCATION: +SEQUENCE:2 +STATUS:CONFIRMED +SUMMARY:testRScuAD +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR"; + + var calendars = Calendar.LoadFromStream(new StringReader(ical)); + var events = calendars.SelectMany(c => c.Events).ToList(); + + var startSearch = DateTime.Parse("2016-08-01T00:00:00"); + var endSearch = startSearch.AddDays(45); + + //The API should be something like: + //var occurrences = calendar.GetOccurrences(string eventUid, DateTime startSearch, DateTime endSearch); + + var occurrences = new HashSet(); + + var orderedOccurrences = occurrences + .Select(o => o.Period as Period) + .OrderBy(p => p.StartTime) + .ToList(); + + var expectedSept1Start = new CalDateTime(DateTime.Parse("2016-09-01T16:30:00"), "Europe/Bucharest"); + var expectedSept1End = new CalDateTime(DateTime.Parse("2016-09-01T22:00:00"), "Europe/Bucharest"); + Assert.AreEqual(expectedSept1Start, orderedOccurrences[3].StartTime); + Assert.AreEqual(expectedSept1End, orderedOccurrences[3].EndTime); + + var expectedSept3Start = new CalDateTime(DateTime.Parse("2016-09-03T07:00:00"), "Europe/Bucharest"); + var expectedSept3End = new CalDateTime(DateTime.Parse("2016-09-01T12:30:00"), "Europe/Bucharest"); + Assert.AreEqual(expectedSept3Start, orderedOccurrences[5].StartTime); + Assert.AreEqual(expectedSept3End, orderedOccurrences[5].EndTime); + } + + [Test] + public void AddExDateToEventAfterGetOccurrencesShouldRecomputeResult() + { + var searchStart = _now.AddDays(-1); + var searchEnd = _now.AddDays(7); + var e = GetEventWithRecurrenceRules(); + var occurrences = e.GetOccurrences(searchStart, searchEnd); + Assert.IsTrue(occurrences.Count == 5); + + var exDate = _now.AddDays(1); + var period = new Period(new CalDateTime(exDate)); + var periodList = new PeriodList { period }; + e.ExceptionDates.Add(periodList); + occurrences = e.GetOccurrences(searchStart, searchEnd); + Assert.IsTrue(occurrences.Count == 4); + + //Specifying just a date should "black out" that date + var excludeTwoDaysFromNow = _now.AddDays(2).Date; + period = new Period(new CalDateTime(excludeTwoDaysFromNow)); + periodList.Add(period); + occurrences = e.GetOccurrences(searchStart, searchEnd); + Assert.IsTrue(occurrences.Count == 3); + } + + private static readonly DateTime _now = DateTime.Now; + private static readonly DateTime _later = _now.AddHours(1); + private static CalendarEvent GetEventWithRecurrenceRules() + { + var dailyForFiveDays = new RecurrencePattern(FrequencyType.Daily, 1) + { + Count = 5, + }; + + var calendarEvent = new CalendarEvent + { + Start = new CalDateTime(_now), + End = new CalDateTime(_later), + RecurrenceRules = new List { dailyForFiveDays }, + Resources = new List(new[] { "Foo", "Bar", "Baz" }), + }; + return calendarEvent; + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/SerializationTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/SerializationTests.cs new file mode 100644 index 00000000..663f141a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/SerializationTests.cs @@ -0,0 +1,371 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Serialization.iCalendar.Serializers; +using Ical.Net.Serialization.iCalendar.Serializers.Other; +using Ical.Net.UnitTests.ExtensionMethods; +using NUnit.Framework; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class SerializationTests + { + private static readonly DateTime _nowTime = DateTime.Now; + private static readonly DateTime _later = _nowTime.AddHours(1); + private static CalendarSerializer GetNewSerializer() => new CalendarSerializer(); + private static string SerializeToString(Calendar c) => GetNewSerializer().SerializeToString(c); + private static CalendarEvent GetSimpleEvent() => new CalendarEvent { DtStart = new CalDateTime(_nowTime), DtEnd = new CalDateTime(_later), Duration = _later - _nowTime }; + private static Calendar UnserializeCalendar(string s) => Calendar.LoadFromStream(new StringReader(s)).Single(); + + public static void CompareCalendars(Calendar cal1, Calendar cal2) + { + CompareComponents(cal1, cal2); + + Assert.AreEqual(cal1.Children.Count, cal2.Children.Count, "Children count is different between calendars."); + + for (var i = 0; i < cal1.Children.Count; i++) + { + var component1 = cal1.Children[i] as ICalendarComponent; + var component2 = cal2.Children[i] as ICalendarComponent; + if (component1 != null && component2 != null) + { + CompareComponents(component1, component2); + } + } + } + + public static void CompareComponents(ICalendarComponent cb1, ICalendarComponent cb2) + { + foreach (var p1 in cb1.Properties) + { + var isMatch = false; + foreach (var p2 in cb2.Properties.AllOf(p1.Name)) + { + try + { + Assert.AreEqual(p1, p2, "The properties '" + p1.Name + "' are not equal."); + if (p1.Value is IComparable) + { + Assert.AreEqual(0, ((IComparable) p1.Value).CompareTo(p2.Value), "The '" + p1.Name + "' property values do not match."); + } + else if (p1.Value is IEnumerable) + { + CompareEnumerables((IEnumerable) p1.Value, (IEnumerable) p2.Value, p1.Name); + } + else + { + Assert.AreEqual(p1.Value, p2.Value, "The '" + p1.Name + "' property values are not equal."); + } + + isMatch = true; + break; + } + catch { } + } + + Assert.IsTrue(isMatch, "Could not find a matching property - " + p1.Name + ":" + (p1.Value?.ToString() ?? string.Empty)); + } + + Assert.AreEqual(cb1.Children.Count, cb2.Children.Count, "The number of children are not equal."); + for (var i = 0; i < cb1.Children.Count; i++) + { + var child1 = cb1.Children[i] as ICalendarComponent; + var child2 = cb2.Children[i] as ICalendarComponent; + if (child1 != null && child2 != null) + { + CompareComponents(child1, child2); + } + else + { + Assert.AreEqual(child1, child2, "The child objects are not equal."); + } + } + } + + public static void CompareEnumerables(IEnumerable a1, IEnumerable a2, string value) + { + if (a1 == null && a2 == null) + { + return; + } + + Assert.IsFalse((a1 == null && a2 != null) || (a1 != null && a2 == null), value + " do not match - one item is null"); + + var enum1 = a1.GetEnumerator(); + var enum2 = a2.GetEnumerator(); + + while (enum1.MoveNext() && enum2.MoveNext()) + { + Assert.AreEqual(enum1.Current, enum2.Current, value + " do not match"); + } + } + + public static string InspectSerializedSection(string serialized, string sectionName, IEnumerable elements) + { + const string notFound = "expected '{0}' not found"; + var searchFor = "BEGIN:" + sectionName; + var begin = serialized.IndexOf(searchFor); + Assert.AreNotEqual(-1, begin, notFound, searchFor); + + searchFor = "END:" + sectionName; + var end = serialized.IndexOf(searchFor, begin); + Assert.AreNotEqual(-1, end, notFound, searchFor); + + var searchRegion = serialized.Substring(begin, end - begin + 1); + + foreach (var e in elements) + { + Assert.IsTrue(searchRegion.Contains(SerializationConstants.LineBreak + e + SerializationConstants.LineBreak), notFound, e); + } + + return searchRegion; + } + + //3 formats - UTC, local time as defined in vTimeZone, and floating, + //at some point it would be great to independently unit test string serialization of an IDateTime object, into its 3 forms + //http://www.kanzaki.com/docs/ical/dateTime.html + static string CalDateString(IDateTime cdt) + { + var returnVar = $"{cdt.Year}{cdt.Month:D2}{cdt.Day:D2}T{cdt.Hour:D2}{cdt.Minute:D2}{cdt.Second:D2}"; + if (cdt.IsUniversalTime) + { + return returnVar + 'Z'; + } + + return string.IsNullOrEmpty(cdt.TzId) + ? returnVar + : $"TZID={cdt.TzId}:{returnVar}"; + } + + //This method needs renaming + static Dictionary GetValues(string serialized, string name, string value) + { + var lengthened = serialized.Replace(SerializationConstants.LineBreak + ' ', string.Empty); + //using a regex for now - for the sake of speed, it may be worth creating a C# text search later + var match = Regex.Match(lengthened, '^' + Regex.Escape(name) + "(;.+)?:" + Regex.Escape(value) + SerializationConstants.LineBreak, RegexOptions.Multiline); + Assert.IsTrue(match.Success, $"could not find a(n) '{name}' with value '{value}'"); + return match.Groups[1].Value.Length == 0 + ? new Dictionary() + : match.Groups[1].Value.Substring(1).Split(';').Select(v=>v.Split('=')).ToDictionary(v=>v[0], v=>v.Length>1 ? v[1] : null); + } + + [Test, Category("Serialization"), Ignore("TODO: standard time, for NZ standard time (current example)")] + public void TimeZoneSerialize() + { + //ToDo: This test is broken as of 2016-07-13 + var cal = new Calendar + { + Method = "PUBLISH", + Version = "2.0" + }; + + const string exampleTz = "New Zealand Standard Time"; + var tzi = TimeZoneInfo.FindSystemTimeZoneById(exampleTz); + var tz = new VTimeZone(exampleTz); + cal.AddTimeZone(tz); + var evt = new CalendarEvent + { + Summary = "Testing", + Start = new CalDateTime(2016, 7, 14, tz.TzId), + End = new CalDateTime(2016, 7, 15, tz.TzId) + }; + cal.Events.Add(evt); + + var serializer = new CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(cal); + + Console.Write(serializedCalendar); + + var vTimezone = InspectSerializedSection(serializedCalendar, "VTIMEZONE", new[] {"TZID:" + tz.TzId}); + var o = tzi.BaseUtcOffset.ToString("hhmm", CultureInfo.InvariantCulture); + + InspectSerializedSection(vTimezone, "STANDARD", new[] {"TZNAME:" + tzi.StandardName, "TZOFFSETTO:" + o + //"DTSTART:20150402T030000", + //"RRULE:FREQ=YEARLY;BYDAY=1SU;BYHOUR=3;BYMINUTE=0;BYMONTH=4", + //"TZOFFSETFROM:+1300" + }); + + + InspectSerializedSection(vTimezone, "DAYLIGHT", new[] {"TZNAME:" + tzi.DaylightName, "TZOFFSETFROM:" + o}); + } + [Test, Category("Serialization")] + public void SerializeDeserialize() + { + //ToDo: This test is broken as of 2016-07-13 + var cal1 = new Calendar + { + Method = "PUBLISH", + Version = "2.0" + }; + + var evt = new CalendarEvent + { + Class = "PRIVATE", + Created = new CalDateTime(2010, 3, 25, 12, 53, 35), + DtStamp = new CalDateTime(2010, 3, 25, 12, 53, 35), + LastModified = new CalDateTime(2010, 3, 27, 13, 53, 35), + Sequence = 0, + Uid = "42f58d4f-847e-46f8-9f4a-ce52697682cf", + Priority = 5, + Location = "here", + Summary = "test", + DtStart = new CalDateTime(2012, 3, 25, 12, 50, 00), + DtEnd = new CalDateTime(2012, 3, 25, 13, 10, 00) + }; + cal1.Events.Add(evt); + + var serializer = new CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(cal1); + using (var sr = new StringReader(serializedCalendar)) + { + var cal2 = Calendar.LoadFromStream(sr)[0]; + CompareCalendars(cal1, cal2); + } + } + + [Test, Category("Serialization")] + public void EventPropertiesSerialized() + { + //ToDo: This test is broken as of 2016-07-13 + var cal = new Calendar + { + Method = "PUBLISH", + Version = "2.0" + }; + + var evt = new CalendarEvent + { + Class = "PRIVATE", + Created = new CalDateTime(2010, 3, 25, 12, 53, 35), + DtStamp = new CalDateTime(2010, 3, 25, 12, 53, 35), + LastModified = new CalDateTime(2010, 3, 27, 13, 53, 35), + Sequence = 0, + Uid = "42f58d4f-847e-46f8-9f4a-ce52697682cf", + Priority = 5, + Location = "here", + Summary = "test", + DtStart = new CalDateTime(2012, 3, 25, 12, 50, 00), + DtEnd = new CalDateTime(2012, 3, 25, 13, 10, 00) + //not yet testing property below as serialized output currently does not comply with RTFC 2445 + //Transparency = TransparencyType.Opaque, + //Status = EventStatus.Confirmed + }; + cal.Events.Add(evt); + + var serializer = new CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(cal); + + Console.Write(serializedCalendar); + Assert.IsTrue(serializedCalendar.StartsWith("BEGIN:VCALENDAR")); + Assert.IsTrue(serializedCalendar.EndsWith("END:VCALENDAR" + SerializationConstants.LineBreak)); + + var expectProperties = new[] {"METHOD:PUBLISH", "VERSION:2.0"}; + + foreach (var p in expectProperties) + { + Assert.IsTrue(serializedCalendar.Contains(SerializationConstants.LineBreak + p + SerializationConstants.LineBreak), "expected '" + p + "' not found"); + } + + InspectSerializedSection(serializedCalendar, "VEVENT", + new[] + { + "CLASS:" + evt.Class, "CREATED:" + CalDateString(evt.Created), "DTSTAMP:" + CalDateString(evt.DtStamp), + "LAST-MODIFIED:" + CalDateString(evt.LastModified), "SEQUENCE:" + evt.Sequence, "UID:" + evt.Uid, "PRIORITY:" + evt.Priority, + "LOCATION:" + evt.Location, "SUMMARY:" + evt.Summary, "DTSTART:" + CalDateString(evt.DtStart), "DTEND:" + CalDateString(evt.DtEnd) + //"TRANSPARENCY:" + TransparencyType.Opaque.ToString().ToUpperInvariant(), + //"STATUS:" + EventStatus.Confirmed.ToString().ToUpperInvariant() + }); + } + + private static readonly IList _attendees = new List + { + new Attendee("MAILTO:james@example.com") + { + CommonName = "James James", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Tentative + }, + new Attendee("MAILTO:mary@example.com") + { + CommonName = "Mary Mary", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Accepted + } + }.AsReadOnly(); + + [Test, Category("Serialization")] + public void AttendeesSerialized() + { + //ToDo: This test is broken as of 2016-07-13 + var cal = new Calendar + { + Method = "REQUEST", + Version = "2.0" + }; + + var evt = AttendeeTest.VEventFactory(); + cal.Events.Add(evt); + const string org = "MAILTO:james@example.com"; + evt.Organizer = new Organizer(org); + + evt.Attendees.AddRange(_attendees); + + var serializer = new CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(cal); + + Console.Write(serializedCalendar); + + var vEvt = InspectSerializedSection(serializedCalendar, "VEVENT", new[] {"ORGANIZER:" + org}); + + foreach (var a in evt.Attendees) + { + var vals = GetValues(vEvt, "ATTENDEE", a.Value.OriginalString); + foreach (var v in new Dictionary + { + ["CN"] = a.CommonName, + ["ROLE"] = a.Role, + ["RSVP"] = a.Rsvp.ToString() + .ToUpperInvariant(), + ["PARTSTAT"] = a.ParticipationStatus + }) + { + Assert.IsTrue(vals.ContainsKey(v.Key), $"could not find key '{v.Key}'"); + Assert.AreEqual(v.Value, vals[v.Key], $"ATENDEE prop '{v.Key}' differ"); + } + } + } + + //todo test event: + //-GeographicLocation + //-Alarm + + [Test] + public void ZeroTimeSpan_Test() + { + var result = new TimeSpanSerializer().SerializeToString(TimeSpan.Zero); + Assert.IsTrue("P0D".Equals(result, StringComparison.Ordinal)); + } + + [Test] + public void DurationIsStable_Tests() + { + var e = GetSimpleEvent(); + var originalDuration = e.Duration; + var c = new Calendar(); + c.Events.Add(e); + var serialized = SerializeToString(c); + Assert.AreEqual(originalDuration, e.Duration); + Assert.IsTrue(!serialized.Contains("DURATION")); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/SymmetricSerializationTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/SymmetricSerializationTests.cs new file mode 100644 index 00000000..f1b10fc4 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/SymmetricSerializationTests.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Ical.Net.DataTypes; +using Ical.Net.Serialization.iCalendar.Serializers; +using NUnit.Framework; +using NUnit.Framework.Interfaces; + +namespace Ical.Net.UnitTests +{ + public class SymmetricSerializationTests + { + private const string _ldapUri = "ldap://example.com:6666/o=eDABC Industries,c=3DUS??(cn=3DBMary Accepted)"; + + private static readonly DateTime _nowTime = DateTime.Now; + private static readonly DateTime _later = _nowTime.AddHours(1); + private static CalendarSerializer GetNewSerializer() => new CalendarSerializer(); + private static string SerializeToString(Calendar c) => GetNewSerializer().SerializeToString(c); + private static CalendarEvent GetSimpleEvent() => new CalendarEvent {DtStart = new CalDateTime(_nowTime), DtEnd = new CalDateTime(_later), Duration = _later - _nowTime}; + private static Calendar UnserializeCalendar(string s) => Calendar.LoadFromStream(new StringReader(s)).Single(); + + [Test, TestCaseSource(nameof(Event_TestCases))] + public void Event_Tests(Calendar iCalendar) + { + var originalEvent = iCalendar.Events.Single(); + + var serializedCalendar = SerializeToString(iCalendar); + var unserializedCalendar = UnserializeCalendar(serializedCalendar); + + var onlyEvent = unserializedCalendar.Events.Single(); + + Assert.AreEqual(originalEvent.GetHashCode(), onlyEvent.GetHashCode()); + Assert.AreEqual(originalEvent, onlyEvent); + Assert.AreEqual(iCalendar, unserializedCalendar); + } + + public static IEnumerable Event_TestCases() + { + var rrule = new RecurrencePattern(FrequencyType.Daily, 1) { Count = 5 }; + var e = new CalendarEvent + { + DtStart = new CalDateTime(_nowTime), + DtEnd = new CalDateTime(_later), + Duration = TimeSpan.FromHours(1), + RecurrenceRules = new List { rrule }, + }; + + var calendar = new Calendar(); + calendar.Events.Add(e); + yield return new TestCaseData(calendar).SetName("readme.md example"); + + e = GetSimpleEvent(); + e.Description = "This is an event description that is really rather long. Hopefully the line breaks work now, and it's serialized properly."; + calendar = new Calendar(); + calendar.Events.Add(e); + yield return new TestCaseData(calendar).SetName("Description serialization isn't working properly. Issue #60"); + } + + [Test] + public void VTimeZoneSerialization_Test() + { + var originalCalendar = new Calendar(); + var tz = new VTimeZone + { + TzId = "New Zealand Standard Time" + }; + originalCalendar.AddTimeZone(tz); + var serializer = new CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(originalCalendar); + var unserializedCalendar = Calendar.LoadFromStream(new StringReader(serializedCalendar)).Single(); + + CollectionAssert.AreEqual(originalCalendar.TimeZones, unserializedCalendar.TimeZones); + Assert.AreEqual(originalCalendar, unserializedCalendar); + Assert.AreEqual(originalCalendar.GetHashCode(), unserializedCalendar.GetHashCode()); + } + + [Test, TestCaseSource(nameof(AttendeeSerialization_TestCases))] + public void AttendeeSerialization_Test(Attendee attendee) + { + var calendar = new Calendar(); + calendar.AddTimeZone(new VTimeZone("America/Los_Angeles")); + var someEvent = GetSimpleEvent(); + someEvent.Attendees = new List {attendee}; + calendar.Events.Add(someEvent); + + var serialized = SerializeToString(calendar); + var unserialized = UnserializeCalendar(serialized); + + Assert.AreEqual(calendar.GetHashCode(), unserialized.GetHashCode()); + Assert.IsTrue(calendar.Events.SequenceEqual(unserialized.Events)); + Assert.AreEqual(calendar, unserialized); + } + + public static IEnumerable AttendeeSerialization_TestCases() + { + var complex1 = new Attendee("MAILTO:mary@example.com") + { + CommonName = "Mary Accepted", + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Accepted, + SentBy = new Uri("mailto:someone@example.com"), + DirectoryEntry = new Uri(_ldapUri), + Type = "CuType", + Members = new List { "Group A", "Group B" }, + Role = ParticipationRole.Chair, + DelegatedTo = new List { "Peon A", "Peon B" }, + DelegatedFrom = new List { "Bigwig A", "Bigwig B" } + }; + yield return new TestCaseData(complex1).SetName("Complex attendee"); + + var simple = new Attendee("MAILTO:james@example.com") + { + CommonName = "James James", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Tentative + }; + yield return new TestCaseData(simple).SetName("Simple attendee"); + } + + [Test, TestCaseSource(nameof(BinaryAttachment_TestCases))] + public void BinaryAttachment_Tests(string theString, string expectedAttachment) + { + var asBytes = Encoding.UTF8.GetBytes(theString); + var binaryAttachment = new Attachment(asBytes); + + var calendar = new Calendar(); + var vEvent = GetSimpleEvent(); + vEvent.Attachments = new List { binaryAttachment }; + calendar.Events.Add(vEvent); + + var serialized = SerializeToString(calendar); + var unserialized = UnserializeCalendar(serialized); + var unserializedAttachment = unserialized + .Events + .First() + .Attachments + .Select(a => Encoding.UTF8.GetString(a.Data)) + .First(); + + Assert.AreEqual(expectedAttachment, unserializedAttachment); + Assert.AreEqual(calendar.GetHashCode(), unserialized.GetHashCode()); + Assert.AreEqual(calendar, unserialized); + } + + public static IEnumerable BinaryAttachment_TestCases() + { + const string shortString = "This is a string."; + yield return new TestCaseData(shortString, shortString) + .SetName("Short string"); + + const string mediumString = "This is a stringThisThis is a"; + yield return new TestCaseData(mediumString, mediumString) + .SetName("Moderate string"); + + const string longishString = "This is a song that never ends. It just goes on and on my friends. Some people started singing it not..."; + yield return new TestCaseData(longishString, longishString) + .SetName("Much longer string"); + + const string jsonString = + "{\"TheList\":[\"Foo\",\"Bar\",\"Baz\",\"Foo\",\"Bar\",\"Baz\",\"Foo\",\"Bar\",\"Baz\",\"Foo\",\"Bar\",\"Baz\",\"Foo\",\"Bar\",\"Baz\",\"Foo\",\"Bar\",\"Baz\"],\"TheNumber\":42,\"TheSet\":[\"Foo\",\"Bar\",\"Baz\"]}"; + yield return new TestCaseData(jsonString, jsonString).SetName("JSON-serialized text"); + } + + [Test, TestCaseSource(nameof(UriAttachment_TestCases))] + public void UriAttachment_Tests(string uri, Uri expectedUri) + { + var attachment = new Attachment(uri); + + var calendar = new Calendar(); + var vEvent = GetSimpleEvent(); + vEvent.Attachments = new List { attachment }; + calendar.Events.Add(vEvent); + + var serialized = SerializeToString(calendar); + var unserialized = UnserializeCalendar(serialized); + var unserializedUri = unserialized + .Events + .First() + .Attachments + .Select(a => a.Uri) + .Single(); + + Assert.AreEqual(expectedUri, unserializedUri); + Assert.AreEqual(calendar.GetHashCode(), unserialized.GetHashCode()); + Assert.AreEqual(calendar, unserialized); + } + + public static IEnumerable UriAttachment_TestCases() + { + yield return new TestCaseData("http://www.google.com", new Uri("http://www.google.com")).SetName("HTTP URL"); + yield return new TestCaseData("mailto:rstockbower@gmail.com", new Uri("mailto:rstockbower@gmail.com")).SetName("mailto: URL"); + yield return new TestCaseData(_ldapUri, new Uri(_ldapUri)).SetName("ldap URL"); + yield return new TestCaseData("C:\\path\\to\\file.txt", new Uri("C:\\path\\to\\file.txt")).SetName("Local file path URL"); + yield return new TestCaseData("\\\\uncPath\\to\\resource.txt", new Uri("\\\\uncPath\\to\\resource.txt")).SetName("UNC path URL"); + } + + [Test, Ignore("TODO: Fix CATEGORIES multiple serializations")] + public void CategoriesTest() + { + var vEvent = GetSimpleEvent(); + vEvent.Categories = new List { "Foo", "Bar", "Baz" }; + var c = new Calendar(); + c.Events.Add(vEvent); + + var serialized = SerializeToString(c); + var categoriesCount = Regex.Matches(serialized, "CATEGORIES").Count; + Assert.AreEqual(1, categoriesCount); + + var deserialized = UnserializeCalendar(serialized); + Assert.AreEqual(vEvent, deserialized); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/TextUtilTests.cs b/net-core/Ical.Net/Ical.Net.UnitTests/TextUtilTests.cs new file mode 100644 index 00000000..3dd2e7f9 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/TextUtilTests.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using Ical.Net.Utility; +using NUnit.Framework; +using NUnit.Framework.Interfaces; + +namespace Ical.Net.UnitTests +{ + public class TextUtilTests + { + [Test, TestCaseSource(nameof(FoldLines_TestCases))] + public string FoldLines_Tests(string incoming) => TextUtil.FoldLines(incoming); + + public static IEnumerable FoldLines_TestCases() + { + yield return new TestCaseData("Short") + .Returns("Short" + SerializationConstants.LineBreak) + .SetName("Short string remains unfolded"); + + const string moderatelyLongReturns = + "HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHell" + SerializationConstants.LineBreak + + " oWorld" + SerializationConstants.LineBreak; + + yield return new TestCaseData("HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld") + .Returns(moderatelyLongReturns) + .SetName("Long string is folded"); + + yield return new TestCaseData(" HelloWorldHelloWorldHelloWorldHelloWorldHelloWorld ") + .Returns("HelloWorldHelloWorldHelloWorldHelloWorldHelloWorld" + SerializationConstants.LineBreak) + .SetName("Long string with front and rear whitespace is trimmed and fits in the allotted width"); + + const string reallyLong = + "HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld"; + + const string reallyLongReturns = + "HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHell" + SerializationConstants.LineBreak + + " oWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWo" + SerializationConstants.LineBreak + + " rldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorld" + SerializationConstants.LineBreak + + " HelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHelloWorldHel" + SerializationConstants.LineBreak + + " loWorldHelloWorld" + SerializationConstants.LineBreak; + + yield return new TestCaseData(reallyLong) + .Returns(reallyLongReturns) + .SetName("Really long string is split onto multiple lines at a width of 75 chars, prefixed with a space"); + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.UnitTests/TodoTest.cs b/net-core/Ical.Net/Ical.Net.UnitTests/TodoTest.cs new file mode 100644 index 00000000..6e61a7aa --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.UnitTests/TodoTest.cs @@ -0,0 +1,229 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Ical.Net.DataTypes; + +using NUnit.Framework; +using NUnit.Framework.Interfaces; + +namespace Ical.Net.UnitTests +{ + [TestFixture] + public class TodoTest + { + private const string _tzid = "US-Eastern"; + + [Test, TestCaseSource(nameof(ActiveTodo_TestCases)), Category("Todo")] + public void ActiveTodo_Tests(string calendarString, IList> incoming) + { + var iCal = Calendar.LoadFromStream(new StringReader(calendarString))[0]; + ProgramTest.TestCal(iCal); + var todo = iCal.Todos[0]; + + foreach (var calDateTime in incoming) + { + var dt = calDateTime.Key; + dt.TzId = _tzid; + Assert.AreEqual(calDateTime.Value, todo.IsActive(dt)); + } + } + + public static IEnumerable ActiveTodo_TestCases() + { + var testVals = new List> + { + new KeyValuePair(new CalDateTime(2200, 12, 31, 0, 0, 0), true) + }; + yield return new TestCaseData(IcsFiles.Todo1, testVals) + .SetName("Todo1"); + + testVals = new List> + { + new KeyValuePair(new CalDateTime(2006, 7, 28, 8, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 28, 8, 59, 59), false), + + new KeyValuePair(new CalDateTime(2006, 7, 28, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2200, 12, 31, 0, 0, 0), true), + }; + yield return new TestCaseData(IcsFiles.Todo2, testVals) + .SetName("Todo2"); + + testVals = new List> + { + new KeyValuePair(new CalDateTime(2006, 7, 28, 8, 0, 0), false), + new KeyValuePair(new CalDateTime(2200, 12, 31, 0, 0, 0), false), + }; + yield return new TestCaseData(IcsFiles.Todo3, testVals).SetName("Todo3"); + + testVals = new List> + { + new KeyValuePair(new CalDateTime(2006, 7, 28, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 29, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 30, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 31, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 1, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 2, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 3, 9, 0, 0), false), + + new KeyValuePair(new CalDateTime(2006, 8, 4, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 8, 5, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 8, 6, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 8, 7, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 8, 8, 9, 0, 0), true), + }; + yield return new TestCaseData(IcsFiles.Todo5, testVals).SetName("Todo5"); + + testVals = new List> + { + new KeyValuePair(new CalDateTime(2006, 7, 28, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 29, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 30, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 31, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 1, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 2, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 3, 9, 0, 0), false), + + new KeyValuePair(new CalDateTime(2006, 8, 4, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 8, 5, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 8, 6, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 8, 7, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 8, 8, 9, 0, 0), true), + }; + yield return new TestCaseData(IcsFiles.Todo6, testVals).SetName("Todo6"); + + testVals = new List> + { + new KeyValuePair(new CalDateTime(2006, 7, 28, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 29, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 30, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 31, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 1, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 2, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 3, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 4, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 5, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 6, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 30, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 31, 9, 0, 0), false), + + new KeyValuePair(new CalDateTime(2006, 9, 1, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 9, 2, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 9, 3, 9, 0, 0), true), + }; + yield return new TestCaseData(IcsFiles.Todo7, testVals).SetName("Todo7"); + + testVals = new List> + { + new KeyValuePair(new CalDateTime(2006, 7, 28, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 29, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 30, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 31, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 1, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 2, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 3, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 4, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 5, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 6, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 30, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 31, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 9, 1, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 9, 2, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 9, 3, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 10, 10, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 11, 15, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 12, 5, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2007, 1, 3, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2007, 1, 4, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2007, 1, 5, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2007, 1, 6, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2007, 1, 7, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2007, 2, 1, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2007, 2, 2, 8, 59, 59), false), + + new KeyValuePair(new CalDateTime(2007, 2, 2, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2007, 2, 3, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2007, 2, 4, 9, 0, 0), true), + }; + yield return new TestCaseData(IcsFiles.Todo8, testVals).SetName("Todo8"); + + testVals = new List> + { + new KeyValuePair(new CalDateTime(2006, 7, 28, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 29, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 7, 30, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 17, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 18, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 8, 19, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 9, 7, 9, 0, 0), false), + new KeyValuePair(new CalDateTime(2006, 9, 8, 8, 59, 59), false), + + new KeyValuePair(new CalDateTime(2006, 9, 8, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 9, 9, 9, 0, 0), true), + }; + yield return new TestCaseData(IcsFiles.Todo9, testVals).SetName("Todo9"); + } + + [Test, TestCaseSource(nameof(CompletedTodo_TestCases)), Category("Todo")] + public void CompletedTodo_Tests(string calendarString, IList> incoming) + { + var iCal = Calendar.LoadFromStream(new StringReader(calendarString))[0]; + ProgramTest.TestCal(iCal); + var todo = iCal.Todos[0]; + + foreach (var calDateTime in incoming) + { + var dt = calDateTime.Key; + dt.TzId = _tzid; + Assert.AreEqual(calDateTime.Value, todo.IsCompleted(dt)); + } + } + + public static IEnumerable CompletedTodo_TestCases() + { + var testVals = new List> + { + new KeyValuePair(new CalDateTime(2006, 07, 28, 8, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 07, 28, 9, 0, 0), true), + new KeyValuePair(new CalDateTime(2006, 8, 1, 0, 0, 0), true), + }; + yield return new TestCaseData(IcsFiles.Todo4, testVals).SetName("Todo4"); + } + + [Test, Category("Todo")] + public void Todo7_1() + { + var iCal = Calendar.LoadFromStream(new StringReader(IcsFiles.Todo7))[0]; + var todo = iCal.Todos[0]; + + var items = new List + { + new CalDateTime(2006, 7, 28, 9, 0, 0, _tzid), + new CalDateTime(2006, 8, 4, 9, 0, 0, _tzid), + new CalDateTime(2006, 9, 1, 9, 0, 0, _tzid), + new CalDateTime(2006, 10, 6, 9, 0, 0, _tzid), + new CalDateTime(2006, 11, 3, 9, 0, 0, _tzid), + new CalDateTime(2006, 12, 1, 9, 0, 0, _tzid), + new CalDateTime(2007, 1, 5, 9, 0, 0, _tzid), + new CalDateTime(2007, 2, 2, 9, 0, 0, _tzid), + new CalDateTime(2007, 3, 2, 9, 0, 0, _tzid), + new CalDateTime(2007, 4, 6, 9, 0, 0, _tzid) + }; + + var occurrences = todo.GetOccurrences( + new CalDateTime(2006, 7, 1, 9, 0, 0), + new CalDateTime(2007, 7, 1, 9, 0, 0)).OrderBy(o => o.Period.StartTime).ToList(); + + // FIXME: Count is not properly restricting recurrences to 10. + // What's going wrong here? + Assert.AreEqual( + items.Count, + occurrences.Count, + "TODO should have " + items.Count + " occurrences; it has " + occurrences.Count); + + for (var i = 0; i < items.Count; i++) + { + Assert.AreEqual(items[i], occurrences[i].Period.StartTime, "TODO should occur at " + items[i] + ", but does not."); + } + } + } +} diff --git a/net-core/Ical.Net/Ical.Net.sln b/net-core/Ical.Net/Ical.Net.sln new file mode 100644 index 00000000..2458d882 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net.sln @@ -0,0 +1,76 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26014.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ical.Net", "Ical.Net\Ical.Net.csproj", "{71446356-D4EC-4141-B25C-4FAE6FA86265}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ical.Net.Collections", "Ical.Net.Collections\Ical.Net.Collections.csproj", "{30F333DC-EF44-4FA5-9586-A21979949A09}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "antlr.runtime", "antlr.runtime\antlr.runtime.csproj", "{7DBBA63C-131F-4E6C-86F5-65444DA7CD92}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ical.Net.UnitTests", "Ical.Net.UnitTests\Ical.Net.UnitTests.csproj", "{32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Debug|x64.ActiveCfg = Debug|x64 + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Debug|x64.Build.0 = Debug|x64 + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Debug|x86.ActiveCfg = Debug|x86 + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Debug|x86.Build.0 = Debug|x86 + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Release|Any CPU.Build.0 = Release|Any CPU + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Release|x64.ActiveCfg = Release|x64 + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Release|x64.Build.0 = Release|x64 + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Release|x86.ActiveCfg = Release|x86 + {71446356-D4EC-4141-B25C-4FAE6FA86265}.Release|x86.Build.0 = Release|x86 + {30F333DC-EF44-4FA5-9586-A21979949A09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {30F333DC-EF44-4FA5-9586-A21979949A09}.Debug|Any CPU.Build.0 = Debug|Any CPU + {30F333DC-EF44-4FA5-9586-A21979949A09}.Debug|x64.ActiveCfg = Debug|x64 + {30F333DC-EF44-4FA5-9586-A21979949A09}.Debug|x64.Build.0 = Debug|x64 + {30F333DC-EF44-4FA5-9586-A21979949A09}.Debug|x86.ActiveCfg = Debug|x86 + {30F333DC-EF44-4FA5-9586-A21979949A09}.Debug|x86.Build.0 = Debug|x86 + {30F333DC-EF44-4FA5-9586-A21979949A09}.Release|Any CPU.ActiveCfg = Release|Any CPU + {30F333DC-EF44-4FA5-9586-A21979949A09}.Release|Any CPU.Build.0 = Release|Any CPU + {30F333DC-EF44-4FA5-9586-A21979949A09}.Release|x64.ActiveCfg = Release|x64 + {30F333DC-EF44-4FA5-9586-A21979949A09}.Release|x64.Build.0 = Release|x64 + {30F333DC-EF44-4FA5-9586-A21979949A09}.Release|x86.ActiveCfg = Release|x86 + {30F333DC-EF44-4FA5-9586-A21979949A09}.Release|x86.Build.0 = Release|x86 + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Debug|x64.ActiveCfg = Debug|x64 + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Debug|x64.Build.0 = Debug|x64 + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Debug|x86.ActiveCfg = Debug|x86 + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Debug|x86.Build.0 = Debug|x86 + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Release|Any CPU.Build.0 = Release|Any CPU + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Release|x64.ActiveCfg = Release|x64 + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Release|x64.Build.0 = Release|x64 + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Release|x86.ActiveCfg = Release|x86 + {7DBBA63C-131F-4E6C-86F5-65444DA7CD92}.Release|x86.Build.0 = Release|x86 + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Debug|x64.ActiveCfg = Debug|x64 + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Debug|x64.Build.0 = Debug|x64 + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Debug|x86.ActiveCfg = Debug|x86 + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Debug|x86.Build.0 = Debug|x86 + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Release|Any CPU.Build.0 = Release|Any CPU + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Release|x64.ActiveCfg = Release|x64 + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Release|x64.Build.0 = Release|x64 + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Release|x86.ActiveCfg = Release|x86 + {32252FAF-D2A8-4126-B6E7-F20CA2A22DCF}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/net-core/Ical.Net/Ical.Net/Calendar.cs b/net-core/Ical.Net/Ical.Net/Calendar.cs new file mode 100644 index 00000000..048ac42b --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Calendar.cs @@ -0,0 +1,428 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using Ical.Net.DataTypes; +using Ical.Net.ExtensionMethods; +using Ical.Net.General.Proxies; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Evaluation; +using Ical.Net.Interfaces.General; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Serialization.iCalendar.Serializers; +using Ical.Net.Utility; + +namespace Ical.Net +{ + public class Calendar : CalendarComponent, IGetOccurrencesTyped, IGetFreeBusy, IMergeable + { + public static CalendarCollection Load(string iCalendarString) + { + using (var reader = new StringReader(iCalendarString)) + { + return LoadFromStream(reader); + } + } + + /// + /// Loads an from an open stream. + /// + /// The stream from which to load the object + /// An object + public new static CalendarCollection LoadFromStream(Stream s) => LoadFromStream(s, Encoding.UTF8, new CalendarSerializer()); + + public static CalendarCollection LoadFromStream(Stream s) where T : Calendar => LoadFromStream(typeof (T), s); + + public static CalendarCollection LoadFromStream(Type iCalendarType, Stream s) + { + ISerializer serializer = new CalendarSerializer(); + serializer.GetService().CalendarType = iCalendarType; + return LoadFromStream(s, Encoding.UTF8, serializer); + } + + public static CalendarCollection LoadFromStream(Type iCalendarType, Stream s, Encoding encoding) + { + ISerializer serializer = new CalendarSerializer(); + serializer.GetService().CalendarType = iCalendarType; + return LoadFromStream(s, encoding, serializer); + } + + public new static CalendarCollection LoadFromStream(Stream s, Encoding e, ISerializer serializer) => serializer.Deserialize(s, e) as CalendarCollection; + + public static CalendarCollection LoadFromStream(TextReader tr) => LoadFromStream(tr, new CalendarSerializer()); + + public static CalendarCollection LoadFromStream(TextReader tr) where T : Calendar => LoadFromStream(typeof (T), tr); + + public static CalendarCollection LoadFromStream(Type iCalendarType, TextReader tr) + { + ISerializer serializer = new CalendarSerializer(); + serializer.GetService().CalendarType = iCalendarType; + return LoadFromStream(tr, serializer); + } + + public static CalendarCollection LoadFromStream(TextReader tr, ISerializer serializer) + { + var text = tr.ReadToEnd(); + var ms = new MemoryStream(Encoding.UTF8.GetBytes(text)); + return LoadFromStream(ms, Encoding.UTF8, serializer); + } + + private IUniqueComponentList _mUniqueComponents; + private IUniqueComponentList _mEvents; + private IUniqueComponentList _mTodos; + private ICalendarObjectList _mJournals; + private IUniqueComponentList _mFreeBusy; + private ICalendarObjectList _mTimeZones; + + /// + /// To load an existing an iCalendar object, use one of the provided LoadFromXXX methods. + /// + /// For example, use the following code to load an iCalendar object from a URL: + /// + /// IICalendar iCal = iCalendar.LoadFromUri(new Uri("http://somesite.com/calendar.ics")); + /// + /// + /// + public Calendar() + { + Name = Components.Calendar; + Initialize(); + } + + private void Initialize() + { + _mUniqueComponents = new UniqueComponentListProxy(Children); + _mEvents = new UniqueComponentListProxy(Children); + _mTodos = new UniqueComponentListProxy(Children); + _mJournals = new CalendarObjectListProxy(Children); + _mFreeBusy = new UniqueComponentListProxy(Children); + _mTimeZones = new CalendarObjectListProxy(Children); + } + + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); + + Initialize(); + } + + protected bool Equals(Calendar other) + { + var foo = CollectionHelpers.Equals(UniqueComponents, other.UniqueComponents); + return string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase) + && CollectionHelpers.Equals(UniqueComponents, other.UniqueComponents) + && CollectionHelpers.Equals(Events, other.Events) + && CollectionHelpers.Equals(Todos, other.Todos) + && CollectionHelpers.Equals(Journals, other.Journals) + && CollectionHelpers.Equals(FreeBusy, other.FreeBusy) + && CollectionHelpers.Equals(TimeZones, other.TimeZones); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Calendar)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Name?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(UniqueComponents); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Events); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Todos); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Journals); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(FreeBusy); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(TimeZones); + return hashCode; + } + } + + public virtual IUniqueComponentList UniqueComponents => _mUniqueComponents; + + public virtual IEnumerable RecurringItems => Children.OfType(); + + /// + /// A collection of components in the iCalendar. + /// + public virtual IUniqueComponentList Events => _mEvents; + + /// + /// A collection of components in the iCalendar. + /// + public virtual IUniqueComponentList FreeBusy => _mFreeBusy; + + /// + /// A collection of components in the iCalendar. + /// + public virtual ICalendarObjectList Journals => _mJournals; + + /// + /// A collection of VTimeZone components in the iCalendar. + /// + public virtual ICalendarObjectList TimeZones => _mTimeZones; + + /// + /// A collection of components in the iCalendar. + /// + public virtual IUniqueComponentList Todos => _mTodos; + + public virtual string Version + { + get { return Properties.Get("VERSION"); } + set { Properties.Set("VERSION", value); } + } + + public virtual string ProductId + { + get { return Properties.Get("PRODID"); } + set { Properties.Set("PRODID", value); } + } + + public virtual string Scale + { + get { return Properties.Get("CALSCALE"); } + set { Properties.Set("CALSCALE", value); } + } + + public virtual string Method + { + get { return Properties.Get("METHOD"); } + set { Properties.Set("METHOD", value); } + } + + public virtual RecurrenceRestrictionType RecurrenceRestriction + { + get { return Properties.Get("X-DDAY-ICAL-RECURRENCE-RESTRICTION"); } + set { Properties.Set("X-DDAY-ICAL-RECURRENCE-RESTRICTION", value); } + } + + public virtual RecurrenceEvaluationModeType RecurrenceEvaluationMode + { + get { return Properties.Get("X-DDAY-ICAL-RECURRENCE-EVALUATION-MODE"); } + set { Properties.Set("X-DDAY-ICAL-RECURRENCE-EVALUATION-MODE", value); } + } + + /// + /// Adds a time zone to the iCalendar. This time zone may + /// then be used in date/time objects contained in the + /// calendar. + /// + /// The time zone added to the calendar. + public VTimeZone AddTimeZone(VTimeZone tz) + { + this.AddChild(tz); + return tz; + } + + /// + /// Evaluates component recurrences for the given range of time. + /// + /// For example, if you are displaying a month-view for January 2007, + /// you would want to evaluate recurrences for Jan. 1, 2007 to Jan. 31, 2007 + /// to display relevant information for those dates. + /// + /// + /// The beginning date/time of the range to test. + /// The end date/time of the range to test. + [Obsolete("This method is no longer supported. Use GetOccurrences() instead.")] + public void Evaluate(IDateTime fromDate, IDateTime toDate) + { + throw new NotSupportedException("Evaluate() is no longer supported as a public method. Use GetOccurrences() instead."); + } + + /// + /// Evaluates component recurrences for the given range of time, for + /// the type of recurring component specified. + /// + /// The type of component to be evaluated for recurrences. + /// The beginning date/time of the range to test. + /// The end date/time of the range to test. + [Obsolete("This method is no longer supported. Use GetOccurrences() instead.")] + public void Evaluate(IDateTime fromDate, IDateTime toDate) + { + throw new NotSupportedException("Evaluate() is no longer supported as a public method. Use GetOccurrences() instead."); + } + + /// + /// Clears recurrence evaluations for recurring components. + /// + public void ClearEvaluation() + { + foreach (var recurrable in RecurringItems) + { + recurrable.ClearEvaluation(); + } + } + + /// + /// Returns a list of occurrences of each recurring component + /// for the date provided (). + /// + /// The date for which to return occurrences. Time is ignored on this parameter. + /// A list of occurrences that occur on the given date (). + public virtual HashSet GetOccurrences(IDateTime dt) + { + return GetOccurrences(new CalDateTime(dt.AsSystemLocal.Date), new CalDateTime(dt.AsSystemLocal.Date.AddDays(1).AddSeconds(-1))); + } + + public virtual HashSet GetOccurrences(DateTime dt) + { + return GetOccurrences(new CalDateTime(dt.Date), new CalDateTime(dt.Date.AddDays(1).AddSeconds(-1))); + } + + /// + /// Returns a list of occurrences of each recurring component + /// that occur between and . + /// + /// The beginning date/time of the range. + /// The end date/time of the range. + /// A list of occurrences that fall between the dates provided. + public virtual HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) + => GetOccurrences(startTime, endTime); + + public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) + => GetOccurrences(new CalDateTime(startTime), new CalDateTime(endTime)); + + /// + /// Returns all occurrences of components of type T that start on the date provided. + /// All components starting between 12:00:00AM and 11:59:59 PM will be + /// returned. + /// + /// This will first Evaluate() the date range required in order to + /// determine the occurrences for the date provided, and then return + /// the occurrences. + /// + /// + /// The date for which to return occurrences. + /// A list of Periods representing the occurrences of this object. + public virtual HashSet GetOccurrences(IDateTime dt) where T : IRecurringComponent + => GetOccurrences(new CalDateTime(dt.AsSystemLocal.Date), new CalDateTime(dt.AsSystemLocal.Date.AddDays(1).AddTicks(-1))); + + public virtual HashSet GetOccurrences(DateTime dt) where T : IRecurringComponent + => GetOccurrences(new CalDateTime(dt.Date), new CalDateTime(dt.Date.AddDays(1).AddTicks(-1))); + + /// + /// Returns all occurrences of components of type T that start within the date range provided. + /// All components occurring between and + /// will be returned. + /// + /// The starting date range + /// The ending date range + public virtual HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) where T : IRecurringComponent + { + var occurrences = new HashSet(RecurringItems + .OfType() + .SelectMany(recurrable => recurrable.GetOccurrences(startTime, endTime))); + + var removeOccurrencesQuery = occurrences + .Where(o => o.Source is UniqueComponent) + .GroupBy(o => ((UniqueComponent)o.Source).Uid) + .SelectMany(group => group + .Where(o => o.Source.RecurrenceId != null) + .SelectMany(occurrence => group. + Where(o => o.Source.RecurrenceId == null && occurrence.Source.RecurrenceId.Date.Equals(o.Period.StartTime.Date)))); + + occurrences.ExceptWith(removeOccurrencesQuery); + return occurrences; + } + + public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) where T : IRecurringComponent + => GetOccurrences(new CalDateTime(startTime), new CalDateTime(endTime)); + + /// + /// Creates a typed object that is a direct child of the iCalendar itself. Generally, + /// you would invoke this method to create an Event, Todo, Journal, VTimeZone, FreeBusy, + /// or other base component type. + /// + /// + /// To create an event, use the following: + /// + /// IICalendar iCal = new iCalendar(); + /// + /// Event evt = iCal.Create<Event>(); + /// + /// + /// This creates the event, and adds it to the Events list of the iCalendar. + /// + /// The type of object to create + /// An object of the type specified + public T Create() where T : ICalendarComponent + { + var obj = Activator.CreateInstance(typeof (T)) as ICalendarObject; + if (obj is T) + { + this.AddChild(obj); + return (T) obj; + } + return default(T); + } + + public void Dispose() + { + Children.Clear(); + } + + public virtual void MergeWith(IMergeable obj) + { + var c = obj as Calendar; + if (c == null) + { + return; + } + + if (Name == null) + { + Name = c.Name; + } + + Method = c.Method; + Version = c.Version; + ProductId = c.ProductId; + Scale = c.Scale; + + foreach (var p in c.Properties.Where(p => !Properties.ContainsKey(p.Name))) + { + Properties.Add(p); + } + + foreach (var child in c.Children) + { + if (child is IUniqueComponent) + { + if (!UniqueComponents.ContainsKey(((IUniqueComponent) child).Uid)) + { + this.AddChild(child); + } + } + else + { + this.AddChild(child); + } + } + } + + public virtual FreeBusy GetFreeBusy(FreeBusy freeBusyRequest) => Net.FreeBusy.Create(this, freeBusyRequest); + + public virtual FreeBusy GetFreeBusy(IDateTime fromInclusive, IDateTime toExclusive) + => Net.FreeBusy.Create(this, Net.FreeBusy.CreateRequest(fromInclusive, toExclusive, null, null)); + + public virtual FreeBusy GetFreeBusy(Organizer organizer, IEnumerable contacts, IDateTime fromInclusive, IDateTime toExclusive) + => Net.FreeBusy.Create(this, Net.FreeBusy.CreateRequest(fromInclusive, toExclusive, organizer, contacts)); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/CalendarCollection.cs b/net-core/Ical.Net/Ical.Net/CalendarCollection.cs new file mode 100644 index 00000000..6513beaa --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/CalendarCollection.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Utility; + +namespace Ical.Net +{ + /// + /// A list of iCalendars. + /// + public class CalendarCollection : List + { + public void ClearEvaluation() + { + foreach (var iCal in this) + { + iCal.ClearEvaluation(); + } + } + + public HashSet GetOccurrences(IDateTime dt) + { + var occurrences = new HashSet(); + foreach (var iCal in this) + { + occurrences.UnionWith(iCal.GetOccurrences(dt)); + } + return occurrences; + } + + public HashSet GetOccurrences(DateTime dt) + { + var occurrences = new HashSet(); + foreach (var iCal in this) + { + occurrences.UnionWith(iCal.GetOccurrences(dt)); + } + return occurrences; + } + + public HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) + { + var occurrences = new HashSet(); + foreach (var iCal in this) + { + occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); + } + return occurrences; + } + + public HashSet GetOccurrences(DateTime startTime, DateTime endTime) + { + var occurrences = new HashSet(); + foreach (var iCal in this) + { + occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); + } + return occurrences; + } + + public HashSet GetOccurrences(IDateTime dt) where T : IRecurringComponent + { + var occurrences = new HashSet(); + foreach (var iCal in this) + { + occurrences.UnionWith(iCal.GetOccurrences(dt)); + } + return occurrences; + } + + public HashSet GetOccurrences(DateTime dt) where T : IRecurringComponent + { + var occurrences = new HashSet(); + foreach (var iCal in this) + { + occurrences.UnionWith(iCal.GetOccurrences(dt)); + } + return occurrences; + } + + public HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) where T : IRecurringComponent + { + var occurrences = new HashSet(); + foreach (var iCal in this) + { + occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); + } + return occurrences; + } + + public HashSet GetOccurrences(DateTime startTime, DateTime endTime) where T : IRecurringComponent + { + var occurrences = new HashSet(); + foreach (var iCal in this) + { + occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); + } + return occurrences; + } + + private FreeBusy CombineFreeBusy(FreeBusy main, FreeBusy current) + { + main?.MergeWith(current); + return current; + } + + public FreeBusy GetFreeBusy(FreeBusy freeBusyRequest) + { + return this.Aggregate(null, (current, iCal) => CombineFreeBusy(current, iCal.GetFreeBusy(freeBusyRequest))); + } + + public FreeBusy GetFreeBusy(IDateTime fromInclusive, IDateTime toExclusive) + { + return this.Aggregate(null, (current, iCal) => CombineFreeBusy(current, iCal.GetFreeBusy(fromInclusive, toExclusive))); + } + + public FreeBusy GetFreeBusy(Organizer organizer, IEnumerable contacts, IDateTime fromInclusive, IDateTime toExclusive) + { + return this.Aggregate(null, (current, iCal) => CombineFreeBusy(current, iCal.GetFreeBusy(organizer, contacts, fromInclusive, toExclusive))); + } + + public override int GetHashCode() => CollectionHelpers.GetHashCode(this); + + protected bool Equals(CalendarCollection obj) => CollectionHelpers.Equals(this, obj); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((CalendarEvent)obj); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Components/Alarm.cs b/net-core/Ical.Net/Ical.Net/Components/Alarm.cs new file mode 100644 index 00000000..7164695f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Components/Alarm.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net +{ + /// + /// A class that represents an RFC 2445 VALARM component. + /// FIXME: move GetOccurrences() logic into an AlarmEvaluator. + /// + public class Alarm : CalendarComponent + { + public virtual AlarmAction Action + { + get { return Properties.Get("ACTION"); } + set { Properties.Set("ACTION", value); } + } + + public virtual Attachment Attachment + { + get { return Properties.Get("ATTACH"); } + set { Properties.Set("ATTACH", value); } + } + + public virtual IList Attendees + { + get { return Properties.GetMany("ATTENDEE"); } + set { Properties.Set("ATTENDEE", value); } + } + + public virtual string Description + { + get { return Properties.Get("DESCRIPTION"); } + set { Properties.Set("DESCRIPTION", value); } + } + + public virtual TimeSpan Duration + { + get { return Properties.Get("DURATION"); } + set { Properties.Set("DURATION", value); } + } + + public virtual int Repeat + { + get { return Properties.Get("REPEAT"); } + set { Properties.Set("REPEAT", value); } + } + + public virtual string Summary + { + get { return Properties.Get("SUMMARY"); } + set { Properties.Set("SUMMARY", value); } + } + + public virtual Trigger Trigger + { + get { return Properties.Get("TRIGGER"); } + set { Properties.Set("TRIGGER", value); } + } + + protected virtual IList Occurrences { get; set; } + + public Alarm() + { + Name = Components.Alarm; + Occurrences = new List(16); + } + + /// + /// Gets a list of alarm occurrences for the given recurring component, + /// that occur between and . + /// + public virtual IList GetOccurrences(IRecurringComponent rc, IDateTime fromDate, IDateTime toDate) + { + Occurrences.Clear(); + + if (Trigger == null) + { + return Occurrences; + } + + // If the trigger is relative, it can recur right along with + // the recurring items, otherwise, it happens once and + // only once (at a precise time). + if (Trigger.IsRelative) + { + // Ensure that "FromDate" has already been set + if (fromDate == null) + { + fromDate = rc.Start.Copy(); + } + + var d = default(TimeSpan); + foreach (var o in rc.GetOccurrences(fromDate, toDate)) + { + var dt = o.Period.StartTime; + if (Trigger.Related == TriggerRelation.End) + { + if (o.Period.EndTime != null) + { + dt = o.Period.EndTime; + if (d == default(TimeSpan)) + { + d = o.Period.Duration; + } + } + // Use the "last-found" duration as a reference point + else if (d != default(TimeSpan)) + { + dt = o.Period.StartTime.Add(d); + } + else + { + throw new ArgumentException( + "Alarm trigger is relative to the START of the occurrence; however, the occurence has no discernible end."); + } + } + + Occurrences.Add(new AlarmOccurrence(this, dt.Add(Trigger.Duration.Value), rc)); + } + } + else + { + var dt = Trigger.DateTime.Copy(); + dt.AssociatedObject = this; + Occurrences.Add(new AlarmOccurrence(this, dt, rc)); + } + + // If a REPEAT and DURATION value were specified, + // then handle those repetitions here. + AddRepeatedItems(); + + return Occurrences; + } + + /// + /// Polls the component for alarms that have been triggered + /// since the provided date/time. If + /// is null, all triggered alarms will be returned. + /// + /// The earliest date/time to poll trigered alarms for. + /// A list of objects, each containing a triggered alarm. + public virtual IList Poll(IDateTime start, IDateTime end) + { + var results = new List(16); + + // Evaluate the alarms to determine the recurrences + var rc = Parent as RecurringComponent; + if (rc == null) + { + return results; + } + + results.AddRange(GetOccurrences(rc, start, end)); + return results; + } + + /// + /// Handles the repetitions that occur from the REPEAT and + /// DURATION properties. Each recurrence of the alarm will + /// have its own set of generated repetitions. + /// + protected virtual void AddRepeatedItems() + { + var len = Occurrences.Count; + for (var i = 0; i < len; i++) + { + var ao = Occurrences[i]; + var alarmTime = ao.DateTime.Copy(); + + for (var j = 0; j < Repeat; j++) + { + alarmTime = alarmTime.Add(Duration); + Occurrences.Add(new AlarmOccurrence(this, alarmTime.Copy(), ao.Component)); + } + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Components/CalendarComponent.cs b/net-core/Ical.Net/Ical.Net/Components/CalendarComponent.cs new file mode 100644 index 00000000..6bf0ecd1 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Components/CalendarComponent.cs @@ -0,0 +1,111 @@ +using System.Diagnostics; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using Ical.Net.General; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.General; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Serialization.iCalendar.Serializers.Components; + +namespace Ical.Net +{ + /// + /// This class is used by the parsing framework for iCalendar components. + /// Generally, you should not need to use this class directly. + /// + [DebuggerDisplay("Component: {Name}")] + public class CalendarComponent : CalendarObject, ICalendarComponent + { + /// + /// Loads an iCalendar component (Event, Todo, Journal, etc.) from an open stream. + /// + public static ICalendarComponent LoadFromStream(Stream s) + { + return LoadFromStream(s, Encoding.UTF8); + } + + public static ICalendarComponent LoadFromStream(Stream stream, Encoding encoding) + { + return LoadFromStream(stream, encoding, new ComponentSerializer()); + } + + public static T LoadFromStream(Stream stream, Encoding encoding) where T : ICalendarComponent + { + var serializer = new ComponentSerializer(); + object obj = LoadFromStream(stream, encoding, serializer); + if (obj is T) + { + return (T) obj; + } + return default(T); + } + + public static ICalendarComponent LoadFromStream(Stream stream, Encoding encoding, ISerializer serializer) + { + return serializer.Deserialize(stream, encoding) as ICalendarComponent; + } + + /// + /// Returns a list of properties that are associated with the iCalendar object. + /// + public virtual CalendarPropertyList Properties { get; protected set; } + + public CalendarComponent() : base() + { + Initialize(); + } + + public CalendarComponent(string name) : base(name) + { + Initialize(); + } + + private void Initialize() + { + Properties = new CalendarPropertyList(this); + } + + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); + + Initialize(); + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + + var c = obj as ICalendarComponent; + if (c == null) + { + return; + } + + Properties.Clear(); + foreach (var p in c.Properties) + { + Properties.Add(p); + } + } + + /// + /// Adds a property to this component. + /// + public virtual void AddProperty(string name, string value) + { + var p = new CalendarProperty(name, value); + AddProperty(p); + } + + /// + /// Adds a property to this component. + /// + public virtual void AddProperty(ICalendarProperty p) + { + p.Parent = this; + Properties.Set(p.Name, p.Value); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Components/CalendarEvent.cs b/net-core/Ical.Net/Ical.Net/Components/CalendarEvent.cs new file mode 100644 index 00000000..6b6f5632 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Components/CalendarEvent.cs @@ -0,0 +1,351 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using Ical.Net.DataTypes; +using Ical.Net.Evaluation; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Utility; + +namespace Ical.Net +{ + /// + /// A class that represents an RFC 5545 VEVENT component. + /// + /// + /// TODO: Add support for the following properties: + /// + /// Add support for the Organizer and Attendee properties + /// Add support for the Class property + /// Add support for the Geo property + /// Add support for the Priority property + /// Add support for the Related property + /// Create a TextCollection DataType for 'text' items separated by commas + /// + /// + public class CalendarEvent : RecurringComponent, IAlarmContainer, IComparable + { + internal const string ComponentName = "VEVENT"; + + /// + /// The start date/time of the event. + /// + /// If the duration has not been set, but + /// the start/end time of the event is available, + /// the duration is automatically determined. + /// Likewise, if the end date/time has not been + /// set, but a start and duration are available, + /// the end date/time will be extrapolated. + /// + /// + public override IDateTime DtStart + { + get { return base.DtStart; } + set + { + base.DtStart = value; + ExtrapolateTimes(); + } + } + + /// + /// The end date/time of the event. + /// + /// If the duration has not been set, but + /// the start/end time of the event is available, + /// the duration is automatically determined. + /// Likewise, if an end time and duration are available, + /// but a start time has not been set, the start time + /// will be extrapolated. + /// + /// + public virtual IDateTime DtEnd + { + get { return Properties.Get("DTEND"); } + set + { + if (!Equals(DtEnd, value)) + { + Properties.Set("DTEND", value); + ExtrapolateTimes(); + } + } + } + + /// + /// The duration of the event. + /// + /// If a start time and duration is available, + /// the end time is automatically determined. + /// Likewise, if the end time and duration is + /// available, but a start time is not determined, + /// the start time will be extrapolated from + /// available information. + /// + /// + // NOTE: Duration is not supported by all systems, + // (i.e. iPhone) and cannot co-exist with DtEnd. + // RFC 5545 states: + // + // ; either 'dtend' or 'duration' may appear in + // ; a 'eventprop', but 'dtend' and 'duration' + // ; MUST NOT occur in the same 'eventprop' + // + // Therefore, Duration is not serialized, as DtEnd + // should always be extrapolated from the duration. + public virtual TimeSpan Duration + { + get { return Properties.Get("DURATION"); } + set + { + if (!Equals(Duration, value)) + { + Properties.Set("DURATION", value); + ExtrapolateTimes(); + } + } + } + + /// + /// An alias to the DtEnd field (i.e. end date/time). + /// + public virtual IDateTime End + { + get { return DtEnd; } + set { DtEnd = value; } + } + + /// + /// Returns true if the event is an all-day event. + /// + public virtual bool IsAllDay + { + get { return !Start.HasTime; } + set + { + // Set whether or not the start date/time + // has a time value. + if (Start != null) + { + Start.HasTime = !value; + } + if (End != null) + { + End.HasTime = !value; + } + + if (value && Start != null && End != null && Equals(Start.Date, End.Date)) + { + Duration = default(TimeSpan); + End = Start.AddDays(1); + } + } + } + + /// + /// The geographic location (lat/long) of the event. + /// + public GeographicLocation GeographicLocation + { + get { return Properties.Get("GEO"); } + set { Properties.Set("GEO", value); } + } + + /// + /// The location of the event. + /// + public string Location + { + get { return Properties.Get("LOCATION"); } + set { Properties.Set("LOCATION", value); } + } + + /// + /// Resources that will be used during the event. + /// Conference room #2 + /// Projector + /// + public virtual IList Resources + { + get { return Properties.GetMany("RESOURCES"); } + set { Properties.Set("RESOURCES", value ?? new List()); } + } + + /// + /// The status of the event. + /// + public EventStatus Status + { + get { return Properties.Get("STATUS"); } + set { Properties.Set("STATUS", value); } + } + + /// + /// The transparency of the event. In other words, + /// whether or not the period of time this event + /// occupies can contain other events (transparent), + /// or if the time cannot be scheduled for anything + /// else (opaque). + /// + public TransparencyType Transparency + { + get { return Properties.Get("TRANSP"); } + set { Properties.Set("TRANSP", value); } + } + + private EventEvaluator _mEvaluator; + + /// + /// Constructs an Event object, with an iCalObject + /// (usually an iCalendar object) as its parent. + /// + public CalendarEvent() + { + Initialize(); + } + + private void Initialize() + { + Name = Components.Event; + + _mEvaluator = new EventEvaluator(this); + SetService(_mEvaluator); + } + + /// + /// Use this method to determine if an event occurs on a given date. + /// + /// This event should be called only after the Evaluate + /// method has calculated the dates for which this event occurs. + /// + /// + /// The date to test. + /// True if the event occurs on the provided, False otherwise. + public virtual bool OccursOn(IDateTime dateTime) + { + return _mEvaluator.Periods.Any(p => p.StartTime.Date == dateTime.Date || // It's the start date OR + (p.StartTime.Date <= dateTime.Date && // It's after the start date AND + (p.EndTime.HasTime && p.EndTime.Date >= dateTime.Date || // an end time was specified, and it's after the test date + (!p.EndTime.HasTime && p.EndTime.Date > dateTime.Date)))); + } + + /// + /// Use this method to determine if an event begins at a given date and time. + /// + /// The date and time to test. + /// True if the event begins at the given date and time + public virtual bool OccursAt(IDateTime dateTime) + { + return _mEvaluator.Periods.Any(p => p.StartTime.Equals(dateTime)); + } + + /// + /// Determines whether or not the is actively displayed + /// as an upcoming or occurred event. + /// + /// True if the event has not been cancelled, False otherwise. + public virtual bool IsActive() + { + return (Status != EventStatus.Cancelled); + } + + protected override bool EvaluationIncludesReferenceDate => true; + + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); + + Initialize(); + } + + protected override void OnDeserialized(StreamingContext context) + { + base.OnDeserialized(context); + + ExtrapolateTimes(); + } + + private void ExtrapolateTimes() + { + if (DtEnd == null && DtStart != null && Duration != default(TimeSpan)) + { + DtEnd = DtStart.Add(Duration); + } + else if (Duration == default(TimeSpan) && DtStart != null && DtEnd != null) + { + Duration = DtEnd.Subtract(DtStart); + } + else if (DtStart == null && Duration != default(TimeSpan) && DtEnd != null) + { + DtStart = DtEnd.Subtract(Duration); + } + } + + protected bool Equals(CalendarEvent other) + { + var resourcesSet = new HashSet(StringComparer.OrdinalIgnoreCase); + resourcesSet.UnionWith(Resources); + + var result = Equals(DtStart, other.DtStart) + && Equals(DtEnd, other.DtEnd) + && string.Equals(Location, other.Location, StringComparison.OrdinalIgnoreCase) + && resourcesSet.SetEquals(other.Resources) + && Status.Equals(other.Status) + && IsActive() == other.IsActive() + && Transparency.Equals(other.Transparency) + && EvaluationIncludesReferenceDate == other.EvaluationIncludesReferenceDate + && Attachments.SequenceEqual(other.Attachments) + && CollectionHelpers.Equals(ExceptionDates, other.ExceptionDates) + && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) + && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules, true) + && CollectionHelpers.Equals(RecurrenceDates, other.RecurrenceDates, true); + + return result; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((CalendarEvent)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = DtStart?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (DtEnd?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Location?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ Status.GetHashCode(); + hashCode = (hashCode * 397) ^ IsActive().GetHashCode(); + hashCode = (hashCode * 397) ^ Transparency.GetHashCode(); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Resources); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceDates); + return hashCode; + } + } + + public int CompareTo(CalendarEvent other) + { + if (DtStart.Equals(other.DtStart)) + { + return 0; + } + if (DtStart.LessThan(other.DtStart)) + { + return -1; + } + if (DtStart.GreaterThan(other.DtStart)) + { + return 1; + } + throw new Exception("An error occurred while comparing two CalDateTimes."); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Components/FreeBusy.cs b/net-core/Ical.Net/Ical.Net/Components/FreeBusy.cs new file mode 100644 index 00000000..62d8501c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Components/FreeBusy.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Evaluation; +using Ical.Net.Interfaces.General; + +namespace Ical.Net +{ + public class FreeBusy : UniqueComponent, IMergeable + { + public static FreeBusy Create(ICalendarObject obj, FreeBusy freeBusyRequest) + { + if (!(obj is IGetOccurrencesTyped)) + { + return null; + } + var getOccurrences = (IGetOccurrencesTyped) obj; + var occurrences = getOccurrences.GetOccurrences(freeBusyRequest.Start, freeBusyRequest.End); + var contacts = new List(32); + var isFilteredByAttendees = false; + + if (freeBusyRequest.Attendees != null && freeBusyRequest.Attendees.Count > 0) + { + isFilteredByAttendees = true; + var attendees = freeBusyRequest.Attendees + .Where(a => a.Value != null) + .Select(a => a.Value.OriginalString.Trim()); + contacts.AddRange(attendees); + } + + var fb = freeBusyRequest; + fb.Uid = Guid.NewGuid().ToString(); + fb.Entries.Clear(); + fb.DtStamp = CalDateTime.Now; + + foreach (var o in occurrences) + { + var uc = o.Source as IUniqueComponent; + + if (uc == null) + { + continue; + } + + var evt = uc as CalendarEvent; + var accepted = false; + var type = FreeBusyStatus.Busy; + + // We only accept events, and only "opaque" events. + if (evt != null && evt.Transparency != TransparencyType.Transparent) + { + accepted = true; + } + + // If the result is filtered by attendees, then + // we won't accept it until we find an event + // that is being attended by this person. + if (accepted && isFilteredByAttendees) + { + accepted = false; + + var participatingAttendeeQuery = uc.Attendees + .Where(attendee => + attendee.Value != null + && contacts.Contains(attendee.Value.OriginalString.Trim()) + && attendee.ParticipationStatus != null) + .Select(pa => pa.ParticipationStatus.ToUpperInvariant()); + + foreach (var participatingAttendee in participatingAttendeeQuery) + { + switch (participatingAttendee) + { + case EventParticipationStatus.Tentative: + accepted = true; + type = FreeBusyStatus.BusyTentative; + break; + case EventParticipationStatus.Accepted: + accepted = true; + type = FreeBusyStatus.Busy; + break; + } + } + } + + if (accepted) + { + // If the entry was accepted, add it to our list! + fb.Entries.Add(new FreeBusyEntry(o.Period, type)); + } + } + + return fb; + } + + public static FreeBusy CreateRequest(IDateTime fromInclusive, IDateTime toExclusive, Organizer organizer, IEnumerable contacts) + { + var fb = new FreeBusy + { + DtStamp = CalDateTime.Now, + DtStart = fromInclusive, + DtEnd = toExclusive + }; + if (organizer != null) + { + fb.Organizer = organizer; + } + + if (contacts == null) + { + return fb; + } + foreach (var attendee in contacts) + { + fb.Attendees.Add(attendee); + } + + return fb; + } + + public FreeBusy() + { + Name = Components.Freebusy; + } + + public virtual IList Entries + { + get { return Properties.GetMany("FREEBUSY"); } + set { Properties.Set("FREEBUSY", value); } + } + + public virtual IDateTime DtStart + { + get { return Properties.Get("DTSTART"); } + set { Properties.Set("DTSTART", value); } + } + + public virtual IDateTime DtEnd + { + get { return Properties.Get("DTEND"); } + set { Properties.Set("DTEND", value); } + } + + public virtual IDateTime Start + { + get { return Properties.Get("DTSTART"); } + set { Properties.Set("DTSTART", value); } + } + + public virtual IDateTime End + { + get { return Properties.Get("DTEND"); } + set { Properties.Set("DTEND", value); } + } + + public virtual FreeBusyStatus GetFreeBusyStatus(Period period) + { + var status = FreeBusyStatus.Free; + if (period == null) + { + return status; + } + + foreach (var fbe in Entries.Where(fbe => fbe.CollidesWith(period) && status < fbe.Status)) + { + status = fbe.Status; + } + return status; + } + + public virtual FreeBusyStatus GetFreeBusyStatus(IDateTime dt) + { + var status = FreeBusyStatus.Free; + if (dt == null) + { + return status; + } + + foreach (var fbe in Entries.Where(fbe => fbe.Contains(dt) && status < fbe.Status)) + { + status = fbe.Status; + } + return status; + } + + public virtual void MergeWith(IMergeable obj) + { + var fb = obj as FreeBusy; + if (fb == null) + { + return; + } + + foreach (var entry in fb.Entries.Where(entry => !Entries.Contains(entry))) + { + Entries.Add(entry); + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Components/Journal.cs b/net-core/Ical.Net/Ical.Net/Components/Journal.cs new file mode 100644 index 00000000..da2b6cce --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Components/Journal.cs @@ -0,0 +1,30 @@ +using System.Runtime.Serialization; + +namespace Ical.Net +{ + /// + /// A class that represents an RFC 5545 VJOURNAL component. + /// + public class Journal : RecurringComponent + { + public JournalStatus Status + { + get { return Properties.Get("STATUS"); } + set { Properties.Set("STATUS", value); } + } + + private void Initialize() + { + Name = Components.Journal; + } + + protected override bool EvaluationIncludesReferenceDate => true; + + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); + + Initialize(); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Components/RecurringComponent.cs b/net-core/Ical.Net/Ical.Net/Components/RecurringComponent.cs new file mode 100644 index 00000000..dc5362cd --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Components/RecurringComponent.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using Ical.Net.DataTypes; +using Ical.Net.Evaluation; +using Ical.Net.General.Proxies; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Utility; + +namespace Ical.Net +{ + /// + /// An iCalendar component that recurs. + /// + /// + /// This component automatically handles + /// RRULEs, RDATE, EXRULEs, and EXDATEs, as well as the DTSTART + /// for the recurring item (all recurring items must have a DTSTART). + /// + public class RecurringComponent : UniqueComponent, IRecurringComponent + { + public static IEnumerable SortByDate(IEnumerable list) + { + return SortByDate(list); + } + + public static IEnumerable SortByDate(IEnumerable list) => list.OrderBy(d => d); + + protected virtual bool EvaluationIncludesReferenceDate => false; + + public virtual IList Attachments + { + get { return Properties.GetMany("ATTACH"); } + set { Properties.Set("ATTACH", value); } + } + + public virtual IList Categories + { + get { return Properties.GetMany("CATEGORIES"); } + set { Properties.Set("CATEGORIES", value); } + } + + public virtual string Class + { + get { return Properties.Get("CLASS"); } + set { Properties.Set("CLASS", value); } + } + + public virtual IList Contacts + { + get { return Properties.GetMany("CONTACT"); } + set { Properties.Set("CONTACT", value); } + } + + public virtual IDateTime Created + { + get { return Properties.Get("CREATED"); } + set { Properties.Set("CREATED", value); } + } + + public virtual string Description + { + get { return Properties.Get("DESCRIPTION"); } + set { Properties.Set("DESCRIPTION", value); } + } + + /// + /// The start date/time of the component. + /// + public virtual IDateTime DtStart + { + get { return Properties.Get("DTSTART"); } + set { Properties.Set("DTSTART", value); } + } + + public virtual IList ExceptionDates + { + get { return Properties.GetMany("EXDATE"); } + set { Properties.Set("EXDATE", value); } + } + + public virtual IList ExceptionRules + { + get { return Properties.GetMany("EXRULE"); } + set { Properties.Set("EXRULE", value); } + } + + public virtual IDateTime LastModified + { + get { return Properties.Get("LAST-MODIFIED"); } + set { Properties.Set("LAST-MODIFIED", value); } + } + + public virtual int Priority + { + get { return Properties.Get("PRIORITY"); } + set { Properties.Set("PRIORITY", value); } + } + + public virtual IList RecurrenceDates + { + get { return Properties.GetMany("RDATE"); } + set { Properties.Set("RDATE", value); } + } + + public virtual IList RecurrenceRules + { + get { return Properties.GetMany("RRULE"); } + set { Properties.Set("RRULE", value); } + } + + public virtual IDateTime RecurrenceId + { + get { return Properties.Get("RECURRENCE-ID"); } + set { Properties.Set("RECURRENCE-ID", value); } + } + + public virtual IList RelatedComponents + { + get { return Properties.GetMany("RELATED-TO"); } + set { Properties.Set("RELATED-TO", value); } + } + + public virtual int Sequence + { + get { return Properties.Get("SEQUENCE"); } + set { Properties.Set("SEQUENCE", value); } + } + + /// + /// An alias to the DTStart field (i.e. start date/time). + /// + public virtual IDateTime Start + { + get { return DtStart; } + set { DtStart = value; } + } + + public virtual string Summary + { + get { return Properties.Get("SUMMARY"); } + set { Properties.Set("SUMMARY", value); } + } + + /// + /// A list of s for this recurring component. + /// + public virtual ICalendarObjectList Alarms => new CalendarObjectListProxy(Children); + + public RecurringComponent() + { + Initialize(); + EnsureProperties(); + } + + public RecurringComponent(string name) : base(name) + { + Initialize(); + EnsureProperties(); + } + + private void Initialize() + { + SetService(new RecurringEvaluator(this)); + } + + private void EnsureProperties() + { + if (!Properties.ContainsKey("SEQUENCE")) + { + Sequence = 0; + } + } + + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); + + Initialize(); + } + + public virtual void ClearEvaluation() + { + RecurrenceUtil.ClearEvaluation(this); + } + + public virtual HashSet GetOccurrences(IDateTime dt) + { + return RecurrenceUtil.GetOccurrences(this, dt, EvaluationIncludesReferenceDate); + } + + public virtual HashSet GetOccurrences(DateTime dt) + { + return RecurrenceUtil.GetOccurrences(this, new CalDateTime(dt), EvaluationIncludesReferenceDate); + } + + public virtual HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) + { + return RecurrenceUtil.GetOccurrences(this, startTime, endTime, EvaluationIncludesReferenceDate); + } + + public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) + { + return RecurrenceUtil.GetOccurrences(this, new CalDateTime(startTime), new CalDateTime(endTime), EvaluationIncludesReferenceDate); + } + + public virtual IList PollAlarms() + { + return PollAlarms(null, null); + } + + public virtual IList PollAlarms(IDateTime startTime, IDateTime endTime) + { + if (Alarms == null || !Alarms.Any()) + { + return new List(); + } + + var occurrences = new List(16); + foreach (var alarm in Alarms) + { + occurrences.AddRange(alarm.Poll(startTime, endTime)); + } + return occurrences; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Components/Todo.cs b/net-core/Ical.Net/Ical.Net/Components/Todo.cs new file mode 100644 index 00000000..c62fa1ff --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Components/Todo.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.Serialization; +using Ical.Net.DataTypes; +using Ical.Net.Evaluation; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net +{ + /// + /// A class that represents an RFC 5545 VTODO component. + /// + [DebuggerDisplay("{Summary} - {Status}")] + public class Todo : RecurringComponent, IAlarmContainer + { + private readonly TodoEvaluator _mEvaluator; + + /// + /// The date/time the todo was completed. + /// + public virtual IDateTime Completed + { + get { return Properties.Get("COMPLETED"); } + set { Properties.Set("COMPLETED", value); } + } + + /// + /// The start date/time of the todo item. + /// + public override IDateTime DtStart + { + get { return base.DtStart; } + set + { + base.DtStart = value; + ExtrapolateTimes(); + } + } + + /// + /// The due date of the todo item. + /// + public virtual IDateTime Due + { + get { return Properties.Get("DUE"); } + set + { + Properties.Set("DUE", value); + ExtrapolateTimes(); + } + } + + /// + /// The duration of the todo item. + /// + // NOTE: Duration is not supported by all systems, + // (i.e. iPhone) and cannot co-exist with Due. + // RFC 5545 states: + // + // ; either 'due' or 'duration' may appear in + // ; a 'todoprop', but 'due' and 'duration' + // ; MUST NOT occur in the same 'todoprop' + // + // Therefore, Duration is not serialized, as Due + // should always be extrapolated from the duration. + public virtual TimeSpan Duration + { + get { return Properties.Get("DURATION"); } + set + { + Properties.Set("DURATION", value); + ExtrapolateTimes(); + } + } + + public virtual GeographicLocation GeographicLocation + { + get { return Properties.Get("GEO"); } + set { Properties.Set("GEO", value); } + } + + public virtual string Location + { + get { return Properties.Get("LOCATION"); } + set { Properties.Set("LOCATION", value); } + } + + public virtual int PercentComplete + { + get { return Properties.Get("PERCENT-COMPLETE"); } + set { Properties.Set("PERCENT-COMPLETE", value); } + } + + public virtual IList Resources + { + get { return Properties.GetMany("RESOURCES"); } + set { Properties.Set("RESOURCES", value ?? new List()); } + } + + /// + /// The status of the todo item. + /// + public virtual TodoStatus Status + { + get { return Properties.Get("STATUS"); } + set + { + if (Status != value) + { + // Automatically set/unset the Completed time, once the + // component is fully loaded (When deserializing, it shouldn't + // automatically set the completed time just because the + // status was changed). + if (IsLoaded) + { + Completed = value == TodoStatus.Completed + ? CalDateTime.Now + : null; + } + + Properties.Set("STATUS", value); + } + } + } + + public Todo() + { + Name = Components.Todo; + + _mEvaluator = new TodoEvaluator(this); + SetService(_mEvaluator); + } + + /// + /// Use this method to determine if a todo item has been completed. + /// This takes into account recurrence items and the previous date + /// of completion, if any. + /// + /// This method evaluates the recurrence pattern for this TODO + /// as necessary to ensure all relevant information is taken + /// into account to give the most accurate result possible. + /// + /// + /// True if the todo item has been completed + public virtual bool IsCompleted(IDateTime currDt) + { + if (Status == TodoStatus.Completed) + { + if (Completed == null || Completed.GreaterThan(currDt)) + { + return true; + } + + // Evaluate to the previous occurrence. + _mEvaluator.EvaluateToPreviousOccurrence(Completed, currDt); + + return _mEvaluator.Periods.Cast().All(p => !p.StartTime.GreaterThan(Completed) || !currDt.GreaterThanOrEqual(p.StartTime)); + } + return false; + } + + /// + /// Returns 'True' if the todo item is Active as of . + /// An item is Active if it requires action of some sort. + /// + /// The date and time to test. + /// True if the item is Active as of , False otherwise. + public virtual bool IsActive(IDateTime currDt) + { + if (DtStart == null) + { + return !IsCompleted(currDt) && !IsCancelled(); + } + if (currDt.GreaterThanOrEqual(DtStart)) + { + return !IsCompleted(currDt) && !IsCancelled(); + } + return false; + } + + /// + /// Returns True if the todo item was cancelled. + /// + /// True if the todo was cancelled, False otherwise. + public virtual bool IsCancelled() + { + return Status == TodoStatus.Cancelled; + } + + protected override bool EvaluationIncludesReferenceDate => true; + + protected override void OnDeserializing(StreamingContext context) + { + //ToDo: a necessary evil, for now + base.OnDeserializing(context); + } + + private void ExtrapolateTimes() + { + if (Due == null && DtStart != null && Duration != default(TimeSpan)) + { + Due = DtStart.Add(Duration); + } + else if (Duration == default(TimeSpan) && DtStart != null && Due != null) + { + Duration = Due.Subtract(DtStart); + } + else if (DtStart == null && Duration != default(TimeSpan) && Due != null) + { + DtStart = Due.Subtract(Duration); + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Components/UniqueComponent.cs b/net-core/Ical.Net/Ical.Net/Components/UniqueComponent.cs new file mode 100644 index 00000000..fe667cdf --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Components/UniqueComponent.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net +{ + /// + /// Represents a unique component, a component with a unique UID, + /// which can be used to uniquely identify the component. + /// + public class UniqueComponent : CalendarComponent, IUniqueComponent, IComparable + { + // TODO: Add AddRelationship() public method. + // This method will add the UID of a related component + // to the Related_To property, along with any "RELTYPE" + // parameter ("PARENT", "CHILD", "SIBLING", or other) + // TODO: Add RemoveRelationship() public method. + + public UniqueComponent() + { + EnsureProperties(); + } + + public UniqueComponent(string name) : base(name) + { + EnsureProperties(); + } + + private void EnsureProperties() + { + if (string.IsNullOrEmpty(Uid)) + { + // Create a new UID for the component + Uid = Guid.NewGuid().ToString(); + } + + // NOTE: removed setting the 'CREATED' property here since it breaks serialization. + // See https://sourceforge.net/projects/dday-ical/forums/forum/656447/topic/3754354 + if (DtStamp == null) + { + // Here, we don't simply set to DateTime.Now because DateTime.Now contains milliseconds, and + // the iCalendar standard doesn't care at all about milliseconds. Therefore, when comparing + // two calendars, one generated, and one loaded from file, they may be functionally identical, + // but be determined to be different due to millisecond differences. + var now = DateTime.SpecifyKind(DateTime.Today.Add(DateTime.UtcNow.TimeOfDay), DateTimeKind.Utc); + DtStamp = new CalDateTime(now); + } + } + + public virtual IList Attendees + { + get { return Properties.GetMany("ATTENDEE"); } + set { Properties.Set("ATTENDEE", value); } + } + + public virtual IList Comments + { + get { return Properties.GetMany("COMMENT"); } + set { Properties.Set("COMMENT", value); } + } + + public virtual IDateTime DtStamp + { + get { return Properties.Get("DTSTAMP"); } + set { Properties.Set("DTSTAMP", value); } + } + + public virtual Organizer Organizer + { + get { return Properties.Get("ORGANIZER"); } + set { Properties.Set("ORGANIZER", value); } + } + + public virtual IList RequestStatuses + { + get { return Properties.GetMany("REQUEST-STATUS"); } + set { Properties.Set("REQUEST-STATUS", value); } + } + + public virtual Uri Url + { + get { return Properties.Get("URL"); } + set { Properties.Set("URL", value); } + } + + protected override void OnDeserialized(StreamingContext context) + { + base.OnDeserialized(context); + + EnsureProperties(); + } + + public int CompareTo(UniqueComponent other) + => string.Compare(Uid, other.Uid, StringComparison.OrdinalIgnoreCase); + + public override bool Equals(object obj) + { + if (obj is RecurringComponent && obj != this) + { + var r = (RecurringComponent) obj; + if (Uid != null) + { + return Uid.Equals(r.Uid); + } + return Uid == r.Uid; + } + return base.Equals(obj); + } + + public override int GetHashCode() => Uid?.GetHashCode() ?? base.GetHashCode(); + + public virtual string Uid + { + get { return Properties.Get("UID"); } + set { Properties.Set("UID", value); } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Components/VTimeZone.cs b/net-core/Ical.Net/Ical.Net/Components/VTimeZone.cs new file mode 100644 index 00000000..9266bdae --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Components/VTimeZone.cs @@ -0,0 +1,75 @@ +using System; + +namespace Ical.Net +{ + /// + /// Represents an RFC 5545 VTIMEZONE component. + /// + public class VTimeZone : CalendarComponent + { + public VTimeZone() + { + Name = Components.Timezone; + } + + public VTimeZone(string tzId) : this() + { + TzId = tzId; + } + + private string _tzId; + public virtual string TzId + { + get + { + if (string.IsNullOrEmpty(_tzId)) + { + _tzId = Properties.Get("TZID"); + } + return _tzId; + } + set + { + _tzId = value; + Properties.Set("TZID", _tzId); + } + } + + private Uri _url; + public virtual Uri Url + { + get { return _url ?? (_url = Properties.Get("TZURL")); } + set + { + _url = value; + Properties.Set("TZURL", _url); + } + } + + protected bool Equals(VTimeZone other) + { + return string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase) + && string.Equals(TzId, other.TzId, StringComparison.OrdinalIgnoreCase) + && Equals(Url, other.Url); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((VTimeZone)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Name.GetHashCode(); + hashCode = (hashCode * 397) ^ (TzId?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Url?.GetHashCode() ?? 0); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Constants.cs b/net-core/Ical.Net/Ical.Net/Constants.cs new file mode 100644 index 00000000..7ae2ed19 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Constants.cs @@ -0,0 +1,336 @@ +namespace Ical.Net +{ + public enum AlarmAction + { + Audio, + Display, + Email, + Procedure + } + + public enum TriggerRelation + { + Start, + End + } + + public class Components + { + public const string Alarm = "VALARM"; + public const string Calendar = "VCALENDAR"; + public const string Event = "VEVENT"; + public const string Freebusy = "VFREEBUSY"; + public const string Todo = "VTODO"; + public const string Journal = "VJOURNAL"; + public const string Timezone = "VTIMEZONE"; + public const string Daylight = "DAYLIGHT"; + public const string Standard = "STANDARD"; + } + + public static class EventParticipationStatus + { + public const string ParticipationStatus = "PARTSTAT"; + + /// Event needs action + public const string NeedsAction = "NEEDS-ACTION"; + /// Event accepted + public const string Accepted = "ACCEPTED"; + /// Event declined + public const string Declined = "DECLINED"; + /// Event tentatively accepted + public const string Tentative = "TENTATIVE"; + /// Event delegated + public const string Delegated = "DELEGATED"; + + public static string ParamName => ParticipationStatus; + public static string Default => NeedsAction; + } + + public static class ToDoParticipationStatus + { + public const string ParticipationStatus = "PARTSTAT"; + + /// To-do needs action + public const string NeedsAction = "NEEDS-ACTION"; + /// To-do accepted + public const string Accepted = "ACCEPTED"; + /// To-do declined + public const string Declined = "DECLINED"; + /// To-do tentatively accepted + public const string Tentative = "TENTATIVE"; + /// To-do delegated + public const string Delegated = "DELEGATED"; + /// To-do completed + public const string Completed = "COMPLETED"; + /// To-do in process + public const string InProcess = "IN-PROCESS"; + + public static string ParamName => ParticipationStatus; + public static string Default => NeedsAction; + } + + public static class JournalParticipationStatus + { + public const string ParticipationStatus = "PARTSTAT"; + + /// Event needs action + public const string NeedsAction = "NEEDS-ACTION"; + /// Event accepted + public const string Accepted = "ACCEPTED"; + /// Event declined + public const string Declined = "DECLINED"; + + public static string ParamName => ParticipationStatus; + public static string Default => NeedsAction; + } + + public static class ParticipationRole + { + public const string Role = "ROLE"; + + /// Indicates the chair of the calendar entity + public const string Chair = "CHAIR"; + + /// Indicates a participant whose participation is required + public const string RequiredParticipant = "REQ-PARTICIPANT"; + + /// Indicates a participant whose participation is optional + public const string OptionalParticipant = "OPT-PARTICIPANT"; + + /// Indicates a participant who is copied for information purposes only + public const string NonParticipant = "NON-PARTICIPANT"; + + public static string Default => RequiredParticipant; + public static string ParamName => Role; + } + + public class SerializationConstants + { + public const string LineBreak = "\r\n"; + } + + /// + /// Status codes available to an item + /// + public enum EventStatus + { + Tentative, + Confirmed, + Cancelled + } + + /// + /// Status codes available to a item. + /// + public enum TodoStatus + { + NeedsAction, + Completed, + InProcess, + Cancelled + } + + /// + /// Status codes available to a entry. + /// + public enum JournalStatus + { + Draft, // Indicates journal is draft. + Final, // Indicates journal is final. + Cancelled // Indicates journal is removed. + } + + public enum FreeBusyStatus + { + Free = 0, + BusyTentative = 1, + BusyUnavailable = 2, + Busy = 3 + } + + public enum FrequencyType + { + None, + Secondly, + Minutely, + Hourly, + Daily, + Weekly, + Monthly, + Yearly + } + + /// + /// Indicates the occurrence of the specific day within a + /// MONTHLY or YEARLY recurrence frequency. For example, within + /// a MONTHLY frequency, consider the following: + /// + /// RecurrencePattern r = new RecurrencePattern(); + /// r.Frequency = FrequencyType.Monthly; + /// r.ByDay.Add(new WeekDay(DayOfWeek.Monday, FrequencyOccurrence.First)); + /// + /// The above example represents the first Monday within the month, + /// whereas if FrequencyOccurrence.Last were specified, it would + /// represent the last Monday of the month. + /// + /// For a YEARLY frequency, consider the following: + /// + /// Recur r = new Recur(); + /// r.Frequency = FrequencyType.Yearly; + /// r.ByDay.Add(new WeekDay(DayOfWeek.Monday, FrequencyOccurrence.Second)); + /// + /// The above example represents the second Monday of the year. This can + /// also be represented with the following code: + /// + /// r.ByDay.Add(new WeekDay(DayOfWeek.Monday, 2)); + /// + public enum FrequencyOccurrence + { + None = int.MinValue, + Last = -1, + SecondToLast = -2, + ThirdToLast = -3, + FourthToLast = -4, + FifthToLast = -5, + First = 1, + Second = 2, + Third = 3, + Fourth = 4, + Fifth = 5 + } + + public enum RecurrenceRestrictionType + { + /// + /// Same as RestrictSecondly. + /// + Default, + + /// + /// Does not restrict recurrence evaluation - WARNING: this may cause very slow performance! + /// + NoRestriction, + + /// + /// Disallows use of the SECONDLY frequency for recurrence evaluation + /// + RestrictSecondly, + + /// + /// Disallows use of the MINUTELY and SECONDLY frequencies for recurrence evaluation + /// + RestrictMinutely, + + /// + /// Disallows use of the HOURLY, MINUTELY, and SECONDLY frequencies for recurrence evaluation + /// + RestrictHourly + } + + public enum RecurrenceEvaluationModeType + { + /// + /// Same as ThrowException. + /// + Default, + + /// + /// Automatically adjusts the evaluation to the next-best frequency based on the restriction type. + /// For example, if the restriction were IgnoreSeconds, and the frequency were SECONDLY, then + /// this would cause the frequency to be adjusted to MINUTELY, the next closest thing. + /// + AdjustAutomatically, + + /// + /// This will throw an exception if a recurrence rule is evaluated that does not meet the minimum + /// restrictions. For example, if the restriction were IgnoreSeconds, and a SECONDLY frequency + /// were evaluated, an exception would be thrown. + /// + ThrowException + } + + public enum TransparencyType + { + Opaque, + Transparent + } + + public class CalendarProductIDs + { + public const string Default = "-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN"; + } + + public class CalendarVersions + { + public const string Latest = "2.0"; + } + + public class CalendarScales + { + public const string Gregorian = "GREGORIAN"; + } + + public class CalendarMethods + { + /// + /// Used to publish an iCalendar object to one or + /// more "Calendar Users". There is no interactivity + /// between the publisher and any other "Calendar User". + /// An example might include a baseball team publishing + /// its schedule to the public. + /// + public const string Publish = "PUBLISH"; + + /// + /// Used to schedule an iCalendar object with other + /// "Calendar Users". Requests are interactive in + /// that they require the receiver to respond using + /// the reply methods. Meeting requests, busy-time + /// requests, and the assignment of tasks to other + /// "Calendar Users" are all examples. Requests are + /// also used by the Organizer to update the status + /// of an iCalendar object. + /// + public const string Request = "REQUEST"; + + /// + /// A reply is used in response to a request to + /// convey Attendee status to the Organizer. + /// Replies are commonly used to respond to meeting + /// and task requests. + /// + public const string Reply = "REPLY"; + + /// + /// Add one or more new instances to an existing + /// recurring iCalendar object. + /// + public const string Add = "ADD"; + + /// + /// Cancel one or more instances of an existing + /// iCalendar object. + /// + public const string Cancel = "CANCEL"; + + /// + /// Used by an Attendee to request the latest + /// version of an iCalendar object. + /// + public const string Refresh = "REFRESH"; + + /// + /// Used by an Attendee to negotiate a change in an + /// iCalendar object. Examples include the request + /// to change a proposed event time or change the + /// due date for a task. + /// + public const string Counter = "COUNTER"; + + /// + /// Used by the Organizer to decline the proposed + /// counter-proposal. + /// + public const string DeclineCounter = "DECLINECOUNTER"; + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/AlarmOccurrence.cs b/net-core/Ical.Net/Ical.Net/DataTypes/AlarmOccurrence.cs new file mode 100644 index 00000000..ffcdd8e2 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/AlarmOccurrence.cs @@ -0,0 +1,78 @@ +using System; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net.DataTypes +{ + /// + /// A class that represents a specific occurrence of an . + /// + /// + /// The contains the when + /// the alarm occurs, the that fired, and the + /// component on which the alarm fired. + /// + public class AlarmOccurrence : IComparable + { + public Period Period { get; set; } + + public IRecurringComponent Component { get; set; } + + public Alarm Alarm { get; set; } + + public IDateTime DateTime + { + get { return Period.StartTime; } + set { Period = new Period(value); } + } + + public AlarmOccurrence(AlarmOccurrence ao) + { + Period = ao.Period; + Component = ao.Component; + Alarm = ao.Alarm; + } + + public AlarmOccurrence(Alarm a, IDateTime dt, IRecurringComponent rc) + { + Alarm = a; + Period = new Period(dt); + Component = rc; + } + + public int CompareTo(AlarmOccurrence other) + { + return Period.CompareTo(other.Period); + } + + protected bool Equals(AlarmOccurrence other) + { + return Equals(Period, other.Period) && Equals(Component, other.Component) && Equals(Alarm, other.Alarm); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((AlarmOccurrence)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = (Period != null + ? Period.GetHashCode() + : 0); + hashCode = (hashCode * 397) ^ (Component != null + ? Component.GetHashCode() + : 0); + hashCode = (hashCode * 397) ^ (Alarm != null + ? Alarm.GetHashCode() + : 0); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/Attachment.cs b/net-core/Ical.Net/Ical.Net/DataTypes/Attachment.cs new file mode 100644 index 00000000..3afbd268 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/Attachment.cs @@ -0,0 +1,102 @@ +using System; +using System.Linq; +using System.Text; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; +using Ical.Net.Utility; + +namespace Ical.Net.DataTypes +{ + /// + /// Attachments represent the ATTACH element that can be associated with Alarms, Journals, Todos, and Events. There are two kinds of attachments: + /// 1) A string representing a URI which is typically human-readable, OR + /// 2) A base64-encoded string that can represent anything + /// + public class Attachment : EncodableDataType + { + public virtual Uri Uri { get; set; } + public virtual byte[] Data { get; } + + private Encoding _valueEncoding = System.Text.Encoding.UTF8; + public virtual Encoding ValueEncoding + { + get + { + return _valueEncoding; + } + set + { + if (value == null) + { + return; + } + _valueEncoding = value; + } + } + + public virtual string FormatType + { + get { return Parameters.Get("FMTTYPE"); } + set { Parameters.Set("FMTTYPE", value); } + } + + public Attachment() {} + + public Attachment(byte[] value) : this() + { + Data = value; + } + + public Attachment(string value) : this() + { + var serializer = new AttachmentSerializer(); + var a = serializer.Deserialize(value); + if (a == null) + { + throw new ArgumentException($"{value} is not a valid ATTACH component"); + } + + ValueEncoding = a.ValueEncoding; + + Data = a.Data; + Uri = a.Uri; + } + + public override string ToString() + { + return Data == null + ? string.Empty + : ValueEncoding.GetString(Data); + } + + //ToDo: See if this can be deleted + public override void CopyFrom(ICopyable obj) { } + + protected bool Equals(Attachment other) + { + var firstPart = Equals(Uri, other.Uri) && ValueEncoding.Equals(other.ValueEncoding); + return Data == null + ? firstPart + : firstPart && Data.SequenceEqual(other.Data); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((Attachment) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Uri?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (CollectionHelpers.GetHashCode(Data)); + hashCode = (hashCode * 397) ^ (ValueEncoding?.GetHashCode() ?? 0); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/Attendee.cs b/net-core/Ical.Net/Ical.Net/DataTypes/Attendee.cs new file mode 100644 index 00000000..4c32d56e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/Attendee.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.Interfaces.General; +using Ical.Net.Utility; + +namespace Ical.Net.DataTypes +{ + public class Attendee : EncodableDataType + { + private Uri _sentBy; + /// SENT-BY, to indicate who is acting on behalf of the ATTENDEE + public virtual Uri SentBy + { + get + { + if (_sentBy != null) + { + return _sentBy; + } + + var newUrl = Parameters.Get("SENT-BY"); + Uri.TryCreate(newUrl, UriKind.RelativeOrAbsolute, out _sentBy); + return _sentBy; + } + set + { + if (value == null || value == _sentBy) + { + return; + } + _sentBy = value; + Parameters.Set("SENT-BY", value.OriginalString); + } + } + + private string _commonName; + /// CN: to show the common or displayable name associated with the calendar address + public virtual string CommonName + { + get + { + if (string.IsNullOrEmpty(_commonName)) + { + _commonName = Parameters.Get("CN"); + } + return _commonName; + } + set + { + if (string.Equals(_commonName, value, StringComparison.OrdinalIgnoreCase)) + { + return; + } + _commonName = value; + Parameters.Set("CN", value); + } + } + + private Uri _directoryEntry; + /// DIR, to indicate the URI that points to the directory information corresponding to the attendee + public virtual Uri DirectoryEntry + { + get + { + if (_directoryEntry != null) + { + return _directoryEntry; + } + + var newUrl = Parameters.Get("SENT-BY"); + Uri.TryCreate(newUrl, UriKind.RelativeOrAbsolute, out _directoryEntry); + return _directoryEntry; + } + set + { + if (value == null || value == _directoryEntry) + { + return; + } + _directoryEntry = value; + Parameters.Set("DIR", value.OriginalString); + } + } + + private string _type; + /// CUTYPE: the type of calendar user + public virtual string Type + { + get + { + if (string.IsNullOrEmpty(_type)) + { + _type = Parameters.Get("CUTYPE"); + } + return _type; + } + set + { + if (string.Equals(_type, value, StringComparison.OrdinalIgnoreCase)) + { + return; + } + + _type = value; + Parameters.Set("CUTYPE", value); + } + } + + private List _members; + /// MEMBER: the groups the user belongs to + public virtual IList Members + { + get { return _members ?? (_members = new List(Parameters.GetMany("MEMBER"))); } + set + { + _members = new List(value); + Parameters.Set("MEMBER", value); + } + } + + private string _role; + /// ROLE: the intended role the attendee will have + public virtual string Role + { + get + { + if (string.IsNullOrEmpty(_role)) + { + _role = Parameters.Get("ROLE"); + } + return _role; + } + set + { + if (string.Equals(_role, value, StringComparison.OrdinalIgnoreCase)) + { + return; + } + _role = value; + Parameters.Set("ROLE", value); + } + } + + private string _participationStatus; + public virtual string ParticipationStatus + { + get + { + if (string.IsNullOrEmpty(_participationStatus)) + { + _participationStatus = Parameters.Get("PARTSTAT"); + } + return _participationStatus; + } + set + { + if (string.Equals(_participationStatus, value, StringComparison.OrdinalIgnoreCase)) + { + return; + } + _participationStatus = value; + Parameters.Set("PARTSTAT", value); + } + } + + private bool? _rsvp; + /// RSVP, to indicate whether a reply is requested + public virtual bool Rsvp + { + get + { + if (_rsvp != null) + { + return _rsvp.Value; + } + + bool val; + var rsvp = Parameters.Get("RSVP"); + if (rsvp != null && bool.TryParse(rsvp, out val)) + { + _rsvp = val; + return _rsvp.Value; + } + return false; + } + set + { + _rsvp = value; + var val = value.ToString().ToUpperInvariant(); + Parameters.Set("RSVP", val); + } + } + + private List _delegatedTo; + /// DELEGATED-TO, to indicate the calendar users that the original request was delegated to + public virtual IList DelegatedTo + { + get { return _delegatedTo ?? (_delegatedTo = new List(Parameters.GetMany("DELEGATED-TO"))); } + set + { + if (value == null) + { + return; + } + _delegatedTo = new List(value); + Parameters.Set("DELEGATED-TO", value); + } + } + + private List _delegatedFrom; + /// DELEGATED-FROM, to indicate whom the request was delegated from + public virtual IList DelegatedFrom + { + get { return _delegatedFrom ?? (_delegatedFrom = new List(Parameters.GetMany("DELEGATED-FROM"))); } + set + { + if (value == null) + { + return; + } + _delegatedFrom = new List(value); + Parameters.Set("DELEGATED-FROM", value); + } + } + + /// Uri associated with the attendee, typically an email address + public virtual Uri Value { get; set; } + + public Attendee() {} + + public Attendee(Uri attendee) + { + Value = attendee; + } + + public Attendee(string attendeeUri) + { + if (!Uri.IsWellFormedUriString(attendeeUri, UriKind.Absolute)) + { + throw new ArgumentException("attendeeUri"); + } + Value = new Uri(attendeeUri); + } + + //ToDo: See if this can be deleted + public override void CopyFrom(ICopyable obj) {} + + protected bool Equals(Attendee other) + { + return Equals(SentBy, other.SentBy) + && string.Equals(CommonName, other.CommonName, StringComparison.OrdinalIgnoreCase) + && Equals(DirectoryEntry, other.DirectoryEntry) + && string.Equals(Type, other.Type, StringComparison.OrdinalIgnoreCase) + && string.Equals(Role, other.Role) + && string.Equals(ParticipationStatus, other.ParticipationStatus, StringComparison.OrdinalIgnoreCase) + && Rsvp == other.Rsvp + && Equals(Value, other.Value) + && Members.SequenceEqual(other.Members) + && DelegatedTo.SequenceEqual(other.DelegatedTo) + && DelegatedFrom.SequenceEqual(other.DelegatedFrom); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((Attendee) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = SentBy?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (CommonName?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (DirectoryEntry?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Type?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Members); + hashCode = (hashCode * 397) ^ (Role?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (ParticipationStatus?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ Rsvp.GetHashCode(); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(DelegatedTo); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(DelegatedFrom); + hashCode = (hashCode * 397) ^ (Value?.GetHashCode() ?? 0); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/CalendarDataType.cs b/net-core/Ical.Net/Ical.Net/DataTypes/CalendarDataType.cs new file mode 100644 index 00000000..15512f99 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/CalendarDataType.cs @@ -0,0 +1,208 @@ +using System; +using System.Runtime.Serialization; +using Ical.Net.General; +using Ical.Net.General.Proxies; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.DataTypes +{ + /// + /// An abstract class from which all iCalendar data types inherit. + /// + public abstract class CalendarDataType : ICalendarDataType + { + private IParameterCollection _parameters; + private ParameterCollectionProxy _proxy; + private ServiceProvider _serviceProvider; + + protected ICalendarObject _AssociatedObject; + + protected CalendarDataType() + { + Initialize(); + } + + private void Initialize() + { + _parameters = new ParameterList(); + _proxy = new ParameterCollectionProxy(_parameters); + _serviceProvider = new ServiceProvider(); + } + + [OnDeserializing] + internal void DeserializingInternal(StreamingContext context) + { + OnDeserializing(context); + } + + [OnDeserialized] + internal void DeserializedInternal(StreamingContext context) + { + OnDeserialized(context); + } + + protected virtual void OnDeserializing(StreamingContext context) + { + Initialize(); + } + + protected virtual void OnDeserialized(StreamingContext context) {} + + public virtual Type GetValueType() + { + // See RFC 5545 Section 3.2.20. + if (_proxy != null && _proxy.ContainsKey("VALUE")) + { + switch (_proxy.Get("VALUE")) + { + case "BINARY": + return typeof (byte[]); + case "BOOLEAN": + return typeof (bool); + case "CAL-ADDRESS": + return typeof (Uri); + case "DATE": + return typeof (IDateTime); + case "DATE-TIME": + return typeof (IDateTime); + case "DURATION": + return typeof (TimeSpan); + case "FLOAT": + return typeof (double); + case "INTEGER": + return typeof (int); + case "PERIOD": + return typeof (Period); + case "RECUR": + return typeof (RecurrencePattern); + case "TEXT": + return typeof (string); + case "TIME": + // FIXME: implement ISO.8601.2004 + throw new NotImplementedException(); + case "URI": + return typeof (Uri); + case "UTC-OFFSET": + return typeof (UtcOffset); + default: + return null; + } + } + return null; + } + + public virtual void SetValueType(string type) + { + _proxy?.Set("VALUE", type != null ? type : type.ToUpper()); + } + + public virtual ICalendarObject AssociatedObject + { + get { return _AssociatedObject; } + set + { + if (!Equals(_AssociatedObject, value)) + { + _AssociatedObject = value; + if (_AssociatedObject != null) + { + _proxy.SetParent(_AssociatedObject); + if (_AssociatedObject is ICalendarParameterCollectionContainer) + { + _proxy.SetProxiedObject(((ICalendarParameterCollectionContainer) _AssociatedObject).Parameters); + } + } + else + { + _proxy.SetParent(null); + _proxy.SetProxiedObject(_parameters); + } + } + } + } + + public virtual Calendar Calendar => _AssociatedObject?.Calendar; + + public virtual string Language + { + get { return Parameters.Get("LANGUAGE"); } + set { Parameters.Set("LANGUAGE", value); } + } + + /// + /// Copies values from the target object to the + /// current object. + /// + public virtual void CopyFrom(ICopyable obj) + { + if (obj is ICalendarDataType) + { + var dt = (ICalendarDataType) obj; + _AssociatedObject = dt.AssociatedObject; + _proxy.SetParent(_AssociatedObject); + _proxy.SetProxiedObject(dt.Parameters); + } + } + + /// + /// Creates a copy of the object. + /// + /// The copy of the object. + public virtual T Copy() + { + var type = GetType(); + var obj = Activator.CreateInstance(type) as ICopyable; + + // Duplicate our values + if (obj is T) + { + obj.CopyFrom(this); + return (T) obj; + } + return default(T); + } + + public virtual IParameterCollection Parameters => _proxy; + + public virtual object GetService(Type serviceType) + { + return _serviceProvider.GetService(serviceType); + } + + public object GetService(string name) + { + return _serviceProvider.GetService(name); + } + + public T GetService() + { + return _serviceProvider.GetService(); + } + + public T GetService(string name) + { + return _serviceProvider.GetService(name); + } + + public void SetService(string name, object obj) + { + _serviceProvider.SetService(name, obj); + } + + public void SetService(object obj) + { + _serviceProvider.SetService(obj); + } + + public void RemoveService(Type type) + { + _serviceProvider.RemoveService(type); + } + + public void RemoveService(string name) + { + _serviceProvider.RemoveService(name); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/EncodableDataType.cs b/net-core/Ical.Net/Ical.Net/DataTypes/EncodableDataType.cs new file mode 100644 index 00000000..ca105c72 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/EncodableDataType.cs @@ -0,0 +1,16 @@ +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net.DataTypes +{ + /// + /// An abstract class from which all iCalendar data types inherit. + /// + public class EncodableDataType : CalendarDataType, IEncodableDataType + { + public virtual string Encoding + { + get { return Parameters.Get("ENCODING"); } + set { Parameters.Set("ENCODING", value); } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/FreeBusyEntry.cs b/net-core/Ical.Net/Ical.Net/DataTypes/FreeBusyEntry.cs new file mode 100644 index 00000000..07290091 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/FreeBusyEntry.cs @@ -0,0 +1,33 @@ +using Ical.Net.Interfaces.General; + +namespace Ical.Net.DataTypes +{ + public class FreeBusyEntry : Period + { + public virtual FreeBusyStatus Status { get; set; } + + public FreeBusyEntry() + { + Status = FreeBusyStatus.Busy; + } + + public FreeBusyEntry(Period period, FreeBusyStatus status) + { + //Sets the status associated with a given period, which requires copying the period values + //Probably the Period object should just have a FreeBusyStatus directly? + CopyFrom(period); + Status = status; + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + + var fb = obj as FreeBusyEntry; + if (fb != null) + { + Status = fb.Status; + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/GeographicLocation.cs b/net-core/Ical.Net/Ical.Net/DataTypes/GeographicLocation.cs new file mode 100644 index 00000000..055f1c87 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/GeographicLocation.cs @@ -0,0 +1,59 @@ +using System.Diagnostics; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; + +namespace Ical.Net.DataTypes +{ + /// + /// A class that represents the geographical location of an + /// or item. + /// + [DebuggerDisplay("{Latitude};{Longitude}")] + public class GeographicLocation : EncodableDataType + { + public double Latitude { get; set; } + public double Longitude { get; set; } + + public GeographicLocation() {} + + public GeographicLocation(string value) : this() + { + var serializer = new GeographicLocationSerializer(); + serializer.Deserialize(value); + } + + public GeographicLocation(double latitude, double longitude) + { + Latitude = latitude; + Longitude = longitude; + } + + public override void CopyFrom(ICopyable obj) {} + + public override string ToString() + { + return Latitude.ToString("0.000000") + ";" + Longitude.ToString("0.000000"); + } + + protected bool Equals(GeographicLocation other) + { + return Latitude.Equals(other.Latitude) && Longitude.Equals(other.Longitude); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((GeographicLocation)obj); + } + + public override int GetHashCode() + { + unchecked + { + return (Latitude.GetHashCode() * 397) ^ Longitude.GetHashCode(); + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/IGroupedCollection.cs b/net-core/Ical.Net/Ical.Net/DataTypes/IGroupedCollection.cs new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/IGroupedCollection.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/Occurrence.cs b/net-core/Ical.Net/Ical.Net/DataTypes/Occurrence.cs new file mode 100644 index 00000000..52e972e0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/Occurrence.cs @@ -0,0 +1,66 @@ +using System; +using Ical.Net.Interfaces.Evaluation; + +namespace Ical.Net.DataTypes +{ + public class Occurrence : IComparable + { + public Period Period { get; set; } + public IRecurrable Source { get; set; } + + public Occurrence(Occurrence ao) + { + Period = ao.Period; + Source = ao.Source; + } + + public Occurrence(IRecurrable recurrable, Period period) + { + Source = recurrable; + Period = period; + } + + public bool Equals(Occurrence other) + { + return Equals(Period, other.Period) && Equals(Source, other.Source); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is Occurrence && Equals((Occurrence) obj); + } + + public override int GetHashCode() + { + unchecked + { + return ((Period?.GetHashCode() ?? 0) * 397) ^ (Source?.GetHashCode() ?? 0); + } + } + + public override string ToString() + { + var s = "Occurrence"; + if (Source != null) + { + s = Source.GetType().Name + " "; + } + + if (Period != null) + { + s += "(" + Period.StartTime + ")"; + } + + return s; + } + + public int CompareTo(Occurrence other) + { + return Period.CompareTo(other.Period); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/Organizer.cs b/net-core/Ical.Net/Ical.Net/DataTypes/Organizer.cs new file mode 100644 index 00000000..0d8444ef --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/Organizer.cs @@ -0,0 +1,101 @@ +using System; +using System.Diagnostics; +using System.IO; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; + +namespace Ical.Net.DataTypes +{ + /// + /// A class that represents the organizer of an event/todo/journal. + /// + [DebuggerDisplay("{Value}")] + public class Organizer : EncodableDataType + { + public virtual Uri SentBy + { + get { return new Uri(Parameters.Get("SENT-BY")); } + set + { + if (value != null) + { + Parameters.Set("SENT-BY", value.OriginalString); + } + else + { + Parameters.Set("SENT-BY", (string) null); + } + } + } + + public virtual string CommonName + { + get { return Parameters.Get("CN"); } + set { Parameters.Set("CN", value); } + } + + public virtual Uri DirectoryEntry + { + get { return new Uri(Parameters.Get("DIR")); } + set + { + if (value != null) + { + Parameters.Set("DIR", value.OriginalString); + } + else + { + Parameters.Set("DIR", (string) null); + } + } + } + + public virtual Uri Value { get; set; } + + public Organizer() {} + + public Organizer(string value) : this() + { + var serializer = new OrganizerSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } + + protected bool Equals(Organizer other) + { + return Equals(Value, other.Value); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Organizer) obj); + } + + public override int GetHashCode() + { + return Value?.GetHashCode() ?? 0; + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + + var o = obj as Organizer; + if (o != null) + { + Value = o.Value; + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/Period.cs b/net-core/Ical.Net/Ical.Net/DataTypes/Period.cs new file mode 100644 index 00000000..6562006f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/Period.cs @@ -0,0 +1,198 @@ +using System; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; + +namespace Ical.Net.DataTypes +{ + /// Represents an iCalendar period of time. + public class Period : EncodableDataType, IComparable + { + public Period() { } + + public Period(IDateTime occurs) + : this(occurs, default(TimeSpan)) {} + + public Period(IDateTime start, IDateTime end) + { + if (end != null && end.LessThanOrEqual(start)) + { + throw new ArgumentException($"Start time ( {start} ) must come before the end time ( {end} )"); + } + + StartTime = start; + if (end == null) + { + return; + } + EndTime = end; + Duration = end.Subtract(start); + } + + public Period(IDateTime start, TimeSpan duration) + { + if (duration < TimeSpan.Zero) + { + throw new ArgumentException($"Duration ( ${duration} ) cannot be less than zero"); + } + + StartTime = start; + if (duration == default(TimeSpan)) + { + return; + } + + Duration = duration; + EndTime = start.Add(duration); + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + + var p = obj as Period; + if (p == null) + { + return; + } + StartTime = p.StartTime; + EndTime = p.EndTime; + Duration = p.Duration; + } + + protected bool Equals(Period other) + { + return Equals(StartTime, other.StartTime) && Equals(EndTime, other.EndTime) && Duration.Equals(other.Duration); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((Period) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = StartTime?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (EndTime?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ Duration.GetHashCode(); + return hashCode; + } + } + + public override string ToString() + { + var periodSerializer = new PeriodSerializer(); + return periodSerializer.SerializeToString(this); + } + + private void ExtrapolateTimes() + { + if (EndTime == null && StartTime != null && Duration != default(TimeSpan)) + { + EndTime = StartTime.Add(Duration); + } + else if (Duration == default(TimeSpan) && StartTime != null && EndTime != null) + { + Duration = EndTime.Subtract(StartTime); + } + else if (StartTime == null && Duration != default(TimeSpan) && EndTime != null) + { + StartTime = EndTime.Subtract(Duration); + } + } + + private IDateTime _startTime; + public virtual IDateTime StartTime + { + get { return _startTime; } + set + { + if (Equals(_startTime, value)) + { + return; + } + _startTime = value; + ExtrapolateTimes(); + } + } + + private IDateTime _endTime; + public virtual IDateTime EndTime + { + get { return _endTime; } + set + { + if (Equals(_endTime, value)) + { + return; + } + _endTime = value; + ExtrapolateTimes(); + } + } + + private TimeSpan _duration; + public virtual TimeSpan Duration + { + get + { + if (StartTime != null + && EndTime == null + && StartTime.Value.TimeOfDay == TimeSpan.Zero) + { + return TimeSpan.FromDays(1); + } + return _duration; + } + set + { + if (Equals(_duration, value)) + { + return; + } + _duration = value; + ExtrapolateTimes(); + } + } + + public virtual bool Contains(IDateTime dt) + { + // Start time is inclusive + if (dt == null || StartTime == null || !StartTime.LessThanOrEqual(dt)) + { + return false; + } + + // End time is exclusive + return EndTime == null || EndTime.GreaterThan(dt); + } + + public virtual bool CollidesWith(Period period) + { + return period != null + && ((period.StartTime != null && Contains(period.StartTime)) || (period.EndTime != null && Contains(period.EndTime))); + } + + public int CompareTo(Period other) + { + if (StartTime.Equals(other.StartTime)) + { + return 0; + } + if (StartTime.LessThan(other.StartTime)) + { + return -1; + } + if (StartTime.GreaterThan(other.StartTime)) + { + return 1; + } + throw new Exception("An error occurred while comparing two Periods."); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/PeriodList.cs b/net-core/Ical.Net/Ical.Net/DataTypes/PeriodList.cs new file mode 100644 index 00000000..ba9eb93a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/PeriodList.cs @@ -0,0 +1,84 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using Ical.Net.Evaluation; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; +using Ical.Net.Utility; + +namespace Ical.Net.DataTypes +{ + /// + /// An iCalendar list of recurring dates (or date exclusions) + /// + public class PeriodList : EncodableDataType, IEnumerable + { + public string TzId { get; set; } + public int Count => Periods.Count; + + protected IList Periods { get; set; } = new List(64); + + public PeriodList() + { + SetService(new PeriodListEvaluator(this)); + } + + public PeriodList(string value) : this() + { + var serializer = new PeriodListSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } + + protected bool Equals(PeriodList other) + { + return string.Equals(TzId, other.TzId) && CollectionHelpers.Equals(Periods, other.Periods); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((PeriodList)obj); + } + + public override int GetHashCode() + { + unchecked + { + return ((TzId?.GetHashCode() ?? 0) * 397) ^ CollectionHelpers.GetHashCode(Periods); + } + } + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + var list = obj as PeriodList; + if (list == null) + { + return; + } + + foreach (var p in list) + { + Add(p); + } + } + + public override string ToString() => new PeriodListSerializer().SerializeToString(this); + + public virtual void Add(IDateTime dt) => Periods.Add(new Period(dt)); + + public Period this[int index] + { + get { return Periods[index]; } + set { Periods[index] = value; } + } + + public virtual void Add(Period item) => Periods.Add(item); + + public IEnumerator GetEnumerator() => Periods.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => Periods.GetEnumerator(); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/RecurrencePattern.cs b/net-core/Ical.Net/Ical.Net/DataTypes/RecurrencePattern.cs new file mode 100644 index 00000000..7d85b765 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/RecurrencePattern.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Ical.Net.Evaluation; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; +using Ical.Net.Utility; + +namespace Ical.Net.DataTypes +{ + /// + /// An iCalendar representation of the RRULE property. + /// + public class RecurrencePattern : EncodableDataType + { + private int _interval = int.MinValue; + private RecurrenceRestrictionType? _restrictionType; + private RecurrenceEvaluationModeType? _evaluationMode; + + public FrequencyType Frequency { get; set; } + + public DateTime Until { get; set; } = DateTime.MinValue; + + public int Count { get; set; } = int.MinValue; + + public int Interval + { + get + { + return _interval == int.MinValue + ? 1 + : _interval; + } + set { _interval = value; } + } + + public List BySecond { get; set; } = new List(16); + + public List ByMinute { get; set; } = new List(16); + + public List ByHour { get; set; } = new List(16); + + public List ByDay { get; set; } = new List(16); + + public List ByMonthDay { get; set; } = new List(16); + + public List ByYearDay { get; set; } = new List(16); + + public List ByWeekNo { get; set; } = new List(16); + + public List ByMonth { get; set; } = new List(16); + + public List BySetPosition { get; set; } = new List(16); + + public DayOfWeek FirstDayOfWeek { get; set; } = DayOfWeek.Monday; + + public RecurrenceRestrictionType RestrictionType + { + get + { + // NOTE: Fixes bug #1924358 - Cannot evaluate Secondly patterns + if (_restrictionType != null) + { + return _restrictionType.Value; + } + return Calendar?.RecurrenceRestriction ?? RecurrenceRestrictionType.Default; + } + set { _restrictionType = value; } + } + + public RecurrenceEvaluationModeType EvaluationMode + { + get + { + // NOTE: Fixes bug #1924358 - Cannot evaluate Secondly patterns + if (_evaluationMode != null) + { + return _evaluationMode.Value; + } + return Calendar?.RecurrenceEvaluationMode ?? RecurrenceEvaluationModeType.Default; + } + set { _evaluationMode = value; } + } + + public RecurrencePattern() + { + SetService(new RecurrencePatternEvaluator(this)); + } + + public RecurrencePattern(FrequencyType frequency) : this(frequency, 1) {} + + public RecurrencePattern(FrequencyType frequency, int interval) : this() + { + Frequency = frequency; + Interval = interval; + } + + public RecurrencePattern(string value) : this() + { + if (string.IsNullOrWhiteSpace(value)) + { + return; + } + var serializer = new RecurrencePatternSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } + + public override string ToString() + { + var serializer = new RecurrencePatternSerializer(); + return serializer.SerializeToString(this); + } + + protected bool Equals(RecurrencePattern other) + { + return (Interval == other.Interval) + && (RestrictionType == other.RestrictionType) + && (EvaluationMode == other.EvaluationMode) + && (Frequency == other.Frequency) + && Until.Equals(other.Until) + && (Count == other.Count) + && (FirstDayOfWeek == other.FirstDayOfWeek) + && CollectionEquals(BySecond, other.BySecond) + && CollectionEquals(ByMinute, other.ByMinute) + && CollectionEquals(ByHour, other.ByHour) + && CollectionEquals(ByDay, other.ByDay) + && CollectionEquals(ByMonthDay, other.ByMonthDay) + && CollectionEquals(ByYearDay, other.ByYearDay) + && CollectionEquals(ByWeekNo, other.ByWeekNo) + && CollectionEquals(ByMonth, other.ByMonth) + && CollectionEquals(BySetPosition, other.BySetPosition); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((RecurrencePattern)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = Interval.GetHashCode(); + hashCode = (hashCode * 397) ^ RestrictionType.GetHashCode(); + hashCode = (hashCode * 397) ^ EvaluationMode.GetHashCode(); + hashCode = (hashCode * 397) ^ (int)Frequency; + hashCode = (hashCode * 397) ^ Until.GetHashCode(); + hashCode = (hashCode * 397) ^ Count; + hashCode = (hashCode * 397) ^ (int)FirstDayOfWeek; + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(BySecond); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByMinute); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByHour); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByDay); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByMonthDay); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByYearDay); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByWeekNo); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ByMonth); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(BySetPosition); + return hashCode; + } + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (!(obj is RecurrencePattern)) + { + return; + } + + var r = (RecurrencePattern) obj; + + Frequency = r.Frequency; + Until = r.Until; + Count = r.Count; + Interval = r.Interval; + BySecond = new List(r.BySecond); + ByMinute = new List(r.ByMinute); + ByHour = new List(r.ByHour); + ByDay = new List(r.ByDay); + ByMonthDay = new List(r.ByMonthDay); + ByYearDay = new List(r.ByYearDay); + ByWeekNo = new List(r.ByWeekNo); + ByMonth = new List(r.ByMonth); + BySetPosition = new List(r.BySetPosition); + FirstDayOfWeek = r.FirstDayOfWeek; + RestrictionType = r.RestrictionType; + EvaluationMode = r.EvaluationMode; + } + + private static bool CollectionEquals(IEnumerable c1, IEnumerable c2) => c1.SequenceEqual(c2); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/RequestStatus.cs b/net-core/Ical.Net/Ical.Net/DataTypes/RequestStatus.cs new file mode 100644 index 00000000..666671c0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/RequestStatus.cs @@ -0,0 +1,99 @@ +using System.IO; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; + +namespace Ical.Net.DataTypes +{ + /// + /// A class that represents the return status of an iCalendar request. + /// + public class RequestStatus : EncodableDataType + { + private string _mDescription; + private string _mExtraData; + private StatusCode _mStatusCode; + + public virtual string Description + { + get { return _mDescription; } + set { _mDescription = value; } + } + + public virtual string ExtraData + { + get { return _mExtraData; } + set { _mExtraData = value; } + } + + public virtual StatusCode StatusCode + { + get { return _mStatusCode; } + set { _mStatusCode = value; } + } + + public RequestStatus() {} + + public RequestStatus(string value) : this() + { + var serializer = new RequestStatusSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (!(obj is RequestStatus)) + { + return; + } + + var rs = (RequestStatus) obj; + if (rs.StatusCode != null) + { + StatusCode = rs.StatusCode; + } + Description = rs.Description; + rs.ExtraData = rs.ExtraData; + } + + public override string ToString() + { + var serializer = new RequestStatusSerializer(); + return serializer.SerializeToString(this); + } + + protected bool Equals(RequestStatus other) + { + return string.Equals(_mDescription, other._mDescription) && string.Equals(_mExtraData, other._mExtraData) && + Equals(_mStatusCode, other._mStatusCode); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((RequestStatus) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = _mDescription?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (_mExtraData?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (_mStatusCode?.GetHashCode() ?? 0); + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/StatusCode.cs b/net-core/Ical.Net/Ical.Net/DataTypes/StatusCode.cs new file mode 100644 index 00000000..0494b1cb --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/StatusCode.cs @@ -0,0 +1,95 @@ +using System.IO; +using System.Linq; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; +using Ical.Net.Utility; + +namespace Ical.Net.DataTypes +{ + /// + /// An iCalendar status code. + /// + public class StatusCode : EncodableDataType + { + public int[] Parts { get; private set; } + + public int Primary + { + get + { + if (Parts.Length > 0) + { + return Parts[0]; + } + return 0; + } + } + + public int Secondary + { + get + { + return Parts.Length > 1 + ? Parts[1] + : 0; + } + } + + public int Tertiary + { + get + { + return Parts.Length > 2 + ? Parts[2] + : 0; + } + } + + public StatusCode() {} + + public StatusCode(int[] parts) + { + Parts = parts; + } + + public StatusCode(string value) : this() + { + var serializer = new StatusCodeSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (obj is StatusCode) + { + var sc = (StatusCode) obj; + Parts = new int[sc.Parts.Length]; + sc.Parts.CopyTo(Parts, 0); + } + } + + public override string ToString() => new StatusCodeSerializer().SerializeToString(this); + + protected bool Equals(StatusCode other) => Parts.SequenceEqual(other.Parts); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((StatusCode) obj); + } + + public override int GetHashCode() => CollectionHelpers.GetHashCode(Parts); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/Trigger.cs b/net-core/Ical.Net/Ical.Net/DataTypes/Trigger.cs new file mode 100644 index 00000000..bbf750d8 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/Trigger.cs @@ -0,0 +1,121 @@ +using System; +using System.IO; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; + +namespace Ical.Net.DataTypes +{ + /// + /// A class that is used to specify exactly when an component will trigger. + /// Usually this date/time is relative to the component to which the Alarm is associated. + /// + public class Trigger : EncodableDataType + { + private IDateTime _mDateTime; + private TimeSpan? _mDuration; + private TriggerRelation _mRelated = TriggerRelation.Start; + + public virtual IDateTime DateTime + { + get { return _mDateTime; } + set + { + _mDateTime = value; + if (_mDateTime != null) + { + // NOTE: this, along with the "Duration" setter, fixes the bug tested in + // TODO11(), as well as this thread: https://sourceforge.net/forum/forum.php?thread_id=1926742&forum_id=656447 + + // DateTime and Duration are mutually exclusive + Duration = null; + + // Do not allow timeless date/time values + _mDateTime.HasTime = true; + } + } + } + + public virtual TimeSpan? Duration + { + get { return _mDuration; } + set + { + _mDuration = value; + if (_mDuration != null) + { + // NOTE: see above. + + // DateTime and Duration are mutually exclusive + DateTime = null; + } + } + } + + public virtual TriggerRelation Related + { + get { return _mRelated; } + set { _mRelated = value; } + } + + public virtual bool IsRelative => _mDuration != null; + + public Trigger() {} + + public Trigger(TimeSpan ts) + { + Duration = ts; + } + + public Trigger(string value) : this() + { + var serializer = new TriggerSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (obj is Trigger) + { + var t = (Trigger) obj; + DateTime = t.DateTime; + Duration = t.Duration; + Related = t.Related; + } + } + + protected bool Equals(Trigger other) + { + return Equals(_mDateTime, other._mDateTime) && _mDuration.Equals(other._mDuration) && _mRelated == other._mRelated; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Trigger) obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = _mDateTime?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ _mDuration.GetHashCode(); + hashCode = (hashCode * 397) ^ (int) _mRelated; + return hashCode; + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/UTCOffset.cs b/net-core/Ical.Net/Ical.Net/DataTypes/UTCOffset.cs new file mode 100644 index 00000000..e4d97fbc --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/UTCOffset.cs @@ -0,0 +1,73 @@ +using System; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; + +namespace Ical.Net.DataTypes +{ + /// + /// Represents a time offset from UTC (Coordinated Universal Time). + /// + public class UtcOffset : EncodableDataType + { + public TimeSpan Offset { get; } + + public bool Positive => Offset >= TimeSpan.Zero; + + public int Hours => Math.Abs(Offset.Hours); + + public int Minutes => Math.Abs(Offset.Minutes); + + public int Seconds => Math.Abs(Offset.Seconds); + + public UtcOffset() {} + + public UtcOffset(string value) : this() + { + Offset = UtcOffsetSerializer.GetOffset(value); + } + + public UtcOffset(TimeSpan ts) + { + Offset = ts; + } + + public static implicit operator UtcOffset(TimeSpan ts) => new UtcOffset(ts); + + public static explicit operator TimeSpan(UtcOffset o) => o.Offset; + + public virtual DateTime ToUtc(DateTime dt) => DateTime.SpecifyKind(dt.Add(-Offset), DateTimeKind.Utc); + + public virtual DateTime ToLocal(DateTime dt) => DateTime.SpecifyKind(dt.Add(Offset), DateTimeKind.Local); + + protected bool Equals(UtcOffset other) + { + return Offset == other.Offset; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((UtcOffset) obj); + } + + public override int GetHashCode() + { + return Offset.GetHashCode(); + } + + public override string ToString() + { + return (Positive ? "+" : "-") + Hours.ToString("00") + Minutes.ToString("00") + (Seconds != 0 ? Seconds.ToString("00") : string.Empty); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/WeekDay.cs b/net-core/Ical.Net/Ical.Net/DataTypes/WeekDay.cs new file mode 100644 index 00000000..47c9f33c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/WeekDay.cs @@ -0,0 +1,91 @@ +using System; +using System.IO; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; + +namespace Ical.Net.DataTypes +{ + /// + /// Represents an RFC 5545 "BYDAY" value. + /// + public class WeekDay : EncodableDataType + { + public virtual int Offset { get; set; } = int.MinValue; + + public virtual DayOfWeek DayOfWeek { get; set; } + + public WeekDay() + { + Offset = int.MinValue; + } + + public WeekDay(DayOfWeek day) : this() + { + DayOfWeek = day; + } + + public WeekDay(DayOfWeek day, int num) : this(day) + { + Offset = num; + } + + public WeekDay(DayOfWeek day, FrequencyOccurrence type) : this(day, (int) type) {} + + public WeekDay(string value) + { + var serializer = new WeekDaySerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } + + public override bool Equals(object obj) + { + if (!(obj is WeekDay)) + { + return false; + } + + var ds = (WeekDay) obj; + return ds.Offset == Offset && ds.DayOfWeek == DayOfWeek; + } + + public override int GetHashCode() + { + return Offset.GetHashCode() ^ DayOfWeek.GetHashCode(); + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (obj is WeekDay) + { + var bd = (WeekDay) obj; + Offset = bd.Offset; + DayOfWeek = bd.DayOfWeek; + } + } + + public int CompareTo(object obj) + { + WeekDay bd = null; + if (obj is string) + { + bd = new WeekDay(obj.ToString()); + } + else if (obj is WeekDay) + { + bd = (WeekDay) obj; + } + + if (bd == null) + { + throw new ArgumentException(); + } + var compare = DayOfWeek.CompareTo(bd.DayOfWeek); + if (compare == 0) + { + compare = Offset.CompareTo(bd.Offset); + } + return compare; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/DataTypes/iCalDateTime.cs b/net-core/Ical.Net/Ical.Net/DataTypes/iCalDateTime.cs new file mode 100644 index 00000000..7d122fd9 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/DataTypes/iCalDateTime.cs @@ -0,0 +1,564 @@ +using System; +using System.IO; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; +using Ical.Net.Utility; + +namespace Ical.Net.DataTypes +{ + /// + /// The iCalendar equivalent of the .NET class. + /// + /// In addition to the features of the class, the + /// class handles time zone differences, and integrates seamlessly into the iCalendar framework. + /// + /// + public sealed class CalDateTime : EncodableDataType, IDateTime + { + public static CalDateTime Now => new CalDateTime(DateTime.Now); + + public static CalDateTime Today => new CalDateTime(DateTime.Today); + + private DateTime _value; + private bool _hasDate; + private bool _hasTime; + private bool _isUniversalTime; + + public CalDateTime() {} + + public CalDateTime(IDateTime value) + { + Initialize(value.Value, value.TzId, null); + } + + public CalDateTime(DateTime value) : this(value, null) {} + + public CalDateTime(DateTime value, string tzId) + { + Initialize(value, tzId, null); + } + + public CalDateTime(int year, int month, int day, int hour, int minute, int second) + { + Initialize(year, month, day, hour, minute, second, null, null); + HasTime = true; + } + + public CalDateTime(int year, int month, int day, int hour, int minute, int second, string tzId) + { + Initialize(year, month, day, hour, minute, second, tzId, null); + HasTime = true; + } + + public CalDateTime(int year, int month, int day, int hour, int minute, int second, string tzId, Calendar cal) + { + Initialize(year, month, day, hour, minute, second, tzId, cal); + HasTime = true; + } + + public CalDateTime(int year, int month, int day) : this(year, month, day, 0, 0, 0) {} + public CalDateTime(int year, int month, int day, string tzId) : this(year, month, day, 0, 0, 0, tzId) {} + + public CalDateTime(string value) + { + var serializer = new DateTimeSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } + + private void Initialize(int year, int month, int day, int hour, int minute, int second, string tzId, Calendar cal) + { + Initialize(CoerceDateTime(year, month, day, hour, minute, second, DateTimeKind.Local), tzId, cal); + } + + private void Initialize(DateTime value, string tzId, Calendar cal) + { + if (value.Kind == DateTimeKind.Utc) + { + IsUniversalTime = true; + } + + // Convert all incoming values to UTC. + Value = new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second, DateTimeKind.Utc); + HasDate = true; + HasTime = value.Second != 0 || value.Minute != 0 || value.Hour != 0; + TzId = tzId; + AssociatedObject = cal; + } + + private DateTime CoerceDateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + { + var dt = DateTime.MinValue; + + // NOTE: determine if a date/time value exceeds the representable date/time values in .NET. + // If so, let's automatically adjust the date/time to compensate. + // FIXME: should we have a parsing setting that will throw an exception + // instead of automatically adjusting the date/time value to the + // closest representable date/time? + try + { + if (year > 9999) + { + dt = DateTime.MaxValue; + } + else if (year > 0) + { + dt = new DateTime(year, month, day, hour, minute, second, kind); + } + } + catch {} + + return dt; + } + + public override ICalendarObject AssociatedObject + { + get { return base.AssociatedObject; } + set + { + if (!Equals(AssociatedObject, value)) + { + base.AssociatedObject = value; + } + } + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + + var dt = obj as IDateTime; + if (dt != null) + { + _value = dt.Value; + _isUniversalTime = dt.IsUniversalTime; + _hasDate = dt.HasDate; + _hasTime = dt.HasTime; + + AssociateWith(dt); + } + } + + //ToDo: Time zone equality should include time zone values. Perhaps we should separate the idea of "equal" with "equivalent + /// Equality is determined by the unambiguous UTC representation of the time. Time zone string values are ignored. + public override bool Equals(object obj) + { + if (obj is IDateTime) + { + AssociateWith((IDateTime)obj); + var thisDt = ((IDateTime)obj).AsUtc; + return thisDt.Equals(AsUtc); + } + if (obj is DateTime) + { + var dt = (CalDateTime) obj; + AssociateWith(dt); + return Equals(dt.AsUtc, AsUtc); + } + return false; + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public override string ToString() + { + return ToString(null, null); + } + + public static bool operator <(CalDateTime left, IDateTime right) + { + left.AssociateWith(right); + + if (left.HasTime || right.HasTime) + { + return left.AsUtc < right.AsUtc; + } + return left.AsUtc.Date < right.AsUtc.Date; + } + + public static bool operator >(CalDateTime left, IDateTime right) + { + left.AssociateWith(right); + + if (left.HasTime || right.HasTime) + { + return left.AsUtc > right.AsUtc; + } + return left.AsUtc.Date > right.AsUtc.Date; + } + + public static bool operator <=(CalDateTime left, IDateTime right) + { + left.AssociateWith(right); + + if (left.HasTime || right.HasTime) + { + return left.AsUtc <= right.AsUtc; + } + return left.AsUtc.Date <= right.AsUtc.Date; + } + + public static bool operator >=(CalDateTime left, IDateTime right) + { + left.AssociateWith(right); + + if (left.HasTime || right.HasTime) + { + return left.AsUtc >= right.AsUtc; + } + return left.AsUtc.Date >= right.AsUtc.Date; + } + + public static bool operator ==(CalDateTime left, IDateTime right) + { + left.AssociateWith(right); + + if (left.HasTime || right.HasTime) + { + return left.AsUtc.Equals(right.AsUtc); + } + return left.AsUtc.Date.Equals(right.AsUtc.Date); + } + + public static bool operator !=(CalDateTime left, IDateTime right) + { + left.AssociateWith(right); + + if (left.HasTime || right.HasTime) + { + return !left.AsUtc.Equals(right.AsUtc); + } + return !left.AsUtc.Date.Equals(right.AsUtc.Date); + } + + public static TimeSpan operator -(CalDateTime left, IDateTime right) + { + left.AssociateWith(right); + return left.AsUtc - right.AsUtc; + } + + public static IDateTime operator -(CalDateTime left, TimeSpan right) + { + var copy = left.Copy(); + copy.Value -= right; + return copy; + } + + public static IDateTime operator +(CalDateTime left, TimeSpan right) + { + var copy = left.Copy(); + copy.Value += right; + return copy; + } + + public static implicit operator CalDateTime(DateTime left) + { + return new CalDateTime(left); + } + + /// + /// Converts the date/time to this computer's local date/time. + /// + public DateTime AsSystemLocal + { + get + { + if (!HasTime) + { + return DateTime.SpecifyKind(Value.Date, DateTimeKind.Local); + } + if (IsUniversalTime) + { + return Value.ToLocalTime(); + } + return AsUtc.ToLocalTime(); + } + } + + private DateTime _utc; + + /// + /// Converts the date/time to UTC (Coordinated Universal Time) + /// + public DateTime AsUtc + { + get + { + if (IsUniversalTime) + { + _utc = DateTime.SpecifyKind(_value, DateTimeKind.Utc); + return _utc; + } + if (!string.IsNullOrWhiteSpace(TzId)) + { + var newUtc = DateUtil.ToZonedDateTimeLeniently(Value, TzId); + _utc = newUtc.ToDateTimeUtc(); + return _utc; + } + _utc = DateTime.SpecifyKind(Value, DateTimeKind.Local).ToUniversalTime(); + + // Fallback to the OS-conversion + return _utc; + } + } + + public bool IsUniversalTime + { + get { return _isUniversalTime; } + set { _isUniversalTime = value; } + } + + public string TimeZoneName => TzId; + + public DateTime Value + { + get { return _value; } + set { _value = value; } + } + + public bool HasDate + { + get { return _hasDate; } + set { _hasDate = value; } + } + + public bool HasTime + { + get { return _hasTime; } + set { _hasTime = value; } + } + + private string _tzId = string.Empty; + public string TzId + { + get + { + if (IsUniversalTime) + { + return "UTC"; + } + return !string.IsNullOrWhiteSpace(_tzId) + ? _tzId + : Parameters.Get("TZID"); + } + set + { + if (!Equals(TzId, value)) + { + Parameters.Set("TZID", value); + _tzId = value; + } + } + } + + public int Year => Value.Year; + + public int Month => Value.Month; + + public int Day => Value.Day; + + public int Hour => Value.Hour; + + public int Minute => Value.Minute; + + public int Second => Value.Second; + + public int Millisecond => Value.Millisecond; + + public long Ticks => Value.Ticks; + + public DayOfWeek DayOfWeek => Value.DayOfWeek; + + public int DayOfYear => Value.DayOfYear; + + public DateTime Date => Value.Date; + + public TimeSpan TimeOfDay => Value.TimeOfDay; + + public IDateTime ToTimeZone(string newTimeZone) + { + if (string.IsNullOrWhiteSpace(newTimeZone)) + { + throw new ArgumentException("You must provide a valid TZID to the ToTimeZone() method", nameof(newTimeZone)); + } + if (Calendar == null) + { + throw new Exception("The iCalDateTime object must have an iCalendar associated with it in order to use TimeZones."); + } + + var newDt = string.IsNullOrWhiteSpace(TzId) + ? DateUtil.ToZonedDateTimeLeniently(Value, newTimeZone).ToDateTimeUtc() + : DateUtil.FromTimeZoneToTimeZone(Value, TzId, newTimeZone).ToDateTimeUtc(); + + return new CalDateTime(newDt, newTimeZone); + } + + public IDateTime Add(TimeSpan ts) + { + return this + ts; + } + + public IDateTime Subtract(TimeSpan ts) + { + return this - ts; + } + + public TimeSpan Subtract(IDateTime dt) + { + return this - dt; + } + + public IDateTime AddYears(int years) + { + var dt = Copy(); + dt.Value = Value.AddYears(years); + return dt; + } + + public IDateTime AddMonths(int months) + { + var dt = Copy(); + dt.Value = Value.AddMonths(months); + return dt; + } + + public IDateTime AddDays(int days) + { + var dt = Copy(); + dt.Value = Value.AddDays(days); + return dt; + } + + public IDateTime AddHours(int hours) + { + var dt = Copy(); + if (!dt.HasTime && hours % 24 > 0) + { + dt.HasTime = true; + } + dt.Value = Value.AddHours(hours); + return dt; + } + + public IDateTime AddMinutes(int minutes) + { + var dt = Copy(); + if (!dt.HasTime && minutes % 1440 > 0) + { + dt.HasTime = true; + } + dt.Value = Value.AddMinutes(minutes); + return dt; + } + + public IDateTime AddSeconds(int seconds) + { + var dt = Copy(); + if (!dt.HasTime && seconds % 86400 > 0) + { + dt.HasTime = true; + } + dt.Value = Value.AddSeconds(seconds); + return dt; + } + + public IDateTime AddMilliseconds(int milliseconds) + { + var dt = Copy(); + if (!dt.HasTime && milliseconds % 86400000 > 0) + { + dt.HasTime = true; + } + dt.Value = Value.AddMilliseconds(milliseconds); + return dt; + } + + public IDateTime AddTicks(long ticks) + { + var dt = Copy(); + dt.HasTime = true; + dt.Value = Value.AddTicks(ticks); + return dt; + } + + public bool LessThan(IDateTime dt) + { + return this < dt; + } + + public bool GreaterThan(IDateTime dt) + { + return this > dt; + } + + public bool LessThanOrEqual(IDateTime dt) + { + return this <= dt; + } + + public bool GreaterThanOrEqual(IDateTime dt) + { + return this >= dt; + } + + public void AssociateWith(IDateTime dt) + { + if (AssociatedObject == null && dt.AssociatedObject != null) + { + AssociatedObject = dt.AssociatedObject; + } + else if (AssociatedObject != null && dt.AssociatedObject == null) + { + dt.AssociatedObject = AssociatedObject; + } + } + + public int CompareTo(IDateTime dt) + { + if (Equals(dt)) + { + return 0; + } + if (this < dt) + { + return -1; + } + if (this > dt) + { + return 1; + } + throw new Exception("An error occurred while comparing two IDateTime values."); + } + + public string ToString(string format) + { + return ToString(format, null); + } + + public string ToString(string format, IFormatProvider formatProvider) + { + var tz = TimeZoneName; + if (!string.IsNullOrEmpty(tz)) + { + tz = " " + tz; + } + + if (format != null) + { + return Value.ToString(format, formatProvider) + tz; + } + if (HasTime && HasDate) + { + return Value + tz; + } + if (HasTime) + { + return Value.TimeOfDay + tz; + } + return Value.ToString("d") + tz; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Evaluation/Evaluator.cs b/net-core/Ical.Net/Ical.Net/Evaluation/Evaluator.cs new file mode 100644 index 00000000..da5bd7a1 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Evaluation/Evaluator.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Evaluation; +using Ical.Net.Interfaces.General; +using Ical.Net.Utility; + +namespace Ical.Net.Evaluation +{ + public abstract class Evaluator : IEvaluator + { + private DateTime _mEvaluationStartBounds = DateTime.MaxValue; + private DateTime _mEvaluationEndBounds = DateTime.MinValue; + + private ICalendarObject _mAssociatedObject; + private readonly ICalendarDataType _mAssociatedDataType; + + protected HashSet MPeriods; + + protected Evaluator() + { + Initialize(); + } + + protected Evaluator(ICalendarObject associatedObject) + { + _mAssociatedObject = associatedObject; + + Initialize(); + } + + protected Evaluator(ICalendarDataType dataType) + { + _mAssociatedDataType = dataType; + + Initialize(); + } + + private void Initialize() + { + Calendar = CultureInfo.CurrentCulture.Calendar; + MPeriods = new HashSet(); + } + + protected IDateTime ConvertToIDateTime(DateTime dt, IDateTime referenceDate) + { + IDateTime newDt = new CalDateTime(dt, referenceDate.TzId); + newDt.AssociateWith(referenceDate); + return newDt; + } + + protected void IncrementDate(ref DateTime dt, RecurrencePattern pattern, int interval) + { + // FIXME: use a more specific exception. + if (interval == 0) + { + throw new Exception("Cannot evaluate with an interval of zero. Please use an interval other than zero."); + } + + var old = dt; + switch (pattern.Frequency) + { + case FrequencyType.Secondly: + dt = old.AddSeconds(interval); + break; + case FrequencyType.Minutely: + dt = old.AddMinutes(interval); + break; + case FrequencyType.Hourly: + dt = old.AddHours(interval); + break; + case FrequencyType.Daily: + dt = old.AddDays(interval); + break; + case FrequencyType.Weekly: + dt = DateUtil.AddWeeks(old, interval, pattern.FirstDayOfWeek); + break; + case FrequencyType.Monthly: + dt = old.AddDays(-old.Day + 1).AddMonths(interval); + break; + case FrequencyType.Yearly: + dt = old.AddDays(-old.DayOfYear + 1).AddYears(interval); + break; + // FIXME: use a more specific exception. + default: + throw new Exception("FrequencyType.NONE cannot be evaluated. Please specify a FrequencyType before evaluating the recurrence."); + } + } + + public System.Globalization.Calendar Calendar { get; private set; } + + public virtual DateTime EvaluationStartBounds + { + get { return _mEvaluationStartBounds; } + set { _mEvaluationStartBounds = value; } + } + + public virtual DateTime EvaluationEndBounds + { + get { return _mEvaluationEndBounds; } + set { _mEvaluationEndBounds = value; } + } + + public virtual ICalendarObject AssociatedObject + { + get + { + return _mAssociatedObject ?? _mAssociatedDataType?.AssociatedObject; + } + protected set { _mAssociatedObject = value; } + } + + public virtual HashSet Periods => MPeriods; + + public virtual void Clear() + { + _mEvaluationStartBounds = DateTime.MaxValue; + _mEvaluationEndBounds = DateTime.MinValue; + MPeriods.Clear(); + } + + public abstract HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Evaluation/EventEvaluator.cs b/net-core/Ical.Net/Ical.Net/Evaluation/EventEvaluator.cs new file mode 100644 index 00000000..a6df5d03 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Evaluation/EventEvaluator.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net.Evaluation +{ + public class EventEvaluator : RecurringEvaluator + { + protected CalendarEvent CalendarEvent + { + get { return Recurrable as CalendarEvent; } + set { Recurrable = value; } + } + + public EventEvaluator(CalendarEvent evt) : base(evt) {} + + /// + /// Evaluates this event to determine the dates and times for which the event occurs. + /// This method only evaluates events which occur between + /// and ; therefore, if you require a list of events which + /// occur outside of this range, you must specify a and + /// which encapsulate the date(s) of interest. + /// + /// For events with very complex recurrence rules, this method may be a bottleneck + /// during processing time, especially when this method in called for a large number + /// of events, in sequence, or for a very large time span. + /// + /// + /// + /// The beginning date of the range to evaluate. + /// The end date of the range to evaluate. + /// + /// + public override HashSet Evaluate(IDateTime referenceTime, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + { + // Evaluate recurrences normally + base.Evaluate(referenceTime, periodStart, periodEnd, includeReferenceDateInResults); + + foreach (var period in Periods) + { + period.Duration = CalendarEvent.Duration; + period.EndTime = period.Duration == null + ? period.StartTime + : period.StartTime.Add(CalendarEvent.Duration); + } + + // Ensure each period has a duration + foreach (var period in Periods.Where(p => p.EndTime == null)) + { + period.Duration = CalendarEvent.Duration; + period.EndTime = period.Duration == null + ? period.StartTime + : period.StartTime.Add(CalendarEvent.Duration); + } + + return Periods; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Evaluation/PeriodListEvaluator.cs b/net-core/Ical.Net/Ical.Net/Evaluation/PeriodListEvaluator.cs new file mode 100644 index 00000000..6fc58e78 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Evaluation/PeriodListEvaluator.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net.Evaluation +{ + public class PeriodListEvaluator : Evaluator + { + private readonly PeriodList _mPeriodList; + + public PeriodListEvaluator(PeriodList rdt) + { + _mPeriodList = rdt; + } + + public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + { + var periods = new HashSet(); + + if (includeReferenceDateInResults) + { + Period p = new Period(referenceDate); + periods.Add(p); + } + + if (periodEnd < periodStart) + { + return periods; + } + + periods.UnionWith(_mPeriodList); + return periods; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs b/net-core/Ical.Net/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs new file mode 100644 index 00000000..2bac3f2a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs @@ -0,0 +1,936 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Ical.Net.DataTypes; +using Ical.Net.Exceptions; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Utility; +using NodaTime; +using Period = Ical.Net.DataTypes.Period; + +namespace Ical.Net.Evaluation +{ + /// + /// Much of this code comes from iCal4j, as Ben Fortuna has done an + /// excellent job with the recurrence pattern evaluation there. + /// + /// Here's the iCal4j license: + /// ================== + /// iCal4j - License + /// ================== + /// + /// Copyright (c) 2009, Ben Fortuna + /// All rights reserved. + /// + /// Redistribution and use in source and binary forms, with or without + /// modification, are permitted provided that the following conditions + /// are met: + /// + /// o Redistributions of source code must retain the above copyright + /// notice, this list of conditions and the following disclaimer. + /// + /// o Redistributions in binary form must reproduce the above copyright + /// notice, this list of conditions and the following disclaimer in the + /// documentation and/or other materials provided with the distribution. + /// + /// o Neither the name of Ben Fortuna nor the names of any other contributors + /// may be used to endorse or promote products derived from this software + /// without specific prior written permission. + /// + /// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + /// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + /// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + /// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + /// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + /// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + /// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + /// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + /// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + /// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + /// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + /// + public class RecurrencePatternEvaluator : Evaluator + { + // FIXME: in ical4j this is configurable. + private static readonly int _maxIncrementCount = 1000; + + protected RecurrencePattern Pattern { get; set; } + + public RecurrencePatternEvaluator(RecurrencePattern pattern) + { + Pattern = pattern; + } + + private RecurrencePattern ProcessRecurrencePattern(IDateTime referenceDate) + { + var r = new RecurrencePattern(); + r.CopyFrom(Pattern); + + // Convert the UNTIL value to one that matches the same time information as the reference date + if (r.Until != DateTime.MinValue) + { + r.Until = DateUtil.MatchTimeZone(referenceDate, new CalDateTime(r.Until)).Value; + } + + if (r.Frequency > FrequencyType.Secondly && r.BySecond.Count == 0 && referenceDate.HasTime + /* NOTE: Fixes a bug where all-day events have BySecond/ByMinute/ByHour added incorrectly */) + { + r.BySecond.Add(referenceDate.Second); + } + if (r.Frequency > FrequencyType.Minutely && r.ByMinute.Count == 0 && referenceDate.HasTime + /* NOTE: Fixes a bug where all-day events have BySecond/ByMinute/ByHour added incorrectly */) + { + r.ByMinute.Add(referenceDate.Minute); + } + if (r.Frequency > FrequencyType.Hourly && r.ByHour.Count == 0 && referenceDate.HasTime + /* NOTE: Fixes a bug where all-day events have BySecond/ByMinute/ByHour added incorrectly */) + { + r.ByHour.Add(referenceDate.Hour); + } + + // If BYDAY, BYYEARDAY, or BYWEEKNO is specified, then + // we don't default BYDAY, BYMONTH or BYMONTHDAY + if (r.ByDay.Count == 0) + { + // If the frequency is weekly, use the original date's day of week. + // NOTE: fixes WeeklyCount1() and WeeklyUntil1() handling + // If BYWEEKNO is specified and BYMONTHDAY/BYYEARDAY is not specified, + // then let's add BYDAY to BYWEEKNO. + // NOTE: fixes YearlyByWeekNoX() handling + if (r.Frequency == FrequencyType.Weekly || (r.ByWeekNo.Count > 0 && r.ByMonthDay.Count == 0 && r.ByYearDay.Count == 0)) + { + r.ByDay.Add(new WeekDay(referenceDate.DayOfWeek)); + } + + // If BYMONTHDAY is not specified, + // default to the current day of month. + // NOTE: fixes YearlyByMonth1() handling, added BYYEARDAY exclusion + // to fix YearlyCountByYearDay1() handling + if (r.Frequency > FrequencyType.Weekly && r.ByWeekNo.Count == 0 && r.ByYearDay.Count == 0 && r.ByMonthDay.Count == 0) + { + r.ByMonthDay.Add(referenceDate.Day); + } + + // If BYMONTH is not specified, default to + // the current month. + // NOTE: fixes YearlyCountByYearDay1() handling + if (r.Frequency > FrequencyType.Monthly && r.ByWeekNo.Count == 0 && r.ByYearDay.Count == 0 && r.ByMonth.Count == 0) + { + r.ByMonth.Add(referenceDate.Month); + } + } + + return r; + } + + private void EnforceEvaluationRestrictions(RecurrencePattern pattern) + { + RecurrenceEvaluationModeType? evaluationMode = pattern.EvaluationMode; + RecurrenceRestrictionType? evaluationRestriction = pattern.RestrictionType; + + if (evaluationRestriction != RecurrenceRestrictionType.NoRestriction) + { + switch (evaluationMode) + { + case RecurrenceEvaluationModeType.AdjustAutomatically: + switch (pattern.Frequency) + { + case FrequencyType.Secondly: + { + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.Default: + case RecurrenceRestrictionType.RestrictSecondly: + pattern.Frequency = FrequencyType.Minutely; + break; + case RecurrenceRestrictionType.RestrictMinutely: + pattern.Frequency = FrequencyType.Hourly; + break; + case RecurrenceRestrictionType.RestrictHourly: + pattern.Frequency = FrequencyType.Daily; + break; + } + } + break; + case FrequencyType.Minutely: + { + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.RestrictMinutely: + pattern.Frequency = FrequencyType.Hourly; + break; + case RecurrenceRestrictionType.RestrictHourly: + pattern.Frequency = FrequencyType.Daily; + break; + } + } + break; + case FrequencyType.Hourly: + { + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.RestrictHourly: + pattern.Frequency = FrequencyType.Daily; + break; + } + } + break; + } + break; + case RecurrenceEvaluationModeType.ThrowException: + case RecurrenceEvaluationModeType.Default: + switch (pattern.Frequency) + { + case FrequencyType.Secondly: + { + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.Default: + case RecurrenceRestrictionType.RestrictSecondly: + case RecurrenceRestrictionType.RestrictMinutely: + case RecurrenceRestrictionType.RestrictHourly: + throw new EvaluationEngineException(); + } + } + break; + case FrequencyType.Minutely: + { + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.RestrictMinutely: + case RecurrenceRestrictionType.RestrictHourly: + throw new EvaluationEngineException(); + } + } + break; + case FrequencyType.Hourly: + { + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.RestrictHourly: + throw new EvaluationEngineException(); + } + } + break; + } + break; + } + } + } + + /** + * Returns a list of start dates in the specified period represented by this recur. This method includes a base date + * argument, which indicates the start of the fist occurrence of this recurrence. The base date is used to inject + * default values to return a set of dates in the correct format. For example, if the search start date (start) is + * Wed, Mar 23, 12:19PM, but the recurrence is Mon - Fri, 9:00AM - 5:00PM, the start dates returned should all be at + * 9:00AM, and not 12:19PM. + */ + + private HashSet GetDates(IDateTime seed, DateTime periodStart, DateTime periodEnd, int maxCount, RecurrencePattern pattern, + bool includeReferenceDateInResults) + { + var dates = new HashSet(); + var originalDate = DateUtil.GetSimpleDateTimeData(seed); + var seedCopy = DateUtil.GetSimpleDateTimeData(seed); + + if (includeReferenceDateInResults) + { + dates.Add(seedCopy); + } + + // optimize the start time for selecting candidates + // (only applicable where a COUNT is not specified) + if (pattern.Count == int.MinValue) + { + var incremented = seedCopy; + IncrementDate(ref incremented, pattern, pattern.Interval); + while (incremented < periodStart) + { + seedCopy = incremented; + IncrementDate(ref incremented, pattern, pattern.Interval); + } + } + + var expandBehavior = RecurrenceUtil.GetExpandBehaviorList(pattern); + + var invalidCandidateCount = 0; + var noCandidateIncrementCount = 0; + var candidate = DateTime.MinValue; + while ((maxCount < 0) || (dates.Count < maxCount)) + { + if (pattern.Until != DateTime.MinValue && candidate != DateTime.MinValue && candidate > pattern.Until) + { + break; + } + + if (candidate != DateTime.MinValue && candidate > periodEnd) + { + break; + } + + if (pattern.Count >= 1 && (dates.Count + invalidCandidateCount) >= pattern.Count) + { + break; + } + + var candidates = GetCandidates(seedCopy, pattern, expandBehavior); + if (candidates.Count > 0) + { + noCandidateIncrementCount = 0; + + // sort candidates for identifying when UNTIL date is exceeded.. + candidates.Sort(); + + foreach (var t in candidates.Where(t => t >= originalDate)) + { + candidate = t; + + // candidates MAY occur before periodStart + // For example, FREQ=YEARLY;BYWEEKNO=1 could return dates + // from the previous year. + // + // candidates exclusive of periodEnd.. + if (candidate >= periodEnd) + { + invalidCandidateCount++; + } + else if (pattern.Count >= 1 && (dates.Count + invalidCandidateCount) >= pattern.Count) + { + break; + } + else if (pattern.Until == DateTime.MinValue || candidate <= pattern.Until) + { + var utcCandidate = DateUtil.FromTimeZoneToTimeZone(candidate, DateUtil.GetZone(seed.TzId), DateTimeZone.Utc).ToDateTimeUtc(); + if (!dates.Contains(candidate) && (pattern.Until == DateTime.MinValue || utcCandidate <= pattern.Until)) + { + dates.Add(candidate); + } + } + } + } + else + { + noCandidateIncrementCount++; + if ((_maxIncrementCount > 0) && (noCandidateIncrementCount > _maxIncrementCount)) + { + break; + } + } + + IncrementDate(ref seedCopy, pattern, pattern.Interval); + } + + // sort final list.. + return dates; + } + + /** + * Returns a list of possible dates generated from the applicable BY* rules, using the specified date as a seed. + * @param date the seed date + * @param value the type of date list to return + * @return a DateList + */ + + private List GetCandidates(DateTime date, RecurrencePattern pattern, bool?[] expandBehaviors) + { + var dates = new List(128) {date}; + dates = GetMonthVariants(dates, pattern, expandBehaviors[0]); + dates = GetWeekNoVariants(dates, pattern, expandBehaviors[1]); + dates = GetYearDayVariants(dates, pattern, expandBehaviors[2]); + dates = GetMonthDayVariants(dates, pattern, expandBehaviors[3]); + dates = GetDayVariants(dates, pattern, expandBehaviors[4]); + dates = GetHourVariants(dates, pattern, expandBehaviors[5]); + dates = GetMinuteVariants(dates, pattern, expandBehaviors[6]); + dates = GetSecondVariants(dates, pattern, expandBehaviors[7]); + dates = ApplySetPosRules(dates, pattern); + return dates; + } + + /** + * Applies BYSETPOS rules to dates. Valid positions are from 1 to the size of the date list. Invalid + * positions are ignored. + * @param dates + */ + + private List ApplySetPosRules(List dates, RecurrencePattern pattern) + { + // return if no SETPOS rules specified.. + if (pattern.BySetPosition.Count == 0) + { + return dates; + } + + // sort the list before processing.. + dates.Sort(); + + var setPosDates = new List(dates.Count); + var size = dates.Count; + + foreach (var pos in pattern.BySetPosition) + { + if (pos > 0 && pos <= size) + { + setPosDates.Add(dates[pos - 1]); + } + else if (pos < 0 && pos >= -size) + { + setPosDates.Add(dates[size + pos]); + } + } + return setPosDates; + } + + /** + * Applies BYMONTH rules specified in this Recur instance to the specified date list. If no BYMONTH rules are + * specified the date list is returned unmodified. + * @param dates + * @return + */ + + private List GetMonthVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByMonth.Count == 0) + { + return dates; + } + + if (expand.Value) + { + // Expand behavior + var monthlyDates = new List(128); + foreach (var date in dates) + { + monthlyDates.AddRange(pattern.ByMonth.Select(month => date.AddMonths(month - date.Month))); + } + return monthlyDates; + } + // Limit behavior + for (var i = dates.Count - 1; i >= 0; i--) + { + var date = dates[i]; + for (var j = 0; j < pattern.ByMonth.Count; j++) + { + if (date.Month == pattern.ByMonth[j]) + { + goto Next; + } + } + dates.RemoveAt(i); + Next: + ; + } + return dates; + } + + /** + * Applies BYWEEKNO rules specified in this Recur instance to the specified date list. If no BYWEEKNO rules are + * specified the date list is returned unmodified. + * @param dates + * @return + */ + + private List GetWeekNoVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByWeekNo.Count == 0) + { + return dates; + } + + if (!expand.Value) + { + return new List(); + } + + // Expand behavior + var weekNoDates = new List(128); + for (var i = 0; i < dates.Count; i++) + { + var date = dates[i]; + foreach (var weekNo in pattern.ByWeekNo) + { + // Determine our current week number + var currWeekNo = Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); + while (currWeekNo > weekNo) + { + // If currWeekNo > weekNo, then we're likely at the start of a year + // where currWeekNo could be 52 or 53. If we simply step ahead 7 days + // we should be back to week 1, where we can easily make the calculation + // to move to weekNo. + date = date.AddDays(7); + currWeekNo = Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); + } + + // Move ahead to the correct week of the year + date = date.AddDays((weekNo - currWeekNo) * 7); + + // Step backward single days until we're at the correct DayOfWeek + while (date.DayOfWeek != pattern.FirstDayOfWeek) + { + date = date.AddDays(-1); + } + + for (var k = 0; k < 7; k++) + { + weekNoDates.Add(date); + date = date.AddDays(1); + } + } + } + return weekNoDates; + } + + /** + * Applies BYYEARDAY rules specified in this Recur instance to the specified date list. If no BYYEARDAY rules are + * specified the date list is returned unmodified. + * @param dates + * @return + */ + + private List GetYearDayVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByYearDay.Count == 0) + { + return dates; + } + + if (expand.Value) + { + var yearDayDates = new List(dates.Count); + foreach (var date in dates) + { + var date1 = date; + yearDayDates.AddRange(pattern.ByYearDay.Select(yearDay => yearDay > 0 + ? date1.AddDays(-date1.DayOfYear + yearDay) + : date1.AddDays(-date1.DayOfYear + 1).AddYears(1).AddDays(yearDay))); + } + return yearDayDates; + } + // Limit behavior + for (var i = dates.Count - 1; i >= 0; i--) + { + var date = dates[i]; + for (var j = 0; j < pattern.ByYearDay.Count; j++) + { + var yearDay = pattern.ByYearDay[j]; + + var newDate = yearDay > 0 + ? date.AddDays(-date.DayOfYear + yearDay) + : date.AddDays(-date.DayOfYear + 1).AddYears(1).AddDays(yearDay); + + if (newDate.DayOfYear == date.DayOfYear) + { + goto Next; + } + } + + dates.RemoveAt(i); + Next: + ; + } + + return dates; + } + + /** + * Applies BYMONTHDAY rules specified in this Recur instance to the specified date list. If no BYMONTHDAY rules are + * specified the date list is returned unmodified. + * @param dates + * @return + */ + + private List GetMonthDayVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByMonthDay.Count == 0) + { + return dates; + } + + if (expand.Value) + { + var monthDayDates = new List(128); + for (var i = 0; i < dates.Count; i++) + { + var date = dates[i]; + monthDayDates.AddRange( + from monthDay in pattern.ByMonthDay + let daysInMonth = Calendar.GetDaysInMonth(date.Year, date.Month) + where Math.Abs(monthDay) <= daysInMonth + select monthDay > 0 + ? date.AddDays(-date.Day + monthDay) + : date.AddDays(-date.Day + 1).AddMonths(1).AddDays(monthDay) + ); + } + return monthDayDates; + } + // Limit behavior + for (var i = dates.Count - 1; i >= 0; i--) + { + var date = dates[i]; + for (var j = 0; j < pattern.ByMonthDay.Count; j++) + { + var monthDay = pattern.ByMonthDay[j]; + + var daysInMonth = Calendar.GetDaysInMonth(date.Year, date.Month); + if (Math.Abs(monthDay) > daysInMonth) + { + throw new ArgumentException("Invalid day of month: " + date + " (day " + monthDay + ")"); + } + + // Account for positive or negative numbers + var newDate = monthDay > 0 + ? date.AddDays(-date.Day + monthDay) + : date.AddDays(-date.Day + 1).AddMonths(1).AddDays(monthDay); + + if (newDate.Day.Equals(date.Day)) + { + goto Next; + } + } + + Next: + dates.RemoveAt(i); + } + + return dates; + } + + /** + * Applies BYDAY rules specified in this Recur instance to the specified date list. If no BYDAY rules are specified + * the date list is returned unmodified. + * @param dates + * @return + */ + + private List GetDayVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByDay.Count == 0) + { + return dates; + } + + if (expand.Value) + { + // Expand behavior + var weekDayDates = new List(); + for (var i = 0; i < dates.Count; i++) + { + var date = dates[i]; + for (var j = 0; j < pattern.ByDay.Count; j++) + { + weekDayDates.AddRange(GetAbsWeekDays(date, pattern.ByDay[j], pattern)); + } + } + + return weekDayDates; + } + // Limit behavior + for (var i = dates.Count - 1; i >= 0; i--) + { + var date = dates[i]; + for (var j = 0; j < pattern.ByDay.Count; j++) + { + var weekDay = pattern.ByDay[j]; + if (weekDay.DayOfWeek.Equals(date.DayOfWeek)) + { + // If no offset is specified, simply test the day of week! + // FIXME: test with offset... + if (date.DayOfWeek.Equals(weekDay.DayOfWeek)) + { + goto Next; + } + } + } + dates.RemoveAt(i); + Next: + ; + } + + return dates; + } + + /** + * Returns a list of applicable dates corresponding to the specified week day in accordance with the frequency + * specified by this recurrence rule. + * @param date + * @param weekDay + * @return + */ + + private List GetAbsWeekDays(DateTime date, WeekDay weekDay, RecurrencePattern pattern) + { + var days = new List(64); + + var dayOfWeek = weekDay.DayOfWeek; + if (pattern.Frequency == FrequencyType.Daily) + { + if (date.DayOfWeek == dayOfWeek) + { + days.Add(date); + } + } + else if (pattern.Frequency == FrequencyType.Weekly || pattern.ByWeekNo.Count > 0) + { + var weekNo = Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); + + // construct a list of possible week days.. + while (date.DayOfWeek != dayOfWeek) + { + date = date.AddDays(1); + } + + while (Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek) == weekNo) + { + days.Add(date); + date = date.AddDays(7); + } + } + else if (pattern.Frequency == FrequencyType.Monthly || pattern.ByMonth.Count > 0) + { + var month = date.Month; + + // construct a list of possible month days.. + date = date.AddDays(-date.Day + 1); + while (date.DayOfWeek != dayOfWeek) + { + date = date.AddDays(1); + } + + while (date.Month == month) + { + days.Add(date); + date = date.AddDays(7); + } + } + else if (pattern.Frequency == FrequencyType.Yearly) + { + var year = date.Year; + + // construct a list of possible year days.. + date = date.AddDays(-date.DayOfYear + 1); + while (date.DayOfWeek != dayOfWeek) + { + date = date.AddDays(1); + } + + while (date.Year == year) + { + days.Add(date); + date = date.AddDays(7); + } + } + return GetOffsetDates(days, weekDay.Offset); + } + + /** + * Returns a single-element sublist containing the element of list at offset. Valid + * offsets are from 1 to the size of the list. If an invalid offset is supplied, all elements from list + * are added to sublist. + * @param list + * @param offset + * @param sublist + */ + + private List GetOffsetDates(List dates, int offset) + { + if (offset == int.MinValue) + { + return dates; + } + + var offsetDates = new List(16); + var size = dates.Count; + if (offset < 0 && offset >= -size) + { + offsetDates.Add(dates[size + offset]); + } + else if (offset > 0 && offset <= size) + { + offsetDates.Add(dates[offset - 1]); + } + return offsetDates; + } + + /** + * Applies BYHOUR rules specified in this Recur instance to the specified date list. If no BYHOUR rules are + * specified the date list is returned unmodified. + * @param dates + * @return + */ + + private List GetHourVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByHour.Count == 0) + { + return dates; + } + + if (expand.Value) + { + // Expand behavior + var hourlyDates = new List(128); + for (var i = 0; i < dates.Count; i++) + { + var date = dates[i]; + for (var j = 0; j < pattern.ByHour.Count; j++) + { + var hour = pattern.ByHour[j]; + date = date.AddHours(-date.Hour + hour); + hourlyDates.Add(date); + } + } + return hourlyDates; + } + // Limit behavior + for (var i = dates.Count - 1; i >= 0; i--) + { + var date = dates[i]; + for (var j = 0; j < pattern.ByHour.Count; j++) + { + var hour = pattern.ByHour[j]; + if (date.Hour == hour) + { + goto Next; + } + } + // Remove unmatched dates + dates.RemoveAt(i); + Next: + ; + } + return dates; + } + + /** + * Applies BYMINUTE rules specified in this Recur instance to the specified date list. If no BYMINUTE rules are + * specified the date list is returned unmodified. + * @param dates + * @return + */ + + private List GetMinuteVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByMinute.Count == 0) + { + return dates; + } + + if (expand.Value) + { + // Expand behavior + var minutelyDates = new List(128); + for (var i = 0; i < dates.Count; i++) + { + var date = dates[i]; + for (var j = 0; j < pattern.ByMinute.Count; j++) + { + var minute = pattern.ByMinute[j]; + date = date.AddMinutes(-date.Minute + minute); + minutelyDates.Add(date); + } + } + return minutelyDates; + } + // Limit behavior + for (var i = dates.Count - 1; i >= 0; i--) + { + var date = dates[i]; + for (var j = 0; j < pattern.ByMinute.Count; j++) + { + var minute = pattern.ByMinute[j]; + if (date.Minute == minute) + { + goto Next; + } + } + // Remove unmatched dates + dates.RemoveAt(i); + Next: + ; + } + return dates; + } + + /** + * Applies BYSECOND rules specified in this Recur instance to the specified date list. If no BYSECOND rules are + * specified the date list is returned unmodified. + * @param dates + * @return + */ + + private List GetSecondVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.BySecond.Count == 0) + { + return dates; + } + + if (expand.Value) + { + // Expand behavior + var secondlyDates = new List(128); + for (var i = 0; i < dates.Count; i++) + { + var date = dates[i]; + for (var j = 0; j < pattern.BySecond.Count; j++) + { + var second = pattern.BySecond[j]; + date = date.AddSeconds(-date.Second + second); + secondlyDates.Add(date); + } + } + return secondlyDates; + } + // Limit behavior + for (var i = dates.Count - 1; i >= 0; i--) + { + var date = dates[i]; + for (var j = 0; j < pattern.BySecond.Count; j++) + { + var second = pattern.BySecond[j]; + if (date.Second == second) + { + goto Next; + } + } + // Remove unmatched dates + dates.RemoveAt(i); + Next: + ; + } + return dates; + } + + private Period CreatePeriod(DateTime dt, IDateTime referenceDate) + { + // Turn each resulting date/time into an IDateTime and associate it + // with the reference date. + IDateTime newDt = new CalDateTime(dt, referenceDate.TzId); + + // NOTE: fixes bug #2938007 - hasTime missing + newDt.HasTime = referenceDate.HasTime; + + newDt.AssociateWith(referenceDate); + + // Create a period from the new date/time. + return new Period(newDt); + } + + public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + { + // Create a recurrence pattern suitable for use during evaluation. + var pattern = ProcessRecurrencePattern(referenceDate); + + // Enforce evaluation restrictions on the pattern. + EnforceEvaluationRestrictions(pattern); + Periods.Clear(); + + var periodQuery = GetDates(referenceDate, periodStart, periodEnd, -1, pattern, includeReferenceDateInResults) + .Select(dt => CreatePeriod(dt, referenceDate)); + + Periods.UnionWith(periodQuery); + + return Periods; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Evaluation/RecurringEvaluator.cs b/net-core/Ical.Net/Ical.Net/Evaluation/RecurringEvaluator.cs new file mode 100644 index 00000000..bb30af54 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Evaluation/RecurringEvaluator.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Evaluation; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.Evaluation +{ + public class RecurringEvaluator : Evaluator + { + protected IRecurrable Recurrable { get; set; } + + public RecurringEvaluator(IRecurrable obj) + { + Recurrable = obj; + + // We're not sure if the object is a calendar object + // or a calendar data type, so we need to assign + // the associated object manually + if (obj is ICalendarObject) + { + AssociatedObject = (ICalendarObject) obj; + } + if (obj is ICalendarDataType) + { + var dt = (ICalendarDataType) obj; + AssociatedObject = dt.AssociatedObject; + } + } + + /// + /// Evaulates the RRule component, and adds each specified Period to the Periods collection. + /// + /// + /// The beginning date of the range to evaluate. + /// The end date of the range to evaluate. + /// + protected HashSet EvaluateRRule(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + { + if (Recurrable.RecurrenceRules == null || !Recurrable.RecurrenceRules.Any()) + { + return new HashSet(); + } + + var evaluator = Recurrable.RecurrenceRules.First().GetService(typeof(IEvaluator)) as IEvaluator; + if (evaluator == null) + { + return new HashSet(); + } + var periods = evaluator.Evaluate(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); + + if (includeReferenceDateInResults) + { + periods.UnionWith(new[] { new Period(referenceDate) }); + } + return periods; + } + + /// Evalates the RDate component, and adds each specified DateTime or Period to the Periods collection. + protected HashSet EvaluateRDate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd) + { + if (Recurrable.RecurrenceDates == null || !Recurrable.RecurrenceDates.Any()) + { + return new HashSet(); + } + + var recurrences = new HashSet(Recurrable.RecurrenceDates.SelectMany(rdate => rdate)); + return recurrences; + } + + /// + /// Evaulates the ExRule component, and excludes each specified DateTime from the Periods collection. + /// + /// + /// The beginning date of the range to evaluate. + /// The end date of the range to evaluate. + protected HashSet EvaluateExRule(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd) + { + if (Recurrable.ExceptionRules == null || !Recurrable.ExceptionRules.Any()) + { + return new HashSet(); + } + + var evaluator = Recurrable.ExceptionRules.First().GetService(typeof(IEvaluator)) as IEvaluator; + if (evaluator == null) + { + return new HashSet(); + } + + var exRuleEvaluatorQuery = Recurrable.ExceptionRules.SelectMany(exRule => evaluator.Evaluate(referenceDate, periodStart, periodEnd, false)); + var exRuleExclusions = new HashSet(exRuleEvaluatorQuery); + return exRuleExclusions; + } + + /// + /// Evalates the ExDate component, and excludes each specified DateTime or Period from the Periods collection. + /// + /// + /// The beginning date of the range to evaluate. + /// The end date of the range to evaluate. + protected HashSet EvaluateExDate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd) + { + if (Recurrable.ExceptionDates == null || !Recurrable.ExceptionDates.Any()) + { + return new HashSet(); + } + + var exDates = new HashSet(Recurrable.ExceptionDates.SelectMany(exDate => exDate)); + return exDates; + } + + public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + { + Periods.Clear(); + + var rruleOccurrences = EvaluateRRule(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); + if (includeReferenceDateInResults) + { + rruleOccurrences.UnionWith(new[] { new Period(referenceDate), }); + } + + var rdateOccurrences = EvaluateRDate(referenceDate, periodStart, periodEnd); + + var exRuleExclusions = EvaluateExRule(referenceDate, periodStart, periodEnd); + var exDateExclusions = EvaluateExDate(referenceDate, periodStart, periodEnd); + + //Exclusions trump inclusions + Periods.UnionWith(rruleOccurrences); + Periods.UnionWith(rdateOccurrences); + Periods.ExceptWith(exRuleExclusions); + Periods.ExceptWith(exDateExclusions); + + var dateOverlaps = FindDateOverlaps(exDateExclusions); + Periods.ExceptWith(dateOverlaps); + + if (EvaluationStartBounds == DateTime.MaxValue || EvaluationStartBounds > periodStart) + { + EvaluationStartBounds = periodStart; + } + if (EvaluationEndBounds == DateTime.MinValue || EvaluationEndBounds < periodEnd) + { + EvaluationEndBounds = periodEnd; + } + + return Periods; + } + + private HashSet FindDateOverlaps(HashSet dates) + { + var datesWithoutTimes = new HashSet(dates.Where(d => d.StartTime.Value.TimeOfDay == TimeSpan.Zero).Select(d => d.StartTime.Value)); + var overlaps = new HashSet(Periods.Where(p => datesWithoutTimes.Contains(p.StartTime.Value.Date))); + return overlaps; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Evaluation/TodoEvaluator.cs b/net-core/Ical.Net/Ical.Net/Evaluation/TodoEvaluator.cs new file mode 100644 index 00000000..60cda022 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Evaluation/TodoEvaluator.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Evaluation; +using Ical.Net.Utility; + +namespace Ical.Net.Evaluation +{ + public class TodoEvaluator : RecurringEvaluator + { + protected Todo Todo => Recurrable as Todo; + + public TodoEvaluator(Todo todo) : base(todo) {} + + public void EvaluateToPreviousOccurrence(IDateTime completedDate, IDateTime currDt) + { + var beginningDate = completedDate.Copy(); + + if (Todo.RecurrenceRules != null) + { + foreach (var rrule in Todo.RecurrenceRules) + { + DetermineStartingRecurrence(rrule, ref beginningDate); + } + } + if (Todo.RecurrenceDates != null) + { + foreach (var rdate in Todo.RecurrenceDates) + { + DetermineStartingRecurrence(rdate, ref beginningDate); + } + } + if (Todo.ExceptionRules != null) + { + foreach (var exrule in Todo.ExceptionRules) + { + DetermineStartingRecurrence(exrule, ref beginningDate); + } + } + if (Todo.ExceptionDates != null) + { + foreach (var exdate in Todo.ExceptionDates) + { + DetermineStartingRecurrence(exdate, ref beginningDate); + } + } + + Evaluate(Todo.Start, DateUtil.GetSimpleDateTimeData(beginningDate), DateUtil.GetSimpleDateTimeData(currDt).AddTicks(1), true); + } + + public void DetermineStartingRecurrence(PeriodList rdate, ref IDateTime referenceDateTime) + { + var evaluator = rdate.GetService(); + + var dt2 = referenceDateTime; + foreach (var p in evaluator.Periods.Where(p => p.StartTime.LessThan(dt2))) + { + referenceDateTime = p.StartTime; + } + } + + public void DetermineStartingRecurrence(RecurrencePattern recur, ref IDateTime referenceDateTime) + { + if (recur.Count != int.MinValue) + { + referenceDateTime = Todo.Start.Copy(); + } + else + { + var dtVal = referenceDateTime.Value; + IncrementDate(ref dtVal, recur, -recur.Interval); + referenceDateTime.Value = dtVal; + } + } + + public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + { + // TODO items can only recur if a start date is specified + if (Todo.Start == null) + { + return new HashSet(); + } + + base.Evaluate(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); + + // Ensure each period has a duration + foreach (var period in Periods.Where(period => period.EndTime == null)) + { + period.Duration = Todo.Duration; + if (period.Duration != null) + { + period.EndTime = period.StartTime.Add(Todo.Duration); + } + else + { + period.Duration = Todo.Duration; + } + } + return Periods; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Exceptions/EvaluationEngineException.cs b/net-core/Ical.Net/Ical.Net/Exceptions/EvaluationEngineException.cs new file mode 100644 index 00000000..a5ca1bb9 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Exceptions/EvaluationEngineException.cs @@ -0,0 +1,10 @@ +using System; + +namespace Ical.Net.Exceptions +{ + public class EvaluationEngineException : Exception + { + public EvaluationEngineException() + : base("An error occurred during the evaluation process that could not be automatically handled. Check the evaluation mode and restrictions.") {} + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/ExtensionMethods/CalendarObjectExtensions.cs b/net-core/Ical.Net/Ical.Net/ExtensionMethods/CalendarObjectExtensions.cs new file mode 100644 index 00000000..8f0d2e0d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/ExtensionMethods/CalendarObjectExtensions.cs @@ -0,0 +1,17 @@ +using Ical.Net.Interfaces.General; + +namespace Ical.Net.ExtensionMethods +{ + public static class CalendarObjectExtensions + { + public static void AddChild(this ICalendarObject obj, TItem child) where TItem : ICalendarObject + { + obj.Children.Add(child); + } + + public static void RemoveChild(this ICalendarObject obj, TItem child) where TItem : ICalendarObject + { + obj.Children.Remove(child); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/CalendarObject.cs b/net-core/Ical.Net/Ical.Net/General/CalendarObject.cs new file mode 100644 index 00000000..df405c6d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/CalendarObject.cs @@ -0,0 +1,202 @@ +using System; +using System.Runtime.Serialization; +using Ical.Net.Collections; +using Ical.Net.ExtensionMethods; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.General +{ + /// + /// The base class for all iCalendar objects and components. + /// + public class CalendarObject : CalendarObjectBase, ICalendarObject + { + private ICalendarObjectList _children; + private ServiceProvider _serviceProvider; + + internal CalendarObject() + { + Initialize(); + } + + public CalendarObject(string name) : this() + { + Name = name; + } + + public CalendarObject(int line, int col) : this() + { + Line = line; + Column = col; + } + + private void Initialize() + { + //ToDo: I'm fairly certain this is ONLY used for null checking. If so, maybe it can just be a bool? CalendarObjectList is an empty object, and + //ToDo: its constructor parameter is ignored + _children = new CalendarObjectList(this); + _serviceProvider = new ServiceProvider(); + + _children.ItemAdded += Children_ItemAdded; + } + + [OnDeserializing] + internal void DeserializingInternal(StreamingContext context) + { + OnDeserializing(context); + } + + [OnDeserialized] + internal void DeserializedInternal(StreamingContext context) + { + OnDeserialized(context); + } + + protected virtual void OnDeserializing(StreamingContext context) + { + Initialize(); + } + + protected virtual void OnDeserialized(StreamingContext context) {} + + private void Children_ItemAdded(object sender, ObjectEventArgs e) + { + e.First.Parent = this; + } + + protected bool Equals(CalendarObject other) => string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((CalendarObject) obj); + } + + public override int GetHashCode() + { + return Name?.GetHashCode() ?? 0; + } + + public override void CopyFrom(ICopyable c) + { + var obj = c as ICalendarObject; + if (obj == null) + { + return; + } + + // Copy the name and basic information + Name = obj.Name; + Parent = obj.Parent; + Line = obj.Line; + Column = obj.Column; + + // Add each child + Children.Clear(); + foreach (var child in obj.Children) + { + this.AddChild(child); + } + } + + /// + /// Returns the parent iCalObject that owns this one. + /// + public virtual ICalendarObject Parent { get; set; } + + /// + /// A collection of iCalObjects that are children of the current object. + /// + public virtual ICalendarObjectList Children => _children; + + /// + /// Gets or sets the name of the iCalObject. For iCalendar components, this is the RFC 5545 name of the component. + /// + /// + /// Event - "VEVENT" + /// Todo - "VTODO" + /// TimeZone - "VTIMEZONE" + /// etc. + /// + /// + /// + public virtual string Name { get; set; } + + /// + /// Returns the that this DDayiCalObject belongs to. + /// + public virtual Calendar Calendar + { + get + { + ICalendarObject obj = this; + while (!(obj is Calendar) && obj.Parent != null) + { + obj = obj.Parent; + } + + if (obj is Calendar) + { + return (Calendar) obj; + } + return null; + } + protected set { } + } + + public virtual int Line { get; set; } + + public virtual int Column { get; set; } + + public virtual object GetService(Type serviceType) + { + return _serviceProvider.GetService(serviceType); + } + + public virtual object GetService(string name) + { + return _serviceProvider.GetService(name); + } + + public virtual T GetService() + { + return _serviceProvider.GetService(); + } + + public virtual T GetService(string name) + { + return _serviceProvider.GetService(name); + } + + public virtual void SetService(string name, object obj) + { + _serviceProvider.SetService(name, obj); + } + + public virtual void SetService(object obj) + { + _serviceProvider.SetService(obj); + } + + public virtual void RemoveService(Type type) + { + _serviceProvider.RemoveService(type); + } + + public virtual void RemoveService(string name) + { + _serviceProvider.RemoveService(name); + } + + public virtual string Group + { + get { return Name; } + set + { + Name = value; + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/CalendarObjectBase.cs b/net-core/Ical.Net/Ical.Net/General/CalendarObjectBase.cs new file mode 100644 index 00000000..a7263c52 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/CalendarObjectBase.cs @@ -0,0 +1,52 @@ +using System; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.General +{ + public class CalendarObjectBase : ICopyable, ILoadable + { + private bool _mIsLoaded; + + public CalendarObjectBase() + { + // Objects that are loaded using a normal constructor + // are "Loaded" by default. Objects that are being + // deserialized do not use the constructor. + _mIsLoaded = true; + } + + /// + /// Copies values from the target object to the + /// current object. + /// + public virtual void CopyFrom(ICopyable c) {} + + /// + /// Creates a copy of the object. + /// + /// The copy of the object. + public virtual T Copy() + { + var type = GetType(); + var obj = Activator.CreateInstance(type) as ICopyable; + + // Duplicate our values + if (obj is T) + { + obj.CopyFrom(this); + return (T) obj; + } + return default(T); + } + + public virtual bool IsLoaded => _mIsLoaded; + + public event EventHandler Loaded; + + public virtual void OnLoaded() + { + _mIsLoaded = true; + Loaded?.Invoke(this, EventArgs.Empty); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/CalendarObjectList.cs b/net-core/Ical.Net/Ical.Net/General/CalendarObjectList.cs new file mode 100644 index 00000000..9af76bb1 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/CalendarObjectList.cs @@ -0,0 +1,13 @@ +using Ical.Net.Collections; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.General +{ + /// + /// A collection of calendar objects. + /// + public class CalendarObjectList : GroupedList, ICalendarObjectList + { + public CalendarObjectList(ICalendarObject parent) {} + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/CalendarParameter.cs b/net-core/Ical.Net/Ical.Net/General/CalendarParameter.cs new file mode 100644 index 00000000..0d939cda --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/CalendarParameter.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.Serialization; +using Ical.Net.Collections.Interfaces; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.General +{ + [DebuggerDisplay("{Name}={string.Join(\",\", Values)}")] + public class CalendarParameter : CalendarObject, IValueObject + { + private HashSet _values; + + public CalendarParameter() + { + Initialize(); + } + + public CalendarParameter(string name) : base(name) + { + Initialize(); + } + + public CalendarParameter(string name, string value) : base(name) + { + Initialize(); + AddValue(value); + } + + public CalendarParameter(string name, IEnumerable values) : base(name) + { + Initialize(); + foreach (var v in values) + { + AddValue(v); + } + } + + private void Initialize() + { + _values = new HashSet(StringComparer.OrdinalIgnoreCase); + } + + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); + + Initialize(); + } + + public override void CopyFrom(ICopyable c) + { + base.CopyFrom(c); + + var p = c as CalendarParameter; + if (p?.Values == null) + { + return; + } + + _values = new HashSet(StringComparer.OrdinalIgnoreCase); + _values.UnionWith(p.Values); + } + + public virtual IEnumerable Values => _values; + + public virtual bool ContainsValue(string value) + { + return _values.Contains(value); + } + + public virtual int ValueCount => _values?.Count ?? 0; + + public virtual void SetValue(string value) + { + _values.Add(value); + } + + public virtual void SetValue(IEnumerable values) + { + // Remove all previous values + _values.Clear(); + _values.UnionWith(values); + } + + public virtual void AddValue(string value) + { + if (value != null) + { + _values.Add(value); + } + } + + public virtual void RemoveValue(string value) {} + + public virtual string Value + { + get + { + return Values?.FirstOrDefault(); + } + set { SetValue(value); } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/CalendarProperty.cs b/net-core/Ical.Net/Ical.Net/General/CalendarProperty.cs new file mode 100644 index 00000000..f155659b --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/CalendarProperty.cs @@ -0,0 +1,156 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.General +{ + /// + /// A class that represents a property of the + /// itself or one of its components. It can also represent non-standard + /// (X-) properties of an iCalendar component, as seen with many + /// applications, such as with Apple's iCal. + /// X-WR-CALNAME:US Holidays + /// + /// + /// Currently, the "known" properties for an iCalendar are as + /// follows: + /// + /// ProdID + /// Version + /// CalScale + /// Method + /// + /// There may be other, custom X-properties applied to the calendar, + /// and X-properties may be applied to calendar components. + /// + [DebuggerDisplay("{Name}:{Value}")] + public class CalendarProperty : CalendarObject, ICalendarProperty + { + private List _values = new List(128); + + /// + /// Returns a list of parameters that are associated with the iCalendar object. + /// + public virtual IParameterCollection Parameters { get; protected set; } = new ParameterList(); + + public CalendarProperty() {} + + public CalendarProperty(string name) : base(name) {} + + public CalendarProperty(string name, object value) : base(name) + { + _values.Add(value); + } + + public CalendarProperty(int line, int col) : base(line, col) {} + + /// + /// Adds a parameter to the iCalendar object. + /// + public virtual void AddParameter(string name, string value) + { + var p = new CalendarParameter(name, value); + Parameters.Add(p); + } + + /// + /// Adds a parameter to the iCalendar object. + /// + public virtual void AddParameter(CalendarParameter p) + { + Parameters.Add(p); + } + + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + + var p = obj as ICalendarProperty; + if (p == null) + { + return; + } + + SetValue(p.Values); + } + + public virtual IEnumerable Values => _values; + + public object Value + { + get + { + return _values?.FirstOrDefault(); + } + set + { + if (value == null) + { + _values = null; + return; + } + + if (_values != null && _values.Count > 0) + { + _values[0] = value; + } + else + { + _values?.Clear(); + _values?.Add(value); + } + } + } + + public virtual bool ContainsValue(object value) + { + return _values.Contains(value); + } + + public virtual int ValueCount => _values?.Count ?? 0; + + public virtual void SetValue(object value) + { + if (_values.Count == 0) + { + _values.Add(value); + } + else if (value != null) + { + // Our list contains values. Let's set the first value! + _values[0] = value; + } + else + { + _values.Clear(); + } + } + + public virtual void SetValue(IEnumerable values) + { + // Remove all previous values + _values.Clear(); + _values.AddRange(values); + } + + public virtual void AddValue(object value) + { + if (value == null) + { + return; + } + + _values.Add(value); + } + + public virtual void RemoveValue(object value) + { + if (value == null) + { + return; + } + _values.Remove(value); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/CalendarPropertyList.cs b/net-core/Ical.Net/Ical.Net/General/CalendarPropertyList.cs new file mode 100644 index 00000000..411099d4 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/CalendarPropertyList.cs @@ -0,0 +1,34 @@ +using System.Linq; +using Ical.Net.Collections; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.General +{ + public class CalendarPropertyList : GroupedValueList + { + private readonly ICalendarObject _mParent; + + public CalendarPropertyList() {} + + public CalendarPropertyList(ICalendarObject parent) + { + _mParent = parent; + ItemAdded += CalendarPropertyList_ItemAdded; + } + + private void CalendarPropertyList_ItemAdded(object sender, ObjectEventArgs e) + { + e.First.Parent = _mParent; + } + + public ICalendarProperty this[string name] + { + get + { + return ContainsKey(name) + ? AllOf(name).FirstOrDefault() + : null; + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/ParameterList.cs b/net-core/Ical.Net/Ical.Net/General/ParameterList.cs new file mode 100644 index 00000000..c4529084 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/ParameterList.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Ical.Net.Collections; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.General +{ + public class ParameterList : GroupedValueList, IParameterCollection + { + public virtual void SetParent(ICalendarObject parent) + { + foreach (var parameter in this) + { + parameter.Parent = parent; + } + } + + public virtual void Add(string name, string value) + { + Add(new CalendarParameter(name, value)); + } + + public virtual string Get(string name) => Get(name); + + public virtual IList GetMany(string name) => GetMany(name); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/Proxies/CalendarObjectListProxy.cs b/net-core/Ical.Net/Ical.Net/General/Proxies/CalendarObjectListProxy.cs new file mode 100644 index 00000000..34437e39 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/Proxies/CalendarObjectListProxy.cs @@ -0,0 +1,15 @@ +using System.Linq; +using Ical.Net.Collections.Interfaces; +using Ical.Net.Collections.Proxies; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.General.Proxies +{ + public class CalendarObjectListProxy : GroupedCollectionProxy, ICalendarObjectList + where TType : class, ICalendarObject + { + public CalendarObjectListProxy(IGroupedCollection list) : base(list) {} + + public virtual TType this[int index] => this.Skip(index).FirstOrDefault(); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/Proxies/ParameterCollectionProxy.cs b/net-core/Ical.Net/Ical.Net/General/Proxies/ParameterCollectionProxy.cs new file mode 100644 index 00000000..c6850c45 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/Proxies/ParameterCollectionProxy.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.Linq; +using Ical.Net.Collections; +using Ical.Net.Collections.Interfaces; +using Ical.Net.Collections.Proxies; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.General.Proxies +{ + public class ParameterCollectionProxy : GroupedCollectionProxy, IParameterCollection + { + protected GroupedValueList Parameters + => RealObject as GroupedValueList; + + public ParameterCollectionProxy(IGroupedList realObject) : base(realObject) {} + + public virtual void SetParent(ICalendarObject parent) + { + foreach (var parameter in this) + { + parameter.Parent = parent; + } + } + + public virtual void Add(string name, string value) + { + RealObject.Add(new CalendarParameter(name, value)); + } + + public virtual string Get(string name) + { + var parameter = RealObject.FirstOrDefault(o => o.Name == name); + + return parameter?.Value; + } + + public virtual IList GetMany(string name) + { + return new GroupedValueListProxy(Parameters, name); + } + + public virtual void Set(string name, string value) + { + var parameter = RealObject.FirstOrDefault(o => o.Name == name); + + if (parameter == null) + { + RealObject.Add(new CalendarParameter(name, value)); + } + else + { + parameter.SetValue(value); + } + } + + public virtual void Set(string name, IEnumerable values) + { + var parameter = RealObject.FirstOrDefault(o => o.Name == name); + + if (parameter == null) + { + RealObject.Add(new CalendarParameter(name, values)); + } + else + { + parameter.SetValue(values); + } + } + + public virtual int IndexOf(CalendarParameter obj) + { + return 0; + } + + public virtual void Insert(int index, CalendarParameter item) {} + + public virtual void RemoveAt(int index) {} + + public virtual CalendarParameter this[int index] + { + get { return Parameters[index]; } + set { } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/Proxies/UniqueComponentListProxy.cs b/net-core/Ical.Net/Ical.Net/General/Proxies/UniqueComponentListProxy.cs new file mode 100644 index 00000000..e2dbc2fc --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/Proxies/UniqueComponentListProxy.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Ical.Net.Collections.Interfaces; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.General.Proxies +{ + public class UniqueComponentListProxy : CalendarObjectListProxy, IUniqueComponentList + where TComponentType : class, IUniqueComponent + { + private readonly Dictionary _lookup; + + public UniqueComponentListProxy(IGroupedCollection children) : base(children) + { + _lookup = new Dictionary(); + } + + private TComponentType Search(string uid) + { + if (_lookup.TryGetValue(uid, out var componentType)) + { + return componentType; + } + + var item = this.FirstOrDefault(c => string.Equals(c.Uid, uid, StringComparison.OrdinalIgnoreCase)); + + if (item == null) + { + return default(TComponentType); + } + + _lookup[uid] = item; + return item; + } + + public virtual TComponentType this[string uid] + { + get { return Search(uid); } + set + { + // Find the item matching the UID + var item = Search(uid); + + if (item != null) + { + Remove(item); + } + + if (value != null) + { + Add(value); + } + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/General/ServiceProvider.cs b/net-core/Ical.Net/Ical.Net/General/ServiceProvider.cs new file mode 100644 index 00000000..6a6d831d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/General/ServiceProvider.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using static System.Reflection.TypeExtensions; + +namespace Ical.Net.General +{ + public class ServiceProvider //: IServiceProvider + { + private readonly IDictionary _mTypedServices = new Dictionary(); + private readonly IDictionary _mNamedServices = new Dictionary(); + + public virtual object GetService(Type serviceType) + { + object service; + _mTypedServices.TryGetValue(serviceType, out service); + return service; + } + + public virtual object GetService(string name) + { + object service; + _mNamedServices.TryGetValue(name, out service); + return service; + } + + public virtual T GetService() + { + var service = GetService(typeof (T)); + if (service is T) + { + return (T) service; + } + return default(T); + } + + public virtual T GetService(string name) + { + var service = GetService(name); + if (service is T) + { + return (T) service; + } + return default(T); + } + + public virtual void SetService(string name, object obj) + { + if (!string.IsNullOrEmpty(name) && obj != null) + { + _mNamedServices[name] = obj; + } + } + + public virtual void SetService(object obj) + { + if (obj != null) + { + var type = obj.GetType(); + _mTypedServices[type] = obj; + + // Get interfaces for the given type + foreach (var iface in type.GetInterfaces()) + { + _mTypedServices[iface] = obj; + } + } + } + + public virtual void RemoveService(Type type) + { + if (type != null) + { + if (_mTypedServices.ContainsKey(type)) + { + _mTypedServices.Remove(type); + } + + // Get interfaces for the given type + foreach (var iface in type.GetInterfaces().Where(iface => _mTypedServices.ContainsKey(iface))) + { + _mTypedServices.Remove(iface); + } + } + } + + public virtual void RemoveService(string name) + { + if (_mNamedServices.ContainsKey(name)) + { + _mNamedServices.Remove(name); + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Ical.Net.csproj b/net-core/Ical.Net/Ical.Net/Ical.Net.csproj new file mode 100644 index 00000000..8b2a8a95 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Ical.Net.csproj @@ -0,0 +1,25 @@ + + + netstandard1.6 + + + library + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Components/IAlarmContainer.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Components/IAlarmContainer.cs new file mode 100644 index 00000000..0e960d44 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Components/IAlarmContainer.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.Interfaces.Components +{ + public interface IAlarmContainer + { + /// + /// A list of s for this recurring component. + /// + ICalendarObjectList Alarms { get; } + + /// + /// Polls s for occurrences within the d + /// time frame of this . For each evaluated + /// occurrence if this component, each is polled for its + /// corresponding alarm occurrences. + /// + /// The earliest allowable alarm occurrence to poll, or null. + /// + /// A List of objects, one for each occurrence of the . + IList PollAlarms(IDateTime startTime, IDateTime endTime); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Components/ICalendarComponent.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Components/ICalendarComponent.cs new file mode 100644 index 00000000..1f30bd74 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Components/ICalendarComponent.cs @@ -0,0 +1,6 @@ +using Ical.Net.Interfaces.General; + +namespace Ical.Net.Interfaces.Components +{ + public interface ICalendarComponent : ICalendarPropertyListContainer {} +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Components/IRecurringComponent.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Components/IRecurringComponent.cs new file mode 100644 index 00000000..385a7600 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Components/IRecurringComponent.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Evaluation; + +namespace Ical.Net.Interfaces.Components +{ + public interface IRecurringComponent : IUniqueComponent, IRecurrable + { + IList Attachments { get; set; } + IList Categories { get; set; } + string Class { get; set; } + IList Contacts { get; set; } + IDateTime Created { get; set; } + string Description { get; set; } + IDateTime LastModified { get; set; } + int Priority { get; set; } + IList RelatedComponents { get; set; } + int Sequence { get; set; } + string Summary { get; set; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Components/IUniqueComponent.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Components/IUniqueComponent.cs new file mode 100644 index 00000000..e0bdad93 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Components/IUniqueComponent.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net.Interfaces.Components +{ + public interface IUniqueComponent : ICalendarComponent + { + string Uid { get; set; } + + IList Attendees { get; set; } + IList Comments { get; set; } + IDateTime DtStamp { get; set; } + Organizer Organizer { get; set; } + IList RequestStatuses { get; set; } + Uri Url { get; set; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/DataTypes/ICalendarDataType.cs b/net-core/Ical.Net/Ical.Net/Interfaces/DataTypes/ICalendarDataType.cs new file mode 100644 index 00000000..3cdf7832 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/DataTypes/ICalendarDataType.cs @@ -0,0 +1,16 @@ +using System; +using Ical.Net.Interfaces.General; +using IServiceProvider = Ical.Net.Interfaces.General.IServiceProvider; + +namespace Ical.Net.Interfaces.DataTypes +{ + public interface ICalendarDataType : ICalendarParameterCollectionContainer, ICopyable, IServiceProvider + { + Type GetValueType(); + void SetValueType(string type); + ICalendarObject AssociatedObject { get; set; } + Calendar Calendar { get; } + + string Language { get; set; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/DataTypes/IDateTime.cs b/net-core/Ical.Net/Ical.Net/Interfaces/DataTypes/IDateTime.cs new file mode 100644 index 00000000..83948a3f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/DataTypes/IDateTime.cs @@ -0,0 +1,127 @@ +using System; + +namespace Ical.Net.Interfaces.DataTypes +{ + public interface IDateTime : IEncodableDataType, IComparable, IFormattable, ICalendarDataType + { + /// + /// Converts the date/time to this computer's local date/time. + /// + DateTime AsSystemLocal { get; } + + /// + /// Converts the date/time to UTC (Coordinated Universal Time) + /// + DateTime AsUtc { get; } + + /// + /// Gets/sets whether the Value of this date/time represents + /// a universal time. + /// + bool IsUniversalTime { get; set; } + + /// + /// Gets the time zone name this time is in, if it references a time zone. + /// + string TimeZoneName { get; } + + /// + /// Gets/sets the underlying DateTime value stored. This should always + /// use DateTimeKind.Utc, regardless of its actual representation. + /// Use IsUniversalTime along with the TZID to control how this + /// date/time is handled. + /// + DateTime Value { get; set; } + + /// + /// Gets/sets whether or not this date/time value contains a 'date' part. + /// + bool HasDate { get; set; } + + /// + /// Gets/sets whether or not this date/time value contains a 'time' part. + /// + bool HasTime { get; set; } + + /// + /// Gets/sets the time zone ID for this date/time value. + /// + string TzId { get; set; } + + /// + /// Gets the year for this date/time value. + /// + int Year { get; } + + /// + /// Gets the month for this date/time value. + /// + int Month { get; } + + /// + /// Gets the day for this date/time value. + /// + int Day { get; } + + /// + /// Gets the hour for this date/time value. + /// + int Hour { get; } + + /// + /// Gets the minute for this date/time value. + /// + int Minute { get; } + + /// + /// Gets the second for this date/time value. + /// + int Second { get; } + + /// + /// Gets the millisecond for this date/time value. + /// + int Millisecond { get; } + + /// + /// Gets the ticks for this date/time value. + /// + long Ticks { get; } + + /// + /// Gets the DayOfWeek for this date/time value. + /// + DayOfWeek DayOfWeek { get; } + + /// + /// Gets the date portion of the date/time value. + /// + DateTime Date { get; } + + /// + /// Converts the date/time value to a local time + /// within the specified time zone. + /// + IDateTime ToTimeZone(string newTimeZone); + + IDateTime Add(TimeSpan ts); + IDateTime Subtract(TimeSpan ts); + TimeSpan Subtract(IDateTime dt); + + IDateTime AddYears(int years); + IDateTime AddMonths(int months); + IDateTime AddDays(int days); + IDateTime AddHours(int hours); + IDateTime AddMinutes(int minutes); + IDateTime AddSeconds(int seconds); + IDateTime AddMilliseconds(int milliseconds); + IDateTime AddTicks(long ticks); + + bool LessThan(IDateTime dt); + bool GreaterThan(IDateTime dt); + bool LessThanOrEqual(IDateTime dt); + bool GreaterThanOrEqual(IDateTime dt); + + void AssociateWith(IDateTime dt); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/DataTypes/IEncodableDataType.cs b/net-core/Ical.Net/Ical.Net/Interfaces/DataTypes/IEncodableDataType.cs new file mode 100644 index 00000000..ef0ea132 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/DataTypes/IEncodableDataType.cs @@ -0,0 +1,7 @@ +namespace Ical.Net.Interfaces.DataTypes +{ + public interface IEncodableDataType + { + string Encoding { get; set; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IEvaluator.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IEvaluator.cs new file mode 100644 index 00000000..a7babdb1 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IEvaluator.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.Interfaces.Evaluation +{ + public interface IEvaluator + { + /// + /// The system calendar that governs the evaluation rules. + /// + System.Globalization.Calendar Calendar { get; } + + /// + /// The start bounds of the evaluation. This gives + /// the first date/time that is covered by the evaluation. + /// This together with EvaluationEndBounds determines + /// what time frames have already been evaluated, so + /// duplicate evaluation doesn't occur. + /// + DateTime EvaluationStartBounds { get; } + + /// + /// The end bounds of the evaluation. + /// See for more info. + /// + DateTime EvaluationEndBounds { get; } + + /// + /// Gets a list of periods collected so far during + /// the evaluation process. + /// + HashSet Periods { get; } + + /// + /// Gets the object associated with this evaluator. + /// + ICalendarObject AssociatedObject { get; } + + /// + /// Clears the evaluation, eliminating all data that has + /// been collected up to this point. Since this data is cached + /// as needed, this method can be useful to gather information + /// that is guaranteed to not be out-of-date. + /// + void Clear(); + + /// + /// Evaluates this item to determine the dates and times for which it occurs/recurs. + /// This method only evaluates items which occur/recur between + /// and ; therefore, if you require a list of items which + /// occur outside of this range, you must specify a and + /// which encapsulate the date(s) of interest. + /// This method evaluates using the as the beginning + /// point. For example, for a WEEKLY occurrence, the + /// determines the day of week that this item will recur on. + /// + /// For events with very complex recurrence rules, this method may be a bottleneck + /// during processing time, especially when this method is called for a large number + /// of items, in sequence, or for a very large time span. + /// + /// + /// + /// + /// + /// + /// + /// A list of objects for + /// each date/time when this item occurs/recurs. + /// + HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IGetFreeBusy.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IGetFreeBusy.cs new file mode 100644 index 00000000..1a565281 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IGetFreeBusy.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net.Interfaces.Evaluation +{ + public interface IGetFreeBusy + { + FreeBusy GetFreeBusy(FreeBusy freeBusyRequest); + FreeBusy GetFreeBusy(IDateTime fromInclusive, IDateTime toExclusive); + FreeBusy GetFreeBusy(Organizer organizer, IEnumerable contacts, IDateTime fromInclusive, IDateTime toExclusive); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IGetOccurrences.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IGetOccurrences.cs new file mode 100644 index 00000000..f9a4bc63 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IGetOccurrences.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net.Interfaces.Evaluation +{ + public interface IGetOccurrences + { + /// + /// Clears a previous evaluation, usually because one of the + /// key elements used for evaluation has changed + /// (Start, End, Duration, recurrence rules, exceptions, etc.). + /// + void ClearEvaluation(); + + /// + /// Returns all occurrences of this component that start on the date provided. + /// All components starting between 12:00:00AM and 11:59:59 PM will be + /// returned. + /// + /// This will first Evaluate() the date range required in order to + /// determine the occurrences for the date provided, and then return + /// the occurrences. + /// + /// + /// The date for which to return occurrences. + /// A list of Periods representing the occurrences of this object. + HashSet GetOccurrences(IDateTime dt); + + HashSet GetOccurrences(DateTime dt); + + /// + /// Returns all occurrences of this component that overlap with the date range provided. + /// All components that overlap with the time range between and will be returned. + /// + /// The starting date range + /// The ending date range + HashSet GetOccurrences(IDateTime startTime, IDateTime endTime); + + HashSet GetOccurrences(DateTime startTime, DateTime endTime); + } + + public interface IGetOccurrencesTyped : IGetOccurrences + { + /// + /// Returns all occurrences of components of type T that start on the date provided. + /// All components starting between 12:00:00AM and 11:59:59 PM will be + /// returned. + /// + /// This will first Evaluate() the date range required in order to + /// determine the occurrences for the date provided, and then return + /// the occurrences. + /// + /// + /// The date for which to return occurrences. + /// A list of Periods representing the occurrences of this object. + HashSet GetOccurrences(IDateTime dt) where T : IRecurringComponent; + + HashSet GetOccurrences(DateTime dt) where T : IRecurringComponent; + + /// + /// Returns all occurrences of components of type T that start within the date range provided. + /// All components occurring between and + /// will be returned. + /// + /// The starting date range + /// The ending date range + HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) where T : IRecurringComponent; + + HashSet GetOccurrences(DateTime startTime, DateTime endTime) where T : IRecurringComponent; + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IRecurrable.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IRecurrable.cs new file mode 100644 index 00000000..5e4aa7e9 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Evaluation/IRecurrable.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using IServiceProvider = Ical.Net.Interfaces.General.IServiceProvider; + +namespace Ical.Net.Interfaces.Evaluation +{ + public interface IRecurrable : IGetOccurrences, IServiceProvider + { + /// + /// Gets/sets the start date/time of the component. + /// + IDateTime Start { get; set; } + + IList ExceptionDates { get; set; } + IList ExceptionRules { get; set; } + IList RecurrenceDates { get; set; } + IList RecurrenceRules { get; set; } + IDateTime RecurrenceId { get; set; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarObject.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarObject.cs new file mode 100644 index 00000000..4a2ebdea --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarObject.cs @@ -0,0 +1,42 @@ +using Ical.Net.Collections.Interfaces; + +namespace Ical.Net.Interfaces.General +{ + public interface ICalendarObject : IGroupedObject, ILoadable, ICopyable, IServiceProvider + { + /// + /// The name of the calendar object. + /// Every calendar object can be assigned + /// a name. + /// + string Name { get; set; } + + /// + /// Returns the parent of this object. + /// + ICalendarObject Parent { get; set; } + + /// + /// Returns a collection of children of this object. + /// + ICalendarObjectList Children { get; } + + /// + /// Returns the iCalendar that this object + /// is associated with. + /// + Calendar Calendar { get; } + + /// + /// Returns the line number where this calendar + /// object was found during parsing. + /// + int Line { get; set; } + + /// + /// Returns the column number where this calendar + /// object was found during parsing. + /// + int Column { get; set; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarObjectList.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarObjectList.cs new file mode 100644 index 00000000..17232692 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarObjectList.cs @@ -0,0 +1,9 @@ +using Ical.Net.Collections.Interfaces; + +namespace Ical.Net.Interfaces.General +{ + public interface ICalendarObjectList : IGroupedCollection where TType : class, ICalendarObject + { + TType this[int index] { get; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarParameterCollectionContainer.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarParameterCollectionContainer.cs new file mode 100644 index 00000000..78b50187 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarParameterCollectionContainer.cs @@ -0,0 +1,7 @@ +namespace Ical.Net.Interfaces.General +{ + public interface ICalendarParameterCollectionContainer + { + IParameterCollection Parameters { get; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarProperty.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarProperty.cs new file mode 100644 index 00000000..63481d7a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarProperty.cs @@ -0,0 +1,9 @@ +using Ical.Net.Collections.Interfaces; + +namespace Ical.Net.Interfaces.General +{ + public interface ICalendarProperty : ICalendarParameterCollectionContainer, ICalendarObject, IValueObject + { + object Value { get; set; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarPropertyListContainer.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarPropertyListContainer.cs new file mode 100644 index 00000000..ef90fe56 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICalendarPropertyListContainer.cs @@ -0,0 +1,9 @@ +using Ical.Net.General; + +namespace Ical.Net.Interfaces.General +{ + public interface ICalendarPropertyListContainer : ICalendarObject + { + CalendarPropertyList Properties { get; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/ICopyable.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICopyable.cs new file mode 100644 index 00000000..facbcd3d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/ICopyable.cs @@ -0,0 +1,19 @@ +namespace Ical.Net.Interfaces.General +{ + public interface ICopyable + { + /// + /// Copies all relevant fields/properties from + /// the target object to the current one. + /// + void CopyFrom(ICopyable obj); + + /// + /// Returns a deep copy of the current object. For the most part, this is only necessary when working with mutable reference types, + /// (i.e. iCalDateTime). For most other types, it's unnecessary overhead. The pattern that identifies whether it's necessary to copy + /// or not is whether arithmetic operations mutate fields or properties. iCalDateTime is a good example where + and - would otherwise + /// change the Value of the underlying DateTime. + /// + T Copy(); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/ILoadable.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/ILoadable.cs new file mode 100644 index 00000000..cd6e1483 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/ILoadable.cs @@ -0,0 +1,22 @@ +using System; + +namespace Ical.Net.Interfaces.General +{ + public interface ILoadable + { + /// + /// Gets whether or not the object has been loaded. + /// + bool IsLoaded { get; } + + /// + /// An event that fires when the object has been loaded. + /// + event EventHandler Loaded; + + /// + /// Fires the Loaded event. + /// + void OnLoaded(); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/IMergeable.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/IMergeable.cs new file mode 100644 index 00000000..44c8388e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/IMergeable.cs @@ -0,0 +1,10 @@ +namespace Ical.Net.Interfaces.General +{ + public interface IMergeable + { + /// + /// Merges this object with another. + /// + void MergeWith(IMergeable obj); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/IParameterCollection.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/IParameterCollection.cs new file mode 100644 index 00000000..007091b2 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/IParameterCollection.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Ical.Net.Collections.Interfaces; +using Ical.Net.General; + +namespace Ical.Net.Interfaces.General +{ + public interface IParameterCollection : IGroupedList + { + void SetParent(ICalendarObject parent); + void Add(string name, string value); + string Get(string name); + IList GetMany(string name); + void Set(string name, string value); + void Set(string name, IEnumerable values); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/IServiceProvider.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/IServiceProvider.cs new file mode 100644 index 00000000..77a9820d --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/IServiceProvider.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ical.Net.Interfaces.General +{ + public interface IServiceProvider + { + object GetService(string name); + object GetService(Type type); + T GetService(); + T GetService(string name); + void SetService(string name, object obj); + void SetService(object obj); + void RemoveService(Type type); + void RemoveService(string name); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/General/IUniqueComponentList.cs b/net-core/Ical.Net/Ical.Net/Interfaces/General/IUniqueComponentList.cs new file mode 100644 index 00000000..282968af --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/General/IUniqueComponentList.cs @@ -0,0 +1,9 @@ +using Ical.Net.Interfaces.Components; + +namespace Ical.Net.Interfaces.General +{ + public interface IUniqueComponentList : ICalendarObjectList where TComponentType : class, IUniqueComponent + { + TComponentType this[string uid] { get; set; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/Factory/ICalendarComponentFactory.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/Factory/ICalendarComponentFactory.cs new file mode 100644 index 00000000..e07e6aa9 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/Factory/ICalendarComponentFactory.cs @@ -0,0 +1,9 @@ +using Ical.Net.Interfaces.Components; + +namespace Ical.Net.Interfaces.Serialization.Factory +{ + public interface ICalendarComponentFactory + { + ICalendarComponent Build(string objectName); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/Factory/ISerializerFactory.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/Factory/ISerializerFactory.cs new file mode 100644 index 00000000..d9f3bd39 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/Factory/ISerializerFactory.cs @@ -0,0 +1,10 @@ +using System; +using Ical.Net.Serialization; + +namespace Ical.Net.Interfaces.Serialization.Factory +{ + public interface ISerializerFactory + { + ISerializer Build(Type objectType, SerializationContext ctx); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/IDataTypeMapper.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/IDataTypeMapper.cs new file mode 100644 index 00000000..903051a0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/IDataTypeMapper.cs @@ -0,0 +1,15 @@ +using System; +using Ical.Net.Serialization; + +namespace Ical.Net.Interfaces.Serialization +{ + internal interface IDataTypeMapper + { + void AddPropertyMapping(string name, Type objectType, bool allowsMultipleValuesPerProperty); + void AddPropertyMapping(string name, TypeResolverDelegate resolver, bool allowsMultipleValuesPerProperty); + void RemovePropertyMapping(string name); + + bool GetPropertyAllowsMultipleValues(object obj); + Type GetPropertyMapping(object obj); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/IEncodingProvider.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/IEncodingProvider.cs new file mode 100644 index 00000000..75bc36b5 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/IEncodingProvider.cs @@ -0,0 +1,9 @@ +namespace Ical.Net.Interfaces.Serialization +{ + internal interface IEncodingProvider + { + string Encode(string encoding, byte[] data); + string DecodeString(string encoding, string value); + byte[] DecodeData(string encoding, string value); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/ISerializationSettings.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/ISerializationSettings.cs new file mode 100644 index 00000000..39e8a336 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/ISerializationSettings.cs @@ -0,0 +1,18 @@ +using System; + +namespace Ical.Net.Interfaces.Serialization +{ + internal interface ISerializationSettings + { + Type CalendarType { get; set; } + bool EnsureAccurateLineNumbers { get; set; } + ParsingModeType ParsingMode { get; set; } + bool StoreExtraSerializationData { get; set; } + } + + internal enum ParsingModeType + { + Strict, + Loose + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/ISerializer.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/ISerializer.cs new file mode 100644 index 00000000..f27869c5 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/ISerializer.cs @@ -0,0 +1,17 @@ +using System; +using System.IO; +using System.Text; +using Ical.Net.Serialization; +using IServiceProvider = Ical.Net.Interfaces.General.IServiceProvider; + +namespace Ical.Net.Interfaces.Serialization +{ + public interface ISerializer : IServiceProvider + { + SerializationContext SerializationContext { get; set; } + + Type TargetType { get; } + void Serialize(object obj, Stream stream, Encoding encoding); + object Deserialize(Stream stream, Encoding encoding); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/IStringSerializer.cs b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/IStringSerializer.cs new file mode 100644 index 00000000..30fa762e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Interfaces/Serialization/IStringSerializer.cs @@ -0,0 +1,10 @@ +using System.IO; + +namespace Ical.Net.Interfaces.Serialization +{ + public interface IStringSerializer : ISerializer + { + string SerializeToString(object obj); + object Deserialize(TextReader tr); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Properties/AssemblyInfo.cs b/net-core/Ical.Net/Ical.Net/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..8df6422a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Ical.Net.UnitTests")] diff --git a/net-core/Ical.Net/Ical.Net/Serialization/DataTypeMapper.cs b/net-core/Ical.Net/Ical.Net/Serialization/DataTypeMapper.cs new file mode 100644 index 00000000..55448239 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/DataTypeMapper.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Interfaces.Serialization; + +namespace Ical.Net.Serialization +{ + public delegate Type TypeResolverDelegate(object context); + + internal class DataTypeMapper : IDataTypeMapper + { + private class PropertyMapping + { + public Type ObjectType { get; set; } + public TypeResolverDelegate Resolver { get; set; } + public bool AllowsMultipleValuesPerProperty { get; set; } + } + + private readonly IDictionary _propertyMap = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public DataTypeMapper() + { + AddPropertyMapping("ACTION", typeof (AlarmAction), false); + AddPropertyMapping("ATTACH", typeof (Attachment), false); + AddPropertyMapping("ATTENDEE", typeof (Attendee), false); + AddPropertyMapping("CATEGORIES", typeof (string), true); + AddPropertyMapping("COMMENT", typeof (string), false); + AddPropertyMapping("COMPLETED", typeof (IDateTime), false); + AddPropertyMapping("CONTACT", typeof (string), false); + AddPropertyMapping("CREATED", typeof (IDateTime), false); + AddPropertyMapping("DTEND", typeof (IDateTime), false); + AddPropertyMapping("DTSTAMP", typeof (IDateTime), false); + AddPropertyMapping("DTSTART", typeof (IDateTime), false); + AddPropertyMapping("DUE", typeof (IDateTime), false); + AddPropertyMapping("DURATION", typeof (TimeSpan), false); + AddPropertyMapping("EXDATE", typeof (PeriodList), false); + AddPropertyMapping("EXRULE", typeof (RecurrencePattern), false); + AddPropertyMapping("FREEBUSY", typeof (FreeBusyEntry), true); + AddPropertyMapping("GEO", typeof (GeographicLocation), false); + AddPropertyMapping("LAST-MODIFIED", typeof (IDateTime), false); + AddPropertyMapping("ORGANIZER", typeof (Organizer), false); + AddPropertyMapping("PERCENT-COMPLETE", typeof (int), false); + AddPropertyMapping("PRIORITY", typeof (int), false); + AddPropertyMapping("RDATE", typeof (PeriodList), false); + AddPropertyMapping("RECURRENCE-ID", typeof (IDateTime), false); + AddPropertyMapping("RELATED-TO", typeof (string), false); + AddPropertyMapping("REQUEST-STATUS", typeof (RequestStatus), false); + AddPropertyMapping("REPEAT", typeof (int), false); + AddPropertyMapping("RESOURCES", typeof (string), true); + AddPropertyMapping("RRULE", typeof (RecurrencePattern), false); + AddPropertyMapping("SEQUENCE", typeof (int), false); + AddPropertyMapping("STATUS", ResolveStatusProperty, false); + AddPropertyMapping("TRANSP", typeof (TransparencyType), false); + AddPropertyMapping("TRIGGER", typeof (Trigger), false); + AddPropertyMapping("TZNAME", typeof (string), false); + AddPropertyMapping("TZOFFSETFROM", typeof (UtcOffset), false); + AddPropertyMapping("TZOFFSETTO", typeof (UtcOffset), false); + AddPropertyMapping("TZURL", typeof (Uri), false); + AddPropertyMapping("URL", typeof (Uri), false); + } + + protected Type ResolveStatusProperty(object context) + { + var obj = context as ICalendarObject; + if (obj != null) + { + if (obj.Parent is CalendarEvent) + { + return typeof (EventStatus); + } + if (obj.Parent is Todo) + { + return typeof (TodoStatus); + } + if (obj.Parent is Journal) + { + return typeof (JournalStatus); + } + } + + return null; + } + + public void AddPropertyMapping(string name, Type objectType, bool allowsMultipleValues) + { + if (name != null && objectType != null) + { + var m = new PropertyMapping + { + ObjectType = objectType, + AllowsMultipleValuesPerProperty = allowsMultipleValues + }; + + _propertyMap[name] = m; + } + } + + public void AddPropertyMapping(string name, TypeResolverDelegate resolver, bool allowsMultipleValues) + { + if (name != null && resolver != null) + { + var m = new PropertyMapping + { + Resolver = resolver, + AllowsMultipleValuesPerProperty = allowsMultipleValues + }; + + _propertyMap[name] = m; + } + } + + public void RemovePropertyMapping(string name) + { + if (name != null && _propertyMap.ContainsKey(name)) + { + _propertyMap.Remove(name); + } + } + + public virtual bool GetPropertyAllowsMultipleValues(object obj) + { + var p = obj as ICalendarProperty; + PropertyMapping m; + return p?.Name != null && _propertyMap.TryGetValue(p.Name, out m) && m.AllowsMultipleValuesPerProperty; + } + + public virtual Type GetPropertyMapping(object obj) + { + var p = obj as ICalendarProperty; + if (p?.Name == null) + { + return null; + } + + PropertyMapping m; + if (!_propertyMap.TryGetValue(p.Name, out m)) + { + return null; + } + + return m.Resolver == null + ? m.ObjectType + : m.Resolver(p); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/EncodingProvider.cs b/net-core/Ical.Net/Ical.Net/Serialization/EncodingProvider.cs new file mode 100644 index 00000000..78d2153a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/EncodingProvider.cs @@ -0,0 +1,178 @@ +using System; +using System.Text; +using Ical.Net.Interfaces.Serialization; + +namespace Ical.Net.Serialization +{ + internal class EncodingProvider : IEncodingProvider + { + public delegate string EncoderDelegate(byte[] data); + + public delegate byte[] DecoderDelegate(string value); + + private readonly SerializationContext _mSerializationContext; + + public EncodingProvider(SerializationContext ctx) + { + _mSerializationContext = ctx; + } + + protected byte[] Decode7Bit(string value) + { + try + { + var utf7 = new UTF7Encoding(); + return utf7.GetBytes(value); + } + catch + { + return null; + } + } + + protected byte[] Decode8Bit(string value) + { + try + { + var utf8 = new UTF8Encoding(); + return utf8.GetBytes(value); + } + catch + { + return null; + } + } + + protected byte[] DecodeBase64(string value) + { + try + { + return Convert.FromBase64String(value); + } + catch + { + return null; + } + } + + protected virtual DecoderDelegate GetDecoderFor(string encoding) + { + if (encoding == null) + { + return null; + } + + switch (encoding.ToUpper()) + { + case "7BIT": + return Decode7Bit; + case "8BIT": + return Decode8Bit; + case "BASE64": + return DecodeBase64; + default: + return null; + } + } + + protected string Encode7Bit(byte[] data) + { + try + { + var utf7 = new UTF7Encoding(); + return utf7.GetString(data); + } + catch + { + return null; + } + } + + protected string Encode8Bit(byte[] data) + { + try + { + var utf8 = new UTF8Encoding(); + return utf8.GetString(data); + } + catch + { + return null; + } + } + + protected string EncodeBase64(byte[] data) + { + try + { + return Convert.ToBase64String(data); + } + catch + { + return null; + } + } + + protected virtual EncoderDelegate GetEncoderFor(string encoding) + { + if (encoding == null) + { + return null; + } + + switch (encoding.ToUpper()) + { + case "7BIT": + return Encode7Bit; + case "8BIT": + return Encode8Bit; + case "BASE64": + return EncodeBase64; + default: + return null; + } + } + + public string Encode(string encoding, byte[] data) + { + if (encoding == null || data == null) + { + return null; + } + + var encoder = GetEncoderFor(encoding); + //var wrapped = TextUtil.FoldLines(encoder?.Invoke(data)); + //return wrapped; + return encoder?.Invoke(data); + } + + public string DecodeString(string encoding, string value) + { + if (encoding == null || value == null) + { + return null; + } + + var data = DecodeData(encoding, value); + if (data == null) + { + return null; + } + + // Decode the string into the current encoding + var encodingStack = _mSerializationContext.GetService(typeof (EncodingStack)) as EncodingStack; + return encodingStack.Current.GetString(data); + } + + public byte[] DecodeData(string encoding, string value) + { + if (encoding == null || value == null) + { + return null; + } + + var decoder = GetDecoderFor(encoding); + return decoder?.Invoke(value); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/EncodingStack.cs b/net-core/Ical.Net/Ical.Net/Serialization/EncodingStack.cs new file mode 100644 index 00000000..f4507061 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/EncodingStack.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Text; + +namespace Ical.Net.Serialization +{ + internal class EncodingStack + { + private readonly Stack _mStack; + + public EncodingStack() + { + _mStack = new Stack(); + } + + public Encoding Current + { + get + { + return _mStack.Count > 0 + ? _mStack.Peek() + : Encoding.UTF8; + } + } + + public void Push(Encoding encoding) + { + if (encoding != null) + { + _mStack.Push(encoding); + } + } + + public Encoding Pop() + { + return _mStack.Count > 0 + ? _mStack.Pop() + : null; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/Factory/ComponentFactory.cs b/net-core/Ical.Net/Ical.Net/Serialization/Factory/ComponentFactory.cs new file mode 100644 index 00000000..6db5b74c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/Factory/ComponentFactory.cs @@ -0,0 +1,41 @@ +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.Serialization.Factory; + +namespace Ical.Net.Serialization.Factory +{ + public class ComponentFactory : ICalendarComponentFactory + { + public virtual ICalendarComponent Build(string objectName) + { + ICalendarComponent c = null; + var name = objectName.ToUpper(); + + switch (name) + { + case Components.Alarm: + c = new Alarm(); + break; + case Components.Event: + c = new CalendarEvent(); + break; + case Components.Freebusy: + c = new FreeBusy(); + break; + case Components.Journal: + c = new Journal(); + break; + case Components.Timezone: + c = new VTimeZone(); + break; + case Components.Todo: + c = new Todo(); + break; + default: + c = new CalendarComponent(); + break; + } + c.Name = name; + return c; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/SerializationContext.cs b/net-core/Ical.Net/Ical.Net/Serialization/SerializationContext.cs new file mode 100644 index 00000000..71745f02 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/SerializationContext.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using Ical.Net.General; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.Factory; +using Ical.Net.Serialization.iCalendar.Factory; +using Ical.Net.Serialization.iCalendar.Processors; + +namespace Ical.Net.Serialization +{ + public class SerializationContext + { + private static SerializationContext _default; + + /// + /// Gets the Singleton instance of the SerializationContext class. + /// + public static SerializationContext Default + { + get + { + if (_default == null) + { + _default = new SerializationContext(); + } + + // Create a new serialization context that doesn't contain any objects + // (and is non-static). That way, if any objects get pushed onto + // the serialization stack when the Default serialization context is used, + // and something goes wrong and the objects don't get popped off the stack, + // we don't need to worry (as much) about a memory leak, because the + // objects weren't pushed onto a stack referenced by a static variable. + var ctx = new SerializationContext + { + _mServiceProvider = _default._mServiceProvider + }; + return ctx; + } + } + + private readonly Stack _mStack = new Stack(); + private ServiceProvider _mServiceProvider = new ServiceProvider(); + + public SerializationContext() + { + // Add some services by default + SetService(new SerializationSettings()); + SetService(new SerializerFactory()); + SetService(new ComponentFactory()); + SetService(new DataTypeMapper()); + SetService(new EncodingStack()); + SetService(new EncodingProvider(this)); + SetService(new CompositeProcessor()); + SetService(new CompositeProcessor()); + SetService(new CompositeProcessor()); + } + + public virtual void Push(object item) + { + if (item != null) + { + _mStack.Push(new WeakReference(item)); + } + } + + public virtual object Pop() + { + if (_mStack.Count > 0) + { + var r = _mStack.Pop(); + if (r.IsAlive) + { + return r.Target; + } + } + return null; + } + + public virtual object Peek() + { + if (_mStack.Count > 0) + { + var r = _mStack.Peek(); + if (r.IsAlive) + { + return r.Target; + } + } + return null; + } + + public virtual object GetService(Type serviceType) + { + return _mServiceProvider.GetService(serviceType); + } + + public virtual object GetService(string name) + { + return _mServiceProvider.GetService(name); + } + + public virtual T GetService() + { + return _mServiceProvider.GetService(); + } + + public virtual T GetService(string name) + { + return _mServiceProvider.GetService(name); + } + + public virtual void SetService(string name, object obj) + { + _mServiceProvider.SetService(name, obj); + } + + public virtual void SetService(object obj) + { + _mServiceProvider.SetService(obj); + } + + public virtual void RemoveService(Type type) + { + _mServiceProvider.RemoveService(type); + } + + public virtual void RemoveService(string name) + { + _mServiceProvider.RemoveService(name); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/SerializationSettings.cs b/net-core/Ical.Net/Ical.Net/Serialization/SerializationSettings.cs new file mode 100644 index 00000000..d3a622b5 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/SerializationSettings.cs @@ -0,0 +1,16 @@ +using System; +using Ical.Net.Interfaces.Serialization; + +namespace Ical.Net.Serialization +{ + internal class SerializationSettings : ISerializationSettings + { + public virtual Type CalendarType { get; set; } = typeof (Calendar); + + public virtual bool EnsureAccurateLineNumbers { get; set; } + + public virtual ParsingModeType ParsingMode { get; set; } = ParsingModeType.Strict; + + public virtual bool StoreExtraSerializationData { get; set; } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Factory/DataTypeSerializerFactory.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Factory/DataTypeSerializerFactory.cs new file mode 100644 index 00000000..cbe3532a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Factory/DataTypeSerializerFactory.cs @@ -0,0 +1,100 @@ +using System; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Interfaces.Serialization.Factory; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; +using Ical.Net.Serialization.iCalendar.Serializers.Other; +using static System.Reflection.TypeExtensions; + +namespace Ical.Net.Serialization.iCalendar.Factory +{ + public class DataTypeSerializerFactory : ISerializerFactory + { + /// + /// Returns a serializer that can be used to serialize and object + /// of type . + /// + /// TODO: Add support for caching. + /// + /// + /// The type of object to be serialized. + /// The serialization context. + public virtual ISerializer Build(Type objectType, SerializationContext ctx) + { + if (objectType != null) + { + ISerializer s; + + if (typeof (Attachment).IsAssignableFrom(objectType)) + { + s = new AttachmentSerializer(); + } + else if (typeof (Attendee).IsAssignableFrom(objectType)) + { + s = new AttendeeSerializer(); + } + else if (typeof (IDateTime).IsAssignableFrom(objectType)) + { + s = new DateTimeSerializer(); + } + else if (typeof (FreeBusyEntry).IsAssignableFrom(objectType)) + { + s = new FreeBusyEntrySerializer(); + } + else if (typeof (GeographicLocation).IsAssignableFrom(objectType)) + { + s = new GeographicLocationSerializer(); + } + else if (typeof (Organizer).IsAssignableFrom(objectType)) + { + s = new OrganizerSerializer(); + } + else if (typeof (Period).IsAssignableFrom(objectType)) + { + s = new PeriodSerializer(); + } + else if (typeof (PeriodList).IsAssignableFrom(objectType)) + { + s = new PeriodListSerializer(); + } + else if (typeof (RecurrencePattern).IsAssignableFrom(objectType)) + { + s = new RecurrencePatternSerializer(); + } + else if (typeof (RequestStatus).IsAssignableFrom(objectType)) + { + s = new RequestStatusSerializer(); + } + else if (typeof (StatusCode).IsAssignableFrom(objectType)) + { + s = new StatusCodeSerializer(); + } + else if (typeof (Trigger).IsAssignableFrom(objectType)) + { + s = new TriggerSerializer(); + } + else if (typeof (UtcOffset).IsAssignableFrom(objectType)) + { + s = new UtcOffsetSerializer(); + } + else if (typeof (WeekDay).IsAssignableFrom(objectType)) + { + s = new WeekDaySerializer(); + } + // Default to a string serializer, which simply calls + // ToString() on the value to serialize it. + else + { + s = new StringSerializer(); + } + + // Set the serialization context + s.SerializationContext = ctx; + + return s; + } + return null; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Factory/SerializerFactory.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Factory/SerializerFactory.cs new file mode 100644 index 00000000..2abf398f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Factory/SerializerFactory.cs @@ -0,0 +1,106 @@ +using System; +using System.Reflection; +using Ical.Net.General; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Interfaces.Serialization.Factory; +using Ical.Net.Serialization.iCalendar.Serializers; +using Ical.Net.Serialization.iCalendar.Serializers.Components; +using Ical.Net.Serialization.iCalendar.Serializers.Other; + +namespace Ical.Net.Serialization.iCalendar.Factory +{ + public class SerializerFactory : ISerializerFactory + { + private readonly ISerializerFactory _mDataTypeSerializerFactory; + + public SerializerFactory() + { + _mDataTypeSerializerFactory = new DataTypeSerializerFactory(); + } + + /// + /// Returns a serializer that can be used to serialize and object + /// of type . + /// + /// TODO: Add support for caching. + /// + /// + /// The type of object to be serialized. + /// The serialization context. + public virtual ISerializer Build(Type objectType, SerializationContext ctx) + { + if (objectType != null) + { + ISerializer s; + + if (typeof (Calendar).IsAssignableFrom(objectType)) + { + s = new CalendarSerializer(); + } + else if (typeof (ICalendarComponent).IsAssignableFrom(objectType)) + { + s = typeof (CalendarEvent).IsAssignableFrom(objectType) + ? new EventSerializer() + : new ComponentSerializer(); + } + else if (typeof (ICalendarProperty).IsAssignableFrom(objectType)) + { + s = new PropertySerializer(); + } + else if (typeof (CalendarParameter).IsAssignableFrom(objectType)) + { + s = new ParameterSerializer(); + } + else if (typeof (string).IsAssignableFrom(objectType)) + { + s = new StringSerializer(); + } +#if NET_4 + else if (objectType.IsEnum) + { + s = new EnumSerializer(objectType); + } +#else + else if (objectType.GetTypeInfo().IsEnum) + { + s = new EnumSerializer(objectType); + } +#endif + else if (typeof (TimeSpan).IsAssignableFrom(objectType)) + { + s = new TimeSpanSerializer(); + } + else if (typeof (int).IsAssignableFrom(objectType)) + { + s = new IntegerSerializer(); + } + else if (typeof (Uri).IsAssignableFrom(objectType)) + { + s = new UriSerializer(); + } + else if (typeof (ICalendarDataType).IsAssignableFrom(objectType)) + { + s = _mDataTypeSerializerFactory.Build(objectType, ctx); + } + // Default to a string serializer, which simply calls + // ToString() on the value to serialize it. + else + { + s = new StringSerializer(); + } + + // Set the serialization context + if (s != null) + { + s.SerializationContext = ctx; + } + + return s; + } + return null; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Processors/CompositeProcessor.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Processors/CompositeProcessor.cs new file mode 100644 index 00000000..a2df35a8 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Processors/CompositeProcessor.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; + +namespace Ical.Net.Serialization.iCalendar.Processors +{ + public class CompositeProcessor : List> + { + public CompositeProcessor() {} + + public CompositeProcessor(IEnumerable> processors) + { + AddRange(processors); + } + + public virtual void PreSerialization(T obj) + { + foreach (var p in this) + { + p.PreSerialization(obj); + } + } + + public virtual void PostSerialization(T obj) + { + foreach (var p in this) + { + p.PostSerialization(obj); + } + } + + public virtual void PreDeserialization(T obj) + { + foreach (var p in this) + { + p.PreDeserialization(obj); + } + } + + public virtual void PostDeserialization(T obj) + { + foreach (var p in this) + { + p.PostDeserialization(obj); + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/CalendarSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/CalendarSerializer.cs new file mode 100644 index 00000000..0280dd08 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/CalendarSerializer.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.Components; +using Ical.Net.Utility; + +namespace Ical.Net.Serialization.iCalendar.Serializers +{ + public class CalendarSerializer : ComponentSerializer + { + private readonly Calendar _calendar; + + public CalendarSerializer() + :this(new SerializationContext()) { } + + public CalendarSerializer(Calendar cal) + { + _calendar = cal; + } + + public CalendarSerializer(SerializationContext ctx) : base(ctx) {} + + public virtual string SerializeToString() => SerializeToString(_calendar); + + protected override IComparer PropertySorter => new CalendarPropertySorter(); + + public override string SerializeToString(object obj) + { + var iCal = obj as Calendar; + + // Ensure VERSION and PRODUCTID are both set, + // as they are required by RFC5545. + if (string.IsNullOrWhiteSpace(iCal.Version)) + { + iCal.Version = CalendarVersions.Latest; + } + if (string.IsNullOrWhiteSpace(iCal.ProductId)) + { + iCal.ProductId = CalendarProductIDs.Default; + } + + return base.SerializeToString(iCal); + } + + public override object Deserialize(TextReader tr) + { + if (tr == null) + { + return null; + } + + using (tr) + { + var ctx = SerializationContext; + var contents = tr.ReadToEnd(); + + using (var normalized = TextUtil.Normalize(contents, ctx)) + { + var lexer = new IcalLexer(normalized); + var parser = new IcalParser(lexer); + + // Parse the iCalendar(s)! + var iCalendars = parser.icalendar(SerializationContext); + + // Return the parsed iCalendar(s) + return iCalendars; + } + } + } + + private class CalendarPropertySorter : IComparer + { + public int Compare(ICalendarProperty x, ICalendarProperty y) + { + if (x == y || (x == null && y == null)) + { + return 0; + } + if (x == null) + { + return -1; + } + if (y == null) + { + return 1; + } + // Alphabetize all properties except VERSION, which should appear first. + if (string.Equals("VERSION", x.Name, StringComparison.OrdinalIgnoreCase)) + { + return -1; + } + if (string.Equals("VERSION", y.Name, StringComparison.OrdinalIgnoreCase)) + { + return 1; + } + return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Components/ComponentSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Components/ComponentSerializer.cs new file mode 100644 index 00000000..d14b5e44 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Components/ComponentSerializer.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Ical.Net.Interfaces.Components; +using Ical.Net.Interfaces.General; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Interfaces.Serialization.Factory; +using Ical.Net.Utility; + +namespace Ical.Net.Serialization.iCalendar.Serializers.Components +{ + public class ComponentSerializer : SerializerBase + { + protected virtual IComparer PropertySorter => new PropertyAlphabetizer(); + + public ComponentSerializer() { } + + public ComponentSerializer(SerializationContext ctx) : base(ctx) { } + + public override Type TargetType => typeof(CalendarComponent); + + public override string SerializeToString(object obj) + { + var c = obj as ICalendarComponent; + if (c == null) + { + return null; + } + + var sb = new StringBuilder(512); + var upperName = c.Name.ToUpperInvariant(); + sb.Append(TextUtil.FoldLines($"BEGIN:{upperName}")); + + // Get a serializer factory + var sf = GetService(); + + // Sort the calendar properties in alphabetical order before serializing them! + var properties = c.Properties.OrderBy(p => p.Name).ToList(); + + // Serialize properties + foreach (var p in properties) + { + // Get a serializer for each property. + var serializer = sf.Build(p.GetType(), SerializationContext) as IStringSerializer; + sb.Append(serializer.SerializeToString(p)); + } + + // Serialize child objects + foreach (var child in c.Children) + { + // Get a serializer for each child object. + var serializer = sf.Build(child.GetType(), SerializationContext) as IStringSerializer; + sb.Append(serializer.SerializeToString(child)); + } + + sb.Append(TextUtil.FoldLines($"END:{upperName}")); + return sb.ToString(); + } + + public override object Deserialize(TextReader tr) + { + if (tr == null) + { + return null; + } + + using (tr) + { + var ctx = SerializationContext; + var contents = tr.ReadToEnd(); + + using (var normalized = TextUtil.Normalize(contents, ctx)) + { + // Create a lexer for our text stream + var lexer = new IcalLexer(normalized); + var parser = new IcalParser(lexer); + + // Get a serializer factory from our serialization services + var sf = GetService(); + + // Get a calendar component factory from our serialization services + var cf = GetService(); + + var parsedComponent = parser.component(ctx, sf, cf, null); + return parsedComponent; + } + } + } + + public class PropertyAlphabetizer : IComparer + { + public int Compare(ICalendarProperty x, ICalendarProperty y) + { + if (x == y || (x == null && y == null)) + { + return 0; + } + if (x == null) + { + return -1; + } + if (y == null) + { + return 1; + } + return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); + } + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Components/EventSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Components/EventSerializer.cs new file mode 100644 index 00000000..09d806ac --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Components/EventSerializer.cs @@ -0,0 +1,26 @@ +using System; + +namespace Ical.Net.Serialization.iCalendar.Serializers.Components +{ + public class EventSerializer : ComponentSerializer + { + public override Type TargetType => typeof (CalendarEvent); + + public override string SerializeToString(object obj) + { + var evt = obj as CalendarEvent; + + CalendarEvent actualEvent; + if (evt.Properties.ContainsKey("DURATION") && evt.Properties.ContainsKey("DTEND")) + { + actualEvent = evt.Copy(); + actualEvent.Properties.Remove("DURATION"); + } + else + { + actualEvent = evt; + } + return base.SerializeToString(actualEvent); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataMapSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataMapSerializer.cs new file mode 100644 index 00000000..a5de9a4c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataMapSerializer.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Interfaces.Serialization.Factory; +using Ical.Net.Serialization.iCalendar.Serializers.Other; + +namespace Ical.Net.Serialization.iCalendar.Serializers +{ + public class DataMapSerializer : SerializerBase + { + public DataMapSerializer() {} + + public DataMapSerializer(SerializationContext ctx) : base(ctx) {} + + protected IStringSerializer GetMappedSerializer() + { + var sf = GetService(); + var mapper = GetService(); + if (sf != null && mapper != null) + { + var obj = SerializationContext.Peek(); + + // Get the data type for this object + var type = mapper.GetPropertyMapping(obj); + + if (type != null) + { + return sf.Build(type, SerializationContext) as IStringSerializer; + } + return new StringSerializer(SerializationContext); + } + return null; + } + + public override Type TargetType + { + get + { + ISerializer serializer = GetMappedSerializer(); + return serializer?.TargetType; + } + } + + public override string SerializeToString(object obj) + { + var serializer = GetMappedSerializer(); + return serializer?.SerializeToString(obj); + } + + public override object Deserialize(TextReader tr) + { + var serializer = GetMappedSerializer(); + if (serializer != null) + { + var value = tr.ReadToEnd(); + var returnValue = serializer.Deserialize(new StringReader(value)); + + // Default to returning the string representation of the value + // if the value wasn't formatted correctly. + // FIXME: should this be a try/catch? Should serializers be throwing + // an InvalidFormatException? This may have some performance issues + // as try/catch is much slower than other means. + return returnValue ?? value; + } + return null; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/AttachmentSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/AttachmentSerializer.cs new file mode 100644 index 00000000..4adfc2e4 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/AttachmentSerializer.cs @@ -0,0 +1,81 @@ +using System; +using System.IO; +using Ical.Net.DataTypes; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class AttachmentSerializer : EncodableDataTypeSerializer + { + public override Type TargetType => typeof (Attachment); + + public override string SerializeToString(object obj) + { + var a = obj as Attachment; + if (a == null) + { + return null; + } + + if (a.Uri != null) + { + if (a.Parameters.ContainsKey("VALUE")) + { + // Ensure no VALUE type is provided + a.Parameters.Remove("VALUE"); + } + + return Encode(a, a.Uri.OriginalString); + } + if (a.Data == null) + { + return null; + } + + // Ensure the VALUE type is set to BINARY + a.SetValueType("BINARY"); + + // BASE64 encoding for BINARY inline attachments. + a.Parameters.Set("ENCODING", "BASE64"); + + return Encode(a, a.Data); + } + + public Attachment Deserialize(string attachment) + { + try + { + var a = CreateAndAssociate() as Attachment; + // Decode the value, if necessary + var data = DecodeData(a, attachment); + + // Get the currently-used encoding off the encoding stack. + var encodingStack = GetService(); + a.ValueEncoding = encodingStack.Current; + + // Get the format of the attachment + var valueType = a.GetValueType(); + if (valueType == typeof(byte[])) + { + // If the VALUE type is specifically set to BINARY, + // then set the Data property instead. + return new Attachment(data) {ValueEncoding = a.ValueEncoding}; + } + + // The default VALUE type for attachments is URI. So, let's + // grab the URI by default. + var uriValue = Decode(a, attachment); + a.Uri = new Uri(uriValue); + + return a; + } + catch + { + // ignored + } + + return null; + } + + public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/AttendeeSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/AttendeeSerializer.cs new file mode 100644 index 00000000..09f77797 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/AttendeeSerializer.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using Ical.Net.DataTypes; +using Ical.Net.Serialization.iCalendar.Serializers.Other; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class AttendeeSerializer : StringSerializer + { + public override Type TargetType => typeof (Attendee); + + public override string SerializeToString(object obj) + { + var a = obj as Attendee; + return a?.Value == null + ? null + : Encode(a, a.Value.OriginalString); + } + + public Attendee Deserialize(string attendee) + { + try + { + var a = CreateAndAssociate() as Attendee; + var uriString = Unescape(Decode(a, attendee)); + + // Prepend "mailto:" if necessary + if (!uriString.StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) + { + uriString = "mailto:" + uriString; + } + + a.Value = new Uri(uriString); + return a; + } + catch + { + // ignored + } + + return null; + } + + public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/DataTypeSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/DataTypeSerializer.cs new file mode 100644 index 00000000..9e6d7188 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/DataTypeSerializer.cs @@ -0,0 +1,30 @@ +using System; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.General; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public abstract class DataTypeSerializer : SerializerBase + { + protected DataTypeSerializer() {} + + protected DataTypeSerializer(SerializationContext ctx) : base(ctx) {} + + protected virtual ICalendarDataType CreateAndAssociate() + { + // Create an instance of the object + var dt = Activator.CreateInstance(TargetType) as ICalendarDataType; + if (dt != null) + { + var associatedObject = SerializationContext.Peek() as ICalendarObject; + if (associatedObject != null) + { + dt.AssociatedObject = associatedObject; + } + + return dt; + } + return null; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/DateTimeSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/DateTimeSerializer.cs new file mode 100644 index 00000000..d31ae856 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/DateTimeSerializer.cs @@ -0,0 +1,137 @@ +using System; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class DateTimeSerializer : EncodableDataTypeSerializer + { + private DateTime CoerceDateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + { + var dt = DateTime.MinValue; + + // NOTE: determine if a date/time value exceeds the representable date/time values in .NET. + // If so, let's automatically adjust the date/time to compensate. + // FIXME: should we have a parsing setting that will throw an exception + // instead of automatically adjusting the date/time value to the + // closest representable date/time? + try + { + if (year > 9999) + { + dt = DateTime.MaxValue; + } + else if (year > 0) + { + dt = new DateTime(year, month, day, hour, minute, second, kind); + } + } + catch {} + + return dt; + } + + public override Type TargetType => typeof (CalDateTime); + + public override string SerializeToString(object obj) + { + var dt = obj as IDateTime; + + // Assign the TZID for the date/time value. + if (dt.IsUniversalTime) + { + dt.Parameters.Remove("TZID"); + } + else if (dt.TzId != null) + { + dt.Parameters.Set("TZID", dt.TzId); + } + + // FIXME: what if DATE is the default value type for this? + // Also, what if the DATE-TIME value type is specified on something + // where DATE-TIME is the default value type? It should be removed + // during serialization, as it's redundant... + if (!dt.HasTime) + { + dt.SetValueType("DATE"); + } + + var value = new StringBuilder(32); + value.Append($"{dt.Year:0000}{dt.Month:00}{dt.Day:00}"); + if (dt.HasTime) + { + value.Append($"T{dt.Hour:00}{dt.Minute:00}{dt.Second:00}"); + if (dt.IsUniversalTime) + { + value.Append("Z"); + } + } + + // Encode the value as necessary + return Encode(dt, value.ToString()); + } + + private const RegexOptions _ciCompiled = RegexOptions.Compiled | RegexOptions.IgnoreCase; + internal static readonly Regex DateOnlyMatch = new Regex(@"^((\d{4})(\d{2})(\d{2}))?$", _ciCompiled); + internal static readonly Regex FullDateTimePatternMatch = new Regex(@"^((\d{4})(\d{2})(\d{2}))T((\d{2})(\d{2})(\d{2})(Z)?)$", _ciCompiled); + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + var dt = CreateAndAssociate() as IDateTime; + if (dt != null) + { + // Decode the value as necessary + value = Decode(dt, value); + + var match = FullDateTimePatternMatch.Match(value); + if (!match.Success) + { + match = DateOnlyMatch.Match(value); + } + + if (!match.Success) + { + return null; + } + var now = DateTime.Now; + + var year = now.Year; + var month = now.Month; + var date = now.Day; + var hour = 0; + var minute = 0; + var second = 0; + + if (match.Groups[1].Success) + { + dt.HasDate = true; + year = Convert.ToInt32(match.Groups[2].Value); + month = Convert.ToInt32(match.Groups[3].Value); + date = Convert.ToInt32(match.Groups[4].Value); + } + if (match.Groups.Count >= 6 && match.Groups[5].Success) + { + dt.HasTime = true; + hour = Convert.ToInt32(match.Groups[6].Value); + minute = Convert.ToInt32(match.Groups[7].Value); + second = Convert.ToInt32(match.Groups[8].Value); + } + + if (match.Groups[9].Success) + { + dt.IsUniversalTime = true; + } + + dt.Value = CoerceDateTime(year, month, date, hour, minute, second, DateTimeKind.Utc); + return dt; + } + + return null; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/EncodableDataTypeSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/EncodableDataTypeSerializer.cs new file mode 100644 index 00000000..7daee99e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/EncodableDataTypeSerializer.cs @@ -0,0 +1,78 @@ +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Serialization; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public abstract class EncodableDataTypeSerializer : DataTypeSerializer + { + protected EncodableDataTypeSerializer() {} + + protected EncodableDataTypeSerializer(SerializationContext ctx) : base(ctx) {} + + protected string Encode(IEncodableDataType dt, string value) + { + if (value == null) + { + return null; + } + + if (dt?.Encoding == null) + { + return value; + } + + // Return the value in the current encoding + var encodingStack = GetService(); + return Encode(dt, encodingStack.Current.GetBytes(value)); + } + + protected string Encode(IEncodableDataType dt, byte[] data) + { + if (data == null) + { + return null; + } + + if (dt?.Encoding == null) + { + // Default to the current encoding + var encodingStack = GetService(); + return encodingStack.Current.GetString(data); + } + + var encodingProvider = GetService(); + return encodingProvider?.Encode(dt.Encoding, data); + } + + protected string Decode(IEncodableDataType dt, string value) + { + var data = DecodeData(dt, value); + if (data == null) + { + return null; + } + + // Default to the current encoding + var encodingStack = GetService(); + return encodingStack.Current.GetString(data); + } + + protected byte[] DecodeData(IEncodableDataType dt, string value) + { + if (value == null) + { + return null; + } + + if (dt?.Encoding == null) + { + // Default to the current encoding + var encodingStack = GetService(); + return encodingStack.Current.GetBytes(value); + } + + var encodingProvider = GetService(); + return encodingProvider?.DecodeData(dt.Encoding, value); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/FreeBusyEntrySerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/FreeBusyEntrySerializer.cs new file mode 100644 index 00000000..f07c186b --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/FreeBusyEntrySerializer.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; +using Ical.Net.DataTypes; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class FreeBusyEntrySerializer : PeriodSerializer + { + public override Type TargetType => typeof (FreeBusyEntry); + + public override string SerializeToString(object obj) + { + var entry = obj as FreeBusyEntry; + if (entry != null) + { + switch (entry.Status) + { + case FreeBusyStatus.Busy: + entry.Parameters.Remove("FBTYPE"); + break; + case FreeBusyStatus.BusyTentative: + entry.Parameters.Set("FBTYPE", "BUSY-TENTATIVE"); + break; + case FreeBusyStatus.BusyUnavailable: + entry.Parameters.Set("FBTYPE", "BUSY-UNAVAILABLE"); + break; + case FreeBusyStatus.Free: + entry.Parameters.Set("FBTYPE", "FREE"); + break; + } + } + + return base.SerializeToString(obj); + } + + public override object Deserialize(TextReader tr) + { + var entry = base.Deserialize(tr) as FreeBusyEntry; + if (entry != null) + { + if (entry.Parameters.ContainsKey("FBTYPE")) + { + var value = entry.Parameters.Get("FBTYPE"); + if (value == null) + { + return entry; + } + switch (value.ToUpperInvariant()) + { + case "FREE": + entry.Status = FreeBusyStatus.Free; + break; + case "BUSY": + entry.Status = FreeBusyStatus.Busy; + break; + case "BUSY-UNAVAILABLE": + entry.Status = FreeBusyStatus.BusyUnavailable; + break; + case "BUSY-TENTATIVE": + entry.Status = FreeBusyStatus.BusyTentative; + break; + } + } + } + + return entry; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/GeographicLocationSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/GeographicLocationSerializer.cs new file mode 100644 index 00000000..32a406cd --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/GeographicLocationSerializer.cs @@ -0,0 +1,59 @@ +using System; +using System.IO; +using Ical.Net.DataTypes; +using System.Globalization; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class GeographicLocationSerializer : EncodableDataTypeSerializer + { + public override Type TargetType => typeof (GeographicLocation); + + public override string SerializeToString(object obj) + { + var g = obj as GeographicLocation; + if (g == null) + { + return null; + } + + var value = g.Latitude.ToString("0.000000", CultureInfo.InvariantCulture.NumberFormat) + ";" + + g.Longitude.ToString("0.000000", CultureInfo.InvariantCulture.NumberFormat); + return Encode(g, value); + } + + public GeographicLocation Deserialize(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } + + var g = CreateAndAssociate() as GeographicLocation; + if (g == null) + { + return null; + } + + // Decode the value, if necessary! + value = Decode(g, value); + + var values = value.Split(new [] {';'}, StringSplitOptions.RemoveEmptyEntries); + if (values.Length != 2) + { + return null; + } + + double lat; + double lon; + double.TryParse(values[0], NumberStyles.Any, CultureInfo.InvariantCulture, out lat); + double.TryParse(values[1], NumberStyles.Any, CultureInfo.InvariantCulture, out lon); + g.Latitude = lat; + g.Longitude = lon; + + return g; + } + + public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/OrganizerSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/OrganizerSerializer.cs new file mode 100644 index 00000000..6efa8e25 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/OrganizerSerializer.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using Ical.Net.DataTypes; +using Ical.Net.Serialization.iCalendar.Serializers.Other; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class OrganizerSerializer : StringSerializer + { + public override Type TargetType => typeof (Organizer); + + public override string SerializeToString(object obj) + { + try + { + var o = obj as Organizer; + return o?.Value == null + ? null + : Encode(o, Escape(o.Value.OriginalString)); + } + catch + { + return null; + } + } + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + Organizer o = null; + try + { + o = CreateAndAssociate() as Organizer; + if (o != null) + { + var uriString = Unescape(Decode(o, value)); + + // Prepend "mailto:" if necessary + if (!uriString.StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) + { + uriString = "mailto:" + uriString; + } + + o.Value = new Uri(uriString); + } + } + catch {} + + return o; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/PeriodListSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/PeriodListSerializer.cs new file mode 100644 index 00000000..37c5b238 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/PeriodListSerializer.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Interfaces.Serialization.Factory; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class PeriodListSerializer : EncodableDataTypeSerializer + { + public override Type TargetType => typeof (PeriodList); + + public override string SerializeToString(object obj) + { + var rdt = obj as PeriodList; + var factory = GetService(); + if (rdt == null || factory == null) + { + return null; + } + + var dtSerializer = factory.Build(typeof (IDateTime), SerializationContext) as IStringSerializer; + var periodSerializer = factory.Build(typeof (Period), SerializationContext) as IStringSerializer; + if (dtSerializer == null || periodSerializer == null) + { + return null; + } + + var parts = new List(rdt.Count); + foreach (var p in rdt) + { + if (p.EndTime != null) + { + parts.Add(periodSerializer.SerializeToString(p)); + } + else if (p.StartTime != null) + { + parts.Add(dtSerializer.SerializeToString(p.StartTime)); + } + } + + return Encode(rdt, string.Join(",", parts)); + } + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + // Create the day specifier and associate it with a calendar object + var rdt = CreateAndAssociate() as PeriodList; + var factory = GetService(); + if (rdt == null || factory == null) + { + return null; + } + + // Decode the value, if necessary + value = Decode(rdt, value); + + var dtSerializer = factory.Build(typeof (IDateTime), SerializationContext) as IStringSerializer; + var periodSerializer = factory.Build(typeof (Period), SerializationContext) as IStringSerializer; + if (dtSerializer == null || periodSerializer == null) + { + return null; + } + + var values = value.Split(','); + foreach (var v in values) + { + var dt = dtSerializer.Deserialize(new StringReader(v)) as IDateTime; + var p = periodSerializer.Deserialize(new StringReader(v)) as Period; + + if (dt != null) + { + dt.AssociatedObject = rdt.AssociatedObject; + rdt.Add(dt); + } + else if (p != null) + { + p.AssociatedObject = rdt.AssociatedObject; + rdt.Add(p); + } + } + return rdt; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/PeriodSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/PeriodSerializer.cs new file mode 100644 index 00000000..177db10f --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/PeriodSerializer.cs @@ -0,0 +1,92 @@ +using System; +using System.IO; +using System.Text; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Interfaces.Serialization.Factory; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class PeriodSerializer : EncodableDataTypeSerializer + { + public override Type TargetType => typeof (Period); + + public override string SerializeToString(object obj) + { + var p = obj as Period; + var factory = GetService(); + + if (p != null && factory != null) + { + // Push the period onto the serialization context stack + SerializationContext.Push(p); + + try + { + var dtSerializer = factory.Build(typeof (IDateTime), SerializationContext) as IStringSerializer; + var timeSpanSerializer = factory.Build(typeof (TimeSpan), SerializationContext) as IStringSerializer; + if (dtSerializer != null && timeSpanSerializer != null) + { + var sb = new StringBuilder(); + + // Serialize the start time + sb.Append(dtSerializer.SerializeToString(p.StartTime)); + + // Serialize the duration + sb.Append("/"); + sb.Append(timeSpanSerializer.SerializeToString(p.Duration)); + + // Encode the value as necessary + return Encode(p, sb.ToString()); + } + } + finally + { + // Pop the period off the serialization context stack + SerializationContext.Pop(); + } + } + return null; + } + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + var p = CreateAndAssociate() as Period; + var factory = GetService(); + if (p != null && factory != null) + { + var dtSerializer = factory.Build(typeof (IDateTime), SerializationContext) as IStringSerializer; + var durationSerializer = factory.Build(typeof (TimeSpan), SerializationContext) as IStringSerializer; + if (dtSerializer != null && durationSerializer != null) + { + // Decode the value as necessary + value = Decode(p, value); + + var values = value.Split('/'); + if (values.Length != 2) + { + return false; + } + + p.StartTime = dtSerializer.Deserialize(new StringReader(values[0])) as IDateTime; + p.EndTime = dtSerializer.Deserialize(new StringReader(values[1])) as IDateTime; + if (p.EndTime == null) + { + p.Duration = (TimeSpan) durationSerializer.Deserialize(new StringReader(values[1])); + } + + // Only return an object if it has been deserialized correctly. + if (p.StartTime != null && p.Duration != null) + { + return p; + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/RecurrencePatternSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/RecurrencePatternSerializer.cs new file mode 100644 index 00000000..eccabeee --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/RecurrencePatternSerializer.cs @@ -0,0 +1,492 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Interfaces.Serialization.Factory; +using static System.Reflection.TypeExtensions; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class RecurrencePatternSerializer : EncodableDataTypeSerializer + { + public static DayOfWeek GetDayOfWeek(string value) + { + switch (value.ToUpper()) + { + case "SU": + return DayOfWeek.Sunday; + case "MO": + return DayOfWeek.Monday; + case "TU": + return DayOfWeek.Tuesday; + case "WE": + return DayOfWeek.Wednesday; + case "TH": + return DayOfWeek.Thursday; + case "FR": + return DayOfWeek.Friday; + case "SA": + return DayOfWeek.Saturday; + } + throw new ArgumentException(value + " is not a valid iCal day-of-week indicator."); + } + + protected static void AddInt32Values(IList list, string value) + { + var values = value.Split(','); + foreach (var v in values) + { + list.Add(Convert.ToInt32(v)); + } + } + + public virtual void CheckRange(string name, IList values, int min, int max) + { + var allowZero = (min == 0 || max == 0); + foreach (var value in values) + { + CheckRange(name, value, min, max, allowZero); + } + } + + public virtual void CheckRange(string name, int value, int min, int max) + { + var allowZero = min == 0 || max == 0; + CheckRange(name, value, min, max, allowZero); + } + + public virtual void CheckRange(string name, int value, int min, int max, bool allowZero) + { + if (value != int.MinValue && (value < min || value > max || (!allowZero && value == 0))) + { + throw new ArgumentException(name + " value " + value + " is out of range. Valid values are between " + min + " and " + max + + (allowZero ? "" : ", excluding zero (0)") + "."); + } + } + + public virtual void CheckMutuallyExclusive(string name1, string name2, T obj1, TU obj2) + { + if (Equals(obj1, default(T)) || Equals(obj2, default(TU))) + { + return; + } + // If the object is MinValue instead of its default, consider + // that to be unassigned. + + var t1 = obj1.GetType(); + + var fi1 = t1.GetField("MinValue"); + var fi2 = t1.GetField("MinValue"); + + var isMin1 = fi1 != null && obj1.Equals(fi1.GetValue(null)); + var isMin2 = fi2 != null && obj2.Equals(fi2.GetValue(null)); + if (isMin1 || isMin2) + { + return; + } + + throw new ArgumentException("Both " + name1 + " and " + name2 + " cannot be supplied together; they are mutually exclusive."); + } + + private void SerializeByValue(List aggregate, IList byValue, string name) + { + if (byValue.Any()) + { + aggregate.Add(name + "=" + string.Join(",", byValue.Select(i => i.ToString()))); + } + } + + public override Type TargetType => typeof (RecurrencePattern); + + public override string SerializeToString(object obj) + { + var recur = obj as RecurrencePattern; + var factory = GetService(); + if (recur == null || factory == null) + { + return null; + } + + // Push the recurrence pattern onto the serialization stack + SerializationContext.Push(recur); + var values = new List(16) + { + "FREQ=" + Enum.GetName(typeof(FrequencyType), recur.Frequency).ToUpper() + }; + + + //-- FROM RFC2445 -- + //The INTERVAL rule part contains a positive integer representing how + //often the recurrence rule repeats. The default value is "1", meaning + //every second for a SECONDLY rule, or every minute for a MINUTELY + //rule, every hour for an HOURLY rule, every day for a DAILY rule, + //every week for a WEEKLY rule, every month for a MONTHLY rule and + //every year for a YEARLY rule. + var interval = recur.Interval; + if (interval == int.MinValue) + { + interval = 1; + } + + if (interval != 1) + { + values.Add("INTERVAL=" + interval); + } + + if (recur.Until != DateTime.MinValue) + { + var serializer = factory.Build(typeof (IDateTime), SerializationContext) as IStringSerializer; + if (serializer != null) + { + IDateTime until = new CalDateTime(recur.Until); + until.HasTime = true; + values.Add("UNTIL=" + serializer.SerializeToString(until)); + } + } + + if (recur.FirstDayOfWeek != DayOfWeek.Monday) + { + values.Add("WKST=" + Enum.GetName(typeof (DayOfWeek), recur.FirstDayOfWeek).ToUpper().Substring(0, 2)); + } + + if (recur.Count != int.MinValue) + { + values.Add("COUNT=" + recur.Count); + } + + if (recur.ByDay.Count > 0) + { + var bydayValues = new List(128); + + var serializer = factory.Build(typeof (WeekDay), SerializationContext) as IStringSerializer; + if (serializer != null) + { + bydayValues.AddRange(recur.ByDay.Select(byday => serializer.SerializeToString(byday))); + } + + values.Add("BYDAY=" + string.Join(",", bydayValues.ToArray())); + } + + SerializeByValue(values, recur.ByHour, "BYHOUR"); + SerializeByValue(values, recur.ByMinute, "BYMINUTE"); + SerializeByValue(values, recur.ByMonth, "BYMONTH"); + SerializeByValue(values, recur.ByMonthDay, "BYMONTHDAY"); + SerializeByValue(values, recur.BySecond, "BYSECOND"); + SerializeByValue(values, recur.BySetPosition, "BYSETPOS"); + SerializeByValue(values, recur.ByWeekNo, "BYWEEKNO"); + SerializeByValue(values, recur.ByYearDay, "BYYEARDAY"); + + // Pop the recurrence pattern off the serialization stack + SerializationContext.Pop(); + + return Encode(recur, string.Join(";", values.ToArray())); + } + + //Compiling these is a one-time penalty of about 80ms + private const RegexOptions _ciCompiled = RegexOptions.IgnoreCase | RegexOptions.Compiled; + + internal static readonly Regex OtherInterval = + new Regex(@"every\s+(?other|\d+)?\w{0,2}\s*(?second|minute|hour|day|week|month|year)s?,?\s*(?.+)", _ciCompiled); + + internal static readonly Regex AdverbFrequencies = new Regex(@"FREQ=(SECONDLY|MINUTELY|HOURLY|DAILY|WEEKLY|MONTHLY|YEARLY);?(.*)", _ciCompiled); + + internal static readonly Regex NumericTemporalUnits = new Regex(@"(?\d+)\w\w\s+(?second|minute|hour|day|week|month)", _ciCompiled); + + internal static readonly Regex TemporalUnitType = new Regex(@"(?second|minute|hour|day|week|month)\s+(?\d+)", _ciCompiled); + + internal static readonly Regex RelativeDaysOfWeek = + new Regex( + @"(?\d+\w{0,2})?(\w|\s)+?(?first)?(?last)?\s*((?sunday|monday|tuesday|wednesday|thursday|friday|saturday)\s*(and|or)?\s*)+", + _ciCompiled); + + internal static readonly Regex Time = new Regex(@"at\s+(?\d{1,2})(:(?\d{2})((:|\.)(?\d{2}))?)?\s*(?(a|p)m?)?", + _ciCompiled); + + internal static readonly Regex RecurUntil = new Regex(@"^\s*until\s+(?.+)$", _ciCompiled); + + internal static readonly Regex SpecificRecurrenceCount = new Regex(@"^\s*for\s+(?\d+)\s+occurrences\s*$", _ciCompiled); + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + // Instantiate the data type + var r = CreateAndAssociate() as RecurrencePattern; + var factory = GetService(); + if (r == null || factory == null) + { + return r; + } + + // Decode the value, if necessary + value = Decode(r, value); + + var match = AdverbFrequencies.Match(value); + if (match.Success) + { + // Parse the frequency type + r.Frequency = (FrequencyType) Enum.Parse(typeof (FrequencyType), match.Groups[1].Value, true); + + // NOTE: fixed a bug where the group 2 match + // resulted in an empty string, which caused + // an error. + if (match.Groups[2].Success && match.Groups[2].Length > 0) + { + var keywordPairs = match.Groups[2].Value.Split(';'); + foreach (var keywordPair in keywordPairs) + { + var keyValues = keywordPair.Split('='); + var keyword = keyValues[0]; + var keyValue = keyValues[1]; + + switch (keyword.ToUpper()) + { + case "UNTIL": + { + var serializer = factory.Build(typeof (IDateTime), SerializationContext) as IStringSerializer; + var dt = serializer?.Deserialize(new StringReader(keyValue)) as IDateTime; + if (dt != null) + { + r.Until = dt.Value; + } + } + break; + case "COUNT": + r.Count = Convert.ToInt32(keyValue); + break; + case "INTERVAL": + r.Interval = Convert.ToInt32(keyValue); + break; + case "BYSECOND": + AddInt32Values(r.BySecond, keyValue); + break; + case "BYMINUTE": + AddInt32Values(r.ByMinute, keyValue); + break; + case "BYHOUR": + AddInt32Values(r.ByHour, keyValue); + break; + case "BYDAY": + { + var days = keyValue.Split(','); + foreach (var day in days) + { + r.ByDay.Add(new WeekDay(day)); + } + } + break; + case "BYMONTHDAY": + AddInt32Values(r.ByMonthDay, keyValue); + break; + case "BYYEARDAY": + AddInt32Values(r.ByYearDay, keyValue); + break; + case "BYWEEKNO": + AddInt32Values(r.ByWeekNo, keyValue); + break; + case "BYMONTH": + AddInt32Values(r.ByMonth, keyValue); + break; + case "BYSETPOS": + AddInt32Values(r.BySetPosition, keyValue); + break; + case "WKST": + r.FirstDayOfWeek = GetDayOfWeek(keyValue); + break; + } + } + } + } + + // + // This matches strings such as: + // + // "Every 6 minutes" + // "Every 3 days" + // + else if ((match = OtherInterval.Match(value)).Success) + { + if (match.Groups["Interval"].Success) + { + int interval; + r.Interval = !int.TryParse(match.Groups["Interval"].Value, out interval) + ? 2 + : interval; + } + else + { + r.Interval = 1; + } + + switch (match.Groups["Freq"].Value.ToLower()) + { + case "second": + r.Frequency = FrequencyType.Secondly; + break; + case "minute": + r.Frequency = FrequencyType.Minutely; + break; + case "hour": + r.Frequency = FrequencyType.Hourly; + break; + case "day": + r.Frequency = FrequencyType.Daily; + break; + case "week": + r.Frequency = FrequencyType.Weekly; + break; + case "month": + r.Frequency = FrequencyType.Monthly; + break; + case "year": + r.Frequency = FrequencyType.Yearly; + break; + } + + var values = match.Groups["More"].Value.Split(','); + foreach (var item in values) + { + if ((match = NumericTemporalUnits.Match(item)).Success || (match = TemporalUnitType.Match(item)).Success) + { + int num; + if (!int.TryParse(match.Groups["Num"].Value, out num)) + { + continue; + } + + switch (match.Groups["Type"].Value.ToLower()) + { + case "second": + r.BySecond.Add(num); + break; + case "minute": + r.ByMinute.Add(num); + break; + case "hour": + r.ByHour.Add(num); + break; + case "day": + switch (r.Frequency) + { + case FrequencyType.Yearly: + r.ByYearDay.Add(num); + break; + case FrequencyType.Monthly: + r.ByMonthDay.Add(num); + break; + } + break; + case "week": + r.ByWeekNo.Add(num); + break; + case "month": + r.ByMonth.Add(num); + break; + } + } + else if ((match = RelativeDaysOfWeek.Match(item)).Success) + { + var num = int.MinValue; + if (match.Groups["Num"].Success) + { + if (int.TryParse(match.Groups["Num"].Value, out num)) + { + if (match.Groups["Last"].Success) + { + // Make number negative + num *= -1; + } + } + } + else if (match.Groups["Last"].Success) + { + num = -1; + } + else if (match.Groups["First"].Success) + { + num = 1; + } + + var dayOfWeekQuery = from Capture capture in match.Groups["Day"].Captures + select (DayOfWeek) Enum.Parse(typeof(DayOfWeek), capture.Value, true) into dayOfWeek + select new WeekDay(dayOfWeek) {Offset = num}; + + r.ByDay.AddRange(dayOfWeekQuery); + } + else if ((match = Time.Match(item)).Success) + { + int hour; + + if (!int.TryParse(match.Groups["Hour"].Value, out hour)) + { + continue; + } + + // Adjust for PM + if (match.Groups["Meridian"].Success && match.Groups["Meridian"].Value.ToUpper().StartsWith("P")) + { + hour += 12; + } + + r.ByHour.Add(hour); + + int minute; + if (match.Groups["Minute"].Success && int.TryParse(match.Groups["Minute"].Value, out minute)) + { + r.ByMinute.Add(minute); + int second; + if (match.Groups["Second"].Success && int.TryParse(match.Groups["Second"].Value, out second)) + { + r.BySecond.Add(second); + } + } + } + else if ((match = RecurUntil.Match(item)).Success) + { + var dt = DateTime.Parse(match.Groups["DateTime"].Value); + DateTime.SpecifyKind(dt, DateTimeKind.Utc); + + r.Until = dt; + } + else if ((match = SpecificRecurrenceCount.Match(item)).Success) + { + int count; + if (!int.TryParse(match.Groups["Count"].Value, out count)) + { + return false; + } + r.Count = count; + } + } + } + else + { + // Couldn't parse the object, return null! + r = null; + } + + if (r == null) + { + return r; + } + + CheckMutuallyExclusive("COUNT", "UNTIL", r.Count, r.Until); + CheckRange("INTERVAL", r.Interval, 1, int.MaxValue); + CheckRange("COUNT", r.Count, 1, int.MaxValue); + CheckRange("BYSECOND", r.BySecond, 0, 59); + CheckRange("BYMINUTE", r.ByMinute, 0, 59); + CheckRange("BYHOUR", r.ByHour, 0, 23); + CheckRange("BYMONTHDAY", r.ByMonthDay, -31, 31); + CheckRange("BYYEARDAY", r.ByYearDay, -366, 366); + CheckRange("BYWEEKNO", r.ByWeekNo, -53, 53); + CheckRange("BYMONTH", r.ByMonth, 1, 12); + CheckRange("BYSETPOS", r.BySetPosition, -366, 366); + + return r; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/RequestStatusSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/RequestStatusSerializer.cs new file mode 100644 index 00000000..4f30a0b0 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/RequestStatusSerializer.cs @@ -0,0 +1,120 @@ +using System; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Interfaces.Serialization.Factory; +using Ical.Net.Serialization.iCalendar.Serializers.Other; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class RequestStatusSerializer : StringSerializer + { + public override Type TargetType => typeof (RequestStatus); + + public override string SerializeToString(object obj) + { + try + { + var rs = obj as RequestStatus; + if (rs == null) + { + return null; + } + + // Push the object onto the serialization stack + SerializationContext.Push(rs); + + try + { + var factory = GetService(); + var serializer = factory?.Build(typeof (StatusCode), SerializationContext) as IStringSerializer; + if (serializer == null) + { + return null; + } + + var builder = new StringBuilder(256); + builder.Append(Escape(serializer.SerializeToString(rs.StatusCode))); + builder.Append(";"); + builder.Append(Escape(rs.Description)); + if (!string.IsNullOrWhiteSpace(rs.ExtraData)) + { + builder.Append(";"); + builder.Append(Escape(rs.ExtraData)); + } + return Encode(rs, builder.ToString()); + } + finally + { + // Pop the object off the serialization stack + SerializationContext.Pop(); + } + } + catch + { + return null; + } + } + + internal static readonly Regex NarrowRequestMatch = new Regex(@"(.*?[^\\]);(.*?[^\\]);(.+)", RegexOptions.Compiled); + internal static readonly Regex BroadRequestMatch = new Regex(@"(.*?[^\\]);(.+)", RegexOptions.Compiled); + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + var rs = CreateAndAssociate() as RequestStatus; + if (rs == null) + { + return null; + } + + // Decode the value as needed + value = Decode(rs, value); + + // Push the object onto the serialization stack + SerializationContext.Push(rs); + + try + { + var factory = GetService(); + if (factory == null) + { + return null; + } + + var match = NarrowRequestMatch.Match(value); + if (!match.Success) + { + match = BroadRequestMatch.Match(value); + } + + if (match.Success) + { + var serializer = factory.Build(typeof(StatusCode), SerializationContext) as IStringSerializer; + if (serializer == null) + { + return null; + } + + rs.StatusCode = serializer.Deserialize(new StringReader(Unescape(match.Groups[1].Value))) as StatusCode; + rs.Description = Unescape(match.Groups[2].Value); + if (match.Groups.Count == 4) + { + rs.ExtraData = Unescape(match.Groups[3].Value); + } + + return rs; + } + } + finally + { + // Pop the object off the serialization stack + SerializationContext.Pop(); + } + return null; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/StatusCodeSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/StatusCodeSerializer.cs new file mode 100644 index 00000000..3d8f9155 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/StatusCodeSerializer.cs @@ -0,0 +1,65 @@ +using System; +using System.IO; +using System.Text.RegularExpressions; +using Ical.Net.DataTypes; +using Ical.Net.Serialization.iCalendar.Serializers.Other; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class StatusCodeSerializer : StringSerializer + { + public override Type TargetType => typeof (StatusCode); + + public override string SerializeToString(object obj) + { + var sc = obj as StatusCode; + if (sc == null) + { + return null; + } + + var vals = new string[sc.Parts.Length]; + for (var i = 0; i < sc.Parts.Length; i++) + { + vals[i] = sc.Parts[i].ToString(); + } + return Encode(sc, Escape(string.Join(".", vals))); + } + + internal static readonly Regex StatusCode = new Regex(@"\d(\.\d+)*", RegexOptions.Compiled | RegexOptions.CultureInvariant); + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + var sc = CreateAndAssociate() as StatusCode; + if (sc == null) + { + return null; + } + + // Decode the value as needed + value = Decode(sc, value); + + var match = StatusCode.Match(value); + if (!match.Success) + { + return null; + } + + var parts = match.Value.Split('.'); + var iparts = new int[parts.Length]; + for (var i = 0; i < parts.Length; i++) + { + int num; + if (!int.TryParse(parts[i], out num)) + { + return false; + } + iparts[i] = num; + } + + return new StatusCode(iparts); + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/TriggerSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/TriggerSerializer.cs new file mode 100644 index 00000000..3dab872c --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/TriggerSerializer.cs @@ -0,0 +1,102 @@ +using System; +using System.IO; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.DataTypes; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Interfaces.Serialization.Factory; +using Ical.Net.Serialization.iCalendar.Serializers.Other; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class TriggerSerializer : StringSerializer + { + public override Type TargetType => typeof (Trigger); + + public override string SerializeToString(object obj) + { + try + { + var t = obj as Trigger; + if (t != null) + { + // Push the trigger onto the serialization stack + SerializationContext.Push(t); + try + { + var factory = GetService(); + if (factory != null) + { + var valueType = t.GetValueType() ?? typeof (TimeSpan); + var serializer = factory.Build(valueType, SerializationContext) as IStringSerializer; + if (serializer != null) + { + var value = (valueType == typeof (IDateTime)) ? t.DateTime : (object) t.Duration; + return serializer.SerializeToString(value); + } + } + } + finally + { + // Pop the trigger off the serialization stack + SerializationContext.Pop(); + } + } + return null; + } + catch + { + return null; + } + } + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + var t = CreateAndAssociate() as Trigger; + if (t != null) + { + // Push the trigger onto the serialization stack + SerializationContext.Push(t); + try + { + // Decode the value as needed + value = Decode(t, value); + + // Set the trigger relation + if (t.Parameters.ContainsKey("RELATED") && t.Parameters.Get("RELATED").Equals("END")) + { + t.Related = TriggerRelation.End; + } + + var factory = GetService(); + if (factory != null) + { + var valueType = t.GetValueType() ?? typeof (TimeSpan); + var serializer = factory.Build(valueType, SerializationContext) as IStringSerializer; + var obj = serializer?.Deserialize(new StringReader(value)); + if (obj != null) + { + if (obj is IDateTime) + { + t.DateTime = (IDateTime) obj; + } + else + { + t.Duration = (TimeSpan) obj; + } + + return t; + } + } + } + finally + { + // Pop the trigger off the serialization stack + SerializationContext.Pop(); + } + } + return null; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/UTCOffsetSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/UTCOffsetSerializer.cs new file mode 100644 index 00000000..94c42cfd --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/UTCOffsetSerializer.cs @@ -0,0 +1,67 @@ +using System; +using System.Globalization; +using System.IO; +using System.Text.RegularExpressions; +using Ical.Net.DataTypes; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class UtcOffsetSerializer : EncodableDataTypeSerializer + { + public override Type TargetType => typeof (UtcOffset); + + public override string SerializeToString(object obj) + { + var offset = obj as UtcOffset; + if (offset != null) + { + var value = (offset.Positive ? "+" : "-") + offset.Hours.ToString("00") + offset.Minutes.ToString("00") + + (offset.Seconds != 0 ? offset.Seconds.ToString("00") : string.Empty); + + // Encode the value as necessary + return Encode(offset, value); + } + return null; + } + + internal static readonly Regex DecodeOffset = new Regex(@"(\+|-)(\d{2})(\d{2})(\d{2})?", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public override object Deserialize(TextReader tr) + { + var offsetString = tr.ReadToEnd(); + var offset = new UtcOffset(offsetString); + return offset; + } + + public static TimeSpan GetOffset(string rawOffset) + { + if (rawOffset.EndsWith("00")) + { + rawOffset = rawOffset.Substring(0, rawOffset.Length - 2); + } + + DateTimeOffset temp; + if (DateTimeOffset.TryParse("2016-01-01 00:00:00 " + rawOffset, out temp)) + { + return temp.Offset; + } + + TimeSpan ts; + var isNegative = rawOffset.StartsWith("-"); + + if (isNegative || rawOffset.StartsWith("+")) + { + rawOffset = rawOffset.Substring(1, rawOffset.Length - 1); + } + + if (!TimeSpan.TryParseExact(rawOffset, "hhmmss", CultureInfo.InvariantCulture, out ts)) + { + throw new FormatException($"{rawOffset} is not a valid UTC offset"); + } + + return isNegative + ? -ts + : ts; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/WeekDaySerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/WeekDaySerializer.cs new file mode 100644 index 00000000..1e3b40d6 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/DataTypes/WeekDaySerializer.cs @@ -0,0 +1,59 @@ +using System; +using System.IO; +using System.Text.RegularExpressions; +using Ical.Net.DataTypes; + +namespace Ical.Net.Serialization.iCalendar.Serializers.DataTypes +{ + public class WeekDaySerializer : EncodableDataTypeSerializer + { + public override Type TargetType => typeof (WeekDay); + + public override string SerializeToString(object obj) + { + var ds = obj as WeekDay; + if (ds != null) + { + var value = string.Empty; + if (ds.Offset != int.MinValue) + { + value += ds.Offset; + } + value += Enum.GetName(typeof (DayOfWeek), ds.DayOfWeek).ToUpper().Substring(0, 2); + + return Encode(ds, value); + } + return null; + } + + internal static readonly Regex DayOfWeek = new Regex(@"(\+|-)?(\d{1,2})?(\w{2})", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + // Create the day specifier and associate it with a calendar object + var ds = CreateAndAssociate() as WeekDay; + + // Decode the value, if necessary + value = Decode(ds, value); + + var match = DayOfWeek.Match(value); + if (!match.Success) + { + return null; + } + + if (match.Groups[2].Success) + { + ds.Offset = Convert.ToInt32(match.Groups[2].Value); + if (match.Groups[1].Success && match.Groups[1].Value.Contains("-")) + { + ds.Offset *= -1; + } + } + ds.DayOfWeek = RecurrencePatternSerializer.GetDayOfWeek(match.Groups[3].Value); + return ds; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/GenericListSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/GenericListSerializer.cs new file mode 100644 index 00000000..1d407b3e --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/GenericListSerializer.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Ical.Net.Interfaces.General; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Interfaces.Serialization.Factory; + +namespace Ical.Net.Serialization.iCalendar.Serializers +{ + public class GenericListSerializer : SerializerBase + { + private readonly Type _innerType; + private readonly Type _objectType; + + public GenericListSerializer(Type objectType) + { + _innerType = objectType.GetGenericArguments()[0]; + + var listDef = typeof (List<>); + _objectType = listDef.MakeGenericType(typeof (object)); + } + + public override Type TargetType => _objectType; + + public override string SerializeToString(object obj) + { + // NOTE: this behavior is implemented in the PropertySerializer. + throw new NotImplementedException(); + } + + private MethodInfo _addMethodInfo; + public override object Deserialize(TextReader tr) + { + var p = SerializationContext.Peek() as ICalendarProperty; + if (p == null) + { + return null; + } + + // Get a serializer factory to deserialize the contents of this list + var listObj = Activator.CreateInstance(_objectType); + if (listObj == null) + { + return null; + } + + // Get a serializer for the inner type + var sf = GetService(); + var stringSerializer = sf.Build(_innerType, SerializationContext) as IStringSerializer; + if (stringSerializer == null) + { + return null; + } + // Deserialize the inner object + var value = tr.ReadToEnd(); + + // If deserialization failed, pass the string value into the list. + var objToAdd = stringSerializer.Deserialize(new StringReader(value)) ?? value; + + // FIXME: cache this + if (_addMethodInfo == null) + { + _addMethodInfo = _objectType.GetMethod("Add"); + } + + // Determine if the returned object is an IList, rather than just an ObjectType. + var add = objToAdd as IList; + if (add != null) + { + //Deserialization returned an IList, instead of an ObjectType. So enumerate through the items in the list and add + //them individually to our list. + foreach (var innerObj in add) + { + _addMethodInfo.Invoke(listObj, new[] {innerObj}); + } + } + else + { + // Add the object to the list + _addMethodInfo.Invoke(listObj, new[] {objToAdd}); + } + return listObj; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/EnumSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/EnumSerializer.cs new file mode 100644 index 00000000..db0b7674 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/EnumSerializer.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; + +namespace Ical.Net.Serialization.iCalendar.Serializers.Other +{ + public class EnumSerializer : EncodableDataTypeSerializer + { + private readonly Type _mEnumType; + + public EnumSerializer(Type enumType) + { + _mEnumType = enumType; + } + + public override Type TargetType => _mEnumType; + + public override string SerializeToString(object enumValue) + { + try + { + var obj = SerializationContext.Peek() as ICalendarObject; + if (obj != null) + { + // Encode the value as needed. + var dt = new EncodableDataType + { + AssociatedObject = obj + }; + return Encode(dt, enumValue.ToString()); + } + return enumValue.ToString(); + } + catch + { + return null; + } + } + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + try + { + var obj = SerializationContext.Peek() as ICalendarObject; + if (obj != null) + { + // Decode the value, if necessary! + var dt = new EncodableDataType + { + AssociatedObject = obj + }; + value = Decode(dt, value); + } + + // Remove "-" characters while parsing Enum values. + return Enum.Parse(_mEnumType, value.Replace("-", ""), true); + } + catch {} + + return value; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/IntegerSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/IntegerSerializer.cs new file mode 100644 index 00000000..8561963a --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/IntegerSerializer.cs @@ -0,0 +1,65 @@ +using System; +using System.IO; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; + +namespace Ical.Net.Serialization.iCalendar.Serializers.Other +{ + public class IntegerSerializer : EncodableDataTypeSerializer + { + public override Type TargetType => typeof (int); + + public override string SerializeToString(object integer) + { + try + { + var i = Convert.ToInt32(integer); + + var obj = SerializationContext.Peek() as ICalendarObject; + if (obj != null) + { + // Encode the value as needed. + var dt = new EncodableDataType + { + AssociatedObject = obj + }; + return Encode(dt, i.ToString()); + } + return i.ToString(); + } + catch + { + return null; + } + } + + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + try + { + var obj = SerializationContext.Peek() as ICalendarObject; + if (obj != null) + { + // Decode the value, if necessary! + var dt = new EncodableDataType + { + AssociatedObject = obj + }; + value = Decode(dt, value); + } + + int i; + if (Int32.TryParse(value, out i)) + { + return i; + } + } + catch {} + + return value; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/StringSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/StringSerializer.cs new file mode 100644 index 00000000..aed97bb9 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/StringSerializer.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Ical.Net.DataTypes; +using Ical.Net.Interfaces.General; +using Ical.Net.Interfaces.Serialization; +using Ical.Net.Serialization.iCalendar.Serializers.DataTypes; +using Ical.Net.Utility; + +namespace Ical.Net.Serialization.iCalendar.Serializers.Other +{ + public class StringSerializer : EncodableDataTypeSerializer + { + public StringSerializer() {} + + public StringSerializer(SerializationContext ctx) : base(ctx) {} + + internal static readonly Regex SingleBackslashMatch = new Regex(@"(? typeof (string); + + public override string SerializeToString(object obj) + { + if (obj == null) + { + return null; + } + + var values = new List(128); + if (obj is string) + { + values.Add((string) obj); + } + else if (obj is IEnumerable) + { + values.AddRange(from object child in (IEnumerable) obj select child.ToString()); + } + + var co = SerializationContext.Peek() as ICalendarObject; + if (co != null) + { + // Encode the string as needed. + var dt = new EncodableDataType + { + AssociatedObject = co + }; + for (var i = 0; i < values.Count; i++) + { + values[i] = Encode(dt, Escape(values[i])); + } + + return string.Join(",", values); + } + + for (var i = 0; i < values.Count; i++) + { + values[i] = Escape(values[i]); + } + return string.Join(",", values); + } + + internal static readonly Regex UnescapedCommas = new Regex(@"[^\\](,)", RegexOptions.Compiled); + public override object Deserialize(TextReader tr) + { + if (tr == null) + { + return null; + } + + var value = tr.ReadToEnd(); + + // NOTE: this can deserialize into an IList or simply a string, + // depending on the input text. Anything that uses this serializer should + // be prepared to receive either a string, or an IList. + + var serializeAsList = false; + + // Determine if we can serialize this property + // with multiple values per line. + var co = SerializationContext.Peek() as ICalendarObject; + if (co is ICalendarProperty) + { + serializeAsList = GetService().GetPropertyAllowsMultipleValues(co); + } + + value = TextUtil.Normalize(value, SerializationContext).ReadToEnd(); + + // Try to decode the string + EncodableDataType dt = null; + if (co != null) + { + dt = new EncodableDataType + { + AssociatedObject = co + }; + } + + var escapedValues = new List(128); + var values = new List(128); + + var i = 0; + if (serializeAsList) + { + var matches = UnescapedCommas.Matches(value); + foreach (Match match in matches) + { + var newValue = Decode(dt, value.Substring(i, match.Index - i + 1)); + escapedValues.Add(newValue); + values.Add(Unescape(newValue)); + i = match.Index + 2; + } + } + + if (i < value.Length) + { + var newValue = dt != null ? Decode(dt, value.Substring(i, value.Length - i)) : value.Substring(i, value.Length - i); + escapedValues.Add(newValue); + values.Add(Unescape(newValue)); + } + + if (co is ICalendarProperty) + { + // Determine if our we're supposed to store extra information during + // the serialization process. If so, let's store the escaped value. + var settings = GetService(); + if (settings != null && settings.StoreExtraSerializationData) + { + // Store the escaped value + co.SetService("EscapedValue", escapedValues.Count == 1 ? escapedValues[0] : (object) escapedValues); + } + } + + // Return either a single value, or the entire list. + if (values.Count == 1) + { + return values[0]; + } + return values; + } + } +} \ No newline at end of file diff --git a/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/TimeSpanSerializer.cs b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/TimeSpanSerializer.cs new file mode 100644 index 00000000..a902b931 --- /dev/null +++ b/net-core/Ical.Net/Ical.Net/Serialization/iCalendar/Serializers/Other/TimeSpanSerializer.cs @@ -0,0 +1,124 @@ +using System; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +namespace Ical.Net.Serialization.iCalendar.Serializers.Other +{ + public class TimeSpanSerializer : SerializerBase + { + public override Type TargetType => typeof (TimeSpan); + + public override string SerializeToString(object obj) + { + if (!(obj is TimeSpan)) + { + return null; + } + + var ts = (TimeSpan) obj; + + if (ts == TimeSpan.Zero) + { + return "P0D"; + } + + var sb = new StringBuilder(32); + + if (ts < TimeSpan.Zero) + { + sb.Append("-"); + } + + sb.Append("P"); + if (ts.Days > 7 && ts.Days % 7 == 0 && ts.Hours == 0 && ts.Minutes == 0 && ts.Seconds == 0) + { + sb.Append(Math.Round(Math.Abs((double) ts.Days) / 7) + "W"); + } + else + { + if (ts.Days != 0) + { + sb.Append(Math.Abs(ts.Days) + "D"); + } + if (ts.Hours != 0 || ts.Minutes != 0 || ts.Seconds != 0) + { + sb.Append("T"); + if (ts.Hours != 0) + { + sb.Append(Math.Abs(ts.Hours) + "H"); + } + if (ts.Minutes != 0) + { + sb.Append(Math.Abs(ts.Minutes) + "M"); + } + if (ts.Seconds != 0) + { + sb.Append(Math.Abs(ts.Seconds) + "S"); + } + } + } + + return sb.ToString(); + } + + internal static readonly Regex TimespanMatch = + new Regex(@"^(?\+|-)?P(((?\d+)W)|(?
((?\d+)D)?(?