From d1dc919be7face0aa74084ee431d7ffd06ec6699 Mon Sep 17 00:00:00 2001 From: axunonb Date: Tue, 5 Nov 2024 09:50:09 +0100 Subject: [PATCH 1/2] Unify utf8 file encoding and code style settings * No changes in code logic * Store files with utf8 encoding (set with .gitattributes, rewrite existing files) * Update .editorconfig and apply rules --- .editorconfig | 265 +++++++++++++++++- .gitattributes | 45 ++- Ical.Net.Benchmarks/ApplicationWorkflows.cs | 11 +- Ical.Net.Benchmarks/CalDateTimePerfTests.cs | 11 +- Ical.Net.Benchmarks/OccurencePerfTests.cs | 11 +- Ical.Net.Benchmarks/Runner.cs | 9 +- Ical.Net.Benchmarks/SerializationPerfTests.cs | 13 +- Ical.Net.Benchmarks/ThroughputTests.cs | 9 +- Ical.Net.Tests/AlarmTest.cs | 9 +- Ical.Net.Tests/AttendeeTest.cs | 11 +- Ical.Net.Tests/CalDateTimeTests.cs | 13 +- Ical.Net.Tests/CalendarEventTest.cs | 13 +- Ical.Net.Tests/CalendarPropertiesTest.cs | 13 +- Ical.Net.Tests/CollectionHelpersTests.cs | 9 +- Ical.Net.Tests/ComponentTest.cs | 7 +- .../ConcurrentDeserializationTests.cs | 7 +- Ical.Net.Tests/CopyComponentTests.cs | 17 +- Ical.Net.Tests/DataTypeTest.cs | 13 +- Ical.Net.Tests/DateTimeSerializerTests.cs | 9 +- Ical.Net.Tests/DeserializationTests.cs | 29 +- Ical.Net.Tests/DocumentationExamples.cs | 17 +- Ical.Net.Tests/EqualityAndHashingTests.cs | 15 +- Ical.Net.Tests/FreeBusyTest.cs | 5 + Ical.Net.Tests/GetOccurrenceTests.cs | 19 +- Ical.Net.Tests/IcsFiles.cs | 7 +- Ical.Net.Tests/JournalTest.cs | 7 +- Ical.Net.Tests/ProgramTest.cs | 11 +- Ical.Net.Tests/RecurrenceTests.cs | 72 ++--- Ical.Net.Tests/SerializationHelpers.cs | 7 +- Ical.Net.Tests/SerializationTests.cs | 23 +- Ical.Net.Tests/SimpleDeserializationTests.cs | 31 +- Ical.Net.Tests/SymmetricSerializationTests.cs | 19 +- Ical.Net.Tests/TextUtilTests.cs | 7 +- Ical.Net.Tests/TodoTest.cs | 11 +- Ical.Net.Tests/VTimeZoneTest.cs | 13 +- Ical.Net.Tests/contrib/libical/readme.txt | 2 +- Ical.Net/Calendar.cs | 25 +- Ical.Net/CalendarCollection.cs | 17 +- Ical.Net/CalendarComponents/Alarm.cs | 7 +- .../CalendarComponents/CalendarComponent.cs | 9 +- Ical.Net/CalendarComponents/CalendarEvent.cs | 15 +- Ical.Net/CalendarComponents/FreeBusy.cs | 13 +- .../CalendarComponents/IAlarmContainer.cs | 9 +- .../CalendarComponents/ICalendarComponent.cs | 9 +- Ical.Net/CalendarComponents/IRecurrable.cs | 9 +- .../CalendarComponents/IRecurringComponent.cs | 9 +- .../CalendarComponents/IUniqueComponent.cs | 9 +- Ical.Net/CalendarComponents/Journal.cs | 9 +- .../CalendarComponents/RecurringComponent.cs | 17 +- Ical.Net/CalendarComponents/Todo.cs | 11 +- .../CalendarComponents/UniqueComponent.cs | 13 +- Ical.Net/CalendarComponents/VTimeZone.cs | 13 +- Ical.Net/CalendarExtensions.cs | 7 +- Ical.Net/CalendarObject.cs | 13 +- Ical.Net/CalendarObjectBase.cs | 11 +- Ical.Net/CalendarObjectExtensions.cs | 9 +- Ical.Net/CalendarObjectList.cs | 9 +- Ical.Net/CalendarParameter.cs | 9 +- Ical.Net/CalendarProperty.cs | 9 +- Ical.Net/CalendarPropertyList.cs | 9 +- Ical.Net/Collections/GroupedList.cs | 9 +- Ical.Net/Collections/GroupedListEnumerator.cs | 7 +- Ical.Net/Collections/GroupedValueList.cs | 9 +- Ical.Net/Collections/IGroupedCollection.cs | 7 +- Ical.Net/Collections/IGroupedList.cs | 7 +- Ical.Net/Collections/IGroupedObject.cs | 7 +- Ical.Net/Collections/IMultiLinkedList.cs | 7 +- .../Collections/Interfaces/IValueObject.cs | 7 +- Ical.Net/Collections/MultiLinkedList.cs | 7 +- Ical.Net/Collections/ObjectEventArgs.cs | 7 +- .../Proxies/GroupedCollectionProxy.cs | 7 +- .../Proxies/GroupedValueListProxy.cs | 19 +- Ical.Net/Constants.cs | 9 +- Ical.Net/DataTypes/AlarmOccurrence.cs | 11 +- Ical.Net/DataTypes/Attachment.cs | 13 +- Ical.Net/DataTypes/Attendee.cs | 13 +- Ical.Net/DataTypes/CalDateTime.cs | 15 +- Ical.Net/DataTypes/CalendarDataType.cs | 11 +- Ical.Net/DataTypes/EncodableDataType.cs | 9 +- Ical.Net/DataTypes/FreeBusyEntry.cs | 9 +- Ical.Net/DataTypes/GeographicLocation.cs | 13 +- Ical.Net/DataTypes/ICalendarDataType.cs | 9 +- .../ICalendarParameterCollectionContainer.cs | 9 +- Ical.Net/DataTypes/IDateTime.cs | 9 +- Ical.Net/DataTypes/IEncodableDataType.cs | 9 +- Ical.Net/DataTypes/Occurrence.cs | 11 +- Ical.Net/DataTypes/Organizer.cs | 15 +- Ical.Net/DataTypes/Period.cs | 11 +- Ical.Net/DataTypes/PeriodList.cs | 13 +- Ical.Net/DataTypes/RecurrencePattern.cs | 19 +- Ical.Net/DataTypes/RequestStatus.cs | 11 +- Ical.Net/DataTypes/StatusCode.cs | 13 +- Ical.Net/DataTypes/Trigger.cs | 13 +- Ical.Net/DataTypes/UTCOffset.cs | 11 +- Ical.Net/DataTypes/WeekDay.cs | 13 +- Ical.Net/Evaluation/Evaluator.cs | 11 +- Ical.Net/Evaluation/EventEvaluator.cs | 11 +- Ical.Net/Evaluation/IEvaluator.cs | 9 +- Ical.Net/Evaluation/PeriodListEvaluator.cs | 9 +- .../Evaluation/RecurrencePatternEvaluator.cs | 13 +- Ical.Net/Evaluation/RecurrenceUtil.cs | 13 +- Ical.Net/Evaluation/RecurringEvaluator.cs | 13 +- Ical.Net/Evaluation/TimeZoneEvaluator.cs | 14 +- Ical.Net/Evaluation/TimeZoneInfoEvaluator.cs | 11 +- Ical.Net/Evaluation/TodoEvaluator.cs | 13 +- Ical.Net/ICalendarObject.cs | 9 +- Ical.Net/ICalendarObjectList.cs | 9 +- Ical.Net/ICalendarProperty.cs | 9 +- Ical.Net/ICalendarPropertyListContainer.cs | 9 +- Ical.Net/ICopyable.cs | 9 +- Ical.Net/IGetFreeBusy.cs | 11 +- Ical.Net/IGetOccurrences.cs | 11 +- Ical.Net/ILoadable.cs | 9 +- Ical.Net/IMergeable.cs | 9 +- Ical.Net/IParameterCollection.cs | 9 +- Ical.Net/IServiceProvider.cs | 9 +- Ical.Net/ParameterList.cs | 9 +- Ical.Net/Proxies/CalendarObjectListProxy.cs | 11 +- Ical.Net/Proxies/IUniqueComponentList.cs | 9 +- Ical.Net/Proxies/ParameterCollectionProxy.cs | 11 +- Ical.Net/Proxies/UniqueComponentListProxy.cs | 11 +- .../Serialization/CalendarComponentFactory.cs | 9 +- Ical.Net/Serialization/CalendarSerializer.cs | 11 +- Ical.Net/Serialization/ComponentSerializer.cs | 11 +- Ical.Net/Serialization/DataMapSerializer.cs | 9 +- Ical.Net/Serialization/DataTypeMapper.cs | 11 +- .../DataTypeSerializerFactory.cs | 11 +- .../DataTypes/AttachmentSerializer.cs | 9 +- .../DataTypes/AttendeeSerializer.cs | 9 +- .../DataTypes/DataTypeSerializer.cs | 9 +- .../DataTypes/DateTimeSerializer.cs | 9 +- .../DataTypes/EncodableDataTypeSerializer.cs | 9 +- .../Serialization/DataTypes/EnumSerializer.cs | 9 +- .../DataTypes/FreeBusyEntrySerializer.cs | 9 +- .../DataTypes/GeographicLocationSerializer.cs | 9 +- .../DataTypes/IntegerSerializer.cs | 9 +- .../DataTypes/OrganizerSerializer.cs | 9 +- .../DataTypes/PeriodListSerializer.cs | 9 +- .../DataTypes/PeriodSerializer.cs | 11 +- .../DataTypes/RecurrencePatternSerializer.cs | 13 +- .../DataTypes/RequestStatusSerializer.cs | 9 +- .../DataTypes/StatusCodeSerializer.cs | 9 +- .../DataTypes/StringSerializer.cs | 15 +- .../DataTypes/TimeSpanSerializer.cs | 13 +- .../DataTypes/TriggerSerializer.cs | 15 +- .../Serialization/DataTypes/UriSerializer.cs | 11 +- .../DataTypes/UtcOffsetSerializer.cs | 9 +- .../DataTypes/WeekDaySerializer.cs | 9 +- Ical.Net/Serialization/EncodingProvider.cs | 9 +- Ical.Net/Serialization/EncodingStack.cs | 9 +- Ical.Net/Serialization/EventSerializer.cs | 9 +- .../Serialization/GenericListSerializer.cs | 9 +- Ical.Net/Serialization/IEncodingProvider.cs | 9 +- Ical.Net/Serialization/ISerializer.cs | 9 +- Ical.Net/Serialization/ISerializerFactory.cs | 9 +- Ical.Net/Serialization/IStringSerializer.cs | 9 +- Ical.Net/Serialization/ParameterSerializer.cs | 9 +- Ical.Net/Serialization/PropertySerializer.cs | 13 +- .../Serialization/SerializationContext.cs | 9 +- Ical.Net/Serialization/SerializationUtil.cs | 12 +- Ical.Net/Serialization/SerializerBase.cs | 9 +- Ical.Net/Serialization/SerializerFactory.cs | 13 +- Ical.Net/Serialization/SimpleDeserializer.cs | 13 +- Ical.Net/ServiceProvider.cs | 13 +- Ical.Net/Utility/CollectionHelpers.cs | 7 +- Ical.Net/Utility/DateUtil.cs | 15 +- Ical.Net/Utility/TextUtil.cs | 9 +- Ical.Net/VTimeZoneInfo.cs | 13 +- contributors.md | 4 +- history.md | 6 +- 170 files changed, 1655 insertions(+), 537 deletions(-) diff --git a/.editorconfig b/.editorconfig index 21943c337..52000afa8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,10 +1,265 @@ -root = true +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true -[*] -charset = utf-8-bom -end_of_line = crlf +[*.{cs,xml,csproj,sln}] + +#Core editorconfig formatting - indentation + +#use soft tabs (spaces) for indentation indent_style = space +indent_size = 4 +tab_width = 4 + +#Formatting - new line options + trim_trailing_whitespace = true +insert_final_newline = true +#require members of anonymous types to be on separate lines +csharp_new_line_before_members_in_anonymous_types = true [*.cs] -indent_size = 4 \ No newline at end of file + +#place catch statements on a new line +csharp_new_line_before_catch = true +#place else statements on a new line +csharp_new_line_before_else = true +#require members of object initializers to be on the same line +csharp_new_line_before_members_in_object_initializers = false +#require braces to be on a new line for object_collection_array_initializers, accessors, types, control_blocks, methods, lambdas, and properties (also known as "Allman" style) +csharp_new_line_before_open_brace = object_collection_array_initializers, accessors, types, control_blocks, methods, lambdas, properties + +#Formatting - organize using options + +#sort System.* using directives alphabetically, and place them before other usings +dotnet_sort_system_directives_first = true + +#Formatting - spacing options + +#require a space between a cast and the value +csharp_space_after_cast = true +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_after_colon_in_inheritance_clause = true +#require a space after a keyword in a control flow statement such as a for loop +csharp_space_after_keywords_in_control_flow_statements = true +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_before_colon_in_inheritance_clause = true +#remove space within empty argument list parentheses +csharp_space_between_method_call_empty_parameter_list_parentheses = false +#remove space between method call name and opening parenthesis +csharp_space_between_method_call_name_and_opening_parenthesis = false +#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call +csharp_space_between_method_call_parameter_list_parentheses = false +#remove space within empty parameter list parentheses for a method declaration +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. +csharp_space_between_method_declaration_parameter_list_parentheses = false +#use space after cast and colon and comma and dot and semicolon +csharp_space_after_cast = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_colon_for_base_or_interface_in_type_declaration = true +csharp_space_before_comma = false +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_before_dot = false +csharp_space_after_semicolon_in_for_statement = true +csharp_space_before_semicolon_in_for_statement = false +# use space between brackets +csharp_space_between_empty_square_brackets = false +csharp_space_between_square_brackets = false +# use space before brackets +csharp_space_before_open_square_brackets = false + +#Formatting - wrapping options + +#leave code block on single line +csharp_preserve_single_line_blocks = true +#leave statements and member declarations on the same line +csharp_preserve_single_line_statements = true + +#Style - Code block preferences + +#prefer no curly braces if allowed +csharp_prefer_braces = false:suggestion + +#Style - expression bodied member options + +# Expression-bodied members +csharp_style_expression_bodied_methods = false:suggestion +csharp_style_expression_bodied_properties = true:suggestion +csharp_style_expression_bodied_properties = when_on_single_line:suggestion +#prefer block bodies for constructors +csharp_style_expression_bodied_constructors = false:suggestion +#prefer block bodies for accessors +csharp_style_expression_bodied_accessors = false:suggestion + + +#Style - expression level options + +#prefer out variables to be declared inline in the argument list of a method call when possible +csharp_style_inlined_variable_declaration = true:suggestion +#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_member_access = true:suggestion + +#Style - Expression-level preferences + +#prefer objects to be initialized using object initializers when possible +dotnet_style_object_initializer = true:suggestion + +#Style - implicit and explicit types + +#prefer var over explicit type in all cases, unless overridden by another code style rule +csharp_style_var_elsewhere = true:suggestion +#prefer var is used to declare variables with built-in system types such as int +csharp_style_var_for_built_in_types = true:suggestion +#prefer var when the type is already mentioned on the right-hand side of a declaration expression +csharp_style_var_when_type_is_apparent = true:suggestion + +#Style - language keyword and framework type options + +#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion + +#Style - modifier options + +#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +#Style - Modifier preferences + +#when this rule is set to a list of modifiers, prefer the specified ordering. +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion + +#Style - Pattern matching + +#prefer pattern matching instead of is expression with type casts +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion + +#Style - qualification options + +#prefer events not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_event = false:suggestion +#prefer fields not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_field = false:suggestion +#prefer methods not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_method = false:suggestion +#prefer properties not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_property = false:suggestion + +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_new_line_before_finally = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_parentheses = false +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_prefer_static_local_function = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion + +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = warning +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Copyright file header +file_header_template = \nCopyright ical.net project maintainers and contributors.\nLicensed under the MIT license.\n diff --git a/.gitattributes b/.gitattributes index 0a2cec4b8..a1ffca9ec 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,45 @@ -# Auto detect text files and perform LF normalization -# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/ +# Set the default behavior, in case people don't have core.autocrlf set. * text=auto -*.cs diff=csharp +# Explicitly declare text files to always be normalized and converted +# to native line endings on checkout: +*.csproj text +*.nuspec text +*.sln eol=crlf +*.msg text +*.txt text +*.yml text +*.cs text diff=csharp +*.md text diff=markdown +*.editorconfig text +*.json text +*.xml text +*.bash text eol=lf +*.sh text eol=lf +*.bat text eol=crlf +*.cmd text eol=crlf +*.css text +*.scss text diff=css +*.htm text diff=html +*.html text diff=html +*.sql text +*.js text +*.ts text +# Declare files that will always have CRLF line endings on checkout. +*.sln text eol=crlf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Documents +*.docx diff=astextplain +*.xlsx binary +*.pdf diff=astextplain +*.csv text + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.gif binary +*.jpg binary +*.cdr binary +*.psd binary diff --git a/Ical.Net.Benchmarks/ApplicationWorkflows.cs b/Ical.Net.Benchmarks/ApplicationWorkflows.cs index e3b82b049..19e7b3fee 100644 --- a/Ical.Net.Benchmarks/ApplicationWorkflows.cs +++ b/Ical.Net.Benchmarks/ApplicationWorkflows.cs @@ -1,9 +1,14 @@ -using BenchmarkDotNet.Attributes; -using Ical.Net.DataTypes; +// +// Copyright ical.net project maintainers and contributors. +// Licensed under the MIT license. +// + using System; using System.Collections.Generic; using System.IO; using System.Linq; +using BenchmarkDotNet.Attributes; +using Ical.Net.DataTypes; namespace Ical.Net.Benchmarks { @@ -71,4 +76,4 @@ public List ParallelDeserializeSequentialGatherEventsParallelGetOccu .ToList(); } } -} \ No newline at end of file +} diff --git a/Ical.Net.Benchmarks/CalDateTimePerfTests.cs b/Ical.Net.Benchmarks/CalDateTimePerfTests.cs index 4377f8112..24a26c72b 100644 --- a/Ical.Net.Benchmarks/CalDateTimePerfTests.cs +++ b/Ical.Net.Benchmarks/CalDateTimePerfTests.cs @@ -1,6 +1,11 @@ -using BenchmarkDotNet.Attributes; -using Ical.Net.DataTypes; +// +// Copyright ical.net project maintainers and contributors. +// Licensed under the MIT license. +// + using System; +using BenchmarkDotNet.Attributes; +using Ical.Net.DataTypes; namespace Ical.Net.Benchmarks { @@ -27,4 +32,4 @@ public class CalDateTimePerfTests [Benchmark] public IDateTime UtcToDifferentTzid() => UtcDateTime().ToTimeZone(_bTzid); } -} \ No newline at end of file +} diff --git a/Ical.Net.Benchmarks/OccurencePerfTests.cs b/Ical.Net.Benchmarks/OccurencePerfTests.cs index c397fe3f2..b0c4e8c66 100644 --- a/Ical.Net.Benchmarks/OccurencePerfTests.cs +++ b/Ical.Net.Benchmarks/OccurencePerfTests.cs @@ -1,9 +1,14 @@ -using BenchmarkDotNet.Attributes; -using Ical.Net.CalendarComponents; -using Ical.Net.DataTypes; +// +// Copyright ical.net project maintainers and contributors. +// Licensed under the MIT license. +// + using System; using System.Collections.Generic; using System.Linq; +using BenchmarkDotNet.Attributes; +using Ical.Net.CalendarComponents; +using Ical.Net.DataTypes; namespace Ical.Net.Benchmarks { diff --git a/Ical.Net.Benchmarks/Runner.cs b/Ical.Net.Benchmarks/Runner.cs index f557dc59a..770b73b79 100644 --- a/Ical.Net.Benchmarks/Runner.cs +++ b/Ical.Net.Benchmarks/Runner.cs @@ -1,6 +1,11 @@ -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Running; +// +// Copyright ical.net project maintainers and contributors. +// Licensed under the MIT license. +// + using System.IO; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Running; namespace Ical.Net.Benchmarks { diff --git a/Ical.Net.Benchmarks/SerializationPerfTests.cs b/Ical.Net.Benchmarks/SerializationPerfTests.cs index 62c08fb94..939e102b4 100644 --- a/Ical.Net.Benchmarks/SerializationPerfTests.cs +++ b/Ical.Net.Benchmarks/SerializationPerfTests.cs @@ -1,10 +1,15 @@ -using BenchmarkDotNet.Attributes; -using Ical.Net.CalendarComponents; -using Ical.Net.DataTypes; -using Ical.Net.Serialization; +// +// Copyright ical.net project maintainers and contributors. +// Licensed under the MIT license. +// + using System; using System.Collections.Generic; using System.Linq; +using BenchmarkDotNet.Attributes; +using Ical.Net.CalendarComponents; +using Ical.Net.DataTypes; +using Ical.Net.Serialization; namespace Ical.Net.Benchmarks { diff --git a/Ical.Net.Benchmarks/ThroughputTests.cs b/Ical.Net.Benchmarks/ThroughputTests.cs index f5a8a833f..130c54eef 100644 --- a/Ical.Net.Benchmarks/ThroughputTests.cs +++ b/Ical.Net.Benchmarks/ThroughputTests.cs @@ -1,6 +1,11 @@ -using BenchmarkDotNet.Attributes; +// +// Copyright ical.net project maintainers and contributors. +// Licensed under the MIT license. +// + using System; using System.Linq; +using BenchmarkDotNet.Attributes; namespace Ical.Net.Benchmarks { @@ -64,7 +69,7 @@ rsion 08.00.0681.000"">\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 - """; + private const string SampleEvent = """ + 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 + """; - [Benchmark] - public void Deserialize() => Calendar.Load(SampleEvent).Events.First(); + [Benchmark] + public void Deserialize() => Calendar.Load(SampleEvent).Events.First(); - [Benchmark] - public void BenchmarkSerializeCalendar() => new CalendarSerializer().SerializeToString(CreateSimpleCalendar()); + [Benchmark] + public void BenchmarkSerializeCalendar() => new CalendarSerializer().SerializeToString(CreateSimpleCalendar()); - private static Calendar CreateSimpleCalendar() - { - const string timeZoneId = "America/New_York"; + private static Calendar CreateSimpleCalendar() + { + const string timeZoneId = "America/New_York"; - var simpleCalendar = new Calendar(); - var calendarEvent = new CalendarEvent + var simpleCalendar = new Calendar(); + var calendarEvent = new CalendarEvent + { + Start = new CalDateTime(DateTime.Now, timeZoneId), + End = new CalDateTime(DateTime.Now + TimeSpan.FromHours(1), timeZoneId), + RecurrenceRules = new List { - Start = new CalDateTime(DateTime.Now, timeZoneId), - End = new CalDateTime(DateTime.Now + TimeSpan.FromHours(1), timeZoneId), - RecurrenceRules = new List + new RecurrencePattern(FrequencyType.Daily, 1) { - new RecurrencePattern(FrequencyType.Daily, 1) - { - Count = 100, - } + Count = 100, } - }; + } + }; - simpleCalendar.Events.Add(calendarEvent); - return simpleCalendar; - } + simpleCalendar.Events.Add(calendarEvent); + return simpleCalendar; } -} - +} \ No newline at end of file diff --git a/Ical.Net.Benchmarks/ThroughputTests.cs b/Ical.Net.Benchmarks/ThroughputTests.cs index 130c54eef..c1d557cee 100644 --- a/Ical.Net.Benchmarks/ThroughputTests.cs +++ b/Ical.Net.Benchmarks/ThroughputTests.cs @@ -7,14 +7,14 @@ using System.Linq; using BenchmarkDotNet.Attributes; -namespace Ical.Net.Benchmarks +namespace Ical.Net.Benchmarks; + +public class ThroughputTests { - public class ThroughputTests + [Benchmark] + public void DeserializeAndComputeUntilOccurrences() { - [Benchmark] - public void DeserializeAndComputeUntilOccurrences() - { - const string e = @"BEGIN:VCALENDAR + const string e = @"BEGIN:VCALENDAR PRODID:-//Microsoft Corporation//Outlook 12.0 MIMEDIR//EN VERSION:2.0 METHOD:PUBLISH @@ -66,17 +66,17 @@ rsion 08.00.0681.000"">\n\n\n\n\n\n

This is some HTML formatted text.

\n\n\n"; + [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 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.That(serialized.Contains("X-ALT-DESC;"), Is.True); - } + var serialized = new CalendarSerializer().SerializeToString(calendar); + Assert.That(serialized.Contains("X-ALT-DESC;"), Is.True); + } - [Test] - public void PropertySetValueMustAllowNull() - { - var property = new CalendarProperty(); - Assert.DoesNotThrow(() => property.SetValue(null)); - } + [Test] + public void PropertySetValueMustAllowNull() + { + var property = new CalendarProperty(); + Assert.DoesNotThrow(() => property.SetValue(null)); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/CollectionHelpersTests.cs b/Ical.Net.Tests/CollectionHelpersTests.cs index 33cbdf563..d152e8629 100644 --- a/Ical.Net.Tests/CollectionHelpersTests.cs +++ b/Ical.Net.Tests/CollectionHelpersTests.cs @@ -9,33 +9,32 @@ using Ical.Net.DataTypes; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +internal class CollectionHelpersTests { - 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 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)) } }; + 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() + [Test] + public void ExDateTests() + { + Assert.Multiple(() => { - Assert.Multiple(() => - { - Assert.That(GetExceptionDates(), Is.EqualTo(GetExceptionDates())); - Assert.That(GetExceptionDates(), Is.Not.Null); - Assert.That(GetExceptionDates(), Is.Not.EqualTo(null)); - }); + Assert.That(GetExceptionDates(), Is.EqualTo(GetExceptionDates())); + Assert.That(GetExceptionDates(), Is.Not.Null); + Assert.That(GetExceptionDates(), Is.Not.EqualTo(null)); + }); - var changedPeriod = GetExceptionDates(); - changedPeriod.First().First().StartTime = new CalDateTime(_now.AddHours(-1)); + var changedPeriod = GetExceptionDates(); + changedPeriod.First().First().StartTime = new CalDateTime(_now.AddHours(-1)); - Assert.That(changedPeriod, Is.Not.EqualTo(GetExceptionDates())); - } + Assert.That(changedPeriod, Is.Not.EqualTo(GetExceptionDates())); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/ComponentTest.cs b/Ical.Net.Tests/ComponentTest.cs index b96f86982..eef17a8be 100644 --- a/Ical.Net.Tests/ComponentTest.cs +++ b/Ical.Net.Tests/ComponentTest.cs @@ -8,44 +8,43 @@ using Ical.Net.DataTypes; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +public class ComponentTest { - public class ComponentTest + [Test, Category("Components")] + public void UniqueComponent1() { - [Test, Category("Components")] - public void UniqueComponent1() + var iCal = new Calendar(); + var evt = iCal.Create(); + + Assert.That(evt.Uid, Is.Not.Null); + Assert.That(evt.Created, Is.Null); // We don't want this to be set automatically + Assert.That(evt.DtStamp, Is.Not.Null); + } + + [Test, Category("Components")] + public void ChangeCalDateTimeValue() + { + var e = new CalendarEvent { - var iCal = new Calendar(); - var evt = iCal.Create(); + Start = new CalDateTime(2017, 11, 22, 11, 00, 01), + End = new CalDateTime(2017, 11, 22, 11, 30, 01), + }; + + var firstStartAsUtc = e.Start.AsUtc; + var firstEndAsUtc = e.End.AsUtc; + + e.Start.Value = new DateTime(2017, 11, 22, 11, 30, 01); + e.End.Value = new DateTime(2017, 11, 22, 12, 00, 01); - Assert.That(evt.Uid, Is.Not.Null); - Assert.That(evt.Created, Is.Null); // We don't want this to be set automatically - Assert.That(evt.DtStamp, Is.Not.Null); - } + var secondStartAsUtc = e.Start.AsUtc; + var secondEndAsUtc = e.End.AsUtc; - [Test, Category("Components")] - public void ChangeCalDateTimeValue() + Assert.Multiple(() => { - var e = new CalendarEvent - { - Start = new CalDateTime(2017, 11, 22, 11, 00, 01), - End = new CalDateTime(2017, 11, 22, 11, 30, 01), - }; - - var firstStartAsUtc = e.Start.AsUtc; - var firstEndAsUtc = e.End.AsUtc; - - e.Start.Value = new DateTime(2017, 11, 22, 11, 30, 01); - e.End.Value = new DateTime(2017, 11, 22, 12, 00, 01); - - var secondStartAsUtc = e.Start.AsUtc; - var secondEndAsUtc = e.End.AsUtc; - - Assert.Multiple(() => - { - Assert.That(secondStartAsUtc, Is.Not.EqualTo(firstStartAsUtc)); - Assert.That(secondEndAsUtc, Is.Not.EqualTo(firstEndAsUtc)); - }); - } + Assert.That(secondStartAsUtc, Is.Not.EqualTo(firstStartAsUtc)); + Assert.That(secondEndAsUtc, Is.Not.EqualTo(firstEndAsUtc)); + }); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/ConcurrentDeserializationTests.cs b/Ical.Net.Tests/ConcurrentDeserializationTests.cs index 4076d114b..e839a618a 100644 --- a/Ical.Net.Tests/ConcurrentDeserializationTests.cs +++ b/Ical.Net.Tests/ConcurrentDeserializationTests.cs @@ -7,26 +7,25 @@ using System.Linq; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +public class ConcurrentDeserializationTests { - public class ConcurrentDeserializationTests + [Test] + public void ConcurrentDeserialization_Test() { - [Test] - public void ConcurrentDeserialization_Test() + // https://github.com/rianjs/ical.net/issues/40 + var calendars = new List { - // https://github.com/rianjs/ical.net/issues/40 - var calendars = new List - { - IcsFiles.DailyCount2, - IcsFiles.DailyInterval2, - IcsFiles.DailyByDay1, - IcsFiles.RecurrenceDates1, - IcsFiles.DailyByHourMinute1, - }; + IcsFiles.DailyCount2, + IcsFiles.DailyInterval2, + IcsFiles.DailyByDay1, + IcsFiles.RecurrenceDates1, + IcsFiles.DailyByHourMinute1, + }; - var deserializedCalendars = calendars.AsParallel().SelectMany(CalendarCollection.Load); - var materialized = deserializedCalendars.ToList(); - Assert.That(materialized, Has.Count.EqualTo(5)); - } + var deserializedCalendars = calendars.AsParallel().SelectMany(CalendarCollection.Load); + var materialized = deserializedCalendars.ToList(); + Assert.That(materialized, Has.Count.EqualTo(5)); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/CopyComponentTests.cs b/Ical.Net.Tests/CopyComponentTests.cs index ecfb72bd0..225b3bb66 100644 --- a/Ical.Net.Tests/CopyComponentTests.cs +++ b/Ical.Net.Tests/CopyComponentTests.cs @@ -12,192 +12,191 @@ using Ical.Net.Serialization; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +/// +/// Tests for deep copying of ICal components. +/// +[TestFixture] +public class CopyComponentTests { - /// - /// Tests for deep copying of ICal components. - /// - [TestFixture] - public class CopyComponentTests + [Test, TestCaseSource(nameof(CopyCalendarTest_TestCases)), Category("Copy tests")] + public void CopyCalendarTest(string calendarString) + { + var iCal1 = Calendar.Load(calendarString); + 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 { - [Test, TestCaseSource(nameof(CopyCalendarTest_TestCases)), Category("Copy tests")] - public void CopyCalendarTest(string calendarString) + 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 CopyCalendarEventTest() + { + var orig = GetSimpleEvent(); + orig.Uid = "Hello"; + orig.Summary = "Original summary"; + orig.Resources = new[] { "A", "B" }; + orig.GeographicLocation = new GeographicLocation(48.210033, 16.363449); + orig.Transparency = TransparencyType.Opaque; + orig.Attachments.Add(new Attachment("https://original.org/")); + var copy = orig.Copy(); + + copy.Uid = "Goodbye"; + copy.Summary = "Copy summary"; + + var resourcesCopyFromOrig = new List(copy.Resources); + copy.Resources = new[] { "C", "D" }; + copy.Attachments[0].Uri = new Uri("https://copy.org/"); + const string uidPattern = "UID:"; + var serializedOrig = SerializeEvent(orig); + var serializedCopy = SerializeEvent(copy); + + Assert.Multiple(() => + { + // Should be a deep copy and changes only apply to the copy instance + Assert.That(copy.Uid, Is.Not.EqualTo(orig.Uid)); + Assert.That(copy.Summary, Is.Not.EqualTo(orig.Summary)); + Assert.That(copy.Attachments[0].Uri, Is.Not.EqualTo(orig.Attachments[0].Uri)); + Assert.That(copy.Resources[0], Is.Not.EqualTo(orig.Resources[0])); + + Assert.That(resourcesCopyFromOrig, Is.EquivalentTo(orig.Resources)); + Assert.That(copy.GeographicLocation, Is.EqualTo(orig.GeographicLocation)); + Assert.That(copy.Transparency, Is.EqualTo(orig.Transparency)); + + Assert.That(Regex.Matches(serializedOrig, uidPattern, RegexOptions.Compiled, TimeSpan.FromSeconds(100)), Has.Count.EqualTo(1)); + Assert.That(Regex.Matches(serializedCopy, uidPattern, RegexOptions.Compiled, TimeSpan.FromSeconds(100)), Has.Count.EqualTo(1)); + }); + } + + [Test] + public void CopyFreeBusyTest() + { + var orig = new FreeBusy { - var iCal1 = Calendar.Load(calendarString); - var iCal2 = iCal1.Copy(); - SerializationTests.CompareCalendars(iCal1, iCal2); - } + Start = new CalDateTime(_now), + End = new CalDateTime(_later), + Entries = { new FreeBusyEntry { Language = "English", StartTime = new CalDateTime(2024, 10, 1), Duration = TimeSpan.FromDays(1), Status = FreeBusyStatus.Busy } } + }; - public static IEnumerable CopyCalendarTest_TestCases() + var copy = orig.Copy(); + + Assert.Multiple(() => { - 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 + // Start/DtStart and End/DtEnd are the same + Assert.That(copy.Start, Is.EqualTo(orig.DtStart)); + Assert.That(copy.End, Is.EqualTo(orig.DtEnd)); + Assert.That(copy.Entries[0].Language, Is.EqualTo(orig.Entries[0].Language)); + Assert.That(copy.Entries[0].StartTime, Is.EqualTo(orig.Entries[0].StartTime)); + Assert.That(copy.Entries[0].Duration, Is.EqualTo(orig.Entries[0].Duration)); + Assert.That(copy.Entries[0].Status, Is.EqualTo(orig.Entries[0].Status)); + }); + } + + [Test] + public void CopyAlarmTest() + { + var orig = new Alarm { - DtStart = new CalDateTime(_now), - DtEnd = new CalDateTime(_later), - Duration = TimeSpan.FromHours(1), + Action = AlarmAction.Display, + Trigger = new Trigger(TimeSpan.FromMinutes(15)), + Description = "Test Alarm" }; - private static string SerializeEvent(CalendarEvent e) => new CalendarSerializer().SerializeToString(new Calendar { Events = { e } }); + var copy = orig.Copy(); - [Test] - public void CopyCalendarEventTest() + Assert.Multiple(() => { - var orig = GetSimpleEvent(); - orig.Uid = "Hello"; - orig.Summary = "Original summary"; - orig.Resources = new[] { "A", "B" }; - orig.GeographicLocation = new GeographicLocation(48.210033, 16.363449); - orig.Transparency = TransparencyType.Opaque; - orig.Attachments.Add(new Attachment("https://original.org/")); - var copy = orig.Copy(); - - copy.Uid = "Goodbye"; - copy.Summary = "Copy summary"; - - var resourcesCopyFromOrig = new List(copy.Resources); - copy.Resources = new[] { "C", "D" }; - copy.Attachments[0].Uri = new Uri("https://copy.org/"); - const string uidPattern = "UID:"; - var serializedOrig = SerializeEvent(orig); - var serializedCopy = SerializeEvent(copy); - - Assert.Multiple(() => - { - // Should be a deep copy and changes only apply to the copy instance - Assert.That(copy.Uid, Is.Not.EqualTo(orig.Uid)); - Assert.That(copy.Summary, Is.Not.EqualTo(orig.Summary)); - Assert.That(copy.Attachments[0].Uri, Is.Not.EqualTo(orig.Attachments[0].Uri)); - Assert.That(copy.Resources[0], Is.Not.EqualTo(orig.Resources[0])); - - Assert.That(resourcesCopyFromOrig, Is.EquivalentTo(orig.Resources)); - Assert.That(copy.GeographicLocation, Is.EqualTo(orig.GeographicLocation)); - Assert.That(copy.Transparency, Is.EqualTo(orig.Transparency)); - - Assert.That(Regex.Matches(serializedOrig, uidPattern, RegexOptions.Compiled, TimeSpan.FromSeconds(100)), Has.Count.EqualTo(1)); - Assert.That(Regex.Matches(serializedCopy, uidPattern, RegexOptions.Compiled, TimeSpan.FromSeconds(100)), Has.Count.EqualTo(1)); - }); - } - - [Test] - public void CopyFreeBusyTest() + Assert.That(copy.Action, Is.EqualTo(orig.Action)); + Assert.That(copy.Trigger, Is.EqualTo(orig.Trigger)); + Assert.That(copy.Description, Is.EqualTo(orig.Description)); + }); + } + + [Test] + public void CopyTodoTest() + { + var orig = new Todo { - var orig = new FreeBusy - { - Start = new CalDateTime(_now), - End = new CalDateTime(_later), - Entries = { new FreeBusyEntry { Language = "English", StartTime = new CalDateTime(2024, 10, 1), Duration = TimeSpan.FromDays(1), Status = FreeBusyStatus.Busy } } - }; - - var copy = orig.Copy(); - - Assert.Multiple(() => - { - // Start/DtStart and End/DtEnd are the same - Assert.That(copy.Start, Is.EqualTo(orig.DtStart)); - Assert.That(copy.End, Is.EqualTo(orig.DtEnd)); - Assert.That(copy.Entries[0].Language, Is.EqualTo(orig.Entries[0].Language)); - Assert.That(copy.Entries[0].StartTime, Is.EqualTo(orig.Entries[0].StartTime)); - Assert.That(copy.Entries[0].Duration, Is.EqualTo(orig.Entries[0].Duration)); - Assert.That(copy.Entries[0].Status, Is.EqualTo(orig.Entries[0].Status)); - }); - } - - [Test] - public void CopyAlarmTest() + Summary = "Test Todo", + Description = "This is a test todo", + Due = new CalDateTime(DateTime.Now.AddDays(10)), + Priority = 1, + Contacts = new[] { "John", "Paul" }, + Status = "NeedsAction" + }; + + var copy = orig.Copy(); + + Assert.Multiple(() => { - var orig = new Alarm - { - Action = AlarmAction.Display, - Trigger = new Trigger(TimeSpan.FromMinutes(15)), - Description = "Test Alarm" - }; - - var copy = orig.Copy(); - - Assert.Multiple(() => - { - Assert.That(copy.Action, Is.EqualTo(orig.Action)); - Assert.That(copy.Trigger, Is.EqualTo(orig.Trigger)); - Assert.That(copy.Description, Is.EqualTo(orig.Description)); - }); - } - - [Test] - public void CopyTodoTest() + Assert.That(copy.Summary, Is.EqualTo(orig.Summary)); + Assert.That(copy.Description, Is.EqualTo(orig.Description)); + Assert.That(copy.Due, Is.EqualTo(orig.Due)); + Assert.That(copy.Priority, Is.EqualTo(orig.Priority)); + Assert.That(copy.Contacts, Is.EquivalentTo(orig.Contacts)); + Assert.That(copy.Status, Is.EqualTo(orig.Status)); + }); + } + + [Test] + public void CopyJournalTest() + { + var orig = new Journal { - var orig = new Todo - { - Summary = "Test Todo", - Description = "This is a test todo", - Due = new CalDateTime(DateTime.Now.AddDays(10)), - Priority = 1, - Contacts = new[] { "John", "Paul" }, - Status = "NeedsAction" - }; - - var copy = orig.Copy(); - - Assert.Multiple(() => - { - Assert.That(copy.Summary, Is.EqualTo(orig.Summary)); - Assert.That(copy.Description, Is.EqualTo(orig.Description)); - Assert.That(copy.Due, Is.EqualTo(orig.Due)); - Assert.That(copy.Priority, Is.EqualTo(orig.Priority)); - Assert.That(copy.Contacts, Is.EquivalentTo(orig.Contacts)); - Assert.That(copy.Status, Is.EqualTo(orig.Status)); - }); - } - - [Test] - public void CopyJournalTest() + Summary = "Test Journal", + Description = "This is a test journal", + DtStart = new CalDateTime(DateTime.Now), + Categories = new List { "Category1", "Category2" }, + Priority = 1, + Status = "Draft" + }; + + var copy = orig.Copy(); + + Assert.Multiple(() => { - var orig = new Journal - { - Summary = "Test Journal", - Description = "This is a test journal", - DtStart = new CalDateTime(DateTime.Now), - Categories = new List { "Category1", "Category2" }, - Priority = 1, - Status = "Draft" - }; - - var copy = orig.Copy(); - - Assert.Multiple(() => - { - Assert.That(copy.Summary, Is.EqualTo(orig.Summary)); - Assert.That(copy.Description, Is.EqualTo(orig.Description)); - Assert.That(copy.DtStart, Is.EqualTo(orig.DtStart)); - Assert.That(copy.Categories, Is.EquivalentTo(orig.Categories)); - Assert.That(copy.Priority, Is.EqualTo(orig.Priority)); - Assert.That(copy.Status, Is.EqualTo(orig.Status)); - }); - } + Assert.That(copy.Summary, Is.EqualTo(orig.Summary)); + Assert.That(copy.Description, Is.EqualTo(orig.Description)); + Assert.That(copy.DtStart, Is.EqualTo(orig.DtStart)); + Assert.That(copy.Categories, Is.EquivalentTo(orig.Categories)); + Assert.That(copy.Priority, Is.EqualTo(orig.Priority)); + Assert.That(copy.Status, Is.EqualTo(orig.Status)); + }); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/DataTypeTest.cs b/Ical.Net.Tests/DataTypeTest.cs index 711796918..c24e2f4b0 100644 --- a/Ical.Net.Tests/DataTypeTest.cs +++ b/Ical.Net.Tests/DataTypeTest.cs @@ -6,22 +6,21 @@ using Ical.Net.DataTypes; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +[TestFixture] +public class DataTypeTest { - [TestFixture] - public class DataTypeTest + [Test, Category("DataType")] + public void OrganizerConstructorMustAcceptNull() { - [Test, Category("DataType")] - public void OrganizerConstructorMustAcceptNull() - { - Assert.DoesNotThrow(() => { var o = new Organizer(null); }); - } + Assert.DoesNotThrow(() => { var o = new Organizer(null); }); + } - [Test, Category("DataType")] - public void AttachmentConstructorMustAcceptNull() - { - Assert.DoesNotThrow(() => { var o = new Attachment((byte[]) null); }); - Assert.DoesNotThrow(() => { var o = new Attachment((string) null); }); - } + [Test, Category("DataType")] + public void AttachmentConstructorMustAcceptNull() + { + Assert.DoesNotThrow(() => { var o = new Attachment((byte[]) null); }); + Assert.DoesNotThrow(() => { var o = new Attachment((string) null); }); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/DateTimeSerializerTests.cs b/Ical.Net.Tests/DateTimeSerializerTests.cs index 2d94df55e..888eea6a5 100644 --- a/Ical.Net.Tests/DateTimeSerializerTests.cs +++ b/Ical.Net.Tests/DateTimeSerializerTests.cs @@ -8,21 +8,20 @@ using Ical.Net.Serialization.DataTypes; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +[TestFixture] +public class DateTimeSerializerTests { - [TestFixture] - public class DateTimeSerializerTests + [Test, Category("Deserialization")] + public void TZIDPropertyShouldBeAppliedForLocalTimezones() { - [Test, Category("Deserialization")] - public void TZIDPropertyShouldBeAppliedForLocalTimezones() - { - // see http://www.ietf.org/rfc/rfc2445.txt p.36 - var result = new DateTimeSerializer() - .SerializeToString( + // see http://www.ietf.org/rfc/rfc2445.txt p.36 + var result = new 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.That(result, Is.EqualTo("19970714T133000")); - } + // TZID is applied elsewhere - just make sure this doesn't have 'Z' appended. + Assert.That(result, Is.EqualTo("19970714T133000")); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/DeserializationTests.cs b/Ical.Net.Tests/DeserializationTests.cs index 531f838aa..57ca53aa0 100644 --- a/Ical.Net.Tests/DeserializationTests.cs +++ b/Ical.Net.Tests/DeserializationTests.cs @@ -15,259 +15,259 @@ using Ical.Net.Serialization.DataTypes; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +[TestFixture, Category("Deserialization")] +public class DeserializationTests { - [TestFixture, Category("Deserialization")] - public class DeserializationTests + [Test] + public void Attendee1() { - [Test] - public void Attendee1() + var iCal = Calendar.Load(IcsFiles.Attendee1); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); + + var evt = iCal.Events.First(); + // Ensure there are 2 attendees + Assert.That(evt.Attendees, Has.Count.EqualTo(2)); + + var attendee1 = evt.Attendees[0]; + var attendee2 = evt.Attendees[1]; + + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.Attendee1); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); + // Values + Assert.That(attendee1.Value, Is.EqualTo(new Uri("mailto:joecool@example.com"))); + Assert.That(attendee2.Value, Is.EqualTo(new Uri("mailto:ildoit@example.com"))); + + // MEMBERS + Assert.That(attendee1.Members, Has.Count.EqualTo(1)); + Assert.That(attendee2.Members.Count, Is.EqualTo(0)); + }); + Assert.Multiple(() => + { + Assert.That(attendee1.Members[0], Is.EqualTo(new Uri("mailto:DEV-GROUP@example.com").ToString())); + + // DELEGATED-FROM + Assert.That(attendee1.DelegatedFrom.Count, Is.EqualTo(0)); + Assert.That(attendee2.DelegatedFrom, Has.Count.EqualTo(1)); + Assert.That(attendee2.DelegatedFrom[0], Is.EqualTo(new Uri("mailto:immud@example.com").ToString())); + }); + Assert.Multiple(() => + { + // DELEGATED-TO + Assert.That(attendee1.DelegatedTo.Count, Is.EqualTo(0)); + Assert.That(attendee2.DelegatedTo.Count, Is.EqualTo(0)); + }); + } - var evt = iCal.Events.First(); - // Ensure there are 2 attendees - Assert.That(evt.Attendees, Has.Count.EqualTo(2)); + /// + /// Tests that multiple parameters of the + /// same name are correctly aggregated into + /// a single list. + /// + [Test] + public void Attendee2() + { + var iCal = Calendar.Load(IcsFiles.Attendee2); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); - var attendee1 = evt.Attendees[0]; - var attendee2 = evt.Attendees[1]; + var evt = iCal.Events.First(); + // Ensure there is 1 attendee + Assert.That(evt.Attendees, Has.Count.EqualTo(1)); - Assert.Multiple(() => - { - // Values - Assert.That(attendee1.Value, Is.EqualTo(new Uri("mailto:joecool@example.com"))); - Assert.That(attendee2.Value, Is.EqualTo(new Uri("mailto:ildoit@example.com"))); + var attendee1 = evt.Attendees; - // MEMBERS - Assert.That(attendee1.Members, Has.Count.EqualTo(1)); - Assert.That(attendee2.Members.Count, Is.EqualTo(0)); - }); - Assert.Multiple(() => - { - Assert.That(attendee1.Members[0], Is.EqualTo(new Uri("mailto:DEV-GROUP@example.com").ToString())); + // Values + Assert.That(attendee1[0].Value, Is.EqualTo(new Uri("mailto:joecool@example.com"))); - // DELEGATED-FROM - Assert.That(attendee1.DelegatedFrom.Count, Is.EqualTo(0)); - Assert.That(attendee2.DelegatedFrom, Has.Count.EqualTo(1)); - Assert.That(attendee2.DelegatedFrom[0], Is.EqualTo(new Uri("mailto:immud@example.com").ToString())); - }); - Assert.Multiple(() => - { - // DELEGATED-TO - Assert.That(attendee1.DelegatedTo.Count, Is.EqualTo(0)); - Assert.That(attendee2.DelegatedTo.Count, Is.EqualTo(0)); - }); - } + Assert.Multiple(() => + { + // MEMBERS + Assert.That(attendee1[0].Members, Has.Count.EqualTo(3)); + Assert.That(attendee1[0].Members[0], Is.EqualTo(new Uri("mailto:DEV-GROUP@example.com").ToString())); + Assert.That(attendee1[0].Members[1], Is.EqualTo(new Uri("mailto:ANOTHER-GROUP@example.com").ToString())); + Assert.That(attendee1[0].Members[2], Is.EqualTo(new Uri("mailto:THIRD-GROUP@example.com").ToString())); + }); + } - /// - /// Tests that multiple parameters of the - /// same name are correctly aggregated into - /// a single list. - /// - [Test] - public void Attendee2() + /// + /// 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] + public void Bug2033495() + { + var iCal = Calendar.Load(IcsFiles.Bug2033495); + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.Attendee2); Assert.That(iCal.Events, Has.Count.EqualTo(1)); + Assert.That(iCal.Properties["X-LOTUS-CHILD_UID"].Value, Is.EqualTo("XXX")); + }); + } - var evt = iCal.Events.First(); - // Ensure there is 1 attendee - Assert.That(evt.Attendees, Has.Count.EqualTo(1)); - - var attendee1 = evt.Attendees; - - // Values - Assert.That(attendee1[0].Value, Is.EqualTo(new Uri("mailto:joecool@example.com"))); + /// + /// 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] + public void Bug2938007() + { + var iCal = Calendar.Load(IcsFiles.Bug2938007); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); - Assert.Multiple(() => - { - // MEMBERS - Assert.That(attendee1[0].Members, Has.Count.EqualTo(3)); - Assert.That(attendee1[0].Members[0], Is.EqualTo(new Uri("mailto:DEV-GROUP@example.com").ToString())); - Assert.That(attendee1[0].Members[1], Is.EqualTo(new Uri("mailto:ANOTHER-GROUP@example.com").ToString())); - Assert.That(attendee1[0].Members[2], Is.EqualTo(new Uri("mailto:THIRD-GROUP@example.com").ToString())); - }); - } + var evt = iCal.Events.First(); + Assert.Multiple(() => + { + Assert.That(evt.Start.HasTime, Is.EqualTo(true)); + Assert.That(evt.End.HasTime, Is.EqualTo(true)); + }); - /// - /// 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] - public void Bug2033495() + foreach (var o in evt.GetOccurrences(new CalDateTime(2010, 1, 17, 0, 0, 0), new CalDateTime(2010, 2, 1, 0, 0, 0))) { - var iCal = Calendar.Load(IcsFiles.Bug2033495); Assert.Multiple(() => { - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - Assert.That(iCal.Properties["X-LOTUS-CHILD_UID"].Value, Is.EqualTo("XXX")); + Assert.That(o.Period.StartTime.HasTime, Is.EqualTo(true)); + Assert.That(o.Period.EndTime.HasTime, Is.EqualTo(true)); }); } + } - /// - /// 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] - public void Bug2938007() - { - var iCal = Calendar.Load(IcsFiles.Bug2938007); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - - var evt = iCal.Events.First(); - Assert.Multiple(() => - { - Assert.That(evt.Start.HasTime, Is.EqualTo(true)); - Assert.That(evt.End.HasTime, Is.EqualTo(true)); - }); + /// + /// Tests bug #3177278 - Serialize closes stream + /// See https://sourceforge.net/tracker/?func=detail&aid=3177278&group_id=187422&atid=921236 + /// + [Test] + public void Bug3177278() + { + var calendar = new Calendar(); + var serializer = new CalendarSerializer(); - foreach (var o in evt.GetOccurrences(new CalDateTime(2010, 1, 17, 0, 0, 0), new CalDateTime(2010, 2, 1, 0, 0, 0))) - { - Assert.Multiple(() => - { - Assert.That(o.Period.StartTime.HasTime, Is.EqualTo(true)); - Assert.That(o.Period.EndTime.HasTime, Is.EqualTo(true)); - }); - } - } + var ms = new MemoryStream(); + serializer.Serialize(calendar, ms, Encoding.UTF8); - /// - /// Tests bug #3177278 - Serialize closes stream - /// See https://sourceforge.net/tracker/?func=detail&aid=3177278&group_id=187422&atid=921236 - /// - [Test] - public void Bug3177278() - { - var calendar = new Calendar(); - var serializer = new CalendarSerializer(); + Assert.That(ms.CanWrite, Is.True); + } - var ms = new MemoryStream(); - serializer.Serialize(calendar, ms, Encoding.UTF8); + /// + /// Tests that a mixed-case VERSION property is loaded properly + /// + [Test] + public void CaseInsensitive4() + { + var iCal = Calendar.Load(IcsFiles.CaseInsensitive4); + Assert.That(iCal.Version, Is.EqualTo("2.5")); + } - Assert.That(ms.CanWrite, Is.True); - } + [Test] + public void Categories1_2() + { + var iCal = Calendar.Load(IcsFiles.Categories1); + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); - /// - /// Tests that a mixed-case VERSION property is loaded properly - /// - [Test] - public void CaseInsensitive4() + var items = new List(); + items.AddRange(new[] { - var iCal = Calendar.Load(IcsFiles.CaseInsensitive4); - Assert.That(iCal.Version, Is.EqualTo("2.5")); - } + "One", "Two", "Three", + "Four", "Five", "Six", + "Seven", "A string of text with nothing less than a comma, semicolon; and a newline\n." + }); - [Test] - public void Categories1_2() + var found = new Dictionary(); + foreach (var s in evt.Categories.Where(s => items.Contains(s))) { - var iCal = Calendar.Load(IcsFiles.Categories1); - 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.That(found.ContainsKey(item), Is.True, "Event should contain CATEGORY '" + item + "', but it was not found."); - } + found[s] = true; } - [Test] - public void EmptyLines1() + foreach (string item in items) { - var iCal = Calendar.Load(IcsFiles.EmptyLines1); - Assert.That(iCal.Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); + Assert.That(found.ContainsKey(item), Is.True, "Event should contain CATEGORY '" + item + "', but it was not found."); } + } - [Test] - public void EmptyLines2() - { - var calendars = CalendarCollection.Load(IcsFiles.EmptyLines2); - Assert.That(calendars, Has.Count.EqualTo(2)); - Assert.Multiple(() => - { - Assert.That(calendars[0].Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); - Assert.That(calendars[1].Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); - }); - } + [Test] + public void EmptyLines1() + { + var iCal = Calendar.Load(IcsFiles.EmptyLines1); + Assert.That(iCal.Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); + } - /// - /// Verifies that blank lines between components are allowed - /// (as occurs with some applications/parsers - i.e. KOrganizer) - /// - [Test] - public void EmptyLines3() + [Test] + public void EmptyLines2() + { + var calendars = CalendarCollection.Load(IcsFiles.EmptyLines2); + Assert.That(calendars, Has.Count.EqualTo(2)); + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.EmptyLines3); - Assert.That(iCal.Todos, Has.Count.EqualTo(1), "iCalendar should have 1 todo"); - } + Assert.That(calendars[0].Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); + Assert.That(calendars[1].Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); + }); + } - /// - /// Similar to PARSE4 and PARSE5 tests. - /// - [Test] - public void EmptyLines4() - { - var iCal = Calendar.Load(IcsFiles.EmptyLines4); - Assert.That(iCal.Events, Has.Count.EqualTo(28)); - } + /// + /// Verifies that blank lines between components are allowed + /// (as occurs with some applications/parsers - i.e. KOrganizer) + /// + [Test] + public void EmptyLines3() + { + var iCal = Calendar.Load(IcsFiles.EmptyLines3); + Assert.That(iCal.Todos, Has.Count.EqualTo(1), "iCalendar should have 1 todo"); + } - [Test] - public void Encoding2() - { - var iCal = Calendar.Load(IcsFiles.Encoding2); - ProgramTest.TestCal(iCal); - var evt = iCal.Events.First(); - - Assert.That( - evt.Attachments[0].ToString(), - Is.EqualTo("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."), - "Attached value does not match."); - } + /// + /// Similar to PARSE4 and PARSE5 tests. + /// + [Test] + public void EmptyLines4() + { + var iCal = Calendar.Load(IcsFiles.EmptyLines4); + Assert.That(iCal.Events, Has.Count.EqualTo(28)); + } - [Test] - public void Encoding3() - { - var iCal = Calendar.Load(IcsFiles.Encoding3); - ProgramTest.TestCal(iCal); - var evt = iCal.Events.First(); + [Test] + public void Encoding2() + { + var iCal = Calendar.Load(IcsFiles.Encoding2); + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); + + Assert.That( + evt.Attachments[0].ToString(), + Is.EqualTo("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."), + "Attached value does not match."); + } - Assert.Multiple(() => - { - Assert.That(evt.Uid, Is.EqualTo("uuid1153170430406"), "UID should be 'uuid1153170430406'; it is " + evt.Uid); - Assert.That(evt.Sequence, Is.EqualTo(1), "SEQUENCE should be 1; it is " + evt.Sequence); - }); - } + [Test] + public void Encoding3() + { + var iCal = Calendar.Load(IcsFiles.Encoding3); + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); - [Test] - public void Event8() + Assert.Multiple(() => { - var sr = @"BEGIN:VCALENDAR + Assert.That(evt.Uid, Is.EqualTo("uuid1153170430406"), "UID should be 'uuid1153170430406'; it is " + evt.Uid); + Assert.That(evt.Sequence, Is.EqualTo(1), "SEQUENCE should be 1; it is " + evt.Sequence); + }); + } + + [Test] + public void Event8() + { + var sr = @"BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Apple Computer\, Inc//iCal 1.0//EN CALSCALE:GREGORIAN @@ -297,260 +297,259 @@ public void Event8() END:VEVENT END:VCALENDAR "; - var iCal = Calendar.Load(sr); - Assert.That(iCal.Events.Count == 2, Is.True, "There should be 2 events in the parsed calendar"); - Assert.That(iCal.Events["fd940618-45e2-4d19-b118-37fd7a8e3906"], Is.Not.Null, "Event fd940618-45e2-4d19-b118-37fd7a8e3906 should exist in the calendar"); - Assert.That(iCal.Events["ebfbd3e3-cc1e-4a64-98eb-ced2598b3908"], Is.Not.Null, "Event ebfbd3e3-cc1e-4a64-98eb-ced2598b3908 should exist in the calendar"); - } + var iCal = Calendar.Load(sr); + Assert.That(iCal.Events.Count == 2, Is.True, "There should be 2 events in the parsed calendar"); + Assert.That(iCal.Events["fd940618-45e2-4d19-b118-37fd7a8e3906"], Is.Not.Null, "Event fd940618-45e2-4d19-b118-37fd7a8e3906 should exist in the calendar"); + Assert.That(iCal.Events["ebfbd3e3-cc1e-4a64-98eb-ced2598b3908"], Is.Not.Null, "Event ebfbd3e3-cc1e-4a64-98eb-ced2598b3908 should exist in the calendar"); + } + + [Test] + public void GeographicLocation1_2() + { + var iCal = Calendar.Load(IcsFiles.GeographicLocation1); + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); - [Test] - public void GeographicLocation1_2() + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.GeographicLocation1); - ProgramTest.TestCal(iCal); - var evt = iCal.Events.First(); + Assert.That(evt.GeographicLocation.Latitude, Is.EqualTo(37.386013), "Latitude should be 37.386013; it is not."); + Assert.That(evt.GeographicLocation.Longitude, Is.EqualTo(-122.082932), "Longitude should be -122.082932; it is not."); + }); + } - Assert.Multiple(() => - { - Assert.That(evt.GeographicLocation.Latitude, Is.EqualTo(37.386013), "Latitude should be 37.386013; it is not."); - Assert.That(evt.GeographicLocation.Longitude, Is.EqualTo(-122.082932), "Longitude should be -122.082932; it is not."); - }); - } + [Test] + public void Google1() + { + var tzId = "Europe/Berlin"; + var iCal = Calendar.Load(IcsFiles.Google1); + var evt = iCal.Events["594oeajmftl3r9qlkb476rpr3c@google.com"]; + Assert.That(evt, Is.Not.Null); - [Test] - public void Google1() - { - var tzId = "Europe/Berlin"; - var iCal = Calendar.Load(IcsFiles.Google1); - var evt = iCal.Events["594oeajmftl3r9qlkb476rpr3c@google.com"]; - Assert.That(evt, Is.Not.Null); + 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(); - 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) + }; - 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.That(occurrences[i].Period.StartTime, Is.EqualTo(dateTimes[i]), "Event should occur at " + dateTimes[i]); - for (var i = 0; i < dateTimes.Length; i++) - Assert.That(occurrences[i].Period.StartTime, Is.EqualTo(dateTimes[i]), "Event should occur at " + dateTimes[i]); + Assert.That(occurrences, Has.Count.EqualTo(dateTimes.Length), "There should be exactly " + dateTimes.Length + " occurrences; there were " + occurrences.Count); + } - Assert.That(occurrences, Has.Count.EqualTo(dateTimes.Length), "There should be exactly " + dateTimes.Length + " occurrences; there were " + occurrences.Count); - } + /// + /// Tests that valid RDATE properties are parsed correctly. + /// + [Test] + public void RecurrenceDates1() + { + var iCal = Calendar.Load(IcsFiles.RecurrenceDates1); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); + Assert.That(iCal.Events.First().RecurrenceDates, Has.Count.EqualTo(3)); - /// - /// Tests that valid RDATE properties are parsed correctly. - /// - [Test] - public void RecurrenceDates1() + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.RecurrenceDates1); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - Assert.That(iCal.Events.First().RecurrenceDates, Has.Count.EqualTo(3)); + Assert.That(iCal.Events.First().RecurrenceDates[0][0].StartTime, Is.EqualTo((CalDateTime) new DateTime(1997, 7, 14, 12, 30, 0, DateTimeKind.Utc))); + Assert.That(iCal.Events.First().RecurrenceDates[1][0].StartTime, Is.EqualTo((CalDateTime) new DateTime(1996, 4, 3, 2, 0, 0, DateTimeKind.Utc))); + Assert.That(iCal.Events.First().RecurrenceDates[1][0].EndTime, Is.EqualTo((CalDateTime) new DateTime(1996, 4, 3, 4, 0, 0, DateTimeKind.Utc))); + Assert.That(iCal.Events.First().RecurrenceDates[2][0].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 1))); + Assert.That(iCal.Events.First().RecurrenceDates[2][1].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 20))); + Assert.That(iCal.Events.First().RecurrenceDates[2][2].StartTime, Is.EqualTo(new CalDateTime(1997, 2, 17))); + Assert.That(iCal.Events.First().RecurrenceDates[2][3].StartTime, Is.EqualTo(new CalDateTime(1997, 4, 21))); + Assert.That(iCal.Events.First().RecurrenceDates[2][4].StartTime, Is.EqualTo(new CalDateTime(1997, 5, 26))); + Assert.That(iCal.Events.First().RecurrenceDates[2][5].StartTime, Is.EqualTo(new CalDateTime(1997, 7, 4))); + Assert.That(iCal.Events.First().RecurrenceDates[2][6].StartTime, Is.EqualTo(new CalDateTime(1997, 9, 1))); + Assert.That(iCal.Events.First().RecurrenceDates[2][7].StartTime, Is.EqualTo(new CalDateTime(1997, 10, 14))); + Assert.That(iCal.Events.First().RecurrenceDates[2][8].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 28))); + Assert.That(iCal.Events.First().RecurrenceDates[2][9].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 29))); + Assert.That(iCal.Events.First().RecurrenceDates[2][10].StartTime, Is.EqualTo(new CalDateTime(1997, 12, 25))); + }); + } - Assert.Multiple(() => - { - Assert.That(iCal.Events.First().RecurrenceDates[0][0].StartTime, Is.EqualTo((CalDateTime) new DateTime(1997, 7, 14, 12, 30, 0, DateTimeKind.Utc))); - Assert.That(iCal.Events.First().RecurrenceDates[1][0].StartTime, Is.EqualTo((CalDateTime) new DateTime(1996, 4, 3, 2, 0, 0, DateTimeKind.Utc))); - Assert.That(iCal.Events.First().RecurrenceDates[1][0].EndTime, Is.EqualTo((CalDateTime) new DateTime(1996, 4, 3, 4, 0, 0, DateTimeKind.Utc))); - Assert.That(iCal.Events.First().RecurrenceDates[2][0].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 1))); - Assert.That(iCal.Events.First().RecurrenceDates[2][1].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 20))); - Assert.That(iCal.Events.First().RecurrenceDates[2][2].StartTime, Is.EqualTo(new CalDateTime(1997, 2, 17))); - Assert.That(iCal.Events.First().RecurrenceDates[2][3].StartTime, Is.EqualTo(new CalDateTime(1997, 4, 21))); - Assert.That(iCal.Events.First().RecurrenceDates[2][4].StartTime, Is.EqualTo(new CalDateTime(1997, 5, 26))); - Assert.That(iCal.Events.First().RecurrenceDates[2][5].StartTime, Is.EqualTo(new CalDateTime(1997, 7, 4))); - Assert.That(iCal.Events.First().RecurrenceDates[2][6].StartTime, Is.EqualTo(new CalDateTime(1997, 9, 1))); - Assert.That(iCal.Events.First().RecurrenceDates[2][7].StartTime, Is.EqualTo(new CalDateTime(1997, 10, 14))); - Assert.That(iCal.Events.First().RecurrenceDates[2][8].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 28))); - Assert.That(iCal.Events.First().RecurrenceDates[2][9].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 29))); - Assert.That(iCal.Events.First().RecurrenceDates[2][10].StartTime, Is.EqualTo(new CalDateTime(1997, 12, 25))); - }); - } + /// + /// Tests that valid REQUEST-STATUS properties are parsed correctly. + /// + [Test] + public void RequestStatus1() + { + var iCal = Calendar.Load(IcsFiles.RequestStatus1); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); + Assert.That(iCal.Events.First().RequestStatuses, Has.Count.EqualTo(4)); - /// - /// Tests that valid REQUEST-STATUS properties are parsed correctly. - /// - [Test] - public void RequestStatus1() + var rs = iCal.Events.First().RequestStatuses[0]; + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.RequestStatus1); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - Assert.That(iCal.Events.First().RequestStatuses, Has.Count.EqualTo(4)); - - var rs = iCal.Events.First().RequestStatuses[0]; - Assert.Multiple(() => - { - Assert.That(rs.StatusCode.Primary, Is.EqualTo(2)); - Assert.That(rs.StatusCode.Secondary, Is.EqualTo(0)); - Assert.That(rs.Description, Is.EqualTo("Success")); - }); - Assert.That(rs.ExtraData, Is.Null); + Assert.That(rs.StatusCode.Primary, Is.EqualTo(2)); + Assert.That(rs.StatusCode.Secondary, Is.EqualTo(0)); + Assert.That(rs.Description, Is.EqualTo("Success")); + }); + Assert.That(rs.ExtraData, Is.Null); + + rs = iCal.Events.First().RequestStatuses[1]; + Assert.Multiple(() => + { + Assert.That(rs.StatusCode.Primary, Is.EqualTo(3)); + Assert.That(rs.StatusCode.Secondary, Is.EqualTo(1)); + Assert.That(rs.Description, Is.EqualTo("Invalid property value")); + Assert.That(rs.ExtraData, Is.EqualTo("DTSTART:96-Apr-01")); + }); + + rs = iCal.Events.First().RequestStatuses[2]; + Assert.Multiple(() => + { + Assert.That(rs.StatusCode.Primary, Is.EqualTo(2)); + Assert.That(rs.StatusCode.Secondary, Is.EqualTo(8)); + Assert.That(rs.Description, Is.EqualTo(" Success, repeating event ignored. Scheduled as a single event.")); + Assert.That(rs.ExtraData, Is.EqualTo("RRULE:FREQ=WEEKLY;INTERVAL=2")); + }); + + rs = iCal.Events.First().RequestStatuses[3]; + Assert.Multiple(() => + { + Assert.That(rs.StatusCode.Primary, Is.EqualTo(4)); + Assert.That(rs.StatusCode.Secondary, Is.EqualTo(1)); + Assert.That(rs.Description, Is.EqualTo("Event conflict. Date/time is busy.")); + }); + Assert.That(rs.ExtraData, Is.Null); + } - rs = iCal.Events.First().RequestStatuses[1]; - Assert.Multiple(() => - { - Assert.That(rs.StatusCode.Primary, Is.EqualTo(3)); - Assert.That(rs.StatusCode.Secondary, Is.EqualTo(1)); - Assert.That(rs.Description, Is.EqualTo("Invalid property value")); - Assert.That(rs.ExtraData, Is.EqualTo("DTSTART:96-Apr-01")); - }); + /// + /// Tests that string escaping works with Text elements. + /// + [Test] + public void String2() + { + var serializer = new StringSerializer(); + var value = @"test\with\;characters"; + var unescaped = (string) serializer.Deserialize(new StringReader(value)); - rs = iCal.Events.First().RequestStatuses[2]; - Assert.Multiple(() => - { - Assert.That(rs.StatusCode.Primary, Is.EqualTo(2)); - Assert.That(rs.StatusCode.Secondary, Is.EqualTo(8)); - Assert.That(rs.Description, Is.EqualTo(" Success, repeating event ignored. Scheduled as a single event.")); - Assert.That(rs.ExtraData, Is.EqualTo("RRULE:FREQ=WEEKLY;INTERVAL=2")); - }); + Assert.That(unescaped, Is.EqualTo(@"test\with;characters"), "String unescaping was incorrect."); - rs = iCal.Events.First().RequestStatuses[3]; - Assert.Multiple(() => - { - Assert.That(rs.StatusCode.Primary, Is.EqualTo(4)); - Assert.That(rs.StatusCode.Secondary, Is.EqualTo(1)); - Assert.That(rs.Description, Is.EqualTo("Event conflict. Date/time is busy.")); - }); - Assert.That(rs.ExtraData, Is.Null); - } + value = @"C:\Path\To\My\New\Information"; + unescaped = (string) serializer.Deserialize(new StringReader(value)); + Assert.That(unescaped, Is.EqualTo("C:\\Path\\To\\My\new\\Information"), "String unescaping was incorrect."); - /// - /// Tests that string escaping works with Text elements. - /// - [Test] - public void String2() - { - var serializer = new StringSerializer(); - var value = @"test\with\;characters"; - var unescaped = (string) serializer.Deserialize(new StringReader(value)); + value = @"\""This\r\nis\Na\, test\""\;\\;,"; + unescaped = (string) serializer.Deserialize(new StringReader(value)); - Assert.That(unescaped, Is.EqualTo(@"test\with;characters"), "String unescaping was incorrect."); + Assert.That(unescaped, Is.EqualTo("\"This\\r\nis\na, test\";\\;,"), "String unescaping was incorrect."); + } - value = @"C:\Path\To\My\New\Information"; - unescaped = (string) serializer.Deserialize(new StringReader(value)); - Assert.That(unescaped, Is.EqualTo("C:\\Path\\To\\My\new\\Information"), "String unescaping was incorrect."); + [Test] + public void Transparency2() + { + var iCal = Calendar.Load(IcsFiles.Transparency2); - value = @"\""This\r\nis\Na\, test\""\;\\;,"; - unescaped = (string) serializer.Deserialize(new StringReader(value)); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); + var evt = iCal.Events.First(); - Assert.That(unescaped, Is.EqualTo("\"This\\r\nis\na, test\";\\;,"), "String unescaping was incorrect."); - } + Assert.That(evt.Transparency, Is.EqualTo(TransparencyType.Transparent)); + } - [Test] - public void Transparency2() - { - var iCal = Calendar.Load(IcsFiles.Transparency2); + /// + /// Tests that DateTime values that are out-of-range are still parsed correctly + /// and set to the closest representable date/time in .NET. + /// + [Test] + public void DateTime1() + { + var iCal = Calendar.Load(IcsFiles.DateTime1); + Assert.That(iCal.Events, Has.Count.EqualTo(6)); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - var evt = iCal.Events.First(); + var evt = iCal.Events["nc2o66s0u36iesitl2l0b8inn8@google.com"]; + Assert.That(evt, Is.Not.Null); - Assert.That(evt.Transparency, Is.EqualTo(TransparencyType.Transparent)); - } + // The "Created" date is out-of-bounds. It should be coerced to the + // closest representable date/time. + Assert.That(evt.Created.Value, Is.EqualTo(DateTime.MinValue)); + } - /// - /// Tests that DateTime values that are out-of-range are still parsed correctly - /// and set to the closest representable date/time in .NET. - /// - [Test] - public void DateTime1() - { - var iCal = Calendar.Load(IcsFiles.DateTime1); - Assert.That(iCal.Events, Has.Count.EqualTo(6)); + [Test] + public void Language4() + { + var iCal = Calendar.Load(IcsFiles.Language4); + Assert.That(iCal, Is.Not.Null); + } - var evt = iCal.Events["nc2o66s0u36iesitl2l0b8inn8@google.com"]; - Assert.That(evt, Is.Not.Null); + [Test] + public void Outlook2007_LineFolds1() + { + var iCal = Calendar.Load(IcsFiles.Outlook2007LineFolds); + var events = iCal.GetOccurrences(new CalDateTime(2009, 06, 20), new CalDateTime(2009, 06, 22)); + Assert.That(events, Has.Count.EqualTo(1)); + } - // The "Created" date is out-of-bounds. It should be coerced to the - // closest representable date/time. - Assert.That(evt.Created.Value, Is.EqualTo(DateTime.MinValue)); - } + [Test] + 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.Load(IcsFiles.Outlook2007LineFolds); + var events = iCal.GetOccurrences(new CalDateTime(2009, 06, 20), new CalDateTime(2009, 06, 22)).OrderBy(o => o.Period.StartTime).ToList(); + Assert.That(((CalendarEvent) events[0].Source).Location, Is.EqualTo(longName)); + } - [Test] - public void Language4() - { - var iCal = Calendar.Load(IcsFiles.Language4); - Assert.That(iCal, Is.Not.Null); - } + /// + /// Tests that multiple parameters are allowed in iCalObjects + /// + [Test] + public void Parameter1() + { + var iCal = Calendar.Load(IcsFiles.Parameter1); - [Test] - public void Outlook2007_LineFolds1() + var evt = iCal.Events.First(); + IList parms = evt.Properties["DTSTART"].Parameters.AllOf("VALUE").ToList(); + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.Outlook2007LineFolds); - var events = iCal.GetOccurrences(new CalDateTime(2009, 06, 20), new CalDateTime(2009, 06, 22)); - Assert.That(events, Has.Count.EqualTo(1)); - } + Assert.That(parms, Has.Count.EqualTo(2)); + Assert.That(parms[0].Values.First(), Is.EqualTo("DATE")); + Assert.That(parms[1].Values.First(), Is.EqualTo("OTHER")); + }); + } - [Test] - 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.Load(IcsFiles.Outlook2007LineFolds); - var events = iCal.GetOccurrences(new CalDateTime(2009, 06, 20), new CalDateTime(2009, 06, 22)).OrderBy(o => o.Period.StartTime).ToList(); - Assert.That(((CalendarEvent) events[0].Source).Location, Is.EqualTo(longName)); - } + /// + /// Tests that empty parameters are allowed in iCalObjects + /// + [Test] + public void Parameter2() + { + var iCal = Calendar.Load(IcsFiles.Parameter2); + Assert.That(iCal.Events, Has.Count.EqualTo(2)); + } - /// - /// Tests that multiple parameters are allowed in iCalObjects - /// - [Test] - public void Parameter1() + /// + /// Tests a calendar that should fail to properly parse. + /// + [Test] + public void Parse1() + { + Assert.That(() => { - var iCal = Calendar.Load(IcsFiles.Parameter1); - - var evt = iCal.Events.First(); - IList parms = evt.Properties["DTSTART"].Parameters.AllOf("VALUE").ToList(); - Assert.Multiple(() => - { - Assert.That(parms, Has.Count.EqualTo(2)); - Assert.That(parms[0].Values.First(), Is.EqualTo("DATE")); - Assert.That(parms[1].Values.First(), Is.EqualTo("OTHER")); - }); - } + var content = IcsFiles.Parse1; + var iCal = Calendar.Load(content); + }, Throws.Exception.TypeOf()); + } - /// - /// Tests that empty parameters are allowed in iCalObjects - /// - [Test] - public void Parameter2() - { - var iCal = Calendar.Load(IcsFiles.Parameter2); - Assert.That(iCal.Events, Has.Count.EqualTo(2)); - } + /// + /// Tests that multiple properties are allowed in iCalObjects + /// + [Test] + public void Property1() + { + var iCal = Calendar.Load(IcsFiles.Property1); - /// - /// Tests a calendar that should fail to properly parse. - /// - [Test] - public void Parse1() - { - Assert.That(() => - { - var content = IcsFiles.Parse1; - var iCal = Calendar.Load(content); - }, Throws.Exception.TypeOf()); - } + IList props = iCal.Properties.AllOf("VERSION").ToList(); + Assert.That(props, Has.Count.EqualTo(2)); - /// - /// Tests that multiple properties are allowed in iCalObjects - /// - [Test] - public void Property1() + for (var i = 0; i < props.Count; i++) { - var iCal = Calendar.Load(IcsFiles.Property1); - - IList props = iCal.Properties.AllOf("VERSION").ToList(); - Assert.That(props, Has.Count.EqualTo(2)); - - for (var i = 0; i < props.Count; i++) - { - Assert.That(props[i].Value, Is.EqualTo("2." + i)); - } + Assert.That(props[i].Value, Is.EqualTo("2." + i)); } } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/DocumentationExamples.cs b/Ical.Net.Tests/DocumentationExamples.cs index 20c74f5c5..a7aecc6e5 100644 --- a/Ical.Net.Tests/DocumentationExamples.cs +++ b/Ical.Net.Tests/DocumentationExamples.cs @@ -9,125 +9,124 @@ using Ical.Net.DataTypes; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +public class DocumentationExamples { - public class DocumentationExamples + [Test] + public void Daily_Test() { - [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 { - // 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-31T23: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.That(occurrences, Has.Count.EqualTo(12)); - } + DtStart = new CalDateTime(DateTime.Parse("2016-07-01T07:00")), + DtEnd = new CalDateTime(DateTime.Parse("2016-07-01T08:00")), + }; - [Test] - public void EveryOtherTuesdayUntilTheEndOfTheYear_Test() + //Recur daily through the end of the day, July 31, 2016 + var recurrenceRule = new RecurrencePattern(FrequencyType.Daily, 1) { - // 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.That(tuesdays, Has.Count.EqualTo(13)); - } + Until = DateTime.Parse("2016-07-31T23: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.That(occurrences, Has.Count.EqualTo(12)); + } - [Test] - public void FourthThursdayOfNovember_Tests() + [Test] + public void EveryOtherTuesdayUntilTheEndOfTheYear_Test() + { + // An event taking place between 07:00 and 08:00, beginning July 5 (a Tuesday) + var vEvent = new CalendarEvent { - // (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.That(usThanksgivings, Has.Count.EqualTo(17)); - foreach (var thanksgiving in usThanksgivings) - { - Assert.That(thanksgiving.Period.StartTime.DayOfWeek == DayOfWeek.Thursday, Is.True); - } - } + 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); - [Test] - public void DailyExceptSunday_Test() + Assert.That(tuesdays, Has.Count.EqualTo(13)); + } + + [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.That(usThanksgivings, Has.Count.EqualTo(17)); + foreach (var thanksgiving in usThanksgivings) { - //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.That(occurrences, Has.Count.EqualTo(314)); + Assert.That(thanksgiving.Period.StartTime.DayOfWeek == DayOfWeek.Thursday, Is.True); } } -} + + [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.That(occurrences, Has.Count.EqualTo(314)); + } +} \ No newline at end of file diff --git a/Ical.Net.Tests/EqualityAndHashingTests.cs b/Ical.Net.Tests/EqualityAndHashingTests.cs index 3d1f1c23f..a9c926527 100644 --- a/Ical.Net.Tests/EqualityAndHashingTests.cs +++ b/Ical.Net.Tests/EqualityAndHashingTests.cs @@ -14,338 +14,338 @@ using Ical.Net.Utility; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +public class EqualityAndHashingTests { - 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); + 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) + [Test, TestCaseSource(nameof(CalDateTime_TestCases))] + public void CalDateTime_Tests(CalDateTime incomingDt, CalDateTime expectedDt) + { + Assert.Multiple(() => { - Assert.Multiple(() => - { - Assert.That(expectedDt.Value, Is.EqualTo(incomingDt.Value)); - Assert.That(expectedDt.GetHashCode(), Is.EqualTo(incomingDt.GetHashCode())); - Assert.That(expectedDt.TzId, Is.EqualTo(incomingDt.TzId)); - Assert.That(incomingDt.Equals(expectedDt), Is.True); - }); - } + Assert.That(expectedDt.Value, Is.EqualTo(incomingDt.Value)); + Assert.That(expectedDt.GetHashCode(), Is.EqualTo(incomingDt.GetHashCode())); + Assert.That(expectedDt.TzId, Is.EqualTo(incomingDt.TzId)); + Assert.That(incomingDt.Equals(expectedDt), Is.True); + }); + } - public static IEnumerable CalDateTime_TestCases() - { - var nowCalDt = new CalDateTime(_nowTime); - yield return new TestCaseData(nowCalDt, new CalDateTime(_nowTime)).SetName("Now, no time zone"); + 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"); - } + 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(); + [Test] + public void RecurrencePatternTests() + { + var patternA = GetSimpleRecurrencePattern(); + var patternB = GetSimpleRecurrencePattern(); - Assert.That(patternB, Is.EqualTo(patternA)); - Assert.That(patternB.GetHashCode(), Is.EqualTo(patternA.GetHashCode())); - } + Assert.That(patternB, Is.EqualTo(patternA)); + Assert.That(patternB.GetHashCode(), Is.EqualTo(patternA.GetHashCode())); + } - [Test, TestCaseSource(nameof(Event_TestCases))] - public void Event_Tests(CalendarEvent incoming, CalendarEvent expected) + [Test, TestCaseSource(nameof(Event_TestCases))] + public void Event_Tests(CalendarEvent incoming, CalendarEvent expected) + { + Assert.Multiple(() => { - Assert.Multiple(() => - { - Assert.That(expected.DtStart, Is.EqualTo(incoming.DtStart)); - Assert.That(expected.DtEnd, Is.EqualTo(incoming.DtEnd)); - Assert.That(expected.Location, Is.EqualTo(incoming.Location)); - Assert.That(expected.Status, Is.EqualTo(incoming.Status)); - Assert.That(expected.IsActive, Is.EqualTo(incoming.IsActive)); - Assert.That(expected.Duration, Is.EqualTo(incoming.Duration)); - Assert.That(expected.Transparency, Is.EqualTo(incoming.Transparency)); - Assert.That(expected.GetHashCode(), Is.EqualTo(incoming.GetHashCode())); - Assert.That(incoming.Equals(expected), Is.True); - }); - } + Assert.That(expected.DtStart, Is.EqualTo(incoming.DtStart)); + Assert.That(expected.DtEnd, Is.EqualTo(incoming.DtEnd)); + Assert.That(expected.Location, Is.EqualTo(incoming.Location)); + Assert.That(expected.Status, Is.EqualTo(incoming.Status)); + Assert.That(expected.IsActive, Is.EqualTo(incoming.IsActive)); + Assert.That(expected.Duration, Is.EqualTo(incoming.Duration)); + Assert.That(expected.Transparency, Is.EqualTo(incoming.Transparency)); + Assert.That(expected.GetHashCode(), Is.EqualTo(incoming.GetHashCode())); + Assert.That(incoming.Equals(expected), Is.True); + }); + } + + 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 RecurrencePattern GetSimpleRecurrencePattern() => new RecurrencePattern(FrequencyType.Daily, 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 }; - private static CalendarEvent GetSimpleEvent() => new CalendarEvent + var e = new CalendarEvent { DtStart = new CalDateTime(_nowTime), DtEnd = new CalDateTime(_later), Duration = TimeSpan.FromHours(1), + RecurrenceRules = new List { rruleA }, }; - private static string SerializeEvent(CalendarEvent e) => new CalendarSerializer().SerializeToString(new Calendar { Events = { e } }); + var actualCalendar = new Calendar(); + actualCalendar.Events.Add(e); - - public static IEnumerable Event_TestCases() + //Work around referential equality... + var rruleB = new RecurrencePattern(FrequencyType.Daily, 1) { - 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"); - } + Count = 5 + }; - [Test] - public void Calendar_Tests() + var expectedCalendar = new Calendar(); + expectedCalendar.Events.Add(new CalendarEvent { - 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 }, - }; + DtStart = new CalDateTime(_nowTime), + DtEnd = new CalDateTime(_later), + Duration = TimeSpan.FromHours(1), + RecurrenceRules = new List { rruleB }, + }); - var actualCalendar = new Calendar(); - actualCalendar.Events.Add(e); + Assert.Multiple(() => + { + Assert.That(expectedCalendar.GetHashCode(), Is.EqualTo(actualCalendar.GetHashCode())); + Assert.That(actualCalendar.Equals(expectedCalendar), Is.True); + }); + } - //Work around referential equality... - var rruleB = new RecurrencePattern(FrequencyType.Daily, 1) - { - Count = 5 - }; + [Test, TestCaseSource(nameof(VTimeZone_TestCases))] + public void VTimeZone_Tests(VTimeZone actual, VTimeZone expected) + { + Assert.Multiple(() => + { + Assert.That(expected.Url, Is.EqualTo(actual.Url)); + Assert.That(expected.TzId, Is.EqualTo(actual.TzId)); + Assert.That(expected, Is.EqualTo(actual)); + Assert.That(expected.GetHashCode(), Is.EqualTo(actual.GetHashCode())); + }); + } - 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 }, - }); + public static IEnumerable VTimeZone_TestCases() + { + const string nzSt = "New Zealand Standard Time"; + var first = new VTimeZone + { + TzId = nzSt, + }; + var second = new VTimeZone(nzSt); + yield return new TestCaseData(first, second); - Assert.Multiple(() => - { - Assert.That(expectedCalendar.GetHashCode(), Is.EqualTo(actualCalendar.GetHashCode())); - Assert.That(actualCalendar.Equals(expectedCalendar), Is.True); - }); - } + first.Url = new Uri("http://example.com/"); + second.Url = new Uri("http://example.com"); + yield return new TestCaseData(first, second); + } - [Test, TestCaseSource(nameof(VTimeZone_TestCases))] - public void VTimeZone_Tests(VTimeZone actual, VTimeZone expected) + [Test, TestCaseSource(nameof(Attendees_TestCases))] + public void Attendees_Tests(Attendee actual, Attendee expected) + { + Assert.Multiple(() => { - Assert.Multiple(() => - { - Assert.That(expected.Url, Is.EqualTo(actual.Url)); - Assert.That(expected.TzId, Is.EqualTo(actual.TzId)); - Assert.That(expected, Is.EqualTo(actual)); - Assert.That(expected.GetHashCode(), Is.EqualTo(actual.GetHashCode())); - }); - } + Assert.That(actual.GetHashCode(), Is.EqualTo(expected.GetHashCode())); + Assert.That(actual, Is.EqualTo(expected)); + }); + } - public static IEnumerable VTimeZone_TestCases() + public static IEnumerable Attendees_TestCases() + { + var tentative1 = new Attendee("MAILTO:james@example.com") { - const string nzSt = "New Zealand Standard Time"; - var first = new VTimeZone - { - TzId = nzSt, - }; - var second = new VTimeZone(nzSt); - 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) + CommonName = "James Tentative", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Tentative + }; + var tentative2 = new Attendee("MAILTO:james@example.com") { - Assert.Multiple(() => - { - Assert.That(actual.GetHashCode(), Is.EqualTo(expected.GetHashCode())); - Assert.That(actual, Is.EqualTo(expected)); - }); - } + CommonName = "James Tentative", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Tentative + }; + yield return new TestCaseData(tentative1, tentative2).SetName("Simple attendee test case"); - public static IEnumerable Attendees_TestCases() + var complex1 = new Attendee("MAILTO:mary@example.com") { - 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) + 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") { - var a = Calendar.Load(IcsFiles.UsHolidays); - var b = Calendar.Load(IcsFiles.UsHolidays); + 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"); + } - Assert.That(a, Is.Not.Null); - Assert.That(b, Is.Not.Null); - Assert.Multiple(() => - { - Assert.That(b.GetHashCode(), Is.EqualTo(a.GetHashCode())); - Assert.That(b, Is.EqualTo(a)); - }); - } + [Test, TestCaseSource(nameof(CalendarCollection_TestCases))] + public void CalendarCollection_Tests(string rawCalendar) + { + var a = Calendar.Load(IcsFiles.UsHolidays); + var b = Calendar.Load(IcsFiles.UsHolidays); - public static IEnumerable CalendarCollection_TestCases() + Assert.That(a, Is.Not.Null); + Assert.That(b, Is.Not.Null); + Assert.Multiple(() => { - 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)"); - } + Assert.That(b.GetHashCode(), Is.EqualTo(a.GetHashCode())); + Assert.That(b, Is.EqualTo(a)); + }); + } + + 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() + [Test] + public void Resources_Tests() + { + var origContents = new[] { "Foo", "Bar" }; + var e = GetSimpleEvent(); + e.Resources = new List(origContents); + Assert.That(e.Resources.Count == 2, Is.True); + + e.Resources.Add("Baz"); + Assert.That(e.Resources.Count == 3, Is.True); + var serialized = SerializeEvent(e); + Assert.That(serialized.Contains("Baz"), Is.True); + + e.Resources.Remove("Baz"); + Assert.That(e.Resources.Count == 2, Is.True); + serialized = SerializeEvent(e); + Assert.That(serialized.Contains("Baz"), Is.False); + + e.Resources.Add("Hello"); + Assert.That(e.Resources.Contains("Hello"), Is.True); + serialized = SerializeEvent(e); + Assert.That(serialized.Contains("Hello"), Is.True); + + e.Resources.Clear(); + e.Resources.AddRange(origContents); + Assert.That(origContents, Is.EquivalentTo(e.Resources)); + serialized = SerializeEvent(e); + Assert.Multiple(() => { - var origContents = new[] { "Foo", "Bar" }; - var e = GetSimpleEvent(); - e.Resources = new List(origContents); - Assert.That(e.Resources.Count == 2, Is.True); - - e.Resources.Add("Baz"); - Assert.That(e.Resources.Count == 3, Is.True); - var serialized = SerializeEvent(e); - Assert.That(serialized.Contains("Baz"), Is.True); - - e.Resources.Remove("Baz"); - Assert.That(e.Resources.Count == 2, Is.True); - serialized = SerializeEvent(e); + Assert.That(serialized.Contains("Foo"), Is.True); + Assert.That(serialized.Contains("Bar"), Is.True); Assert.That(serialized.Contains("Baz"), Is.False); + Assert.That(serialized.Contains("Hello"), Is.False); + }); + } - e.Resources.Add("Hello"); - Assert.That(e.Resources.Contains("Hello"), Is.True); - serialized = SerializeEvent(e); - Assert.That(serialized.Contains("Hello"), Is.True); - - e.Resources.Clear(); - e.Resources.AddRange(origContents); - Assert.That(origContents, Is.EquivalentTo(e.Resources)); - serialized = SerializeEvent(e); - Assert.Multiple(() => - { - Assert.That(serialized.Contains("Foo"), Is.True); - Assert.That(serialized.Contains("Bar"), Is.True); - Assert.That(serialized.Contains("Baz"), Is.False); - Assert.That(serialized.Contains("Hello"), Is.False); - }); - } - - internal static (byte[] original, byte[] copy) GetAttachments() - { - var payload = Encoding.UTF8.GetBytes("This is an attachment!"); - var payloadCopy = new byte[payload.Length]; - Array.Copy(payload, payloadCopy, payload.Length); - return (payload, payloadCopy); - } + internal static (byte[] original, byte[] copy) GetAttachments() + { + var payload = Encoding.UTF8.GetBytes("This is an attachment!"); + var payloadCopy = new byte[payload.Length]; + Array.Copy(payload, payloadCopy, payload.Length); + return (payload, payloadCopy); + } - [Test, TestCaseSource(nameof(RecurringComponentAttachment_TestCases))] - public void RecurringComponentAttachmentTests(RecurringComponent noAttachment, RecurringComponent withAttachment) - { - var attachments = GetAttachments(); + [Test, TestCaseSource(nameof(RecurringComponentAttachment_TestCases))] + public void RecurringComponentAttachmentTests(RecurringComponent noAttachment, RecurringComponent withAttachment) + { + var attachments = GetAttachments(); - Assert.That(withAttachment, Is.Not.EqualTo(noAttachment)); - Assert.That(withAttachment.GetHashCode(), Is.Not.EqualTo(noAttachment.GetHashCode())); + Assert.That(withAttachment, Is.Not.EqualTo(noAttachment)); + Assert.That(withAttachment.GetHashCode(), Is.Not.EqualTo(noAttachment.GetHashCode())); - noAttachment.Attachments.Add(new Attachment(attachments.copy)); + noAttachment.Attachments.Add(new Attachment(attachments.copy)); - Assert.That(withAttachment, Is.EqualTo(noAttachment)); - Assert.That(withAttachment.GetHashCode(), Is.EqualTo(noAttachment.GetHashCode())); - } + Assert.That(withAttachment, Is.EqualTo(noAttachment)); + Assert.That(withAttachment.GetHashCode(), Is.EqualTo(noAttachment.GetHashCode())); + } - public static IEnumerable RecurringComponentAttachment_TestCases() - { - var attachments = GetAttachments(); - - var journalNoAttach = new Journal { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; - var journalWithAttach = new Journal { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; - journalWithAttach.Attachments.Add(new Attachment(attachments.original)); - yield return new TestCaseData(journalNoAttach, journalWithAttach).SetName("Journal recurring component attachment"); - - var todoNoAttach = new Todo { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; - var todoWithAttach = new Todo { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; - todoWithAttach.Attachments.Add(new Attachment(attachments.original)); - yield return new TestCaseData(todoNoAttach, todoWithAttach).SetName("Todo recurring component attachment"); - - var eventNoAttach = GetSimpleEvent(); - var eventWithAttach = GetSimpleEvent(); - eventWithAttach.Attachments.Add(new Attachment(attachments.original)); - yield return new TestCaseData(eventNoAttach, eventWithAttach).SetName("Event recurring component attachment"); - } + public static IEnumerable RecurringComponentAttachment_TestCases() + { + var attachments = GetAttachments(); + + var journalNoAttach = new Journal { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; + var journalWithAttach = new Journal { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; + journalWithAttach.Attachments.Add(new Attachment(attachments.original)); + yield return new TestCaseData(journalNoAttach, journalWithAttach).SetName("Journal recurring component attachment"); + + var todoNoAttach = new Todo { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; + var todoWithAttach = new Todo { Start = new CalDateTime(_nowTime), Summary = "A summary!", Class = "Some class!" }; + todoWithAttach.Attachments.Add(new Attachment(attachments.original)); + yield return new TestCaseData(todoNoAttach, todoWithAttach).SetName("Todo recurring component attachment"); + + var eventNoAttach = GetSimpleEvent(); + var eventWithAttach = GetSimpleEvent(); + eventWithAttach.Attachments.Add(new Attachment(attachments.original)); + yield return new TestCaseData(eventNoAttach, eventWithAttach).SetName("Event recurring component attachment"); + } - [Test, TestCaseSource(nameof(PeriodTestCases))] - public void PeriodTests(Period a, Period b) + [Test, TestCaseSource(nameof(PeriodTestCases))] + public void PeriodTests(Period a, Period b) + { + Assert.Multiple(() => { - Assert.Multiple(() => - { - Assert.That(b.GetHashCode(), Is.EqualTo(a.GetHashCode())); - Assert.That(b, Is.EqualTo(a)); - }); - } + Assert.That(b.GetHashCode(), Is.EqualTo(a.GetHashCode())); + Assert.That(b, Is.EqualTo(a)); + }); + } - public static IEnumerable PeriodTestCases() - { - yield return new TestCaseData(new Period(new CalDateTime(_nowTime)), new Period(new CalDateTime(_nowTime))) - .SetName("Two identical CalDateTimes are equal"); - } + public static IEnumerable PeriodTestCases() + { + yield return new TestCaseData(new Period(new CalDateTime(_nowTime)), new Period(new CalDateTime(_nowTime))) + .SetName("Two identical CalDateTimes are equal"); + } - [Test] - public void PeriodListTests() - { - var startTimesA = new List + [Test] + public void PeriodListTests() + { + var startTimesA = new List { new DateTime(2017, 03, 02, 06, 00, 00), new DateTime(2017, 03, 03, 06, 00, 00), @@ -387,14 +387,14 @@ public void PeriodListTests() new DateTime(2017, 05, 01, 06, 00, 00), } .Select(dt => new Period(new CalDateTime(dt))).ToList(); - var a = new PeriodList(); - foreach (var period in startTimesA) - { - a.Add(period); - } + var a = new PeriodList(); + foreach (var period in startTimesA) + { + a.Add(period); + } - //Difference from A: first element became the second, and last element became the second-to-last element - var startTimesB = new List + //Difference from A: first element became the second, and last element became the second-to-last element + var startTimesB = new List { new DateTime(2017, 03, 03, 06, 00, 00), new DateTime(2017, 03, 02, 06, 00, 00), @@ -436,70 +436,69 @@ public void PeriodListTests() new DateTime(2017, 04, 28, 06, 00, 00), } .Select(dt => new Period(new CalDateTime(dt))).ToList(); - var b = new PeriodList(); - foreach (var period in startTimesB) - { - b.Add(period); - } - - var collectionEqual = CollectionHelpers.Equals(a, b); - Assert.Multiple(() => - { - Assert.That(collectionEqual, Is.EqualTo(true)); - Assert.That(b.GetHashCode(), Is.EqualTo(a.GetHashCode())); - }); - - var listOfListA = new List { a }; - var listOfListB = new List { b }; - Assert.That(CollectionHelpers.Equals(listOfListA, listOfListB), Is.True); - - var aThenB = new List { a, b }; - var bThenA = new List { b, a }; - Assert.That(CollectionHelpers.Equals(aThenB, bThenA), Is.True); + var b = new PeriodList(); + foreach (var period in startTimesB) + { + b.Add(period); } - [Test] - public void CalDateTimeTests() + var collectionEqual = CollectionHelpers.Equals(a, b); + Assert.Multiple(() => { - var nowLocal = DateTime.Now; - var nowUtc = nowLocal.ToUniversalTime(); + Assert.That(collectionEqual, Is.EqualTo(true)); + Assert.That(b.GetHashCode(), Is.EqualTo(a.GetHashCode())); + }); - var asLocal = new CalDateTime(nowLocal, "America/New_York"); - var asUtc = new CalDateTime(nowUtc, "UTC"); + var listOfListA = new List { a }; + var listOfListB = new List { b }; + Assert.That(CollectionHelpers.Equals(listOfListA, listOfListB), Is.True); - Assert.That(asUtc, Is.Not.EqualTo(asLocal)); - } + var aThenB = new List { a, b }; + var bThenA = new List { b, a }; + Assert.That(CollectionHelpers.Equals(aThenB, bThenA), Is.True); + } - private void TestComparison(Func calOp, Func intOp) - { - int? intSome = 1; - int? intGreater = 2; + [Test] + public void CalDateTimeTests() + { + var nowLocal = DateTime.Now; + var nowUtc = nowLocal.ToUniversalTime(); - var dtSome = new CalDateTime(2018, 1, 1); - var dtGreater = new CalDateTime(2019, 1, 1); + var asLocal = new CalDateTime(nowLocal, "America/New_York"); + var asUtc = new CalDateTime(nowUtc, "UTC"); - Assert.Multiple(() => - { - Assert.That(calOp(null, null), Is.EqualTo(intOp(null, null))); - Assert.That(calOp(null, dtSome), Is.EqualTo(intOp(null, intSome))); - Assert.That(calOp(dtSome, null), Is.EqualTo(intOp(intSome, null))); - Assert.That(calOp(dtSome, dtSome), Is.EqualTo(intOp(intSome, intSome))); - Assert.That(calOp(dtSome, dtGreater), Is.EqualTo(intOp(intSome, intGreater))); - Assert.That(calOp(dtGreater, dtSome), Is.EqualTo(intOp(intGreater, intSome))); - }); - } + Assert.That(asUtc, Is.Not.EqualTo(asLocal)); + } + + private void TestComparison(Func calOp, Func intOp) + { + int? intSome = 1; + int? intGreater = 2; + + var dtSome = new CalDateTime(2018, 1, 1); + var dtGreater = new CalDateTime(2019, 1, 1); - [Test] - public void CalDateTimeComparisonOperatorTests() + Assert.Multiple(() => { - // Assumption: comparison operators on CalDateTime are expected to - // work like operators on Nullable. - TestComparison((dt1, dt2) => dt1 == dt2, (i1, i2) => i1 == i2); - TestComparison((dt1, dt2) => dt1 != dt2, (i1, i2) => i1 != i2); - TestComparison((dt1, dt2) => dt1 > dt2, (i1, i2) => i1 > i2); - TestComparison((dt1, dt2) => dt1 >= dt2, (i1, i2) => i1 >= i2); - TestComparison((dt1, dt2) => dt1 < dt2, (i1, i2) => i1 < i2); - TestComparison((dt1, dt2) => dt1 <= dt2, (i1, i2) => i1 <= i2); - } + Assert.That(calOp(null, null), Is.EqualTo(intOp(null, null))); + Assert.That(calOp(null, dtSome), Is.EqualTo(intOp(null, intSome))); + Assert.That(calOp(dtSome, null), Is.EqualTo(intOp(intSome, null))); + Assert.That(calOp(dtSome, dtSome), Is.EqualTo(intOp(intSome, intSome))); + Assert.That(calOp(dtSome, dtGreater), Is.EqualTo(intOp(intSome, intGreater))); + Assert.That(calOp(dtGreater, dtSome), Is.EqualTo(intOp(intGreater, intSome))); + }); + } + + [Test] + public void CalDateTimeComparisonOperatorTests() + { + // Assumption: comparison operators on CalDateTime are expected to + // work like operators on Nullable. + TestComparison((dt1, dt2) => dt1 == dt2, (i1, i2) => i1 == i2); + TestComparison((dt1, dt2) => dt1 != dt2, (i1, i2) => i1 != i2); + TestComparison((dt1, dt2) => dt1 > dt2, (i1, i2) => i1 > i2); + TestComparison((dt1, dt2) => dt1 >= dt2, (i1, i2) => i1 >= i2); + TestComparison((dt1, dt2) => dt1 < dt2, (i1, i2) => i1 < i2); + TestComparison((dt1, dt2) => dt1 <= dt2, (i1, i2) => i1 <= i2); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/FreeBusyTest.cs b/Ical.Net.Tests/FreeBusyTest.cs index bb9641d15..2371c8ab0 100644 --- a/Ical.Net.Tests/FreeBusyTest.cs +++ b/Ical.Net.Tests/FreeBusyTest.cs @@ -7,32 +7,31 @@ using Ical.Net.DataTypes; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +[TestFixture] +public class FreeBusyTest { - [TestFixture] - public class FreeBusyTest + /// + /// Ensures that GetFreeBusyStatus() return the correct status. + /// + [Test, Category("FreeBusy")] + public void GetFreeBusyStatus1() { - /// - /// Ensures that GetFreeBusyStatus() return the correct status. - /// - [Test, Category("FreeBusy")] - public void GetFreeBusyStatus1() - { - Calendar cal = new Calendar(); + 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); + 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.Multiple(() => - { - Assert.That(freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 7, 59, 59)), Is.EqualTo(FreeBusyStatus.Free)); - Assert.That(freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 8, 0, 0)), Is.EqualTo(FreeBusyStatus.Busy)); - Assert.That(freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 8, 59, 59)), Is.EqualTo(FreeBusyStatus.Busy)); - Assert.That(freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 9, 0, 0)), Is.EqualTo(FreeBusyStatus.Free)); - }); - } + var freeBusy = cal.GetFreeBusy(new CalDateTime(2010, 10, 1, 0, 0, 0), new CalDateTime(2010, 10, 7, 11, 59, 59)); + Assert.Multiple(() => + { + Assert.That(freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 7, 59, 59)), Is.EqualTo(FreeBusyStatus.Free)); + Assert.That(freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 8, 0, 0)), Is.EqualTo(FreeBusyStatus.Busy)); + Assert.That(freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 8, 59, 59)), Is.EqualTo(FreeBusyStatus.Busy)); + Assert.That(freeBusy.GetFreeBusyStatus(new CalDateTime(2010, 10, 1, 9, 0, 0)), Is.EqualTo(FreeBusyStatus.Free)); + }); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/GetOccurrenceTests.cs b/Ical.Net.Tests/GetOccurrenceTests.cs index 378ec3f8e..fe3f91d3d 100644 --- a/Ical.Net.Tests/GetOccurrenceTests.cs +++ b/Ical.Net.Tests/GetOccurrenceTests.cs @@ -11,95 +11,95 @@ using Ical.Net.Evaluation; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +internal class GetOccurrenceTests { - internal class GetOccurrenceTests + public static CalendarCollection GetCalendars(string incoming) => CalendarCollection.Load(incoming); + + [Test] + public void WrongDurationTest() { - public static CalendarCollection GetCalendars(string incoming) => CalendarCollection.Load(incoming); + 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.Multiple(() => + { + Assert.That(firstOccurrence.Period.StartTime, Is.EqualTo(firstStartCopy)); + Assert.That(firstOccurrence.Period.EndTime, Is.EqualTo(firstEndCopy)); + }); + + var secondOccurrence = occurrences.Last(); + var secondStartCopy = secondStart.Copy(); + var secondEndCopy = secondEnd.Copy(); + Assert.Multiple(() => + { + Assert.That(secondOccurrence.Period.StartTime, Is.EqualTo(secondStartCopy)); + Assert.That(secondOccurrence.Period.EndTime, Is.EqualTo(secondEndCopy)); + }); + } - [Test] - public void WrongDurationTest() + [Test] + public void SkippedOccurrenceOnWeeklyPattern() + { + const int evaluationsCount = 1000; + var eventStart = new CalDateTime(new DateTime(2016, 1, 1, 10, 0, 0, DateTimeKind.Utc)); + var eventEnd = new CalDateTime(new DateTime(2016, 1, 1, 11, 0, 0, DateTimeKind.Utc)); + var vEvent = new CalendarEvent { - 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.Multiple(() => - { - Assert.That(firstOccurrence.Period.StartTime, Is.EqualTo(firstStartCopy)); - Assert.That(firstOccurrence.Period.EndTime, Is.EqualTo(firstEndCopy)); - }); - - var secondOccurrence = occurrences.Last(); - var secondStartCopy = secondStart.Copy(); - var secondEndCopy = secondEnd.Copy(); - Assert.Multiple(() => - { - Assert.That(secondOccurrence.Period.StartTime, Is.EqualTo(secondStartCopy)); - Assert.That(secondOccurrence.Period.EndTime, Is.EqualTo(secondEndCopy)); - }); - } + DtStart = eventStart, + DtEnd = eventEnd, + }; - [Test] - public void SkippedOccurrenceOnWeeklyPattern() + var pattern = new RecurrencePattern + { + Frequency = FrequencyType.Weekly, + ByDay = new List { new WeekDay(DayOfWeek.Friday) } + }; + vEvent.RecurrenceRules.Add(pattern); + var calendar = new Calendar(); + calendar.Events.Add(vEvent); + + var intervalStart = eventStart; + var intervalEnd = intervalStart.AddDays(7 * evaluationsCount); + + var occurrences = RecurrenceUtil.GetOccurrences( + recurrable: vEvent, + periodStart: intervalStart, + periodEnd: intervalEnd, + includeReferenceDateInResults: false); + var occurrenceSet = new HashSet(occurrences.Select(o => o.Period.StartTime)); + + Assert.That(occurrenceSet, Has.Count.EqualTo(evaluationsCount)); + + for (var currentOccurrence = intervalStart; currentOccurrence.CompareTo(intervalEnd) < 0; currentOccurrence = (CalDateTime) currentOccurrence.AddDays(7)) { - const int evaluationsCount = 1000; - var eventStart = new CalDateTime(new DateTime(2016, 1, 1, 10, 0, 0, DateTimeKind.Utc)); - var eventEnd = new CalDateTime(new DateTime(2016, 1, 1, 11, 0, 0, DateTimeKind.Utc)); - var vEvent = new CalendarEvent - { - DtStart = eventStart, - DtEnd = eventEnd, - }; - - var pattern = new RecurrencePattern - { - Frequency = FrequencyType.Weekly, - ByDay = new List { new WeekDay(DayOfWeek.Friday) } - }; - vEvent.RecurrenceRules.Add(pattern); - var calendar = new Calendar(); - calendar.Events.Add(vEvent); - - var intervalStart = eventStart; - var intervalEnd = intervalStart.AddDays(7 * evaluationsCount); - - var occurrences = RecurrenceUtil.GetOccurrences( - recurrable: vEvent, - periodStart: intervalStart, - periodEnd: intervalEnd, - includeReferenceDateInResults: false); - var occurrenceSet = new HashSet(occurrences.Select(o => o.Period.StartTime)); - - Assert.That(occurrenceSet, Has.Count.EqualTo(evaluationsCount)); - - for (var currentOccurrence = intervalStart; currentOccurrence.CompareTo(intervalEnd) < 0; currentOccurrence = (CalDateTime) currentOccurrence.AddDays(7)) - { - var contains = occurrenceSet.Contains(currentOccurrence); - Assert.That(contains, Is.True, $"Collection does not contain {currentOccurrence}, but it is a {currentOccurrence.DayOfWeek}"); - } + var contains = occurrenceSet.Contains(currentOccurrence); + Assert.That(contains, Is.True, $"Collection does not contain {currentOccurrence}, but it is a {currentOccurrence.DayOfWeek}"); } + } - [Test] - public void EnumerationChangedException() - { - const string ical = @"BEGIN:VCALENDAR + [Test] + public void EnumerationChangedException() + { + const string ical = @"BEGIN:VCALENDAR PRODID:-//Google Inc//Google Calendar 70.9054//EN VERSION:2.0 CALSCALE:GREGORIAN @@ -143,19 +143,19 @@ public void EnumerationChangedException() END:VCALENDAR"; - var calendar = GetCalendars(ical); - var date = new DateTime(2016, 10, 11); - var occurrences = calendar.GetOccurrences(date); + var calendar = GetCalendars(ical); + var date = new DateTime(2016, 10, 11); + var occurrences = calendar.GetOccurrences(date); - //We really want to make sure this doesn't explode - Assert.That(occurrences, Has.Count.EqualTo(1)); - } + //We really want to make sure this doesn't explode + Assert.That(occurrences, Has.Count.EqualTo(1)); + } - [Test] - public void GetOccurrencesShouldEnumerate() - { - const string ical = - @"BEGIN:VCALENDAR + [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 @@ -207,11 +207,10 @@ public void GetOccurrencesShouldEnumerate() END:VCALENDAR "; - var collection = Calendar.Load(ical); - var startCheck = new DateTime(2016, 11, 11); - var occurrences = collection.GetOccurrences(startCheck, startCheck.AddMonths(1)); + var collection = Calendar.Load(ical); + var startCheck = new DateTime(2016, 11, 11); + var occurrences = collection.GetOccurrences(startCheck, startCheck.AddMonths(1)); - Assert.That(occurrences.Count == 4, Is.True); - } + Assert.That(occurrences.Count == 4, Is.True); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/IcsFiles.cs b/Ical.Net.Tests/IcsFiles.cs index a2e46878f..135080b2f 100644 --- a/Ical.Net.Tests/IcsFiles.cs +++ b/Ical.Net.Tests/IcsFiles.cs @@ -6,167 +6,166 @@ using System.IO; using System.Reflection; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +internal class IcsFiles { - internal class IcsFiles - { - private static readonly Assembly _assembly = typeof(IcsFiles).GetTypeInfo().Assembly; + private static readonly Assembly _assembly = typeof(IcsFiles).GetTypeInfo().Assembly; - internal static string ReadStream(string manifestResource) + internal static string ReadStream(string manifestResource) + { + using (var stream = _assembly.GetManifestResourceStream(manifestResource)) { - using (var stream = _assembly.GetManifestResourceStream(manifestResource)) - { - return new StreamReader(stream).ReadToEnd(); - } + return new StreamReader(stream).ReadToEnd(); } + } - internal static string Alarm1 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM1.ics"); - internal static string Alarm2 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM2.ics"); - internal static string Alarm3 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM3.ics"); - internal static string Alarm4 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM4.ics"); - internal static string Alarm5 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM5.ics"); - internal static string Alarm6 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM6.ics"); - internal static string Alarm7 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM7.ics"); - internal static string Attachment3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Attachment3.ics"); - internal static string Attachment4 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Attachment4.ics"); - internal static string Attendee1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Attendee1.ics"); - internal static string Attendee2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Attendee2.ics"); - internal static string Bug1741093 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug1741093.ics"); - internal static string Bug2033495 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Bug2033495.ics"); - internal static string Bug2148092 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Bug2148092.ics"); - internal static string Bug2912657 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug2912657.ics"); - internal static string Bug2916581 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug2916581.ics"); - internal static string Bug2938007 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Bug2938007.ics"); - internal static string Bug2959692 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug2959692.ics"); - internal static string Bug2966236 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug2966236.ics"); - internal static string Bug3007244 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug3007244.ics"); - internal static string ByMonth1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.ByMonth1.ics"); - internal static string ByMonth2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.ByMonth2.ics"); - internal static string ByMonthDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.ByMonthDay1.ics"); - internal static string Calendar1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Calendar1.ics"); - internal static string CalendarParameters2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.CalendarParameters2.ics"); - internal static string CaseInsensitive1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.CaseInsensitive1.ics"); - internal static string CaseInsensitive2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.CaseInsensitive2.ics"); - internal static string CaseInsensitive3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.CaseInsensitive3.ics"); - internal static string CaseInsensitive4 => ReadStream("Ical.Net.Tests.Calendars.Serialization.CaseInsensitive4.ics"); - internal static string Categories1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Categories1.ics"); - internal static string Daily1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Daily1.ics"); - internal static string DailyByDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyByDay1.ics"); - internal static string DailyByHourMinute1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyByHourMinute1.ics"); - internal static string DailyCount1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyCount1.ics"); - internal static string DailyCount2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyCount2.ics"); - internal static string DailyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyInterval1.ics"); - internal static string DailyInterval2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyInterval2.ics"); - internal static string DailyUntil1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyUntil1.ics"); - internal static string DateTime1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.DateTime1.ics"); - internal static string DateTime2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.DateTime2.ics"); - internal static string Duration1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Duration1.ics"); - internal static string Empty1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Empty1.ics"); - internal static string EmptyLines1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.EmptyLines1.ics"); - internal static string EmptyLines2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.EmptyLines2.ics"); - internal static string EmptyLines3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.EmptyLines3.ics"); - internal static string EmptyLines4 => ReadStream("Ical.Net.Tests.Calendars.Serialization.EmptyLines4.ics"); - internal static string Encoding1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Encoding1.ics"); - internal static string Encoding2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Encoding2.ics"); - internal static string Encoding3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Encoding3.ics"); - internal static string Event1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Event1.ics"); - internal static string Event2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Event2.ics"); - internal static string Event3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Event3.ics"); - internal static string Event4 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Event4.ics"); - internal static string EventStatus => ReadStream("Ical.Net.Tests.Calendars.Serialization.EventStatus.ics"); - internal static string GeographicLocation1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.GeographicLocation1.ics"); - internal static string Google1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Google1.ics"); - internal static string Hourly1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Hourly1.ics"); - internal static string HourlyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.HourlyInterval1.ics"); - internal static string HourlyInterval2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.HourlyInterval2.ics"); - internal static string HourlyUntil1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.HourlyUntil1.ics"); - internal static string Journal1 => ReadStream("Ical.Net.Tests.Calendars.Journal.JOURNAL1.ics"); - internal static string Journal2 => ReadStream("Ical.Net.Tests.Calendars.Journal.JOURNAL2.ics"); - internal static string Language1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Language1.ics"); - internal static string Language2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Language2.ics"); - internal static string Language3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Language3.ics"); - internal static string Language4 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Language4.ics"); - internal static string Minutely1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Minutely1.ics"); - internal static string MinutelyByHour1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyByHour1.ics"); - internal static string MinutelyCount1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyCount1.ics"); - internal static string MinutelyCount2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyCount2.ics"); - internal static string MinutelyCount3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyCount3.ics"); - internal static string MinutelyCount4 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyCount4.ics"); - internal static string MinutelyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyInterval1.ics"); - internal static string Monthly1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Monthly1.ics"); - internal static string MonthlyByDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyByDay1.ics"); - internal static string MonthlyByMonthDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyByMonthDay1.ics"); - internal static string MonthlyByMonthDay2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyByMonthDay2.ics"); - internal static string MonthlyBySetPos1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyBySetPos1.ics"); - internal static string MonthlyBySetPos2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyBySetPos2.ics"); - internal static string MonthlyCountByDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByDay1.ics"); - internal static string MonthlyCountByDay2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByDay2.ics"); - internal static string MonthlyCountByDay3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByDay3.ics"); - internal static string MonthlyCountByMonthDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByMonthDay1.ics"); - internal static string MonthlyCountByMonthDay2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByMonthDay2.ics"); - internal static string MonthlyCountByMonthDay3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByMonthDay3.ics"); - internal static string MonthlyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyInterval1.ics"); - internal static string MonthlyUntilByDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyUntilByDay1.ics"); - internal static string Outlook2007LineFolds => ReadStream("Ical.Net.Tests.Calendars.Serialization.Outlook2007LineFolds.ics"); - internal static string Parameter1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Parameter1.ics"); - internal static string Parameter2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Parameter2.ics"); - internal static string Parse1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Parse1.ics"); - internal static string Parse17 => ReadStream("Ical.Net.Tests.Calendars.Serialization.PARSE17.ics"); - internal static string ProdId1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.ProdID1.ics"); - internal static string ProdId2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.ProdID2.ics"); - internal static string Property1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Property1.ics"); - internal static string RecurrenceDates1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.RecurrenceDates1.ics"); - internal static string RequestStatus1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.RequestStatus1.ics"); - internal static string Secondly1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Secondly1.ics"); - internal static string TimeZone1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.TimeZone1.ics"); - internal static string TimeZone2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.TimeZone2.ics"); - internal static string TimeZone3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.TimeZone3.ics"); - internal static string Todo1 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo1.ics"); - internal static string Todo2 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo2.ics"); - internal static string Todo3 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo3.ics"); - internal static string Todo4 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo4.ics"); - internal static string Todo5 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo5.ics"); - internal static string Todo6 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo6.ics"); - internal static string Todo7 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo7.ics"); - internal static string Todo8 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo8.ics"); - internal static string Todo9 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo9.ics"); - internal static string Transparency1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Transparency1.ics"); - internal static string Transparency2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Transparency2.ics"); - internal static string Trigger1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Trigger1.ics"); - internal static string UsHolidays => ReadStream("Ical.Net.Tests.Calendars.Serialization.USHolidays.ics"); - internal static string WeeklyCount1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyCount1.ics"); - internal static string WeeklyCountWkst1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyCountWkst1.ics"); - internal static string WeeklyCountWkst2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyCountWkst2.ics"); - internal static string WeeklyCountWkst3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyCountWkst3.ics"); - internal static string WeeklyCountWkst4 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyCountWkst4.ics"); - internal static string WeeklyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyInterval1.ics"); - internal static string WeeklyUntil1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyUntil1.ics"); - internal static string WeeklyUntilWkst1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyUntilWkst1.ics"); - internal static string WeeklyUntilWkst2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyUntilWkst2.ics"); - internal static string WeeklyWeekStartsLastYear => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyWeekStartsLastYear.ics"); - internal static string WeeklyWkst1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyWkst1.ics"); - internal static string XProperty1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.XProperty1.ics"); - internal static string XProperty2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.XProperty2.ics"); - internal static string Yearly1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Yearly1.ics"); - internal static string YearlyByDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByDay1.ics"); - internal static string YearlyByMonth1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByMonth1.ics"); - internal static string YearlyByMonth2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByMonth2.ics"); - internal static string YearlyByMonth3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByMonth3.ics"); - internal static string YearlyByMonthDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByMonthDay1.ics"); - internal static string YearlyBySetPos1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyBySetPos1.ics"); - internal static string YearlyByWeekNo1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByWeekNo1.ics"); - internal static string YearlyByWeekNo2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByWeekNo2.ics"); - internal static string YearlyByWeekNo3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByWeekNo3.ics"); - internal static string YearlyByWeekNo4 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByWeekNo4.ics"); - internal static string YearlyByWeekNo5 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByWeekNo5.ics"); - internal static string YearlyComplex1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyComplex1.ics"); - internal static string YearlyCountByMonth1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyCountByMonth1.ics"); - internal static string YearlyCountByYearDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyCountByYearDay1.ics"); - internal static string YearlyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyInterval1.ics"); + internal static string Alarm1 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM1.ics"); + internal static string Alarm2 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM2.ics"); + internal static string Alarm3 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM3.ics"); + internal static string Alarm4 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM4.ics"); + internal static string Alarm5 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM5.ics"); + internal static string Alarm6 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM6.ics"); + internal static string Alarm7 => ReadStream("Ical.Net.Tests.Calendars.Alarm.ALARM7.ics"); + internal static string Attachment3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Attachment3.ics"); + internal static string Attachment4 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Attachment4.ics"); + internal static string Attendee1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Attendee1.ics"); + internal static string Attendee2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Attendee2.ics"); + internal static string Bug1741093 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug1741093.ics"); + internal static string Bug2033495 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Bug2033495.ics"); + internal static string Bug2148092 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Bug2148092.ics"); + internal static string Bug2912657 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug2912657.ics"); + internal static string Bug2916581 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug2916581.ics"); + internal static string Bug2938007 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Bug2938007.ics"); + internal static string Bug2959692 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug2959692.ics"); + internal static string Bug2966236 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug2966236.ics"); + internal static string Bug3007244 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Bug3007244.ics"); + internal static string ByMonth1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.ByMonth1.ics"); + internal static string ByMonth2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.ByMonth2.ics"); + internal static string ByMonthDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.ByMonthDay1.ics"); + internal static string Calendar1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Calendar1.ics"); + internal static string CalendarParameters2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.CalendarParameters2.ics"); + internal static string CaseInsensitive1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.CaseInsensitive1.ics"); + internal static string CaseInsensitive2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.CaseInsensitive2.ics"); + internal static string CaseInsensitive3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.CaseInsensitive3.ics"); + internal static string CaseInsensitive4 => ReadStream("Ical.Net.Tests.Calendars.Serialization.CaseInsensitive4.ics"); + internal static string Categories1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Categories1.ics"); + internal static string Daily1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Daily1.ics"); + internal static string DailyByDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyByDay1.ics"); + internal static string DailyByHourMinute1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyByHourMinute1.ics"); + internal static string DailyCount1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyCount1.ics"); + internal static string DailyCount2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyCount2.ics"); + internal static string DailyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyInterval1.ics"); + internal static string DailyInterval2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyInterval2.ics"); + internal static string DailyUntil1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.DailyUntil1.ics"); + internal static string DateTime1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.DateTime1.ics"); + internal static string DateTime2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.DateTime2.ics"); + internal static string Duration1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Duration1.ics"); + internal static string Empty1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Empty1.ics"); + internal static string EmptyLines1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.EmptyLines1.ics"); + internal static string EmptyLines2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.EmptyLines2.ics"); + internal static string EmptyLines3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.EmptyLines3.ics"); + internal static string EmptyLines4 => ReadStream("Ical.Net.Tests.Calendars.Serialization.EmptyLines4.ics"); + internal static string Encoding1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Encoding1.ics"); + internal static string Encoding2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Encoding2.ics"); + internal static string Encoding3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Encoding3.ics"); + internal static string Event1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Event1.ics"); + internal static string Event2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Event2.ics"); + internal static string Event3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Event3.ics"); + internal static string Event4 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Event4.ics"); + internal static string EventStatus => ReadStream("Ical.Net.Tests.Calendars.Serialization.EventStatus.ics"); + internal static string GeographicLocation1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.GeographicLocation1.ics"); + internal static string Google1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Google1.ics"); + internal static string Hourly1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Hourly1.ics"); + internal static string HourlyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.HourlyInterval1.ics"); + internal static string HourlyInterval2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.HourlyInterval2.ics"); + internal static string HourlyUntil1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.HourlyUntil1.ics"); + internal static string Journal1 => ReadStream("Ical.Net.Tests.Calendars.Journal.JOURNAL1.ics"); + internal static string Journal2 => ReadStream("Ical.Net.Tests.Calendars.Journal.JOURNAL2.ics"); + internal static string Language1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Language1.ics"); + internal static string Language2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Language2.ics"); + internal static string Language3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Language3.ics"); + internal static string Language4 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Language4.ics"); + internal static string Minutely1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Minutely1.ics"); + internal static string MinutelyByHour1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyByHour1.ics"); + internal static string MinutelyCount1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyCount1.ics"); + internal static string MinutelyCount2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyCount2.ics"); + internal static string MinutelyCount3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyCount3.ics"); + internal static string MinutelyCount4 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyCount4.ics"); + internal static string MinutelyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MinutelyInterval1.ics"); + internal static string Monthly1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Monthly1.ics"); + internal static string MonthlyByDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyByDay1.ics"); + internal static string MonthlyByMonthDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyByMonthDay1.ics"); + internal static string MonthlyByMonthDay2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyByMonthDay2.ics"); + internal static string MonthlyBySetPos1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyBySetPos1.ics"); + internal static string MonthlyBySetPos2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyBySetPos2.ics"); + internal static string MonthlyCountByDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByDay1.ics"); + internal static string MonthlyCountByDay2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByDay2.ics"); + internal static string MonthlyCountByDay3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByDay3.ics"); + internal static string MonthlyCountByMonthDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByMonthDay1.ics"); + internal static string MonthlyCountByMonthDay2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByMonthDay2.ics"); + internal static string MonthlyCountByMonthDay3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyCountByMonthDay3.ics"); + internal static string MonthlyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyInterval1.ics"); + internal static string MonthlyUntilByDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.MonthlyUntilByDay1.ics"); + internal static string Outlook2007LineFolds => ReadStream("Ical.Net.Tests.Calendars.Serialization.Outlook2007LineFolds.ics"); + internal static string Parameter1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Parameter1.ics"); + internal static string Parameter2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Parameter2.ics"); + internal static string Parse1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Parse1.ics"); + internal static string Parse17 => ReadStream("Ical.Net.Tests.Calendars.Serialization.PARSE17.ics"); + internal static string ProdId1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.ProdID1.ics"); + internal static string ProdId2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.ProdID2.ics"); + internal static string Property1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Property1.ics"); + internal static string RecurrenceDates1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.RecurrenceDates1.ics"); + internal static string RequestStatus1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.RequestStatus1.ics"); + internal static string Secondly1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Secondly1.ics"); + internal static string TimeZone1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.TimeZone1.ics"); + internal static string TimeZone2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.TimeZone2.ics"); + internal static string TimeZone3 => ReadStream("Ical.Net.Tests.Calendars.Serialization.TimeZone3.ics"); + internal static string Todo1 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo1.ics"); + internal static string Todo2 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo2.ics"); + internal static string Todo3 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo3.ics"); + internal static string Todo4 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo4.ics"); + internal static string Todo5 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo5.ics"); + internal static string Todo6 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo6.ics"); + internal static string Todo7 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo7.ics"); + internal static string Todo8 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo8.ics"); + internal static string Todo9 => ReadStream("Ical.Net.Tests.Calendars.Todo.Todo9.ics"); + internal static string Transparency1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Transparency1.ics"); + internal static string Transparency2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Transparency2.ics"); + internal static string Trigger1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.Trigger1.ics"); + internal static string UsHolidays => ReadStream("Ical.Net.Tests.Calendars.Serialization.USHolidays.ics"); + internal static string WeeklyCount1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyCount1.ics"); + internal static string WeeklyCountWkst1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyCountWkst1.ics"); + internal static string WeeklyCountWkst2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyCountWkst2.ics"); + internal static string WeeklyCountWkst3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyCountWkst3.ics"); + internal static string WeeklyCountWkst4 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyCountWkst4.ics"); + internal static string WeeklyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyInterval1.ics"); + internal static string WeeklyUntil1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyUntil1.ics"); + internal static string WeeklyUntilWkst1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyUntilWkst1.ics"); + internal static string WeeklyUntilWkst2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyUntilWkst2.ics"); + internal static string WeeklyWeekStartsLastYear => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyWeekStartsLastYear.ics"); + internal static string WeeklyWkst1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.WeeklyWkst1.ics"); + internal static string XProperty1 => ReadStream("Ical.Net.Tests.Calendars.Serialization.XProperty1.ics"); + internal static string XProperty2 => ReadStream("Ical.Net.Tests.Calendars.Serialization.XProperty2.ics"); + internal static string Yearly1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.Yearly1.ics"); + internal static string YearlyByDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByDay1.ics"); + internal static string YearlyByMonth1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByMonth1.ics"); + internal static string YearlyByMonth2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByMonth2.ics"); + internal static string YearlyByMonth3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByMonth3.ics"); + internal static string YearlyByMonthDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByMonthDay1.ics"); + internal static string YearlyBySetPos1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyBySetPos1.ics"); + internal static string YearlyByWeekNo1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByWeekNo1.ics"); + internal static string YearlyByWeekNo2 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByWeekNo2.ics"); + internal static string YearlyByWeekNo3 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByWeekNo3.ics"); + internal static string YearlyByWeekNo4 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByWeekNo4.ics"); + internal static string YearlyByWeekNo5 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyByWeekNo5.ics"); + internal static string YearlyComplex1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyComplex1.ics"); + internal static string YearlyCountByMonth1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyCountByMonth1.ics"); + internal static string YearlyCountByYearDay1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyCountByYearDay1.ics"); + internal static string YearlyInterval1 => ReadStream("Ical.Net.Tests.Calendars.Recurrence.YearlyInterval1.ics"); - internal static string RecurrrenceTestCases => ReadStream("Ical.Net.Tests.Calendars.Recurrence.RecurrenceTestCases.txt"); + internal static string RecurrrenceTestCases => ReadStream("Ical.Net.Tests.Calendars.Recurrence.RecurrenceTestCases.txt"); - internal static string LibicalIcalrecurTest => ReadStream("Ical.Net.Tests.contrib.libical.icalrecur_test.out"); + internal static string LibicalIcalrecurTest => ReadStream("Ical.Net.Tests.contrib.libical.icalrecur_test.out"); - } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/JournalTest.cs b/Ical.Net.Tests/JournalTest.cs index e445f73c2..7fa4fff86 100644 --- a/Ical.Net.Tests/JournalTest.cs +++ b/Ical.Net.Tests/JournalTest.cs @@ -7,67 +7,66 @@ using System.Linq; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +[TestFixture] +public class JournalTest { - [TestFixture] - public class JournalTest + [Test, Category("Journal")] + public void Journal1() { - [Test, Category("Journal")] - public void Journal1() + var iCal = Calendar.Load(IcsFiles.Journal1); + ProgramTest.TestCal(iCal); + Assert.That(iCal.Journals, Has.Count.EqualTo(1)); + var j = iCal.Journals[0]; + + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.Journal1); - ProgramTest.TestCal(iCal); - Assert.That(iCal.Journals, Has.Count.EqualTo(1)); - var j = iCal.Journals[0]; + Assert.That(j, Is.Not.Null, "Journal entry was null"); + Assert.That(j.Status, Is.EqualTo(JournalStatus.Draft), "Journal entry should have been in DRAFT status, but it was in " + j.Status + " status."); + Assert.That(j.Class, Is.EqualTo("PUBLIC"), "Journal class should have been PUBLIC, but was " + j.Class + "."); + }); + Assert.That(j.Start, Is.Null); + } - Assert.Multiple(() => - { - Assert.That(j, Is.Not.Null, "Journal entry was null"); - Assert.That(j.Status, Is.EqualTo(JournalStatus.Draft), "Journal entry should have been in DRAFT status, but it was in " + j.Status + " status."); - Assert.That(j.Class, Is.EqualTo("PUBLIC"), "Journal class should have been PUBLIC, but was " + j.Class + "."); - }); - Assert.That(j.Start, Is.Null); - } + [Test, Category("Journal")] + public void Journal2() + { + var iCal = Calendar.Load(IcsFiles.Journal2); + ProgramTest.TestCal(iCal); + Assert.That(iCal.Journals, Has.Count.EqualTo(1)); + var j = iCal.Journals.First(); - [Test, Category("Journal")] - public void Journal2() + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.Journal2); - ProgramTest.TestCal(iCal); - Assert.That(iCal.Journals, Has.Count.EqualTo(1)); - var j = iCal.Journals.First(); - - Assert.Multiple(() => - { - Assert.That(j, Is.Not.Null, "Journal entry was null"); - Assert.That(j.Status, Is.EqualTo(JournalStatus.Final), "Journal entry should have been in FINAL status, but it was in " + j.Status + " status."); - Assert.That(j.Class, Is.EqualTo("PRIVATE"), "Journal class should have been PRIVATE, but was " + j.Class + "."); - Assert.That(j.Organizer.CommonName, Is.EqualTo("JohnSmith"), "Organizer common name should have been JohnSmith, but was " + j.Organizer.CommonName); - Assert.That( - string.Equals( - j.Organizer.SentBy.OriginalString, - "mailto:jane_doe@host.com", - StringComparison.OrdinalIgnoreCase), - Is.True, - "Organizer should have had been SENT-BY 'mailto:jane_doe@host.com'; it was sent by '" + j.Organizer.SentBy + "'"); - Assert.That( - string.Equals( - j.Organizer.DirectoryEntry.OriginalString, - "ldap://host.com:6666/o=3DDC%20Associates,c=3DUS??(cn=3DJohn%20Smith)", - StringComparison.OrdinalIgnoreCase), - Is.True, - "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.That( - j.Organizer.Value.OriginalString, - Is.EqualTo("MAILTO:jsmith@host.com")); - Assert.That( - j.Organizer.Value.UserInfo, - Is.EqualTo("jsmith")); - Assert.That( - j.Organizer.Value.Host, - Is.EqualTo("host.com")); - Assert.That(j.Start, Is.Null); - }); - } + Assert.That(j, Is.Not.Null, "Journal entry was null"); + Assert.That(j.Status, Is.EqualTo(JournalStatus.Final), "Journal entry should have been in FINAL status, but it was in " + j.Status + " status."); + Assert.That(j.Class, Is.EqualTo("PRIVATE"), "Journal class should have been PRIVATE, but was " + j.Class + "."); + Assert.That(j.Organizer.CommonName, Is.EqualTo("JohnSmith"), "Organizer common name should have been JohnSmith, but was " + j.Organizer.CommonName); + Assert.That( + string.Equals( + j.Organizer.SentBy.OriginalString, + "mailto:jane_doe@host.com", + StringComparison.OrdinalIgnoreCase), + Is.True, + "Organizer should have had been SENT-BY 'mailto:jane_doe@host.com'; it was sent by '" + j.Organizer.SentBy + "'"); + Assert.That( + string.Equals( + j.Organizer.DirectoryEntry.OriginalString, + "ldap://host.com:6666/o=3DDC%20Associates,c=3DUS??(cn=3DJohn%20Smith)", + StringComparison.OrdinalIgnoreCase), + Is.True, + "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.That( + j.Organizer.Value.OriginalString, + Is.EqualTo("MAILTO:jsmith@host.com")); + Assert.That( + j.Organizer.Value.UserInfo, + Is.EqualTo("jsmith")); + Assert.That( + j.Organizer.Value.Host, + Is.EqualTo("host.com")); + Assert.That(j.Start, Is.Null); + }); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/ProgramTest.cs b/Ical.Net.Tests/ProgramTest.cs index 71e0bd010..f842f0c91 100644 --- a/Ical.Net.Tests/ProgramTest.cs +++ b/Ical.Net.Tests/ProgramTest.cs @@ -9,176 +9,175 @@ using Ical.Net.Utility; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +[TestFixture] +public class ProgramTest { - [TestFixture] - public class ProgramTest + [Test] + public void LoadAndDisplayCalendar() { - [Test] - public void LoadAndDisplayCalendar() - { - // The following code loads and displays an iCalendar - // with US Holidays for 2006. - var iCal = Calendar.Load(IcsFiles.UsHolidays); - Assert.That(iCal, Is.Not.Null, "iCalendar did not load."); - } + // The following code loads and displays an iCalendar + // with US Holidays for 2006. + var iCal = Calendar.Load(IcsFiles.UsHolidays); + Assert.That(iCal, Is.Not.Null, "iCalendar did not load."); + } - private const string _tzid = "US-Eastern"; + private const string _tzid = "US-Eastern"; - public static void TestCal(Calendar cal) - { - Assert.That(cal, Is.Not.Null, "The iCalendar was not loaded"); - if (cal.Events.Count > 0) - Assert.That(cal.Events.Count == 1, Is.True, "Calendar should contain 1 event; however, the iCalendar loaded " + cal.Events.Count + " events"); - else if (cal.Todos.Count > 0) - Assert.That(cal.Todos.Count == 1, Is.True, "Calendar should contain 1 todo; however, the iCalendar loaded " + cal.Todos.Count + " todos"); - } + public static void TestCal(Calendar cal) + { + Assert.That(cal, Is.Not.Null, "The iCalendar was not loaded"); + if (cal.Events.Count > 0) + Assert.That(cal.Events.Count == 1, Is.True, "Calendar should contain 1 event; however, the iCalendar loaded " + cal.Events.Count + " events"); + else if (cal.Todos.Count > 0) + Assert.That(cal.Todos.Count == 1, Is.True, "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 - /// - [Test] - public void Merge1() - { - var iCal1 = Calendar.Load(IcsFiles.MonthlyCountByMonthDay3); - var iCal2 = Calendar.Load(IcsFiles.MonthlyByDay1); + /// + /// The following test is an aggregate of MonthlyCountByMonthDay3() and MonthlyByDay1() in the + /// + [Test] + public void Merge1() + { + var iCal1 = Calendar.Load(IcsFiles.MonthlyCountByMonthDay3); + var iCal2 = Calendar.Load(IcsFiles.MonthlyByDay1); - // Change the UID of the 2nd event to make sure it's different - iCal2.Events[iCal1.Events[0].Uid].Uid = "1234567890"; - iCal1.MergeWith(iCal2); + // 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(); + 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(); + // 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.That(start, Is.EqualTo(dt)); + 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.That(start, Is.EqualTo(dt)); - var expectedZone = DateUtil.GetZone(dt.TimeZoneName); - var actualZone = DateUtil.GetZone(timeZones[i]); + var expectedZone = DateUtil.GetZone(dt.TimeZoneName); + var actualZone = DateUtil.GetZone(timeZones[i]); - //Assert.AreEqual(); + //Assert.AreEqual(); - //Normalize the time zones and then compare equality - Assert.That(actualZone, Is.EqualTo(expectedZone)); + //Normalize the time zones and then compare equality + Assert.That(actualZone, Is.EqualTo(expectedZone)); - //Assert.IsTrue(dt.TimeZoneName == TimeZones[i], "Event " + dt + " should occur in the " + TimeZones[i] + " timezone"); - } + //Assert.IsTrue(dt.TimeZoneName == TimeZones[i], "Event " + dt + " should occur in the " + TimeZones[i] + " timezone"); + } - Assert.That(occurrences.Count == dateTimes.Length, Is.True, "There should be exactly " + dateTimes.Length + " occurrences; there were " + occurrences.Count); + Assert.That(occurrences.Count == dateTimes.Length, Is.True, "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(); + // 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++) + 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.Multiple(() => { - IDateTime dt = dateTimes1[i]; - var start = occurrences[i].Period.StartTime; - Assert.Multiple(() => - { - Assert.That(start, Is.EqualTo(dt)); - Assert.That(dt.TimeZoneName == timeZones1[i], Is.True, "Event " + dt + " should occur in the " + timeZones1[i] + " timezone"); - }); - } - - Assert.That(occurrences, Has.Count.EqualTo(dateTimes1.Length), "There should be exactly " + dateTimes1.Length + " occurrences; there were " + occurrences.Count); + Assert.That(start, Is.EqualTo(dt)); + Assert.That(dt.TimeZoneName == timeZones1[i], Is.True, "Event " + dt + " should occur in the " + timeZones1[i] + " timezone"); + }); } - [Test] - public void SystemTimeZone3() + Assert.That(occurrences, Has.Count.EqualTo(dateTimes1.Length), "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) { - // 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) + Assert.That(() => { - Assert.That(() => - { - TimeZoneInfo.FindSystemTimeZoneById(zone.Id); - }, Throws.Nothing, "Time zone should be found."); - } + TimeZoneInfo.FindSystemTimeZoneById(zone.Id); + }, Throws.Nothing, "Time zone should be found."); } } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/RecurrenceTests.cs b/Ical.Net.Tests/RecurrenceTests.cs index 53b49f9e0..bac40fba3 100644 --- a/Ical.Net.Tests/RecurrenceTests.cs +++ b/Ical.Net.Tests/RecurrenceTests.cs @@ -18,2979 +18,2979 @@ using Ical.Net.Utility; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +[TestFixture] +public class RecurrenceTests { - [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(); - var rule = evt.RecurrenceRules.FirstOrDefault(); + 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(); + var rule = evt.RecurrenceRules.FirstOrDefault(); #pragma warning disable 0618 - if (rule != null) rule.RestrictionType = RecurrenceRestrictionType.NoRestriction; + if (rule != null) rule.RestrictionType = RecurrenceRestrictionType.NoRestriction; #pragma warning restore 0618 - fromDate.AssociatedObject = cal; - toDate.AssociatedObject = cal; - - var occurrences = evt.GetOccurrences(fromDate, toDate) - .OrderBy(o => o.Period.StartTime) - .ToList(); - - Assert.Multiple(() => - { - Assert.That( - occurrences, - Has.Count.EqualTo(dateTimes.Length), - "There should have been " + dateTimes.Length + " occurrences; there were " + occurrences.Count); - - if (evt.RecurrenceRules.Count > 0) - { - Assert.That(evt.RecurrenceRules, Has.Count.EqualTo(1)); - } - - 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.That(occurrences[i].Period.StartTime, Is.EqualTo(dt), "Event should occur on " + dt); - if (timeZones != null) - Assert.That(dt.TimeZoneName, Is.EqualTo(timeZones[i]), - "Event " + dt + " should occur in the " + timeZones[i] + " timezone"); - } - }); - } + fromDate.AssociatedObject = cal; + toDate.AssociatedObject = cal; - private void EventOccurrenceTest( - Calendar cal, - IDateTime fromDate, - IDateTime toDate, - IDateTime[] dateTimes, - string[] timeZones - ) - { - EventOccurrenceTest(cal, fromDate, toDate, dateTimes, timeZones, 0); - } + var occurrences = evt.GetOccurrences(fromDate, toDate) + .OrderBy(o => o.Period.StartTime) + .ToList(); - /// - /// 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() + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.YearlyComplex1); - 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.Multiple(() => - { - Assert.That(occurrences[i].Period.StartTime, Is.EqualTo(dt), "Event should occur at " + dt); - Assert.That(occurrences[i + 1].Period.StartTime, Is.EqualTo(dt1), "Event should occur at " + dt); - }); - i += 2; - } + Assert.That( + occurrences, + Has.Count.EqualTo(dateTimes.Length), + "There should have been " + dateTimes.Length + " occurrences; there were " + occurrences.Count); - dt = dt.AddDays(1); + if (evt.RecurrenceRules.Count > 0) + { + Assert.That(evt.RecurrenceRules, Has.Count.EqualTo(1)); } - } - - /// - /// See Page 118 of RFC 2445 - RRULE:FREQ=DAILY;COUNT=10;INTERVAL=2 - /// - [Test, Category("Recurrence")] - public void DailyCount1() - { - var iCal = Calendar.Load(IcsFiles.DailyCount1); - 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.Load(IcsFiles.DailyUntil1); - ProgramTest.TestCal(iCal); - var evt = iCal.Events.First(); + 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.That(occurrences[i].Period.StartTime, Is.EqualTo(dt), "Event should occur on " + dt); + if (timeZones != null) + Assert.That(dt.TimeZoneName, Is.EqualTo(timeZones[i]), + "Event " + dt + " should occur in the " + timeZones[i] + " timezone"); + } + }); + } - var occurrences = evt.GetOccurrences( - new CalDateTime(1997, 9, 1, _tzid), - new CalDateTime(1998, 1, 1, _tzid)).OrderBy(o => o.Period.StartTime).ToList(); + private void EventOccurrenceTest( + Calendar cal, + IDateTime fromDate, + IDateTime toDate, + IDateTime[] dateTimes, + string[] timeZones + ) + { + EventOccurrenceTest(cal, fromDate, toDate, dateTimes, timeZones, 0); + } - IDateTime dt = new CalDateTime(1997, 9, 2, 9, 0, 0, _tzid); - var i = 0; - while (dt.Year < 1998) + /// + /// 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.Load(IcsFiles.YearlyComplex1); + 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)) { - if (dt.GreaterThanOrEqual(evt.Start) && - dt.LessThan(new CalDateTime(1997, 12, 24, 0, 0, 0, _tzid))) + var dt1 = dt.AddHours(1); + Assert.Multiple(() => { - Assert.Multiple(() => - { - Assert.That(occurrences[i].Period.StartTime, Is.EqualTo(dt), "Event should occur at " + dt); - Assert.That( - (dt.LessThan(new CalDateTime(1997, 10, 26, _tzid)) && dt.TimeZoneName == "US-Eastern") || - (dt.GreaterThan(new CalDateTime(1997, 10, 26, _tzid)) && dt.TimeZoneName == "US-Eastern"), - Is.True, - "Event " + dt + " doesn't occur in the correct time zone (including Daylight & Standard time zones)"); - }); - i++; - } - - dt = dt.AddDays(1); + Assert.That(occurrences[i].Period.StartTime, Is.EqualTo(dt), "Event should occur at " + dt); + Assert.That(occurrences[i + 1].Period.StartTime, Is.EqualTo(dt1), "Event should occur at " + dt); + }); + i += 2; } - } - /// - /// See Page 118 of RFC 2445 - RRULE:FREQ=DAILY;INTERVAL=2 - /// - [Test, Category("Recurrence")] - public void Daily1() - { - var iCal = Calendar.Load(IcsFiles.Daily1); - 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" - } - ); + dt = dt.AddDays(1); } + } - /// - /// See Page 119 of RFC 2445 - RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5 - /// - [Test, Category("Recurrence")] - public void DailyCount2() - { - var iCal = Calendar.Load(IcsFiles.DailyCount2); - 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 118 of RFC 2445 - RRULE:FREQ=DAILY;COUNT=10;INTERVAL=2 + /// + [Test, Category("Recurrence")] + public void DailyCount1() + { + var iCal = Calendar.Load(IcsFiles.DailyCount1); + 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 119 of RFC 2445 - RRULE:FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1 - /// - [Test, Category("Recurrence")] - public void ByMonth1() - { - var iCal = Calendar.Load(IcsFiles.ByMonth1); - ProgramTest.TestCal(iCal); - var evt = iCal.Events.First(); + /// + /// See Page 118 of RFC 2445 - RRULE:FREQ=DAILY;UNTIL=19971224T000000Z + /// + [Test, Category("Recurrence")] + public void DailyUntil1() + { + var iCal = Calendar.Load(IcsFiles.DailyUntil1); + 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(); + 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(1998, 1, 1, 9, 0, 0, _tzid); - var i = 0; - while (dt.Year < 2001) + 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))) { - if (dt.GreaterThanOrEqual(evt.Start) && - dt.Month == 1 && - dt.LessThanOrEqual(new CalDateTime(2000, 1, 31, 9, 0, 0, _tzid))) + Assert.Multiple(() => { Assert.That(occurrences[i].Period.StartTime, Is.EqualTo(dt), "Event should occur at " + dt); - i++; - } - - dt = dt.AddDays(1); + Assert.That( + (dt.LessThan(new CalDateTime(1997, 10, 26, _tzid)) && dt.TimeZoneName == "US-Eastern") || + (dt.GreaterThan(new CalDateTime(1997, 10, 26, _tzid)) && dt.TimeZoneName == "US-Eastern"), + Is.True, + "Event " + dt + " doesn't occur in the correct time zone (including Daylight & Standard time zones)"); + }); + i++; } - } - /// - /// 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.Load(IcsFiles.ByMonth1); - var iCal2 = Calendar.Load(IcsFiles.ByMonth2); - ProgramTest.TestCal(iCal1); - ProgramTest.TestCal(iCal2); - var evt1 = iCal1.Events.First(); - var evt2 = 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.That(evt1Occurrences.Count == evt2Occurrences.Count, Is.True, "ByMonth1 does not match ByMonth2 as it should"); - for (var i = 0; i < evt1Occurrences.Count; i++) - Assert.That(evt2Occurrences[i].Period, Is.EqualTo(evt1Occurrences[i].Period), "PERIOD " + i + " from ByMonth1 (" + evt1Occurrences[i] + ") does not match PERIOD " + i + " from ByMonth2 (" + evt2Occurrences[i] + ")"); + dt = dt.AddDays(1); } + } - /// - /// See Page 119 of RFC 2445 - RRULE:FREQ=WEEKLY;COUNT=10 - /// - [Test, Category("Recurrence")] - public void WeeklyCount1() - { - var iCal = Calendar.Load(IcsFiles.WeeklyCount1); - 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 118 of RFC 2445 - RRULE:FREQ=DAILY;INTERVAL=2 + /// + [Test, Category("Recurrence")] + public void Daily1() + { + var iCal = Calendar.Load(IcsFiles.Daily1); + 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=WEEKLY;UNTIL=19971224T000000Z - /// - [Test, Category("Recurrence")] - public void WeeklyUntil1() - { - var iCal = Calendar.Load(IcsFiles.WeeklyUntil1); - 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=DAILY;INTERVAL=10;COUNT=5 + /// + [Test, Category("Recurrence")] + public void DailyCount2() + { + var iCal = Calendar.Load(IcsFiles.DailyCount2); + 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=WEEKLY;INTERVAL=2;WKST=SU - /// - [Test, Category("Recurrence")] - public void WeeklyWkst1() - { - var iCal = Calendar.Load(IcsFiles.WeeklyWkst1); - 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=DAILY;UNTIL=20000131T090000Z;BYMONTH=1 + /// + [Test, Category("Recurrence")] + public void ByMonth1() + { + var iCal = Calendar.Load(IcsFiles.ByMonth1); + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); - /// - /// 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.Load(IcsFiles.WeeklyUntilWkst1); - 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 - ); - } + var occurrences = evt.GetOccurrences( + new CalDateTime(1998, 1, 1, _tzid), + new CalDateTime(2000, 12, 31, _tzid)).OrderBy(o => o.Period.StartTime).ToList(); - /// - /// See Page 120 of RFC 2445 - RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH - /// - [Test, Category("Recurrence")] - public void WeeklyCountWkst1() + IDateTime dt = new CalDateTime(1998, 1, 1, 9, 0, 0, _tzid); + var i = 0; + while (dt.Year < 2001) { - var iCal1 = Calendar.Load(IcsFiles.WeeklyUntilWkst1); - var iCal2 = Calendar.Load(IcsFiles.WeeklyCountWkst1); - 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.That(evt2Occ, Has.Count.EqualTo(evt1Occ.Count), "WeeklyCountWkst1() does not match WeeklyUntilWkst1() as it should"); - for (var i = 0; i < evt1Occ.Count; i++) - { - Assert.That(evt2Occ[i].Period, Is.EqualTo(evt1Occ[i].Period), "PERIOD " + i + " from WeeklyUntilWkst1 (" + evt1Occ[i].Period + ") does not match PERIOD " + i + " from WeeklyCountWkst1 (" + evt2Occ[i].Period + ")"); + if (dt.GreaterThanOrEqual(evt.Start) && + dt.Month == 1 && + dt.LessThanOrEqual(new CalDateTime(2000, 1, 31, 9, 0, 0, _tzid))) + { + Assert.That(occurrences[i].Period.StartTime, Is.EqualTo(dt), "Event should occur at " + dt); + i++; } - } - /// - /// 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.Load(IcsFiles.WeeklyUntilWkst2); - EventOccurrenceTest( - iCal, - new CalDateTime(1996, 1, 1, _tzid), - new CalDateTime(1999, 1, 1, _tzid), - new[] - { - 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" - } - ); + dt = dt.AddDays(1); } + } - /// - /// 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.Load(IcsFiles.WeeklyUntilWkst2); - 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 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.Load(IcsFiles.ByMonth1); + var iCal2 = Calendar.Load(IcsFiles.ByMonth2); + ProgramTest.TestCal(iCal1); + ProgramTest.TestCal(iCal2); + var evt1 = iCal1.Events.First(); + var evt2 = 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.That(evt1Occurrences.Count == evt2Occurrences.Count, Is.True, "ByMonth1 does not match ByMonth2 as it should"); + for (var i = 0; i < evt1Occurrences.Count; i++) + Assert.That(evt2Occurrences[i].Period, Is.EqualTo(evt1Occurrences[i].Period), "PERIOD " + i + " from ByMonth1 (" + evt1Occurrences[i] + ") does not match PERIOD " + i + " from ByMonth2 (" + evt2Occurrences[i] + ")"); + } - /// - /// 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.Load(IcsFiles.WeeklyCountWkst2); - 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 119 of RFC 2445 - RRULE:FREQ=WEEKLY;COUNT=10 + /// + [Test, Category("Recurrence")] + public void WeeklyCount1() + { + var iCal = Calendar.Load(IcsFiles.WeeklyCount1); + 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 120 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR - /// - [Test, Category("Recurrence")] - public void MonthlyCountByDay1() - { - var iCal = Calendar.Load(IcsFiles.MonthlyCountByDay1); - 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 119 of RFC 2445 - RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z + /// + [Test, Category("Recurrence")] + public void WeeklyUntil1() + { + var iCal = Calendar.Load(IcsFiles.WeeklyUntil1); + 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 120 of RFC 2445 - RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR - /// - [Test, Category("Recurrence")] - public void MonthlyUntilByDay1() - { - var iCal = Calendar.Load(IcsFiles.MonthlyUntilByDay1); - 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 119 of RFC 2445 - RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU + /// + [Test, Category("Recurrence")] + public void WeeklyWkst1() + { + var iCal = Calendar.Load(IcsFiles.WeeklyWkst1); + 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 120 of RFC 2445 - RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU - /// - [Test, Category("Recurrence")] - public void MonthlyCountByDay2() - { - var iCal = Calendar.Load(IcsFiles.MonthlyCountByDay2); - 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 119 of RFC 2445 - RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH + /// + [Test, Category("Recurrence")] + public void WeeklyUntilWkst1() + { + var iCal = Calendar.Load(IcsFiles.WeeklyUntilWkst1); + 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 121 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO - /// - [Test, Category("Recurrence")] - public void MonthlyCountByDay3() - { - var iCal = Calendar.Load(IcsFiles.MonthlyCountByDay3); - 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 120 of RFC 2445 - RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH + /// + [Test, Category("Recurrence")] + public void WeeklyCountWkst1() + { + var iCal1 = Calendar.Load(IcsFiles.WeeklyUntilWkst1); + var iCal2 = Calendar.Load(IcsFiles.WeeklyCountWkst1); + ProgramTest.TestCal(iCal1); + ProgramTest.TestCal(iCal2); + var evt1 = iCal1.Events.First(); + var evt2 = iCal2.Events.First(); - /// - /// See Page 121 of RFC 2445 - RRULE:FREQ=MONTHLY;BYMONTHDAY=-3 - /// - [Test, Category("Recurrence")] - public void ByMonthDay1() + 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.That(evt2Occ, Has.Count.EqualTo(evt1Occ.Count), "WeeklyCountWkst1() does not match WeeklyUntilWkst1() as it should"); + for (var i = 0; i < evt1Occ.Count; i++) { - var iCal = Calendar.Load(IcsFiles.ByMonthDay1); - 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" - } - ); + Assert.That(evt2Occ[i].Period, Is.EqualTo(evt1Occ[i].Period), "PERIOD " + i + " from WeeklyUntilWkst1 (" + evt1Occ[i].Period + ") does not match PERIOD " + i + " from WeeklyCountWkst1 (" + evt2Occ[i].Period + ")"); } + } - /// - /// See Page 121 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15 - /// - [Test, Category("Recurrence")] - public void MonthlyCountByMonthDay1() - { - var iCal = Calendar.Load(IcsFiles.MonthlyCountByMonthDay1); + /// + /// 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.Load(IcsFiles.WeeklyUntilWkst2); + EventOccurrenceTest( + iCal, + new CalDateTime(1996, 1, 1, _tzid), + new CalDateTime(1999, 1, 1, _tzid), + new[] + { + 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" + } + ); + } - 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" - } - ); - } + /// + /// 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.Load(IcsFiles.WeeklyUntilWkst2); + 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 121 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1 - /// - [Test, Category("Recurrence")] - public void MonthlyCountByMonthDay2() - { - var iCal = Calendar.Load(IcsFiles.MonthlyCountByMonthDay2); - 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 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.Load(IcsFiles.WeeklyCountWkst2); + 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 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.Load(IcsFiles.MonthlyCountByMonthDay3); - 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 120 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR + /// + [Test, Category("Recurrence")] + public void MonthlyCountByDay1() + { + var iCal = Calendar.Load(IcsFiles.MonthlyCountByDay1); + 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 122 of RFC 2445 - RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU - /// - [Test, Category("Recurrence")] - public void MonthlyByDay1() - { - var iCal = Calendar.Load(IcsFiles.MonthlyByDay1); - 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 120 of RFC 2445 - RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR + /// + [Test, Category("Recurrence")] + public void MonthlyUntilByDay1() + { + var iCal = Calendar.Load(IcsFiles.MonthlyUntilByDay1); + 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 122 of RFC 2445 - RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7 - /// - [Test, Category("Recurrence")] - public void YearlyByMonth1() - { - var iCal = Calendar.Load(IcsFiles.YearlyByMonth1); - 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 120 of RFC 2445 - RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU + /// + [Test, Category("Recurrence")] + public void MonthlyCountByDay2() + { + var iCal = Calendar.Load(IcsFiles.MonthlyCountByDay2); + 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 122 of RFC 2445 - RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3 - /// - [Test, Category("Recurrence")] - public void YearlyCountByMonth1() - { - var iCal = Calendar.Load(IcsFiles.YearlyCountByMonth1); - 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 121 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO + /// + [Test, Category("Recurrence")] + public void MonthlyCountByDay3() + { + var iCal = Calendar.Load(IcsFiles.MonthlyCountByDay3); + 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 122 of RFC 2445 - RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200 - /// - [Test, Category("Recurrence")] - public void YearlyCountByYearDay1() - { - var iCal = Calendar.Load(IcsFiles.YearlyCountByYearDay1); - 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 121 of RFC 2445 - RRULE:FREQ=MONTHLY;BYMONTHDAY=-3 + /// + [Test, Category("Recurrence")] + public void ByMonthDay1() + { + var iCal = Calendar.Load(IcsFiles.ByMonthDay1); + 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 123 of RFC 2445 - RRULE:FREQ=YEARLY;BYDAY=20MO - /// - [Test, Category("Recurrence")] - public void YearlyByDay1() - { - var iCal = Calendar.Load(IcsFiles.YearlyByDay1); - 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 121 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15 + /// + [Test, Category("Recurrence")] + public void MonthlyCountByMonthDay1() + { + var iCal = Calendar.Load(IcsFiles.MonthlyCountByMonthDay1); - /// - /// Ordering of byweekno should not matter - /// - [Test, Category("Recurrence")] - public void WeekNoOrderingShouldNotMatter() - { - var start = new DateTime(2019, 1, 1); - var end = new DateTime(2019, 12, 31); - var rpe1 = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=YEARLY;WKST=MO;BYDAY=MO;BYWEEKNO=1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53")); - var rpe2 = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=YEARLY;WKST=MO;BYDAY=MO;BYWEEKNO=53,51,49,47,45,43,41,39,37,35,33,31,29,27,25,23,21,19,17,15,13,11,9,7,5,3,1")); + 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" + } + ); + } - var recurringPeriods1 = rpe1.Evaluate(new CalDateTime(start), start, end, false); - var recurringPeriods2 = rpe2.Evaluate(new CalDateTime(start), start, end, false); + /// + /// See Page 121 of RFC 2445 - RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1 + /// + [Test, Category("Recurrence")] + public void MonthlyCountByMonthDay2() + { + var iCal = Calendar.Load(IcsFiles.MonthlyCountByMonthDay2); + 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" + } + ); + } - Assert.That(recurringPeriods2, Has.Count.EqualTo(recurringPeriods1.Count)); - } + /// + /// 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.Load(IcsFiles.MonthlyCountByMonthDay3); + 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 123 of RFC 2445 - RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO - /// - [Test, Category("Recurrence")] - public void YearlyByWeekNo1() - { - var iCal = Calendar.Load(IcsFiles.YearlyByWeekNo1); - 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 - ); - } + /// + /// See Page 122 of RFC 2445 - RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU + /// + [Test, Category("Recurrence")] + public void MonthlyByDay1() + { + var iCal = Calendar.Load(IcsFiles.MonthlyByDay1); + 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" + } + ); + } - /// - /// 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.Load(IcsFiles.YearlyByWeekNo2); - 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 - ); - } + /// + /// See Page 122 of RFC 2445 - RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7 + /// + [Test, Category("Recurrence")] + public void YearlyByMonth1() + { + var iCal = Calendar.Load(IcsFiles.YearlyByMonth1); + 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 + ); + } - /// - /// 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.Load(IcsFiles.YearlyByWeekNo3); - 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 - ); - } + /// + /// 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.Load(IcsFiles.YearlyCountByMonth1); + 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 + ); + } - /// - /// 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.Load(IcsFiles.YearlyByWeekNo4); - 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 - ); - } + /// + /// 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.Load(IcsFiles.YearlyCountByYearDay1); + 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" + } + ); + } - /// - /// 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.Load(IcsFiles.YearlyByWeekNo5); - 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;BYDAY=20MO + /// + [Test, Category("Recurrence")] + public void YearlyByDay1() + { + var iCal = Calendar.Load(IcsFiles.YearlyByDay1); + 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;BYMONTH=3;BYDAY=TH - /// - [Test, Category("Recurrence")] - public void YearlyByMonth2() - { - var iCal = Calendar.Load(IcsFiles.YearlyByMonth2); - 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 - ); - } + /// + /// Ordering of byweekno should not matter + /// + [Test, Category("Recurrence")] + public void WeekNoOrderingShouldNotMatter() + { + var start = new DateTime(2019, 1, 1); + var end = new DateTime(2019, 12, 31); + var rpe1 = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=YEARLY;WKST=MO;BYDAY=MO;BYWEEKNO=1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53")); + var rpe2 = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=YEARLY;WKST=MO;BYDAY=MO;BYWEEKNO=53,51,49,47,45,43,41,39,37,35,33,31,29,27,25,23,21,19,17,15,13,11,9,7,5,3,1")); - /// - /// See Page 123 of RFC 2445 - RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8 - /// - [Test, Category("Recurrence")] - public void YearlyByMonth3() - { - var iCal = Calendar.Load(IcsFiles.YearlyByMonth3); - 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 - ); - } + var recurringPeriods1 = rpe1.Evaluate(new CalDateTime(start), start, end, false); + var recurringPeriods2 = rpe2.Evaluate(new CalDateTime(start), start, end, false); - /// - /// 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.Load(IcsFiles.MonthlyByMonthDay1); - 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" - } - ); - } + Assert.That(recurringPeriods2, Has.Count.EqualTo(recurringPeriods1.Count)); + } - /// - /// 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.Load(IcsFiles.MonthlyByMonthDay2); - 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 123 of RFC 2445 - RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO + /// + [Test, Category("Recurrence")] + public void YearlyByWeekNo1() + { + var iCal = Calendar.Load(IcsFiles.YearlyByWeekNo1); + 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 + ); + } - /// - /// 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.Load(IcsFiles.YearlyByMonthDay1); - 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 - ); - } + /// + /// 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.Load(IcsFiles.YearlyByWeekNo2); + 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 + ); + } - /// - /// 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.Load(IcsFiles.MonthlyBySetPos1); - 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" - } - ); - } + /// + /// 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.Load(IcsFiles.YearlyByWeekNo3); + 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 + ); + } - /// - /// 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.Load(IcsFiles.MonthlyBySetPos2); - 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" - } - ); - } + /// + /// 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.Load(IcsFiles.YearlyByWeekNo4); + 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 + ); + } - /// - /// 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.Load(IcsFiles.HourlyUntil1); - EventOccurrenceTest( - iCal, - fromDate: new CalDateTime(1996, 1, 1, _tzid), - toDate: new CalDateTime(1998, 3, 31, _tzid), - dateTimes: 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), - new CalDateTime(1997, 9, 2, 18, 0, 0, _tzid), - }, - timeZones: 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.Load(IcsFiles.YearlyByWeekNo5); + 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 125 of RFC 2445 - RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6 - /// - [Test, Category("Recurrence")] - public void MinutelyCount1() - { - var iCal = Calendar.Load(IcsFiles.MinutelyCount1); - 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 123 of RFC 2445 - RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH + /// + [Test, Category("Recurrence")] + public void YearlyByMonth2() + { + var iCal = Calendar.Load(IcsFiles.YearlyByMonth2); + 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 125 of RFC 2445 - RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4 - /// - [Test, Category("Recurrence")] - public void MinutelyCount2() - { - var iCal = Calendar.Load(IcsFiles.MinutelyCount2); - 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 Page 123 of RFC 2445 - RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8 + /// + [Test, Category("Recurrence")] + public void YearlyByMonth3() + { + var iCal = Calendar.Load(IcsFiles.YearlyByMonth3); + 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 https://sourceforge.net/projects/dday-ical/forums/forum/656447/topic/3827441 - /// - [Test, Category("Recurrence")] - public void MinutelyCount3() - { - var iCal = Calendar.Load(IcsFiles.MinutelyCount3); - 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 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.Load(IcsFiles.MonthlyByMonthDay1); + 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 https://sourceforge.net/projects/dday-ical/forums/forum/656447/topic/3827441 - /// - [Test, Category("Recurrence")] - public void MinutelyCount4() - { - var iCal = Calendar.Load(IcsFiles.MinutelyCount4); - 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 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.Load(IcsFiles.MonthlyByMonthDay2); + 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 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.Load(IcsFiles.DailyByHourMinute1); - 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 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.Load(IcsFiles.YearlyByMonthDay1); + 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 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.Load(IcsFiles.DailyByHourMinute1); - var iCal2 = Calendar.Load(IcsFiles.MinutelyByHour1); - 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.That(evt1Occ.Count == evt2Occ.Count, Is.True, "MinutelyByHour1() does not match DailyByHourMinute1() as it should"); - for (var i = 0; i < evt1Occ.Count; i++) - Assert.That(evt2Occ[i].Period, Is.EqualTo(evt1Occ[i].Period), "PERIOD " + i + " from DailyByHourMinute1 (" + evt1Occ[i].Period + ") does not match PERIOD " + i + " from MinutelyByHour1 (" + evt2Occ[i].Period + ")"); - } + /// + /// 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.Load(IcsFiles.MonthlyBySetPos1); + 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 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.Load(IcsFiles.WeeklyCountWkst3); - 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 124 of RFC 2445 - RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2 + /// + [Test, Category("Recurrence")] + public void MonthlyBySetPos2() + { + var iCal = Calendar.Load(IcsFiles.MonthlyBySetPos2); + 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=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.Load(IcsFiles.WeeklyCountWkst4); - 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 - ); - } + /// + /// 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.Load(IcsFiles.HourlyUntil1); + EventOccurrenceTest( + iCal, + fromDate: new CalDateTime(1996, 1, 1, _tzid), + toDate: new CalDateTime(1998, 3, 31, _tzid), + dateTimes: 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), + new CalDateTime(1997, 9, 2, 18, 0, 0, _tzid), + }, + timeZones: 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.Load(IcsFiles.Bug1741093); - 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 - ); - } + /// + /// See Page 125 of RFC 2445 - RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6 + /// + [Test, Category("Recurrence")] + public void MinutelyCount1() + { + var iCal = Calendar.Load(IcsFiles.MinutelyCount1); + 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 + ); + } - [Test, Category("Recurrence")] - public void Secondly_DefinedNumberOfOccurrences_ShouldSucceed() - { - var iCal = Calendar.Load(IcsFiles.Secondly1); + /// + /// See Page 125 of RFC 2445 - RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4 + /// + [Test, Category("Recurrence")] + public void MinutelyCount2() + { + var iCal = Calendar.Load(IcsFiles.MinutelyCount2); + 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.Load(IcsFiles.MinutelyCount3); + 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 + ); + } - var start = new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid); - var end = new CalDateTime(2007, 6, 21, 8, 1, 0, _tzid); // End period is exclusive, not inclusive. + /// + /// See https://sourceforge.net/projects/dday-ical/forums/forum/656447/topic/3827441 + /// + [Test, Category("Recurrence")] + public void MinutelyCount4() + { + var iCal = Calendar.Load(IcsFiles.MinutelyCount4); + 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 + ); + } - var dateTimes = new List(); - for (var dt = start; dt.LessThan(end); dt = (CalDateTime) dt.AddSeconds(1)) + /// + /// 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.Load(IcsFiles.DailyByHourMinute1); + EventOccurrenceTest( + iCal, + new CalDateTime(1997, 9, 2, _tzid), + new CalDateTime(1997, 9, 4, _tzid), + new[] { - dateTimes.Add(new CalDateTime(dt)); - } + 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 + ); + } - EventOccurrenceTest(iCal, start, end, dateTimes.ToArray(), 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.Load(IcsFiles.DailyByHourMinute1); + var iCal2 = Calendar.Load(IcsFiles.MinutelyByHour1); + 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.That(evt1Occ.Count == evt2Occ.Count, Is.True, "MinutelyByHour1() does not match DailyByHourMinute1() as it should"); + for (var i = 0; i < evt1Occ.Count; i++) + Assert.That(evt2Occ[i].Period, Is.EqualTo(evt1Occ[i].Period), "PERIOD " + i + " from DailyByHourMinute1 (" + evt1Occ[i].Period + ") does not match PERIOD " + i + " from MinutelyByHour1 (" + evt2Occ[i].Period + ")"); + } - [Test, Category("Recurrence")] - public void Minutely_DefinedNumberOfOccurrences_ShouldSucceed() - { - var iCal = Calendar.Load(IcsFiles.Minutely1); + /// + /// 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.Load(IcsFiles.WeeklyCountWkst3); + 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 + ); + } - var start = new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid); - var end = new CalDateTime(2007, 6, 21, 12, 0, 1, _tzid); // End period is exclusive, not inclusive. + /// + /// 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.Load(IcsFiles.WeeklyCountWkst4); + 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 + ); + } - var dateTimes = new List(); - for (var dt = start; dt.LessThan(end); dt = (CalDateTime) dt.AddMinutes(1)) + /// + /// 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.Load(IcsFiles.Bug1741093); + EventOccurrenceTest( + iCal, + new CalDateTime(2007, 7, 1, _tzid), + new CalDateTime(2007, 8, 1, _tzid), + new[] { - dateTimes.Add(new CalDateTime(dt)); - } + 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 + ); + } - EventOccurrenceTest(iCal, start, end, dateTimes.ToArray(), null); - } + [Test, Category("Recurrence")] + public void Secondly_DefinedNumberOfOccurrences_ShouldSucceed() + { + var iCal = Calendar.Load(IcsFiles.Secondly1); - [Test, Category("Recurrence")] - public void Hourly_DefinedNumberOfOccurrences_ShouldSucceed() + var start = new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid); + var end = new CalDateTime(2007, 6, 21, 8, 1, 0, _tzid); // End period is exclusive, not inclusive. + + var dateTimes = new List(); + for (var dt = start; dt.LessThan(end); dt = (CalDateTime) dt.AddSeconds(1)) { - var iCal = Calendar.Load(IcsFiles.Hourly1); + dateTimes.Add(new CalDateTime(dt)); + } - var start = new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid); - var end = new CalDateTime(2007, 6, 25, 8, 0, 1, _tzid); // End period is exclusive, not inclusive. + EventOccurrenceTest(iCal, start, end, dateTimes.ToArray(), null); + } - var dateTimes = new List(); - for (var dt = start; dt.LessThan(end); dt = (CalDateTime) dt.AddHours(1)) - { - dateTimes.Add(new CalDateTime(dt)); - } + [Test, Category("Recurrence")] + public void Minutely_DefinedNumberOfOccurrences_ShouldSucceed() + { + var iCal = Calendar.Load(IcsFiles.Minutely1); - EventOccurrenceTest(iCal, start, end, dateTimes.ToArray(), null); - } + var start = new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid); + var end = new CalDateTime(2007, 6, 21, 12, 0, 1, _tzid); // End period is exclusive, not inclusive. - /// - /// Ensures that "off-month" calculation works correctly - /// - [Test, Category("Recurrence")] - public void MonthlyInterval1() + var dateTimes = new List(); + for (var dt = start; dt.LessThan(end); dt = (CalDateTime) dt.AddMinutes(1)) { - var iCal = Calendar.Load(IcsFiles.MonthlyInterval1); - 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 - ); + dateTimes.Add(new CalDateTime(dt)); } - /// - /// Ensures that "off-year" calculation works correctly - /// - [Test, Category("Recurrence")] - public void YearlyInterval1() - { - var iCal = Calendar.Load(IcsFiles.YearlyInterval1); - 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 - ); - } + EventOccurrenceTest(iCal, start, end, dateTimes.ToArray(), null); + } - /// - /// Ensures that "off-day" calculation works correctly - /// - [Test, Category("Recurrence")] - public void DailyInterval1() - { - var iCal = Calendar.Load(IcsFiles.DailyInterval1); - 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 - ); - } + [Test, Category("Recurrence")] + public void Hourly_DefinedNumberOfOccurrences_ShouldSucceed() + { + var iCal = Calendar.Load(IcsFiles.Hourly1); - /// - /// Ensures that "off-hour" calculation works correctly - /// - [Test, Category("Recurrence")] - public void HourlyInterval1() - { - var iCal = Calendar.Load(IcsFiles.HourlyInterval1); - 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 - ); - } + var start = new CalDateTime(2007, 6, 21, 8, 0, 0, _tzid); + var end = new CalDateTime(2007, 6, 25, 8, 0, 1, _tzid); // End period is exclusive, not inclusive. - /// - /// 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 dateTimes = new List(); + for (var dt = start; dt.LessThan(end); dt = (CalDateTime) dt.AddHours(1)) { - var iCal = Calendar.Load(IcsFiles.YearlyBySetPos1); - 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 - ); + dateTimes.Add(new CalDateTime(dt)); } - /// - /// Ensures that GetOccurrences() always returns a single occurrence - /// for a non-recurring event. - /// - [Test, Category("Recurrence")] - public void Empty1() - { - var iCal = Calendar.Load(IcsFiles.Empty1); - 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 - ); - } + EventOccurrenceTest(iCal, start, end, dateTimes.ToArray(), null); + } - /// - /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for an HOURLY frequency. - /// - [Test, Category("Recurrence")] - public void HourlyInterval2() - { - var iCal = Calendar.Load(IcsFiles.HourlyInterval2); - EventOccurrenceTest( - iCal, + /// + /// Ensures that "off-month" calculation works correctly + /// + [Test, Category("Recurrence")] + public void MonthlyInterval1() + { + var iCal = Calendar.Load(IcsFiles.MonthlyInterval1); + 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.Load(IcsFiles.YearlyInterval1); + 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" calculation works correctly + /// + [Test, Category("Recurrence")] + public void DailyInterval1() + { + var iCal = Calendar.Load(IcsFiles.DailyInterval1); + 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.Load(IcsFiles.HourlyInterval1); + 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.Load(IcsFiles.YearlyBySetPos1); + 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.Load(IcsFiles.Empty1); + 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.Load(IcsFiles.HourlyInterval2); + 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, 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 - ); - } + 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.Load(IcsFiles.MinutelyInterval1); - EventOccurrenceTest( - iCal, + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for an MINUTELY frequency. + /// + [Test, Category("Recurrence")] + public void MinutelyInterval1() + { + var iCal = Calendar.Load(IcsFiles.MinutelyInterval1); + 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, 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 - ); - } + 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.Load(IcsFiles.DailyInterval2); - EventOccurrenceTest( - iCal, + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for an DAILY frequency with an INTERVAL. + /// + [Test, Category("Recurrence")] + public void DailyInterval2() + { + var iCal = Calendar.Load(IcsFiles.DailyInterval2); + 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, 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 - ); - } + 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.Load(IcsFiles.DailyByDay1); - EventOccurrenceTest( - iCal, + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for an DAILY frequency with a BYDAY value. + /// + [Test, Category("Recurrence")] + public void DailyByDay1() + { + var iCal = Calendar.Load(IcsFiles.DailyByDay1); + 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, 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 - ); - } + 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.Load(IcsFiles.WeeklyWeekStartsLastYear); - 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 DateUtil.AddWeeks works properly when week number is for previous year for selected date. + /// + [Test, Category("Recurrence")] + public void WeeklyWeekStartsLastYear() + { + var iCal = Calendar.Load(IcsFiles.WeeklyWeekStartsLastYear); + 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.Load(IcsFiles.WeeklyInterval1); - EventOccurrenceTest( - iCal, + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for a WEEKLY frequency with an INTERVAL. + /// + [Test, Category("Recurrence")] + public void WeeklyInterval1() + { + var iCal = Calendar.Load(IcsFiles.WeeklyInterval1); + 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, 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 - ); - } + 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.Load(IcsFiles.Monthly1); - EventOccurrenceTest( - iCal, + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for a MONTHLY frequency. + /// + [Test, Category("Recurrence")] + public void Monthly1() + { + var iCal = Calendar.Load(IcsFiles.Monthly1); + 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(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 - ); - } + 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.Load(IcsFiles.Yearly1); - EventOccurrenceTest( - iCal, + /// + /// Ensures that RecurrencePattern.GetNextOccurrence() functions properly for a YEARLY frequency. + /// + [Test, Category("Recurrence")] + public void Yearly1() + { + var iCal = Calendar.Load(IcsFiles.Yearly1); + 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(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.Load(IcsFiles.Bug2912657); - 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 - ); - } + 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 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.Load(IcsFiles.Bug2916581); - 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 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.Load(IcsFiles.Bug2912657); + 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 - /// 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.Load(IcsFiles.Bug2959692); - 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 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.Load(IcsFiles.Bug2916581); + 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 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.Load(IcsFiles.Bug2966236); - 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 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.Load(IcsFiles.Bug2959692); + 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 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.Load(IcsFiles.Bug3007244); - - 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 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.Load(IcsFiles.Bug2966236); + 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 bug BYWEEKNO not working - /// - [Test, Category("Recurrence")] - public void BugByWeekNoNotWorking() - { - var start = new DateTime(2019, 1, 1); - var end = new DateTime(2019, 12, 31); - var rpe = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=WEEKLY;BYDAY=MO;BYWEEKNO=2")); + /// + /// 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.Load(IcsFiles.Bug3007244); + + 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 + ); + } - var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, end, false); + /// + /// Tests bug BYWEEKNO not working + /// + [Test, Category("Recurrence")] + public void BugByWeekNoNotWorking() + { + var start = new DateTime(2019, 1, 1); + var end = new DateTime(2019, 12, 31); + var rpe = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=WEEKLY;BYDAY=MO;BYWEEKNO=2")); - Assert.That(recurringPeriods, Has.Count.EqualTo(1)); - Assert.That(recurringPeriods.First().StartTime, Is.EqualTo(new CalDateTime(2019, 1, 7))); - } + var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, end, false); - /// - /// Tests bug BYMONTH while FREQ=WEEKLY not working - /// - [Test, Category("Recurrence")] - public void BugByMonthWhileFreqIsWeekly() - { - var start = new DateTime(2020, 1, 1); - var end = new DateTime(2020, 12, 31); - var rpe = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=WEEKLY;BYDAY=MO;BYMONTH=1")); + Assert.That(recurringPeriods, Has.Count.EqualTo(1)); + Assert.That(recurringPeriods.First().StartTime, Is.EqualTo(new CalDateTime(2019, 1, 7))); + } - var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, end, false).OrderBy(x => x).ToList(); + /// + /// Tests bug BYMONTH while FREQ=WEEKLY not working + /// + [Test, Category("Recurrence")] + public void BugByMonthWhileFreqIsWeekly() + { + var start = new DateTime(2020, 1, 1); + var end = new DateTime(2020, 12, 31); + var rpe = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=WEEKLY;BYDAY=MO;BYMONTH=1")); - Assert.That(recurringPeriods, Has.Count.EqualTo(4)); - Assert.Multiple(() => - { - Assert.That(recurringPeriods[0].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 6))); - Assert.That(recurringPeriods[1].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 13))); - Assert.That(recurringPeriods[2].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 20))); - Assert.That(recurringPeriods[3].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 27))); - }); - } + var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, end, false).OrderBy(x => x).ToList(); - [Test, Category("Recurrence")] - public void ReccurencePattern_MaxDate_StopsOnCount() + Assert.That(recurringPeriods, Has.Count.EqualTo(4)); + Assert.Multiple(() => { - var evt = new CalendarEvent - { - Start = new CalDateTime(2018, 1, 1, 12, 0, 0), - Duration = TimeSpan.FromHours(1) - }; + Assert.That(recurringPeriods[0].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 6))); + Assert.That(recurringPeriods[1].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 13))); + Assert.That(recurringPeriods[2].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 20))); + Assert.That(recurringPeriods[3].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 27))); + }); + } - var pattern = new RecurrencePattern - { - Frequency = FrequencyType.Daily, - Count = 10 - }; + [Test, Category("Recurrence")] + public void ReccurencePattern_MaxDate_StopsOnCount() + { + var evt = new CalendarEvent + { + Start = new CalDateTime(2018, 1, 1, 12, 0, 0), + Duration = TimeSpan.FromHours(1) + }; - evt.RecurrenceRules.Add(pattern); + var pattern = new RecurrencePattern + { + Frequency = FrequencyType.Daily, + Count = 10 + }; - var occurrences = evt.GetOccurrences(new DateTime(2018, 1, 1), DateTime.MaxValue); - Assert.That(occurrences, Has.Count.EqualTo(10), "There should be 10 occurrences of this event."); - } + evt.RecurrenceRules.Add(pattern); - /// - /// Tests bug BYMONTH while FREQ=MONTHLY not working - /// - [Test, Category("Recurrence")] - public void BugByMonthWhileFreqIsMonthly() - { - var start = new DateTime(2020, 1, 1); - var end = new DateTime(2020, 12, 31); - var rpe = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=MONTHLY;BYDAY=MO;BYMONTH=1")); + var occurrences = evt.GetOccurrences(new DateTime(2018, 1, 1), DateTime.MaxValue); + Assert.That(occurrences, Has.Count.EqualTo(10), "There should be 10 occurrences of this event."); + } - var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, end, false).OrderBy(x => x).ToList(); + /// + /// Tests bug BYMONTH while FREQ=MONTHLY not working + /// + [Test, Category("Recurrence")] + public void BugByMonthWhileFreqIsMonthly() + { + var start = new DateTime(2020, 1, 1); + var end = new DateTime(2020, 12, 31); + var rpe = new RecurrencePatternEvaluator(new RecurrencePattern("FREQ=MONTHLY;BYDAY=MO;BYMONTH=1")); - Assert.That(recurringPeriods, Has.Count.EqualTo(4)); - Assert.Multiple(() => - { - Assert.That(recurringPeriods[0].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 6))); - Assert.That(recurringPeriods[1].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 13))); - Assert.That(recurringPeriods[2].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 20))); - Assert.That(recurringPeriods[3].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 27))); - }); - } + var recurringPeriods = rpe.Evaluate(new CalDateTime(start), start, end, false).OrderBy(x => x).ToList(); - /// - /// 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() + Assert.That(recurringPeriods, Has.Count.EqualTo(4)); + Assert.Multiple(() => { - 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); + Assert.That(recurringPeriods[0].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 6))); + Assert.That(recurringPeriods[1].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 13))); + Assert.That(recurringPeriods[2].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 20))); + Assert.That(recurringPeriods[3].StartTime, Is.EqualTo(new CalDateTime(2020, 1, 27))); + }); + } + + /// + /// 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); + var period = recurringPeriods.ElementAt(recurringPeriods.Count - 1); - Assert.That(period.StartTime, Is.EqualTo(new CalDateTime(2025, 11, 24, 9, 0, 0))); - } + Assert.That(period.StartTime, Is.EqualTo(new CalDateTime(2025, 11, 24, 9, 0, 0))); } + } - /// - /// 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() + /// + /// 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 { - var evt = new CalendarEvent - { - Start = new CalDateTime(2011, 1, 29, 11, 0, 0), - Duration = TimeSpan.FromHours(1.5), - Summary = "29th February Test" - }; + 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 }) - }; + 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); + evt.RecurrenceRules.Add(pattern); - var occurrences = evt.GetOccurrences(new DateTime(2011, 1, 1), new DateTime(2012, 1, 1)); - Assert.That(occurrences, Has.Count.EqualTo(10), "There should be 10 occurrences of this event, one for each month except February and December."); - } + var occurrences = evt.GetOccurrences(new DateTime(2011, 1, 1), new DateTime(2012, 1, 1)); + Assert.That(occurrences, Has.Count.EqualTo(10), "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() + /// + /// 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")) { - using (var sr = new StringReader("FREQ=WEEKLY;UNTIL=20251126")) - { - var serializer = new RecurrencePatternSerializer(); - var rp = (RecurrencePattern) serializer.Deserialize(sr); + var serializer = new RecurrencePatternSerializer(); + var rp = (RecurrencePattern) serializer.Deserialize(sr); - Assert.That(rp, Is.Not.Null); - Assert.That(rp.Until, Is.EqualTo(new DateTime(2025, 11, 26))); - } + Assert.That(rp, Is.Not.Null); + Assert.That(rp.Until, Is.EqualTo(new DateTime(2025, 11, 26))); } + } - /// - /// Tests Issue #432 - /// See https://github.com/rianjs/ical.net/issues/432 - /// - [Test, Category("Recurrence")] - public void Issue432() + /// + /// Tests Issue #432 + /// See https://github.com/rianjs/ical.net/issues/432 + /// + [Test, Category("Recurrence")] + public void Issue432() + { + var rrule = new RecurrencePattern { - var rrule = new RecurrencePattern - { - Frequency = FrequencyType.Daily, - Until = DateTime.Today.AddMonths(4), - }; - var vEvent = new CalendarEvent - { - Start = new CalDateTime(DateTime.Parse("2019-01-04T08:00Z")), - }; + Frequency = FrequencyType.Daily, + Until = DateTime.Today.AddMonths(4), + }; + var vEvent = new CalendarEvent + { + Start = new CalDateTime(DateTime.Parse("2019-01-04T08:00Z")), + }; - vEvent.RecurrenceRules.Add(rrule); + vEvent.RecurrenceRules.Add(rrule); - //Testing on both the first day and the next, results used to be different - for (var i = 0; i <= 1; i++) - { - var checkTime = DateTime.Parse("2019-01-04T08:00Z"); - checkTime = checkTime.AddDays(i); - //Valid asking for the exact moment - var occurrences = vEvent.GetOccurrences(checkTime, checkTime); - Assert.That(occurrences, Has.Count.EqualTo(1)); + //Testing on both the first day and the next, results used to be different + for (var i = 0; i <= 1; i++) + { + var checkTime = DateTime.Parse("2019-01-04T08:00Z"); + checkTime = checkTime.AddDays(i); + //Valid asking for the exact moment + var occurrences = vEvent.GetOccurrences(checkTime, checkTime); + Assert.That(occurrences, Has.Count.EqualTo(1)); - //Valid if asking for a range starting at the same moment - occurrences = vEvent.GetOccurrences(checkTime, checkTime.AddSeconds(1)); - Assert.That(occurrences, Has.Count.EqualTo(1)); + //Valid if asking for a range starting at the same moment + occurrences = vEvent.GetOccurrences(checkTime, checkTime.AddSeconds(1)); + Assert.That(occurrences, Has.Count.EqualTo(1)); - //Valid if asking for a range starting before and ending after - occurrences = vEvent.GetOccurrences(checkTime.AddSeconds(-1), checkTime.AddSeconds(1)); - Assert.That(occurrences, Has.Count.EqualTo(1)); + //Valid if asking for a range starting before and ending after + occurrences = vEvent.GetOccurrences(checkTime.AddSeconds(-1), checkTime.AddSeconds(1)); + Assert.That(occurrences, Has.Count.EqualTo(1)); - //Not valid if asking for a range starting before but ending at the same moment - occurrences = vEvent.GetOccurrences(checkTime.AddSeconds(-1), checkTime); - Assert.That(occurrences.Count, Is.EqualTo(0)); - } + //Not valid if asking for a range starting before but ending at the same moment + occurrences = vEvent.GetOccurrences(checkTime.AddSeconds(-1), checkTime); + Assert.That(occurrences.Count, Is.EqualTo(0)); } + } - [Test, Category("Recurrence")] - public void Issue432_AllDay() + [Test, Category("Recurrence")] + public void Issue432_AllDay() + { + var vEvent = new CalendarEvent { - var vEvent = new CalendarEvent - { - Start = new CalDateTime(DateTime.Parse("2020-01-11T00:00")), - End = new CalDateTime(DateTime.Parse("2020-01-11T00:00")), - IsAllDay = true, - }; + Start = new CalDateTime(DateTime.Parse("2020-01-11T00:00")), + End = new CalDateTime(DateTime.Parse("2020-01-11T00:00")), + IsAllDay = true, + }; - var occurrences = vEvent.GetOccurrences(DateTime.Parse("2020-01-10T00:00"), DateTime.Parse("2020-01-11T00:00")); - Assert.That(occurrences.Count, Is.EqualTo(0)); - } + var occurrences = vEvent.GetOccurrences(DateTime.Parse("2020-01-10T00:00"), DateTime.Parse("2020-01-11T00:00")); + Assert.That(occurrences.Count, Is.EqualTo(0)); + } - /// - /// Tests the iCal holidays downloaded from apple.com - /// - [Test, Category("Recurrence")] - public void UsHolidays() - { - var iCal = Calendar.Load(IcsFiles.UsHolidays); - Assert.That(iCal, Is.Not.Null, "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.That(occurrences, Has.Count.EqualTo(items.Count), "The number of holidays did not evaluate correctly."); - foreach (var o in occurrences) - { - var evt = o.Source as CalendarEvent; - Assert.That(evt, Is.Not.Null); - Assert.Multiple(() => - { - Assert.That(items.ContainsKey(evt.Summary), Is.True, "Holiday text '" + evt.Summary + "' did not match known holidays."); - Assert.That(o.Period.StartTime, Is.EqualTo(items[evt.Summary]), "Date/time of holiday '" + evt.Summary + "' did not match."); - }); - } + /// + /// Tests the iCal holidays downloaded from apple.com + /// + [Test, Category("Recurrence")] + public void UsHolidays() + { + var iCal = Calendar.Load(IcsFiles.UsHolidays); + Assert.That(iCal, Is.Not.Null, "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.That(occurrences, Has.Count.EqualTo(items.Count), "The number of holidays did not evaluate correctly."); + foreach (var o in occurrences) + { + var evt = o.Source as CalendarEvent; + Assert.That(evt, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(items.ContainsKey(evt.Summary), Is.True, "Holiday text '" + evt.Summary + "' did not match known holidays."); + Assert.That(o.Period.StartTime, Is.EqualTo(items[evt.Summary]), "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. - /// - [Category("Recurrence")] - [TestCase("SECONDLY", 1, true)] - [TestCase("MINUTELY", 60, true)] - [TestCase("HOURLY", 3600, true)] - [TestCase("DAILY", 24 * 3600, false)] - public void Evaluate1(string freq, int secsPerInterval, bool hasTime) - { - Calendar cal = new Calendar(); + /// + /// Ensures that the StartTime and EndTime of periods have + /// HasTime set to true if the beginning time had HasTime set + /// to false. + /// + [Category("Recurrence")] + [TestCase("SECONDLY", 1, true)] + [TestCase("MINUTELY", 60, true)] + [TestCase("HOURLY", 3600, true)] + [TestCase("DAILY", 24 * 3600, false)] + public void Evaluate1(string freq, int secsPerInterval, bool hasTime) + { + Calendar cal = new Calendar(); - CalendarEvent evt = cal.Create(); - evt.Summary = "Event summary"; + CalendarEvent evt = cal.Create(); + evt.Summary = "Event summary"; - // Start at midnight, UTC time - evt.Start = new CalDateTime(DateTime.SpecifyKind(DateTime.Today, DateTimeKind.Utc)) { HasTime = false }; + // Start at midnight, UTC time + evt.Start = new CalDateTime(DateTime.SpecifyKind(DateTime.Today, DateTimeKind.Utc)) { HasTime = false }; - // This case (DTSTART of type DATE and FREQ=MINUTELY) is undefined in RFC 5545. - // ical.net handles the case by pretending DTSTART has the time set to midnight. - evt.RecurrenceRules.Add(new RecurrencePattern($"FREQ={freq};INTERVAL=10;COUNT=5")); + // This case (DTSTART of type DATE and FREQ=MINUTELY) is undefined in RFC 5545. + // ical.net handles the case by pretending DTSTART has the time set to midnight. + evt.RecurrenceRules.Add(new RecurrencePattern($"FREQ={freq};INTERVAL=10;COUNT=5")); #pragma warning disable 0618 - evt.RecurrenceRules[0].RestrictionType = RecurrenceRestrictionType.NoRestriction; + evt.RecurrenceRules[0].RestrictionType = RecurrenceRestrictionType.NoRestriction; #pragma warning restore 0618 - var occurrences = evt.GetOccurrences(CalDateTime.Today.AddDays(-1), CalDateTime.Today.AddDays(100)) - .OrderBy(x => x) - .ToList(); - - var startDates = occurrences.Select(x => x.Period.StartTime.Value).ToList(); + var occurrences = evt.GetOccurrences(CalDateTime.Today.AddDays(-1), CalDateTime.Today.AddDays(100)) + .OrderBy(x => x) + .ToList(); - var expectedStartDates = Enumerable.Range(0, 5) - .Select(i => DateTime.SpecifyKind(DateTime.Today, DateTimeKind.Utc).AddSeconds(i * secsPerInterval * 10)) - .ToList(); + var startDates = occurrences.Select(x => x.Period.StartTime.Value).ToList(); - Assert.Multiple(() => - { - Assert.That(occurrences.Select(x => x.Period.StartTime.HasTime == hasTime), Is.All.True); - Assert.That(startDates, Is.EqualTo(expectedStartDates)); - }); - } + var expectedStartDates = Enumerable.Range(0, 5) + .Select(i => DateTime.SpecifyKind(DateTime.Today, DateTimeKind.Utc).AddSeconds(i * secsPerInterval * 10)) + .ToList(); - [Test, Category("Recurrence")] - public void RecurrencePattern1() + Assert.Multiple(() => { - // 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. - var pattern = new RecurrencePattern("FREQ=SECONDLY;INTERVAL=10"); + Assert.That(occurrences.Select(x => x.Period.StartTime.HasTime == hasTime), Is.All.True); + Assert.That(startDates, Is.EqualTo(expectedStartDates)); + }); + } + + [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. + var pattern = new RecurrencePattern("FREQ=SECONDLY;INTERVAL=10"); #pragma warning disable 0618 - pattern.RestrictionType = RecurrenceRestrictionType.NoRestriction; + pattern.RestrictionType = RecurrenceRestrictionType.NoRestriction; #pragma warning restore 0618 - var us = new CultureInfo("en-US"); + 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 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.That(evaluator, Is.Not.Null); + var evaluator = pattern.GetService(typeof(IEvaluator)) as IEvaluator; + Assert.That(evaluator, Is.Not.Null); - var occurrences = evaluator.Evaluate( + var occurrences = evaluator.Evaluate( startDate, DateUtil.SimpleDateTimeToMatch(fromDate, startDate), DateUtil.SimpleDateTimeToMatch(toDate, startDate), false) - .OrderBy(o => o.StartTime) - .ToList(); - Assert.That(occurrences, Has.Count.EqualTo(4)); - Assert.Multiple(() => - { - Assert.That(occurrences[0].StartTime, Is.EqualTo(new CalDateTime(DateTime.Parse("03/30/08 11:59:40 PM", us)))); - Assert.That(occurrences[1].StartTime, Is.EqualTo(new CalDateTime(DateTime.Parse("03/30/08 11:59:50 PM", us)))); - Assert.That(occurrences[2].StartTime, Is.EqualTo(new CalDateTime(DateTime.Parse("03/31/08 12:00:00 AM", us)))); - Assert.That(occurrences[3].StartTime, Is.EqualTo(new CalDateTime(DateTime.Parse("03/31/08 12:00:10 AM", us)))); - }); - } - - [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"); + .OrderBy(o => o.StartTime) + .ToList(); + Assert.That(occurrences, Has.Count.EqualTo(4)); + Assert.Multiple(() => + { + Assert.That(occurrences[0].StartTime, Is.EqualTo(new CalDateTime(DateTime.Parse("03/30/08 11:59:40 PM", us)))); + Assert.That(occurrences[1].StartTime, Is.EqualTo(new CalDateTime(DateTime.Parse("03/30/08 11:59:50 PM", us)))); + Assert.That(occurrences[2].StartTime, Is.EqualTo(new CalDateTime(DateTime.Parse("03/31/08 12:00:00 AM", us)))); + Assert.That(occurrences[3].StartTime, Is.EqualTo(new CalDateTime(DateTime.Parse("03/31/08 12:00:10 AM", us)))); + }); + } - var us = new CultureInfo("en-US"); + [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 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 us = new CultureInfo("en-US"); - var evaluator = pattern.GetService(typeof(IEvaluator)) as IEvaluator; - Assert.That(evaluator, Is.Not.Null); + 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 occurrences = evaluator.Evaluate( - startDate, - DateUtil.SimpleDateTimeToMatch(fromDate, startDate), - DateUtil.SimpleDateTimeToMatch(toDate, startDate), - false); - Assert.That(occurrences.Count, Is.Not.EqualTo(0)); - } + var evaluator = pattern.GetService(typeof(IEvaluator)) as IEvaluator; + Assert.That(evaluator, Is.Not.Null); - [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 occurrences = evaluator.Evaluate( + startDate, + DateUtil.SimpleDateTimeToMatch(fromDate, startDate), + DateUtil.SimpleDateTimeToMatch(toDate, startDate), + false); + Assert.That(occurrences.Count, Is.Not.EqualTo(0)); + } - 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); + [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 occurrences = evt.GetOccurrences(previousDateAndTime, end); - Assert.That(occurrences, Has.Count.EqualTo(5)); + 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); - occurrences = evt.GetOccurrences(previousDateOnly, end); - Assert.That(occurrences, Has.Count.EqualTo(5)); + var occurrences = evt.GetOccurrences(previousDateAndTime, end); + Assert.That(occurrences, Has.Count.EqualTo(5)); - occurrences = evt.GetOccurrences(laterDateOnly, end); - Assert.That(occurrences, Has.Count.EqualTo(4)); + occurrences = evt.GetOccurrences(previousDateOnly, end); + Assert.That(occurrences, Has.Count.EqualTo(5)); - occurrences = evt.GetOccurrences(laterDateAndTime, end); - Assert.That(occurrences, Has.Count.EqualTo(3)); + occurrences = evt.GetOccurrences(laterDateOnly, end); + Assert.That(occurrences, Has.Count.EqualTo(4)); - // Add ByHour "9" and "12" - evt.RecurrenceRules[0].ByHour.Add(9); - evt.RecurrenceRules[0].ByHour.Add(12); + occurrences = evt.GetOccurrences(laterDateAndTime, end); + Assert.That(occurrences, Has.Count.EqualTo(3)); - // Clear the evaluation so we can calculate recurrences again. - evt.ClearEvaluation(); + // Add ByHour "9" and "12" + evt.RecurrenceRules[0].ByHour.Add(9); + evt.RecurrenceRules[0].ByHour.Add(12); - occurrences = evt.GetOccurrences(previousDateAndTime, end); - Assert.That(occurrences, Has.Count.EqualTo(10)); + // Clear the evaluation so we can calculate recurrences again. + evt.ClearEvaluation(); - occurrences = evt.GetOccurrences(previousDateOnly, end); - Assert.That(occurrences, Has.Count.EqualTo(10)); + occurrences = evt.GetOccurrences(previousDateAndTime, end); + Assert.That(occurrences, Has.Count.EqualTo(10)); - occurrences = evt.GetOccurrences(laterDateOnly, end); - Assert.That(occurrences, Has.Count.EqualTo(8)); + occurrences = evt.GetOccurrences(previousDateOnly, end); + Assert.That(occurrences, Has.Count.EqualTo(10)); - occurrences = evt.GetOccurrences(laterDateAndTime, end); - Assert.That(occurrences, Has.Count.EqualTo(7)); - } + occurrences = evt.GetOccurrences(laterDateOnly, end); + Assert.That(occurrences, Has.Count.EqualTo(8)); - [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)); + occurrences = evt.GetOccurrences(laterDateAndTime, end); + Assert.That(occurrences, Has.Count.EqualTo(7)); + } - RecurrencePattern recur = new RecurrencePattern(); - evt.RecurrenceRules.Add(recur); + [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)); - Assert.That(() => - { - _ = evt.GetOccurrences(DateTime.Today.AddDays(1), DateTime.Today.AddDays(2)); - }, Throws.Exception, "An exception should be thrown when evaluating a recurrence with no specified FREQUENCY"); - } + RecurrencePattern recur = new RecurrencePattern(); + evt.RecurrenceRules.Add(recur); - [Test, Category("Recurrence")] - public void Test2() + Assert.That(() => { - 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); + _ = evt.GetOccurrences(DateTime.Today.AddDays(1), DateTime.Today.AddDays(2)); + }, Throws.Exception, "An exception should be thrown when evaluating a recurrence with no specified FREQUENCY"); + } - var serializer = new RecurrencePatternSerializer(); - Assert.That(string.Compare(serializer.SerializeToString(recur), "FREQ=DAILY;COUNT=3;BYDAY=MO,WE,FR", StringComparison.Ordinal) == 0, - Is.True, - "Serialized recurrence string is incorrect"); - } + [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.That(string.Compare(serializer.SerializeToString(recur), "FREQ=DAILY;COUNT=3;BYDAY=MO,WE,FR", StringComparison.Ordinal) == 0, + Is.True, + "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)); + [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; + rpattern.Frequency = FrequencyType.Weekly; - IDateTime evtStart = new CalDateTime(2006, 12, 1); - IDateTime evtEnd = new CalDateTime(2007, 1, 1); + IDateTime evtStart = new CalDateTime(2006, 12, 1); + IDateTime evtEnd = new CalDateTime(2007, 1, 1); - var evaluator = rpattern.GetService(typeof(IEvaluator)) as IEvaluator; - Assert.That(evaluator, Is.Not.Null); + var evaluator = rpattern.GetService(typeof(IEvaluator)) as IEvaluator; + Assert.That(evaluator, Is.Not.Null); - // Add the exception dates - var periods = evaluator.Evaluate( + // Add the exception dates + var periods = evaluator.Evaluate( evtStart, DateUtil.GetSimpleDateTimeData(evtStart), DateUtil.SimpleDateTimeToMatch(evtEnd, evtStart), false) - .OrderBy(p => p.StartTime) - .ToList(); - Assert.That(periods, Has.Count.EqualTo(10)); - Assert.Multiple(() => - { - Assert.That(periods[0].StartTime.Day, Is.EqualTo(2)); - Assert.That(periods[1].StartTime.Day, Is.EqualTo(3)); - Assert.That(periods[2].StartTime.Day, Is.EqualTo(9)); - Assert.That(periods[3].StartTime.Day, Is.EqualTo(10)); - Assert.That(periods[4].StartTime.Day, Is.EqualTo(16)); - Assert.That(periods[5].StartTime.Day, Is.EqualTo(17)); - Assert.That(periods[6].StartTime.Day, Is.EqualTo(23)); - Assert.That(periods[7].StartTime.Day, Is.EqualTo(24)); - Assert.That(periods[8].StartTime.Day, Is.EqualTo(30)); - Assert.That(periods[9].StartTime.Day, Is.EqualTo(31)); - }); - } + .OrderBy(p => p.StartTime) + .ToList(); + Assert.That(periods, Has.Count.EqualTo(10)); + Assert.Multiple(() => + { + Assert.That(periods[0].StartTime.Day, Is.EqualTo(2)); + Assert.That(periods[1].StartTime.Day, Is.EqualTo(3)); + Assert.That(periods[2].StartTime.Day, Is.EqualTo(9)); + Assert.That(periods[3].StartTime.Day, Is.EqualTo(10)); + Assert.That(periods[4].StartTime.Day, Is.EqualTo(16)); + Assert.That(periods[5].StartTime.Day, Is.EqualTo(17)); + Assert.That(periods[6].StartTime.Day, Is.EqualTo(23)); + Assert.That(periods[7].StartTime.Day, Is.EqualTo(24)); + Assert.That(periods[8].StartTime.Day, Is.EqualTo(30)); + Assert.That(periods[9].StartTime.Day, Is.EqualTo(31)); + }); + } - [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 + [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 @@ -3002,21 +3002,21 @@ public void ExDateShouldFilterOutAllPeriods() TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR"; - var calendar = Calendar.Load(ical); - var firstEvent = calendar.Events.First(); - var startSearch = new CalDateTime(2010, 1, 1, _tzid); - var endSearch = new CalDateTime(2016, 12, 31, _tzid); + var calendar = Calendar.Load(ical); + var firstEvent = calendar.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).ToList(); - Assert.That(occurrences.Count == 0, Is.True); - } + var occurrences = firstEvent.GetOccurrences(startSearch, endSearch).Select(o => o.Period).ToList(); + Assert.That(occurrences.Count == 0, Is.True); + } - [Test, Category("Recurrence")] - public void RDateShouldBeUnionedWithRecurrenceSet() - { - //Issues #118 and #107 on Github - const string ical = -@"BEGIN:VCALENDAR + [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 @@ -3031,32 +3031,32 @@ public void RDateShouldBeUnionedWithRecurrenceSet() END:VEVENT END:VCALENDAR"; - var calendar = Calendar.Load(ical); - var firstEvent = calendar.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 calendar = Calendar.Load(ical); + var firstEvent = calendar.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) - .OrderBy(p => p.StartTime) - .ToList(); + var occurrences = firstEvent.GetOccurrences(startSearch, endSearch) + .Select(o => o.Period) + .OrderBy(p => p.StartTime) + .ToList(); - var firstExpectedOccurrence = new CalDateTime(DateTime.Parse("2016-08-29T08:00:00"), _tzid); - Assert.That(occurrences.First().StartTime, Is.EqualTo(firstExpectedOccurrence)); + var firstExpectedOccurrence = new CalDateTime(DateTime.Parse("2016-08-29T08:00:00"), _tzid); + Assert.That(occurrences.First().StartTime, Is.EqualTo(firstExpectedOccurrence)); - var firstExpectedRDate = new CalDateTime(DateTime.Parse("2016-08-30T10:00:00"), _tzid); - Assert.That(occurrences[1].StartTime.Equals(firstExpectedRDate), Is.True); + var firstExpectedRDate = new CalDateTime(DateTime.Parse("2016-08-30T10:00:00"), _tzid); + Assert.That(occurrences[1].StartTime.Equals(firstExpectedRDate), Is.True); - var secondExpectedRDate = new CalDateTime(DateTime.Parse("2016-08-31T10:00:00"), _tzid); - Assert.That(occurrences[2].StartTime.Equals(secondExpectedRDate), Is.True); - } + var secondExpectedRDate = new CalDateTime(DateTime.Parse("2016-08-31T10:00:00"), _tzid); + Assert.That(occurrences[2].StartTime.Equals(secondExpectedRDate), Is.True); + } - [Test] - public void OccurrenceMustBeCompletelyContainedWithinSearchRange() - { - //https://github.com/rianjs/ical.net/issues/121 + [Test] + public void OccurrenceMustBeCompletelyContainedWithinSearchRange() + { + //https://github.com/rianjs/ical.net/issues/121 - const string ical = @"BEGIN:VCALENDAR + const string ical = @"BEGIN:VCALENDAR PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN VERSION:2.0 BEGIN:VEVENT @@ -3069,61 +3069,61 @@ public void OccurrenceMustBeCompletelyContainedWithinSearchRange() 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 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 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 deserializedCalendar = Calendar.Load(ical); - var firstEvent = deserializedCalendar.Events.First(); - var calendar = new Calendar(); - calendar.Events.Add(e); + var deserializedCalendar = Calendar.Load(ical); + var firstEvent = deserializedCalendar.Events.First(); + var calendar = new Calendar(); + calendar.Events.Add(e); - Assert.That(firstEvent, Is.EqualTo(e)); + Assert.That(firstEvent, Is.EqualTo(e)); - 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 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) - .OrderBy(p => p.StartTime) - .ToList(); + var lastExpected = new CalDateTime(DateTime.Parse("2016-08-31T07:00:00"), "UTC"); + var occurrences = firstEvent.GetOccurrences(startSearch, endSearch) + .Select(o => o.Period) + .OrderBy(p => p.StartTime) + .ToList(); - Assert.That(occurrences.Last().StartTime.Equals(lastExpected), Is.False); + Assert.That(occurrences.Last().StartTime.Equals(lastExpected), Is.False); - //Create 1 second of overlap - endSearch = new CalDateTime(endSearch.Value.AddSeconds(1), "UTC"); - occurrences = firstEvent.GetOccurrences(startSearch, endSearch) - .Select(o => o.Period) - .OrderBy(p => p.StartTime) - .ToList(); + //Create 1 second of overlap + endSearch = new CalDateTime(endSearch.Value.AddSeconds(1), "UTC"); + occurrences = firstEvent.GetOccurrences(startSearch, endSearch) + .Select(o => o.Period) + .OrderBy(p => p.StartTime) + .ToList(); - Assert.That(occurrences.Last().StartTime.Equals(lastExpected), Is.True); - } + Assert.That(occurrences.Last().StartTime.Equals(lastExpected), Is.True); + } - /// - /// Evaluate relevancy and validity of the request. - /// Find a solution for issue #120 or close forever - /// - [Test, Ignore("Turn on in v3", Until = "2024-12-31")] - public void EventsWithShareUidsShouldGenerateASingleRecurrenceSet() - { - //https://github.com/rianjs/ical.net/issues/120 dated Sep 5, 2016 - const string ical = -@"BEGIN:VCALENDAR + /// + /// Evaluate relevancy and validity of the request. + /// Find a solution for issue #120 or close forever + /// + [Test, Ignore("Turn on in v3", Until = "2024-12-31")] + public void EventsWithShareUidsShouldGenerateASingleRecurrenceSet() + { + //https://github.com/rianjs/ical.net/issues/120 dated Sep 5, 2016 + const string ical = + @"BEGIN:VCALENDAR PRODID:-//Google Inc//Google Calendar 70.9054//EN VERSION:2.0 CALSCALE:GREGORIAN @@ -3177,218 +3177,218 @@ public void EventsWithShareUidsShouldGenerateASingleRecurrenceSet() END:VEVENT END:VCALENDAR"; - var calendars = CalendarCollection.Load(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 calendars = CalendarCollection.Load(ical); + var events = calendars.SelectMany(c => c.Events).ToList(); - var occurrences = new HashSet(); + var startSearch = DateTime.Parse("2016-08-01T00:00:00"); + var endSearch = startSearch.AddDays(45); - var orderedOccurrences = occurrences - .Select(o => o.Period) - .OrderBy(p => p.StartTime) - .ToList(); + //The API should be something like: + //var occurrences = calendar.GetOccurrences(string eventUid, DateTime startSearch, DateTime endSearch); - 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.Multiple(() => - { - Assert.That(orderedOccurrences[3].StartTime, Is.EqualTo(expectedSept1Start)); - Assert.That(orderedOccurrences[3].EndTime, Is.EqualTo(expectedSept1End)); - }); + var occurrences = new HashSet(); - 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.Multiple(() => - { - Assert.That(orderedOccurrences[5].StartTime, Is.EqualTo(expectedSept3Start)); - Assert.That(orderedOccurrences[5].EndTime, Is.EqualTo(expectedSept3End)); - }); - } + var orderedOccurrences = occurrences + .Select(o => o.Period) + .OrderBy(p => p.StartTime) + .ToList(); - [Test] - public void AddExDateToEventAfterGetOccurrencesShouldRecomputeResult() + 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.Multiple(() => { - var searchStart = _now.AddDays(-1); - var searchEnd = _now.AddDays(7); - var e = GetEventWithRecurrenceRules(); - var occurrences = e.GetOccurrences(searchStart, searchEnd); - Assert.That(occurrences.Count == 5, Is.True); - - 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.That(occurrences.Count == 4, Is.True); - - //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.That(occurrences.Count == 3, Is.True); - } + Assert.That(orderedOccurrences[3].StartTime, Is.EqualTo(expectedSept1Start)); + Assert.That(orderedOccurrences[3].EndTime, Is.EqualTo(expectedSept1End)); + }); - private static readonly DateTime _now = DateTime.Now; - private static readonly DateTime _later = _now.AddHours(1); - private static CalendarEvent GetEventWithRecurrenceRules() + 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.Multiple(() => { - var dailyForFiveDays = new RecurrencePattern(FrequencyType.Daily, 1) - { - Count = 5, - }; + Assert.That(orderedOccurrences[5].StartTime, Is.EqualTo(expectedSept3Start)); + Assert.That(orderedOccurrences[5].EndTime, Is.EqualTo(expectedSept3End)); + }); + } - 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; - } + [Test] + public void AddExDateToEventAfterGetOccurrencesShouldRecomputeResult() + { + var searchStart = _now.AddDays(-1); + var searchEnd = _now.AddDays(7); + var e = GetEventWithRecurrenceRules(); + var occurrences = e.GetOccurrences(searchStart, searchEnd); + Assert.That(occurrences.Count == 5, Is.True); + + 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.That(occurrences.Count == 4, Is.True); + + //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.That(occurrences.Count == 3, Is.True); + } - [Test] - public void ExDateFold_Tests() + 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) { - var start = _now.AddYears(-1); - var end = start.AddHours(1); - var rrule = new RecurrencePattern(FrequencyType.Daily) { Until = start.AddYears(2) }; - var e = new CalendarEvent - { - DtStart = new CalDateTime(start), - DtEnd = new CalDateTime(end), - RecurrenceRules = new List { rrule } - }; - - var firstExclusion = new CalDateTime(start.AddDays(4)); - e.ExceptionDates = new List { new PeriodList { new Period(firstExclusion) } }; - var serialized = SerializationHelpers.SerializeToString(e); - Assert.That(Regex.Matches(serialized, "EXDATE:"), Has.Count.EqualTo(1)); - - var secondExclusion = new CalDateTime(start.AddDays(5)); - e.ExceptionDates.First().Add(new Period(secondExclusion)); - serialized = SerializationHelpers.SerializeToString(e); - Assert.That(Regex.Matches(serialized, "EXDATE:"), Has.Count.EqualTo(1)); - } + Count = 5, + }; - [Test] - public void ExDateTimeZone_Tests() + var calendarEvent = new CalendarEvent { - const string tzid = "Europe/Stockholm"; + Start = new CalDateTime(_now), + End = new CalDateTime(_later), + RecurrenceRules = new List { dailyForFiveDays }, + Resources = new List(new[] { "Foo", "Bar", "Baz" }), + }; + return calendarEvent; + } - //Repeat daily for 10 days - var rrule = GetSimpleRecurrencePattern(10); + [Test] + public void ExDateFold_Tests() + { + var start = _now.AddYears(-1); + var end = start.AddHours(1); + var rrule = new RecurrencePattern(FrequencyType.Daily) { Until = start.AddYears(2) }; + var e = new CalendarEvent + { + DtStart = new CalDateTime(start), + DtEnd = new CalDateTime(end), + RecurrenceRules = new List { rrule } + }; + + var firstExclusion = new CalDateTime(start.AddDays(4)); + e.ExceptionDates = new List { new PeriodList { new Period(firstExclusion) } }; + var serialized = SerializationHelpers.SerializeToString(e); + Assert.That(Regex.Matches(serialized, "EXDATE:"), Has.Count.EqualTo(1)); + + var secondExclusion = new CalDateTime(start.AddDays(5)); + e.ExceptionDates.First().Add(new Period(secondExclusion)); + serialized = SerializationHelpers.SerializeToString(e); + Assert.That(Regex.Matches(serialized, "EXDATE:"), Has.Count.EqualTo(1)); + } - var e = new CalendarEvent - { - DtStart = new CalDateTime(_now, tzid), - DtEnd = new CalDateTime(_later, tzid), - RecurrenceRules = new List { rrule }, - }; + [Test] + public void ExDateTimeZone_Tests() + { + const string tzid = "Europe/Stockholm"; + + //Repeat daily for 10 days + var rrule = GetSimpleRecurrencePattern(10); - var exceptionDateList = new PeriodList { TzId = tzid }; - exceptionDateList.Add(new Period(new CalDateTime(_now.AddDays(1)))); - e.ExceptionDates.Add(exceptionDateList); + var e = new CalendarEvent + { + DtStart = new CalDateTime(_now, tzid), + DtEnd = new CalDateTime(_later, tzid), + RecurrenceRules = new List { rrule }, + }; - var serialized = SerializationHelpers.SerializeToString(e); - const string expected = "TZID=Europe/Stockholm"; - Assert.That(Regex.Matches(serialized, expected), Has.Count.EqualTo(3)); + var exceptionDateList = new PeriodList { TzId = tzid }; + exceptionDateList.Add(new Period(new CalDateTime(_now.AddDays(1)))); + e.ExceptionDates.Add(exceptionDateList); - e.ExceptionDates.First().Add(new Period(new CalDateTime(_now.AddDays(2)))); - serialized = SerializationHelpers.SerializeToString(e); - Assert.That(Regex.Matches(serialized, expected), Has.Count.EqualTo(3)); - } + var serialized = SerializationHelpers.SerializeToString(e); + const string expected = "TZID=Europe/Stockholm"; + Assert.That(Regex.Matches(serialized, expected), Has.Count.EqualTo(3)); - [Test, Category("Recurrence")] - public void OneDayRange() + e.ExceptionDates.First().Add(new Period(new CalDateTime(_now.AddDays(2)))); + serialized = SerializationHelpers.SerializeToString(e); + Assert.That(Regex.Matches(serialized, expected), Has.Count.EqualTo(3)); + } + + [Test, Category("Recurrence")] + public void OneDayRange() + { + var vEvent = new CalendarEvent { - var vEvent = new CalendarEvent - { - Start = new CalDateTime(DateTime.Parse("2019-06-07 0:00:00")), - End = new CalDateTime(DateTime.Parse("2019-06-08 00:00:00")) - }; + Start = new CalDateTime(DateTime.Parse("2019-06-07 0:00:00")), + End = new CalDateTime(DateTime.Parse("2019-06-08 00:00:00")) + }; - //Testing on both the first day and the next, results used to be different - for (var i = 0; i <= 1; i++) - { - var checkTime = DateTime.Parse("2019-06-07 00:00:00"); - checkTime = checkTime.AddDays(i); + //Testing on both the first day and the next, results used to be different + for (var i = 0; i <= 1; i++) + { + var checkTime = DateTime.Parse("2019-06-07 00:00:00"); + checkTime = checkTime.AddDays(i); - //Valid if asking for a range starting at the same moment - var occurrences = vEvent.GetOccurrences(checkTime, checkTime.AddDays(1)); - Assert.That(occurrences, Has.Count.EqualTo(i == 0 ? 1 : 0)); - } + //Valid if asking for a range starting at the same moment + var occurrences = vEvent.GetOccurrences(checkTime, checkTime.AddDays(1)); + Assert.That(occurrences, Has.Count.EqualTo(i == 0 ? 1 : 0)); } + } - [Test, Category("Recurrence")] - public void SpecificMinute() + [Test, Category("Recurrence")] + public void SpecificMinute() + { + var rrule = new RecurrencePattern { - var rrule = new RecurrencePattern - { - Frequency = FrequencyType.Daily - }; - var vEvent = new CalendarEvent - { - Start = new CalDateTime(DateTime.Parse("2009-01-01 09:00:00")), - End = new CalDateTime(DateTime.Parse("2009-01-01 17:00:00")) - }; + Frequency = FrequencyType.Daily + }; + var vEvent = new CalendarEvent + { + Start = new CalDateTime(DateTime.Parse("2009-01-01 09:00:00")), + End = new CalDateTime(DateTime.Parse("2009-01-01 17:00:00")) + }; - vEvent.RecurrenceRules.Add(rrule); + vEvent.RecurrenceRules.Add(rrule); - // Exactly on start time - var testingTime = new DateTime(2019, 6, 7, 9, 0, 0); + // Exactly on start time + var testingTime = new DateTime(2019, 6, 7, 9, 0, 0); - var occurrences = vEvent.GetOccurrences(testingTime, testingTime); - Assert.That(occurrences, Has.Count.EqualTo(1)); + var occurrences = vEvent.GetOccurrences(testingTime, testingTime); + Assert.That(occurrences, Has.Count.EqualTo(1)); - // One second before end time - testingTime = new DateTime(2019, 6, 7, 16, 59, 59); + // One second before end time + testingTime = new DateTime(2019, 6, 7, 16, 59, 59); - occurrences = vEvent.GetOccurrences(testingTime, testingTime); - Assert.That(occurrences, Has.Count.EqualTo(1)); + occurrences = vEvent.GetOccurrences(testingTime, testingTime); + Assert.That(occurrences, Has.Count.EqualTo(1)); - // Exactly on end time - testingTime = new DateTime(2019, 6, 7, 17, 0, 0); + // Exactly on end time + testingTime = new DateTime(2019, 6, 7, 17, 0, 0); - occurrences = vEvent.GetOccurrences(testingTime, testingTime); - Assert.That(occurrences.Count, Is.EqualTo(0)); - } + occurrences = vEvent.GetOccurrences(testingTime, testingTime); + Assert.That(occurrences.Count, Is.EqualTo(0)); + } - private static RecurrencePattern GetSimpleRecurrencePattern(int count) => new RecurrencePattern(FrequencyType.Daily, 1) { Count = count, }; + private static RecurrencePattern GetSimpleRecurrencePattern(int count) => new RecurrencePattern(FrequencyType.Daily, 1) { Count = count, }; - private static CalendarEvent GetSimpleEvent() + private static CalendarEvent GetSimpleEvent() + { + var e = new CalendarEvent { - var e = new CalendarEvent - { - DtStart = new CalDateTime(_now, _tzid), - DtEnd = new CalDateTime(_later, _tzid), - }; - return e; - } + DtStart = new CalDateTime(_now, _tzid), + DtEnd = new CalDateTime(_later, _tzid), + }; + return e; + } - [Test] - public void RecurrenceRuleTests() - { - var five = GetSimpleRecurrencePattern(5); - var ten = GetSimpleRecurrencePattern(10); - Assert.That(ten, Is.Not.EqualTo(five)); - var eventA = GetSimpleEvent(); - eventA.RecurrenceRules.Add(five); - eventA.RecurrenceRules.Add(ten); + [Test] + public void RecurrenceRuleTests() + { + var five = GetSimpleRecurrencePattern(5); + var ten = GetSimpleRecurrencePattern(10); + Assert.That(ten, Is.Not.EqualTo(five)); + var eventA = GetSimpleEvent(); + eventA.RecurrenceRules.Add(five); + eventA.RecurrenceRules.Add(ten); - var eventB = GetSimpleEvent(); - eventB.RecurrenceRules.Add(ten); - eventB.RecurrenceRules.Add(five); + var eventB = GetSimpleEvent(); + eventB.RecurrenceRules.Add(ten); + eventB.RecurrenceRules.Add(five); - Assert.That(eventB, Is.EqualTo(eventA)); + Assert.That(eventB, Is.EqualTo(eventA)); - const string aString = @"BEGIN:VCALENDAR + const string aString = @"BEGIN:VCALENDAR PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN VERSION:2.0 BEGIN:VEVENT @@ -3408,7 +3408,7 @@ public void RecurrenceRuleTests() END:VEVENT END:VCALENDAR"; - const string bString = @"BEGIN:VCALENDAR + const string bString = @"BEGIN:VCALENDAR PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN VERSION:2.0 BEGIN:VEVENT @@ -3429,35 +3429,35 @@ public void RecurrenceRuleTests() END:VEVENT END:VCALENDAR"; - var simpleA = Calendar.Load(aString); - var normalA = Calendar.Load(aString); - var simpleB = Calendar.Load(bString); - var normalB = Calendar.Load(bString); - - var calendarList = new List { simpleA, normalA, simpleB, normalB }; - var eventList = new List - { - simpleA.Events.Single(), - normalA.Events.Single(), - simpleB.Events.Single(), - normalB.Events.Single(), - }; - - //GetHashCode tests also tests Equals() - var calendarSet = new HashSet(calendarList); - Assert.That(calendarSet, Has.Count.EqualTo(1)); - var eventSet = new HashSet(eventList); - Assert.That(eventSet, Has.Count.EqualTo(1)); - - var newEventList = new HashSet(); - newEventList.UnionWith(eventList); - Assert.That(newEventList, Has.Count.EqualTo(1)); - } + var simpleA = Calendar.Load(aString); + var normalA = Calendar.Load(aString); + var simpleB = Calendar.Load(bString); + var normalB = Calendar.Load(bString); + + var calendarList = new List { simpleA, normalA, simpleB, normalB }; + var eventList = new List + { + simpleA.Events.Single(), + normalA.Events.Single(), + simpleB.Events.Single(), + normalB.Events.Single(), + }; + + //GetHashCode tests also tests Equals() + var calendarSet = new HashSet(calendarList); + Assert.That(calendarSet, Has.Count.EqualTo(1)); + var eventSet = new HashSet(eventList); + Assert.That(eventSet, Has.Count.EqualTo(1)); + + var newEventList = new HashSet(); + newEventList.UnionWith(eventList); + Assert.That(newEventList, Has.Count.EqualTo(1)); + } - [Test] - public void ManyExclusionDatesEqualityTesting() - { - const string icalA = @"BEGIN:VCALENDAR + [Test] + public void ManyExclusionDatesEqualityTesting() + { + const string icalA = @"BEGIN:VCALENDAR PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN VERSION:2.0 BEGIN:VEVENT @@ -3479,7 +3479,7 @@ public void ManyExclusionDatesEqualityTesting() END:VEVENT END:VCALENDAR"; - const string icalB = @"BEGIN:VCALENDAR + const string icalB = @"BEGIN:VCALENDAR PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN VERSION:2.0 BEGIN:VEVENT @@ -3501,97 +3501,97 @@ public void ManyExclusionDatesEqualityTesting() END:VEVENT END:VCALENDAR"; - //The only textual difference between A and B is a different DTSTAMP, which is not considered significant for equality or hashing - - //Tautologies... - var collectionA = CalendarCollection.Load(icalA); - Assert.That(collectionA, Is.EqualTo(collectionA)); - Assert.That(collectionA.GetHashCode(), Is.EqualTo(collectionA.GetHashCode())); - var calendarA = collectionA.First(); - Assert.That(calendarA, Is.EqualTo(calendarA)); - Assert.That(calendarA.GetHashCode(), Is.EqualTo(calendarA.GetHashCode())); - var eventA = calendarA.Events.First(); - Assert.That(eventA, Is.EqualTo(eventA)); - Assert.That(eventA.GetHashCode(), Is.EqualTo(eventA.GetHashCode())); - - var collectionB = CalendarCollection.Load(icalB); - Assert.That(collectionB, Is.EqualTo(collectionB)); - Assert.That(collectionB.GetHashCode(), Is.EqualTo(collectionB.GetHashCode())); - var calendarB = collectionB.First(); - Assert.That(calendarB, Is.EqualTo(calendarB)); - Assert.That(calendarB.GetHashCode(), Is.EqualTo(calendarB.GetHashCode())); - var eventB = calendarB.Events.First(); - - Assert.Multiple(() => - { - //Comparing the two... - Assert.That(collectionB, Is.EqualTo(collectionA)); - Assert.That(collectionB.GetHashCode(), Is.EqualTo(collectionA.GetHashCode())); - Assert.That(calendarB, Is.EqualTo(calendarA)); - Assert.That(calendarB.GetHashCode(), Is.EqualTo(calendarA.GetHashCode())); - Assert.That(eventB, Is.EqualTo(eventA)); - Assert.That(eventB.GetHashCode(), Is.EqualTo(eventA.GetHashCode())); - }); + //The only textual difference between A and B is a different DTSTAMP, which is not considered significant for equality or hashing + + //Tautologies... + var collectionA = CalendarCollection.Load(icalA); + Assert.That(collectionA, Is.EqualTo(collectionA)); + Assert.That(collectionA.GetHashCode(), Is.EqualTo(collectionA.GetHashCode())); + var calendarA = collectionA.First(); + Assert.That(calendarA, Is.EqualTo(calendarA)); + Assert.That(calendarA.GetHashCode(), Is.EqualTo(calendarA.GetHashCode())); + var eventA = calendarA.Events.First(); + Assert.That(eventA, Is.EqualTo(eventA)); + Assert.That(eventA.GetHashCode(), Is.EqualTo(eventA.GetHashCode())); + + var collectionB = CalendarCollection.Load(icalB); + Assert.That(collectionB, Is.EqualTo(collectionB)); + Assert.That(collectionB.GetHashCode(), Is.EqualTo(collectionB.GetHashCode())); + var calendarB = collectionB.First(); + Assert.That(calendarB, Is.EqualTo(calendarB)); + Assert.That(calendarB.GetHashCode(), Is.EqualTo(calendarB.GetHashCode())); + var eventB = calendarB.Events.First(); + + Assert.Multiple(() => + { + //Comparing the two... + Assert.That(collectionB, Is.EqualTo(collectionA)); + Assert.That(collectionB.GetHashCode(), Is.EqualTo(collectionA.GetHashCode())); + Assert.That(calendarB, Is.EqualTo(calendarA)); + Assert.That(calendarB.GetHashCode(), Is.EqualTo(calendarA.GetHashCode())); + Assert.That(eventB, Is.EqualTo(eventA)); + Assert.That(eventB.GetHashCode(), Is.EqualTo(eventA.GetHashCode())); + }); - var exDatesA = eventA.ExceptionDates; - var exDatesB = eventB.ExceptionDates; - Assert.That(exDatesB, Is.EqualTo(exDatesA)); + var exDatesA = eventA.ExceptionDates; + var exDatesB = eventB.ExceptionDates; + Assert.That(exDatesB, Is.EqualTo(exDatesA)); - } + } - [Test, TestCaseSource(nameof(UntilTimeZoneSerializationTestCases))] - public void UntilTimeZoneSerializationTests(string tzid, DateTimeKind expectedKind) - { - var now = DateTime.SpecifyKind(DateTime.Parse("2017-11-08 10:30:00"), expectedKind); - var later = now.AddHours(1); + [Test, TestCaseSource(nameof(UntilTimeZoneSerializationTestCases))] + public void UntilTimeZoneSerializationTests(string tzid, DateTimeKind expectedKind) + { + var now = DateTime.SpecifyKind(DateTime.Parse("2017-11-08 10:30:00"), expectedKind); + var later = now.AddHours(1); - var until = DateTime.SpecifyKind(now.AddDays(7), expectedKind); + var until = DateTime.SpecifyKind(now.AddDays(7), expectedKind); - var rrule = new RecurrencePattern(FrequencyType.Daily) - { - Until = until, - }; - var e = new CalendarEvent - { - Start = new CalDateTime(now, tzid), - End = new CalDateTime(later, tzid) - }; - e.RecurrenceRules.Add(rrule); - var calendar = new Calendar - { - Events = { e }, - }; + var rrule = new RecurrencePattern(FrequencyType.Daily) + { + Until = until, + }; + var e = new CalendarEvent + { + Start = new CalDateTime(now, tzid), + End = new CalDateTime(later, tzid) + }; + e.RecurrenceRules.Add(rrule); + var calendar = new Calendar + { + Events = { e }, + }; - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(calendar); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(calendar); - const string contains = "20171108T103000"; - var expectedContains = expectedKind == DateTimeKind.Local - ? $"{contains}{SerializationConstants.LineBreak}" - : $"{contains}Z{SerializationConstants.LineBreak}"; + const string contains = "20171108T103000"; + var expectedContains = expectedKind == DateTimeKind.Local + ? $"{contains}{SerializationConstants.LineBreak}" + : $"{contains}Z{SerializationConstants.LineBreak}"; - Assert.That(serialized.Contains(expectedContains), Is.True); + Assert.That(serialized.Contains(expectedContains), Is.True); - var deserializedKind = Calendar.Load(serialized).Events.First().RecurrenceRules.First().Until.Kind; + var deserializedKind = Calendar.Load(serialized).Events.First().RecurrenceRules.First().Until.Kind; - Assert.That(deserializedKind, Is.EqualTo(expectedKind)); - } + Assert.That(deserializedKind, Is.EqualTo(expectedKind)); + } - public static IEnumerable UntilTimeZoneSerializationTestCases() - { - yield return new TestCaseData("America/New_York", DateTimeKind.Local) - .SetName("IANA time time zone results in a local DateTimeKind"); - yield return new TestCaseData("Eastern Standard Time", DateTimeKind.Local) - .SetName("BCL time zone results in a Local DateTimeKind"); - yield return new TestCaseData("UTC", DateTimeKind.Utc) - .SetName("UTC results in DateTimeKind.Utc"); - } + public static IEnumerable UntilTimeZoneSerializationTestCases() + { + yield return new TestCaseData("America/New_York", DateTimeKind.Local) + .SetName("IANA time time zone results in a local DateTimeKind"); + yield return new TestCaseData("Eastern Standard Time", DateTimeKind.Local) + .SetName("BCL time zone results in a Local DateTimeKind"); + yield return new TestCaseData("UTC", DateTimeKind.Utc) + .SetName("UTC results in DateTimeKind.Utc"); + } - [Test] - public void InclusiveRruleUntil() - { - const string icalText = @"BEGIN:VCALENDAR + [Test] + public void InclusiveRruleUntil() + { + const string icalText = @"BEGIN:VCALENDAR BEGIN:VEVENT DTSTART;VALUE=DATE:20180101 DTEND;VALUE=DATE:20180102 @@ -3609,124 +3609,123 @@ public void InclusiveRruleUntil() END:VEVENT END:VCALENDAR "; - const string timeZoneId = @"Eastern Standard Time"; - var calendar = Calendar.Load(icalText); - var firstEvent = calendar.Events.First(); - var startSearch = new CalDateTime(DateTime.Parse("2017-07-01T00:00:00"), timeZoneId); - var endSearch = new CalDateTime(DateTime.Parse("2018-07-01T00:00:00"), timeZoneId); - - var occurrences = firstEvent.GetOccurrences(startSearch, endSearch); - Assert.That(occurrences, Has.Count.EqualTo(5)); - } + const string timeZoneId = @"Eastern Standard Time"; + var calendar = Calendar.Load(icalText); + var firstEvent = calendar.Events.First(); + var startSearch = new CalDateTime(DateTime.Parse("2017-07-01T00:00:00"), timeZoneId); + var endSearch = new CalDateTime(DateTime.Parse("2018-07-01T00:00:00"), timeZoneId); + + var occurrences = firstEvent.GetOccurrences(startSearch, endSearch); + Assert.That(occurrences, Has.Count.EqualTo(5)); + } - public class RecurrenceTestCase - { - public int LineNumber { get; set; } + public class RecurrenceTestCase + { + public int LineNumber { get; set; } - public string RRule { get; set; } + public string RRule { get; set; } - public CalDateTime DtStart { get; set; } + public CalDateTime DtStart { get; set; } - public CalDateTime StartAt { get; set; } + public CalDateTime StartAt { get; set; } - public IReadOnlyList Instances { get; set; } + public IReadOnlyList Instances { get; set; } - public override string ToString() - => $"Line {LineNumber}: {DtStart}, {RRule}"; - } + public override string ToString() + => $"Line {LineNumber}: {DtStart}, {RRule}"; + } - private static IEnumerable ParseTestCaseFile(string fileContent) - { - RecurrenceTestCase current = null; + private static IEnumerable ParseTestCaseFile(string fileContent) + { + RecurrenceTestCase current = null; - var rd = new StringReader(fileContent); - var lineNo = 0; + var rd = new StringReader(fileContent); + var lineNo = 0; - for (string line = rd.ReadLine(); line != null; line = rd.ReadLine()) - { - lineNo++; + for (string line = rd.ReadLine(); line != null; line = rd.ReadLine()) + { + lineNo++; - if (string.IsNullOrEmpty(line)) + if (string.IsNullOrEmpty(line)) + { + if (current != null) { - if (current != null) - { - yield return current; - current = null; - } - continue; + yield return current; + current = null; } + continue; + } - if (line.StartsWith("#")) - continue; + if (line.StartsWith("#")) + continue; - current = current ?? new RecurrenceTestCase(); + current = current ?? new RecurrenceTestCase(); - var m = Regex.Match(line, @"^(?[A-Z-]+):(?.*)$"); - if (!m.Success) - continue; + var m = Regex.Match(line, @"^(?[A-Z-]+):(?.*)$"); + if (!m.Success) + continue; - var hdr = m.Groups["h"].Value; - var val = m.Groups["v"].Value; + var hdr = m.Groups["h"].Value; + var val = m.Groups["v"].Value; - switch (hdr) - { - case "RRULE": - current.RRule = val; - current.LineNumber = lineNo; - break; - - case "DTSTART": - current.DtStart = new CalDateTime(val) { TzId = "UTC" }; - break; - - case "START-AT": - current.StartAt = new CalDateTime(val) { TzId = "UTC" }; - break; - - case "INSTANCES": - current.Instances = val.Split(',').Select(dt => new CalDateTime(dt) { TzId = "UTC" }).ToList(); - break; - } + switch (hdr) + { + case "RRULE": + current.RRule = val; + current.LineNumber = lineNo; + break; + + case "DTSTART": + current.DtStart = new CalDateTime(val) { TzId = "UTC" }; + break; + + case "START-AT": + current.StartAt = new CalDateTime(val) { TzId = "UTC" }; + break; + + case "INSTANCES": + current.Instances = val.Split(',').Select(dt => new CalDateTime(dt) { TzId = "UTC" }).ToList(); + break; } - - if (current != null) - yield return current; } - private static IEnumerable TestLibicalTestCasesSource - => ParseTestCaseFile(IcsFiles.LibicalIcalrecurTest); + if (current != null) + yield return current; + } + + private static IEnumerable TestLibicalTestCasesSource + => ParseTestCaseFile(IcsFiles.LibicalIcalrecurTest); - [TestCaseSource(nameof(TestLibicalTestCasesSource))] - public void TestLibicalTestCases(RecurrenceTestCase testCase) - { - ExecuteRecurrenceTestCase(testCase); - } + [TestCaseSource(nameof(TestLibicalTestCasesSource))] + public void TestLibicalTestCases(RecurrenceTestCase testCase) + { + ExecuteRecurrenceTestCase(testCase); + } - private static IEnumerable TestFileBasedRecurrenceTestCaseSource - => ParseTestCaseFile(IcsFiles.RecurrrenceTestCases); + private static IEnumerable TestFileBasedRecurrenceTestCaseSource + => ParseTestCaseFile(IcsFiles.RecurrrenceTestCases); - [TestCaseSource(nameof(TestFileBasedRecurrenceTestCaseSource))] - public void TestFileBasedRecurrenceTestCase(RecurrenceTestCase testCase) - => ExecuteRecurrenceTestCase(testCase); + [TestCaseSource(nameof(TestFileBasedRecurrenceTestCaseSource))] + public void TestFileBasedRecurrenceTestCase(RecurrenceTestCase testCase) + => ExecuteRecurrenceTestCase(testCase); - public void ExecuteRecurrenceTestCase(RecurrenceTestCase testCase) - { - Calendar cal = new Calendar(); + public void ExecuteRecurrenceTestCase(RecurrenceTestCase testCase) + { + Calendar cal = new Calendar(); - CalendarEvent evt = cal.Create(); - evt.Summary = "Event summary"; + CalendarEvent evt = cal.Create(); + evt.Summary = "Event summary"; - // Start at midnight, UTC time - evt.Start = testCase.DtStart; - evt.RecurrenceRules.Add(new RecurrencePattern(testCase.RRule)); + // Start at midnight, UTC time + evt.Start = testCase.DtStart; + evt.RecurrenceRules.Add(new RecurrencePattern(testCase.RRule)); - var occurrences = evt.GetOccurrences(testCase.StartAt?.Value ?? DateTime.MinValue, DateTime.MaxValue) - .OrderBy(x => x) - .ToList(); + var occurrences = evt.GetOccurrences(testCase.StartAt?.Value ?? DateTime.MinValue, DateTime.MaxValue) + .OrderBy(x => x) + .ToList(); - var startDates = occurrences.Select(x => x.Period.StartTime).ToList(); + var startDates = occurrences.Select(x => x.Period.StartTime).ToList(); - Assert.That(startDates, Is.EqualTo(testCase.Instances)); - } + Assert.That(startDates, Is.EqualTo(testCase.Instances)); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/SerializationHelpers.cs b/Ical.Net.Tests/SerializationHelpers.cs index e36d319dc..6900b1442 100644 --- a/Ical.Net.Tests/SerializationHelpers.cs +++ b/Ical.Net.Tests/SerializationHelpers.cs @@ -6,14 +6,13 @@ using Ical.Net.CalendarComponents; using Ical.Net.Serialization; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +internal class SerializationHelpers { - internal class SerializationHelpers - { - public static string SerializeToString(CalendarEvent calendarEvent) - => SerializeToString(new Calendar { Events = { calendarEvent } }); + public static string SerializeToString(CalendarEvent calendarEvent) + => SerializeToString(new Calendar { Events = { calendarEvent } }); - public static string SerializeToString(Calendar iCalendar) - => new CalendarSerializer().SerializeToString(iCalendar); - } -} + public static string SerializeToString(Calendar iCalendar) + => new CalendarSerializer().SerializeToString(iCalendar); +} \ No newline at end of file diff --git a/Ical.Net.Tests/SerializationTests.cs b/Ical.Net.Tests/SerializationTests.cs index 019891373..fa6f74f4c 100644 --- a/Ical.Net.Tests/SerializationTests.cs +++ b/Ical.Net.Tests/SerializationTests.cs @@ -17,416 +17,416 @@ using Ical.Net.Utility; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +[TestFixture] +public class SerializationTests { - [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 string SerializeToString(CalendarEvent e) => SerializeToString(new Calendar { Events = { e } }); + private static CalendarEvent GetSimpleEvent() => new CalendarEvent { DtStart = new CalDateTime(_nowTime), DtEnd = new CalDateTime(_later), Duration = _later - _nowTime }; + private static Calendar UnserializeCalendar(string s) => Calendar.Load(s); + + internal static void CompareCalendars(Calendar cal1, Calendar cal2) { - 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 string SerializeToString(CalendarEvent e) => SerializeToString(new Calendar { Events = { e } }); - private static CalendarEvent GetSimpleEvent() => new CalendarEvent { DtStart = new CalDateTime(_nowTime), DtEnd = new CalDateTime(_later), Duration = _later - _nowTime }; - private static Calendar UnserializeCalendar(string s) => Calendar.Load(s); - - internal static void CompareCalendars(Calendar cal1, Calendar cal2) - { - CompareComponents(cal1, cal2); + CompareComponents(cal1, cal2); - Assert.That(cal2.Children, Has.Count.EqualTo(cal1.Children.Count), "Children count is different between calendars."); + Assert.That(cal2.Children, Has.Count.EqualTo(cal1.Children.Count), "Children count is different between calendars."); - for (var i = 0; i < cal1.Children.Count; i++) + 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) { - var component1 = cal1.Children[i] as ICalendarComponent; - var component2 = cal2.Children[i] as ICalendarComponent; - if (component1 != null && component2 != null) - { - CompareComponents(component1, component2); - } + CompareComponents(component1, component2); } } + } - internal static void CompareComponents(ICalendarComponent cb1, ICalendarComponent cb2) + internal static void CompareComponents(ICalendarComponent cb1, ICalendarComponent cb2) + { + foreach (var p1 in cb1.Properties) { - foreach (var p1 in cb1.Properties) + var isMatch = false; + foreach (var p2 in cb2.Properties.AllOf(p1.Name)) { - var isMatch = false; - foreach (var p2 in cb2.Properties.AllOf(p1.Name)) + Assert.That(p2, Is.EqualTo(p1), "The properties '" + p1.Name + "' are not equal."); + if (p1.Value is IComparable) { - Assert.That(p2, Is.EqualTo(p1), "The properties '" + p1.Name + "' are not equal."); - if (p1.Value is IComparable) - { - if (((IComparable) p1.Value).CompareTo(p2.Value) != 0) - continue; - } - else if (p1.Value is IEnumerable) - { - CompareEnumerables((IEnumerable) p1.Value, (IEnumerable) p2.Value, p1.Name); - } - else - { - Assert.That(p2.Value, Is.EqualTo(p1.Value), - "The '" + p1.Name + "' property values are not equal."); - } - - isMatch = true; - break; + if (((IComparable) p1.Value).CompareTo(p2.Value) != 0) + continue; } - - Assert.That(isMatch, Is.True, "Could not find a matching property - " + p1.Name + ":" + (p1.Value?.ToString() ?? string.Empty)); - } - - Assert.That(cb2.Children, Has.Count.EqualTo(cb1.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) + else if (p1.Value is IEnumerable) { - CompareComponents(child1, child2); + CompareEnumerables((IEnumerable) p1.Value, (IEnumerable) p2.Value, p1.Name); } else { - Assert.That(child2, Is.EqualTo(child1), "The child objects are not equal."); + Assert.That(p2.Value, Is.EqualTo(p1.Value), + "The '" + p1.Name + "' property values are not equal."); } + + isMatch = true; + break; } + + Assert.That(isMatch, Is.True, "Could not find a matching property - " + p1.Name + ":" + (p1.Value?.ToString() ?? string.Empty)); } - public static void CompareEnumerables(IEnumerable a1, IEnumerable a2, string value) + Assert.That(cb2.Children, Has.Count.EqualTo(cb1.Children.Count), "The number of children are not equal."); + for (var i = 0; i < cb1.Children.Count; i++) { - if (a1 == null && a2 == null) + var child1 = cb1.Children[i] as ICalendarComponent; + var child2 = cb2.Children[i] as ICalendarComponent; + if (child1 != null && child2 != null) { - return; + CompareComponents(child1, child2); } - - Assert.That((a1 == null && a2 != null) || (a1 != null && a2 == null), Is.False, value + " do not match - one item is null"); - - var enum1 = a1.GetEnumerator(); - var enum2 = a2.GetEnumerator(); - - while (enum1.MoveNext() && enum2.MoveNext()) + else { - Assert.That(enum2.Current, Is.EqualTo(enum1.Current), value + " do not match"); + Assert.That(child2, Is.EqualTo(child1), "The child objects are not equal."); } } + } - public static string InspectSerializedSection(string serialized, string sectionName, IEnumerable elements) + public static void CompareEnumerables(IEnumerable a1, IEnumerable a2, string value) + { + if (a1 == null && a2 == null) { - const string notFound = "expected '{0}' not found"; - var searchFor = "BEGIN:" + sectionName; - var begin = serialized.IndexOf(searchFor); - Assert.That(begin, Is.Not.EqualTo(-1), () => string.Format(notFound, searchFor)); + return; + } - searchFor = "END:" + sectionName; - var end = serialized.IndexOf(searchFor, begin); - Assert.That(end, Is.Not.EqualTo(-1), () => string.Format(notFound, searchFor)); + Assert.That((a1 == null && a2 != null) || (a1 != null && a2 == null), Is.False, value + " do not match - one item is null"); - var searchRegion = serialized.Substring(begin, end - begin + searchFor.Length); + var enum1 = a1.GetEnumerator(); + var enum2 = a2.GetEnumerator(); - foreach (var e in elements) - { - Assert.That(searchRegion.Contains(SerializationConstants.LineBreak + e + SerializationConstants.LineBreak), Is.True, () => string.Format(notFound, e)); - } - - return searchRegion; + while (enum1.MoveNext() && enum2.MoveNext()) + { + Assert.That(enum2.Current, Is.EqualTo(enum1.Current), value + " do not match"); } + } - //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 - private 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.IsUtc) - { - return returnVar + 'Z'; - } + 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.That(begin, Is.Not.EqualTo(-1), () => string.Format(notFound, searchFor)); - return string.IsNullOrEmpty(cdt.TzId) - ? returnVar - : $"TZID={cdt.TzId}:{returnVar}"; - } + searchFor = "END:" + sectionName; + var end = serialized.IndexOf(searchFor, begin); + Assert.That(end, Is.Not.EqualTo(-1), () => string.Format(notFound, searchFor)); - //This method needs renaming - private static Dictionary GetValues(string serialized, string name, string value) + var searchRegion = serialized.Substring(begin, end - begin + searchFor.Length); + + foreach (var e in elements) { - 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.That(match.Success, Is.True, $"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); + Assert.That(searchRegion.Contains(SerializationConstants.LineBreak + e + SerializationConstants.LineBreak), Is.True, () => string.Format(notFound, e)); } - [Test, Category("Serialization"), Ignore("TODO: standard time, for NZ standard time (current example)")] - public void TimeZoneSerialize() + 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 + private 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.IsUtc) { - //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); - - 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" - }); + return returnVar + 'Z'; + } + + return string.IsNullOrEmpty(cdt.TzId) + ? returnVar + : $"TZID={cdt.TzId}:{returnVar}"; + } + //This method needs renaming + private 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.That(match.Success, Is.True, $"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); + } - InspectSerializedSection(vTimezone, "DAYLIGHT", new[] { "TZNAME:" + tzi.DaylightName, "TZOFFSETFROM:" + o }); - } - [Test, Category("Serialization")] - public void SerializeDeserialize() + [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 { - //ToDo: This test is broken as of 2016-07-13 - var cal1 = new Calendar - { - Method = "PUBLISH", - Version = "2.0" - }; + 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 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); - var cal2 = Calendar.Load(serializedCalendar); - CompareCalendars(cal1, cal2); - } + var serializer = new CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(cal); + + var vTimezone = InspectSerializedSection(serializedCalendar, "VTIMEZONE", new[] { "TZID:" + tz.TzId }); + var o = tzi.BaseUtcOffset.ToString("hhmm", CultureInfo.InvariantCulture); - [Test, Category("Serialization")] - public void EventPropertiesSerialized() + 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 { - //ToDo: This test is broken as of 2016-07-13 - var cal = new Calendar - { - Method = "PUBLISH", - Version = "2.0" - }; + 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); - - Assert.Multiple(() => - { - Assert.That(serializedCalendar.StartsWith("BEGIN:VCALENDAR"), Is.True); - Assert.That(serializedCalendar.EndsWith("END:VCALENDAR" + SerializationConstants.LineBreak), Is.True); - }); + 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); + var cal2 = Calendar.Load(serializedCalendar); + CompareCalendars(cal1, cal2); + } - var expectProperties = new[] { "METHOD:PUBLISH", "VERSION:2.0" }; + [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" + }; - foreach (var p in expectProperties) - { - Assert.That(serializedCalendar.Contains(SerializationConstants.LineBreak + p + SerializationConstants.LineBreak), Is.True, "expected '" + p + "' not found"); - } + 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); + + Assert.Multiple(() => + { + Assert.That(serializedCalendar.StartsWith("BEGIN:VCALENDAR"), Is.True); + Assert.That(serializedCalendar.EndsWith("END:VCALENDAR" + SerializationConstants.LineBreak), Is.True); + }); - 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() - }); - } + var expectProperties = new[] { "METHOD:PUBLISH", "VERSION:2.0" }; - private static readonly IList _attendees = new List + foreach (var p in expectProperties) { - new Attendee("MAILTO:james@example.com") - { - CommonName = "James", - Role = ParticipationRole.RequiredParticipant, - Rsvp = true, - ParticipationStatus = EventParticipationStatus.Tentative - }, - new Attendee("MAILTO:mary@example.com") + Assert.That(serializedCalendar.Contains(SerializationConstants.LineBreak + p + SerializationConstants.LineBreak), Is.True, "expected '" + p + "' not found"); + } + + InspectSerializedSection(serializedCalendar, "VEVENT", + new[] { - CommonName = "Mary", - Role = ParticipationRole.RequiredParticipant, - Rsvp = true, - ParticipationStatus = EventParticipationStatus.Accepted - } - }.AsReadOnly(); + "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() + }); + } - [Test, Category("Serialization")] - public void AttendeesSerialized() + private static readonly IList _attendees = new List + { + new Attendee("MAILTO:james@example.com") { - var cal = new Calendar - { - Method = "REQUEST", - Version = "2.0" - }; + CommonName = "James", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Tentative + }, + new Attendee("MAILTO:mary@example.com") + { + CommonName = "Mary", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Accepted + } + }.AsReadOnly(); - var evt = AttendeeTest.VEventFactory(); - cal.Events.Add(evt); - // new Uri() creates lowercase for the "MAILTO:" part - // according to the RFC 2368 specification - const string org = "MAILTO:james@example.com"; - evt.Organizer = new Organizer(org); + [Test, Category("Serialization")] + public void AttendeesSerialized() + { + var cal = new Calendar + { + Method = "REQUEST", + Version = "2.0" + }; - evt.Attendees.AddRange(_attendees); + var evt = AttendeeTest.VEventFactory(); + cal.Events.Add(evt); + // new Uri() creates lowercase for the "MAILTO:" part + // according to the RFC 2368 specification + const string org = "MAILTO:james@example.com"; + evt.Organizer = new Organizer(org); - // Changing the ParticipationStatus just keeps the last status - evt.Attendees[0].ParticipationStatus = EventParticipationStatus.Declined; + evt.Attendees.AddRange(_attendees); - var serializer = new CalendarSerializer(); - var serializedCalendar = serializer.SerializeToString(cal); + // Changing the ParticipationStatus just keeps the last status + evt.Attendees[0].ParticipationStatus = EventParticipationStatus.Declined; - var vEvt = InspectSerializedSection(serializedCalendar, "VEVENT", new[] { "ORGANIZER:" + org }); + var serializer = new CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(cal); - foreach (var a in evt.Attendees) + var vEvt = InspectSerializedSection(serializedCalendar, "VEVENT", new[] { "ORGANIZER:" + org }); + + foreach (var a in evt.Attendees) + { + var vals = GetValues(vEvt, "ATTENDEE", a.Value.ToString()); + foreach (var v in new Dictionary + { + ["CN"] = a.CommonName, + ["ROLE"] = a.Role, + ["RSVP"] = a.Rsvp.ToString() + .ToUpperInvariant(), + ["PARTSTAT"] = a.ParticipationStatus + }) { - var vals = GetValues(vEvt, "ATTENDEE", a.Value.ToString()); - foreach (var v in new Dictionary - { - ["CN"] = a.CommonName, - ["ROLE"] = a.Role, - ["RSVP"] = a.Rsvp.ToString() - .ToUpperInvariant(), - ["PARTSTAT"] = a.ParticipationStatus - }) + Assert.Multiple(() => { - Assert.Multiple(() => - { - Assert.That(vals.ContainsKey(v.Key), Is.True, $"could not find key '{v.Key}'"); - Assert.That(vals[v.Key], Is.EqualTo(v.Value), $"ATTENDEE prop '{v.Key}' differ"); - }); - } + Assert.That(vals.ContainsKey(v.Key), Is.True, $"could not find key '{v.Key}'"); + Assert.That(vals[v.Key], Is.EqualTo(v.Value), $"ATTENDEE prop '{v.Key}' differ"); + }); } } + } - //todo test event: - //-GeographicLocation - //-Alarm + //todo test event: + //-GeographicLocation + //-Alarm - [Test] - public void ZeroTimeSpan_Test() - { - var result = new TimeSpanSerializer().SerializeToString(TimeSpan.Zero); - Assert.That("P0D".Equals(result, StringComparison.Ordinal), Is.True); - } + [Test] + public void ZeroTimeSpan_Test() + { + var result = new TimeSpanSerializer().SerializeToString(TimeSpan.Zero); + Assert.That("P0D".Equals(result, StringComparison.Ordinal), Is.True); + } - [Test] - public void DurationIsStable_Tests() + [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.Multiple(() => { - var e = GetSimpleEvent(); - var originalDuration = e.Duration; - var c = new Calendar(); - c.Events.Add(e); - var serialized = SerializeToString(c); - Assert.Multiple(() => - { - Assert.That(e.Duration, Is.EqualTo(originalDuration)); - Assert.That(!serialized.Contains("DURATION"), Is.True); - }); - } + Assert.That(e.Duration, Is.EqualTo(originalDuration)); + Assert.That(!serialized.Contains("DURATION"), Is.True); + }); + } - [Test] - public void EventStatusAllCaps() - { - var e = GetSimpleEvent(); - e.Status = EventStatus.Confirmed; - var serialized = SerializeToString(e); - Assert.That(serialized.Contains(EventStatus.Confirmed, EventStatus.Comparison), Is.True); - - var calendar = UnserializeCalendar(serialized); - var eventStatus = calendar.Events.First().Status; - Assert.That(string.Equals(EventStatus.Confirmed, eventStatus, EventStatus.Comparison), Is.True); - } + [Test] + public void EventStatusAllCaps() + { + var e = GetSimpleEvent(); + e.Status = EventStatus.Confirmed; + var serialized = SerializeToString(e); + Assert.That(serialized.Contains(EventStatus.Confirmed, EventStatus.Comparison), Is.True); + + var calendar = UnserializeCalendar(serialized); + var eventStatus = calendar.Events.First().Status; + Assert.That(string.Equals(EventStatus.Confirmed, eventStatus, EventStatus.Comparison), Is.True); + } - [Test] - public void ToDoStatusAllCaps() + [Test] + public void ToDoStatusAllCaps() + { + var component = new Todo { - var component = new Todo - { - Status = TodoStatus.NeedsAction - }; + Status = TodoStatus.NeedsAction + }; - var c = new Calendar { Todos = { component } }; - var serialized = SerializeToString(c); - Assert.That(serialized.Contains(TodoStatus.NeedsAction, TodoStatus.Comparison), Is.True); + var c = new Calendar { Todos = { component } }; + var serialized = SerializeToString(c); + Assert.That(serialized.Contains(TodoStatus.NeedsAction, TodoStatus.Comparison), Is.True); - var calendar = UnserializeCalendar(serialized); - var status = calendar.Todos.First().Status; - Assert.That(string.Equals(TodoStatus.NeedsAction, status, TodoStatus.Comparison), Is.True); - } + var calendar = UnserializeCalendar(serialized); + var status = calendar.Todos.First().Status; + Assert.That(string.Equals(TodoStatus.NeedsAction, status, TodoStatus.Comparison), Is.True); + } - [Test] - public void JournalStatusAllCaps() + [Test] + public void JournalStatusAllCaps() + { + var component = new Journal { - var component = new Journal - { - Status = JournalStatus.Final, - }; + Status = JournalStatus.Final, + }; - var c = new Calendar { Journals = { component } }; - var serialized = SerializeToString(c); - Assert.That(serialized.Contains(JournalStatus.Final, JournalStatus.Comparison), Is.True); + var c = new Calendar { Journals = { component } }; + var serialized = SerializeToString(c); + Assert.That(serialized.Contains(JournalStatus.Final, JournalStatus.Comparison), Is.True); - var calendar = UnserializeCalendar(serialized); - var status = calendar.Journals.First().Status; - Assert.That(string.Equals(JournalStatus.Final, status, JournalStatus.Comparison), Is.True); - } + var calendar = UnserializeCalendar(serialized); + var status = calendar.Journals.First().Status; + Assert.That(string.Equals(JournalStatus.Final, status, JournalStatus.Comparison), Is.True); + } - [Test] - public void UnicodeDescription() - { - const string ics = @"BEGIN:VEVENT + [Test] + public void UnicodeDescription() + { + const string ics = @"BEGIN:VEVENT DTSTAMP:20171120T124856Z DTSTART;TZID=Europe/Helsinki:20160707T110000 DTEND;TZID=Europe/Helsinki:20160707T140000 @@ -448,21 +448,21 @@ xt some tex�t some text. TRANSP:TRANSPARENT STATUS:CONFIRMED END:VEVENT"; - var deserializedEvent = Calendar.Load(ics).Single(); - - Assert.Multiple(() => - { - Assert.That(deserializedEvent.Description.Contains("\t"), Is.True); - Assert.That(deserializedEvent.Description.Contains("�"), Is.True); - Assert.That(deserializedEvent.Description.Contains("�"), Is.True); - }); - } + var deserializedEvent = Calendar.Load(ics).Single(); - [Test] - public void TestStandardDaylightTimeZoneInfoDeserialization() + Assert.Multiple(() => { + Assert.That(deserializedEvent.Description.Contains("\t"), Is.True); + Assert.That(deserializedEvent.Description.Contains("�"), Is.True); + Assert.That(deserializedEvent.Description.Contains("�"), Is.True); + }); + } + + [Test] + public void TestStandardDaylightTimeZoneInfoDeserialization() + { - const string ics = @"BEGIN:VTIMEZONE + const string ics = @"BEGIN:VTIMEZONE TZID: BEGIN:STANDARD DTSTART:16010101T030000 @@ -477,108 +477,107 @@ public void TestStandardDaylightTimeZoneInfoDeserialization() RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3 END:DAYLIGHT END:VTIMEZONE"; - var timeZone = Calendar.Load(ics).Single(); - Assert.That(timeZone, Is.Not.Null, "Expected the TimeZone to be successfully deserialized"); - var timeZoneInfos = timeZone.TimeZoneInfos; - Assert.Multiple(() => - { - Assert.That(timeZoneInfos, Is.Not.Null, "Expected TimeZoneInfos to be deserialized"); - Assert.That(timeZoneInfos, Has.Count.EqualTo(2), "Expected 2 TimeZoneInfos"); - Assert.That(timeZoneInfos[0].Name, Is.EqualTo("STANDARD")); - Assert.That(timeZoneInfos[0].OffsetFrom, Is.EqualTo(new UtcOffset("+0200"))); - Assert.That(timeZoneInfos[0].OffsetTo, Is.EqualTo(new UtcOffset("+0100"))); - Assert.That(timeZoneInfos[1].Name, Is.EqualTo("DAYLIGHT")); - Assert.That(timeZoneInfos[1].OffsetFrom, Is.EqualTo(new UtcOffset("+0100"))); - Assert.That(timeZoneInfos[1].OffsetTo, Is.EqualTo(new UtcOffset("+0200"))); - }); - } + var timeZone = Calendar.Load(ics).Single(); + Assert.That(timeZone, Is.Not.Null, "Expected the TimeZone to be successfully deserialized"); + var timeZoneInfos = timeZone.TimeZoneInfos; + Assert.Multiple(() => + { + Assert.That(timeZoneInfos, Is.Not.Null, "Expected TimeZoneInfos to be deserialized"); + Assert.That(timeZoneInfos, Has.Count.EqualTo(2), "Expected 2 TimeZoneInfos"); + Assert.That(timeZoneInfos[0].Name, Is.EqualTo("STANDARD")); + Assert.That(timeZoneInfos[0].OffsetFrom, Is.EqualTo(new UtcOffset("+0200"))); + Assert.That(timeZoneInfos[0].OffsetTo, Is.EqualTo(new UtcOffset("+0100"))); + Assert.That(timeZoneInfos[1].Name, Is.EqualTo("DAYLIGHT")); + Assert.That(timeZoneInfos[1].OffsetFrom, Is.EqualTo(new UtcOffset("+0100"))); + Assert.That(timeZoneInfos[1].OffsetTo, Is.EqualTo(new UtcOffset("+0200"))); + }); + } - [Test] - public void TestRRuleUntilSerialization() + [Test] + public void TestRRuleUntilSerialization() + { + var rrule = new RecurrencePattern(FrequencyType.Daily) { - var rrule = new RecurrencePattern(FrequencyType.Daily) - { - Until = _nowTime.AddDays(7), - }; - const string someTz = "Europe/Volgograd"; - var e = new CalendarEvent - { - Start = new CalDateTime(_nowTime, someTz), - End = new CalDateTime(_nowTime.AddHours(1), someTz), - RecurrenceRules = new List { rrule }, - }; - var c = new Calendar - { - Events = { e }, - }; - var serialized = new CalendarSerializer().SerializeToString(c); - var serializedUntilNotContainsZSuffix = serialized - .Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries) - .Single(line => line.StartsWith("RRULE:", StringComparison.Ordinal)); - var untilIndex = serializedUntilNotContainsZSuffix.IndexOf("UNTIL", StringComparison.Ordinal); - var until = serializedUntilNotContainsZSuffix.Substring(untilIndex); - - Assert.That(!until.EndsWith("Z"), Is.True); - } + Until = _nowTime.AddDays(7), + }; + const string someTz = "Europe/Volgograd"; + var e = new CalendarEvent + { + Start = new CalDateTime(_nowTime, someTz), + End = new CalDateTime(_nowTime.AddHours(1), someTz), + RecurrenceRules = new List { rrule }, + }; + var c = new Calendar + { + Events = { e }, + }; + var serialized = new CalendarSerializer().SerializeToString(c); + var serializedUntilNotContainsZSuffix = serialized + .Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries) + .Single(line => line.StartsWith("RRULE:", StringComparison.Ordinal)); + var untilIndex = serializedUntilNotContainsZSuffix.IndexOf("UNTIL", StringComparison.Ordinal); + var until = serializedUntilNotContainsZSuffix.Substring(untilIndex); + + Assert.That(!until.EndsWith("Z"), Is.True); + } - [Test(Description = "PRODID and VERSION should use ical.net values instead of preserving deserialized values")] - public void LibraryMetadataTests() + [Test(Description = "PRODID and VERSION should use ical.net values instead of preserving deserialized values")] + public void LibraryMetadataTests() + { + var c = new Calendar { - var c = new Calendar - { - ProductId = "FOO", - Version = "BAR" - }; - var serialized = new CalendarSerializer().SerializeToString(c); - var expectedProdid = $"PRODID:{LibraryMetadata.ProdId}"; - Assert.That(serialized.Contains(expectedProdid, StringComparison.Ordinal), Is.True); - - var expectedVersion = $"VERSION:{LibraryMetadata.Version}"; - Assert.That(serialized.Contains(expectedVersion, StringComparison.Ordinal), Is.True); - } + ProductId = "FOO", + Version = "BAR" + }; + var serialized = new CalendarSerializer().SerializeToString(c); + var expectedProdid = $"PRODID:{LibraryMetadata.ProdId}"; + Assert.That(serialized.Contains(expectedProdid, StringComparison.Ordinal), Is.True); + + var expectedVersion = $"VERSION:{LibraryMetadata.Version}"; + Assert.That(serialized.Contains(expectedVersion, StringComparison.Ordinal), Is.True); + } - [Test] - public void AttachmentFormatType() + [Test] + public void AttachmentFormatType() + { + var cal1 = new Calendar { - var cal1 = new Calendar + Events = { - Events = + new CalendarEvent { - new CalendarEvent + Attachments = { - Attachments = + new Attachment(Encoding.UTF8.GetBytes("{}")) { - new Attachment(Encoding.UTF8.GetBytes("{}")) - { - FormatType = "application/json", - }, + FormatType = "application/json", }, }, }, - }; - var serializer = new CalendarSerializer(); - var serializedCalendar = serializer.SerializeToString(cal1); - var cal2 = Calendar.Load(serializedCalendar); - Assert.That(cal2.Events.Single().Attachments.Single().FormatType, Is.EqualTo("application/json")); - } + }, + }; + var serializer = new CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(cal1); + var cal2 = Calendar.Load(serializedCalendar); + Assert.That(cal2.Events.Single().Attachments.Single().FormatType, Is.EqualTo("application/json")); + } - [Test(Description = "It should be possible to serialize a calendar component instead of a whole calendar")] - public void SerializeSubcomponent() + [Test(Description = "It should be possible to serialize a calendar component instead of a whole calendar")] + public void SerializeSubcomponent() + { + const string expectedString = "This is an expected string"; + var e = new CalendarEvent { - const string expectedString = "This is an expected string"; - var e = new CalendarEvent - { - Start = new CalDateTime(_nowTime), - End = new CalDateTime(_later), - Summary = expectedString, - }; + Start = new CalDateTime(_nowTime), + End = new CalDateTime(_later), + Summary = expectedString, + }; - var serialized = new CalendarSerializer().SerializeToString(e); - Assert.Multiple(() => - { - Assert.That(serialized.Contains(expectedString, StringComparison.Ordinal), Is.True); - Assert.That(!serialized.Contains("VCALENDAR", StringComparison.Ordinal), Is.True); - }); - } + var serialized = new CalendarSerializer().SerializeToString(e); + Assert.Multiple(() => + { + Assert.That(serialized.Contains(expectedString, StringComparison.Ordinal), Is.True); + Assert.That(!serialized.Contains("VCALENDAR", StringComparison.Ordinal), Is.True); + }); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/SimpleDeserializationTests.cs b/Ical.Net.Tests/SimpleDeserializationTests.cs index 32a771b3f..f0c73aaf1 100644 --- a/Ical.Net.Tests/SimpleDeserializationTests.cs +++ b/Ical.Net.Tests/SimpleDeserializationTests.cs @@ -15,262 +15,262 @@ using Ical.Net.Serialization.DataTypes; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +[TestFixture] +public class SimpleDeserializationTests { - [TestFixture] - public class SimpleDeserializationTests + + [Test, Category("Deserialization")] + public void Attendee1() { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Attendee1)).Cast().Single(); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); + + var evt = iCal.Events.First(); + // Ensure there are 2 attendees + Assert.That(evt.Attendees, Has.Count.EqualTo(2)); - [Test, Category("Deserialization")] - public void Attendee1() + var attendee1 = evt.Attendees; + var attendee2 = evt.Attendees[1]; + + Assert.Multiple(() => { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Attendee1)).Cast().Single(); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); + // Values + Assert.That(attendee1[0].Value, Is.EqualTo(new Uri("mailto:joecool@example.com"))); + Assert.That(attendee2.Value, Is.EqualTo(new Uri("mailto:ildoit@example.com"))); + }); + Assert.Multiple(() => + { + // MEMBERS + Assert.That(attendee1[0].Members, Has.Count.EqualTo(1)); + Assert.That(attendee2.Members.Count, Is.EqualTo(0)); + Assert.That(attendee1[0].Members[0], Is.EqualTo(new Uri("mailto:DEV-GROUP@example.com").ToString())); + }); + Assert.Multiple(() => + { + // DELEGATED-FROM + Assert.That(attendee1[0].DelegatedFrom.Count, Is.EqualTo(0)); + Assert.That(attendee2.DelegatedFrom, Has.Count.EqualTo(1)); + Assert.That(attendee2.DelegatedFrom[0], Is.EqualTo(new Uri("mailto:immud@example.com").ToString())); + }); + Assert.Multiple(() => + { + // DELEGATED-TO + Assert.That(attendee1[0].DelegatedTo.Count, Is.EqualTo(0)); + Assert.That(attendee2.DelegatedTo.Count, Is.EqualTo(0)); + }); + } - var evt = iCal.Events.First(); - // Ensure there are 2 attendees - Assert.That(evt.Attendees, Has.Count.EqualTo(2)); + /// + /// Tests that multiple parameters of the + /// same name are correctly aggregated into + /// a single list. + /// + [Test, Category("Deserialization")] + public void Attendee2() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Attendee2)).Cast().Single(); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); - var attendee1 = evt.Attendees; - var attendee2 = evt.Attendees[1]; + var evt = iCal.Events.First(); + // Ensure there is 1 attendee + Assert.That(evt.Attendees, Has.Count.EqualTo(1)); - Assert.Multiple(() => - { - // Values - Assert.That(attendee1[0].Value, Is.EqualTo(new Uri("mailto:joecool@example.com"))); - Assert.That(attendee2.Value, Is.EqualTo(new Uri("mailto:ildoit@example.com"))); - }); - Assert.Multiple(() => - { - // MEMBERS - Assert.That(attendee1[0].Members, Has.Count.EqualTo(1)); - Assert.That(attendee2.Members.Count, Is.EqualTo(0)); - Assert.That(attendee1[0].Members[0], Is.EqualTo(new Uri("mailto:DEV-GROUP@example.com").ToString())); - }); - Assert.Multiple(() => - { - // DELEGATED-FROM - Assert.That(attendee1[0].DelegatedFrom.Count, Is.EqualTo(0)); - Assert.That(attendee2.DelegatedFrom, Has.Count.EqualTo(1)); - Assert.That(attendee2.DelegatedFrom[0], Is.EqualTo(new Uri("mailto:immud@example.com").ToString())); - }); - Assert.Multiple(() => - { - // DELEGATED-TO - Assert.That(attendee1[0].DelegatedTo.Count, Is.EqualTo(0)); - Assert.That(attendee2.DelegatedTo.Count, Is.EqualTo(0)); - }); - } + var attendee1 = evt.Attendees; - /// - /// Tests that multiple parameters of the - /// same name are correctly aggregated into - /// a single list. - /// - [Test, Category("Deserialization")] - public void Attendee2() + Assert.Multiple(() => { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Attendee2)).Cast().Single(); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); + // Values + Assert.That(attendee1[0].Value, Is.EqualTo(new Uri("mailto:joecool@example.com"))); - var evt = iCal.Events.First(); - // Ensure there is 1 attendee - Assert.That(evt.Attendees, Has.Count.EqualTo(1)); + // MEMBERS + Assert.That(attendee1[0].Members, Has.Count.EqualTo(3)); + }); + Assert.Multiple(() => + { + Assert.That(attendee1[0].Members[0], Is.EqualTo(new Uri("mailto:DEV-GROUP@example.com").ToString())); + Assert.That(attendee1[0].Members[1], Is.EqualTo(new Uri("mailto:ANOTHER-GROUP@example.com").ToString())); + Assert.That(attendee1[0].Members[2], Is.EqualTo(new Uri("mailto:THIRD-GROUP@example.com").ToString())); + }); + } - var attendee1 = evt.Attendees; + /// + /// 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 = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Bug2033495)).Cast().Single(); + Assert.Multiple(() => + { + Assert.That(iCal.Events, Has.Count.EqualTo(1)); + Assert.That(iCal.Properties["X-LOTUS-CHILD_UID"].Value, Is.EqualTo("XXX")); + }); + } - Assert.Multiple(() => - { - // Values - Assert.That(attendee1[0].Value, Is.EqualTo(new Uri("mailto:joecool@example.com"))); + /// + /// 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 = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Bug2938007)).Cast().Single(); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); - // MEMBERS - Assert.That(attendee1[0].Members, Has.Count.EqualTo(3)); - }); - Assert.Multiple(() => - { - Assert.That(attendee1[0].Members[0], Is.EqualTo(new Uri("mailto:DEV-GROUP@example.com").ToString())); - Assert.That(attendee1[0].Members[1], Is.EqualTo(new Uri("mailto:ANOTHER-GROUP@example.com").ToString())); - Assert.That(attendee1[0].Members[2], Is.EqualTo(new Uri("mailto:THIRD-GROUP@example.com").ToString())); - }); - } + var evt = iCal.Events.First(); + Assert.Multiple(() => + { + Assert.That(evt.Start.HasTime, Is.EqualTo(true)); + Assert.That(evt.End.HasTime, Is.EqualTo(true)); + }); - /// - /// 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() + foreach (var o in evt.GetOccurrences(new CalDateTime(2010, 1, 17, 0, 0, 0), new CalDateTime(2010, 2, 1, 0, 0, 0))) { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Bug2033495)).Cast().Single(); Assert.Multiple(() => { - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - Assert.That(iCal.Properties["X-LOTUS-CHILD_UID"].Value, Is.EqualTo("XXX")); + Assert.That(o.Period.StartTime.HasTime, Is.EqualTo(true)); + Assert.That(o.Period.EndTime.HasTime, Is.EqualTo(true)); }); } + } - /// - /// 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 = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Bug2938007)).Cast().Single(); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); + /// + /// 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 evt = iCal.Events.First(); - Assert.Multiple(() => - { - Assert.That(evt.Start.HasTime, Is.EqualTo(true)); - Assert.That(evt.End.HasTime, Is.EqualTo(true)); - }); + var ms = new MemoryStream(); + serializer.Serialize(calendar, ms, Encoding.UTF8); - foreach (var o in evt.GetOccurrences(new CalDateTime(2010, 1, 17, 0, 0, 0), new CalDateTime(2010, 2, 1, 0, 0, 0))) - { - Assert.Multiple(() => - { - Assert.That(o.Period.StartTime.HasTime, Is.EqualTo(true)); - Assert.That(o.Period.EndTime.HasTime, Is.EqualTo(true)); - }); - } - } - - /// - /// 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(); + Assert.That(ms.CanWrite, Is.True); + } - var ms = new MemoryStream(); - serializer.Serialize(calendar, ms, Encoding.UTF8); + /// + /// Tests that a mixed-case VERSION property is loaded properly + /// + [Test, Category("Deserialization")] + public void CaseInsensitive4() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.CaseInsensitive4)).Cast().Single(); + Assert.That(iCal.Version, Is.EqualTo("2.5")); + } - Assert.That(ms.CanWrite, Is.True); - } + [Test, Category("Deserialization")] + public void Categories1_2() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Categories1)).Cast().Single(); + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); - /// - /// Tests that a mixed-case VERSION property is loaded properly - /// - [Test, Category("Deserialization")] - public void CaseInsensitive4() + var items = new List(); + items.AddRange(new[] { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.CaseInsensitive4)).Cast().Single(); - Assert.That(iCal.Version, Is.EqualTo("2.5")); - } + "One", "Two", "Three", + "Four", "Five", "Six", + "Seven", "A string of text with nothing less than a comma, semicolon; and a newline\n." + }); - [Test, Category("Deserialization")] - public void Categories1_2() + var found = new Dictionary(); + foreach (var s in evt.Categories.Where(s => items.Contains(s))) { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Categories1)).Cast().Single(); - 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.That(found.ContainsKey(item), Is.True, "Event should contain CATEGORY '" + item + "', but it was not found."); + found[s] = true; } - [Test, Category("Deserialization")] - public void EmptyLines1() - { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.EmptyLines1)).Cast().Single(); - Assert.That(iCal.Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); - } + foreach (string item in items) + Assert.That(found.ContainsKey(item), Is.True, "Event should contain CATEGORY '" + item + "', but it was not found."); + } - [Test, Category("Deserialization")] - public void EmptyLines2() - { - var calendars = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.EmptyLines2)).Cast().ToList(); - Assert.That(calendars, Has.Count.EqualTo(2)); - Assert.Multiple(() => - { - Assert.That(calendars[0].Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); - Assert.That(calendars[1].Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); - }); - } + [Test, Category("Deserialization")] + public void EmptyLines1() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.EmptyLines1)).Cast().Single(); + Assert.That(iCal.Events, Has.Count.EqualTo(2), "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() + [Test, Category("Deserialization")] + public void EmptyLines2() + { + var calendars = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.EmptyLines2)).Cast().ToList(); + Assert.That(calendars, Has.Count.EqualTo(2)); + Assert.Multiple(() => { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.EmptyLines3)).Cast().Single(); - Assert.That(iCal.Todos, Has.Count.EqualTo(1), "iCalendar should have 1 todo"); - } + Assert.That(calendars[0].Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); + Assert.That(calendars[1].Events, Has.Count.EqualTo(2), "iCalendar should have 2 events"); + }); + } - /// - /// Similar to PARSE4 and PARSE5 tests. - /// - [Test, Category("Deserialization")] - public void EmptyLines4() - { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.EmptyLines4)).Cast().Single(); - Assert.That(iCal.Events, Has.Count.EqualTo(28)); - } + /// + /// 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 = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.EmptyLines3)).Cast().Single(); + Assert.That(iCal.Todos, Has.Count.EqualTo(1), "iCalendar should have 1 todo"); + } - [Test] - public void Encoding2() - { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Encoding2)).Cast().Single(); - ProgramTest.TestCal(iCal); - var evt = iCal.Events.First(); - - Assert.That( -evt.Attachments[0].ToString(), - Is.EqualTo("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."), - "Attached value does not match."); - } + /// + /// Similar to PARSE4 and PARSE5 tests. + /// + [Test, Category("Deserialization")] + public void EmptyLines4() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.EmptyLines4)).Cast().Single(); + Assert.That(iCal.Events, Has.Count.EqualTo(28)); + } - [Test] - public void Encoding3() - { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Encoding3)).Cast().Single(); - ProgramTest.TestCal(iCal); - var evt = iCal.Events.First(); + [Test] + public void Encoding2() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Encoding2)).Cast().Single(); + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); + + Assert.That( + evt.Attachments[0].ToString(), + Is.EqualTo("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."), + "Attached value does not match."); + } - Assert.Multiple(() => - { - Assert.That(evt.Uid, Is.EqualTo("uuid1153170430406"), "UID should be 'uuid1153170430406'; it is " + evt.Uid); - Assert.That(evt.Sequence, Is.EqualTo(1), "SEQUENCE should be 1; it is " + evt.Sequence); - }); - } + [Test] + public void Encoding3() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Encoding3)).Cast().Single(); + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); - [Test, Category("Deserialization")] - public void Event8() + Assert.Multiple(() => { - var sr = new StringReader(@"BEGIN:VCALENDAR + Assert.That(evt.Uid, Is.EqualTo("uuid1153170430406"), "UID should be 'uuid1153170430406'; it is " + evt.Uid); + Assert.That(evt.Sequence, Is.EqualTo(1), "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 @@ -300,287 +300,286 @@ public void Event8() END:VEVENT END:VCALENDAR "); - var iCal = SimpleDeserializer.Default.Deserialize(sr).Cast().Single(); - Assert.That(iCal.Events.Count == 2, Is.True, "There should be 2 events in the parsed calendar"); - Assert.That(iCal.Events["fd940618-45e2-4d19-b118-37fd7a8e3906"], Is.Not.Null, "Event fd940618-45e2-4d19-b118-37fd7a8e3906 should exist in the calendar"); - Assert.That(iCal.Events["ebfbd3e3-cc1e-4a64-98eb-ced2598b3908"], Is.Not.Null, "Event ebfbd3e3-cc1e-4a64-98eb-ced2598b3908 should exist in the calendar"); - } + var iCal = SimpleDeserializer.Default.Deserialize(sr).Cast().Single(); + Assert.That(iCal.Events.Count == 2, Is.True, "There should be 2 events in the parsed calendar"); + Assert.That(iCal.Events["fd940618-45e2-4d19-b118-37fd7a8e3906"], Is.Not.Null, "Event fd940618-45e2-4d19-b118-37fd7a8e3906 should exist in the calendar"); + Assert.That(iCal.Events["ebfbd3e3-cc1e-4a64-98eb-ced2598b3908"], Is.Not.Null, "Event ebfbd3e3-cc1e-4a64-98eb-ced2598b3908 should exist in the calendar"); + } + + [Test] + public void GeographicLocation1_2() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.GeographicLocation1)).Cast().Single(); + ProgramTest.TestCal(iCal); + var evt = iCal.Events.First(); - [Test] - public void GeographicLocation1_2() + Assert.Multiple(() => { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.GeographicLocation1)).Cast().Single(); - ProgramTest.TestCal(iCal); - var evt = iCal.Events.First(); + Assert.That(evt.GeographicLocation.Latitude, Is.EqualTo(37.386013), "Latitude should be 37.386013; it is not."); + Assert.That(evt.GeographicLocation.Longitude, Is.EqualTo(-122.082932), "Longitude should be -122.082932; it is not."); + }); + } - Assert.Multiple(() => - { - Assert.That(evt.GeographicLocation.Latitude, Is.EqualTo(37.386013), "Latitude should be 37.386013; it is not."); - Assert.That(evt.GeographicLocation.Longitude, Is.EqualTo(-122.082932), "Longitude should be -122.082932; it is not."); - }); - } + [Test, Category("Deserialization")] + public void Google1() + { + var tzId = "Europe/Berlin"; + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Google1)).Cast().Single(); + var evt = iCal.Events["594oeajmftl3r9qlkb476rpr3c@google.com"]; + Assert.That(evt, Is.Not.Null); - [Test, Category("Deserialization")] - public void Google1() - { - var tzId = "Europe/Berlin"; - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Google1)).Cast().Single(); - var evt = iCal.Events["594oeajmftl3r9qlkb476rpr3c@google.com"]; - Assert.That(evt, Is.Not.Null); + 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(); - 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) + }; - 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.That(occurrences[i].Period.StartTime, Is.EqualTo(dateTimes[i]), "Event should occur at " + dateTimes[i]); - for (var i = 0; i < dateTimes.Length; i++) - Assert.That(occurrences[i].Period.StartTime, Is.EqualTo(dateTimes[i]), "Event should occur at " + dateTimes[i]); + Assert.That(occurrences, Has.Count.EqualTo(dateTimes.Length), "There should be exactly " + dateTimes.Length + " occurrences; there were " + occurrences.Count); + } - Assert.That(occurrences, Has.Count.EqualTo(dateTimes.Length), "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 = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.RecurrenceDates1)).Cast().Single(); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); + Assert.That(iCal.Events.First().RecurrenceDates, Has.Count.EqualTo(3)); - /// - /// Tests that valid RDATE properties are parsed correctly. - /// - [Test, Category("Deserialization")] - public void RecurrenceDates1() + Assert.Multiple(() => { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.RecurrenceDates1)).Cast().Single(); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - Assert.That(iCal.Events.First().RecurrenceDates, Has.Count.EqualTo(3)); + Assert.That(iCal.Events.First().RecurrenceDates[0][0].StartTime, Is.EqualTo((CalDateTime) new DateTime(1997, 7, 14, 12, 30, 0, DateTimeKind.Utc))); + Assert.That(iCal.Events.First().RecurrenceDates[1][0].StartTime, Is.EqualTo((CalDateTime) new DateTime(1996, 4, 3, 2, 0, 0, DateTimeKind.Utc))); + Assert.That(iCal.Events.First().RecurrenceDates[1][0].EndTime, Is.EqualTo((CalDateTime) new DateTime(1996, 4, 3, 4, 0, 0, DateTimeKind.Utc))); + Assert.That(iCal.Events.First().RecurrenceDates[2][0].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 1))); + Assert.That(iCal.Events.First().RecurrenceDates[2][1].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 20))); + Assert.That(iCal.Events.First().RecurrenceDates[2][2].StartTime, Is.EqualTo(new CalDateTime(1997, 2, 17))); + Assert.That(iCal.Events.First().RecurrenceDates[2][3].StartTime, Is.EqualTo(new CalDateTime(1997, 4, 21))); + Assert.That(iCal.Events.First().RecurrenceDates[2][4].StartTime, Is.EqualTo(new CalDateTime(1997, 5, 26))); + Assert.That(iCal.Events.First().RecurrenceDates[2][5].StartTime, Is.EqualTo(new CalDateTime(1997, 7, 4))); + Assert.That(iCal.Events.First().RecurrenceDates[2][6].StartTime, Is.EqualTo(new CalDateTime(1997, 9, 1))); + Assert.That(iCal.Events.First().RecurrenceDates[2][7].StartTime, Is.EqualTo(new CalDateTime(1997, 10, 14))); + Assert.That(iCal.Events.First().RecurrenceDates[2][8].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 28))); + Assert.That(iCal.Events.First().RecurrenceDates[2][9].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 29))); + Assert.That(iCal.Events.First().RecurrenceDates[2][10].StartTime, Is.EqualTo(new CalDateTime(1997, 12, 25))); + }); + } - Assert.Multiple(() => - { - Assert.That(iCal.Events.First().RecurrenceDates[0][0].StartTime, Is.EqualTo((CalDateTime) new DateTime(1997, 7, 14, 12, 30, 0, DateTimeKind.Utc))); - Assert.That(iCal.Events.First().RecurrenceDates[1][0].StartTime, Is.EqualTo((CalDateTime) new DateTime(1996, 4, 3, 2, 0, 0, DateTimeKind.Utc))); - Assert.That(iCal.Events.First().RecurrenceDates[1][0].EndTime, Is.EqualTo((CalDateTime) new DateTime(1996, 4, 3, 4, 0, 0, DateTimeKind.Utc))); - Assert.That(iCal.Events.First().RecurrenceDates[2][0].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 1))); - Assert.That(iCal.Events.First().RecurrenceDates[2][1].StartTime, Is.EqualTo(new CalDateTime(1997, 1, 20))); - Assert.That(iCal.Events.First().RecurrenceDates[2][2].StartTime, Is.EqualTo(new CalDateTime(1997, 2, 17))); - Assert.That(iCal.Events.First().RecurrenceDates[2][3].StartTime, Is.EqualTo(new CalDateTime(1997, 4, 21))); - Assert.That(iCal.Events.First().RecurrenceDates[2][4].StartTime, Is.EqualTo(new CalDateTime(1997, 5, 26))); - Assert.That(iCal.Events.First().RecurrenceDates[2][5].StartTime, Is.EqualTo(new CalDateTime(1997, 7, 4))); - Assert.That(iCal.Events.First().RecurrenceDates[2][6].StartTime, Is.EqualTo(new CalDateTime(1997, 9, 1))); - Assert.That(iCal.Events.First().RecurrenceDates[2][7].StartTime, Is.EqualTo(new CalDateTime(1997, 10, 14))); - Assert.That(iCal.Events.First().RecurrenceDates[2][8].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 28))); - Assert.That(iCal.Events.First().RecurrenceDates[2][9].StartTime, Is.EqualTo(new CalDateTime(1997, 11, 29))); - Assert.That(iCal.Events.First().RecurrenceDates[2][10].StartTime, Is.EqualTo(new CalDateTime(1997, 12, 25))); - }); - } + /// + /// Tests that valid REQUEST-STATUS properties are parsed correctly. + /// + [Test, Category("Deserialization")] + public void RequestStatus1() + { + var iCal = Calendar.Load(IcsFiles.RequestStatus1); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); + Assert.That(iCal.Events.First().RequestStatuses, Has.Count.EqualTo(4)); - /// - /// Tests that valid REQUEST-STATUS properties are parsed correctly. - /// - [Test, Category("Deserialization")] - public void RequestStatus1() + var rs = iCal.Events.First().RequestStatuses[0]; + Assert.Multiple(() => { - var iCal = Calendar.Load(IcsFiles.RequestStatus1); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - Assert.That(iCal.Events.First().RequestStatuses, Has.Count.EqualTo(4)); + Assert.That(rs.StatusCode.Primary, Is.EqualTo(2)); + Assert.That(rs.StatusCode.Secondary, Is.EqualTo(0)); + Assert.That(rs.Description, Is.EqualTo("Success")); + }); + Assert.That(rs.ExtraData, Is.Null); + + rs = iCal.Events.First().RequestStatuses[1]; + Assert.Multiple(() => + { + Assert.That(rs.StatusCode.Primary, Is.EqualTo(3)); + Assert.That(rs.StatusCode.Secondary, Is.EqualTo(1)); + Assert.That(rs.Description, Is.EqualTo("Invalid property value")); + Assert.That(rs.ExtraData, Is.EqualTo("DTSTART:96-Apr-01")); + }); + + rs = iCal.Events.First().RequestStatuses[2]; + Assert.Multiple(() => + { + Assert.That(rs.StatusCode.Primary, Is.EqualTo(2)); + Assert.That(rs.StatusCode.Secondary, Is.EqualTo(8)); + Assert.That(rs.Description, Is.EqualTo(" Success, repeating event ignored. Scheduled as a single event.")); + Assert.That(rs.ExtraData, Is.EqualTo("RRULE:FREQ=WEEKLY;INTERVAL=2")); + }); + + rs = iCal.Events.First().RequestStatuses[3]; + Assert.Multiple(() => + { + Assert.That(rs.StatusCode.Primary, Is.EqualTo(4)); + Assert.That(rs.StatusCode.Secondary, Is.EqualTo(1)); + Assert.That(rs.Description, Is.EqualTo("Event conflict. Date/time is busy.")); + }); + Assert.That(rs.ExtraData, Is.Null); + } - var rs = iCal.Events.First().RequestStatuses[0]; - Assert.Multiple(() => - { - Assert.That(rs.StatusCode.Primary, Is.EqualTo(2)); - Assert.That(rs.StatusCode.Secondary, Is.EqualTo(0)); - Assert.That(rs.Description, Is.EqualTo("Success")); - }); - Assert.That(rs.ExtraData, Is.Null); + /// + /// 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)); - rs = iCal.Events.First().RequestStatuses[1]; - Assert.Multiple(() => - { - Assert.That(rs.StatusCode.Primary, Is.EqualTo(3)); - Assert.That(rs.StatusCode.Secondary, Is.EqualTo(1)); - Assert.That(rs.Description, Is.EqualTo("Invalid property value")); - Assert.That(rs.ExtraData, Is.EqualTo("DTSTART:96-Apr-01")); - }); + Assert.That(unescaped, Is.EqualTo(@"test\with;characters"), "String unescaping was incorrect."); - rs = iCal.Events.First().RequestStatuses[2]; - Assert.Multiple(() => - { - Assert.That(rs.StatusCode.Primary, Is.EqualTo(2)); - Assert.That(rs.StatusCode.Secondary, Is.EqualTo(8)); - Assert.That(rs.Description, Is.EqualTo(" Success, repeating event ignored. Scheduled as a single event.")); - Assert.That(rs.ExtraData, Is.EqualTo("RRULE:FREQ=WEEKLY;INTERVAL=2")); - }); + value = @"C:\Path\To\My\New\Information"; + unescaped = (string) serializer.Deserialize(new StringReader(value)); + Assert.That(unescaped, Is.EqualTo("C:\\Path\\To\\My\new\\Information"), "String unescaping was incorrect."); - rs = iCal.Events.First().RequestStatuses[3]; - Assert.Multiple(() => - { - Assert.That(rs.StatusCode.Primary, Is.EqualTo(4)); - Assert.That(rs.StatusCode.Secondary, Is.EqualTo(1)); - Assert.That(rs.Description, Is.EqualTo("Event conflict. Date/time is busy.")); - }); - Assert.That(rs.ExtraData, Is.Null); - } + value = @"\""This\r\nis\Na\, test\""\;\\;,"; + unescaped = (string) serializer.Deserialize(new StringReader(value)); - /// - /// 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.That(unescaped, Is.EqualTo("\"This\\r\nis\na, test\";\\;,"), "String unescaping was incorrect."); + } - Assert.That(unescaped, Is.EqualTo(@"test\with;characters"), "String unescaping was incorrect."); + [Test, Category("Deserialization")] + public void Transparency2() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Transparency2)).Cast().Single(); - value = @"C:\Path\To\My\New\Information"; - unescaped = (string) serializer.Deserialize(new StringReader(value)); - Assert.That(unescaped, Is.EqualTo("C:\\Path\\To\\My\new\\Information"), "String unescaping was incorrect."); + Assert.That(iCal.Events, Has.Count.EqualTo(1)); + var evt = iCal.Events.First(); - value = @"\""This\r\nis\Na\, test\""\;\\;,"; - unescaped = (string) serializer.Deserialize(new StringReader(value)); + Assert.That(evt.Transparency, Is.EqualTo(TransparencyType.Transparent)); + } - Assert.That(unescaped, Is.EqualTo("\"This\\r\nis\na, test\";\\;,"), "String unescaping was incorrect."); - } + /// + /// 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 = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.DateTime1)).Cast().Single(); + Assert.That(iCal.Events, Has.Count.EqualTo(6)); - [Test, Category("Deserialization")] - public void Transparency2() - { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Transparency2)).Cast().Single(); + var evt = iCal.Events["nc2o66s0u36iesitl2l0b8inn8@google.com"]; + Assert.That(evt, Is.Not.Null); - Assert.That(iCal.Events, Has.Count.EqualTo(1)); - var evt = iCal.Events.First(); + // The "Created" date is out-of-bounds. It should be coerced to the + // closest representable date/time. + Assert.That(evt.Created.Value, Is.EqualTo(DateTime.MinValue)); + } - Assert.That(evt.Transparency, Is.EqualTo(TransparencyType.Transparent)); - } + [Test, Category("Deserialization"), Ignore("Ignore until @thoemy commits the EventStatus.ics file")] + public void EventStatus() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.EventStatus)).Cast().Single(); + Assert.That(iCal.Events, Has.Count.EqualTo(4)); - /// - /// 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() + Assert.That(iCal.Events[0].Summary, Is.EqualTo("No status")); + Assert.That(iCal.Events[0].Status, Is.Null); + Assert.Multiple(() => { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.DateTime1)).Cast().Single(); - Assert.That(iCal.Events, Has.Count.EqualTo(6)); + Assert.That(iCal.Events[0].IsActive, Is.True); - var evt = iCal.Events["nc2o66s0u36iesitl2l0b8inn8@google.com"]; - Assert.That(evt, Is.Not.Null); + Assert.That(iCal.Events[1].Summary, Is.EqualTo("Confirmed")); + Assert.That(iCal.Events[1].Status, Is.EqualTo("CONFIRMED")); + Assert.That(iCal.Events[1].IsActive, Is.True); - // The "Created" date is out-of-bounds. It should be coerced to the - // closest representable date/time. - Assert.That(evt.Created.Value, Is.EqualTo(DateTime.MinValue)); - } + Assert.That(iCal.Events[2].Summary, Is.EqualTo("Cancelled")); + Assert.That(iCal.Events[2].Status, Is.EqualTo("CANCELLED")); + }); + Assert.That(iCal.Events[2].IsActive, Is.False); - [Test, Category("Deserialization"), Ignore("Ignore until @thoemy commits the EventStatus.ics file")] - public void EventStatus() + Assert.Multiple(() => { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.EventStatus)).Cast().Single(); - Assert.That(iCal.Events, Has.Count.EqualTo(4)); - - Assert.That(iCal.Events[0].Summary, Is.EqualTo("No status")); - Assert.That(iCal.Events[0].Status, Is.Null); - Assert.Multiple(() => - { - Assert.That(iCal.Events[0].IsActive, Is.True); - - Assert.That(iCal.Events[1].Summary, Is.EqualTo("Confirmed")); - Assert.That(iCal.Events[1].Status, Is.EqualTo("CONFIRMED")); - Assert.That(iCal.Events[1].IsActive, Is.True); - - Assert.That(iCal.Events[2].Summary, Is.EqualTo("Cancelled")); - Assert.That(iCal.Events[2].Status, Is.EqualTo("CANCELLED")); - }); - Assert.That(iCal.Events[2].IsActive, Is.False); + Assert.That(iCal.Events[3].Summary, Is.EqualTo("Tentative")); + Assert.That(iCal.Events[3].Status, Is.EqualTo("TENTATIVE")); + Assert.That(iCal.Events[3].IsActive, Is.True); + }); + } - Assert.Multiple(() => - { - Assert.That(iCal.Events[3].Summary, Is.EqualTo("Tentative")); - Assert.That(iCal.Events[3].Status, Is.EqualTo("TENTATIVE")); - Assert.That(iCal.Events[3].IsActive, Is.True); - }); - } + [Test, Category("Deserialization")] + public void Language4() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Language4)).Cast().Single(); + Assert.That(iCal, Is.Not.Null); + } - [Test, Category("Deserialization")] - public void Language4() - { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Language4)).Cast().Single(); - Assert.That(iCal, Is.Not.Null); - } + [Test, Category("Deserialization")] + public void Outlook2007_LineFolds1() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Outlook2007LineFolds)).Cast().Single(); + var events = iCal.GetOccurrences(new CalDateTime(2009, 06, 20), new CalDateTime(2009, 06, 22)); + Assert.That(events, Has.Count.EqualTo(1)); + } - [Test, Category("Deserialization")] - public void Outlook2007_LineFolds1() - { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Outlook2007LineFolds)).Cast().Single(); - var events = iCal.GetOccurrences(new CalDateTime(2009, 06, 20), new CalDateTime(2009, 06, 22)); - Assert.That(events, Has.Count.EqualTo(1)); - } + [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 = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Outlook2007LineFolds)).Cast().Single(); + var events = iCal.GetOccurrences(new CalDateTime(2009, 06, 20), new CalDateTime(2009, 06, 22)).OrderBy(o => o.Period.StartTime).ToList(); + Assert.That(((CalendarEvent) events[0].Source).Location, Is.EqualTo(longName)); + } - [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 = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Outlook2007LineFolds)).Cast().Single(); - var events = iCal.GetOccurrences(new CalDateTime(2009, 06, 20), new CalDateTime(2009, 06, 22)).OrderBy(o => o.Period.StartTime).ToList(); - Assert.That(((CalendarEvent) events[0].Source).Location, Is.EqualTo(longName)); - } + /// + /// Tests that multiple parameters are allowed in iCalObjects + /// + [Test, Category("Deserialization")] + public void Parameter1() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Parameter1)).Cast().Single(); - /// - /// Tests that multiple parameters are allowed in iCalObjects - /// - [Test, Category("Deserialization")] - public void Parameter1() + var evt = iCal.Events.First(); + IList parms = evt.Properties["DTSTART"].Parameters.AllOf("VALUE").ToList(); + Assert.That(parms, Has.Count.EqualTo(2)); + Assert.Multiple(() => { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Parameter1)).Cast().Single(); - - var evt = iCal.Events.First(); - IList parms = evt.Properties["DTSTART"].Parameters.AllOf("VALUE").ToList(); - Assert.That(parms, Has.Count.EqualTo(2)); - Assert.Multiple(() => - { - Assert.That(parms[0].Values.First(), Is.EqualTo("DATE")); - Assert.That(parms[1].Values.First(), Is.EqualTo("OTHER")); - }); - } + Assert.That(parms[0].Values.First(), Is.EqualTo("DATE")); + Assert.That(parms[1].Values.First(), Is.EqualTo("OTHER")); + }); + } - /// - /// Tests that empty parameters are allowed in iCalObjects - /// - [Test, Category("Deserialization")] - public void Parameter2() - { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Parameter2)).Cast().Single(); - Assert.That(iCal.Events, Has.Count.EqualTo(2)); - } + /// + /// Tests that empty parameters are allowed in iCalObjects + /// + [Test, Category("Deserialization")] + public void Parameter2() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Parameter2)).Cast().Single(); + Assert.That(iCal.Events, Has.Count.EqualTo(2)); + } - /// - /// Tests a calendar that should fail to properly parse. - /// - [Test, Category("Deserialization")] - public void Parse1() + /// + /// Tests a calendar that should fail to properly parse. + /// + [Test, Category("Deserialization")] + public void Parse1() + { + Assert.That(() => { - Assert.That(() => - { - var content = IcsFiles.Parse1; - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(content)).Cast().Single(); - }, Throws.Exception.TypeOf()); - } + var content = IcsFiles.Parse1; + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(content)).Cast().Single(); + }, Throws.Exception.TypeOf()); + } - /// - /// Tests that multiple properties are allowed in iCalObjects - /// - [Test, Category("Deserialization")] - public void Property1() - { - var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Property1)).Cast().Single(); + /// + /// Tests that multiple properties are allowed in iCalObjects + /// + [Test, Category("Deserialization")] + public void Property1() + { + var iCal = SimpleDeserializer.Default.Deserialize(new StringReader(IcsFiles.Property1)).Cast().Single(); - IList props = iCal.Properties.AllOf("VERSION").ToList(); - Assert.That(props, Has.Count.EqualTo(2)); + IList props = iCal.Properties.AllOf("VERSION").ToList(); + Assert.That(props, Has.Count.EqualTo(2)); - for (var i = 0; i < props.Count; i++) - Assert.That(props[i].Value, Is.EqualTo("2." + i)); - } + for (var i = 0; i < props.Count; i++) + Assert.That(props[i].Value, Is.EqualTo("2." + i)); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/SymmetricSerializationTests.cs b/Ical.Net.Tests/SymmetricSerializationTests.cs index 0ad883a66..3ce04099e 100644 --- a/Ical.Net.Tests/SymmetricSerializationTests.cs +++ b/Ical.Net.Tests/SymmetricSerializationTests.cs @@ -15,224 +15,223 @@ using NUnit.Framework; using NUnit.Framework.Interfaces; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +public class SymmetricSerializationTests { - public class SymmetricSerializationTests - { - private const string _ldapUri = "ldap://example.com:6666/o=eDABC Industries,c=3DUS??(cn=3DBMary Accepted)"; + 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.Load(s); + 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.Load(s); - [Test, TestCaseSource(nameof(Event_TestCases))] - public void Event_Tests(Calendar iCalendar) - { - var originalEvent = iCalendar.Events.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 serializedCalendar = SerializeToString(iCalendar); + var unserializedCalendar = UnserializeCalendar(serializedCalendar); - var onlyEvent = unserializedCalendar.Events.Single(); + var onlyEvent = unserializedCalendar.Events.Single(); - Assert.Multiple(() => - { - Assert.That(onlyEvent.GetHashCode(), Is.EqualTo(originalEvent.GetHashCode())); - Assert.That(onlyEvent, Is.EqualTo(originalEvent)); - Assert.That(unserializedCalendar, Is.EqualTo(iCalendar)); - }); - } + Assert.Multiple(() => + { + Assert.That(onlyEvent.GetHashCode(), Is.EqualTo(originalEvent.GetHashCode())); + Assert.That(onlyEvent, Is.EqualTo(originalEvent)); + Assert.That(unserializedCalendar, Is.EqualTo(iCalendar)); + }); + } - public static IEnumerable Event_TestCases() + public static IEnumerable Event_TestCases() + { + var rrule = new RecurrencePattern(FrequencyType.Daily, 1) { Count = 5 }; + var e = new CalendarEvent { - 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() + 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 { - 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.Load(serializedCalendar); - - Assert.Multiple(() => - { - Assert.That(unserializedCalendar.TimeZones, Is.EqualTo(originalCalendar.TimeZones).AsCollection); - Assert.That(unserializedCalendar, Is.EqualTo(originalCalendar)); - }); - Assert.That(unserializedCalendar.GetHashCode(), Is.EqualTo(originalCalendar.GetHashCode())); - } - - [Test, TestCaseSource(nameof(AttendeeSerialization_TestCases))] - public void AttendeeSerialization_Test(Attendee attendee) + TzId = "New Zealand Standard Time" + }; + originalCalendar.AddTimeZone(tz); + var serializer = new CalendarSerializer(); + var serializedCalendar = serializer.SerializeToString(originalCalendar); + var unserializedCalendar = Calendar.Load(serializedCalendar); + + Assert.Multiple(() => { - 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.Multiple(() => - { - Assert.That(unserialized.GetHashCode(), Is.EqualTo(calendar.GetHashCode())); - Assert.That(calendar.Events.SequenceEqual(unserialized.Events), Is.True); - Assert.That(unserialized, Is.EqualTo(calendar)); - }); - } - - public static IEnumerable AttendeeSerialization_TestCases() + Assert.That(unserializedCalendar.TimeZones, Is.EqualTo(originalCalendar.TimeZones).AsCollection); + Assert.That(unserializedCalendar, Is.EqualTo(originalCalendar)); + }); + Assert.That(unserializedCalendar.GetHashCode(), Is.EqualTo(originalCalendar.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.Multiple(() => { - 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) + Assert.That(unserialized.GetHashCode(), Is.EqualTo(calendar.GetHashCode())); + Assert.That(calendar.Events.SequenceEqual(unserialized.Events), Is.True); + Assert.That(unserialized, Is.EqualTo(calendar)); + }); + } + + public static IEnumerable AttendeeSerialization_TestCases() + { + var complex1 = new Attendee("MAILTO:mary@example.com") { - 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.Multiple(() => - { - Assert.That(unserializedAttachment, Is.EqualTo(expectedAttachment)); - Assert.That(unserialized.GetHashCode(), Is.EqualTo(calendar.GetHashCode())); - Assert.That(unserialized, Is.EqualTo(calendar)); - }); - } - - public static IEnumerable BinaryAttachment_TestCases() + 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") { - const string shortString = "This is a string."; - yield return new TestCaseData(shortString, shortString) - .SetName("Short string"); + CommonName = "James James", + Role = ParticipationRole.RequiredParticipant, + Rsvp = true, + ParticipationStatus = EventParticipationStatus.Tentative + }; + yield return new TestCaseData(simple).SetName("Simple attendee"); + } - const string mediumString = "This is a stringThisThis is a"; - yield return new TestCaseData(mediumString, mediumString) - .SetName("Moderate string"); + [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.Multiple(() => + { + Assert.That(unserializedAttachment, Is.EqualTo(expectedAttachment)); + Assert.That(unserialized.GetHashCode(), Is.EqualTo(calendar.GetHashCode())); + Assert.That(unserialized, Is.EqualTo(calendar)); + }); + } - 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"); + public static IEnumerable BinaryAttachment_TestCases() + { + const string shortString = "This is a string."; + yield return new TestCaseData(shortString, shortString) + .SetName("Short 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"); - } + const string mediumString = "This is a stringThisThis is a"; + yield return new TestCaseData(mediumString, mediumString) + .SetName("Moderate string"); - [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.Multiple(() => - { - Assert.That(unserializedUri, Is.EqualTo(expectedUri)); - Assert.That(unserialized.GetHashCode(), Is.EqualTo(calendar.GetHashCode())); - Assert.That(unserialized, Is.EqualTo(calendar)); - }); - } - - 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() + 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.Multiple(() => { - 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.That(categoriesCount, Is.EqualTo(1)); - - var deserialized = UnserializeCalendar(serialized); - Assert.That(deserialized, Is.EqualTo(c)); - } + Assert.That(unserializedUri, Is.EqualTo(expectedUri)); + Assert.That(unserialized.GetHashCode(), Is.EqualTo(calendar.GetHashCode())); + Assert.That(unserialized, Is.EqualTo(calendar)); + }); + } + + 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.That(categoriesCount, Is.EqualTo(1)); + + var deserialized = UnserializeCalendar(serialized); + Assert.That(deserialized, Is.EqualTo(c)); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/TextUtilTests.cs b/Ical.Net.Tests/TextUtilTests.cs index be3dbce36..89b02f327 100644 --- a/Ical.Net.Tests/TextUtilTests.cs +++ b/Ical.Net.Tests/TextUtilTests.cs @@ -7,44 +7,43 @@ using Ical.Net.Utility; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +public class TextUtilTests { - public class TextUtilTests + [Test, TestCaseSource(nameof(FoldLines_TestCases))] + public string FoldLines_Tests(string incoming) => TextUtil.FoldLines(incoming); + + public static IEnumerable FoldLines_TestCases() { - [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"); - } + 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"); } -} +} \ No newline at end of file diff --git a/Ical.Net.Tests/TodoTest.cs b/Ical.Net.Tests/TodoTest.cs index c31e5b012..664468e95 100644 --- a/Ical.Net.Tests/TodoTest.cs +++ b/Ical.Net.Tests/TodoTest.cs @@ -9,216 +9,215 @@ using Ical.Net.DataTypes; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +[TestFixture] +public class TodoTest { - [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) { - private const string _tzid = "US-Eastern"; + var iCal = Calendar.Load(calendarString); + ProgramTest.TestCal(iCal); + var todo = iCal.Todos; - [Test, TestCaseSource(nameof(ActiveTodo_TestCases)), Category("Todo")] - public void ActiveTodo_Tests(string calendarString, IList> incoming) + foreach (var calDateTime in incoming) { - var iCal = Calendar.Load(calendarString); - ProgramTest.TestCal(iCal); - var todo = iCal.Todos; - - foreach (var calDateTime in incoming) - { - var dt = calDateTime.Key; - dt.TzId = _tzid; - Assert.That(todo[0].IsActive(dt), Is.EqualTo(calDateTime.Value)); - } + var dt = calDateTime.Key; + dt.TzId = _tzid; + Assert.That(todo[0].IsActive(dt), Is.EqualTo(calDateTime.Value)); } + } - public static IEnumerable ActiveTodo_TestCases() + public static IEnumerable ActiveTodo_TestCases() + { + var testVals = new List> { - 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"); - } + new KeyValuePair(new CalDateTime(2200, 12, 31, 0, 0, 0), true) + }; + yield return new TestCaseData(IcsFiles.Todo1, testVals) + .SetName("Todo1"); - [Test, TestCaseSource(nameof(CompletedTodo_TestCases)), Category("Todo")] - public void CompletedTodo_Tests(string calendarString, IList> incoming) + testVals = new List> { - var iCal = Calendar.Load(calendarString); - ProgramTest.TestCal(iCal); - var todo = iCal.Todos; - - foreach (var calDateTime in incoming) - { - var dt = calDateTime.Key; - dt.TzId = _tzid; - Assert.That(todo[0].IsCompleted(dt), Is.EqualTo(calDateTime.Value)); - } - } + 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"); - public static IEnumerable CompletedTodo_TestCases() + testVals = new List> { - 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"); - } + 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.Load(calendarString); + ProgramTest.TestCal(iCal); + var todo = iCal.Todos; - [Test, Category("Todo")] - public void Todo7_1() + foreach (var calDateTime in incoming) { - var iCal = Calendar.Load(IcsFiles.Todo7); - var todo = iCal.Todos; - - 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[0].GetOccurrences( - new CalDateTime(2006, 7, 1, 9, 0, 0), - new CalDateTime(2007, 7, 1, 9, 0, 0)).OrderBy(o => o.Period.StartTime).ToList(); - - Assert.That( - occurrences, -Has.Count.EqualTo(items.Count)); + var dt = calDateTime.Key; + dt.TzId = _tzid; + Assert.That(todo[0].IsCompleted(dt), Is.EqualTo(calDateTime.Value)); } } -} + + 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.Load(IcsFiles.Todo7); + var todo = iCal.Todos; + + 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[0].GetOccurrences( + new CalDateTime(2006, 7, 1, 9, 0, 0), + new CalDateTime(2007, 7, 1, 9, 0, 0)).OrderBy(o => o.Period.StartTime).ToList(); + + Assert.That( + occurrences, + Has.Count.EqualTo(items.Count)); + } +} \ No newline at end of file diff --git a/Ical.Net.Tests/VTimeZoneTest.cs b/Ical.Net.Tests/VTimeZoneTest.cs index 5d45a0fb2..ca14f1ecb 100644 --- a/Ical.Net.Tests/VTimeZoneTest.cs +++ b/Ical.Net.Tests/VTimeZoneTest.cs @@ -10,289 +10,288 @@ using Ical.Net.Serialization; using NUnit.Framework; -namespace Ical.Net.Tests +namespace Ical.Net.Tests; + +public class VTimeZoneTest { - public class VTimeZoneTest + [Test, Category("VTimeZone")] + public void InvalidTzIdShouldThrowException() { - [Test, Category("VTimeZone")] - public void InvalidTzIdShouldThrowException() - { - Assert.Throws(() => new VTimeZone("shouldFail")); - } + Assert.Throws(() => new VTimeZone("shouldFail")); + } - [Test, Category("VTimeZone")] - public void VTimeZoneFromDateTimeZoneNullZoneShouldThrowException() - { - Assert.Throws(() => CreateTestCalendar("shouldFail")); - } + [Test, Category("VTimeZone")] + public void VTimeZoneFromDateTimeZoneNullZoneShouldThrowException() + { + Assert.Throws(() => CreateTestCalendar("shouldFail")); + } - [Test, Category("VTimeZone")] - public void VTimeZoneAmericaPhoenixShouldSerializeProperly() - { - var iCal = CreateTestCalendar("America/Phoenix"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); - - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZID:America/Phoenix"), Is.True, "Time zone not found in serialization"); - Assert.That(serialized.Contains("DTSTART:19670430T020000"), Is.True, "Daylight savings for Phoenix was not serialized properly."); - }); - } + [Test, Category("VTimeZone")] + public void VTimeZoneAmericaPhoenixShouldSerializeProperly() + { + var iCal = CreateTestCalendar("America/Phoenix"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); - [Test, Category("VTimeZone")] - public void VTimeZoneAmericaPhoenixShouldSerializeProperly2() + Assert.Multiple(() => { - var iCal = CreateTestCalendar("America/Phoenix", DateTime.Now, false); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); - - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZID:America/Phoenix"), Is.True, "Time zone not found in serialization"); - Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.False, "Daylight savings should not exist for Phoenix."); - }); - } + Assert.That(serialized.Contains("TZID:America/Phoenix"), Is.True, "Time zone not found in serialization"); + Assert.That(serialized.Contains("DTSTART:19670430T020000"), Is.True, "Daylight savings for Phoenix was not serialized properly."); + }); + } - [Test, Category("VTimeZone")] - public void VTimeZoneUsMountainStandardTimeShouldSerializeProperly() - { - var iCal = CreateTestCalendar("US Mountain Standard Time"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); - - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZID:US Mountain Standard Time"), Is.True, "Time zone not found in serialization"); - Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True); - Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True); - Assert.That(serialized.Contains("X-LIC-LOCATION"), Is.True, "X-LIC-LOCATION was not serialized"); - }); - } + [Test, Category("VTimeZone")] + public void VTimeZoneAmericaPhoenixShouldSerializeProperly2() + { + var iCal = CreateTestCalendar("America/Phoenix", DateTime.Now, false); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); - [Test, Category("VTimeZone")] - public void VTimeZonePacificKiritimatiShouldSerializeProperly() + Assert.Multiple(() => { - var iCal = CreateTestCalendar("Pacific/Kiritimati"); - var serializer = new CalendarSerializer(); - Assert.DoesNotThrow(() => serializer.SerializeToString(iCal)); - } + Assert.That(serialized.Contains("TZID:America/Phoenix"), Is.True, "Time zone not found in serialization"); + Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.False, "Daylight savings should not exist for Phoenix."); + }); + } + + [Test, Category("VTimeZone")] + public void VTimeZoneUsMountainStandardTimeShouldSerializeProperly() + { + var iCal = CreateTestCalendar("US Mountain Standard Time"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); - [Test, Category("VTimeZone")] - public void VTimeZoneCentralAmericaStandardTimeShouldSerializeProperly() + Assert.Multiple(() => { - var iCal = CreateTestCalendar("Central America Standard Time"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); + Assert.That(serialized.Contains("TZID:US Mountain Standard Time"), Is.True, "Time zone not found in serialization"); + Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True); + Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True); + Assert.That(serialized.Contains("X-LIC-LOCATION"), Is.True, "X-LIC-LOCATION was not serialized"); + }); + } - Assert.That(serialized.Contains("TZID:Central America Standard Time"), Is.True, "Time zone not found in serialization"); - } + [Test, Category("VTimeZone")] + public void VTimeZonePacificKiritimatiShouldSerializeProperly() + { + var iCal = CreateTestCalendar("Pacific/Kiritimati"); + var serializer = new CalendarSerializer(); + Assert.DoesNotThrow(() => serializer.SerializeToString(iCal)); + } + + [Test, Category("VTimeZone")] + public void VTimeZoneCentralAmericaStandardTimeShouldSerializeProperly() + { + var iCal = CreateTestCalendar("Central America Standard Time"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); + + Assert.That(serialized.Contains("TZID:Central America Standard Time"), Is.True, "Time zone not found in serialization"); + } + + [Test, Category("VTimeZone")] + public void VTimeZoneEasternStandardTimeShouldSerializeProperly() + { + var iCal = CreateTestCalendar("Eastern Standard Time"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); + + Assert.That(serialized.Contains("TZID:Eastern Standard Time"), Is.True, "Time zone not found in serialization"); + } - [Test, Category("VTimeZone")] - public void VTimeZoneEasternStandardTimeShouldSerializeProperly() + [Test, Category("VTimeZone")] + public void VTimeZoneEuropeMoscowShouldSerializeProperly() + { + var iCal = CreateTestCalendar("Europe/Moscow"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); + + Assert.Multiple(() => { - var iCal = CreateTestCalendar("Eastern Standard Time"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); + Assert.That(serialized.Contains("TZID:Europe/Moscow"), Is.True, "Time zone not found in serialization"); + Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); + Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); + }); + Assert.Multiple(() => + { + Assert.That(serialized.Contains("TZNAME:MSD"), Is.True, "MSD was not serialized"); + Assert.That(serialized.Contains("TZNAME:MSK"), Is.True, "MSK info was not serialized"); + Assert.That(serialized.Contains("TZNAME:MSD"), Is.True, "MSD was not serialized"); + Assert.That(serialized.Contains("TZNAME:MST"), Is.True, "MST was not serialized"); + Assert.That(serialized.Contains("TZNAME:MMT"), Is.True, "MMT was not serialized"); + Assert.That(serialized.Contains("TZOFFSETFROM:+023017"), Is.True, "TZOFFSETFROM:+023017 was not serialized"); + Assert.That(serialized.Contains("TZOFFSETTO:+023017"), Is.True, "TZOFFSETTO:+023017 was not serialized"); + Assert.That(serialized.Contains("DTSTART:19180916T010000"), Is.True, "DTSTART:19180916T010000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:19171228T000000"), Is.True, "DTSTART:19171228T000000 was not serialized"); + Assert.That(serialized.Contains("RDATE:19991031T030000"), Is.True, "RDATE:19991031T030000 was not serialized"); + }); + } - Assert.That(serialized.Contains("TZID:Eastern Standard Time"), Is.True, "Time zone not found in serialization"); - } + [Test, Category("VTimeZone")] + public void VTimeZoneAmericaChicagoShouldSerializeProperly() + { + var iCal = CreateTestCalendar("America/Chicago"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); - [Test, Category("VTimeZone")] - public void VTimeZoneEuropeMoscowShouldSerializeProperly() + Assert.Multiple(() => { - var iCal = CreateTestCalendar("Europe/Moscow"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); - - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZID:Europe/Moscow"), Is.True, "Time zone not found in serialization"); - Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); - Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); - }); - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZNAME:MSD"), Is.True, "MSD was not serialized"); - Assert.That(serialized.Contains("TZNAME:MSK"), Is.True, "MSK info was not serialized"); - Assert.That(serialized.Contains("TZNAME:MSD"), Is.True, "MSD was not serialized"); - Assert.That(serialized.Contains("TZNAME:MST"), Is.True, "MST was not serialized"); - Assert.That(serialized.Contains("TZNAME:MMT"), Is.True, "MMT was not serialized"); - Assert.That(serialized.Contains("TZOFFSETFROM:+023017"), Is.True, "TZOFFSETFROM:+023017 was not serialized"); - Assert.That(serialized.Contains("TZOFFSETTO:+023017"), Is.True, "TZOFFSETTO:+023017 was not serialized"); - Assert.That(serialized.Contains("DTSTART:19180916T010000"), Is.True, "DTSTART:19180916T010000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:19171228T000000"), Is.True, "DTSTART:19171228T000000 was not serialized"); - Assert.That(serialized.Contains("RDATE:19991031T030000"), Is.True, "RDATE:19991031T030000 was not serialized"); - }); - } + Assert.That(serialized.Contains("TZID:America/Chicago"), Is.True, "Time zone not found in serialization"); + Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); + Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); + Assert.That(serialized.Contains("TZNAME:CDT"), Is.True, "CDT was not serialized"); + Assert.That(serialized.Contains("TZNAME:CST"), Is.True, "CST was not serialized"); + Assert.That(serialized.Contains("TZNAME:EST"), Is.True, "EST was not serialized"); + Assert.That(serialized.Contains("TZNAME:CWT"), Is.True, "CWT was not serialized"); + Assert.That(serialized.Contains("TZNAME:CPT"), Is.True, "CPT was not serialized"); + Assert.That(serialized.Contains("DTSTART:19181027T020000"), Is.True, "DTSTART:19181027T020000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:19450814T180000"), Is.True, "DTSTART:19450814T180000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:19420209T020000"), Is.True, "DTSTART:19420209T020000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:19360301T020000"), Is.True, "DTSTART:19360301T020000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:20070311T020000"), Is.True, "DTSTART:20070311T020000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:20071104T020000"), Is.True, "DTSTART:20071104T020000 was not serialized"); + }); + } - [Test, Category("VTimeZone")] - public void VTimeZoneAmericaChicagoShouldSerializeProperly() + [Test, Category("VTimeZone")] + public void VTimeZoneAmericaLosAngelesShouldSerializeProperly() + { + var iCal = CreateTestCalendar("America/Los_Angeles"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); + + Assert.Multiple(() => { - var iCal = CreateTestCalendar("America/Chicago"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); - - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZID:America/Chicago"), Is.True, "Time zone not found in serialization"); - Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); - Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); - Assert.That(serialized.Contains("TZNAME:CDT"), Is.True, "CDT was not serialized"); - Assert.That(serialized.Contains("TZNAME:CST"), Is.True, "CST was not serialized"); - Assert.That(serialized.Contains("TZNAME:EST"), Is.True, "EST was not serialized"); - Assert.That(serialized.Contains("TZNAME:CWT"), Is.True, "CWT was not serialized"); - Assert.That(serialized.Contains("TZNAME:CPT"), Is.True, "CPT was not serialized"); - Assert.That(serialized.Contains("DTSTART:19181027T020000"), Is.True, "DTSTART:19181027T020000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:19450814T180000"), Is.True, "DTSTART:19450814T180000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:19420209T020000"), Is.True, "DTSTART:19420209T020000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:19360301T020000"), Is.True, "DTSTART:19360301T020000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:20070311T020000"), Is.True, "DTSTART:20070311T020000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:20071104T020000"), Is.True, "DTSTART:20071104T020000 was not serialized"); - }); - } + Assert.That(serialized.Contains("TZID:America/Los_Angeles"), Is.True, "Time zone not found in serialization"); + Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); + Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); + Assert.That(serialized.Contains("BYDAY=2SU"), Is.True, "BYDAY=2SU was not serialized"); + Assert.That(serialized.Contains("TZNAME:PDT"), Is.True, "PDT was not serialized"); + Assert.That(serialized.Contains("TZNAME:PST"), Is.True, "PST was not serialized"); + Assert.That(serialized.Contains("TZNAME:PPT"), Is.True, "PPT was not serialized"); + Assert.That(serialized.Contains("TZNAME:PWT"), Is.True, "PWT was not serialized"); + Assert.That(serialized.Contains("DTSTART:19180331T020000"), Is.True, "DTSTART:19180331T020000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:20071104T020000"), Is.True, "DTSTART:20071104T020000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:20070311T020000"), Is.True, "DTSTART:20070311T020000 was not serialized"); + }); + + //Assert.IsTrue(serialized.Contains("TZURL:http://tzurl.org/zoneinfo/America/Los_Angeles"), "TZURL:http://tzurl.org/zoneinfo/America/Los_Angeles was not serialized"); + //Assert.IsTrue(serialized.Contains("RDATE:19600424T010000"), "RDATE:19600424T010000 was not serialized"); // NodaTime doesn't match with what tzurl has + } - [Test, Category("VTimeZone")] - public void VTimeZoneAmericaLosAngelesShouldSerializeProperly() + [Test, Category("VTimeZone")] + public void VTimeZoneEuropeOsloShouldSerializeProperly() + { + var iCal = CreateTestCalendar("Europe/Oslo"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); + + Assert.Multiple(() => { - var iCal = CreateTestCalendar("America/Los_Angeles"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); - - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZID:America/Los_Angeles"), Is.True, "Time zone not found in serialization"); - Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); - Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); - Assert.That(serialized.Contains("BYDAY=2SU"), Is.True, "BYDAY=2SU was not serialized"); - Assert.That(serialized.Contains("TZNAME:PDT"), Is.True, "PDT was not serialized"); - Assert.That(serialized.Contains("TZNAME:PST"), Is.True, "PST was not serialized"); - Assert.That(serialized.Contains("TZNAME:PPT"), Is.True, "PPT was not serialized"); - Assert.That(serialized.Contains("TZNAME:PWT"), Is.True, "PWT was not serialized"); - Assert.That(serialized.Contains("DTSTART:19180331T020000"), Is.True, "DTSTART:19180331T020000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:20071104T020000"), Is.True, "DTSTART:20071104T020000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:20070311T020000"), Is.True, "DTSTART:20070311T020000 was not serialized"); - }); - - //Assert.IsTrue(serialized.Contains("TZURL:http://tzurl.org/zoneinfo/America/Los_Angeles"), "TZURL:http://tzurl.org/zoneinfo/America/Los_Angeles was not serialized"); - //Assert.IsTrue(serialized.Contains("RDATE:19600424T010000"), "RDATE:19600424T010000 was not serialized"); // NodaTime doesn't match with what tzurl has - } + Assert.That(serialized.Contains("TZID:Europe/Oslo"), Is.True, "Time zone not found in serialization"); + Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); + Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); + Assert.That(serialized.Contains("BYDAY=-1SU;BYMONTH=3"), Is.True, "BYDAY=-1SU;BYMONTH=3 was not serialized"); + Assert.That(serialized.Contains("BYDAY=-1SU;BYMONTH=10"), Is.True, "BYDAY=-1SU;BYMONTH=10 was not serialized"); + }); + + } + + [Test, Category("VTimeZone")] + public void VTimeZoneAmericaAnchorageShouldSerializeProperly() + { + var iCal = CreateTestCalendar("America/Anchorage"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); - [Test, Category("VTimeZone")] - public void VTimeZoneEuropeOsloShouldSerializeProperly() + Assert.Multiple(() => { - var iCal = CreateTestCalendar("Europe/Oslo"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); - - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZID:Europe/Oslo"), Is.True, "Time zone not found in serialization"); - Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); - Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); - Assert.That(serialized.Contains("BYDAY=-1SU;BYMONTH=3"), Is.True, "BYDAY=-1SU;BYMONTH=3 was not serialized"); - Assert.That(serialized.Contains("BYDAY=-1SU;BYMONTH=10"), Is.True, "BYDAY=-1SU;BYMONTH=10 was not serialized"); - }); + Assert.That(serialized.Contains("TZID:America/Anchorage"), Is.True, "Time zone not found in serialization"); + Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); + Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); + Assert.That(serialized.Contains("TZNAME:AHST"), Is.True, "AHST was not serialized"); + Assert.That(serialized.Contains("TZNAME:AHDT"), Is.True, "AHDT was not serialized"); + Assert.That(serialized.Contains("TZNAME:AKST"), Is.True, "AKST was not serialized"); + Assert.That(serialized.Contains("TZNAME:YST"), Is.True, "YST was not serialized"); + Assert.That(serialized.Contains("TZNAME:AHDT"), Is.True, "AHDT was not serialized"); + Assert.That(serialized.Contains("TZNAME:LMT"), Is.True, "LMT was not serialized"); + Assert.That(serialized.Contains("RDATE:19731028T020000"), Is.True, "RDATE:19731028T020000 was not serialized"); + Assert.That(serialized.Contains("RDATE:19801026T020000"), Is.True, "RDATE:19801026T020000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:19420209T020000"), Is.True, "DTSTART:19420209T020000 was not serialized"); + Assert.That(serialized.Contains("RDATE:19670401/P1D"), Is.False, "RDate was not properly serialized for vtimezone, should be RDATE:19670401T000000"); + }); + } - } + [Test, Category("VTimeZone")] + public void VTimeZoneAmericaEirunepeShouldSerializeProperly() + { + var iCal = CreateTestCalendar("America/Eirunepe"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); - [Test, Category("VTimeZone")] - public void VTimeZoneAmericaAnchorageShouldSerializeProperly() + Assert.Multiple(() => { - var iCal = CreateTestCalendar("America/Anchorage"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); - - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZID:America/Anchorage"), Is.True, "Time zone not found in serialization"); - Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); - Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); - Assert.That(serialized.Contains("TZNAME:AHST"), Is.True, "AHST was not serialized"); - Assert.That(serialized.Contains("TZNAME:AHDT"), Is.True, "AHDT was not serialized"); - Assert.That(serialized.Contains("TZNAME:AKST"), Is.True, "AKST was not serialized"); - Assert.That(serialized.Contains("TZNAME:YST"), Is.True, "YST was not serialized"); - Assert.That(serialized.Contains("TZNAME:AHDT"), Is.True, "AHDT was not serialized"); - Assert.That(serialized.Contains("TZNAME:LMT"), Is.True, "LMT was not serialized"); - Assert.That(serialized.Contains("RDATE:19731028T020000"), Is.True, "RDATE:19731028T020000 was not serialized"); - Assert.That(serialized.Contains("RDATE:19801026T020000"), Is.True, "RDATE:19801026T020000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:19420209T020000"), Is.True, "DTSTART:19420209T020000 was not serialized"); - Assert.That(serialized.Contains("RDATE:19670401/P1D"), Is.False, "RDate was not properly serialized for vtimezone, should be RDATE:19670401T000000"); - }); - } + Assert.That(serialized.Contains("TZID:America/Eirunepe"), Is.True, "Time zone not found in serialization"); + Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); + Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); + Assert.That(serialized.Contains("TZNAME:-04"), Is.True, "-04 was not serialized"); + Assert.That(serialized.Contains("TZNAME:-05"), Is.True, "-05 was not serialized"); + Assert.That(serialized.Contains("DTSTART:19311003T110000"), Is.True, "DTSTART:19311003T110000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:19320401T000000"), Is.True, "DTSTART:19320401T000000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:20080624T000000"), Is.True, "DTSTART:20080624T000000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:19501201T000000"), Is.True, "DTSTART:19501201T000000 was not serialized"); + }); + + // Should not contain the following + Assert.That(serialized.Contains("RDATE:19501201T000000/P1D"), Is.False, "The RDATE was not serialized correctly, should be RDATE:19501201T000000"); + } - [Test, Category("VTimeZone")] - public void VTimeZoneAmericaEirunepeShouldSerializeProperly() + [Test, Category("VTimeZone")] + public void VTimeZoneAmericaDetroitShouldSerializeProperly() + { + var iCal = CreateTestCalendar("America/Detroit"); + var serializer = new CalendarSerializer(); + var serialized = serializer.SerializeToString(iCal); + + Assert.Multiple(() => { - var iCal = CreateTestCalendar("America/Eirunepe"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); - - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZID:America/Eirunepe"), Is.True, "Time zone not found in serialization"); - Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); - Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); - Assert.That(serialized.Contains("TZNAME:-04"), Is.True, "-04 was not serialized"); - Assert.That(serialized.Contains("TZNAME:-05"), Is.True, "-05 was not serialized"); - Assert.That(serialized.Contains("DTSTART:19311003T110000"), Is.True, "DTSTART:19311003T110000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:19320401T000000"), Is.True, "DTSTART:19320401T000000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:20080624T000000"), Is.True, "DTSTART:20080624T000000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:19501201T000000"), Is.True, "DTSTART:19501201T000000 was not serialized"); - }); - - // Should not contain the following - Assert.That(serialized.Contains("RDATE:19501201T000000/P1D"), Is.False, "The RDATE was not serialized correctly, should be RDATE:19501201T000000"); - } + Assert.That(serialized.Contains("TZID:America/Detroit"), Is.True, "Time zone not found in serialization"); + Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); + Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); + Assert.That(serialized.Contains("TZNAME:EDT"), Is.True, "EDT was not serialized"); + Assert.That(serialized.Contains("TZNAME:EPT"), Is.True, "EPT was not serialized"); + Assert.That(serialized.Contains("TZNAME:EST"), Is.True, "EST was not serialized"); + Assert.That(serialized.Contains("DTSTART:20070311T020000"), Is.True, "DTSTART:20070311T020000 was not serialized"); + Assert.That(serialized.Contains("DTSTART:20071104T020000"), Is.True, "DTSTART:20071104T020000 was not serialized"); + }); + } - [Test, Category("VTimeZone")] - public void VTimeZoneAmericaDetroitShouldSerializeProperly() + private static Calendar CreateTestCalendar(string tzId, DateTime? earliestTime = null, bool includeHistoricalData = true) + { + var iCal = new Calendar(); + + if (earliestTime == null) { - var iCal = CreateTestCalendar("America/Detroit"); - var serializer = new CalendarSerializer(); - var serialized = serializer.SerializeToString(iCal); - - Assert.Multiple(() => - { - Assert.That(serialized.Contains("TZID:America/Detroit"), Is.True, "Time zone not found in serialization"); - Assert.That(serialized.Contains("BEGIN:STANDARD"), Is.True, "The standard timezone info was not serialized"); - Assert.That(serialized.Contains("BEGIN:DAYLIGHT"), Is.True, "The daylight timezone info was not serialized"); - Assert.That(serialized.Contains("TZNAME:EDT"), Is.True, "EDT was not serialized"); - Assert.That(serialized.Contains("TZNAME:EPT"), Is.True, "EPT was not serialized"); - Assert.That(serialized.Contains("TZNAME:EST"), Is.True, "EST was not serialized"); - Assert.That(serialized.Contains("DTSTART:20070311T020000"), Is.True, "DTSTART:20070311T020000 was not serialized"); - Assert.That(serialized.Contains("DTSTART:20071104T020000"), Is.True, "DTSTART:20071104T020000 was not serialized"); - }); + earliestTime = new DateTime(1900, 1, 1); } + iCal.AddTimeZone(tzId, earliestTime.Value, includeHistoricalData); - private static Calendar CreateTestCalendar(string tzId, DateTime? earliestTime = null, bool includeHistoricalData = true) + var calEvent = new CalendarEvent { - var iCal = new Calendar(); - - if (earliestTime == null) - { - earliestTime = new DateTime(1900, 1, 1); - } - iCal.AddTimeZone(tzId, earliestTime.Value, includeHistoricalData); - - var calEvent = new CalendarEvent - { - Description = "Test Recurring Event", - Start = new CalDateTime(DateTime.Now, tzId), - End = new CalDateTime(DateTime.Now.AddHours(1), tzId), - RecurrenceRules = new List { new RecurrencePattern(FrequencyType.Daily) } - }; - iCal.Events.Add(calEvent); - - var calEvent2 = new CalendarEvent - { - Description = "Test Recurring Event 2", - Start = new CalDateTime(DateTime.Now.AddHours(2), tzId), - End = new CalDateTime(DateTime.Now.AddHours(3), tzId), - RecurrenceRules = new List { new RecurrencePattern(FrequencyType.Daily) } - }; - iCal.Events.Add(calEvent2); - return iCal; - } + Description = "Test Recurring Event", + Start = new CalDateTime(DateTime.Now, tzId), + End = new CalDateTime(DateTime.Now.AddHours(1), tzId), + RecurrenceRules = new List { new RecurrencePattern(FrequencyType.Daily) } + }; + iCal.Events.Add(calEvent); + + var calEvent2 = new CalendarEvent + { + Description = "Test Recurring Event 2", + Start = new CalDateTime(DateTime.Now.AddHours(2), tzId), + End = new CalDateTime(DateTime.Now.AddHours(3), tzId), + RecurrenceRules = new List { new RecurrencePattern(FrequencyType.Daily) } + }; + iCal.Events.Add(calEvent2); + return iCal; } -} +} \ No newline at end of file diff --git a/Ical.Net/Calendar.cs b/Ical.Net/Calendar.cs index f7ad9b9cb..f91781fe2 100644 --- a/Ical.Net/Calendar.cs +++ b/Ical.Net/Calendar.cs @@ -15,395 +15,394 @@ using Ical.Net.Serialization; using Ical.Net.Utility; -namespace Ical.Net +namespace Ical.Net; + +public class Calendar : CalendarComponent, IGetOccurrencesTyped, IGetFreeBusy, IMergeable { - public class Calendar : CalendarComponent, IGetOccurrencesTyped, IGetFreeBusy, IMergeable + public static Calendar Load(string iCalendarString) + => CalendarCollection.Load(new StringReader(iCalendarString)).SingleOrDefault(); + + /// + /// Loads an from an open stream. + /// + /// The stream from which to load the object + /// An object + public static Calendar Load(Stream s) + => CalendarCollection.Load(new StreamReader(s, Encoding.UTF8)).SingleOrDefault(); + + public static Calendar Load(TextReader tr) + => CalendarCollection.Load(tr)?.SingleOrDefault(); + + public static IList Load(Stream s, Encoding e) + => Load(new StreamReader(s, e)); + + public static IList Load(TextReader tr) + => SimpleDeserializer.Default.Deserialize(tr).OfType().ToList(); + + public static IList Load(string ical) + => Load(new StringReader(ical)); + + 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() { - public static Calendar Load(string iCalendarString) - => CalendarCollection.Load(new StringReader(iCalendarString)).SingleOrDefault(); - - /// - /// Loads an from an open stream. - /// - /// The stream from which to load the object - /// An object - public static Calendar Load(Stream s) - => CalendarCollection.Load(new StreamReader(s, Encoding.UTF8)).SingleOrDefault(); - - public static Calendar Load(TextReader tr) - => CalendarCollection.Load(tr)?.SingleOrDefault(); - - public static IList Load(Stream s, Encoding e) - => Load(new StreamReader(s, e)); - - public static IList Load(TextReader tr) - => SimpleDeserializer.Default.Deserialize(tr).OfType().ToList(); - - public static IList Load(string ical) - => Load(new StringReader(ical)); - - 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(); - } + 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); - } + 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); + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); - Initialize(); - } + Initialize(); + } - protected bool Equals(Calendar other) - => 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); + protected bool Equals(Calendar other) + => 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) + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { - if (ReferenceEquals(null, obj)) - { - return false; - } - if (ReferenceEquals(this, obj)) - { - return true; - } - return obj.GetType() == GetType() && Equals((Calendar) obj); + return false; } + if (ReferenceEquals(this, obj)) + { + return true; + } + return obj.GetType() == GetType() && Equals((Calendar) obj); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - 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; - } + 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 IUniqueComponentList UniqueComponents => _mUniqueComponents; - public virtual IEnumerable RecurringItems => Children.OfType(); + 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 Events => _mEvents; - /// - /// A collection of components in the iCalendar. - /// - public virtual IUniqueComponentList FreeBusy => _mFreeBusy; + /// + /// 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 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 VTimeZone components in the iCalendar. + /// + public virtual ICalendarObjectList TimeZones => _mTimeZones; - /// - /// A collection of components in the iCalendar. - /// - public virtual IUniqueComponentList Todos => _mTodos; + /// + /// A collection of components in the iCalendar. + /// + public virtual IUniqueComponentList Todos => _mTodos; - public virtual string Version - { - get => Properties.Get("VERSION"); - set => Properties.Set("VERSION", value); - } + public virtual string Version + { + get => Properties.Get("VERSION"); + set => Properties.Set("VERSION", value); + } - public virtual string ProductId - { - get => Properties.Get("PRODID"); - set => Properties.Set("PRODID", value); - } + public virtual string ProductId + { + get => Properties.Get("PRODID"); + set => Properties.Set("PRODID", value); + } - public virtual string Scale - { - get => Properties.Get("CALSCALE"); - set => Properties.Set("CALSCALE", value); - } + public virtual string Scale + { + get => Properties.Get("CALSCALE"); + set => Properties.Set("CALSCALE", value); + } - public virtual string Method - { - get => Properties.Get("METHOD"); - set => Properties.Set("METHOD", value); - } + public virtual string Method + { + get => Properties.Get("METHOD"); + set => Properties.Set("METHOD", value); + } - [Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] - public virtual RecurrenceRestrictionType RecurrenceRestriction - { - get => Properties.Get("X-DDAY-ICAL-RECURRENCE-RESTRICTION"); - set => Properties.Set("X-DDAY-ICAL-RECURRENCE-RESTRICTION", value); - } + [Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] + public virtual RecurrenceRestrictionType RecurrenceRestriction + { + get => Properties.Get("X-DDAY-ICAL-RECURRENCE-RESTRICTION"); + set => Properties.Set("X-DDAY-ICAL-RECURRENCE-RESTRICTION", value); + } - [Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] - public virtual RecurrenceEvaluationModeType RecurrenceEvaluationMode - { - get => Properties.Get("X-DDAY-ICAL-RECURRENCE-EVALUATION-MODE"); - set => Properties.Set("X-DDAY-ICAL-RECURRENCE-EVALUATION-MODE", value); - } + [Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] + public virtual RecurrenceEvaluationModeType RecurrenceEvaluationMode + { + get => 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; + } - /// - /// 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) + + /// + /// Clears recurrence evaluations for recurring components. + /// + public void ClearEvaluation() + { + foreach (var recurrable in RecurringItems) { - this.AddChild(tz); - return tz; + 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) + => GetOccurrences(new CalDateTime(dt.Date), new CalDateTime(dt.Date.AddDays(1).AddSeconds(-1))); + + /// + public virtual HashSet GetOccurrences(DateTime dt) + => 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 date/time arguments 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. Time is ignored on this parameter. + /// A list of Periods representing the occurrences of this object. + public virtual HashSet GetOccurrences(IDateTime dt) where T : IRecurringComponent + => GetOccurrences(new CalDateTime(dt.Date), new CalDateTime(dt.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))); + + /// + public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) where T : IRecurringComponent + => GetOccurrences(new CalDateTime(startTime), new CalDateTime(endTime)); + + /// + /// 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; + } - /// - /// Clears recurrence evaluations for recurring components. - /// - public void ClearEvaluation() + /// + /// 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) { - foreach (var recurrable in RecurringItems) - { - recurrable.ClearEvaluation(); - } + this.AddChild(obj); + return (T) obj; } + return default(T); + } + + public void Dispose() + { + Children.Clear(); + } - /// - /// 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) - => GetOccurrences(new CalDateTime(dt.Date), new CalDateTime(dt.Date.AddDays(1).AddSeconds(-1))); - - /// - public virtual HashSet GetOccurrences(DateTime dt) - => 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 date/time arguments 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. Time is ignored on this parameter. - /// A list of Periods representing the occurrences of this object. - public virtual HashSet GetOccurrences(IDateTime dt) where T : IRecurringComponent - => GetOccurrences(new CalDateTime(dt.Date), new CalDateTime(dt.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))); - - /// - public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) where T : IRecurringComponent - => GetOccurrences(new CalDateTime(startTime), new CalDateTime(endTime)); - - /// - /// 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 + public virtual void MergeWith(IMergeable obj) + { + var c = obj as Calendar; + if (c == null) { - 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; + return; } - /// - /// 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 + if (Name == null) { - var obj = Activator.CreateInstance(typeof(T)) as ICalendarObject; - if (obj is T) - { - this.AddChild(obj); - return (T) obj; - } - return default(T); + Name = c.Name; } - public void Dispose() + Method = c.Method; + Version = c.Version; + ProductId = c.ProductId; + Scale = c.Scale; + + foreach (var p in c.Properties.Where(p => !Properties.ContainsKey(p.Name))) { - Children.Clear(); + Properties.Add(p); } - public virtual void MergeWith(IMergeable obj) + foreach (var child in c.Children) { - 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 (child is IUniqueComponent) - { - if (!UniqueComponents.ContainsKey(((IUniqueComponent) child).Uid)) - { - this.AddChild(child); - } - } - else + if (!UniqueComponents.ContainsKey(((IUniqueComponent) child).Uid)) { this.AddChild(child); } } + else + { + this.AddChild(child); + } } + } - public virtual FreeBusy GetFreeBusy(FreeBusy freeBusyRequest) => CalendarComponents.FreeBusy.Create(this, freeBusyRequest); + public virtual FreeBusy GetFreeBusy(FreeBusy freeBusyRequest) => CalendarComponents.FreeBusy.Create(this, freeBusyRequest); - public virtual FreeBusy GetFreeBusy(IDateTime fromInclusive, IDateTime toExclusive) - => CalendarComponents.FreeBusy.Create(this, CalendarComponents.FreeBusy.CreateRequest(fromInclusive, toExclusive, null, null)); + public virtual FreeBusy GetFreeBusy(IDateTime fromInclusive, IDateTime toExclusive) + => CalendarComponents.FreeBusy.Create(this, CalendarComponents.FreeBusy.CreateRequest(fromInclusive, toExclusive, null, null)); - public virtual FreeBusy GetFreeBusy(Organizer organizer, IEnumerable contacts, IDateTime fromInclusive, IDateTime toExclusive) - => CalendarComponents.FreeBusy.Create(this, CalendarComponents.FreeBusy.CreateRequest(fromInclusive, toExclusive, organizer, contacts)); + public virtual FreeBusy GetFreeBusy(Organizer organizer, IEnumerable contacts, IDateTime fromInclusive, IDateTime toExclusive) + => CalendarComponents.FreeBusy.Create(this, CalendarComponents.FreeBusy.CreateRequest(fromInclusive, toExclusive, organizer, contacts)); - /// - /// Adds a system time zone to the iCalendar. This time zone may - /// then be used in date/time objects contained in the - /// calendar. - /// - /// A System.TimeZoneInfo object to add to the calendar. - /// The time zone added to the calendar. - public VTimeZone AddTimeZone(TimeZoneInfo tzi) - { - var tz = VTimeZone.FromSystemTimeZone(tzi); - this.AddChild(tz); - return tz; - } + /// + /// Adds a system time zone to the iCalendar. This time zone may + /// then be used in date/time objects contained in the + /// calendar. + /// + /// A System.TimeZoneInfo object to add to the calendar. + /// The time zone added to the calendar. + public VTimeZone AddTimeZone(TimeZoneInfo tzi) + { + var tz = VTimeZone.FromSystemTimeZone(tzi); + this.AddChild(tz); + return tz; + } - public VTimeZone AddTimeZone(TimeZoneInfo tzi, DateTime earliestDateTimeToSupport, bool includeHistoricalData) - { - var tz = VTimeZone.FromSystemTimeZone(tzi, earliestDateTimeToSupport, includeHistoricalData); - this.AddChild(tz); - return tz; - } + public VTimeZone AddTimeZone(TimeZoneInfo tzi, DateTime earliestDateTimeToSupport, bool includeHistoricalData) + { + var tz = VTimeZone.FromSystemTimeZone(tzi, earliestDateTimeToSupport, includeHistoricalData); + this.AddChild(tz); + return tz; + } - public VTimeZone AddTimeZone(string tzId) - { - var tz = VTimeZone.FromDateTimeZone(tzId); - this.AddChild(tz); - return tz; - } + public VTimeZone AddTimeZone(string tzId) + { + var tz = VTimeZone.FromDateTimeZone(tzId); + this.AddChild(tz); + return tz; + } - public VTimeZone AddTimeZone(string tzId, DateTime earliestDateTimeToSupport, bool includeHistoricalData) - { - var tz = VTimeZone.FromDateTimeZone(tzId, earliestDateTimeToSupport, includeHistoricalData); - this.AddChild(tz); - return tz; - } + public VTimeZone AddTimeZone(string tzId, DateTime earliestDateTimeToSupport, bool includeHistoricalData) + { + var tz = VTimeZone.FromDateTimeZone(tzId, earliestDateTimeToSupport, includeHistoricalData); + this.AddChild(tz); + return tz; + } - public VTimeZone AddLocalTimeZone(DateTime earliestDateTimeToSupport, bool includeHistoricalData) - { - var tz = VTimeZone.FromLocalTimeZone(earliestDateTimeToSupport, includeHistoricalData); - this.AddChild(tz); - return tz; - } + public VTimeZone AddLocalTimeZone(DateTime earliestDateTimeToSupport, bool includeHistoricalData) + { + var tz = VTimeZone.FromLocalTimeZone(earliestDateTimeToSupport, includeHistoricalData); + this.AddChild(tz); + return tz; } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarCollection.cs b/Ical.Net/CalendarCollection.cs index 94bf624a1..77db05592 100644 --- a/Ical.Net/CalendarCollection.cs +++ b/Ical.Net/CalendarCollection.cs @@ -13,150 +13,149 @@ using Ical.Net.Serialization; using Ical.Net.Utility; -namespace Ical.Net +namespace Ical.Net; + +/// +/// A list of iCalendars. +/// +public class CalendarCollection : List { + public static CalendarCollection Load(string iCalendarString) + => Load(new StringReader(iCalendarString)); + /// - /// A list of iCalendars. + /// Loads an from an open stream. /// - public class CalendarCollection : List + /// The stream from which to load the object + /// An object + public static CalendarCollection Load(Stream s) + => Load(new StreamReader(s, Encoding.UTF8)); + + public static CalendarCollection Load(TextReader tr) { - public static CalendarCollection Load(string iCalendarString) - => Load(new StringReader(iCalendarString)); - - /// - /// Loads an from an open stream. - /// - /// The stream from which to load the object - /// An object - public static CalendarCollection Load(Stream s) - => Load(new StreamReader(s, Encoding.UTF8)); - - public static CalendarCollection Load(TextReader tr) - { - var calendars = SimpleDeserializer.Default.Deserialize(tr).OfType(); - var collection = new CalendarCollection(); - collection.AddRange(calendars); - return collection; - } + var calendars = SimpleDeserializer.Default.Deserialize(tr).OfType(); + var collection = new CalendarCollection(); + collection.AddRange(calendars); + return collection; + } - public void ClearEvaluation() + public void ClearEvaluation() + { + foreach (var iCal in this) { - foreach (var iCal in this) - { - iCal.ClearEvaluation(); - } + iCal.ClearEvaluation(); } + } - public HashSet GetOccurrences(IDateTime dt) + public HashSet GetOccurrences(IDateTime dt) + { + var occurrences = new HashSet(); + foreach (var iCal in this) { - var occurrences = new HashSet(); - foreach (var iCal in this) - { - occurrences.UnionWith(iCal.GetOccurrences(dt)); - } - return occurrences; + occurrences.UnionWith(iCal.GetOccurrences(dt)); } + return occurrences; + } - public HashSet GetOccurrences(DateTime dt) + public HashSet GetOccurrences(DateTime dt) + { + var occurrences = new HashSet(); + foreach (var iCal in this) { - var occurrences = new HashSet(); - foreach (var iCal in this) - { - occurrences.UnionWith(iCal.GetOccurrences(dt)); - } - return occurrences; + occurrences.UnionWith(iCal.GetOccurrences(dt)); } + return occurrences; + } - public HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) + public HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) + { + var occurrences = new HashSet(); + foreach (var iCal in this) { - var occurrences = new HashSet(); - foreach (var iCal in this) - { - occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); - } - return occurrences; + occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); } + return occurrences; + } - public HashSet GetOccurrences(DateTime startTime, DateTime endTime) + public HashSet GetOccurrences(DateTime startTime, DateTime endTime) + { + var occurrences = new HashSet(); + foreach (var iCal in this) { - var occurrences = new HashSet(); - foreach (var iCal in this) - { - occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); - } - return occurrences; + occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); } + return occurrences; + } - public HashSet GetOccurrences(IDateTime dt) where T : IRecurringComponent + public HashSet GetOccurrences(IDateTime dt) where T : IRecurringComponent + { + var occurrences = new HashSet(); + foreach (var iCal in this) { - var occurrences = new HashSet(); - foreach (var iCal in this) - { - occurrences.UnionWith(iCal.GetOccurrences(dt)); - } - return occurrences; + occurrences.UnionWith(iCal.GetOccurrences(dt)); } + return occurrences; + } - public HashSet GetOccurrences(DateTime dt) where T : IRecurringComponent + public HashSet GetOccurrences(DateTime dt) where T : IRecurringComponent + { + var occurrences = new HashSet(); + foreach (var iCal in this) { - var occurrences = new HashSet(); - foreach (var iCal in this) - { - occurrences.UnionWith(iCal.GetOccurrences(dt)); - } - return occurrences; + occurrences.UnionWith(iCal.GetOccurrences(dt)); } + return occurrences; + } - public HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) where T : IRecurringComponent + public HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) where T : IRecurringComponent + { + var occurrences = new HashSet(); + foreach (var iCal in this) { - var occurrences = new HashSet(); - foreach (var iCal in this) - { - occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); - } - return occurrences; + occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); } + return occurrences; + } - public HashSet GetOccurrences(DateTime startTime, DateTime endTime) where T : IRecurringComponent + public HashSet GetOccurrences(DateTime startTime, DateTime endTime) where T : IRecurringComponent + { + var occurrences = new HashSet(); + foreach (var iCal in this) { - var occurrences = new HashSet(); - foreach (var iCal in this) - { - occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); - } - return occurrences; + occurrences.UnionWith(iCal.GetOccurrences(startTime, endTime)); } + return occurrences; + } - private FreeBusy CombineFreeBusy(FreeBusy main, FreeBusy current) - { - main?.MergeWith(current); - return current; - } + 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(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(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 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); + public override int GetHashCode() => CollectionHelpers.GetHashCode(this); - protected bool Equals(CalendarCollection obj) => CollectionHelpers.Equals(this, obj); + 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; - return obj.GetType() == GetType() && Equals((CalendarEvent) obj); - } + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((CalendarEvent) obj); } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarComponents/Alarm.cs b/Ical.Net/CalendarComponents/Alarm.cs index 7f6781243..af83a0496 100644 --- a/Ical.Net/CalendarComponents/Alarm.cs +++ b/Ical.Net/CalendarComponents/Alarm.cs @@ -7,178 +7,177 @@ using System.Collections.Generic; using Ical.Net.DataTypes; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +/// +/// A class that represents an RFC 2445 VALARM component. +/// FIXME: move GetOccurrences() logic into an AlarmEvaluator. +/// +public class Alarm : CalendarComponent { - /// - /// A class that represents an RFC 2445 VALARM component. - /// FIXME: move GetOccurrences() logic into an AlarmEvaluator. - /// - public class Alarm : CalendarComponent + public virtual string Action { - public virtual string Action - { - get => Properties.Get(AlarmAction.Key); - set => Properties.Set(AlarmAction.Key, value); - } + get => Properties.Get(AlarmAction.Key); + set => Properties.Set(AlarmAction.Key, value); + } - public virtual Attachment Attachment - { - get => Properties.Get("ATTACH"); - set => Properties.Set("ATTACH", value); - } + public virtual Attachment Attachment + { + get => Properties.Get("ATTACH"); + set => Properties.Set("ATTACH", value); + } - public virtual IList Attendees - { - get => Properties.GetMany("ATTENDEE"); - set => Properties.Set("ATTENDEE", value); - } + public virtual IList Attendees + { + get => Properties.GetMany("ATTENDEE"); + set => Properties.Set("ATTENDEE", value); + } - public virtual string Description - { - get => Properties.Get("DESCRIPTION"); - set => Properties.Set("DESCRIPTION", value); - } + public virtual string Description + { + get => Properties.Get("DESCRIPTION"); + set => Properties.Set("DESCRIPTION", value); + } - public virtual TimeSpan Duration - { - get => Properties.Get("DURATION"); - set => Properties.Set("DURATION", value); - } + public virtual TimeSpan Duration + { + get => Properties.Get("DURATION"); + set => Properties.Set("DURATION", value); + } - public virtual int Repeat - { - get => Properties.Get("REPEAT"); - set => Properties.Set("REPEAT", value); - } + public virtual int Repeat + { + get => Properties.Get("REPEAT"); + set => Properties.Set("REPEAT", value); + } - public virtual string Summary - { - get => Properties.Get("SUMMARY"); - set => Properties.Set("SUMMARY", value); - } + public virtual string Summary + { + get => Properties.Get("SUMMARY"); + set => Properties.Set("SUMMARY", value); + } - public virtual Trigger Trigger - { - get => Properties.Get(TriggerRelation.Key); - set => Properties.Set(TriggerRelation.Key, value); - } + public virtual Trigger Trigger + { + get => Properties.Get(TriggerRelation.Key); + set => Properties.Set(TriggerRelation.Key, value); + } + + protected virtual IList Occurrences { get; set; } + + public Alarm() + { + Name = Components.Alarm; + Occurrences = new List(); + } - protected virtual IList Occurrences { get; set; } + /// + /// 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(); - public Alarm() + if (Trigger == null) { - Name = Components.Alarm; - Occurrences = new List(); + return Occurrences; } - /// - /// 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) + // 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) { - Occurrences.Clear(); - - if (Trigger == null) + // Ensure that "FromDate" has already been set + if (fromDate == null) { - return Occurrences; + fromDate = rc.Start.Copy(); } - // 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) + var d = default(TimeSpan); + foreach (var o in rc.GetOccurrences(fromDate, toDate)) { - // 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 (string.Equals(Trigger.Related, TriggerRelation.End, TriggerRelation.Comparison)) { - var dt = o.Period.StartTime; - if (string.Equals(Trigger.Related, TriggerRelation.End, TriggerRelation.Comparison)) + if (o.Period.EndTime != null) { - if (o.Period.EndTime != null) + dt = o.Period.EndTime; + if (d == default(TimeSpan)) { - 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."); + d = o.Period.Duration; } } - - Occurrences.Add(new AlarmOccurrence(this, dt.Add(Trigger.Duration.Value), rc)); + // 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."); + } } - } - 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; + Occurrences.Add(new AlarmOccurrence(this, dt.Add(Trigger.Duration.Value), rc)); + } } - - /// - /// 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) + else { - var results = new List(); + var dt = Trigger.DateTime.Copy(); + dt.AssociatedObject = this; + Occurrences.Add(new AlarmOccurrence(this, dt, rc)); + } - // Evaluate the alarms to determine the recurrences - var rc = Parent as RecurringComponent; - if (rc == null) - { - return results; - } + // 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(); - results.AddRange(GetOccurrences(rc, start, end)); + // Evaluate the alarms to determine the recurrences + var rc = Parent as RecurringComponent; + if (rc == null) + { 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() + 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 len = Occurrences.Count; - for (var i = 0; i < len; i++) - { - var ao = Occurrences[i]; - var alarmTime = ao.DateTime.Copy(); + 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)); - } + 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/Ical.Net/CalendarComponents/CalendarComponent.cs b/Ical.Net/CalendarComponents/CalendarComponent.cs index 61838b022..408bb4d03 100644 --- a/Ical.Net/CalendarComponents/CalendarComponent.cs +++ b/Ical.Net/CalendarComponents/CalendarComponent.cs @@ -6,77 +6,76 @@ using System.Diagnostics; using System.Runtime.Serialization; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +/// +/// 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 { /// - /// This class is used by the parsing framework for iCalendar components. - /// Generally, you should not need to use this class directly. + /// Returns a list of properties that are associated with the iCalendar object. /// - [DebuggerDisplay("Component: {Name}")] - public class CalendarComponent : CalendarObject, ICalendarComponent + public virtual CalendarPropertyList Properties { get; protected set; } + + public CalendarComponent() : base() { - /// - /// Returns a list of properties that are associated with the iCalendar object. - /// - public virtual CalendarPropertyList Properties { get; protected set; } + Initialize(); + } - public CalendarComponent() : base() - { - Initialize(); - } + public CalendarComponent(string name) : base(name) + { + Initialize(); + } - public CalendarComponent(string name) : base(name) - { - Initialize(); - } + private void Initialize() + { + Properties = new CalendarPropertyList(this); + } - private void Initialize() - { - Properties = new CalendarPropertyList(this); - } + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); - protected override void OnDeserializing(StreamingContext context) - { - base.OnDeserializing(context); + Initialize(); + } - Initialize(); - } + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); - /// - public override void CopyFrom(ICopyable obj) + var c = obj as ICalendarComponent; + if (c == null) { - base.CopyFrom(obj); - - var c = obj as ICalendarComponent; - if (c == null) - { - return; - } - - Properties.Clear(); - foreach (var p in c.Properties) - { - // Uses CalendarObjectBase.Copy() for a deep copy - Properties.Add(p.Copy()); - } + return; } - /// - /// Adds a property to this component. - /// - public virtual void AddProperty(string name, string value) + Properties.Clear(); + foreach (var p in c.Properties) { - var p = new CalendarProperty(name, value); - AddProperty(p); + // Uses CalendarObjectBase.Copy() for a deep copy + Properties.Add(p.Copy()); } + } - /// - /// Adds a property to this component. - /// - public virtual void AddProperty(ICalendarProperty p) - { - p.Parent = this; - Properties.Set(p.Name, p.Value); - } + /// + /// 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/Ical.Net/CalendarComponents/CalendarEvent.cs b/Ical.Net/CalendarComponents/CalendarEvent.cs index 74a9cc41c..2f13549f5 100644 --- a/Ical.Net/CalendarComponents/CalendarEvent.cs +++ b/Ical.Net/CalendarComponents/CalendarEvent.cs @@ -11,385 +11,384 @@ using Ical.Net.Evaluation; using Ical.Net.Utility; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +/// +/// 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"; + /// - /// A class that represents an RFC 5545 VEVENT component. - /// + /// The start date/time of the event. /// - /// 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 - /// + /// 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 class CalendarEvent : RecurringComponent, IAlarmContainer, IComparable + /// + public override IDateTime DtStart { - 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 => base.DtStart; + set { - get => base.DtStart; - set - { - base.DtStart = value; - ExtrapolateTimes(2); - } + base.DtStart = value; + ExtrapolateTimes(2); } + } - /// - /// 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 + /// + /// 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 => Properties.Get("DTEND"); + set { - get => Properties.Get("DTEND"); - set + if (!Equals(DtEnd, value)) { - if (!Equals(DtEnd, value)) - { - Properties.Set("DTEND", value); - ExtrapolateTimes(0); - } + Properties.Set("DTEND", value); + ExtrapolateTimes(0); } } + } - /// - /// 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 + /// + /// 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 => Properties.Get("DURATION"); + set { - get => Properties.Get("DURATION"); - set + if (!Equals(Duration, value)) { - if (!Equals(Duration, value)) - { - Properties.Set("DURATION", value); - ExtrapolateTimes(1); - } + Properties.Set("DURATION", value); + ExtrapolateTimes(1); } } + } - /// - /// An alias to the DtEnd field (i.e. end date/time). - /// - public virtual IDateTime End - { - get => DtEnd; - set => DtEnd = value; - } + /// + /// An alias to the DtEnd field (i.e. end date/time). + /// + public virtual IDateTime End + { + get => DtEnd; + set => DtEnd = value; + } - /// - /// Returns true if the event is an all-day event. - /// - public virtual bool IsAllDay + /// + /// Returns true if the event is an all-day event. + /// + public virtual bool IsAllDay + { + get => !Start.HasTime; + set { - get => !Start.HasTime; - set + // Set whether or not the start date/time + // has a time value. + if (Start != null) { - // 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); - } + Start.HasTime = !value; + } + if (End != null) + { + End.HasTime = !value; } - } - /// - /// The geographic location (lat/long) of the event. - /// - public GeographicLocation GeographicLocation - { - get => Properties.Get("GEO"); - set => Properties.Set("GEO", value); + if (value && Start != null && End != null && Equals(Start.Date, End.Date)) + { + Duration = default(TimeSpan); + End = Start.AddDays(1); + } } + } - /// - /// The location of the event. - /// - public string Location - { - get => Properties.Get("LOCATION"); - set => Properties.Set("LOCATION", value); - } + /// + /// The geographic location (lat/long) of the event. + /// + public GeographicLocation GeographicLocation + { + get => Properties.Get("GEO"); + set => Properties.Set("GEO", value); + } - /// - /// Resources that will be used during the event. - /// To change existing values, assign a new . - /// Examples: - /// Conference room, Projector - /// - /// - public virtual IList Resources - { - get => Properties.GetMany("RESOURCES"); - set => Properties.Set("RESOURCES", value ?? new List()); - } + /// + /// The location of the event. + /// + public string Location + { + get => Properties.Get("LOCATION"); + set => Properties.Set("LOCATION", value); + } - /// - /// The status of the event. - /// - public string Status - { - get => Properties.Get("STATUS"); - set => Properties.Set("STATUS", value); - } + /// + /// Resources that will be used during the event. + /// To change existing values, assign a new . + /// Examples: + /// Conference room, Projector + /// + /// + public virtual IList Resources + { + get => Properties.GetMany("RESOURCES"); + set => Properties.Set("RESOURCES", value ?? new List()); + } - /// - /// 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 string Transparency - { - get => Properties.Get(TransparencyType.Key); - set => Properties.Set(TransparencyType.Key, value); - } + /// + /// The status of the event. + /// + public string Status + { + get => 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 string Transparency + { + get => Properties.Get(TransparencyType.Key); + set => Properties.Set(TransparencyType.Key, value); + } - private EventEvaluator _mEvaluator; + private EventEvaluator _mEvaluator; - /// - /// Constructs an Event object, with an iCalObject - /// (usually an iCalendar object) as its parent. - /// - public CalendarEvent() + /// + /// Constructs an Event object, with an iCalObject + /// (usually an iCalendar object) as its parent. + /// + public CalendarEvent() + { + Initialize(); + } + + private void Initialize() + { + Name = EventStatus.Name; + + _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 => !string.Equals(Status, EventStatus.Cancelled, EventStatus.Comparison); + + 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(-1); + } + + private void ExtrapolateTimes(int source) + { + /* + * Source values, a fix introduced to prevent stack overflow exceptions from occuring. + * -1 = Anybody, stack overflow could maybe still occur in this case? + * 0 = End + * 1 = Duration + * 2 = DtStart + */ + if (DtEnd == null && DtStart != null && Duration != default(TimeSpan) && source != 0) { - Initialize(); + DtEnd = DtStart.Add(Duration); } - - private void Initialize() + else if (Duration == default(TimeSpan) && DtStart != null && DtEnd != null && source != 1) { - Name = EventStatus.Name; - - _mEvaluator = new EventEvaluator(this); - SetService(_mEvaluator); + Duration = DtEnd.Subtract(DtStart); } - - /// - /// 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) + else if (DtStart == null && Duration != default(TimeSpan) && DtEnd != null && source != 2) { - 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)))); + DtStart = DtEnd.Subtract(Duration); } + } - /// - /// 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) + protected bool Equals(CalendarEvent other) + { + var resourcesSet = new HashSet(StringComparer.OrdinalIgnoreCase); + resourcesSet.UnionWith(Resources); + + var result = + Equals(DtStart, other.DtStart) + && string.Equals(Summary, other.Summary, StringComparison.OrdinalIgnoreCase) + && string.Equals(Description, other.Description, StringComparison.OrdinalIgnoreCase) + && Equals(DtEnd, other.DtEnd) + && string.Equals(Location, other.Location, StringComparison.OrdinalIgnoreCase) + && resourcesSet.SetEquals(other.Resources) + && string.Equals(Status, other.Status, StringComparison.Ordinal) + && IsActive == other.IsActive + && string.Equals(Transparency, other.Transparency, TransparencyType.Comparison) + && EvaluationIncludesReferenceDate == other.EvaluationIncludesReferenceDate + && Attachments.SequenceEqual(other.Attachments) + && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) + && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules); + + if (!result) { - return _mEvaluator.Periods.Any(p => p.StartTime.Equals(dateTime)); + return false; } - /// - /// 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 => !string.Equals(Status, EventStatus.Cancelled, EventStatus.Comparison); - - protected override bool EvaluationIncludesReferenceDate => true; + //RDATEs and EXDATEs are all List, because the spec allows for multiple declarations of collections. + //Consequently we have to contrive a normalized representation before we can determine whether two events are equal - protected override void OnDeserializing(StreamingContext context) + var exDates = PeriodList.GetGroupedPeriods(ExceptionDates); + var otherExDates = PeriodList.GetGroupedPeriods(other.ExceptionDates); + if (exDates.Keys.Count != otherExDates.Keys.Count || !exDates.Keys.OrderBy(k => k).SequenceEqual(otherExDates.Keys.OrderBy(k => k))) { - base.OnDeserializing(context); - - Initialize(); + return false; } - protected override void OnDeserialized(StreamingContext context) + if (exDates.Any(exDate => !exDate.Value.OrderBy(d => d).SequenceEqual(otherExDates[exDate.Key].OrderBy(d => d)))) { - base.OnDeserialized(context); - - ExtrapolateTimes(-1); + return false; } - private void ExtrapolateTimes(int source) + var rDates = PeriodList.GetGroupedPeriods(RecurrenceDates); + var otherRDates = PeriodList.GetGroupedPeriods(other.RecurrenceDates); + if (rDates.Keys.Count != otherRDates.Keys.Count || !rDates.Keys.OrderBy(k => k).SequenceEqual(otherRDates.Keys.OrderBy(k => k))) { - /* - * Source values, a fix introduced to prevent stack overflow exceptions from occuring. - * -1 = Anybody, stack overflow could maybe still occur in this case? - * 0 = End - * 1 = Duration - * 2 = DtStart - */ - if (DtEnd == null && DtStart != null && Duration != default(TimeSpan) && source != 0) - { - DtEnd = DtStart.Add(Duration); - } - else if (Duration == default(TimeSpan) && DtStart != null && DtEnd != null && source != 1) - { - Duration = DtEnd.Subtract(DtStart); - } - else if (DtStart == null && Duration != default(TimeSpan) && DtEnd != null && source != 2) - { - DtStart = DtEnd.Subtract(Duration); - } + return false; } - protected bool Equals(CalendarEvent other) + if (rDates.Any(exDate => !exDate.Value.OrderBy(d => d).SequenceEqual(otherRDates[exDate.Key].OrderBy(d => d)))) { - var resourcesSet = new HashSet(StringComparer.OrdinalIgnoreCase); - resourcesSet.UnionWith(Resources); - - var result = - Equals(DtStart, other.DtStart) - && string.Equals(Summary, other.Summary, StringComparison.OrdinalIgnoreCase) - && string.Equals(Description, other.Description, StringComparison.OrdinalIgnoreCase) - && Equals(DtEnd, other.DtEnd) - && string.Equals(Location, other.Location, StringComparison.OrdinalIgnoreCase) - && resourcesSet.SetEquals(other.Resources) - && string.Equals(Status, other.Status, StringComparison.Ordinal) - && IsActive == other.IsActive - && string.Equals(Transparency, other.Transparency, TransparencyType.Comparison) - && EvaluationIncludesReferenceDate == other.EvaluationIncludesReferenceDate - && Attachments.SequenceEqual(other.Attachments) - && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) - && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules); - - if (!result) - { - return false; - } - - //RDATEs and EXDATEs are all List, because the spec allows for multiple declarations of collections. - //Consequently we have to contrive a normalized representation before we can determine whether two events are equal - - var exDates = PeriodList.GetGroupedPeriods(ExceptionDates); - var otherExDates = PeriodList.GetGroupedPeriods(other.ExceptionDates); - if (exDates.Keys.Count != otherExDates.Keys.Count || !exDates.Keys.OrderBy(k => k).SequenceEqual(otherExDates.Keys.OrderBy(k => k))) - { - return false; - } - - if (exDates.Any(exDate => !exDate.Value.OrderBy(d => d).SequenceEqual(otherExDates[exDate.Key].OrderBy(d => d)))) - { - return false; - } + return false; + } - var rDates = PeriodList.GetGroupedPeriods(RecurrenceDates); - var otherRDates = PeriodList.GetGroupedPeriods(other.RecurrenceDates); - if (rDates.Keys.Count != otherRDates.Keys.Count || !rDates.Keys.OrderBy(k => k).SequenceEqual(otherRDates.Keys.OrderBy(k => k))) - { - return false; - } + return true; + } - if (rDates.Any(exDate => !exDate.Value.OrderBy(d => d).SequenceEqual(otherRDates[exDate.Key].OrderBy(d => d)))) - { - return false; - } + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((CalendarEvent) obj); + } - return true; + public override int GetHashCode() + { + unchecked + { + var hashCode = DtStart?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (Summary?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Description?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (DtEnd?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Location?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ Status?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ IsActive.GetHashCode(); + hashCode = (hashCode * 397) ^ Transparency?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Attachments); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Resources); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCodeForNestedCollection(ExceptionDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCodeForNestedCollection(RecurrenceDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); + return hashCode; } + } - public override bool Equals(object obj) + public int CompareTo(CalendarEvent other) + { + if (DtStart.Equals(other.DtStart)) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj.GetType() == GetType() && Equals((CalendarEvent) obj); + return 0; } - - public override int GetHashCode() + if (DtStart.LessThan(other.DtStart)) { - unchecked - { - var hashCode = DtStart?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ (Summary?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (Description?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (DtEnd?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (Location?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ Status?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ IsActive.GetHashCode(); - hashCode = (hashCode * 397) ^ Transparency?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Attachments); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Resources); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCodeForNestedCollection(ExceptionDates); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCodeForNestedCollection(RecurrenceDates); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); - return hashCode; - } + return -1; } - - public int CompareTo(CalendarEvent other) + if (DtStart.GreaterThan(other.DtStart)) { - 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."); + return 1; } + throw new Exception("An error occurred while comparing two CalDateTimes."); } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarComponents/FreeBusy.cs b/Ical.Net/CalendarComponents/FreeBusy.cs index 9a03a92ae..0f1d822dd 100644 --- a/Ical.Net/CalendarComponents/FreeBusy.cs +++ b/Ical.Net/CalendarComponents/FreeBusy.cs @@ -9,192 +9,191 @@ using Ical.Net.DataTypes; using Ical.Net.Utility; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +public class FreeBusy : UniqueComponent, IMergeable { - public class FreeBusy : UniqueComponent, IMergeable + public static FreeBusy Create(ICalendarObject obj, FreeBusy freeBusyRequest) { - public static FreeBusy Create(ICalendarObject obj, FreeBusy freeBusyRequest) + if (!(obj is IGetOccurrencesTyped)) { - if (!(obj is IGetOccurrencesTyped)) - { - return null; - } - var getOccurrences = (IGetOccurrencesTyped) obj; - var occurrences = getOccurrences.GetOccurrences(freeBusyRequest.Start, freeBusyRequest.End); - var contacts = new List(); - var isFilteredByAttendees = false; + return null; + } + var getOccurrences = (IGetOccurrencesTyped) obj; + var occurrences = getOccurrences.GetOccurrences(freeBusyRequest.Start, freeBusyRequest.End); + var contacts = new List(); + var isFilteredByAttendees = false; - if (freeBusyRequest.Attendees != null && freeBusyRequest.Attendees.Count > 0) + 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) { - isFilteredByAttendees = true; - var attendees = freeBusyRequest.Attendees - .Where(a => a.Value != null) - .Select(a => a.Value.OriginalString.Trim()); - contacts.AddRange(attendees); + continue; } - var fb = freeBusyRequest; - fb.Uid = Guid.NewGuid().ToString(); - fb.Entries.Clear(); - fb.DtStamp = CalDateTime.Now; + var evt = uc as CalendarEvent; + var accepted = false; + var type = FreeBusyStatus.Busy; - foreach (var o in occurrences) + // We only accept events, and only "opaque" events. + if (evt != null && evt.Transparency != TransparencyType.Transparent) { - var uc = o.Source as IUniqueComponent; - - if (uc == null) - { - continue; - } + accepted = true; + } - var evt = uc as CalendarEvent; - var accepted = false; - var type = FreeBusyStatus.Busy; + // 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; - // We only accept events, and only "opaque" events. - if (evt != null && evt.Transparency != TransparencyType.Transparent) - { - accepted = true; - } + var participatingAttendeeQuery = uc.Attendees + .Where(attendee => + attendee.Value != null + && contacts.Contains(attendee.Value.OriginalString.Trim()) + && attendee.ParticipationStatus != null) + .Select(pa => pa.ParticipationStatus.ToUpperInvariant()); - // 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) + foreach (var participatingAttendee in participatingAttendeeQuery) { - 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) { - switch (participatingAttendee) - { - case EventParticipationStatus.Tentative: - accepted = true; - type = FreeBusyStatus.BusyTentative; - break; - case EventParticipationStatus.Accepted: - accepted = true; - type = FreeBusyStatus.Busy; - break; - } + 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) + if (accepted) { - fb.Attendees.Add(attendee); + // If the entry was accepted, add it to our list! + fb.Entries.Add(new FreeBusyEntry(o.Period, type)); } - - return fb; } - public FreeBusy() - { - Name = Components.Freebusy; - } + return fb; + } - public virtual IList Entries + 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) { - get => Properties.GetMany("FREEBUSY"); - set => Properties.Set("FREEBUSY", value); + fb.Organizer = organizer; } - public virtual IDateTime DtStart + if (contacts == null) { - get => Properties.Get("DTSTART"); - set => Properties.Set("DTSTART", value); + return fb; } - - public virtual IDateTime DtEnd + foreach (var attendee in contacts) { - get => Properties.Get("DTEND"); - set => Properties.Set("DTEND", value); + fb.Attendees.Add(attendee); } - public virtual IDateTime Start + return fb; + } + + public FreeBusy() + { + Name = Components.Freebusy; + } + + public virtual IList Entries + { + get => Properties.GetMany("FREEBUSY"); + set => Properties.Set("FREEBUSY", value); + } + + public virtual IDateTime DtStart + { + get => Properties.Get("DTSTART"); + set => Properties.Set("DTSTART", value); + } + + public virtual IDateTime DtEnd + { + get => Properties.Get("DTEND"); + set => Properties.Set("DTEND", value); + } + + public virtual IDateTime Start + { + get => Properties.Get("DTSTART"); + set => Properties.Set("DTSTART", value); + } + + public virtual IDateTime End + { + get => Properties.Get("DTEND"); + set => Properties.Set("DTEND", value); + } + + public virtual FreeBusyStatus GetFreeBusyStatus(Period period) + { + var status = FreeBusyStatus.Free; + if (period == null) { - get => Properties.Get("DTSTART"); - set => Properties.Set("DTSTART", value); + return status; } - public virtual IDateTime End + foreach (var fbe in Entries.Where(fbe => fbe.CollidesWith(period) && status < fbe.Status)) { - get => Properties.Get("DTEND"); - set => Properties.Set("DTEND", value); + status = fbe.Status; } + return status; + } - public virtual FreeBusyStatus GetFreeBusyStatus(Period period) + public virtual FreeBusyStatus GetFreeBusyStatus(IDateTime dt) + { + var status = FreeBusyStatus.Free; + if (dt == null) { - 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) + foreach (var fbe in Entries.Where(fbe => fbe.Contains(dt) && status < fbe.Status)) { - 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; + status = fbe.Status; } + return status; + } - public virtual void MergeWith(IMergeable obj) + public virtual void MergeWith(IMergeable obj) + { + if (!(obj is FreeBusy fb)) { - if (!(obj is FreeBusy fb)) - { - return; - } - - Entries.AddRange(fb.Entries.Where(entry => !Entries.Contains(entry))); + return; } + + Entries.AddRange(fb.Entries.Where(entry => !Entries.Contains(entry))); } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarComponents/IAlarmContainer.cs b/Ical.Net/CalendarComponents/IAlarmContainer.cs index a0c3ebd47..8aa5f74d3 100644 --- a/Ical.Net/CalendarComponents/IAlarmContainer.cs +++ b/Ical.Net/CalendarComponents/IAlarmContainer.cs @@ -6,24 +6,23 @@ using System.Collections.Generic; using Ical.Net.DataTypes; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +public interface IAlarmContainer { - public interface IAlarmContainer - { - /// - /// A list of s for this recurring component. - /// - ICalendarObjectList Alarms { get; } + /// + /// 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); - } -} + /// + /// 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/Ical.Net/CalendarComponents/ICalendarComponent.cs b/Ical.Net/CalendarComponents/ICalendarComponent.cs index 39dd613e0..7df4820f2 100644 --- a/Ical.Net/CalendarComponents/ICalendarComponent.cs +++ b/Ical.Net/CalendarComponents/ICalendarComponent.cs @@ -3,7 +3,6 @@ // Licensed under the MIT license. // -namespace Ical.Net.CalendarComponents -{ - public interface ICalendarComponent : ICalendarPropertyListContainer { } -} +namespace Ical.Net.CalendarComponents; + +public interface ICalendarComponent : ICalendarPropertyListContainer { } \ No newline at end of file diff --git a/Ical.Net/CalendarComponents/IRecurrable.cs b/Ical.Net/CalendarComponents/IRecurrable.cs index 03e033985..31d242152 100644 --- a/Ical.Net/CalendarComponents/IRecurrable.cs +++ b/Ical.Net/CalendarComponents/IRecurrable.cs @@ -6,19 +6,18 @@ using System.Collections.Generic; using Ical.Net.DataTypes; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +public interface IRecurrable : IGetOccurrences, IServiceProvider { - public interface IRecurrable : IGetOccurrences, IServiceProvider - { - /// - /// Gets/sets the start date/time of the component. - /// - IDateTime Start { get; set; } + /// + /// 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; } - } -} + 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/Ical.Net/CalendarComponents/IRecurringComponent.cs b/Ical.Net/CalendarComponents/IRecurringComponent.cs index 0a630ddc3..eac21bfa2 100644 --- a/Ical.Net/CalendarComponents/IRecurringComponent.cs +++ b/Ical.Net/CalendarComponents/IRecurringComponent.cs @@ -6,20 +6,19 @@ using System.Collections.Generic; using Ical.Net.DataTypes; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +public interface IRecurringComponent : IUniqueComponent, IRecurrable { - 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; } - } -} + 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/Ical.Net/CalendarComponents/IUniqueComponent.cs b/Ical.Net/CalendarComponents/IUniqueComponent.cs index bd0917c9d..3536a5204 100644 --- a/Ical.Net/CalendarComponents/IUniqueComponent.cs +++ b/Ical.Net/CalendarComponents/IUniqueComponent.cs @@ -7,17 +7,16 @@ using System.Collections.Generic; using Ical.Net.DataTypes; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +public interface IUniqueComponent : ICalendarComponent { - public interface IUniqueComponent : ICalendarComponent - { - string Uid { get; set; } + 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; } - } -} + 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/Ical.Net/CalendarComponents/Journal.cs b/Ical.Net/CalendarComponents/Journal.cs index d620b6107..0cedc9d4a 100644 --- a/Ical.Net/CalendarComponents/Journal.cs +++ b/Ical.Net/CalendarComponents/Journal.cs @@ -5,49 +5,48 @@ using System.Runtime.Serialization; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +/// +/// A class that represents an RFC 5545 VJOURNAL component. +/// +public class Journal : RecurringComponent { + public string Status + { + get => Properties.Get(JournalStatus.Key); + set => Properties.Set(JournalStatus.Key, value); + } + /// - /// A class that represents an RFC 5545 VJOURNAL component. + /// Constructs an Journal object, with an iCalObject + /// (usually an iCalendar object) as its parent. /// - public class Journal : RecurringComponent + public Journal() + { + Name = JournalStatus.Name; + } + + protected override bool EvaluationIncludesReferenceDate => true; + + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); + } + + protected bool Equals(Journal other) => Start.Equals(other.Start) && Equals(other as RecurringComponent); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((Journal) obj); + } + + public override int GetHashCode() { - public string Status - { - get => Properties.Get(JournalStatus.Key); - set => Properties.Set(JournalStatus.Key, value); - } - - /// - /// Constructs an Journal object, with an iCalObject - /// (usually an iCalendar object) as its parent. - /// - public Journal() - { - Name = JournalStatus.Name; - } - - protected override bool EvaluationIncludesReferenceDate => true; - - protected override void OnDeserializing(StreamingContext context) - { - base.OnDeserializing(context); - } - - protected bool Equals(Journal other) => Start.Equals(other.Start) && Equals(other as RecurringComponent); - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj.GetType() == GetType() && Equals((Journal) obj); - } - - public override int GetHashCode() - { - var hashCode = Start?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ base.GetHashCode(); - return hashCode; - } + var hashCode = Start?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ base.GetHashCode(); + return hashCode; } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarComponents/RecurringComponent.cs b/Ical.Net/CalendarComponents/RecurringComponent.cs index 7c953657d..b9e004d5d 100644 --- a/Ical.Net/CalendarComponents/RecurringComponent.cs +++ b/Ical.Net/CalendarComponents/RecurringComponent.cs @@ -12,236 +12,235 @@ using Ical.Net.Proxies; using Ical.Net.Utility; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +/// +/// 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 { - /// - /// 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) => SortByDate(list); + public static IEnumerable SortByDate(IEnumerable list) => SortByDate(list); - public static IEnumerable SortByDate(IEnumerable list) => list.OrderBy(d => d); + public static IEnumerable SortByDate(IEnumerable list) => list.OrderBy(d => d); - protected virtual bool EvaluationIncludesReferenceDate => false; + protected virtual bool EvaluationIncludesReferenceDate => false; - public virtual IList Attachments - { - get => Properties.GetMany("ATTACH"); - set => Properties.Set("ATTACH", value); - } + public virtual IList Attachments + { + get => Properties.GetMany("ATTACH"); + set => Properties.Set("ATTACH", value); + } - public virtual IList Categories - { - get => Properties.GetMany("CATEGORIES"); - set => Properties.Set("CATEGORIES", value); - } + public virtual IList Categories + { + get => Properties.GetMany("CATEGORIES"); + set => Properties.Set("CATEGORIES", value); + } - public virtual string Class - { - get => Properties.Get("CLASS"); - set => Properties.Set("CLASS", value); - } + public virtual string Class + { + get => Properties.Get("CLASS"); + set => Properties.Set("CLASS", value); + } - public virtual IList Contacts - { - get => Properties.GetMany("CONTACT"); - set => Properties.Set("CONTACT", value); - } + public virtual IList Contacts + { + get => Properties.GetMany("CONTACT"); + set => Properties.Set("CONTACT", value); + } - public virtual IDateTime Created - { - get => Properties.Get("CREATED"); - set => Properties.Set("CREATED", value); - } + public virtual IDateTime Created + { + get => Properties.Get("CREATED"); + set => Properties.Set("CREATED", value); + } - public virtual string Description - { - get => Properties.Get("DESCRIPTION"); - set => Properties.Set("DESCRIPTION", value); - } + public virtual string Description + { + get => Properties.Get("DESCRIPTION"); + set => Properties.Set("DESCRIPTION", value); + } - /// - /// The start date/time of the component. - /// - public virtual IDateTime DtStart - { - get => Properties.Get("DTSTART"); - set => Properties.Set("DTSTART", value); - } + /// + /// The start date/time of the component. + /// + public virtual IDateTime DtStart + { + get => Properties.Get("DTSTART"); + set => Properties.Set("DTSTART", value); + } - public virtual IList ExceptionDates - { - get => Properties.GetMany("EXDATE"); - set => Properties.Set("EXDATE", value); - } + public virtual IList ExceptionDates + { + get => Properties.GetMany("EXDATE"); + set => Properties.Set("EXDATE", value); + } - public virtual IList ExceptionRules - { - get => Properties.GetMany("EXRULE"); - set => Properties.Set("EXRULE", value); - } + public virtual IList ExceptionRules + { + get => Properties.GetMany("EXRULE"); + set => Properties.Set("EXRULE", value); + } - public virtual IDateTime LastModified - { - get => Properties.Get("LAST-MODIFIED"); - set => Properties.Set("LAST-MODIFIED", value); - } + public virtual IDateTime LastModified + { + get => Properties.Get("LAST-MODIFIED"); + set => Properties.Set("LAST-MODIFIED", value); + } - public virtual int Priority - { - get => Properties.Get("PRIORITY"); - set => Properties.Set("PRIORITY", value); - } + public virtual int Priority + { + get => Properties.Get("PRIORITY"); + set => Properties.Set("PRIORITY", value); + } - public virtual IList RecurrenceDates - { - get => Properties.GetMany("RDATE"); - set => Properties.Set("RDATE", value); - } + public virtual IList RecurrenceDates + { + get => Properties.GetMany("RDATE"); + set => Properties.Set("RDATE", value); + } - public virtual IList RecurrenceRules - { - get => Properties.GetMany("RRULE"); - set => Properties.Set("RRULE", value); - } + public virtual IList RecurrenceRules + { + get => Properties.GetMany("RRULE"); + set => Properties.Set("RRULE", value); + } - public virtual IDateTime RecurrenceId - { - get => Properties.Get("RECURRENCE-ID"); - set => Properties.Set("RECURRENCE-ID", value); - } + public virtual IDateTime RecurrenceId + { + get => Properties.Get("RECURRENCE-ID"); + set => Properties.Set("RECURRENCE-ID", value); + } - public virtual IList RelatedComponents - { - get => Properties.GetMany("RELATED-TO"); - set => Properties.Set("RELATED-TO", value); - } + public virtual IList RelatedComponents + { + get => Properties.GetMany("RELATED-TO"); + set => Properties.Set("RELATED-TO", value); + } - public virtual int Sequence - { - get => Properties.Get("SEQUENCE"); - set => Properties.Set("SEQUENCE", value); - } + public virtual int Sequence + { + get => Properties.Get("SEQUENCE"); + set => Properties.Set("SEQUENCE", value); + } - /// - /// An alias to the DTStart field (i.e. start date/time). - /// - public virtual IDateTime Start - { - get => DtStart; - set => DtStart = value; - } + /// + /// An alias to the DTStart field (i.e. start date/time). + /// + public virtual IDateTime Start + { + get => DtStart; + set => DtStart = value; + } - public virtual string Summary - { - get => Properties.Get("SUMMARY"); - set => Properties.Set("SUMMARY", value); - } + public virtual string Summary + { + get => Properties.Get("SUMMARY"); + set => Properties.Set("SUMMARY", value); + } - /// - /// A list of s for this recurring component. - /// - public virtual ICalendarObjectList Alarms => new CalendarObjectListProxy(Children); + /// + /// A list of s for this recurring component. + /// + public virtual ICalendarObjectList Alarms => new CalendarObjectListProxy(Children); - public RecurringComponent() - { - Initialize(); - EnsureProperties(); - } + public RecurringComponent() + { + Initialize(); + EnsureProperties(); + } - public RecurringComponent(string name) : base(name) - { - Initialize(); - EnsureProperties(); - } + public RecurringComponent(string name) : base(name) + { + Initialize(); + EnsureProperties(); + } - private void Initialize() => SetService(new RecurringEvaluator(this)); + private void Initialize() => SetService(new RecurringEvaluator(this)); - private void EnsureProperties() + private void EnsureProperties() + { + if (!Properties.ContainsKey("SEQUENCE")) { - if (!Properties.ContainsKey("SEQUENCE")) - { - Sequence = 0; - } + Sequence = 0; } + } - protected override void OnDeserializing(StreamingContext context) - { - base.OnDeserializing(context); + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); - Initialize(); - } + Initialize(); + } - public virtual void ClearEvaluation() => RecurrenceUtil.ClearEvaluation(this); + public virtual void ClearEvaluation() => RecurrenceUtil.ClearEvaluation(this); - public virtual HashSet GetOccurrences(IDateTime dt) => RecurrenceUtil.GetOccurrences(this, dt, EvaluationIncludesReferenceDate); + public virtual HashSet GetOccurrences(IDateTime dt) => RecurrenceUtil.GetOccurrences(this, dt, EvaluationIncludesReferenceDate); - public virtual HashSet GetOccurrences(DateTime dt) - => RecurrenceUtil.GetOccurrences(this, new CalDateTime(dt), EvaluationIncludesReferenceDate); + public virtual HashSet GetOccurrences(DateTime dt) + => RecurrenceUtil.GetOccurrences(this, new CalDateTime(dt), EvaluationIncludesReferenceDate); - public virtual HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) - => RecurrenceUtil.GetOccurrences(this, startTime, endTime, EvaluationIncludesReferenceDate); + public virtual HashSet GetOccurrences(IDateTime startTime, IDateTime endTime) + => RecurrenceUtil.GetOccurrences(this, startTime, endTime, EvaluationIncludesReferenceDate); - public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) - => RecurrenceUtil.GetOccurrences(this, new CalDateTime(startTime), new CalDateTime(endTime), EvaluationIncludesReferenceDate); + public virtual HashSet GetOccurrences(DateTime startTime, DateTime endTime) + => RecurrenceUtil.GetOccurrences(this, new CalDateTime(startTime), new CalDateTime(endTime), EvaluationIncludesReferenceDate); - public virtual IList PollAlarms() => PollAlarms(null, null); + public virtual IList PollAlarms() => PollAlarms(null, null); - public virtual IList PollAlarms(IDateTime startTime, IDateTime endTime) - => Alarms?.SelectMany(a => a.Poll(startTime, endTime)).ToList() - ?? new List(); + public virtual IList PollAlarms(IDateTime startTime, IDateTime endTime) + => Alarms?.SelectMany(a => a.Poll(startTime, endTime)).ToList() + ?? new List(); - protected bool Equals(RecurringComponent other) - { - var result = Equals(DtStart, other.DtStart) - && Equals(Priority, other.Priority) - && string.Equals(Summary, other.Summary, StringComparison.OrdinalIgnoreCase) - && string.Equals(Class, other.Class, StringComparison.OrdinalIgnoreCase) - && string.Equals(Description, other.Description, StringComparison.OrdinalIgnoreCase) - && Equals(RecurrenceId, other.RecurrenceId) - && Attachments.SequenceEqual(other.Attachments) - && CollectionHelpers.Equals(Categories, other.Categories) - && CollectionHelpers.Equals(Contacts, other.Contacts) - && CollectionHelpers.Equals(ExceptionDates, other.ExceptionDates) - && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) - && CollectionHelpers.Equals(RecurrenceDates, other.RecurrenceDates, orderSignificant: true) - && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules, orderSignificant: true); - - return result; - } + protected bool Equals(RecurringComponent other) + { + var result = Equals(DtStart, other.DtStart) + && Equals(Priority, other.Priority) + && string.Equals(Summary, other.Summary, StringComparison.OrdinalIgnoreCase) + && string.Equals(Class, other.Class, StringComparison.OrdinalIgnoreCase) + && string.Equals(Description, other.Description, StringComparison.OrdinalIgnoreCase) + && Equals(RecurrenceId, other.RecurrenceId) + && Attachments.SequenceEqual(other.Attachments) + && CollectionHelpers.Equals(Categories, other.Categories) + && CollectionHelpers.Equals(Contacts, other.Contacts) + && CollectionHelpers.Equals(ExceptionDates, other.ExceptionDates) + && CollectionHelpers.Equals(ExceptionRules, other.ExceptionRules) + && CollectionHelpers.Equals(RecurrenceDates, other.RecurrenceDates, orderSignificant: true) + && CollectionHelpers.Equals(RecurrenceRules, other.RecurrenceRules, orderSignificant: true); + + return result; + } - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj.GetType() == GetType() && Equals((RecurringComponent) obj); - } + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((RecurringComponent) obj); + } - public override int GetHashCode() - { - unchecked - { - var hashCode = DtStart?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ Priority.GetHashCode(); - hashCode = (hashCode * 397) ^ (Summary?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (Class?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (Description?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (RecurrenceId?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Attachments); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Categories); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Contacts); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionDates); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceDates); - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); - return hashCode; - } + public override int GetHashCode() + { + unchecked + { + var hashCode = DtStart?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ Priority.GetHashCode(); + hashCode = (hashCode * 397) ^ (Summary?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Class?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Description?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (RecurrenceId?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Attachments); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Categories); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Contacts); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(ExceptionRules); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceDates); + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(RecurrenceRules); + return hashCode; } } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarComponents/Todo.cs b/Ical.Net/CalendarComponents/Todo.cs index d2242370a..6d8544f33 100644 --- a/Ical.Net/CalendarComponents/Todo.cs +++ b/Ical.Net/CalendarComponents/Todo.cs @@ -11,206 +11,205 @@ using Ical.Net.DataTypes; using Ical.Net.Evaluation; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +/// +/// A class that represents an RFC 5545 VTODO component. +/// +[DebuggerDisplay("{Summary} - {Status}")] +public class Todo : RecurringComponent, IAlarmContainer { + private readonly TodoEvaluator _mEvaluator; + /// - /// A class that represents an RFC 5545 VTODO component. + /// The date/time the todo was completed. /// - [DebuggerDisplay("{Summary} - {Status}")] - public class Todo : RecurringComponent, IAlarmContainer + public virtual IDateTime Completed { - private readonly TodoEvaluator _mEvaluator; + get => Properties.Get("COMPLETED"); + set => Properties.Set("COMPLETED", value); + } - /// - /// The date/time the todo was completed. - /// - public virtual IDateTime Completed + /// + /// The start date/time of the todo item. + /// + public override IDateTime DtStart + { + get => base.DtStart; + set { - get => Properties.Get("COMPLETED"); - set => Properties.Set("COMPLETED", value); + base.DtStart = value; + ExtrapolateTimes(2); } + } - /// - /// The start date/time of the todo item. - /// - public override IDateTime DtStart + /// + /// The due date of the todo item. + /// + public virtual IDateTime Due + { + get => Properties.Get("DUE"); + set { - get => base.DtStart; - set - { - base.DtStart = value; - ExtrapolateTimes(2); - } + Properties.Set("DUE", value); + ExtrapolateTimes(0); } + } - /// - /// The due date of the todo item. - /// - public virtual IDateTime Due + /// + /// 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 => Properties.Get("DURATION"); + set { - get => Properties.Get("DUE"); - set - { - Properties.Set("DUE", value); - ExtrapolateTimes(0); - } + Properties.Set("DURATION", value); + ExtrapolateTimes(1); } + } - /// - /// 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 + public virtual GeographicLocation GeographicLocation + { + get => Properties.Get("GEO"); + set => Properties.Set("GEO", value); + } + + public virtual string Location + { + get => Properties.Get("LOCATION"); + set => Properties.Set("LOCATION", value); + } + + public virtual int PercentComplete + { + get => Properties.Get("PERCENT-COMPLETE"); + set => Properties.Set("PERCENT-COMPLETE", value); + } + + public virtual IList Resources + { + get => Properties.GetMany("RESOURCES"); + set => Properties.Set("RESOURCES", value ?? new List()); + } + + /// + /// The status of the todo item. + /// + public virtual string Status + { + get => Properties.Get(TodoStatus.Key); + set { - get => Properties.Get("DURATION"); - set + if (string.Equals(Status, value, TodoStatus.Comparison)) { - Properties.Set("DURATION", value); - ExtrapolateTimes(1); + return; } - } - public virtual GeographicLocation GeographicLocation - { - get => Properties.Get("GEO"); - set => Properties.Set("GEO", 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 = string.Equals(value, TodoStatus.Completed, TodoStatus.Comparison) + ? CalDateTime.Now + : null; + } - public virtual string Location - { - get => Properties.Get("LOCATION"); - set => Properties.Set("LOCATION", value); + Properties.Set(TodoStatus.Key, value); } + } - public virtual int PercentComplete - { - get => Properties.Get("PERCENT-COMPLETE"); - set => Properties.Set("PERCENT-COMPLETE", value); - } + public Todo() + { + Name = TodoStatus.Name; - public virtual IList Resources - { - get => Properties.GetMany("RESOURCES"); - set => Properties.Set("RESOURCES", value ?? new List()); - } + _mEvaluator = new TodoEvaluator(this); + SetService(_mEvaluator); + } - /// - /// The status of the todo item. - /// - public virtual string Status + /// + /// 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) { - get => Properties.Get(TodoStatus.Key); - set + if (Completed == null || Completed.GreaterThan(currDt)) { - if (string.Equals(Status, value, TodoStatus.Comparison)) - { - return; - } - - // 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 = string.Equals(value, TodoStatus.Completed, TodoStatus.Comparison) - ? CalDateTime.Now - : null; - } - - Properties.Set(TodoStatus.Key, value); + return true; } - } - public Todo() - { - Name = TodoStatus.Name; + // Evaluate to the previous occurrence. + _mEvaluator.EvaluateToPreviousOccurrence(Completed, currDt); - _mEvaluator = new TodoEvaluator(this); - SetService(_mEvaluator); + return _mEvaluator.Periods.Cast().All(p => !p.StartTime.GreaterThan(Completed) || !currDt.GreaterThanOrEqual(p.StartTime)); } + return false; + } - /// - /// 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; - } + /// + /// 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) + => (DtStart == null || currDt.GreaterThanOrEqual(DtStart)) + && (!IsCompleted(currDt) && !IsCancelled); - // Evaluate to the previous occurrence. - _mEvaluator.EvaluateToPreviousOccurrence(Completed, currDt); + /// + /// Returns True if the todo item was cancelled. + /// + /// True if the todo was cancelled, False otherwise. + public virtual bool IsCancelled => string.Equals(Status, TodoStatus.Cancelled, TodoStatus.Comparison); - return _mEvaluator.Periods.Cast().All(p => !p.StartTime.GreaterThan(Completed) || !currDt.GreaterThanOrEqual(p.StartTime)); - } - return false; - } + protected override bool EvaluationIncludesReferenceDate => true; - /// - /// 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) - => (DtStart == null || currDt.GreaterThanOrEqual(DtStart)) - && (!IsCompleted(currDt) && !IsCancelled); - - /// - /// Returns True if the todo item was cancelled. - /// - /// True if the todo was cancelled, False otherwise. - public virtual bool IsCancelled => string.Equals(Status, TodoStatus.Cancelled, TodoStatus.Comparison); - - protected override bool EvaluationIncludesReferenceDate => true; - - protected override void OnDeserializing(StreamingContext context) + protected override void OnDeserializing(StreamingContext context) + { + //ToDo: a necessary evil, for now + base.OnDeserializing(context); + } + + private void ExtrapolateTimes(int source) + { + /* + * Source values, a fix introduced to prevent StackOverflow exceptions from occuring. + * 0 = Due + * 1 = Duration + * 2 = DtStart + */ + if (Due == null && DtStart != null && Duration != default(TimeSpan) && source != 0) { - //ToDo: a necessary evil, for now - base.OnDeserializing(context); + Due = DtStart.Add(Duration); } - - private void ExtrapolateTimes(int source) + else if (Duration == default(TimeSpan) && DtStart != null && Due != null && source != 1) { - /* - * Source values, a fix introduced to prevent StackOverflow exceptions from occuring. - * 0 = Due - * 1 = Duration - * 2 = DtStart - */ - if (Due == null && DtStart != null && Duration != default(TimeSpan) && source != 0) - { - Due = DtStart.Add(Duration); - } - else if (Duration == default(TimeSpan) && DtStart != null && Due != null && source != 1) - { - Duration = Due.Subtract(DtStart); - } - else if (DtStart == null && Duration != default(TimeSpan) && Due != null && source != 2) - { - DtStart = Due.Subtract(Duration); - } + Duration = Due.Subtract(DtStart); + } + else if (DtStart == null && Duration != default(TimeSpan) && Due != null && source != 2) + { + DtStart = Due.Subtract(Duration); } } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarComponents/UniqueComponent.cs b/Ical.Net/CalendarComponents/UniqueComponent.cs index 96a04d767..ea355bd93 100644 --- a/Ical.Net/CalendarComponents/UniqueComponent.cs +++ b/Ical.Net/CalendarComponents/UniqueComponent.cs @@ -9,114 +9,113 @@ using Ical.Net.DataTypes; using Ical.Net.Utility; -namespace Ical.Net.CalendarComponents +namespace Ical.Net.CalendarComponents; + +/// +/// 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 { - /// - /// 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() { - // 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. + EnsureProperties(); + } - public UniqueComponent() - { - EnsureProperties(); - } + public UniqueComponent(string name) : base(name) + { + EnsureProperties(); + } - public UniqueComponent(string name) : base(name) + private void EnsureProperties() + { + if (string.IsNullOrEmpty(Uid)) { - EnsureProperties(); + // Create a new UID for the component + Uid = Guid.NewGuid().ToString(); } - private void EnsureProperties() + // 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) { - 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) - { - // icalendar RFC doesn't care about sub-second time resolution, so shave off everything smaller than seconds. - var utcNow = DateTime.UtcNow.Truncate(TimeSpan.FromSeconds(1)); - DtStamp = new CalDateTime(utcNow, "UTC"); - } + // icalendar RFC doesn't care about sub-second time resolution, so shave off everything smaller than seconds. + var utcNow = DateTime.UtcNow.Truncate(TimeSpan.FromSeconds(1)); + DtStamp = new CalDateTime(utcNow, "UTC"); } + } - public virtual IList Attendees - { - get => Properties.GetMany("ATTENDEE"); - set => Properties.Set("ATTENDEE", value); - } + public virtual IList Attendees + { + get => Properties.GetMany("ATTENDEE"); + set => Properties.Set("ATTENDEE", value); + } - public virtual IList Comments - { - get => Properties.GetMany("COMMENT"); - set => Properties.Set("COMMENT", value); - } + public virtual IList Comments + { + get => Properties.GetMany("COMMENT"); + set => Properties.Set("COMMENT", value); + } - public virtual IDateTime DtStamp - { - get => Properties.Get("DTSTAMP"); - set => Properties.Set("DTSTAMP", value); - } + public virtual IDateTime DtStamp + { + get => Properties.Get("DTSTAMP"); + set => Properties.Set("DTSTAMP", value); + } - public virtual Organizer Organizer - { - get => Properties.Get("ORGANIZER"); - set => Properties.Set("ORGANIZER", value); - } + public virtual Organizer Organizer + { + get => Properties.Get("ORGANIZER"); + set => Properties.Set("ORGANIZER", value); + } - public virtual IList RequestStatuses - { - get => Properties.GetMany("REQUEST-STATUS"); - set => Properties.Set("REQUEST-STATUS", value); - } + public virtual IList RequestStatuses + { + get => Properties.GetMany("REQUEST-STATUS"); + set => Properties.Set("REQUEST-STATUS", value); + } - public virtual Uri Url - { - get => Properties.Get("URL"); - set => Properties.Set("URL", value); - } + public virtual Uri Url + { + get => Properties.Get("URL"); + set => Properties.Set("URL", value); + } - protected override void OnDeserialized(StreamingContext context) - { - base.OnDeserialized(context); + protected override void OnDeserialized(StreamingContext context) + { + base.OnDeserialized(context); - EnsureProperties(); - } + EnsureProperties(); + } - public int CompareTo(UniqueComponent other) - => string.Compare(Uid, other.Uid, StringComparison.OrdinalIgnoreCase); + public int CompareTo(UniqueComponent other) + => string.Compare(Uid, other.Uid, StringComparison.OrdinalIgnoreCase); - public override bool Equals(object obj) + public override bool Equals(object obj) + { + if (obj is RecurringComponent && obj != this) { - if (obj is RecurringComponent && obj != this) + var r = (RecurringComponent) obj; + if (Uid != null) { - var r = (RecurringComponent) obj; - if (Uid != null) - { - return Uid.Equals(r.Uid); - } - return Uid == r.Uid; + return Uid.Equals(r.Uid); } - return base.Equals(obj); + return Uid == r.Uid; } + return base.Equals(obj); + } - public override int GetHashCode() => Uid?.GetHashCode() ?? base.GetHashCode(); + public override int GetHashCode() => Uid?.GetHashCode() ?? base.GetHashCode(); - public virtual string Uid - { - get => Properties.Get("UID"); - set => Properties.Set("UID", value); - } + public virtual string Uid + { + get => Properties.Get("UID"); + set => Properties.Set("UID", value); } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarComponents/VTimeZone.cs b/Ical.Net/CalendarComponents/VTimeZone.cs index abd6d574d..3ca05b2cc 100644 --- a/Ical.Net/CalendarComponents/VTimeZone.cs +++ b/Ical.Net/CalendarComponents/VTimeZone.cs @@ -12,379 +12,378 @@ using NodaTime; using NodaTime.TimeZones; -namespace Ical.Net.CalendarComponents -{ - /// - /// Represents an RFC 5545 VTIMEZONE component. - /// - public class VTimeZone : CalendarComponent - { - public static VTimeZone FromLocalTimeZone() - => FromDateTimeZone(DateUtil.LocalDateTimeZone.Id); - - public static VTimeZone FromLocalTimeZone(DateTime earlistDateTimeToSupport, bool includeHistoricalData) - => FromDateTimeZone(DateUtil.LocalDateTimeZone.Id, earlistDateTimeToSupport, includeHistoricalData); +namespace Ical.Net.CalendarComponents; - public static VTimeZone FromSystemTimeZone(TimeZoneInfo tzinfo) - => FromSystemTimeZone(tzinfo, new DateTime(DateTime.Now.Year, 1, 1), false); +/// +/// Represents an RFC 5545 VTIMEZONE component. +/// +public class VTimeZone : CalendarComponent +{ + public static VTimeZone FromLocalTimeZone() + => FromDateTimeZone(DateUtil.LocalDateTimeZone.Id); - public static VTimeZone FromSystemTimeZone(TimeZoneInfo tzinfo, DateTime earlistDateTimeToSupport, bool includeHistoricalData) - => FromDateTimeZone(tzinfo.Id, earlistDateTimeToSupport, includeHistoricalData); + public static VTimeZone FromLocalTimeZone(DateTime earlistDateTimeToSupport, bool includeHistoricalData) + => FromDateTimeZone(DateUtil.LocalDateTimeZone.Id, earlistDateTimeToSupport, includeHistoricalData); - public static VTimeZone FromDateTimeZone(string tzId) - => FromDateTimeZone(tzId, new DateTime(DateTime.Now.Year, 1, 1), includeHistoricalData: false); + public static VTimeZone FromSystemTimeZone(TimeZoneInfo tzinfo) + => FromSystemTimeZone(tzinfo, new DateTime(DateTime.Now.Year, 1, 1), false); - public static VTimeZone FromDateTimeZone(string tzId, DateTime earlistDateTimeToSupport, bool includeHistoricalData) - { - var vTimeZone = new VTimeZone(tzId); + public static VTimeZone FromSystemTimeZone(TimeZoneInfo tzinfo, DateTime earlistDateTimeToSupport, bool includeHistoricalData) + => FromDateTimeZone(tzinfo.Id, earlistDateTimeToSupport, includeHistoricalData); - var earliestYear = 1900; - var earliestMonth = earlistDateTimeToSupport.Month; - var earliestDay = earlistDateTimeToSupport.Day; - // Support date/times for January 1st of the previous year by default. - if (earlistDateTimeToSupport.Year > 1900) - { - earliestYear = earlistDateTimeToSupport.Year - 1; - // Since we went back a year, we can't still be in a leap-year - if (earliestMonth == 2 && earliestDay == 29) - earliestDay = 28; - } - else - { - // Going back to 1900, which wasn't a leap year, so we need to switch to Feb 20 - if (earliestMonth == 2 && earliestDay == 29) - earliestDay = 28; - } - var earliest = Instant.FromUtc(earliestYear, earliestMonth, earliestDay, - earlistDateTimeToSupport.Hour, earlistDateTimeToSupport.Minute); + public static VTimeZone FromDateTimeZone(string tzId) + => FromDateTimeZone(tzId, new DateTime(DateTime.Now.Year, 1, 1), includeHistoricalData: false); - // Only include historical data if asked to do so. Otherwise, - // use only the most recent adjustment rules available. - var intervals = vTimeZone._nodaZone.GetZoneIntervals(earliest, Instant.FromDateTimeOffset(DateTimeOffset.Now)) - .Where(z => z.HasStart && z.Start != Instant.MinValue) - .ToList(); - - var matchingDaylightIntervals = new List(); - var matchingStandardIntervals = new List(); + public static VTimeZone FromDateTimeZone(string tzId, DateTime earlistDateTimeToSupport, bool includeHistoricalData) + { + var vTimeZone = new VTimeZone(tzId); - // if there are no intervals, create at least one standard interval - if (!intervals.Any()) - { - var start = new DateTimeOffset(new DateTime(earliestYear, 1, 1), new TimeSpan(vTimeZone._nodaZone.MaxOffset.Ticks)); - var interval = new ZoneInterval( - name: vTimeZone._nodaZone.Id, - start: Instant.FromDateTimeOffset(start), - end: Instant.FromDateTimeOffset(start) + Duration.FromHours(1), - wallOffset: vTimeZone._nodaZone.MinOffset, - savings: Offset.Zero); - intervals.Add(interval); - var zoneInfo = CreateTimeZoneInfo(intervals, new List(), true, true); - vTimeZone.AddChild(zoneInfo); - } - else - { - // first, get the latest standard and daylight intervals, find the oldest recurring date in both, set the RRULES for it, and create a VTimeZoneInfos out of them. - //standard - var standardIntervals = intervals.Where(x => x.Savings.ToTimeSpan() == new TimeSpan(0)).ToList(); - var latestStandardInterval = standardIntervals.OrderByDescending(x => x.Start).FirstOrDefault(); - matchingStandardIntervals = GetMatchingIntervals(standardIntervals, latestStandardInterval, true); - var latestStandardTimeZoneInfo = CreateTimeZoneInfo(matchingStandardIntervals, intervals); - vTimeZone.AddChild(latestStandardTimeZoneInfo); - - // check to see if there is no active, future daylight savings (ie, America/Phoenix) - if (latestStandardInterval != null && (latestStandardInterval.HasEnd ? latestStandardInterval.End : Instant.MaxValue) != Instant.MaxValue) - { - //daylight - var daylightIntervals = intervals.Where(x => x.Savings.ToTimeSpan() != new TimeSpan(0)).ToList(); - - if (daylightIntervals.Any()) - { - var latestDaylightInterval = daylightIntervals.OrderByDescending(x => x.Start).FirstOrDefault(); - matchingDaylightIntervals = GetMatchingIntervals(daylightIntervals, latestDaylightInterval, true); - var latestDaylightTimeZoneInfo = CreateTimeZoneInfo(matchingDaylightIntervals, intervals); - vTimeZone.AddChild(latestDaylightTimeZoneInfo); - } - } - } + var earliestYear = 1900; + var earliestMonth = earlistDateTimeToSupport.Month; + var earliestDay = earlistDateTimeToSupport.Day; + // Support date/times for January 1st of the previous year by default. + if (earlistDateTimeToSupport.Year > 1900) + { + earliestYear = earlistDateTimeToSupport.Year - 1; + // Since we went back a year, we can't still be in a leap-year + if (earliestMonth == 2 && earliestDay == 29) + earliestDay = 28; + } + else + { + // Going back to 1900, which wasn't a leap year, so we need to switch to Feb 20 + if (earliestMonth == 2 && earliestDay == 29) + earliestDay = 28; + } + var earliest = Instant.FromUtc(earliestYear, earliestMonth, earliestDay, + earlistDateTimeToSupport.Hour, earlistDateTimeToSupport.Minute); - if (!includeHistoricalData || intervals.Count == 1) - { - return vTimeZone; - } + // Only include historical data if asked to do so. Otherwise, + // use only the most recent adjustment rules available. + var intervals = vTimeZone._nodaZone.GetZoneIntervals(earliest, Instant.FromDateTimeOffset(DateTimeOffset.Now)) + .Where(z => z.HasStart && z.Start != Instant.MinValue) + .ToList(); - // then, do the historic intervals, using RDATE for them - var historicIntervals = intervals.Where(x => !matchingDaylightIntervals.Contains(x) && !matchingStandardIntervals.Contains(x)).ToList(); + var matchingDaylightIntervals = new List(); + var matchingStandardIntervals = new List(); - while (historicIntervals.Any(x => x.Start != Instant.MinValue)) + // if there are no intervals, create at least one standard interval + if (!intervals.Any()) + { + var start = new DateTimeOffset(new DateTime(earliestYear, 1, 1), new TimeSpan(vTimeZone._nodaZone.MaxOffset.Ticks)); + var interval = new ZoneInterval( + name: vTimeZone._nodaZone.Id, + start: Instant.FromDateTimeOffset(start), + end: Instant.FromDateTimeOffset(start) + Duration.FromHours(1), + wallOffset: vTimeZone._nodaZone.MinOffset, + savings: Offset.Zero); + intervals.Add(interval); + var zoneInfo = CreateTimeZoneInfo(intervals, new List(), true, true); + vTimeZone.AddChild(zoneInfo); + } + else + { + // first, get the latest standard and daylight intervals, find the oldest recurring date in both, set the RRULES for it, and create a VTimeZoneInfos out of them. + //standard + var standardIntervals = intervals.Where(x => x.Savings.ToTimeSpan() == new TimeSpan(0)).ToList(); + var latestStandardInterval = standardIntervals.OrderByDescending(x => x.Start).FirstOrDefault(); + matchingStandardIntervals = GetMatchingIntervals(standardIntervals, latestStandardInterval, true); + var latestStandardTimeZoneInfo = CreateTimeZoneInfo(matchingStandardIntervals, intervals); + vTimeZone.AddChild(latestStandardTimeZoneInfo); + + // check to see if there is no active, future daylight savings (ie, America/Phoenix) + if (latestStandardInterval != null && (latestStandardInterval.HasEnd ? latestStandardInterval.End : Instant.MaxValue) != Instant.MaxValue) { - var interval = historicIntervals.FirstOrDefault(x => x.Start != Instant.MinValue); + //daylight + var daylightIntervals = intervals.Where(x => x.Savings.ToTimeSpan() != new TimeSpan(0)).ToList(); - if (interval == null) + if (daylightIntervals.Any()) { - break; + var latestDaylightInterval = daylightIntervals.OrderByDescending(x => x.Start).FirstOrDefault(); + matchingDaylightIntervals = GetMatchingIntervals(daylightIntervals, latestDaylightInterval, true); + var latestDaylightTimeZoneInfo = CreateTimeZoneInfo(matchingDaylightIntervals, intervals); + vTimeZone.AddChild(latestDaylightTimeZoneInfo); } - - var matchedIntervals = GetMatchingIntervals(historicIntervals, interval); - var timeZoneInfo = CreateTimeZoneInfo(matchedIntervals, intervals, false); - vTimeZone.AddChild(timeZoneInfo); - historicIntervals = historicIntervals.Where(x => !matchedIntervals.Contains(x)).ToList(); } + } + if (!includeHistoricalData || intervals.Count == 1) + { return vTimeZone; } - private static VTimeZoneInfo CreateTimeZoneInfo(List matchedIntervals, List intervals, bool isRRule = true, - bool isOnlyInterval = false) + // then, do the historic intervals, using RDATE for them + var historicIntervals = intervals.Where(x => !matchingDaylightIntervals.Contains(x) && !matchingStandardIntervals.Contains(x)).ToList(); + + while (historicIntervals.Any(x => x.Start != Instant.MinValue)) { - if (matchedIntervals == null || !matchedIntervals.Any()) - { - throw new ArgumentException("No intervals found in matchedIntervals"); - } + var interval = historicIntervals.FirstOrDefault(x => x.Start != Instant.MinValue); - var oldestInterval = matchedIntervals.OrderBy(x => x.Start).FirstOrDefault(); - if (oldestInterval == null) + if (interval == null) { - throw new InvalidOperationException("oldestInterval was not found"); + break; } - var previousInterval = intervals.SingleOrDefault(x => (x.HasEnd ? x.End : Instant.MaxValue) == oldestInterval.Start); + var matchedIntervals = GetMatchingIntervals(historicIntervals, interval); + var timeZoneInfo = CreateTimeZoneInfo(matchedIntervals, intervals, false); + vTimeZone.AddChild(timeZoneInfo); + historicIntervals = historicIntervals.Where(x => !matchedIntervals.Contains(x)).ToList(); + } - var delta = new TimeSpan(1, 0, 0); + return vTimeZone; + } - if (previousInterval != null) - { - delta = new TimeSpan(0, 0, previousInterval.WallOffset.Seconds - oldestInterval.WallOffset.Seconds); - } - else if (isOnlyInterval) - { - delta = new TimeSpan(); - } + private static VTimeZoneInfo CreateTimeZoneInfo(List matchedIntervals, List intervals, bool isRRule = true, + bool isOnlyInterval = false) + { + if (matchedIntervals == null || !matchedIntervals.Any()) + { + throw new ArgumentException("No intervals found in matchedIntervals"); + } - var utcOffset = oldestInterval.StandardOffset.ToTimeSpan(); + var oldestInterval = matchedIntervals.OrderBy(x => x.Start).FirstOrDefault(); + if (oldestInterval == null) + { + throw new InvalidOperationException("oldestInterval was not found"); + } - var timeZoneInfo = new VTimeZoneInfo(); + var previousInterval = intervals.SingleOrDefault(x => (x.HasEnd ? x.End : Instant.MaxValue) == oldestInterval.Start); - var isDaylight = oldestInterval.Savings.Ticks > 0; + var delta = new TimeSpan(1, 0, 0); - if (isDaylight) - { - timeZoneInfo.Name = Components.Daylight; - timeZoneInfo.OffsetFrom = new UtcOffset(utcOffset); - timeZoneInfo.OffsetTo = new UtcOffset(utcOffset - delta); - } - else - { - timeZoneInfo.Name = Components.Standard; - timeZoneInfo.OffsetFrom = new UtcOffset(utcOffset + delta); - timeZoneInfo.OffsetTo = new UtcOffset(utcOffset); - } + if (previousInterval != null) + { + delta = new TimeSpan(0, 0, previousInterval.WallOffset.Seconds - oldestInterval.WallOffset.Seconds); + } + else if (isOnlyInterval) + { + delta = new TimeSpan(); + } - timeZoneInfo.TimeZoneName = oldestInterval.Name; + var utcOffset = oldestInterval.StandardOffset.ToTimeSpan(); - var start = oldestInterval.IsoLocalStart.ToDateTimeUnspecified() + delta; - timeZoneInfo.Start = new CalDateTime(start) { HasTime = true }; + var timeZoneInfo = new VTimeZoneInfo(); - if (isRRule) - { - PopulateTimeZoneInfoRecurrenceRules(timeZoneInfo, oldestInterval); - } - else - { - PopulateTimeZoneInfoRecurrenceDates(timeZoneInfo, matchedIntervals, delta); - } + var isDaylight = oldestInterval.Savings.Ticks > 0; - return timeZoneInfo; + if (isDaylight) + { + timeZoneInfo.Name = Components.Daylight; + timeZoneInfo.OffsetFrom = new UtcOffset(utcOffset); + timeZoneInfo.OffsetTo = new UtcOffset(utcOffset - delta); } + else + { + timeZoneInfo.Name = Components.Standard; + timeZoneInfo.OffsetFrom = new UtcOffset(utcOffset + delta); + timeZoneInfo.OffsetTo = new UtcOffset(utcOffset); + } + + timeZoneInfo.TimeZoneName = oldestInterval.Name; + + var start = oldestInterval.IsoLocalStart.ToDateTimeUnspecified() + delta; + timeZoneInfo.Start = new CalDateTime(start) { HasTime = true }; - private static List GetMatchingIntervals(List intervals, ZoneInterval intervalToMatch, bool consecutiveOnly = false) + if (isRRule) { - var matchedIntervals = intervals - .Where(x => x.Start != Instant.MinValue) - .Where(x => x.IsoLocalStart.Month == intervalToMatch.IsoLocalStart.Month + PopulateTimeZoneInfoRecurrenceRules(timeZoneInfo, oldestInterval); + } + else + { + PopulateTimeZoneInfoRecurrenceDates(timeZoneInfo, matchedIntervals, delta); + } + + return timeZoneInfo; + } + + private static List GetMatchingIntervals(List intervals, ZoneInterval intervalToMatch, bool consecutiveOnly = false) + { + var matchedIntervals = intervals + .Where(x => x.Start != Instant.MinValue) + .Where(x => x.IsoLocalStart.Month == intervalToMatch.IsoLocalStart.Month && x.IsoLocalStart.Hour == intervalToMatch.IsoLocalStart.Hour && x.IsoLocalStart.Minute == intervalToMatch.IsoLocalStart.Minute && x.IsoLocalStart.ToDateTimeUnspecified().DayOfWeek == intervalToMatch.IsoLocalStart.ToDateTimeUnspecified().DayOfWeek && x.WallOffset == intervalToMatch.WallOffset && x.Name == intervalToMatch.Name) - .ToList(); + .ToList(); - if (!consecutiveOnly) - { - return matchedIntervals; - } + if (!consecutiveOnly) + { + return matchedIntervals; + } - var consecutiveIntervals = new List(); + var consecutiveIntervals = new List(); - var currentYear = 0; + var currentYear = 0; - // return only the intervals where there are no gaps in years - foreach (var interval in matchedIntervals.OrderByDescending(x => x.IsoLocalStart.Year)) + // return only the intervals where there are no gaps in years + foreach (var interval in matchedIntervals.OrderByDescending(x => x.IsoLocalStart.Year)) + { + if (currentYear == 0) { - if (currentYear == 0) - { - currentYear = interval.IsoLocalStart.Year; - } - - if (currentYear != interval.IsoLocalStart.Year) - { - break; - } + currentYear = interval.IsoLocalStart.Year; + } - consecutiveIntervals.Add(interval); - currentYear--; + if (currentYear != interval.IsoLocalStart.Year) + { + break; } - return consecutiveIntervals; + consecutiveIntervals.Add(interval); + currentYear--; } - private static void PopulateTimeZoneInfoRecurrenceDates(VTimeZoneInfo tzi, List intervals, TimeSpan delta) + return consecutiveIntervals; + } + + private static void PopulateTimeZoneInfoRecurrenceDates(VTimeZoneInfo tzi, List intervals, TimeSpan delta) + { + foreach (var interval in intervals) { - foreach (var interval in intervals) + var periodList = new PeriodList(); + var time = interval.IsoLocalStart.ToDateTimeUnspecified(); + var date = new CalDateTime(time).Add(delta) as CalDateTime; + if (date == null) { - var periodList = new PeriodList(); - var time = interval.IsoLocalStart.ToDateTimeUnspecified(); - var date = new CalDateTime(time).Add(delta) as CalDateTime; - if (date == null) - { - continue; - } - - date.HasTime = true; - periodList.Add(date); - tzi.RecurrenceDates.Add(periodList); + continue; } - } - private static void PopulateTimeZoneInfoRecurrenceRules(VTimeZoneInfo tzi, ZoneInterval interval) - { - var recurrence = new IntervalRecurrencePattern(interval); - tzi.RecurrenceRules.Add(recurrence); + date.HasTime = true; + periodList.Add(date); + tzi.RecurrenceDates.Add(periodList); } + } - private class IntervalRecurrencePattern : RecurrencePattern + private static void PopulateTimeZoneInfoRecurrenceRules(VTimeZoneInfo tzi, ZoneInterval interval) + { + var recurrence = new IntervalRecurrencePattern(interval); + tzi.RecurrenceRules.Add(recurrence); + } + + private class IntervalRecurrencePattern : RecurrencePattern + { + public IntervalRecurrencePattern(ZoneInterval interval) { - public IntervalRecurrencePattern(ZoneInterval interval) - { - Frequency = FrequencyType.Yearly; - ByMonth.Add(interval.IsoLocalStart.Month); + Frequency = FrequencyType.Yearly; + ByMonth.Add(interval.IsoLocalStart.Month); - var date = interval.IsoLocalStart.ToDateTimeUnspecified(); - var weekday = date.DayOfWeek; - var weekNumber = DateUtil.WeekOfMonth(date); + var date = interval.IsoLocalStart.ToDateTimeUnspecified(); + var weekday = date.DayOfWeek; + var weekNumber = DateUtil.WeekOfMonth(date); - if (weekNumber >= 4) - ByDay.Add(new WeekDay(weekday, -1)); // Almost certainly likely last X-day of month. Avoid issues with 4/5 sundays in different year/months. Ideally, use the nodazone tz database rule for this interval instead. - else - ByDay.Add(new WeekDay(weekday, weekNumber)); - } + if (weekNumber >= 4) + ByDay.Add(new WeekDay(weekday, -1)); // Almost certainly likely last X-day of month. Avoid issues with 4/5 sundays in different year/months. Ideally, use the nodazone tz database rule for this interval instead. + else + ByDay.Add(new WeekDay(weekday, weekNumber)); } + } - public VTimeZone() + public VTimeZone() + { + Name = Components.Timezone; + } + + + public VTimeZone(string tzId) : this() + { + if (string.IsNullOrWhiteSpace(tzId)) { - Name = Components.Timezone; + return; } + TzId = tzId; + Location = _nodaZone.Id; + } - public VTimeZone(string tzId) : this() + private DateTimeZone _nodaZone; + private string _tzId; + public virtual string TzId + { + get { - if (string.IsNullOrWhiteSpace(tzId)) + if (string.IsNullOrWhiteSpace(_tzId)) { - return; + _tzId = Properties.Get("TZID"); } - - TzId = tzId; - Location = _nodaZone.Id; + return _tzId; } - - private DateTimeZone _nodaZone; - private string _tzId; - public virtual string TzId + set { - get + if (string.Equals(_tzId, value, StringComparison.OrdinalIgnoreCase)) { - if (string.IsNullOrWhiteSpace(_tzId)) - { - _tzId = Properties.Get("TZID"); - } - return _tzId; + return; } - set - { - if (string.Equals(_tzId, value, StringComparison.OrdinalIgnoreCase)) - { - return; - } - - if (string.IsNullOrWhiteSpace(value)) - { - _tzId = null; - Properties.Remove("TZID"); - } - _nodaZone = DateUtil.GetZone(value, useLocalIfNotFound: false); - var id = _nodaZone.Id; - if (string.IsNullOrWhiteSpace(id)) - { - throw new ArgumentException($"Unrecognized time zone id: {value}"); - } + if (string.IsNullOrWhiteSpace(value)) + { + _tzId = null; + Properties.Remove("TZID"); + } - if (!string.Equals(id, value, StringComparison.OrdinalIgnoreCase)) - { - //It was a BCL time zone, so we should use the original value - id = value; - } + _nodaZone = DateUtil.GetZone(value, useLocalIfNotFound: false); + var id = _nodaZone.Id; + if (string.IsNullOrWhiteSpace(id)) + { + throw new ArgumentException($"Unrecognized time zone id: {value}"); + } - _tzId = id; - Properties.Set("TZID", value); + if (!string.Equals(id, value, StringComparison.OrdinalIgnoreCase)) + { + //It was a BCL time zone, so we should use the original value + id = value; } + + _tzId = id; + Properties.Set("TZID", value); } + } - private Uri _url; - public virtual Uri Url + private Uri _url; + public virtual Uri Url + { + get => _url ?? (_url = Properties.Get("TZURL")); + set { - get => _url ?? (_url = Properties.Get("TZURL")); - set - { - _url = value; - Properties.Set("TZURL", _url); - } + _url = value; + Properties.Set("TZURL", _url); } + } - private string _location; - public string Location + private string _location; + public string Location + { + get => _location ?? (_location = Properties.Get("X-LIC-LOCATION")); + set { - get => _location ?? (_location = Properties.Get("X-LIC-LOCATION")); - set - { - _location = value; - Properties.Set("X-LIC-LOCATION", _location); - } + _location = value; + Properties.Set("X-LIC-LOCATION", _location); } + } - public ICalendarObjectList TimeZoneInfos => new CalendarObjectListProxy(Children); + public ICalendarObjectList TimeZoneInfos => new CalendarObjectListProxy(Children); - protected bool Equals(VTimeZone other) - => string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase) - && string.Equals(TzId, other.TzId, StringComparison.OrdinalIgnoreCase) - && Equals(Url, other.Url); + protected bool Equals(VTimeZone other) + => 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 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() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = Name.GetHashCode(); - hashCode = (hashCode * 397) ^ (TzId?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (Url?.GetHashCode() ?? 0); - return hashCode; - } + 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/Ical.Net/CalendarExtensions.cs b/Ical.Net/CalendarExtensions.cs index 764f9f0f8..0f80956f1 100644 --- a/Ical.Net/CalendarExtensions.cs +++ b/Ical.Net/CalendarExtensions.cs @@ -6,26 +6,25 @@ using System; using System.Globalization; -namespace Ical.Net +namespace Ical.Net; + +public static class CalendarExtensions { - public static class CalendarExtensions + /// + /// https://blogs.msdn.microsoft.com/shawnste/2006/01/24/iso-8601-week-of-year-format-in-microsoft-net/ + /// + public static int GetIso8601WeekOfYear(this System.Globalization.Calendar calendar, DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) { - /// - /// https://blogs.msdn.microsoft.com/shawnste/2006/01/24/iso-8601-week-of-year-format-in-microsoft-net/ - /// - public static int GetIso8601WeekOfYear(this System.Globalization.Calendar calendar, DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek) + // Seriously cheat. If its Monday, Tuesday or Wednesday, then it'll + // be the same week# as whatever Thursday, Friday or Saturday are, + // and we always get those right + var day = calendar.GetDayOfWeek(time); + if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday) { - // Seriously cheat. If its Monday, Tuesday or Wednesday, then it'll - // be the same week# as whatever Thursday, Friday or Saturday are, - // and we always get those right - var day = calendar.GetDayOfWeek(time); - if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday) - { - time = time.AddDays(3); - } - - // Return the week of our adjusted day - return calendar.GetWeekOfYear(time, rule, firstDayOfWeek); + time = time.AddDays(3); } + + // Return the week of our adjusted day + return calendar.GetWeekOfYear(time, rule, firstDayOfWeek); } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarObject.cs b/Ical.Net/CalendarObject.cs index 35bc02779..0dcfd8884 100644 --- a/Ical.Net/CalendarObject.cs +++ b/Ical.Net/CalendarObject.cs @@ -7,143 +7,142 @@ using System.Runtime.Serialization; using Ical.Net.Collections; -namespace Ical.Net +namespace Ical.Net; + +/// +/// The base class for all iCalendar objects and components. +/// +public class CalendarObject : CalendarObjectBase, ICalendarObject { - /// - /// The base class for all iCalendar objects and components. - /// - public class CalendarObject : CalendarObjectBase, ICalendarObject + private ICalendarObjectList _children; + private ServiceProvider _serviceProvider; + + internal CalendarObject() { - private ICalendarObjectList _children; - private ServiceProvider _serviceProvider; + Initialize(); + } - internal CalendarObject() - { - Initialize(); - } + public CalendarObject(string name) : this() + { + Name = name; + } - public CalendarObject(string name) : this() - { - Name = name; - } + public CalendarObject(int line, int col) : this() + { + Line = line; + Column = col; + } - public CalendarObject(int line, int col) : this() - { - Line = line; - Column = col; - } + private void Initialize() + { + _children = new CalendarObjectList(); + _serviceProvider = new ServiceProvider(); + _children.ItemAdded += Children_ItemAdded; + } - private void Initialize() - { - _children = new CalendarObjectList(); - _serviceProvider = new ServiceProvider(); - _children.ItemAdded += Children_ItemAdded; - } + [OnDeserializing] + internal void DeserializingInternal(StreamingContext context) => OnDeserializing(context); - [OnDeserializing] - internal void DeserializingInternal(StreamingContext context) => OnDeserializing(context); + [OnDeserialized] + internal void DeserializedInternal(StreamingContext context) => OnDeserialized(context); - [OnDeserialized] - internal void DeserializedInternal(StreamingContext context) => OnDeserialized(context); + protected virtual void OnDeserializing(StreamingContext context) => Initialize(); - protected virtual void OnDeserializing(StreamingContext context) => Initialize(); + protected virtual void OnDeserialized(StreamingContext context) { } - protected virtual void OnDeserialized(StreamingContext context) { } + private void Children_ItemAdded(object sender, ObjectEventArgs e) => e.First.Parent = this; - private void Children_ItemAdded(object sender, ObjectEventArgs e) => e.First.Parent = this; + protected bool Equals(CalendarObject other) => string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase); - 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; + return obj.GetType() == GetType() && Equals((CalendarObject) obj); + } - public override bool Equals(object obj) + public override int GetHashCode() => Name?.GetHashCode() ?? 0; + + /// + public override void CopyFrom(ICopyable c) + { + if (c is not ICalendarObject obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj.GetType() == GetType() && Equals((CalendarObject) obj); + return; } - public override int GetHashCode() => Name?.GetHashCode() ?? 0; + // Copy the name and basic information + Name = obj.Name; + Parent = obj.Parent; + Line = obj.Line; + Column = obj.Column; - /// - public override void CopyFrom(ICopyable c) + // Add each child + Children.Clear(); + foreach (var child in obj.Children) { - if (c is not ICalendarObject obj) - { - return; - } + // Add a deep copy of the child instead of the child itself + this.AddChild(child.Copy()); + } + } - // Copy the name and basic information - Name = obj.Name; - Parent = obj.Parent; - Line = obj.Line; - Column = obj.Column; + /// + /// Returns the parent iCalObject that owns this one. + /// + public virtual ICalendarObject Parent { get; set; } - // Add each child - Children.Clear(); - foreach (var child in obj.Children) - { - // Add a deep copy of the child instead of the child itself - this.AddChild(child.Copy()); - } - } + /// + /// A collection of iCalObjects that are children of the current object. + /// + public virtual ICalendarObjectList Children => _children; - /// - /// 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. - /// - public virtual string Name { get; set; } - - /// - /// Gets the object. - /// The setter must be implemented in a derived class. - /// - public virtual Calendar Calendar + /// + /// Gets or sets the name of the iCalObject. For iCalendar components, this is the RFC 5545 name of the component. + /// + public virtual string Name { get; set; } + + /// + /// Gets the object. + /// The setter must be implemented in a derived class. + /// + public virtual Calendar Calendar + { + get { - get + ICalendarObject obj = this; + while (obj is not Net.Calendar && obj.Parent != null) { - ICalendarObject obj = this; - while (obj is not Net.Calendar && obj.Parent != null) - { - obj = obj.Parent; - } - - return obj as Calendar; + obj = obj.Parent; } - protected set => throw new NotSupportedException(); + + return obj as Calendar; } + protected set => throw new NotSupportedException(); + } - public virtual int Line { get; set; } + public virtual int Line { get; set; } - public virtual int Column { get; set; } + public virtual int Column { get; set; } - public virtual object GetService(Type serviceType) => _serviceProvider.GetService(serviceType); + public virtual object GetService(Type serviceType) => _serviceProvider.GetService(serviceType); - public virtual object GetService(string name) => _serviceProvider.GetService(name); + public virtual object GetService(string name) => _serviceProvider.GetService(name); - public virtual T GetService() => _serviceProvider.GetService(); + public virtual T GetService() => _serviceProvider.GetService(); - public virtual T GetService(string name) => _serviceProvider.GetService(name); + public virtual T GetService(string name) => _serviceProvider.GetService(name); - public virtual void SetService(string name, object obj) => _serviceProvider.SetService(name, obj); + public virtual void SetService(string name, object obj) => _serviceProvider.SetService(name, obj); - public virtual void SetService(object obj) => _serviceProvider.SetService(obj); + public virtual void SetService(object obj) => _serviceProvider.SetService(obj); - public virtual void RemoveService(Type type) => _serviceProvider.RemoveService(type); + public virtual void RemoveService(Type type) => _serviceProvider.RemoveService(type); - public virtual void RemoveService(string name) => _serviceProvider.RemoveService(name); + public virtual void RemoveService(string name) => _serviceProvider.RemoveService(name); - public virtual string Group - { - get => Name; - set => Name = value; - } + public virtual string Group + { + get => Name; + set => Name = value; } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarObjectBase.cs b/Ical.Net/CalendarObjectBase.cs index 6a94c88fd..8d3cbcda7 100644 --- a/Ical.Net/CalendarObjectBase.cs +++ b/Ical.Net/CalendarObjectBase.cs @@ -5,45 +5,44 @@ using System; -namespace Ical.Net +namespace Ical.Net; + +// This class should be declared as abstract +public class CalendarObjectBase : ICopyable, ILoadable { - // This class should be declared as abstract - public class CalendarObjectBase : ICopyable, ILoadable + private bool _mIsLoaded = true; + + /// + /// Makes a deep copy of the source + /// to the current object. This method must be overridden in a derived class. + /// + public virtual void CopyFrom(ICopyable obj) + { + throw new NotImplementedException("Must be implemented in a derived class."); + } + + /// + /// Creates a deep copy of the object. + /// + /// The copy of the object. + public virtual T Copy() + { + var type = GetType(); + var obj = Activator.CreateInstance(type) as ICopyable; + + if (obj is not T objOfT) return default(T); + + obj.CopyFrom(this); + return objOfT; + } + + public virtual bool IsLoaded => _mIsLoaded; + + public event EventHandler Loaded; + + public virtual void OnLoaded() { - private bool _mIsLoaded = true; - - /// - /// Makes a deep copy of the source - /// to the current object. This method must be overridden in a derived class. - /// - public virtual void CopyFrom(ICopyable obj) - { - throw new NotImplementedException("Must be implemented in a derived class."); - } - - /// - /// Creates a deep copy of the object. - /// - /// The copy of the object. - public virtual T Copy() - { - var type = GetType(); - var obj = Activator.CreateInstance(type) as ICopyable; - - if (obj is not T objOfT) return default(T); - - obj.CopyFrom(this); - return objOfT; - } - - public virtual bool IsLoaded => _mIsLoaded; - - public event EventHandler Loaded; - - public virtual void OnLoaded() - { - _mIsLoaded = true; - Loaded?.Invoke(this, EventArgs.Empty); - } + _mIsLoaded = true; + Loaded?.Invoke(this, EventArgs.Empty); } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarObjectExtensions.cs b/Ical.Net/CalendarObjectExtensions.cs index 5b50031cf..2c7d50cad 100644 --- a/Ical.Net/CalendarObjectExtensions.cs +++ b/Ical.Net/CalendarObjectExtensions.cs @@ -3,18 +3,17 @@ // Licensed under the MIT license. // -namespace Ical.Net +namespace Ical.Net; + +public static class CalendarObjectExtensions { - public static class CalendarObjectExtensions + public static void AddChild(this ICalendarObject obj, TItem child) where TItem : ICalendarObject { - public static void AddChild(this ICalendarObject obj, TItem child) where TItem : ICalendarObject - { - obj.Children.Add(child); - } + obj.Children.Add(child); + } - public static void RemoveChild(this ICalendarObject obj, TItem child) where TItem : ICalendarObject - { - obj.Children.Remove(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/Ical.Net/CalendarObjectList.cs b/Ical.Net/CalendarObjectList.cs index 962f5f8e9..165cc9b89 100644 --- a/Ical.Net/CalendarObjectList.cs +++ b/Ical.Net/CalendarObjectList.cs @@ -6,13 +6,12 @@ using System.Diagnostics.CodeAnalysis; using Ical.Net.Collections; -namespace Ical.Net +namespace Ical.Net; + +/// +/// A collection of calendar objects. +/// +[ExcludeFromCodeCoverage] +public class CalendarObjectList : GroupedList, ICalendarObjectList { - /// - /// A collection of calendar objects. - /// - [ExcludeFromCodeCoverage] - public class CalendarObjectList : GroupedList, ICalendarObjectList - { - } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarParameter.cs b/Ical.Net/CalendarParameter.cs index 4c409a6af..db4e87a21 100644 --- a/Ical.Net/CalendarParameter.cs +++ b/Ical.Net/CalendarParameter.cs @@ -10,103 +10,102 @@ using System.Runtime.Serialization; using Ical.Net.Collections.Interfaces; -namespace Ical.Net +namespace Ical.Net; + +[DebuggerDisplay("{Name}={string.Join(\",\", Values)}")] +public class CalendarParameter : CalendarObject, IValueObject { - [DebuggerDisplay("{Name}={string.Join(\",\", Values)}")] - public class CalendarParameter : CalendarObject, IValueObject + private HashSet _values; + + public CalendarParameter() { - private HashSet _values; + Initialize(); + } - public CalendarParameter() - { - Initialize(); - } + public CalendarParameter(string name) : base(name) + { + Initialize(); + } - public CalendarParameter(string name) : base(name) - { - Initialize(); - } + public CalendarParameter(string name, string value) : base(name) + { + Initialize(); + AddValue(value); + } - public CalendarParameter(string name, string value) : base(name) + public CalendarParameter(string name, IEnumerable values) : base(name) + { + Initialize(); + foreach (var v in values) { - Initialize(); - AddValue(value); + AddValue(v); } + } - public CalendarParameter(string name, IEnumerable values) : base(name) - { - Initialize(); - foreach (var v in values) - { - AddValue(v); - } - } + private void Initialize() + { + _values = new HashSet(StringComparer.OrdinalIgnoreCase); + } - private void Initialize() - { - _values = new HashSet(StringComparer.OrdinalIgnoreCase); - } + protected override void OnDeserializing(StreamingContext context) + { + base.OnDeserializing(context); - protected override void OnDeserializing(StreamingContext context) - { - base.OnDeserializing(context); + Initialize(); + } - Initialize(); - } + /// + public override void CopyFrom(ICopyable c) + { + base.CopyFrom(c); - /// - public override void CopyFrom(ICopyable c) + var p = c as CalendarParameter; + if (p?.Values == null) { - base.CopyFrom(c); - - var p = c as CalendarParameter; - if (p?.Values == null) - { - return; - } - - _values = new HashSet(p.Values.Where(IsValidValue), StringComparer.OrdinalIgnoreCase); + return; } - public virtual IEnumerable Values => _values; + _values = new HashSet(p.Values.Where(IsValidValue), StringComparer.OrdinalIgnoreCase); + } - public virtual bool ContainsValue(string value) => _values.Contains(value); + public virtual IEnumerable Values => _values; - public virtual int ValueCount => _values?.Count ?? 0; + public virtual bool ContainsValue(string value) => _values.Contains(value); - public virtual void SetValue(string value) - { - _values.Clear(); - _values.Add(value); - } + public virtual int ValueCount => _values?.Count ?? 0; - public virtual void SetValue(IEnumerable values) - { - // Remove all previous values - _values.Clear(); - _values.UnionWith(values.Where(IsValidValue)); - } + public virtual void SetValue(string value) + { + _values.Clear(); + _values.Add(value); + } + + public virtual void SetValue(IEnumerable values) + { + // Remove all previous values + _values.Clear(); + _values.UnionWith(values.Where(IsValidValue)); + } - private bool IsValidValue(string value) => !string.IsNullOrWhiteSpace(value); + private bool IsValidValue(string value) => !string.IsNullOrWhiteSpace(value); - public virtual void AddValue(string value) + public virtual void AddValue(string value) + { + if (!IsValidValue(value)) { - if (!IsValidValue(value)) - { - return; - } - _values.Add(value); + return; } + _values.Add(value); + } - public virtual void RemoveValue(string value) - { - _values.Remove(value); - } + public virtual void RemoveValue(string value) + { + _values.Remove(value); + } - public virtual string Value - { - get => Values?.FirstOrDefault(); - set => SetValue(value); - } + public virtual string Value + { + get => Values?.FirstOrDefault(); + set => SetValue(value); } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarProperty.cs b/Ical.Net/CalendarProperty.cs index 40cf407ef..b389b3838 100644 --- a/Ical.Net/CalendarProperty.cs +++ b/Ical.Net/CalendarProperty.cs @@ -7,151 +7,150 @@ using System.Diagnostics; using System.Linq; -namespace Ical.Net +namespace Ical.Net; + +/// +/// 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(); + /// - /// 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 + /// Returns a list of parameters that are associated with the iCalendar object. /// - /// - /// 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(); - - /// - /// 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 virtual IParameterCollection Parameters { get; protected set; } = new ParameterList(); - public CalendarProperty(int line, int col) : base(line, col) { } + public CalendarProperty() { } - /// - /// Adds a parameter to the iCalendar object. - /// - public virtual void AddParameter(string name, string value) - { - var p = new CalendarParameter(name, value); - Parameters.Add(p); - } + public CalendarProperty(string name) : base(name) { } - /// - /// Adds a parameter to the iCalendar object. - /// - public virtual void AddParameter(CalendarParameter p) - { - Parameters.Add(p); - } + public CalendarProperty(string name, object value) : base(name) + { + _values.Add(value); + } - /// - public override void CopyFrom(ICopyable obj) - { - base.CopyFrom(obj); + public CalendarProperty(int line, int col) : base(line, col) { } - if (obj is not ICalendarProperty p) - { - return; - } + /// + /// Adds a parameter to the iCalendar object. + /// + public virtual void AddParameter(string name, string value) + { + var p = new CalendarParameter(name, value); + Parameters.Add(p); + } - SetValue(p.Values); - } + /// + /// Adds a parameter to the iCalendar object. + /// + public virtual void AddParameter(CalendarParameter p) + { + Parameters.Add(p); + } - public virtual IEnumerable Values => _values; + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); - public object Value + if (obj is not ICalendarProperty p) { - get => _values?.FirstOrDefault(); - set - { - if (value == null) - { - _values = null; - return; - } - - if (_values != null && _values.Count > 0) - { - _values[0] = value; - } - else - { - _values?.Clear(); - _values?.Add(value); - } - } + return; } - public virtual bool ContainsValue(object value) => _values.Contains(value); + SetValue(p.Values); + } - public virtual int ValueCount => _values?.Count ?? 0; + public virtual IEnumerable Values => _values; - public virtual void SetValue(object value) + public object Value + { + get => _values?.FirstOrDefault(); + set { - if (_values.Count == 0) + if (value == null) { - _values.Add(value); + _values = null; + return; } - else if (value != null) + + if (_values != null && _values.Count > 0) { - // Our list contains values. Let's set the first value! _values[0] = value; } else { - _values.Clear(); + _values?.Clear(); + _values?.Add(value); } } + } + + public virtual bool ContainsValue(object value) => _values.Contains(value); - public virtual void SetValue(IEnumerable values) + 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 { - // Remove all previous values _values.Clear(); - // If the values are ICopyable, create a deep copy of each value, - // otherwise just add the value - var toAdd = values?.Select(x => (x as ICopyable)?.Copy() ?? x) ?? Enumerable.Empty(); - _values.AddRange(toAdd); } + } - public virtual void AddValue(object value) - { - if (value == null) - { - return; - } + public virtual void SetValue(IEnumerable values) + { + // Remove all previous values + _values.Clear(); + // If the values are ICopyable, create a deep copy of each value, + // otherwise just add the value + var toAdd = values?.Select(x => (x as ICopyable)?.Copy() ?? x) ?? Enumerable.Empty(); + _values.AddRange(toAdd); + } - _values.Add(value); + public virtual void AddValue(object value) + { + if (value == null) + { + return; } - public virtual void RemoveValue(object value) + _values.Add(value); + } + + public virtual void RemoveValue(object value) + { + if (value == null) { - if (value == null) - { - return; - } - _values.Remove(value); + return; } + _values.Remove(value); } -} +} \ No newline at end of file diff --git a/Ical.Net/CalendarPropertyList.cs b/Ical.Net/CalendarPropertyList.cs index 896c1abfb..c93cbb846 100644 --- a/Ical.Net/CalendarPropertyList.cs +++ b/Ical.Net/CalendarPropertyList.cs @@ -6,27 +6,26 @@ using System.Linq; using Ical.Net.Collections; -namespace Ical.Net -{ - public class CalendarPropertyList : GroupedValueList - { - private readonly ICalendarObject _mParent; +namespace Ical.Net; - public CalendarPropertyList() { } +public class CalendarPropertyList : GroupedValueList +{ + private readonly ICalendarObject _mParent; - public CalendarPropertyList(ICalendarObject parent) - { - _mParent = parent; - ItemAdded += CalendarPropertyList_ItemAdded; - } + public CalendarPropertyList() { } - private void CalendarPropertyList_ItemAdded(object sender, ObjectEventArgs e) - { - e.First.Parent = _mParent; - } + public CalendarPropertyList(ICalendarObject parent) + { + _mParent = parent; + ItemAdded += CalendarPropertyList_ItemAdded; + } - public ICalendarProperty this[string name] => ContainsKey(name) - ? AllOf(name).FirstOrDefault() - : null; + private void CalendarPropertyList_ItemAdded(object sender, ObjectEventArgs e) + { + e.First.Parent = _mParent; } -} + + public ICalendarProperty this[string name] => ContainsKey(name) + ? AllOf(name).FirstOrDefault() + : null; +} \ No newline at end of file diff --git a/Ical.Net/Collections/GroupedList.cs b/Ical.Net/Collections/GroupedList.cs index 3e8360ba3..13f00fc28 100644 --- a/Ical.Net/Collections/GroupedList.cs +++ b/Ical.Net/Collections/GroupedList.cs @@ -8,184 +8,204 @@ using System.Collections.Generic; using System.Linq; -namespace Ical.Net.Collections +namespace Ical.Net.Collections; + +/// +/// A list of objects that are keyed. +/// +public class GroupedList : + IGroupedList + where TItem : class, IGroupedObject { - /// - /// A list of objects that are keyed. - /// - public class GroupedList : - IGroupedList - where TItem : class, IGroupedObject - { - private readonly List> _lists = new List>(); - private readonly Dictionary> _dictionary = new Dictionary>(); + private readonly List> _lists = new List>(); + private readonly Dictionary> _dictionary = new Dictionary>(); - private IMultiLinkedList EnsureList(TGroup group) + private IMultiLinkedList EnsureList(TGroup group) + { + if (group == null) { - if (group == null) - { - return null; - } - - if (_dictionary.ContainsKey(group)) - { - return _dictionary[group]; - } - - var list = new MultiLinkedList(); - _dictionary[group] = list; - - _lists.Add(list); - return list; + return null; } - private IMultiLinkedList ListForIndex(int index, out int relativeIndex) + if (_dictionary.ContainsKey(group)) { - var list = _lists.FirstOrDefault(l => l.StartIndex <= index && l.ExclusiveEnd > index); - if (list != null) - { - relativeIndex = index - list.StartIndex; - return list; - } - relativeIndex = -1; - return null; + return _dictionary[group]; } - public event EventHandler> ItemAdded; + var list = new MultiLinkedList(); + _dictionary[group] = list; + + _lists.Add(list); + return list; + } - protected void OnItemAdded(TItem obj, int index) + private IMultiLinkedList ListForIndex(int index, out int relativeIndex) + { + var list = _lists.FirstOrDefault(l => l.StartIndex <= index && l.ExclusiveEnd > index); + if (list != null) { - ItemAdded?.Invoke(this, new ObjectEventArgs(obj, index)); + relativeIndex = index - list.StartIndex; + return list; } + relativeIndex = -1; + return null; + } - public virtual void Add(TItem item) - { - if (item == null) - { - return; - } + public event EventHandler> ItemAdded; - // Add a new list if necessary - var group = item.Group; - var list = EnsureList(group); - var index = list.Count; - list.Add(item); - OnItemAdded(item, list.StartIndex + index); - } + protected void OnItemAdded(TItem obj, int index) + { + ItemAdded?.Invoke(this, new ObjectEventArgs(obj, index)); + } - public virtual int IndexOf(TItem item) + public virtual void Add(TItem item) + { + if (item == null) { - var group = item.Group; - if (!_dictionary.ContainsKey(group)) - { - return -1; - } - - // Get the list associated with this object's group - var list = _dictionary[group]; + return; + } - // Find the object within the list. - var index = list.IndexOf(item); + // Add a new list if necessary + var group = item.Group; + var list = EnsureList(group); + var index = list.Count; + list.Add(item); + OnItemAdded(item, list.StartIndex + index); + } - // Return the index within the overall KeyedList - if (index >= 0) - return list.StartIndex + index; + public virtual int IndexOf(TItem item) + { + var group = item.Group; + if (!_dictionary.ContainsKey(group)) + { return -1; } - public virtual void Clear(TGroup group) - { - if (!_dictionary.ContainsKey(group)) - { - return; - } + // Get the list associated with this object's group + var list = _dictionary[group]; - // Clear the list (note that this also clears the list in the _Lists object). - _dictionary[group].Clear(); - } + // Find the object within the list. + var index = list.IndexOf(item); + + // Return the index within the overall KeyedList + if (index >= 0) + return list.StartIndex + index; + return -1; + } - public virtual void Clear() + public virtual void Clear(TGroup group) + { + if (!_dictionary.ContainsKey(group)) { - _dictionary.Clear(); - _lists.Clear(); + return; } - public virtual bool ContainsKey(TGroup group) => _dictionary.ContainsKey(@group); + // Clear the list (note that this also clears the list in the _Lists object). + _dictionary[group].Clear(); + } - public virtual int Count => _lists.Sum(list => list.Count); + public virtual void Clear() + { + _dictionary.Clear(); + _lists.Clear(); + } - public virtual int CountOf(TGroup group) => _dictionary.ContainsKey(group) - ? _dictionary[group].Count - : 0; + public virtual bool ContainsKey(TGroup group) => _dictionary.ContainsKey(@group); - public virtual IEnumerable Values() => _dictionary.Values.SelectMany(i => i); + public virtual int Count => _lists.Sum(list => list.Count); - public virtual IEnumerable AllOf(TGroup group) => _dictionary.ContainsKey(@group) - ? (IEnumerable) _dictionary[@group] - : new TItem[0]; + public virtual int CountOf(TGroup group) => _dictionary.ContainsKey(group) + ? _dictionary[group].Count + : 0; - public virtual bool Remove(TItem obj) - { - var group = obj.Group; - if (!_dictionary.ContainsKey(group)) - { - return false; - } + public virtual IEnumerable Values() => _dictionary.Values.SelectMany(i => i); - var items = _dictionary[group]; - var index = items.IndexOf(obj); + public virtual IEnumerable AllOf(TGroup group) => _dictionary.ContainsKey(@group) + ? (IEnumerable) _dictionary[@group] + : new TItem[0]; - if (index < 0) - { - return false; - } - - items.RemoveAt(index); - return true; + public virtual bool Remove(TItem obj) + { + var group = obj.Group; + if (!_dictionary.ContainsKey(group)) + { + return false; } - public virtual bool Remove(TGroup group) + var items = _dictionary[group]; + var index = items.IndexOf(obj); + + if (index < 0) { - if (!_dictionary.ContainsKey(group)) - { - return false; - } + return false; + } - var list = _dictionary[group]; - for (var i = list.Count - 1; i >= 0; i--) - { - list.RemoveAt(i); - } - return true; + items.RemoveAt(index); + return true; + } + + public virtual bool Remove(TGroup group) + { + if (!_dictionary.ContainsKey(group)) + { + return false; } - public virtual bool Contains(TItem item) + var list = _dictionary[group]; + for (var i = list.Count - 1; i >= 0; i--) { - var group = item.Group; - return _dictionary.ContainsKey(group) && _dictionary[group].Contains(item); + 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) + 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) + { + int relativeIndex; + var list = ListForIndex(index, out relativeIndex); + if (list == null) { - _dictionary.SelectMany(kvp => kvp.Value).ToArray().CopyTo(array, arrayIndex); + return; } - public virtual bool IsReadOnly => false; + list.Insert(relativeIndex, item); + OnItemAdded(item, index); + } + + public virtual void RemoveAt(int index) + { + int relativeIndex; + var list = ListForIndex(index, out relativeIndex); + if (list == null) + { + return; + } + var item = list[relativeIndex]; + list.RemoveAt(relativeIndex); + } - public virtual void Insert(int index, TItem item) + public virtual TItem this[int index] + { + get { int relativeIndex; var list = ListForIndex(index, out relativeIndex); - if (list == null) - { - return; - } - - list.Insert(relativeIndex, item); - OnItemAdded(item, index); + return list?[relativeIndex]; } - - public virtual void RemoveAt(int index) + set { int relativeIndex; var list = ListForIndex(index, out relativeIndex); @@ -193,37 +213,16 @@ public virtual void RemoveAt(int index) { 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 virtual TItem this[int index] - { - get - { - int relativeIndex; - var list = ListForIndex(index, out relativeIndex); - return list?[relativeIndex]; - } - set - { - 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() => new GroupedListEnumerator(_lists); + public IEnumerator GetEnumerator() => new GroupedListEnumerator(_lists); - IEnumerator IEnumerable.GetEnumerator() => new GroupedListEnumerator(_lists); - } -} + IEnumerator IEnumerable.GetEnumerator() => new GroupedListEnumerator(_lists); +} \ No newline at end of file diff --git a/Ical.Net/Collections/GroupedListEnumerator.cs b/Ical.Net/Collections/GroupedListEnumerator.cs index 02be18af9..e832e0ead 100644 --- a/Ical.Net/Collections/GroupedListEnumerator.cs +++ b/Ical.Net/Collections/GroupedListEnumerator.cs @@ -6,105 +6,104 @@ using System.Collections; using System.Collections.Generic; -namespace Ical.Net.Collections +namespace Ical.Net.Collections; + +public class GroupedListEnumerator : + IEnumerator { - public class GroupedListEnumerator : - IEnumerator - { - private readonly IList> _lists; - private IEnumerator> _listsEnumerator; - private IEnumerator _listEnumerator; + private readonly IList> _lists; + private IEnumerator> _listsEnumerator; + private IEnumerator _listEnumerator; + + public GroupedListEnumerator(IList> lists) => _lists = lists; - public GroupedListEnumerator(IList> lists) => _lists = lists; + public virtual TType Current + => _listEnumerator == null + ? default(TType) + : _listEnumerator.Current; - public virtual TType Current - => _listEnumerator == null - ? default(TType) - : _listEnumerator.Current; + public virtual void Dispose() + { + Reset(); + } - public virtual void Dispose() + private void DisposeListEnumerator() + { + if (_listEnumerator == null) { - Reset(); + return; } + _listEnumerator.Dispose(); + _listEnumerator = null; + } + + object IEnumerator.Current + => _listEnumerator == null + ? default(TType) + : _listEnumerator.Current; - private void DisposeListEnumerator() + private bool MoveNextList() + { + if (_listsEnumerator == null) { - if (_listEnumerator == null) - { - return; - } - _listEnumerator.Dispose(); - _listEnumerator = null; + _listsEnumerator = _lists.GetEnumerator(); } - object IEnumerator.Current - => _listEnumerator == null - ? default(TType) - : _listEnumerator.Current; + if (_listsEnumerator == null) + { + return false; + } - private bool MoveNextList() + if (!_listsEnumerator.MoveNext()) { - if (_listsEnumerator == null) - { - _listsEnumerator = _lists.GetEnumerator(); - } + return false; + } - if (_listsEnumerator == null) - { - return false; - } + DisposeListEnumerator(); + if (_listsEnumerator.Current == null) + { + return false; + } - if (!_listsEnumerator.MoveNext()) - { - return false; - } + _listEnumerator = _listsEnumerator.Current.GetEnumerator(); + return true; + } - DisposeListEnumerator(); - if (_listsEnumerator.Current == null) + public virtual bool MoveNext() + { + while (true) + { + if (_listEnumerator == null) { - return false; + if (MoveNextList()) + { + continue; + } } - - _listEnumerator = _listsEnumerator.Current.GetEnumerator(); - return true; - } - - public virtual bool MoveNext() - { - while (true) + else { - if (_listEnumerator == null) + if (_listEnumerator.MoveNext()) { - if (MoveNextList()) - { - continue; - } + return true; } - else + DisposeListEnumerator(); + if (MoveNextList()) { - if (_listEnumerator.MoveNext()) - { - return true; - } - DisposeListEnumerator(); - if (MoveNextList()) - { - continue; - } + continue; } - return false; } + return false; } + } - public virtual void Reset() + public virtual void Reset() + { + if (_listsEnumerator == null) { - if (_listsEnumerator == null) - { - return; - } - - _listsEnumerator.Dispose(); - _listsEnumerator = null; + return; } + + _listsEnumerator.Dispose(); + _listsEnumerator = null; } -} +} \ No newline at end of file diff --git a/Ical.Net/Collections/GroupedValueList.cs b/Ical.Net/Collections/GroupedValueList.cs index 38c8f8846..e50caa63f 100644 --- a/Ical.Net/Collections/GroupedValueList.cs +++ b/Ical.Net/Collections/GroupedValueList.cs @@ -9,46 +9,45 @@ using Ical.Net.Collections.Interfaces; using Ical.Net.Collections.Proxies; -namespace Ical.Net.Collections +namespace Ical.Net.Collections; + +public class GroupedValueList : + GroupedList + where TInterface : class, IGroupedObject, IValueObject + where TItem : new() { - public class GroupedValueList : - GroupedList - where TInterface : class, IGroupedObject, IValueObject - where TItem : new() + public virtual void Set(TGroup group, TValueType value) { - public virtual void Set(TGroup group, TValueType value) - { - Set(group, new[] { value }); - } + Set(group, new[] { value }); + } - public virtual void Set(TGroup group, IEnumerable values) + public virtual void Set(TGroup group, IEnumerable values) + { + if (ContainsKey(group)) { - if (ContainsKey(group)) - { - AllOf(group)?.FirstOrDefault()?.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; - obj.SetValue(values); - Add(obj); + AllOf(group)?.FirstOrDefault()?.SetValue(values); + return; } - public virtual TType Get(TGroup group) + // No matching item was found, add a new item to the list + var obj = Activator.CreateInstance(typeof(TItem)) as TInterface; + obj.Group = group; + obj.SetValue(values); + Add(obj); + } + + public virtual TType Get(TGroup group) + { + var firstItem = AllOf(group).FirstOrDefault(); + if (firstItem?.Values != null) { - var firstItem = AllOf(group).FirstOrDefault(); - if (firstItem?.Values != null) - { - return firstItem - .Values - .OfType() - .FirstOrDefault(); - } - return default(TType); + return firstItem + .Values + .OfType() + .FirstOrDefault(); } - - public virtual IList GetMany(TGroup group) => new GroupedValueListProxy(this, group); + return default(TType); } -} + + public virtual IList GetMany(TGroup group) => new GroupedValueListProxy(this, group); +} \ No newline at end of file diff --git a/Ical.Net/Collections/IGroupedCollection.cs b/Ical.Net/Collections/IGroupedCollection.cs index 77619b9f5..d41a17fbc 100644 --- a/Ical.Net/Collections/IGroupedCollection.cs +++ b/Ical.Net/Collections/IGroupedCollection.cs @@ -6,44 +6,43 @@ using System; using System.Collections.Generic; -namespace Ical.Net.Collections +namespace Ical.Net.Collections; + +public interface IGroupedCollection : + ICollection + where TItem : class, IGroupedObject { - 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); - } -} + /// + /// 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); +} \ No newline at end of file diff --git a/Ical.Net/Collections/IGroupedList.cs b/Ical.Net/Collections/IGroupedList.cs index 478d47ca2..d38a5a584 100644 --- a/Ical.Net/Collections/IGroupedList.cs +++ b/Ical.Net/Collections/IGroupedList.cs @@ -5,23 +5,22 @@ using System.Collections.Generic; -namespace Ical.Net.Collections +namespace Ical.Net.Collections; + +public interface IGroupedList : + IGroupedCollection, + IList + where TItem : class, IGroupedObject { - 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); + /// + /// 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; } - } -} + /// + /// Gets the object at the specified index. + /// + new TItem this[int index] { get; } +} \ No newline at end of file diff --git a/Ical.Net/Collections/IGroupedObject.cs b/Ical.Net/Collections/IGroupedObject.cs index 351d904bf..17e491179 100644 --- a/Ical.Net/Collections/IGroupedObject.cs +++ b/Ical.Net/Collections/IGroupedObject.cs @@ -3,10 +3,9 @@ // Licensed under the MIT license. // -namespace Ical.Net.Collections +namespace Ical.Net.Collections; + +public interface IGroupedObject { - public interface IGroupedObject - { - TGroup Group { get; set; } - } -} + TGroup Group { get; set; } +} \ No newline at end of file diff --git a/Ical.Net/Collections/IMultiLinkedList.cs b/Ical.Net/Collections/IMultiLinkedList.cs index 0803ab91e..c131dd3dc 100644 --- a/Ical.Net/Collections/IMultiLinkedList.cs +++ b/Ical.Net/Collections/IMultiLinkedList.cs @@ -5,12 +5,11 @@ using System.Collections.Generic; -namespace Ical.Net.Collections +namespace Ical.Net.Collections; + +public interface IMultiLinkedList : + IList { - public interface IMultiLinkedList : - IList - { - int StartIndex { get; } - int ExclusiveEnd { get; } - } -} + int StartIndex { get; } + int ExclusiveEnd { get; } +} \ No newline at end of file diff --git a/Ical.Net/Collections/Interfaces/IValueObject.cs b/Ical.Net/Collections/Interfaces/IValueObject.cs index 0038ee978..7e02f03f7 100644 --- a/Ical.Net/Collections/Interfaces/IValueObject.cs +++ b/Ical.Net/Collections/Interfaces/IValueObject.cs @@ -5,17 +5,16 @@ using System.Collections.Generic; -namespace Ical.Net.Collections.Interfaces +namespace Ical.Net.Collections.Interfaces; + +public interface IValueObject { - public interface IValueObject - { - IEnumerable Values { get; } + 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; } - } -} + bool ContainsValue(T value); + void SetValue(T value); + void SetValue(IEnumerable values); + void AddValue(T value); + void RemoveValue(T value); + int ValueCount { get; } +} \ No newline at end of file diff --git a/Ical.Net/Collections/MultiLinkedList.cs b/Ical.Net/Collections/MultiLinkedList.cs index a7916eeef..3a768224b 100644 --- a/Ical.Net/Collections/MultiLinkedList.cs +++ b/Ical.Net/Collections/MultiLinkedList.cs @@ -5,27 +5,26 @@ using System.Collections.Generic; -namespace Ical.Net.Collections +namespace Ical.Net.Collections; + +public class MultiLinkedList : + List, + IMultiLinkedList { - public class MultiLinkedList : - List, - IMultiLinkedList - { - private IMultiLinkedList _previous; - private IMultiLinkedList _next; + private IMultiLinkedList _previous; + private IMultiLinkedList _next; - public virtual void SetPrevious(IMultiLinkedList previous) - { - _previous = previous; - } + public virtual void SetPrevious(IMultiLinkedList previous) + { + _previous = previous; + } - public virtual void SetNext(IMultiLinkedList next) - { - _next = next; - } + public virtual void SetNext(IMultiLinkedList next) + { + _next = next; + } - public virtual int StartIndex => _previous?.ExclusiveEnd ?? 0; + public virtual int StartIndex => _previous?.ExclusiveEnd ?? 0; - public virtual int ExclusiveEnd => Count > 0 ? StartIndex + Count : StartIndex; - } -} + public virtual int ExclusiveEnd => Count > 0 ? StartIndex + Count : StartIndex; +} \ No newline at end of file diff --git a/Ical.Net/Collections/ObjectEventArgs.cs b/Ical.Net/Collections/ObjectEventArgs.cs index df7345ecb..a47f7b314 100644 --- a/Ical.Net/Collections/ObjectEventArgs.cs +++ b/Ical.Net/Collections/ObjectEventArgs.cs @@ -5,18 +5,17 @@ using System; -namespace Ical.Net.Collections +namespace Ical.Net.Collections; + +public class ObjectEventArgs : + EventArgs { - public class ObjectEventArgs : - EventArgs - { - public T First { get; set; } - public TU Second { get; set; } + public T First { get; set; } + public TU Second { get; set; } - public ObjectEventArgs(T first, TU second) - { - First = first; - Second = second; - } + public ObjectEventArgs(T first, TU second) + { + First = first; + Second = second; } -} +} \ No newline at end of file diff --git a/Ical.Net/Collections/Proxies/GroupedCollectionProxy.cs b/Ical.Net/Collections/Proxies/GroupedCollectionProxy.cs index 0d2b2a74d..f6bf53966 100644 --- a/Ical.Net/Collections/Proxies/GroupedCollectionProxy.cs +++ b/Ical.Net/Collections/Proxies/GroupedCollectionProxy.cs @@ -8,105 +8,104 @@ using System.Collections.Generic; using System.Linq; -namespace Ical.Net.Collections.Proxies +namespace Ical.Net.Collections.Proxies; + +/// +/// A proxy for a keyed list. +/// +public class GroupedCollectionProxy : + IGroupedCollection + where TOriginal : class, IGroupedObject + where TNew : class, TOriginal { - /// - /// A proxy for a keyed list. - /// - public class GroupedCollectionProxy : - IGroupedCollection - where TOriginal : class, IGroupedObject - where TNew : class, TOriginal - { - private readonly Func _predicate; + private readonly Func _predicate; - public GroupedCollectionProxy(IGroupedCollection realObject, Func predicate = null) - { - _predicate = predicate ?? (o => true); - SetProxiedObject(realObject); - } + public GroupedCollectionProxy(IGroupedCollection realObject, Func predicate = null) + { + _predicate = predicate ?? (o => true); + SetProxiedObject(realObject); + } - public virtual event EventHandler> ItemAdded; - public virtual event EventHandler> ItemRemoved; + 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 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)); - } + protected void OnItemRemoved(TNew item, int index) + { + ItemRemoved?.Invoke(this, new ObjectEventArgs(item, index)); + } - public virtual bool Remove(TGroup group) => RealObject.Remove(group); + public virtual bool Remove(TGroup group) => RealObject.Remove(group); - public virtual void Clear(TGroup group) - { - RealObject.Clear(group); - } + public virtual void Clear(TGroup group) + { + RealObject.Clear(group); + } - public virtual bool ContainsKey(TGroup group) => RealObject.ContainsKey(group); + public virtual bool ContainsKey(TGroup group) => RealObject.ContainsKey(group); - public virtual int CountOf(TGroup group) => RealObject.OfType().Count(); + public virtual int CountOf(TGroup group) => RealObject.OfType().Count(); - public virtual IEnumerable AllOf(TGroup group) => RealObject - .AllOf(group) - .OfType() - .Where(_predicate); + public virtual IEnumerable AllOf(TGroup group) => RealObject + .AllOf(group) + .OfType() + .Where(_predicate); - public virtual void Add(TNew item) - { - RealObject.Add(item); - } + public virtual void Add(TNew item) + { + RealObject.Add(item); + } - public virtual void Clear() - { - // Only clear items of this type - // that match the predicate. + public virtual void Clear() + { + // Only clear items of this type + // that match the predicate. - var items = RealObject - .OfType() - .ToArray(); + var items = RealObject + .OfType() + .ToArray(); - foreach (var item in items) - { - RealObject.Remove(item); - } + foreach (var item in items) + { + RealObject.Remove(item); } + } - public virtual bool Contains(TNew item) => RealObject.Contains(item); + public virtual bool Contains(TNew item) => RealObject.Contains(item); - public virtual void CopyTo(TNew[] array, int arrayIndex) + public virtual void CopyTo(TNew[] array, int arrayIndex) + { + var i = 0; + foreach (var item in this) { - var i = 0; - foreach (var item in this) - { - array[arrayIndex + (i++)] = item; - } + array[arrayIndex + (i++)] = item; } + } - public virtual int Count => RealObject - .OfType() - .Count(); + public virtual int Count => RealObject + .OfType() + .Count(); - public virtual bool IsReadOnly => false; + public virtual bool IsReadOnly => false; - public virtual bool Remove(TNew item) => RealObject.Remove(item); + public virtual bool Remove(TNew item) => RealObject.Remove(item); - public virtual IEnumerator GetEnumerator() => RealObject - .OfType() - .GetEnumerator(); + public virtual IEnumerator GetEnumerator() => RealObject + .OfType() + .GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => RealObject - .OfType() - .GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => RealObject + .OfType() + .GetEnumerator(); - public IGroupedCollection RealObject { get; private set; } + public IGroupedCollection RealObject { get; private set; } - public virtual void SetProxiedObject(IGroupedCollection realObject) - { - RealObject = realObject; - } + public virtual void SetProxiedObject(IGroupedCollection realObject) + { + RealObject = realObject; } -} +} \ No newline at end of file diff --git a/Ical.Net/Collections/Proxies/GroupedValueListProxy.cs b/Ical.Net/Collections/Proxies/GroupedValueListProxy.cs index 46d2b4cba..4b59948b3 100644 --- a/Ical.Net/Collections/Proxies/GroupedValueListProxy.cs +++ b/Ical.Net/Collections/Proxies/GroupedValueListProxy.cs @@ -9,229 +9,228 @@ using System.Linq; using Ical.Net.Collections.Interfaces; -namespace Ical.Net.Collections.Proxies +namespace Ical.Net.Collections.Proxies; + +/// +/// A proxy for a keyed list. +/// +public class GroupedValueListProxy : IList + where TInterface : class, IGroupedObject, IValueObject + where TItem : new() { - /// - /// 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) { - private readonly GroupedValueList _realObject; - private readonly TGroup _group; - private TInterface _container; + _realObject = realObject; + _group = group; + } - public GroupedValueListProxy(GroupedValueList realObject, TGroup group) + private TInterface EnsureContainer() + { + if (_container != null) { - _realObject = realObject; - _group = group; + return _container; } - private TInterface EnsureContainer() - { - if (_container != null) - { - return _container; - } - - // Find an item that matches our group - _container = Items.FirstOrDefault(); + // 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); + // 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) + private void IterateValues(Func, int, int, bool> action) + { + var i = 0; + foreach (var obj in _realObject) { - 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; + // 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; + // Perform some action on this item + if (!action(obj, i, count)) + return; - i += count; - } + i += count; } + } + + private IEnumerator GetEnumeratorInternal() + { + return Items + .Where(o => o.ValueCount > 0) + .SelectMany(o => o.Values.OfType()) + .GetEnumerator(); + } - private IEnumerator GetEnumeratorInternal() + public virtual void Add(TNewValue item) + { + // Add the value to the object + if (item is TOriginalValue) { - return Items - .Where(o => o.ValueCount > 0) - .SelectMany(o => o.Values.OfType()) - .GetEnumerator(); + var value = (TOriginalValue) (object) item; + EnsureContainer().AddValue(value); } + } + + public virtual void Clear() + { + var items = Items.Where(o => o.Values != null); - public virtual void Add(TNewValue item) + foreach (var original in items) { - // Add the value to the object - if (item is TOriginalValue) - { - var value = (TOriginalValue) (object) item; - EnsureContainer().AddValue(value); - } + // Clear all values from each matching object + original.SetValue(default(TOriginalValue)); } + } - public virtual void Clear() - { - var items = Items.Where(o => o.Values != null); + public virtual bool Contains(TNewValue item) => Items.Any(o => o.ContainsValue((TOriginalValue) (object) item)); - foreach (var original in items) - { - // Clear all values from each matching object - original.SetValue(default(TOriginalValue)); - } - } + 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 Contains(TNewValue item) => Items.Any(o => o.ContainsValue((TOriginalValue) (object) item)); + public virtual bool IsReadOnly => false; - public virtual void CopyTo(TNewValue[] array, int arrayIndex) + public virtual bool Remove(TNewValue item) + { + if (!(item is TOriginalValue)) { - Items - .Where(o => o.Values != null) - .SelectMany(o => o.Values) - .ToArray() - .CopyTo(array, arrayIndex); + return false; } - public virtual int Count => Items.Sum(o => o.ValueCount); + var value = (TOriginalValue) (object) item; + var container = Items.FirstOrDefault(o => o.ContainsValue(value)); - public virtual bool IsReadOnly => false; - - public virtual bool Remove(TNewValue item) + if (container == null) { - if (!(item is TOriginalValue)) - { - return false; - } - - var value = (TOriginalValue) (object) item; - var container = Items.FirstOrDefault(o => o.ContainsValue(value)); + return false; + } - if (container == null) - { - return false; - } + container.RemoveValue(value); + return true; + } - container.RemoveValue(value); - return true; - } + public virtual IEnumerator GetEnumerator() => GetEnumeratorInternal(); - public virtual IEnumerator GetEnumerator() => GetEnumeratorInternal(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumeratorInternal(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumeratorInternal(); + public virtual int IndexOf(TNewValue item) + { + var index = -1; - public virtual int IndexOf(TNewValue item) + if (!(item is TOriginalValue)) { - var index = -1; + return index; + } - if (!(item is TOriginalValue)) + var value = (TOriginalValue) (object) item; + IterateValues((o, i, count) => + { + if (o.Values != null && o.Values.Contains(value)) { - return index; + 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; - IterateValues((o, i, count) => + + // Determine if this index is found within this object + if (index < i || index >= 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; - } + // 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 Insert(int index, TNewValue item) + public virtual void RemoveAt(int index) + { + IterateValues((o, i, count) => { - IterateValues((o, i, count) => + // Determine if this index is found within this object + if (index >= i && index < 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); + // 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 void RemoveAt(int index) + public virtual TNewValue this[int index] + { + get { - IterateValues((o, i, count) => + if (index >= 0 && index < 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; - }); + return Items + .SelectMany(i => i.Values?.OfType()) + .Skip(index) + .FirstOrDefault(); + } + return default(TNewValue); } - - public virtual TNewValue this[int index] + set { - get + if (index >= 0 && index < Count) { - if (index >= 0 && index < Count) + if (!Equals(value, default(TNewValue))) { - return Items - .SelectMany(i => i.Values?.OfType()) - .Skip(index) - .FirstOrDefault(); - } - return default(TNewValue); - } - set - { - if (index >= 0 && index < Count) - { - if (!Equals(value, default(TNewValue))) - { - Insert(index, value); - index++; - } - RemoveAt(index); + Insert(index, value); + index++; } + RemoveAt(index); } } - - public virtual IEnumerable Items => _group == null - ? _realObject - : _realObject.AllOf(_group); } -} + + public virtual IEnumerable Items => _group == null + ? _realObject + : _realObject.AllOf(_group); +} \ No newline at end of file diff --git a/Ical.Net/Constants.cs b/Ical.Net/Constants.cs index 77110a116..7557dccd2 100644 --- a/Ical.Net/Constants.cs +++ b/Ical.Net/Constants.cs @@ -5,374 +5,373 @@ using System; -namespace Ical.Net +namespace Ical.Net; + +public static class AlarmAction +{ + public const string Name = "ACTION"; + public const string Key = "ACTION"; + public static readonly StringComparison Comparison = StringComparison.Ordinal; + + public const string Audio = "AUDIO"; + public const string Display = "DISPLAY"; + public const string Email = "EMAIL"; + + [Obsolete("Procedure was deprecated by RFC-5545")] + public const string Procedure = "PROCEDURE"; +} + +public static class TriggerRelation +{ + public const string Name = "TRIGGER"; + public const string Key = "TRIGGER"; + public static readonly StringComparison Comparison = StringComparison.Ordinal; + + public const string Start = "START"; + public const string End = "END"; +} + +public static class Components +{ + public const string Alarm = "VALARM"; + public const string Calendar = "VCALENDAR"; + public const string Freebusy = "VFREEBUSY"; + public const string Timezone = "VTIMEZONE"; + public const string Daylight = "DAYLIGHT"; + public const string Standard = "STANDARD"; +} + +public static class EventParticipationStatus +{ + public const string Name = "PARTSTAT"; + public const string Key = "PARTSTAT"; + public static readonly StringComparison Comparison = StringComparison.Ordinal; + + /// 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 Default => NeedsAction; +} + +public static class ToDoParticipationStatus +{ + public const string Name = "PARTSTAT"; + public const string Key = "PARTSTAT"; + public static readonly StringComparison Comparison = StringComparison.Ordinal; + + /// 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 Default => NeedsAction; +} + +public static class JournalParticipationStatus +{ + public const string Name = "PARTSTAT"; + public const string Key = "PARTSTAT"; + public static readonly StringComparison Comparison = StringComparison.Ordinal; + + public const string NeedsAction = "NEEDS-ACTION"; + public const string Accepted = "ACCEPTED"; + public const string Declined = "DECLINED"; + + 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 static class EventStatus +{ + public const string Name = "VEVENT"; + public static readonly StringComparison Comparison = StringComparison.Ordinal; + + public const string Tentative = "TENTATIVE"; + public const string Confirmed = "CONFIRMED"; + public const string Cancelled = "CANCELLED"; +} + +/// +/// Status codes available to a item. +/// +public static class TodoStatus +{ + public const string Name = "VTODO"; + public const string Key = "STATUS"; + public static readonly StringComparison Comparison = StringComparison.Ordinal; + + public const string NeedsAction = "NEEDS-ACTION"; + public const string Completed = "COMPLETED"; + public const string InProcess = "IN-PROCESS"; + public const string Cancelled = "CANCELLED"; +} + +/// +/// Status codes available to a entry. +/// +public static class JournalStatus +{ + public const string Name = "VJOURNAL"; + public const string Key = "STATUS"; + public static readonly StringComparison Comparison = StringComparison.Ordinal; + + public const string Draft = "DRAFT"; + public const string Final = "FINAL"; + public const string Cancelled = "CANCELLED"; +} + +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 +} + +[Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] +public enum RecurrenceRestrictionType { - public static class AlarmAction - { - public const string Name = "ACTION"; - public const string Key = "ACTION"; - public static readonly StringComparison Comparison = StringComparison.Ordinal; - - public const string Audio = "AUDIO"; - public const string Display = "DISPLAY"; - public const string Email = "EMAIL"; - - [Obsolete("Procedure was deprecated by RFC-5545")] - public const string Procedure = "PROCEDURE"; - } - - public static class TriggerRelation - { - public const string Name = "TRIGGER"; - public const string Key = "TRIGGER"; - public static readonly StringComparison Comparison = StringComparison.Ordinal; - - public const string Start = "START"; - public const string End = "END"; - } - - public static class Components - { - public const string Alarm = "VALARM"; - public const string Calendar = "VCALENDAR"; - public const string Freebusy = "VFREEBUSY"; - public const string Timezone = "VTIMEZONE"; - public const string Daylight = "DAYLIGHT"; - public const string Standard = "STANDARD"; - } - - public static class EventParticipationStatus - { - public const string Name = "PARTSTAT"; - public const string Key = "PARTSTAT"; - public static readonly StringComparison Comparison = StringComparison.Ordinal; - - /// 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 Default => NeedsAction; - } - - public static class ToDoParticipationStatus - { - public const string Name = "PARTSTAT"; - public const string Key = "PARTSTAT"; - public static readonly StringComparison Comparison = StringComparison.Ordinal; - - /// 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 Default => NeedsAction; - } - - public static class JournalParticipationStatus - { - public const string Name = "PARTSTAT"; - public const string Key = "PARTSTAT"; - public static readonly StringComparison Comparison = StringComparison.Ordinal; - - public const string NeedsAction = "NEEDS-ACTION"; - public const string Accepted = "ACCEPTED"; - public const string Declined = "DECLINED"; - - 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"; - } + /// + /// Same as RestrictSecondly. + /// + Default, + + /// + /// Does not restrict recurrence evaluation - WARNING: this may cause very slow performance! + /// + NoRestriction, /// - /// Status codes available to an item + /// Disallows use of the SECONDLY frequency for recurrence evaluation /// - public static class EventStatus - { - public const string Name = "VEVENT"; - public static readonly StringComparison Comparison = StringComparison.Ordinal; + RestrictSecondly, - public const string Tentative = "TENTATIVE"; - public const string Confirmed = "CONFIRMED"; - public const string Cancelled = "CANCELLED"; - } + /// + /// Disallows use of the MINUTELY and SECONDLY frequencies for recurrence evaluation + /// + RestrictMinutely, /// - /// Status codes available to a item. + /// Disallows use of the HOURLY, MINUTELY, and SECONDLY frequencies for recurrence evaluation /// - public static class TodoStatus - { - public const string Name = "VTODO"; - public const string Key = "STATUS"; - public static readonly StringComparison Comparison = StringComparison.Ordinal; - - public const string NeedsAction = "NEEDS-ACTION"; - public const string Completed = "COMPLETED"; - public const string InProcess = "IN-PROCESS"; - public const string Cancelled = "CANCELLED"; - } + RestrictHourly +} +[Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] +public enum RecurrenceEvaluationModeType +{ /// - /// Status codes available to a entry. + /// Same as ThrowException. /// - public static class JournalStatus - { - public const string Name = "VJOURNAL"; - public const string Key = "STATUS"; - public static readonly StringComparison Comparison = StringComparison.Ordinal; - - public const string Draft = "DRAFT"; - public const string Final = "FINAL"; - public const string Cancelled = "CANCELLED"; - } - - public enum FreeBusyStatus - { - Free = 0, - BusyTentative = 1, - BusyUnavailable = 2, - Busy = 3 - } - - public enum FrequencyType - { - None, - Secondly, - Minutely, - Hourly, - Daily, - Weekly, - Monthly, - Yearly - } + Default, /// - /// 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)); + /// 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. /// - 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 - } - - [Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] - 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 - } - - [Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] - 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 static class TransparencyType - { - public const string Name = "TRANSP"; - public const string Key = "TRANSP"; - public static readonly StringComparison Comparison = StringComparison.Ordinal; - - public const string Opaque = "OPAQUE"; - public const string Transparent = "TRANSPARENT"; - } - - public static class LibraryMetadata - { - public const string Version = "2.0"; - public static readonly string ProdId = "-//github.com/ical-org/ical.net//NONSGML ical.net 4.0//EN"; - } - - public static class CalendarScales - { - public const string Gregorian = "GREGORIAN"; - } - - public static 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"; - } + AdjustAutomatically, /// - /// The defaults used for regular expressions. + /// 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. /// - public static class RegexDefaults - { - /// - /// The default timeout for regular expressions in milliseconds. - /// - public const int TimeoutMilliseconds = 200; - /// - /// The default timeout for regular expressions. - /// - public static readonly TimeSpan Timeout = TimeSpan.FromMilliseconds(TimeoutMilliseconds); - } + ThrowException } + +public static class TransparencyType +{ + public const string Name = "TRANSP"; + public const string Key = "TRANSP"; + public static readonly StringComparison Comparison = StringComparison.Ordinal; + + public const string Opaque = "OPAQUE"; + public const string Transparent = "TRANSPARENT"; +} + +public static class LibraryMetadata +{ + public const string Version = "2.0"; + public static readonly string ProdId = "-//github.com/ical-org/ical.net//NONSGML ical.net 4.0//EN"; +} + +public static class CalendarScales +{ + public const string Gregorian = "GREGORIAN"; +} + +public static 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"; +} + +/// +/// The defaults used for regular expressions. +/// +public static class RegexDefaults +{ + /// + /// The default timeout for regular expressions in milliseconds. + /// + public const int TimeoutMilliseconds = 200; + /// + /// The default timeout for regular expressions. + /// + public static readonly TimeSpan Timeout = TimeSpan.FromMilliseconds(TimeoutMilliseconds); +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/AlarmOccurrence.cs b/Ical.Net/DataTypes/AlarmOccurrence.cs index 596689198..bd60a65c4 100644 --- a/Ical.Net/DataTypes/AlarmOccurrence.cs +++ b/Ical.Net/DataTypes/AlarmOccurrence.cs @@ -6,68 +6,67 @@ using System; using Ical.Net.CalendarComponents; -namespace Ical.Net.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 { - /// - /// 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 Period Period { get; set; } - public IRecurringComponent Component { get; set; } + public IRecurringComponent Component { get; set; } - public Alarm Alarm { get; set; } + public Alarm Alarm { get; set; } - public IDateTime DateTime - { - get => Period.StartTime; - set => Period = new Period(value); - } + public IDateTime DateTime + { + get => Period.StartTime; + set => Period = new Period(value); + } - public AlarmOccurrence(AlarmOccurrence ao) - { - Period = ao.Period; - Component = ao.Component; - Alarm = ao.Alarm; - } + 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 AlarmOccurrence(Alarm a, IDateTime dt, IRecurringComponent rc) + { + Alarm = a; + Period = new Period(dt); + Component = rc; + } - public int CompareTo(AlarmOccurrence other) => Period.CompareTo(other.Period); + public int CompareTo(AlarmOccurrence other) => Period.CompareTo(other.Period); - protected bool Equals(AlarmOccurrence other) - => Equals(Period, other.Period) - && Equals(Component, other.Component) - && Equals(Alarm, other.Alarm); + protected bool Equals(AlarmOccurrence other) + => 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; - return obj.GetType() == GetType() && Equals((AlarmOccurrence) obj); - } + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((AlarmOccurrence) obj); + } - public override int GetHashCode() + public override int GetHashCode() + { + // ToDo: Alarm doesn't implement Equals or GetHashCode() + unchecked { - // ToDo: Alarm doesn't implement Equals or GetHashCode() - unchecked - { - var hashCode = Period?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ (Component?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (Alarm?.GetHashCode() ?? 0); - return hashCode; - } + var hashCode = Period?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (Component?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (Alarm?.GetHashCode() ?? 0); + return hashCode; } } -} +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/Attachment.cs b/Ical.Net/DataTypes/Attachment.cs index 3794bbe8c..7e277488f 100644 --- a/Ical.Net/DataTypes/Attachment.cs +++ b/Ical.Net/DataTypes/Attachment.cs @@ -9,114 +9,113 @@ using Ical.Net.Serialization.DataTypes; using Ical.Net.Utility; -namespace Ical.Net.DataTypes +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 { - /// - /// 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 set; } // private set for CopyFrom + public virtual Uri Uri { get; set; } + public virtual byte[] Data { get; private set; } // private set for CopyFrom - private Encoding _valueEncoding = System.Text.Encoding.UTF8; - public virtual Encoding ValueEncoding + private Encoding _valueEncoding = System.Text.Encoding.UTF8; + public virtual Encoding ValueEncoding + { + get => _valueEncoding; + set { - get => _valueEncoding; - set + if (value == null) { - if (value == null) - { - return; - } - _valueEncoding = value; + return; } + _valueEncoding = value; } + } - public virtual string FormatType - { - get => Parameters.Get("FMTTYPE"); - set => Parameters.Set("FMTTYPE", value); - } + public virtual string FormatType + { + get => Parameters.Get("FMTTYPE"); + set => Parameters.Set("FMTTYPE", value); + } - public Attachment() { } + public Attachment() { } - public Attachment(byte[] value) : this() + public Attachment(byte[] value) : this() + { + if (value != null) { - if (value != null) - { - Data = value; - } + Data = value; } + } - public Attachment(string value) : this() + public Attachment(string value) : this() + { + if (string.IsNullOrEmpty(value)) { - if (string.IsNullOrEmpty(value)) - { - return; - } - - 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; + return; + } - Data = a.Data; - Uri = a.Uri; + var serializer = new AttachmentSerializer(); + var a = serializer.Deserialize(value); + if (a == null) + { + throw new ArgumentException($"{value} is not a valid ATTACH component"); } - public override string ToString() - => Data == null - ? string.Empty - : ValueEncoding.GetString(Data); + ValueEncoding = a.ValueEncoding; - /// - public override void CopyFrom(ICopyable obj) - { - if (obj is not Attachment att) return; - base.CopyFrom(obj); + Data = a.Data; + Uri = a.Uri; + } - Uri = att.Uri != null ? new Uri(att.Uri.ToString()) : null; - if (att.Data != null) - { - Data = new byte[att.Data.Length]; - Array.Copy(att.Data, Data, att.Data.Length); - } + public override string ToString() + => Data == null + ? string.Empty + : ValueEncoding.GetString(Data); - ValueEncoding = att.ValueEncoding; - FormatType = att.FormatType; - } + /// + public override void CopyFrom(ICopyable obj) + { + if (obj is not Attachment att) return; + base.CopyFrom(obj); - protected bool Equals(Attachment other) + Uri = att.Uri != null ? new Uri(att.Uri.ToString()) : null; + if (att.Data != null) { - var firstPart = Equals(Uri, other.Uri) && ValueEncoding.Equals(other.ValueEncoding); - return Data == null - ? firstPart - : firstPart && Data.SequenceEqual(other.Data); + Data = new byte[att.Data.Length]; + Array.Copy(att.Data, Data, att.Data.Length); } - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj.GetType() == GetType() && Equals((Attachment) obj); - } + ValueEncoding = att.ValueEncoding; + FormatType = att.FormatType; + } + + 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; + return obj.GetType() == GetType() && Equals((Attachment) obj); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = Uri?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ (CollectionHelpers.GetHashCode(Data)); - hashCode = (hashCode * 397) ^ (ValueEncoding?.GetHashCode() ?? 0); - return hashCode; - } + 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/Ical.Net/DataTypes/Attendee.cs b/Ical.Net/DataTypes/Attendee.cs index 9ce9b47fd..3410bea60 100644 --- a/Ical.Net/DataTypes/Attendee.cs +++ b/Ical.Net/DataTypes/Attendee.cs @@ -8,302 +8,301 @@ using System.Linq; using Ical.Net.Utility; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +public class Attendee : EncodableDataType { - public class Attendee : EncodableDataType + private Uri _sentBy; + /// SENT-BY, to indicate who is acting on behalf of the ATTENDEE + public virtual Uri SentBy { - private Uri _sentBy; - /// SENT-BY, to indicate who is acting on behalf of the ATTENDEE - public virtual Uri SentBy + get { - get + if (_sentBy != null) { - if (_sentBy != null) - { - return _sentBy; - } - - var newUrl = Parameters.Get("SENT-BY"); - Uri.TryCreate(newUrl, UriKind.RelativeOrAbsolute, out _sentBy); return _sentBy; } - set + + var newUrl = Parameters.Get("SENT-BY"); + Uri.TryCreate(newUrl, UriKind.RelativeOrAbsolute, out _sentBy); + return _sentBy; + } + set + { + if (value == null || value == _sentBy) { - if (value == null || value == _sentBy) - { - return; - } - _sentBy = value; - Parameters.Set("SENT-BY", value.OriginalString); + 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 + private string _commonName; + /// CN: to show the common or displayable name associated with the calendar address + public virtual string CommonName + { + get { - get + if (string.IsNullOrEmpty(_commonName)) { - if (string.IsNullOrEmpty(_commonName)) - { - _commonName = Parameters.Get("CN"); - } - return _commonName; + _commonName = Parameters.Get("CN"); } - set + return _commonName; + } + set + { + if (string.Equals(_commonName, value, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(_commonName, value, StringComparison.OrdinalIgnoreCase)) - { - return; - } - _commonName = value; - Parameters.Set("CN", value); + 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 + private Uri _directoryEntry; + /// DIR, to indicate the URI that points to the directory information corresponding to the attendee + public virtual Uri DirectoryEntry + { + get { - get + if (_directoryEntry != null) { - if (_directoryEntry != null) - { - return _directoryEntry; - } - - var newUrl = Parameters.Get("SENT-BY"); - Uri.TryCreate(newUrl, UriKind.RelativeOrAbsolute, out _directoryEntry); return _directoryEntry; } - set + + var newUrl = Parameters.Get("SENT-BY"); + Uri.TryCreate(newUrl, UriKind.RelativeOrAbsolute, out _directoryEntry); + return _directoryEntry; + } + set + { + if (value == null || value == _directoryEntry) { - if (value == null || value == _directoryEntry) - { - return; - } - _directoryEntry = value; - Parameters.Set("DIR", value.OriginalString); + return; } + _directoryEntry = value; + Parameters.Set("DIR", value.OriginalString); } + } - private string _type; - /// CUTYPE: the type of calendar user - public virtual string Type + private string _type; + /// CUTYPE: the type of calendar user + public virtual string Type + { + get { - get + if (string.IsNullOrEmpty(_type)) { - if (string.IsNullOrEmpty(_type)) - { - _type = Parameters.Get("CUTYPE"); - } - return _type; + _type = Parameters.Get("CUTYPE"); } - set + return _type; + } + set + { + if (string.Equals(_type, value, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(_type, value, StringComparison.OrdinalIgnoreCase)) - { - return; - } - - _type = value; - Parameters.Set("CUTYPE", value); + return; } + + _type = value; + Parameters.Set("CUTYPE", value); } + } - private List _members; - /// MEMBER: the groups the user belongs to - public virtual IList Members + private List _members; + /// MEMBER: the groups the user belongs to + public virtual IList Members + { + get => _members ?? (_members = new List(Parameters.GetMany("MEMBER"))); + set { - get => _members ?? (_members = new List(Parameters.GetMany("MEMBER"))); - set - { - _members = new List(value); - Parameters.Set("MEMBER", value); - } + _members = new List(value); + Parameters.Set("MEMBER", value); } + } - private string _role; - /// ROLE: the intended role the attendee will have - public virtual string Role + private string _role; + /// ROLE: the intended role the attendee will have + public virtual string Role + { + get { - get + if (string.IsNullOrEmpty(_role)) { - if (string.IsNullOrEmpty(_role)) - { - _role = Parameters.Get("ROLE"); - } - return _role; + _role = Parameters.Get("ROLE"); } - set + return _role; + } + set + { + if (string.Equals(_role, value, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(_role, value, StringComparison.OrdinalIgnoreCase)) - { - return; - } - _role = value; - Parameters.Set("ROLE", value); + return; } + _role = value; + Parameters.Set("ROLE", value); } + } - private string _participationStatus; - public virtual string ParticipationStatus + private string _participationStatus; + public virtual string ParticipationStatus + { + get { - get + if (string.IsNullOrEmpty(_participationStatus)) { - if (string.IsNullOrEmpty(_participationStatus)) - { - _participationStatus = Parameters.Get(EventParticipationStatus.Key); - } - return _participationStatus; + _participationStatus = Parameters.Get(EventParticipationStatus.Key); } - set + return _participationStatus; + } + set + { + if (string.Equals(_participationStatus, value, EventParticipationStatus.Comparison)) { - if (string.Equals(_participationStatus, value, EventParticipationStatus.Comparison)) - { - return; - } - _participationStatus = value; - Parameters.Set(EventParticipationStatus.Key, value); + return; } + _participationStatus = value; + Parameters.Set(EventParticipationStatus.Key, value); } + } - private bool? _rsvp; - /// RSVP, to indicate whether a reply is requested - public virtual bool Rsvp + private bool? _rsvp; + /// RSVP, to indicate whether a reply is requested + public virtual bool Rsvp + { + get { - get + if (_rsvp != null) { - 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; + return _rsvp.Value; } - set + + bool val; + var rsvp = Parameters.Get("RSVP"); + if (rsvp != null && bool.TryParse(rsvp, out val)) { - _rsvp = value; - var val = value.ToString().ToUpperInvariant(); - Parameters.Set("RSVP", 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 + private List _delegatedTo; + /// DELEGATED-TO, to indicate the calendar users that the original request was delegated to + public virtual IList DelegatedTo + { + get => _delegatedTo ?? (_delegatedTo = new List(Parameters.GetMany("DELEGATED-TO"))); + set { - get => _delegatedTo ?? (_delegatedTo = new List(Parameters.GetMany("DELEGATED-TO"))); - set + if (value == null) { - if (value == null) - { - return; - } - _delegatedTo = new List(value); - Parameters.Set("DELEGATED-TO", value); + 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 + private List _delegatedFrom; + /// DELEGATED-FROM, to indicate whom the request was delegated from + public virtual IList DelegatedFrom + { + get => _delegatedFrom ?? (_delegatedFrom = new List(Parameters.GetMany("DELEGATED-FROM"))); + set { - get => _delegatedFrom ?? (_delegatedFrom = new List(Parameters.GetMany("DELEGATED-FROM"))); - set + if (value == null) { - if (value == null) - { - return; - } - _delegatedFrom = new List(value); - Parameters.Set("DELEGATED-FROM", value); + 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; } + /// Uri associated with the attendee, typically an email address + public virtual Uri Value { get; set; } - public Attendee() { } + public Attendee() { } - public Attendee(Uri attendee) - { - Value = attendee; - } + public Attendee(Uri attendee) + { + Value = attendee; + } - public Attendee(string attendeeUri) + public Attendee(string attendeeUri) + { + if (!Uri.IsWellFormedUriString(attendeeUri, UriKind.Absolute)) { - if (!Uri.IsWellFormedUriString(attendeeUri, UriKind.Absolute)) - { - throw new ArgumentException("attendeeUri"); - } - Value = new Uri(attendeeUri); + throw new ArgumentException("attendeeUri"); } + Value = new Uri(attendeeUri); + } - /// - public override void CopyFrom(ICopyable obj) - { - if (obj is not Attendee atn) return; - base.CopyFrom(obj); + /// + public override void CopyFrom(ICopyable obj) + { + if (obj is not Attendee atn) return; + base.CopyFrom(obj); - Value = new Uri(atn.Value.ToString()); + Value = new Uri(atn.Value.ToString()); - // String assignments create new instances - CommonName = atn.CommonName; - ParticipationStatus = atn.ParticipationStatus; - Role = atn.Role; - Type = atn.Type; + // String assignments create new instances + CommonName = atn.CommonName; + ParticipationStatus = atn.ParticipationStatus; + Role = atn.Role; + Type = atn.Type; - Rsvp = atn.Rsvp; + Rsvp = atn.Rsvp; - SentBy = atn.SentBy != null ? new Uri(atn.SentBy.ToString()) : null; - DirectoryEntry = atn.DirectoryEntry != null ? new Uri(atn.DirectoryEntry.ToString()) : null; - } + SentBy = atn.SentBy != null ? new Uri(atn.SentBy.ToString()) : null; + DirectoryEntry = atn.DirectoryEntry != null ? new Uri(atn.DirectoryEntry.ToString()) : null; + } - protected bool Equals(Attendee other) => 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); + protected bool Equals(Attendee other) => 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 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() + public override int GetHashCode() + { + unchecked { - 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; - } + 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/Ical.Net/DataTypes/CalDateTime.cs b/Ical.Net/DataTypes/CalDateTime.cs index a4fc4bcf4..769419249 100644 --- a/Ical.Net/DataTypes/CalDateTime.cs +++ b/Ical.Net/DataTypes/CalDateTime.cs @@ -9,547 +9,546 @@ using Ical.Net.Utility; using NodaTime; -namespace Ical.Net.DataTypes +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 bool _hasDate; + private bool _hasTime; + + public CalDateTime() { } + + public CalDateTime(IDateTime value) + { + Initialize(value.Value, value.TzId, null); + } + + public CalDateTime(DateTime value) : this(value, null) { } + /// - /// 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. - /// + /// Specifying a `tzId` value will override `value`'s `DateTimeKind` property. If the time zone specified is UTC, the underlying `DateTimeKind` will be + /// `Utc`. If a non-UTC time zone is specified, the underlying `DateTimeKind` property will be `Local`. If no time zone is specified, the `DateTimeKind` + /// property will be left untouched. /// - public sealed class CalDateTime : EncodableDataType, IDateTime + public CalDateTime(DateTime value, string tzId) { - public static CalDateTime Now => new CalDateTime(DateTime.Now); + Initialize(value, tzId, null); + } - public static CalDateTime Today => new CalDateTime(DateTime.Today); + 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; + } - private bool _hasDate; - private bool _hasTime; + 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() { } + 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(IDateTime value) - { - Initialize(value.Value, value.TzId, null); - } + 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(DateTime value) : this(value, null) { } + public CalDateTime(string value) + { + var serializer = new DateTimeSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } - /// - /// Specifying a `tzId` value will override `value`'s `DateTimeKind` property. If the time zone specified is UTC, the underlying `DateTimeKind` will be - /// `Utc`. If a non-UTC time zone is specified, the underlying `DateTimeKind` property will be `Local`. If no time zone is specified, the `DateTimeKind` - /// property will be left untouched. - /// - public CalDateTime(DateTime value, string tzId) - { - Initialize(value, tzId, null); - } + 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); + } - public CalDateTime(int year, int month, int day, int hour, int minute, int second) + private void Initialize(DateTime value, string tzId, Calendar cal) + { + if (!string.IsNullOrWhiteSpace(tzId) && !tzId.Equals("UTC", StringComparison.OrdinalIgnoreCase)) { - Initialize(year, month, day, hour, minute, second, null, null); - HasTime = true; + // Definitely local + value = DateTime.SpecifyKind(value, DateTimeKind.Local); + TzId = tzId; } - - public CalDateTime(int year, int month, int day, int hour, int minute, int second, string tzId) + else if (string.Equals("UTC", tzId, StringComparison.OrdinalIgnoreCase) || value.Kind == DateTimeKind.Utc) { - Initialize(year, month, day, hour, minute, second, tzId, null); - HasTime = true; + // Probably UTC + value = DateTime.SpecifyKind(value, DateTimeKind.Utc); + TzId = "UTC"; } - 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); - } + Value = new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second, value.Kind); + HasDate = true; + HasTime = value.Second != 0 || value.Minute != 0 || value.Hour != 0; + AssociatedObject = cal; + } - 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 DateTime CoerceDateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + { + var dt = DateTime.MinValue; - private void Initialize(DateTime value, string tzId, Calendar cal) + // 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 (!string.IsNullOrWhiteSpace(tzId) && !tzId.Equals("UTC", StringComparison.OrdinalIgnoreCase)) + if (year > 9999) { - // Definitely local - value = DateTime.SpecifyKind(value, DateTimeKind.Local); - TzId = tzId; + dt = DateTime.MaxValue; } - else if (string.Equals("UTC", tzId, StringComparison.OrdinalIgnoreCase) || value.Kind == DateTimeKind.Utc) + else if (year > 0) { - // Probably UTC - value = DateTime.SpecifyKind(value, DateTimeKind.Utc); - TzId = "UTC"; + dt = new DateTime(year, month, day, hour, minute, second, kind); } - - Value = new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second, value.Kind); - HasDate = true; - HasTime = value.Second != 0 || value.Minute != 0 || value.Hour != 0; - AssociatedObject = cal; } + catch { } - 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; - } + return dt; + } - public override ICalendarObject AssociatedObject + public override ICalendarObject AssociatedObject + { + get => base.AssociatedObject; + set { - get => base.AssociatedObject; - set + if (!Equals(AssociatedObject, value)) { - if (!Equals(AssociatedObject, value)) - { - base.AssociatedObject = value; - } + base.AssociatedObject = value; } } + } - /// - public override void CopyFrom(ICopyable obj) - { - base.CopyFrom(obj); + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); - var dt = obj as IDateTime; - if (dt == null) - { - return; - } + var dt = obj as IDateTime; + if (dt == null) + { + return; + } - _value = dt.Value; - _hasDate = dt.HasDate; - _hasTime = dt.HasTime; - // String assignments create new instances - TzId = dt.TzId; + _value = dt.Value; + _hasDate = dt.HasDate; + _hasTime = dt.HasTime; + // String assignments create new instances + TzId = dt.TzId; - AssociateWith(dt); - } + AssociateWith(dt); + } - public bool Equals(CalDateTime other) - => this == other; + public bool Equals(CalDateTime other) + => this == other; - public override bool Equals(object other) - => other is IDateTime && (CalDateTime) other == this; + public override bool Equals(object other) + => other is IDateTime && (CalDateTime) other == this; - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = Value.GetHashCode(); - hashCode = (hashCode * 397) ^ HasDate.GetHashCode(); - hashCode = (hashCode * 397) ^ AsUtc.GetHashCode(); - hashCode = (hashCode * 397) ^ (TzId != null ? TzId.GetHashCode() : 0); - return hashCode; - } + var hashCode = Value.GetHashCode(); + hashCode = (hashCode * 397) ^ HasDate.GetHashCode(); + hashCode = (hashCode * 397) ^ AsUtc.GetHashCode(); + hashCode = (hashCode * 397) ^ (TzId != null ? TzId.GetHashCode() : 0); + return hashCode; } + } - public static bool operator <(CalDateTime left, IDateTime right) - => left != null && right != null && left.AsUtc < right.AsUtc; + public static bool operator <(CalDateTime left, IDateTime right) + => left != null && right != null && left.AsUtc < right.AsUtc; - public static bool operator >(CalDateTime left, IDateTime right) - => left != null && right != null && left.AsUtc > right.AsUtc; + public static bool operator >(CalDateTime left, IDateTime right) + => left != null && right != null && left.AsUtc > right.AsUtc; - public static bool operator <=(CalDateTime left, IDateTime right) - => left != null && right != null && left.AsUtc <= right.AsUtc; + public static bool operator <=(CalDateTime left, IDateTime right) + => left != null && right != null && left.AsUtc <= right.AsUtc; - public static bool operator >=(CalDateTime left, IDateTime right) - => left != null && right != null && left.AsUtc >= right.AsUtc; + public static bool operator >=(CalDateTime left, IDateTime right) + => left != null && right != null && left.AsUtc >= right.AsUtc; - public static bool operator ==(CalDateTime left, IDateTime right) - { - return ReferenceEquals(left, null) || ReferenceEquals(right, null) - ? ReferenceEquals(left, right) - : right is CalDateTime - && left.Value.Equals(right.Value) - && left.HasDate == right.HasDate - && left.AsUtc.Equals(right.AsUtc) - && string.Equals(left.TzId, right.TzId, StringComparison.OrdinalIgnoreCase); - } + public static bool operator ==(CalDateTime left, IDateTime right) + { + return ReferenceEquals(left, null) || ReferenceEquals(right, null) + ? ReferenceEquals(left, right) + : right is CalDateTime + && left.Value.Equals(right.Value) + && left.HasDate == right.HasDate + && left.AsUtc.Equals(right.AsUtc) + && string.Equals(left.TzId, right.TzId, StringComparison.OrdinalIgnoreCase); + } - public static bool operator !=(CalDateTime left, IDateTime right) - => !(left == right); + public static bool operator !=(CalDateTime left, IDateTime right) + => !(left == right); - public static TimeSpan operator -(CalDateTime left, IDateTime right) - { - left.AssociateWith(right); - return left.AsUtc - right.AsUtc; - } + 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 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) => new CalDateTime(left); + public static implicit operator CalDateTime(DateTime left) => new CalDateTime(left); - /// - /// Converts the date/time to the date/time of the computer running the program. If the DateTimeKind is Unspecified, it's assumed that the underlying - /// Value already represents the system's datetime. - /// - public DateTime AsSystemLocal + /// + /// Converts the date/time to the date/time of the computer running the program. If the DateTimeKind is Unspecified, it's assumed that the underlying + /// Value already represents the system's datetime. + /// + public DateTime AsSystemLocal + { + get { - get + if (Value.Kind == DateTimeKind.Unspecified) { - if (Value.Kind == DateTimeKind.Unspecified) - { - return HasTime - ? Value - : Value.Date; - } - return HasTime - ? Value.ToLocalTime() - : Value.ToLocalTime().Date; + ? Value + : Value.Date; } + + return HasTime + ? Value.ToLocalTime() + : Value.ToLocalTime().Date; } + } - private DateTime _asUtc = DateTime.MinValue; - /// - /// Returns a representation of the DateTime in Coordinated Universal Time (UTC) - /// - public DateTime AsUtc + private DateTime _asUtc = DateTime.MinValue; + /// + /// Returns a representation of the DateTime in Coordinated Universal Time (UTC) + /// + public DateTime AsUtc + { + get { - get + if (_asUtc == DateTime.MinValue) { - if (_asUtc == DateTime.MinValue) + // In order of weighting: + // 1) Specified TzId + // 2) Value having a DateTimeKind.Utc + // 3) Use the OS's time zone + + if (!string.IsNullOrWhiteSpace(TzId)) + { + var asLocal = DateUtil.ToZonedDateTimeLeniently(Value, TzId); + _asUtc = asLocal.ToDateTimeUtc(); + } + else if (IsUtc || Value.Kind == DateTimeKind.Utc) { - // In order of weighting: - // 1) Specified TzId - // 2) Value having a DateTimeKind.Utc - // 3) Use the OS's time zone - - if (!string.IsNullOrWhiteSpace(TzId)) - { - var asLocal = DateUtil.ToZonedDateTimeLeniently(Value, TzId); - _asUtc = asLocal.ToDateTimeUtc(); - } - else if (IsUtc || Value.Kind == DateTimeKind.Utc) - { - _asUtc = DateTime.SpecifyKind(Value, DateTimeKind.Utc); - } - else - { - _asUtc = DateTime.SpecifyKind(Value, DateTimeKind.Local).ToUniversalTime(); - } + _asUtc = DateTime.SpecifyKind(Value, DateTimeKind.Utc); + } + else + { + _asUtc = DateTime.SpecifyKind(Value, DateTimeKind.Local).ToUniversalTime(); } - return _asUtc; } + return _asUtc; } + } - private DateTime _value; - public DateTime Value + private DateTime _value; + public DateTime Value + { + get => _value; + set { - get => _value; - set + if (_value == value && _value.Kind == value.Kind) { - if (_value == value && _value.Kind == value.Kind) - { - return; - } - - _asUtc = DateTime.MinValue; - _value = value; + return; } + + _asUtc = DateTime.MinValue; + _value = value; } + } - public bool IsUtc => _value.Kind == DateTimeKind.Utc; + public bool IsUtc => _value.Kind == DateTimeKind.Utc; - public bool HasDate - { - get => _hasDate; - set => _hasDate = value; - } + public bool HasDate + { + get => _hasDate; + set => _hasDate = value; + } - public bool HasTime - { - get => _hasTime; - set => _hasTime = value; - } + public bool HasTime + { + get => _hasTime; + set => _hasTime = value; + } - private string _tzId = string.Empty; + private string _tzId = string.Empty; - /// - /// Setting the TzId to a local time zone will set Value.Kind to Local. Setting TzId to UTC will set Value.Kind to Utc. If the incoming value is null - /// or whitespace, Value.Kind will be set to Unspecified. Setting the TzId will NOT incur a UTC offset conversion under any circumstances. To convert - /// to another time zone, use the ToTimeZone() method. - /// - public string TzId + /// + /// Setting the TzId to a local time zone will set Value.Kind to Local. Setting TzId to UTC will set Value.Kind to Utc. If the incoming value is null + /// or whitespace, Value.Kind will be set to Unspecified. Setting the TzId will NOT incur a UTC offset conversion under any circumstances. To convert + /// to another time zone, use the ToTimeZone() method. + /// + public string TzId + { + get { - get + if (string.IsNullOrWhiteSpace(_tzId)) { - if (string.IsNullOrWhiteSpace(_tzId)) - { - _tzId = Parameters.Get("TZID"); - } - return _tzId; + _tzId = Parameters.Get("TZID"); } - set + return _tzId; + } + set + { + if (string.Equals(_tzId, value, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(_tzId, value, StringComparison.OrdinalIgnoreCase)) - { - return; - } + return; + } - _asUtc = DateTime.MinValue; + _asUtc = DateTime.MinValue; - var isEmpty = string.IsNullOrWhiteSpace(value); - if (isEmpty) - { - Parameters.Remove("TZID"); - _tzId = null; - Value = DateTime.SpecifyKind(Value, DateTimeKind.Local); - return; - } + var isEmpty = string.IsNullOrWhiteSpace(value); + if (isEmpty) + { + Parameters.Remove("TZID"); + _tzId = null; + Value = DateTime.SpecifyKind(Value, DateTimeKind.Local); + return; + } - var kind = string.Equals(value, "UTC", StringComparison.OrdinalIgnoreCase) - ? DateTimeKind.Utc - : DateTimeKind.Local; + var kind = string.Equals(value, "UTC", StringComparison.OrdinalIgnoreCase) + ? DateTimeKind.Utc + : DateTimeKind.Local; - Value = DateTime.SpecifyKind(Value, kind); - Parameters.Set("TZID", value); - _tzId = value; - } + Value = DateTime.SpecifyKind(Value, kind); + Parameters.Set("TZID", value); + _tzId = value; } + } - public string TimeZoneName => TzId; + public string TimeZoneName => TzId; - public int Year => Value.Year; + public int Year => Value.Year; - public int Month => Value.Month; + public int Month => Value.Month; - public int Day => Value.Day; + public int Day => Value.Day; - public int Hour => Value.Hour; + public int Hour => Value.Hour; - public int Minute => Value.Minute; + public int Minute => Value.Minute; - public int Second => Value.Second; + public int Second => Value.Second; - public int Millisecond => Value.Millisecond; + public int Millisecond => Value.Millisecond; - public long Ticks => Value.Ticks; + public long Ticks => Value.Ticks; - public DayOfWeek DayOfWeek => Value.DayOfWeek; + public DayOfWeek DayOfWeek => Value.DayOfWeek; - public int DayOfYear => Value.DayOfYear; + public int DayOfYear => Value.DayOfYear; - public DateTime Date => Value.Date; + public DateTime Date => Value.Date; - public TimeSpan TimeOfDay => Value.TimeOfDay; + public TimeSpan TimeOfDay => Value.TimeOfDay; - /// - /// Returns a representation of the IDateTime in the specified time zone - /// - public IDateTime ToTimeZone(string tzId) + /// + /// Returns a representation of the IDateTime in the specified time zone + /// + public IDateTime ToTimeZone(string tzId) + { + if (string.IsNullOrWhiteSpace(tzId)) { - if (string.IsNullOrWhiteSpace(tzId)) - { - throw new ArgumentException("You must provide a valid time zone id", nameof(tzId)); - } + throw new ArgumentException("You must provide a valid time zone id", nameof(tzId)); + } - // If TzId is empty, it's a system-local datetime, so we should use the system time zone as the starting point. - var originalTzId = string.IsNullOrWhiteSpace(TzId) - ? TimeZoneInfo.Local.Id - : TzId; + // If TzId is empty, it's a system-local datetime, so we should use the system time zone as the starting point. + var originalTzId = string.IsNullOrWhiteSpace(TzId) + ? TimeZoneInfo.Local.Id + : TzId; - var zonedOriginal = DateUtil.ToZonedDateTimeLeniently(Value, originalTzId); - var converted = zonedOriginal.WithZone(DateUtil.GetZone(tzId)); + var zonedOriginal = DateUtil.ToZonedDateTimeLeniently(Value, originalTzId); + var converted = zonedOriginal.WithZone(DateUtil.GetZone(tzId)); - return converted.Zone == DateTimeZone.Utc - ? new CalDateTime(converted.ToDateTimeUtc(), tzId) - : new CalDateTime(DateTime.SpecifyKind(converted.ToDateTimeUnspecified(), DateTimeKind.Local), tzId); - } + return converted.Zone == DateTimeZone.Utc + ? new CalDateTime(converted.ToDateTimeUtc(), tzId) + : new CalDateTime(DateTime.SpecifyKind(converted.ToDateTimeUnspecified(), DateTimeKind.Local), tzId); + } - /// - /// Returns a DateTimeOffset representation of the Value. If a TzId is specified, it will use that time zone's UTC offset, otherwise it will use the - /// system-local time zone. - /// - public DateTimeOffset AsDateTimeOffset => - string.IsNullOrWhiteSpace(TzId) - ? new DateTimeOffset(AsSystemLocal) - : DateUtil.ToZonedDateTimeLeniently(Value, TzId).ToDateTimeOffset(); + /// + /// Returns a DateTimeOffset representation of the Value. If a TzId is specified, it will use that time zone's UTC offset, otherwise it will use the + /// system-local time zone. + /// + public DateTimeOffset AsDateTimeOffset => + string.IsNullOrWhiteSpace(TzId) + ? new DateTimeOffset(AsSystemLocal) + : DateUtil.ToZonedDateTimeLeniently(Value, TzId).ToDateTimeOffset(); - public IDateTime Add(TimeSpan ts) => this + ts; + public IDateTime Add(TimeSpan ts) => this + ts; - public IDateTime Subtract(TimeSpan ts) => this - ts; + public IDateTime Subtract(TimeSpan ts) => this - ts; - public TimeSpan Subtract(IDateTime dt) => this - dt; + public TimeSpan Subtract(IDateTime dt) => 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 AddYears(int years) + { + var dt = Copy(); + dt.Value = Value.AddYears(years); + return dt; + } - public IDateTime AddDays(int days) - { - var dt = Copy(); - dt.Value = Value.AddDays(days); - return dt; - } + public IDateTime AddMonths(int months) + { + var dt = Copy(); + dt.Value = Value.AddMonths(months); + 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 AddDays(int days) + { + var dt = Copy(); + dt.Value = Value.AddDays(days); + return dt; + } - public IDateTime AddMinutes(int minutes) + public IDateTime AddHours(int hours) + { + var dt = Copy(); + if (!dt.HasTime && hours % 24 > 0) { - var dt = Copy(); - if (!dt.HasTime && minutes % 1440 > 0) - { - dt.HasTime = true; - } - dt.Value = Value.AddMinutes(minutes); - return dt; + dt.HasTime = true; } + dt.Value = Value.AddHours(hours); + return dt; + } - public IDateTime AddSeconds(int seconds) + public IDateTime AddMinutes(int minutes) + { + var dt = Copy(); + if (!dt.HasTime && minutes % 1440 > 0) { - var dt = Copy(); - if (!dt.HasTime && seconds % 86400 > 0) - { - dt.HasTime = true; - } - dt.Value = Value.AddSeconds(seconds); - return dt; + dt.HasTime = true; } + dt.Value = Value.AddMinutes(minutes); + return dt; + } - public IDateTime AddMilliseconds(int milliseconds) + public IDateTime AddSeconds(int seconds) + { + var dt = Copy(); + if (!dt.HasTime && seconds % 86400 > 0) { - var dt = Copy(); - if (!dt.HasTime && milliseconds % 86400000 > 0) - { - dt.HasTime = true; - } - dt.Value = Value.AddMilliseconds(milliseconds); - return dt; + dt.HasTime = true; } + dt.Value = Value.AddSeconds(seconds); + return dt; + } - public IDateTime AddTicks(long ticks) + public IDateTime AddMilliseconds(int milliseconds) + { + var dt = Copy(); + if (!dt.HasTime && milliseconds % 86400000 > 0) { - var dt = Copy(); dt.HasTime = true; - dt.Value = Value.AddTicks(ticks); - return dt; } + 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) => this < dt; + public bool LessThan(IDateTime dt) => this < dt; - public bool GreaterThan(IDateTime dt) => this > dt; + public bool GreaterThan(IDateTime dt) => this > dt; - public bool LessThanOrEqual(IDateTime dt) => this <= dt; + public bool LessThanOrEqual(IDateTime dt) => this <= dt; - public bool GreaterThanOrEqual(IDateTime dt) => this >= dt; + public bool GreaterThanOrEqual(IDateTime dt) => this >= dt; - public void AssociateWith(IDateTime dt) + public void AssociateWith(IDateTime dt) + { + if (AssociatedObject == null && dt.AssociatedObject != null) { - if (AssociatedObject == null && dt.AssociatedObject != null) - { - AssociatedObject = dt.AssociatedObject; - } - else if (AssociatedObject != null && dt.AssociatedObject == null) - { - dt.AssociatedObject = AssociatedObject; - } + AssociatedObject = dt.AssociatedObject; + } + else if (AssociatedObject != null && dt.AssociatedObject == null) + { + dt.AssociatedObject = AssociatedObject; } + } - public int CompareTo(IDateTime dt) + public int CompareTo(IDateTime dt) + { + if (Equals(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."); + return 0; } + if (this < dt) + { + return -1; + } + if (this > dt) + { + return 1; + } + throw new Exception("An error occurred while comparing two IDateTime values."); + } - public override string ToString() => ToString(null, null); + public override string ToString() => ToString(null, null); - public string ToString(string format) => ToString(format, null); + public string ToString(string format) => ToString(format, null); - public string ToString(string format, IFormatProvider formatProvider) + public string ToString(string format, IFormatProvider formatProvider) + { + var tz = TimeZoneName; + if (!string.IsNullOrEmpty(tz)) { - var tz = TimeZoneName; - if (!string.IsNullOrEmpty(tz)) - { - tz = " " + 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; + 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/Ical.Net/DataTypes/CalendarDataType.cs b/Ical.Net/DataTypes/CalendarDataType.cs index 0ee01c507..a3889d877 100644 --- a/Ical.Net/DataTypes/CalendarDataType.cs +++ b/Ical.Net/DataTypes/CalendarDataType.cs @@ -7,177 +7,176 @@ using System.Runtime.Serialization; using Ical.Net.Proxies; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// +/// An abstract class from which all iCalendar data types inherit. +/// +public abstract class CalendarDataType : ICalendarDataType { - /// - /// An abstract class from which all iCalendar data types inherit. - /// - public abstract class CalendarDataType : ICalendarDataType - { - private IParameterCollection _parameters; - private ParameterCollectionProxy _proxy; - private ServiceProvider _serviceProvider; + private IParameterCollection _parameters; + private ParameterCollectionProxy _proxy; + private ServiceProvider _serviceProvider; - private ICalendarObject _associatedObject; + private ICalendarObject _associatedObject; - protected CalendarDataType() - { - Initialize(); - } + protected CalendarDataType() + { + Initialize(); + } - private void Initialize() - { - _parameters = new ParameterList(); - _proxy = new ParameterCollectionProxy(_parameters); - _serviceProvider = new ServiceProvider(); - } + private void Initialize() + { + _parameters = new ParameterList(); + _proxy = new ParameterCollectionProxy(_parameters); + _serviceProvider = new ServiceProvider(); + } - [OnDeserializing] - internal void DeserializingInternal(StreamingContext context) - { - OnDeserializing(context); - } + [OnDeserializing] + internal void DeserializingInternal(StreamingContext context) + { + OnDeserializing(context); + } - [OnDeserialized] - internal void DeserializedInternal(StreamingContext context) - { - OnDeserialized(context); - } + [OnDeserialized] + internal void DeserializedInternal(StreamingContext context) + { + OnDeserialized(context); + } - protected virtual void OnDeserializing(StreamingContext context) - { - Initialize(); - } + protected virtual void OnDeserializing(StreamingContext context) + { + Initialize(); + } - protected virtual void OnDeserialized(StreamingContext context) { } + protected virtual void OnDeserialized(StreamingContext context) { } - public virtual Type GetValueType() + public virtual Type GetValueType() + { + // See RFC 5545 Section 3.2.20. + if (_proxy != null && _proxy.ContainsKey("VALUE")) { - // See RFC 5545 Section 3.2.20. - if (_proxy != null && _proxy.ContainsKey("VALUE")) + switch (_proxy.Get("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; - } + 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; } + return null; + } - public virtual void SetValueType(string type) - { - _proxy?.Set("VALUE", type?.ToUpper()); - } + public virtual void SetValueType(string type) + { + _proxy?.Set("VALUE", type?.ToUpper()); + } - public virtual ICalendarObject AssociatedObject + public virtual ICalendarObject AssociatedObject + { + get => _associatedObject; + set { - get => _associatedObject; - set + if (Equals(_associatedObject, value)) { - if (Equals(_associatedObject, value)) - { - return; - } + return; + } - _associatedObject = value; - if (_associatedObject != null) - { - _proxy.SetParent(_associatedObject); - if (_associatedObject is ICalendarParameterCollectionContainer) - { - _proxy.SetProxiedObject(((ICalendarParameterCollectionContainer) _associatedObject).Parameters); - } - } - else + _associatedObject = value; + if (_associatedObject != null) + { + _proxy.SetParent(_associatedObject); + if (_associatedObject is ICalendarParameterCollectionContainer) { - _proxy.SetParent(null); - _proxy.SetProxiedObject(_parameters); + _proxy.SetProxiedObject(((ICalendarParameterCollectionContainer) _associatedObject).Parameters); } } + else + { + _proxy.SetParent(null); + _proxy.SetProxiedObject(_parameters); + } } + } - public virtual Calendar Calendar => _associatedObject?.Calendar; + public virtual Calendar Calendar => _associatedObject?.Calendar; - public virtual string Language - { - get => Parameters.Get("LANGUAGE"); - set => Parameters.Set("LANGUAGE", value); - } + public virtual string Language + { + get => Parameters.Get("LANGUAGE"); + set => Parameters.Set("LANGUAGE", value); + } - /// - public virtual void CopyFrom(ICopyable obj) + /// + public virtual void CopyFrom(ICopyable obj) + { + if (obj is not ICalendarDataType dt) { - if (obj is not ICalendarDataType dt) - { - return; - } - - _associatedObject = dt.AssociatedObject; - _proxy.SetParent(_associatedObject); - _proxy.SetProxiedObject(dt.Parameters); + return; } - /// - /// Creates a deep copy of the object. - /// - /// The copy of the object. - public virtual T Copy() - { - var type = GetType(); - var obj = Activator.CreateInstance(type) as ICopyable; + _associatedObject = dt.AssociatedObject; + _proxy.SetParent(_associatedObject); + _proxy.SetProxiedObject(dt.Parameters); + } - if (obj is not T o) return default(T); + /// + /// Creates a deep copy of the object. + /// + /// The copy of the object. + public virtual T Copy() + { + var type = GetType(); + var obj = Activator.CreateInstance(type) as ICopyable; - obj.CopyFrom(this); - return o; - } + if (obj is not T o) return default(T); - public virtual IParameterCollection Parameters => _proxy; + obj.CopyFrom(this); + return o; + } - public virtual object GetService(Type serviceType) => _serviceProvider.GetService(serviceType); + public virtual IParameterCollection Parameters => _proxy; - public object GetService(string name) => _serviceProvider.GetService(name); + public virtual object GetService(Type serviceType) => _serviceProvider.GetService(serviceType); - public T GetService() => _serviceProvider.GetService(); + public object GetService(string name) => _serviceProvider.GetService(name); - public T GetService(string name) => _serviceProvider.GetService(name); + public T GetService() => _serviceProvider.GetService(); - public void SetService(string name, object obj) => _serviceProvider.SetService(name, obj); + public T GetService(string name) => _serviceProvider.GetService(name); - public void SetService(object obj) => _serviceProvider.SetService(obj); + public void SetService(string name, object obj) => _serviceProvider.SetService(name, obj); - public void RemoveService(Type type) => _serviceProvider.RemoveService(type); + public void SetService(object obj) => _serviceProvider.SetService(obj); - public void RemoveService(string name) => _serviceProvider.RemoveService(name); - } -} + 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/Ical.Net/DataTypes/EncodableDataType.cs b/Ical.Net/DataTypes/EncodableDataType.cs index 9cfce2f52..c07243041 100644 --- a/Ical.Net/DataTypes/EncodableDataType.cs +++ b/Ical.Net/DataTypes/EncodableDataType.cs @@ -3,17 +3,16 @@ // Licensed under the MIT license. // -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// +/// An abstract class from which all iCalendar data types inherit. +/// +public class EncodableDataType : CalendarDataType, IEncodableDataType { - /// - /// An abstract class from which all iCalendar data types inherit. - /// - public class EncodableDataType : CalendarDataType, IEncodableDataType + public virtual string Encoding { - public virtual string Encoding - { - get => Parameters.Get("ENCODING"); - set => Parameters.Set("ENCODING", value); - } + get => Parameters.Get("ENCODING"); + set => Parameters.Set("ENCODING", value); } -} +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/FreeBusyEntry.cs b/Ical.Net/DataTypes/FreeBusyEntry.cs index d8d775a05..19fddf976 100644 --- a/Ical.Net/DataTypes/FreeBusyEntry.cs +++ b/Ical.Net/DataTypes/FreeBusyEntry.cs @@ -3,34 +3,33 @@ // Licensed under the MIT license. // -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +public class FreeBusyEntry : Period { - public class FreeBusyEntry : Period + public virtual FreeBusyStatus Status { get; set; } + + public FreeBusyEntry() { - public virtual FreeBusyStatus Status { get; set; } + Status = FreeBusyStatus.Busy; + } - 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 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); - /// - public override void CopyFrom(ICopyable obj) + if (obj is FreeBusyEntry fb) { - base.CopyFrom(obj); - - if (obj is FreeBusyEntry fb) - { - Status = fb.Status; - } + Status = fb.Status; } } -} +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/GeographicLocation.cs b/Ical.Net/DataTypes/GeographicLocation.cs index 4a3c5662c..6a6e3cad4 100644 --- a/Ical.Net/DataTypes/GeographicLocation.cs +++ b/Ical.Net/DataTypes/GeographicLocation.cs @@ -7,64 +7,63 @@ using Ical.Net.CalendarComponents; using Ical.Net.Serialization.DataTypes; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// +/// A class that represents the geographical location of an +/// or item. +/// +[DebuggerDisplay("{Latitude};{Longitude}")] +public class GeographicLocation : EncodableDataType { - /// - /// 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() { - public double Latitude { get; set; } - public double Longitude { get; set; } + var serializer = new GeographicLocationSerializer(); + serializer.Deserialize(value); + } - public GeographicLocation() { } + public GeographicLocation(double latitude, double longitude) + { + Latitude = latitude; + Longitude = longitude; + } - public GeographicLocation(string value) : this() - { - var serializer = new GeographicLocationSerializer(); - serializer.Deserialize(value); - } + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); - public GeographicLocation(double latitude, double longitude) + var geo = obj as GeographicLocation; + if (geo == null) { - Latitude = latitude; - Longitude = longitude; + return; } - /// - public override void CopyFrom(ICopyable obj) - { - base.CopyFrom(obj); - - var geo = obj as GeographicLocation; - if (geo == null) - { - return; - } - - Latitude = geo.Latitude; - Longitude = geo.Longitude; - } + Latitude = geo.Latitude; + Longitude = geo.Longitude; + } - public override string ToString() => Latitude.ToString("0.000000") + ";" + Longitude.ToString("0.000000"); + public override string ToString() => Latitude.ToString("0.000000") + ";" + Longitude.ToString("0.000000"); - protected bool Equals(GeographicLocation other) => Latitude.Equals(other.Latitude) && Longitude.Equals(other.Longitude); + protected bool Equals(GeographicLocation other) => 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; - return obj.GetType() == GetType() && Equals((GeographicLocation) obj); - } + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((GeographicLocation) obj); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - return (Latitude.GetHashCode() * 397) ^ Longitude.GetHashCode(); - } + return (Latitude.GetHashCode() * 397) ^ Longitude.GetHashCode(); } } -} +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/ICalendarDataType.cs b/Ical.Net/DataTypes/ICalendarDataType.cs index 7a3a43cdc..8b99b439e 100644 --- a/Ical.Net/DataTypes/ICalendarDataType.cs +++ b/Ical.Net/DataTypes/ICalendarDataType.cs @@ -5,15 +5,14 @@ using System; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +public interface ICalendarDataType : ICalendarParameterCollectionContainer, ICopyable, IServiceProvider { - public interface ICalendarDataType : ICalendarParameterCollectionContainer, ICopyable, IServiceProvider - { - Type GetValueType(); - void SetValueType(string type); - ICalendarObject AssociatedObject { get; set; } - Calendar Calendar { get; } + Type GetValueType(); + void SetValueType(string type); + ICalendarObject AssociatedObject { get; set; } + Calendar Calendar { get; } - string Language { get; set; } - } -} + string Language { get; set; } +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/ICalendarParameterCollectionContainer.cs b/Ical.Net/DataTypes/ICalendarParameterCollectionContainer.cs index d4850edfd..36ca6a57b 100644 --- a/Ical.Net/DataTypes/ICalendarParameterCollectionContainer.cs +++ b/Ical.Net/DataTypes/ICalendarParameterCollectionContainer.cs @@ -3,10 +3,9 @@ // Licensed under the MIT license. // -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +public interface ICalendarParameterCollectionContainer { - public interface ICalendarParameterCollectionContainer - { - IParameterCollection Parameters { get; } - } -} + IParameterCollection Parameters { get; } +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/IDateTime.cs b/Ical.Net/DataTypes/IDateTime.cs index dc988a25e..155d374fd 100644 --- a/Ical.Net/DataTypes/IDateTime.cs +++ b/Ical.Net/DataTypes/IDateTime.cs @@ -5,134 +5,133 @@ using System; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +public interface IDateTime : IEncodableDataType, IComparable, IFormattable, ICalendarDataType { - 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; } - - /// - /// Returns a DateTimeOffset representation of the Value. If a TzId is specified, it will use that time zone's UTC offset, otherwise it will use the - /// system-local time zone. - /// - DateTimeOffset AsDateTimeOffset { get; } - - /// - /// Gets/sets whether the Value of this date/time represents - /// a universal time. - /// - bool IsUtc { get; } - - /// - /// 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 IsUtc 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 tzId); - - 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); - } -} + /// + /// 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; } + + /// + /// Returns a DateTimeOffset representation of the Value. If a TzId is specified, it will use that time zone's UTC offset, otherwise it will use the + /// system-local time zone. + /// + DateTimeOffset AsDateTimeOffset { get; } + + /// + /// Gets/sets whether the Value of this date/time represents + /// a universal time. + /// + bool IsUtc { get; } + + /// + /// 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 IsUtc 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 tzId); + + 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/Ical.Net/DataTypes/IEncodableDataType.cs b/Ical.Net/DataTypes/IEncodableDataType.cs index 1108c219e..82cd0283f 100644 --- a/Ical.Net/DataTypes/IEncodableDataType.cs +++ b/Ical.Net/DataTypes/IEncodableDataType.cs @@ -3,10 +3,9 @@ // Licensed under the MIT license. // -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +public interface IEncodableDataType { - public interface IEncodableDataType - { - string Encoding { get; set; } - } -} + string Encoding { get; set; } +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/Occurrence.cs b/Ical.Net/DataTypes/Occurrence.cs index 0e93b601a..afbbf18de 100644 --- a/Ical.Net/DataTypes/Occurrence.cs +++ b/Ical.Net/DataTypes/Occurrence.cs @@ -6,60 +6,59 @@ using System; using Ical.Net.CalendarComponents; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +public class Occurrence : IComparable { - public class Occurrence : IComparable + public Period Period { get; set; } + public IRecurrable Source { get; set; } + + public Occurrence(Occurrence ao) { - public Period Period { get; set; } - public IRecurrable Source { get; set; } + Period = ao.Period; + Source = ao.Source; + } - public Occurrence(Occurrence ao) - { - Period = ao.Period; - Source = ao.Source; - } + public Occurrence(IRecurrable recurrable, Period period) + { + Source = recurrable; + Period = period; + } - public Occurrence(IRecurrable recurrable, Period period) + public bool Equals(Occurrence other) => Equals(Period, other.Period) && Equals(Source, other.Source); + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { - Source = recurrable; - Period = period; + return false; } + return obj is Occurrence && Equals((Occurrence) obj); + } - public bool Equals(Occurrence other) => Equals(Period, other.Period) && Equals(Source, other.Source); - - public override bool Equals(object obj) + public override int GetHashCode() + { + unchecked { - if (ReferenceEquals(null, obj)) - { - return false; - } - return obj is Occurrence && Equals((Occurrence) obj); + return ((Period?.GetHashCode() ?? 0) * 397) ^ (Source?.GetHashCode() ?? 0); } + } - public override int GetHashCode() + public override string ToString() + { + var s = "Occurrence"; + if (Source != null) { - unchecked - { - return ((Period?.GetHashCode() ?? 0) * 397) ^ (Source?.GetHashCode() ?? 0); - } + s = Source.GetType().Name + " "; } - public override string ToString() + if (Period != null) { - var s = "Occurrence"; - if (Source != null) - { - s = Source.GetType().Name + " "; - } - - if (Period != null) - { - s += "(" + Period.StartTime + ")"; - } - - return s; + s += "(" + Period.StartTime + ")"; } - public int CompareTo(Occurrence other) => Period.CompareTo(other.Period); + return s; } -} + + public int CompareTo(Occurrence other) => Period.CompareTo(other.Period); +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/Organizer.cs b/Ical.Net/DataTypes/Organizer.cs index 575bf0e7d..0eed39872 100644 --- a/Ical.Net/DataTypes/Organizer.cs +++ b/Ical.Net/DataTypes/Organizer.cs @@ -8,97 +8,96 @@ using System.IO; using Ical.Net.Serialization.DataTypes; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// +/// A class that represents the organizer of an event/todo/journal. +/// +[DebuggerDisplay("{Value}")] +public class Organizer : EncodableDataType { - /// - /// A class that represents the organizer of an event/todo/journal. - /// - [DebuggerDisplay("{Value}")] - public class Organizer : EncodableDataType + public virtual Uri SentBy { - public virtual Uri SentBy + get => new Uri(Parameters.Get("SENT-BY")); + set { - get => new Uri(Parameters.Get("SENT-BY")); - set + if (value != null) + { + Parameters.Set("SENT-BY", value.OriginalString); + } + else { - if (value != null) - { - Parameters.Set("SENT-BY", value.OriginalString); - } - else - { - Parameters.Set("SENT-BY", (string) null); - } + Parameters.Set("SENT-BY", (string) null); } } + } - public virtual string CommonName - { - get => Parameters.Get("CN"); - set => Parameters.Set("CN", value); - } + public virtual string CommonName + { + get => Parameters.Get("CN"); + set => Parameters.Set("CN", value); + } - public virtual Uri DirectoryEntry + public virtual Uri DirectoryEntry + { + get => new Uri(Parameters.Get("DIR")); + set { - get => new Uri(Parameters.Get("DIR")); - set + if (value != null) { - if (value != null) - { - Parameters.Set("DIR", value.OriginalString); - } - else - { - Parameters.Set("DIR", (string) null); - } + Parameters.Set("DIR", value.OriginalString); + } + else + { + Parameters.Set("DIR", (string) null); } } + } - public virtual Uri Value { get; set; } + public virtual Uri Value { get; set; } - public Organizer() { } + public Organizer() { } - public Organizer(string value) : this() + public Organizer(string value) : this() + { + if (string.IsNullOrWhiteSpace(value)) { - if (string.IsNullOrWhiteSpace(value)) - { - return; - } - - var serializer = new OrganizerSerializer(); - CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + return; } - protected bool Equals(Organizer other) => Equals(Value, other.Value); + var serializer = new OrganizerSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } + + protected bool Equals(Organizer other) => Equals(Value, other.Value); - public override bool Equals(object obj) + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { - if (ReferenceEquals(null, obj)) - { - return false; - } - if (ReferenceEquals(this, obj)) - { - return true; - } - if (obj.GetType() != GetType()) - { - return false; - } - return Equals((Organizer) obj); + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; } + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Organizer) obj); + } - public override int GetHashCode() => Value?.GetHashCode() ?? 0; + public override int GetHashCode() => Value?.GetHashCode() ?? 0; - /// - public override void CopyFrom(ICopyable obj) - { - base.CopyFrom(obj); + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); - if (obj is Organizer o) - { - Value = o.Value; - } + if (obj is Organizer o) + { + Value = o.Value; } } -} +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/Period.cs b/Ical.Net/DataTypes/Period.cs index 15de3ad6f..c64bcd11b 100644 --- a/Ical.Net/DataTypes/Period.cs +++ b/Ical.Net/DataTypes/Period.cs @@ -6,189 +6,188 @@ using System; using Ical.Net.Serialization.DataTypes; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// Represents an iCalendar period of time. +public class Period : EncodableDataType, IComparable { - /// Represents an iCalendar period of time. - public class Period : EncodableDataType, IComparable - { - public Period() { } + public Period() { } - public Period(IDateTime occurs) - : this(occurs, default(TimeSpan)) { } + public Period(IDateTime occurs) + : this(occurs, default(TimeSpan)) { } - public Period(IDateTime start, IDateTime end) + public Period(IDateTime start, IDateTime end) + { + if (end != null && end.LessThanOrEqual(start)) { - 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); + throw new ArgumentException($"Start time ( {start} ) must come before the end time ( {end} )"); } - public Period(IDateTime start, TimeSpan duration) + StartTime = start; + if (end == null) { - if (duration < TimeSpan.Zero) - { - throw new ArgumentException($"Duration ( ${duration} ) cannot be less than zero"); - } - - StartTime = start; - if (duration == default(TimeSpan)) - { - return; - } + return; + } + EndTime = end; + Duration = end.Subtract(start); + } - Duration = duration; - EndTime = start.Add(duration); + public Period(IDateTime start, TimeSpan duration) + { + if (duration < TimeSpan.Zero) + { + throw new ArgumentException($"Duration ( ${duration} ) cannot be less than zero"); } - /// - public override void CopyFrom(ICopyable obj) + StartTime = start; + if (duration == default(TimeSpan)) { - base.CopyFrom(obj); + return; + } - if (obj is not Period p) return; + Duration = duration; + EndTime = start.Add(duration); + } - StartTime = p.StartTime?.Copy(); - EndTime = p.EndTime?.Copy(); - Duration = p.Duration; - } + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + + if (obj is not Period p) return; - protected bool Equals(Period other) => Equals(StartTime, other.StartTime) && Equals(EndTime, other.EndTime) && Duration.Equals(other.Duration); + StartTime = p.StartTime?.Copy(); + EndTime = p.EndTime?.Copy(); + Duration = p.Duration; + } + + protected bool Equals(Period other) => 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; + return obj.GetType() == GetType() && Equals((Period) obj); + } - public override bool Equals(object obj) + public override int GetHashCode() + { + unchecked { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj.GetType() == GetType() && Equals((Period) obj); + var hashCode = StartTime?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (EndTime?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ Duration.GetHashCode(); + return hashCode; } + } - public override int GetHashCode() + public override string ToString() + { + var periodSerializer = new PeriodSerializer(); + return periodSerializer.SerializeToString(this); + } + + private void ExtrapolateTimes() + { + if (EndTime == null && StartTime != null && Duration != default(TimeSpan)) { - unchecked - { - var hashCode = StartTime?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ (EndTime?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ Duration.GetHashCode(); - return hashCode; - } + EndTime = StartTime.Add(Duration); } - - public override string ToString() + else if (Duration == default(TimeSpan) && StartTime != null && EndTime != null) { - var periodSerializer = new PeriodSerializer(); - return periodSerializer.SerializeToString(this); + Duration = EndTime.Subtract(StartTime); } - - private void ExtrapolateTimes() + else if (StartTime == null && Duration != default(TimeSpan) && EndTime != null) { - 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); - } + StartTime = EndTime.Subtract(Duration); } + } - private IDateTime _startTime; - public virtual IDateTime StartTime + private IDateTime _startTime; + public virtual IDateTime StartTime + { + get => _startTime.HasTime + ? _startTime + : new CalDateTime(new DateTime(_startTime.Value.Year, _startTime.Value.Month, _startTime.Value.Day, 0, 0, 0), _startTime.TzId); + set { - get => _startTime.HasTime - ? _startTime - : new CalDateTime(new DateTime(_startTime.Value.Year, _startTime.Value.Month, _startTime.Value.Day, 0, 0, 0), _startTime.TzId); - set + if (Equals(_startTime, value)) { - if (Equals(_startTime, value)) - { - return; - } - _startTime = value; - ExtrapolateTimes(); + return; } + _startTime = value; + ExtrapolateTimes(); } + } - private IDateTime _endTime; - public virtual IDateTime EndTime + private IDateTime _endTime; + public virtual IDateTime EndTime + { + get => _endTime; + set { - get => _endTime; - set + if (Equals(_endTime, value)) { - if (Equals(_endTime, value)) - { - return; - } - _endTime = value; - ExtrapolateTimes(); + return; } + _endTime = value; + ExtrapolateTimes(); } + } - private TimeSpan _duration; - public virtual TimeSpan Duration + private TimeSpan _duration; + public virtual TimeSpan Duration + { + get { - get - { - if (StartTime != null - && EndTime == null - && StartTime.Value.TimeOfDay == TimeSpan.Zero) - { - return TimeSpan.FromDays(1); - } - return _duration; - } - set + if (StartTime != null + && EndTime == null + && StartTime.Value.TimeOfDay == TimeSpan.Zero) { - if (Equals(_duration, value)) - { - return; - } - _duration = value; - ExtrapolateTimes(); + return TimeSpan.FromDays(1); } + return _duration; } - - public virtual bool Contains(IDateTime dt) + set { - // Start time is inclusive - if (dt == null || StartTime == null || !StartTime.LessThanOrEqual(dt)) + if (Equals(_duration, value)) { - return false; + return; } + _duration = value; + ExtrapolateTimes(); + } + } - // End time is exclusive - return EndTime == null || EndTime.GreaterThan(dt); + public virtual bool Contains(IDateTime dt) + { + // Start time is inclusive + if (dt == null || StartTime == null || !StartTime.LessThanOrEqual(dt)) + { + return false; } - public virtual bool CollidesWith(Period period) => period != null - && ((period.StartTime != null && Contains(period.StartTime)) || (period.EndTime != null && Contains(period.EndTime))); + // End time is exclusive + return EndTime == null || EndTime.GreaterThan(dt); + } + + public virtual bool CollidesWith(Period period) => period != null + && ((period.StartTime != null && Contains(period.StartTime)) || (period.EndTime != null && Contains(period.EndTime))); - public int CompareTo(Period other) + public int CompareTo(Period other) + { + if (StartTime.Equals(other.StartTime)) { - 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."); + 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/Ical.Net/DataTypes/PeriodList.cs b/Ical.Net/DataTypes/PeriodList.cs index 20443b8c8..44fd59794 100644 --- a/Ical.Net/DataTypes/PeriodList.cs +++ b/Ical.Net/DataTypes/PeriodList.cs @@ -12,119 +12,118 @@ using Ical.Net.Serialization.DataTypes; using Ical.Net.Utility; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// +/// An iCalendar list of recurring dates (or date exclusions) +/// +public class PeriodList : EncodableDataType, IList { - /// - /// An iCalendar list of recurring dates (or date exclusions) - /// - public class PeriodList : EncodableDataType, IList + public string TzId { get; set; } + public int Count => Periods.Count; + + protected IList Periods { get; set; } = new List(); + + public PeriodList() { - public string TzId { get; set; } - public int Count => Periods.Count; + SetService(new PeriodListEvaluator(this)); + } - protected IList Periods { get; set; } = new List(); + public PeriodList(string value) : this() + { + var serializer = new PeriodListSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } - public PeriodList() + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (obj is not PeriodList list) { - SetService(new PeriodListEvaluator(this)); + return; } - public PeriodList(string value) : this() + foreach (var p in list) { - var serializer = new PeriodListSerializer(); - CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + Add(p.Copy()); } - /// - public override void CopyFrom(ICopyable obj) - { - base.CopyFrom(obj); - if (obj is not PeriodList list) - { - return; - } - - foreach (var p in list) - { - Add(p.Copy()); - } - - // String assignments create new instances - TzId = list.TzId; - } + // String assignments create new instances + TzId = list.TzId; + } - public override string ToString() => new PeriodListSerializer().SerializeToString(this); + public override string ToString() => new PeriodListSerializer().SerializeToString(this); - public void Add(IDateTime dt) => Periods.Add(new Period(dt)); + public void Add(IDateTime dt) => Periods.Add(new Period(dt)); - public static Dictionary> GetGroupedPeriods(IList periodLists) + public static Dictionary> GetGroupedPeriods(IList periodLists) + { + // In order to know if two events are equal, a semantic understanding of exdates, rdates, rrules, and exrules is required. This could be done by + // computing the complete recurrence set (expensive) while being time-zone sensitive, or by comparing each List in each IPeriodList. + + // For example, events containing these rules generate the same recurrence set, including having the same time zone for each occurrence, so + // they're the same: + // Event A: + // RDATE:20170302T060000Z,20170303T060000Z + // Event B: + // RDATE:20170302T060000Z + // RDATE:20170303T060000Z + + var grouped = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (var periodList in periodLists) { - // In order to know if two events are equal, a semantic understanding of exdates, rdates, rrules, and exrules is required. This could be done by - // computing the complete recurrence set (expensive) while being time-zone sensitive, or by comparing each List in each IPeriodList. - - // For example, events containing these rules generate the same recurrence set, including having the same time zone for each occurrence, so - // they're the same: - // Event A: - // RDATE:20170302T060000Z,20170303T060000Z - // Event B: - // RDATE:20170302T060000Z - // RDATE:20170303T060000Z - - var grouped = new Dictionary>(StringComparer.OrdinalIgnoreCase); - foreach (var periodList in periodLists) + var defaultBucket = string.IsNullOrWhiteSpace(periodList.TzId) ? "" : periodList.TzId; + + foreach (var period in periodList) { - var defaultBucket = string.IsNullOrWhiteSpace(periodList.TzId) ? "" : periodList.TzId; + var actualBucket = string.IsNullOrWhiteSpace(period.StartTime.TzId) ? defaultBucket : period.StartTime.TzId; - foreach (var period in periodList) + if (!grouped.ContainsKey(actualBucket)) { - var actualBucket = string.IsNullOrWhiteSpace(period.StartTime.TzId) ? defaultBucket : period.StartTime.TzId; - - if (!grouped.ContainsKey(actualBucket)) - { - grouped.Add(actualBucket, new HashSet()); - } - grouped[actualBucket].Add(period); + grouped.Add(actualBucket, new HashSet()); } + grouped[actualBucket].Add(period); } - return grouped.ToDictionary(k => k.Key, v => v.Value.OrderBy(d => d.StartTime).ToList()); } + return grouped.ToDictionary(k => k.Key, v => v.Value.OrderBy(d => d.StartTime).ToList()); + } - protected bool Equals(PeriodList other) => string.Equals(TzId, other.TzId, StringComparison.OrdinalIgnoreCase) - && CollectionHelpers.Equals(Periods, other.Periods); - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - return obj.GetType() == GetType() && Equals((PeriodList) obj); - } + protected bool Equals(PeriodList other) => string.Equals(TzId, other.TzId, StringComparison.OrdinalIgnoreCase) + && CollectionHelpers.Equals(Periods, other.Periods); - public override int GetHashCode() - { - unchecked - { - var hashCode = TzId?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Periods); - return hashCode; - } - } + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == GetType() && Equals((PeriodList) obj); + } - public Period this[int index] + public override int GetHashCode() + { + unchecked { - get => Periods[index]; - set => Periods[index] = value; + var hashCode = TzId?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ CollectionHelpers.GetHashCode(Periods); + return hashCode; } + } - public bool Remove(Period item) => Periods.Remove(item); - public bool IsReadOnly => Periods.IsReadOnly; - public int IndexOf(Period item) => Periods.IndexOf(item); - public void Insert(int index, Period item) => Periods.Insert(index, item); - public void RemoveAt(int index) => Periods.RemoveAt(index); - public void Add(Period item) => Periods.Add(item); - public void Clear() => Periods.Clear(); - public bool Contains(Period item) => Periods.Contains(item); - public void CopyTo(Period[] array, int arrayIndex) => Periods.CopyTo(array, arrayIndex); - public IEnumerator GetEnumerator() => Periods.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => Periods.GetEnumerator(); + public Period this[int index] + { + get => Periods[index]; + set => Periods[index] = value; } -} + + public bool Remove(Period item) => Periods.Remove(item); + public bool IsReadOnly => Periods.IsReadOnly; + public int IndexOf(Period item) => Periods.IndexOf(item); + public void Insert(int index, Period item) => Periods.Insert(index, item); + public void RemoveAt(int index) => Periods.RemoveAt(index); + public void Add(Period item) => Periods.Add(item); + public void Clear() => Periods.Clear(); + public bool Contains(Period item) => Periods.Contains(item); + public void CopyTo(Period[] array, int arrayIndex) => Periods.CopyTo(array, arrayIndex); + public IEnumerator GetEnumerator() => Periods.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => Periods.GetEnumerator(); +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/RecurrencePattern.cs b/Ical.Net/DataTypes/RecurrencePattern.cs index 224b03c4e..79dc07fb1 100644 --- a/Ical.Net/DataTypes/RecurrencePattern.cs +++ b/Ical.Net/DataTypes/RecurrencePattern.cs @@ -11,231 +11,230 @@ using Ical.Net.Serialization.DataTypes; using Ical.Net.Utility; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// +/// An iCalendar representation of the RRULE property. +/// https://tools.ietf.org/html/rfc5545#section-3.3.10 +/// +public class RecurrencePattern : EncodableDataType { - /// - /// An iCalendar representation of the RRULE property. - /// https://tools.ietf.org/html/rfc5545#section-3.3.10 - /// - public class RecurrencePattern : EncodableDataType - { - private int _interval = int.MinValue; + private int _interval = int.MinValue; #pragma warning disable 0618 - private RecurrenceRestrictionType? _restrictionType; - private RecurrenceEvaluationModeType? _evaluationMode; + private RecurrenceRestrictionType? _restrictionType; + private RecurrenceEvaluationModeType? _evaluationMode; #pragma warning restore 0618 - public FrequencyType Frequency { get; set; } + public FrequencyType Frequency { get; set; } - private DateTime _until = DateTime.MinValue; - public DateTime Until + private DateTime _until = DateTime.MinValue; + public DateTime Until + { + get => _until; + set { - get => _until; - set + if (_until == value && _until.Kind == value.Kind) { - if (_until == value && _until.Kind == value.Kind) - { - return; - } - - _until = value; + return; } + + _until = value; } + } - public int Count { get; set; } = int.MinValue; + public int Count { get; set; } = int.MinValue; - /// - /// Specifies how often the recurrence should repeat. - /// - 1 = every - /// - 2 = every second - /// - 3 = every third - /// - public int Interval - { - get => _interval == int.MinValue - ? 1 - : _interval; - set => _interval = value; - } + /// + /// Specifies how often the recurrence should repeat. + /// - 1 = every + /// - 2 = every second + /// - 3 = every third + /// + public int Interval + { + get => _interval == int.MinValue + ? 1 + : _interval; + set => _interval = value; + } - public List BySecond { get; set; } = new List(); + public List BySecond { get; set; } = new List(); - /// The ordinal minutes of the hour associated with this recurrence pattern. Valid values are 0-59. - public List ByMinute { get; set; } = new List(); + /// The ordinal minutes of the hour associated with this recurrence pattern. Valid values are 0-59. + public List ByMinute { get; set; } = new List(); - public List ByHour { get; set; } = new List(); + public List ByHour { get; set; } = new List(); - public List ByDay { get; set; } = new List(); + public List ByDay { get; set; } = new List(); - /// The ordinal days of the month associated with this recurrence pattern. Valid values are 1-31. - public List ByMonthDay { get; set; } = new List(); + /// The ordinal days of the month associated with this recurrence pattern. Valid values are 1-31. + public List ByMonthDay { get; set; } = new List(); - /// - /// The ordinal days of the year associated with this recurrence pattern. Something recurring on the first day of the year would be a list containing - /// 1, and would also be New Year's Day. - /// - public List ByYearDay { get; set; } = new List(); + /// + /// The ordinal days of the year associated with this recurrence pattern. Something recurring on the first day of the year would be a list containing + /// 1, and would also be New Year's Day. + /// + public List ByYearDay { get; set; } = new List(); - /// - /// The ordinal week of the year. Valid values are -53 to +53. Negative values count backwards from the end of the specified year. - /// A week is defined by ISO.8601.2004 - /// - public List ByWeekNo { get; set; } = new List(); + /// + /// The ordinal week of the year. Valid values are -53 to +53. Negative values count backwards from the end of the specified year. + /// A week is defined by ISO.8601.2004 + /// + public List ByWeekNo { get; set; } = new List(); - /// - /// List of months in the year associated with this rule. Valid values are 1 through 12. - /// - public List ByMonth { get; set; } = new List(); + /// + /// List of months in the year associated with this rule. Valid values are 1 through 12. + /// + public List ByMonth { get; set; } = new List(); - public List BySetPosition { get; set; } = new List(); + public List BySetPosition { get; set; } = new List(); - public DayOfWeek FirstDayOfWeek { get; set; } = DayOfWeek.Monday; + public DayOfWeek FirstDayOfWeek { get; set; } = DayOfWeek.Monday; #pragma warning disable 0618 - /// - /// The type of restriction to apply to the evaluation of this recurrence pattern. - /// Returns if not set. - /// - [Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] - public RecurrenceRestrictionType RestrictionType + /// + /// The type of restriction to apply to the evaluation of this recurrence pattern. + /// Returns if not set. + /// + [Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] + public RecurrenceRestrictionType RestrictionType + { + get { - get + // NOTE: Fixes bug #1924358 - Cannot evaluate Secondly patterns + if (_restrictionType != null) { - // NOTE: Fixes bug #1924358 - Cannot evaluate Secondly patterns - if (_restrictionType != null) - { - return _restrictionType.Value; - } - return Calendar?.RecurrenceRestriction ?? RecurrenceRestrictionType.Default; + return _restrictionType.Value; } - set => _restrictionType = value; + return Calendar?.RecurrenceRestriction ?? RecurrenceRestrictionType.Default; } + set => _restrictionType = value; + } - [Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] - public RecurrenceEvaluationModeType EvaluationMode + [Obsolete("Usage may cause undesired results or exceptions. Will be removed.", false)] + public RecurrenceEvaluationModeType EvaluationMode + { + get { - get + // NOTE: Fixes bug #1924358 - Cannot evaluate Secondly patterns + if (_evaluationMode != null) { - // NOTE: Fixes bug #1924358 - Cannot evaluate Secondly patterns - if (_evaluationMode != null) - { - return _evaluationMode.Value; - } - return Calendar?.RecurrenceEvaluationMode ?? RecurrenceEvaluationModeType.Default; + return _evaluationMode.Value; } - set => _evaluationMode = value; + return Calendar?.RecurrenceEvaluationMode ?? RecurrenceEvaluationModeType.Default; } + set => _evaluationMode = value; + } #pragma warning restore 0618 - public RecurrencePattern() - { - SetService(new RecurrencePatternEvaluator(this)); - } + public RecurrencePattern() + { + SetService(new RecurrencePatternEvaluator(this)); + } - public RecurrencePattern(FrequencyType frequency) : this(frequency, 1) { } + public RecurrencePattern(FrequencyType frequency) : this(frequency, 1) { } - public RecurrencePattern(FrequencyType frequency, int interval) : this() - { - Frequency = frequency; - Interval = interval; - } + public RecurrencePattern(FrequencyType frequency, int interval) : this() + { + Frequency = frequency; + Interval = interval; + } - public RecurrencePattern(string value) : this() + public RecurrencePattern(string value) : this() + { + if (string.IsNullOrWhiteSpace(value)) { - if (string.IsNullOrWhiteSpace(value)) - { - return; - } - var serializer = new RecurrencePatternSerializer(); - CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + 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); - } + public override string ToString() + { + var serializer = new RecurrencePatternSerializer(); + return serializer.SerializeToString(this); + } - protected bool Equals(RecurrencePattern other) => (Interval == other.Interval) + protected bool Equals(RecurrencePattern other) => (Interval == other.Interval) #pragma warning disable 0618 - && RestrictionType == other.RestrictionType - && EvaluationMode == other.EvaluationMode + && RestrictionType == other.RestrictionType + && EvaluationMode == other.EvaluationMode #pragma warning restore 0618 - && 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; - return obj.GetType() == GetType() && Equals((RecurrencePattern) obj); - } + && 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; + return obj.GetType() == GetType() && Equals((RecurrencePattern) obj); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = Interval.GetHashCode(); + var hashCode = Interval.GetHashCode(); #pragma warning disable 0618 - hashCode = (hashCode * 397) ^ RestrictionType.GetHashCode(); - hashCode = (hashCode * 397) ^ EvaluationMode.GetHashCode(); + hashCode = (hashCode * 397) ^ RestrictionType.GetHashCode(); + hashCode = (hashCode * 397) ^ EvaluationMode.GetHashCode(); #pragma warning restore 0618 - 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; - } + 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) + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (obj is not RecurrencePattern r) { - base.CopyFrom(obj); - if (obj is not RecurrencePattern r) - { - return; - } + return; + } - 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; + 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; #pragma warning disable 0618 - RestrictionType = r.RestrictionType; - EvaluationMode = r.EvaluationMode; + RestrictionType = r.RestrictionType; + EvaluationMode = r.EvaluationMode; #pragma warning restore 0618 - } - - private static bool CollectionEquals(IEnumerable c1, IEnumerable c2) => c1.SequenceEqual(c2); } -} + + private static bool CollectionEquals(IEnumerable c1, IEnumerable c2) => c1.SequenceEqual(c2); +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/RequestStatus.cs b/Ical.Net/DataTypes/RequestStatus.cs index a038e8bbc..d768fe486 100644 --- a/Ical.Net/DataTypes/RequestStatus.cs +++ b/Ical.Net/DataTypes/RequestStatus.cs @@ -6,95 +6,94 @@ using System.IO; using Ical.Net.Serialization.DataTypes; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// +/// A class that represents the return status of an iCalendar request. +/// +public class RequestStatus : EncodableDataType { - /// - /// 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 { - private string _mDescription; - private string _mExtraData; - private StatusCode _mStatusCode; + get => _mDescription; + set => _mDescription = value; + } - public virtual string Description - { - get => _mDescription; - set => _mDescription = value; - } + public virtual string ExtraData + { + get => _mExtraData; + set => _mExtraData = value; + } + + public virtual StatusCode StatusCode + { + get => _mStatusCode; + set => _mStatusCode = value; + } + + public RequestStatus() { } + + public RequestStatus(string value) : this() + { + var serializer = new RequestStatusSerializer(); + CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + } - public virtual string ExtraData + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (obj is not RequestStatus rs) { - get => _mExtraData; - set => _mExtraData = value; + return; } - public virtual StatusCode StatusCode + if (rs.StatusCode != null) { - get => _mStatusCode; - set => _mStatusCode = value; + StatusCode = rs.StatusCode; } + Description = rs.Description; + ExtraData = rs.ExtraData; + } - public RequestStatus() { } + public override string ToString() + { + var serializer = new RequestStatusSerializer(); + return serializer.SerializeToString(this); + } - public RequestStatus(string value) : this() - { - var serializer = new RequestStatusSerializer(); - CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); - } + protected bool Equals(RequestStatus other) => string.Equals(_mDescription, other._mDescription) && string.Equals(_mExtraData, other._mExtraData) && + Equals(_mStatusCode, other._mStatusCode); - /// - public override void CopyFrom(ICopyable obj) + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { - base.CopyFrom(obj); - if (obj is not RequestStatus rs) - { - return; - } - - if (rs.StatusCode != null) - { - StatusCode = rs.StatusCode; - } - Description = rs.Description; - ExtraData = rs.ExtraData; + return false; } - - public override string ToString() + if (ReferenceEquals(this, obj)) { - var serializer = new RequestStatusSerializer(); - return serializer.SerializeToString(this); + return true; } - - protected bool Equals(RequestStatus other) => string.Equals(_mDescription, other._mDescription) && string.Equals(_mExtraData, other._mExtraData) && - Equals(_mStatusCode, other._mStatusCode); - - public override bool Equals(object obj) + if (obj.GetType() != GetType()) { - if (ReferenceEquals(null, obj)) - { - return false; - } - if (ReferenceEquals(this, obj)) - { - return true; - } - if (obj.GetType() != GetType()) - { - return false; - } - return Equals((RequestStatus) obj); + return false; } + return Equals((RequestStatus) obj); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = _mDescription?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ (_mExtraData?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ (_mStatusCode?.GetHashCode() ?? 0); - return hashCode; - } + 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/Ical.Net/DataTypes/StatusCode.cs b/Ical.Net/DataTypes/StatusCode.cs index 90693f6c5..ebecb3554 100644 --- a/Ical.Net/DataTypes/StatusCode.cs +++ b/Ical.Net/DataTypes/StatusCode.cs @@ -8,79 +8,78 @@ using Ical.Net.Serialization.DataTypes; using Ical.Net.Utility; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// +/// An iCalendar status code. +/// +public class StatusCode : EncodableDataType { - /// - /// An iCalendar status code. - /// - public class StatusCode : EncodableDataType - { - public int[] Parts { get; private set; } + public int[] Parts { get; private set; } - public int Primary + public int Primary + { + get { - get + if (Parts.Length > 0) { - if (Parts.Length > 0) - { - return Parts[0]; - } - return 0; + return Parts[0]; } + return 0; } + } - public int Secondary => Parts.Length > 1 - ? Parts[1] - : 0; + public int Secondary => Parts.Length > 1 + ? Parts[1] + : 0; - public int Tertiary => Parts.Length > 2 - ? Parts[2] - : 0; + public int Tertiary => Parts.Length > 2 + ? Parts[2] + : 0; - public StatusCode() { } + public StatusCode() { } - public StatusCode(int[] parts) - { - Parts = parts; - } + public StatusCode(int[] parts) + { + Parts = parts; + } - public StatusCode(string value) : this() - { - var serializer = new StatusCodeSerializer(); - CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); - } + 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 not StatusCode statusCode) return; + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (obj is not StatusCode statusCode) return; - Parts = new int[statusCode.Parts.Length]; - statusCode.Parts.CopyTo(Parts, 0); - } + Parts = new int[statusCode.Parts.Length]; + statusCode.Parts.CopyTo(Parts, 0); + } - public override string ToString() => new StatusCodeSerializer().SerializeToString(this); + public override string ToString() => new StatusCodeSerializer().SerializeToString(this); - protected bool Equals(StatusCode other) => Parts.SequenceEqual(other.Parts); + protected bool Equals(StatusCode other) => Parts.SequenceEqual(other.Parts); - public override bool Equals(object obj) + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { - if (ReferenceEquals(null, obj)) - { - return false; - } - if (ReferenceEquals(this, obj)) - { - return true; - } - if (obj.GetType() != GetType()) - { - return false; - } - return Equals((StatusCode) obj); + return false; } - - public override int GetHashCode() => CollectionHelpers.GetHashCode(Parts); + 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/Ical.Net/DataTypes/Trigger.cs b/Ical.Net/DataTypes/Trigger.cs index 585f897b1..904aceb8b 100644 --- a/Ical.Net/DataTypes/Trigger.cs +++ b/Ical.Net/DataTypes/Trigger.cs @@ -7,119 +7,118 @@ using System.IO; using Ical.Net.Serialization.DataTypes; -namespace Ical.Net.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 { - /// - /// 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 string _mRelated = TriggerRelation.Start; + private IDateTime _mDateTime; + private TimeSpan? _mDuration; + private string _mRelated = TriggerRelation.Start; - public virtual IDateTime DateTime + public virtual IDateTime DateTime + { + get => _mDateTime; + set { - get => _mDateTime; - set + _mDateTime = value; + if (_mDateTime == null) { - _mDateTime = value; - if (_mDateTime == null) - { - return; - } + return; + } - // 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 + // 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; + // DateTime and Duration are mutually exclusive + Duration = null; - // Do not allow timeless date/time values - _mDateTime.HasTime = true; - } + // Do not allow timeless date/time values + _mDateTime.HasTime = true; } + } - public virtual TimeSpan? Duration + public virtual TimeSpan? Duration + { + get => _mDuration; + set { - get => _mDuration; - set + _mDuration = value; + if (_mDuration != null) { - _mDuration = value; - if (_mDuration != null) - { - // NOTE: see above. - - // DateTime and Duration are mutually exclusive - DateTime = null; - } + // NOTE: see above. + + // DateTime and Duration are mutually exclusive + DateTime = null; } } + } - public virtual string Related - { - get => _mRelated; - set => _mRelated = value; - } + public virtual string Related + { + get => _mRelated; + set => _mRelated = value; + } + + public virtual bool IsRelative => _mDuration != null; - public virtual bool IsRelative => _mDuration != null; + public Trigger() { } - 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 Trigger(TimeSpan ts) + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (obj is not Trigger t) { - Duration = ts; + return; } - public Trigger(string value) : this() + DateTime = t.DateTime?.Copy(); + Duration = t.Duration; + Related = t.Related; + } + + protected bool Equals(Trigger other) => Equals(_mDateTime, other._mDateTime) && _mDuration.Equals(other._mDuration) && _mRelated == other._mRelated; + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { - var serializer = new TriggerSerializer(); - CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + return false; } - - /// - public override void CopyFrom(ICopyable obj) + if (ReferenceEquals(this, obj)) { - base.CopyFrom(obj); - if (obj is not Trigger t) - { - return; - } - - DateTime = t.DateTime?.Copy(); - Duration = t.Duration; - Related = t.Related; + return true; } - - protected bool Equals(Trigger other) => Equals(_mDateTime, other._mDateTime) && _mDuration.Equals(other._mDuration) && _mRelated == other._mRelated; - - public override bool Equals(object obj) + if (obj.GetType() != GetType()) { - if (ReferenceEquals(null, obj)) - { - return false; - } - if (ReferenceEquals(this, obj)) - { - return true; - } - if (obj.GetType() != GetType()) - { - return false; - } - return Equals((Trigger) obj); + return false; } + return Equals((Trigger) obj); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = _mDateTime?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ _mDuration.GetHashCode(); - hashCode = (hashCode * 397) ^ _mRelated?.GetHashCode() ?? 0; - return hashCode; - } + var hashCode = _mDateTime?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ _mDuration.GetHashCode(); + hashCode = (hashCode * 397) ^ _mRelated?.GetHashCode() ?? 0; + return hashCode; } } -} +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/UTCOffset.cs b/Ical.Net/DataTypes/UTCOffset.cs index 5da5de886..e9c2ad981 100644 --- a/Ical.Net/DataTypes/UTCOffset.cs +++ b/Ical.Net/DataTypes/UTCOffset.cs @@ -6,75 +6,74 @@ using System; using Ical.Net.Serialization.DataTypes; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// +/// Represents a time offset from UTC (Coordinated Universal Time). +/// +public class UtcOffset : EncodableDataType { - /// - /// Represents a time offset from UTC (Coordinated Universal Time). - /// - public class UtcOffset : EncodableDataType - { - public TimeSpan Offset { get; private set; } + public TimeSpan Offset { get; private set; } - public bool Positive => Offset >= TimeSpan.Zero; + public bool Positive => Offset >= TimeSpan.Zero; - public int Hours => Math.Abs(Offset.Hours); + public int Hours => Math.Abs(Offset.Hours); - public int Minutes => Math.Abs(Offset.Minutes); + public int Minutes => Math.Abs(Offset.Minutes); - public int Seconds => Math.Abs(Offset.Seconds); + public int Seconds => Math.Abs(Offset.Seconds); - public UtcOffset() { } + public UtcOffset() { } - public UtcOffset(string value) : this() - { - Offset = UtcOffsetSerializer.GetOffset(value); - } + public UtcOffset(string value) : this() + { + Offset = UtcOffsetSerializer.GetOffset(value); + } - public UtcOffset(TimeSpan ts) - { - Offset = ts; - } + public UtcOffset(TimeSpan ts) + { + Offset = ts; + } - public static implicit operator UtcOffset(TimeSpan ts) => new UtcOffset(ts); + public static implicit operator UtcOffset(TimeSpan ts) => new UtcOffset(ts); - public static explicit operator TimeSpan(UtcOffset o) => o.Offset; + 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 ToUtc(DateTime dt) => DateTime.SpecifyKind(dt.Add(-Offset), DateTimeKind.Utc); - public virtual DateTime ToLocal(DateTime dt) => DateTime.SpecifyKind(dt.Add(Offset), DateTimeKind.Local); + public virtual DateTime ToLocal(DateTime dt) => DateTime.SpecifyKind(dt.Add(Offset), DateTimeKind.Local); - protected bool Equals(UtcOffset other) => Offset == other.Offset; + protected bool Equals(UtcOffset other) => Offset == other.Offset; - public override bool Equals(object obj) + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj.GetType() != GetType()) { - if (ReferenceEquals(null, obj)) - { - return false; - } - if (ReferenceEquals(this, obj)) - { - return true; - } - if (obj.GetType() != GetType()) - { - return false; - } - return Equals((UtcOffset) obj); + return false; } + return Equals((UtcOffset) obj); + } - public override int GetHashCode() => Offset.GetHashCode(); + public override int GetHashCode() => Offset.GetHashCode(); - public override string ToString() => (Positive ? "+" : "-") + Hours.ToString("00") + Minutes.ToString("00") + (Seconds != 0 ? Seconds.ToString("00") : string.Empty); + public override string ToString() => (Positive ? "+" : "-") + Hours.ToString("00") + Minutes.ToString("00") + (Seconds != 0 ? Seconds.ToString("00") : string.Empty); - /// - public override void CopyFrom(ICopyable obj) - { - base.CopyFrom(obj); + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); - if (obj is UtcOffset o) - { - Offset = o.Offset; - } + if (obj is UtcOffset o) + { + Offset = o.Offset; } } -} +} \ No newline at end of file diff --git a/Ical.Net/DataTypes/WeekDay.cs b/Ical.Net/DataTypes/WeekDay.cs index c5a26a5f0..eb59a3eaa 100644 --- a/Ical.Net/DataTypes/WeekDay.cs +++ b/Ical.Net/DataTypes/WeekDay.cs @@ -7,83 +7,82 @@ using System.IO; using Ical.Net.Serialization.DataTypes; -namespace Ical.Net.DataTypes +namespace Ical.Net.DataTypes; + +/// +/// Represents an RFC 5545 "BYDAY" value. +/// +public class WeekDay : EncodableDataType { - /// - /// Represents an RFC 5545 "BYDAY" value. - /// - public class WeekDay : EncodableDataType - { - public virtual int Offset { get; set; } = int.MinValue; + public virtual int Offset { get; set; } = int.MinValue; - public virtual DayOfWeek DayOfWeek { get; set; } + public virtual DayOfWeek DayOfWeek { get; set; } - public WeekDay() - { - Offset = int.MinValue; - } + public WeekDay() + { + Offset = int.MinValue; + } - public WeekDay(DayOfWeek day) : this() - { - DayOfWeek = day; - } + public WeekDay(DayOfWeek day) : this() + { + DayOfWeek = day; + } - public WeekDay(DayOfWeek day, int num) : this(day) - { - Offset = num; - } + public WeekDay(DayOfWeek day, int num) : this(day) + { + Offset = num; + } + + public WeekDay(DayOfWeek day, FrequencyOccurrence type) : this(day, (int) type) { } - 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 WeekDay(string value) + public override bool Equals(object obj) + { + if (!(obj is WeekDay)) { - var serializer = new WeekDaySerializer(); - CopyFrom(serializer.Deserialize(new StringReader(value)) as ICopyable); + return false; } - public override bool Equals(object obj) - { - if (!(obj is WeekDay)) - { - return false; - } + var ds = (WeekDay) obj; + return ds.Offset == Offset && ds.DayOfWeek == DayOfWeek; + } - var ds = (WeekDay) obj; - return ds.Offset == Offset && ds.DayOfWeek == DayOfWeek; - } + public override int GetHashCode() => Offset.GetHashCode() ^ DayOfWeek.GetHashCode(); + + /// + public override void CopyFrom(ICopyable obj) + { + base.CopyFrom(obj); + if (obj is not WeekDay weekday) return; - public override int GetHashCode() => Offset.GetHashCode() ^ DayOfWeek.GetHashCode(); + Offset = weekday.Offset; + DayOfWeek = weekday.DayOfWeek; + } - /// - public override void CopyFrom(ICopyable obj) + public int CompareTo(object obj) + { + var weekday = obj switch { - base.CopyFrom(obj); - if (obj is not WeekDay weekday) return; + string => new WeekDay(obj.ToString()), + WeekDay day => day, + _ => null + }; - Offset = weekday.Offset; - DayOfWeek = weekday.DayOfWeek; + if (weekday == null) + { + throw new ArgumentException(); } - public int CompareTo(object obj) + var compare = DayOfWeek.CompareTo(weekday.DayOfWeek); + if (compare == 0) { - var weekday = obj switch - { - string => new WeekDay(obj.ToString()), - WeekDay day => day, - _ => null - }; - - if (weekday == null) - { - throw new ArgumentException(); - } - - var compare = DayOfWeek.CompareTo(weekday.DayOfWeek); - if (compare == 0) - { - compare = Offset.CompareTo(weekday.Offset); - } - return compare; + compare = Offset.CompareTo(weekday.Offset); } + return compare; } -} +} \ No newline at end of file diff --git a/Ical.Net/Evaluation/Evaluator.cs b/Ical.Net/Evaluation/Evaluator.cs index 7c696b876..073251c3e 100644 --- a/Ical.Net/Evaluation/Evaluator.cs +++ b/Ical.Net/Evaluation/Evaluator.cs @@ -9,117 +9,116 @@ using Ical.Net.DataTypes; using Ical.Net.Utility; -namespace Ical.Net.Evaluation +namespace Ical.Net.Evaluation; + +public abstract class Evaluator : IEvaluator { - public abstract class Evaluator : IEvaluator - { - private DateTime _mEvaluationStartBounds = DateTime.MaxValue; - private DateTime _mEvaluationEndBounds = DateTime.MinValue; + private DateTime _mEvaluationStartBounds = DateTime.MaxValue; + private DateTime _mEvaluationEndBounds = DateTime.MinValue; - private ICalendarObject _mAssociatedObject; - private readonly ICalendarDataType _mAssociatedDataType; + private ICalendarObject _mAssociatedObject; + private readonly ICalendarDataType _mAssociatedDataType; - protected HashSet MPeriods; + protected HashSet MPeriods; - protected Evaluator() - { - Initialize(); - } + protected Evaluator() + { + Initialize(); + } - protected Evaluator(ICalendarObject associatedObject) - { - _mAssociatedObject = associatedObject; + protected Evaluator(ICalendarObject associatedObject) + { + _mAssociatedObject = associatedObject; - Initialize(); - } + Initialize(); + } - protected Evaluator(ICalendarDataType dataType) - { - _mAssociatedDataType = dataType; + protected Evaluator(ICalendarDataType dataType) + { + _mAssociatedDataType = dataType; - Initialize(); - } + Initialize(); + } - private void Initialize() - { - Calendar = CultureInfo.CurrentCulture.Calendar; - MPeriods = new HashSet(); - } + 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 IDateTime ConvertToIDateTime(DateTime dt, IDateTime referenceDate) + protected void IncrementDate(ref DateTime dt, RecurrencePattern pattern, int interval) + { + // FIXME: use a more specific exception. + if (interval == 0) { - IDateTime newDt = new CalDateTime(dt, referenceDate.TzId); - newDt.AssociateWith(referenceDate); - return newDt; + throw new Exception("Cannot evaluate with an interval of zero. Please use an interval other than zero."); } - protected void IncrementDate(ref DateTime dt, RecurrencePattern pattern, int interval) + 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. - 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."); - } + 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 => _mEvaluationStartBounds; - set => _mEvaluationStartBounds = value; - } + public System.Globalization.Calendar Calendar { get; private set; } - public virtual DateTime EvaluationEndBounds - { - get => _mEvaluationEndBounds; - set => _mEvaluationEndBounds = value; - } + public virtual DateTime EvaluationStartBounds + { + get => _mEvaluationStartBounds; + set => _mEvaluationStartBounds = value; + } - public virtual ICalendarObject AssociatedObject - { - get => _mAssociatedObject ?? _mAssociatedDataType?.AssociatedObject; - protected set => _mAssociatedObject = value; - } + public virtual DateTime EvaluationEndBounds + { + get => _mEvaluationEndBounds; + set => _mEvaluationEndBounds = value; + } - public virtual HashSet Periods => MPeriods; + public virtual ICalendarObject AssociatedObject + { + get => _mAssociatedObject ?? _mAssociatedDataType?.AssociatedObject; + protected set => _mAssociatedObject = value; + } - public virtual void Clear() - { - _mEvaluationStartBounds = DateTime.MaxValue; - _mEvaluationEndBounds = DateTime.MinValue; - MPeriods.Clear(); - } + public virtual HashSet Periods => MPeriods; - public abstract HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults); + 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/Ical.Net/Evaluation/EventEvaluator.cs b/Ical.Net/Evaluation/EventEvaluator.cs index 803845305..d0e07c98f 100644 --- a/Ical.Net/Evaluation/EventEvaluator.cs +++ b/Ical.Net/Evaluation/EventEvaluator.cs @@ -9,58 +9,57 @@ using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; -namespace Ical.Net.Evaluation +namespace Ical.Net.Evaluation; + +public class EventEvaluator : RecurringEvaluator { - public class EventEvaluator : RecurringEvaluator + protected CalendarEvent CalendarEvent { - protected CalendarEvent CalendarEvent - { - get => Recurrable as CalendarEvent; - set => Recurrable = value; - } - - public EventEvaluator(CalendarEvent evt) : base(evt) { } + get => Recurrable as CalendarEvent; + set => Recurrable = value; + } - /// - /// 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); + public EventEvaluator(CalendarEvent evt) : base(evt) { } - foreach (var period in Periods) - { - period.Duration = CalendarEvent.Duration; - period.EndTime = period.Duration == default - ? period.StartTime - : period.StartTime.Add(CalendarEvent.Duration); - } + /// + /// 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); - // Ensure each period has a duration - foreach (var period in Periods.Where(p => p.EndTime == null)) - { - period.Duration = CalendarEvent.Duration; - period.EndTime = period.Duration == default - ? period.StartTime - : period.StartTime.Add(CalendarEvent.Duration); - } + foreach (var period in Periods) + { + period.Duration = CalendarEvent.Duration; + period.EndTime = period.Duration == default + ? period.StartTime + : period.StartTime.Add(CalendarEvent.Duration); + } - return Periods; + // Ensure each period has a duration + foreach (var period in Periods.Where(p => p.EndTime == null)) + { + period.Duration = CalendarEvent.Duration; + period.EndTime = period.Duration == default + ? period.StartTime + : period.StartTime.Add(CalendarEvent.Duration); } + + return Periods; } -} +} \ No newline at end of file diff --git a/Ical.Net/Evaluation/IEvaluator.cs b/Ical.Net/Evaluation/IEvaluator.cs index 86713348c..9f1519359 100644 --- a/Ical.Net/Evaluation/IEvaluator.cs +++ b/Ical.Net/Evaluation/IEvaluator.cs @@ -7,72 +7,71 @@ using System.Collections.Generic; using Ical.Net.DataTypes; -namespace Ical.Net.Evaluation +namespace Ical.Net.Evaluation; + +public interface IEvaluator { - public interface IEvaluator - { - /// - /// The system calendar that governs the evaluation rules. - /// - System.Globalization.Calendar Calendar { get; } + /// + /// 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 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; } + /// + /// 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 a list of periods collected so far during + /// the evaluation process. + /// + HashSet Periods { get; } - /// - /// Gets the object associated with this evaluator. - /// - ICalendarObject AssociatedObject { 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(); + /// + /// 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); - } -} + /// + /// 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/Ical.Net/Evaluation/PeriodListEvaluator.cs b/Ical.Net/Evaluation/PeriodListEvaluator.cs index 8b6874f90..5d5b4f78a 100644 --- a/Ical.Net/Evaluation/PeriodListEvaluator.cs +++ b/Ical.Net/Evaluation/PeriodListEvaluator.cs @@ -7,34 +7,33 @@ using System.Collections.Generic; using Ical.Net.DataTypes; -namespace Ical.Net.Evaluation +namespace Ical.Net.Evaluation; + +public class PeriodListEvaluator : Evaluator { - 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) { - private readonly PeriodList _mPeriodList; + var periods = new HashSet(); - public PeriodListEvaluator(PeriodList rdt) + if (includeReferenceDateInResults) { - _mPeriodList = rdt; + Period p = new Period(referenceDate); + periods.Add(p); } - public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + if (periodEnd < periodStart) { - var periods = new HashSet(); - - if (includeReferenceDateInResults) - { - Period p = new Period(referenceDate); - periods.Add(p); - } - - if (periodEnd < periodStart) - { - return periods; - } - - periods.UnionWith(_mPeriodList); return periods; } + + periods.UnionWith(_mPeriodList); + return periods; } -} +} \ No newline at end of file diff --git a/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs b/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs index 8eaf3584a..32b2967ba 100644 --- a/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs +++ b/Ical.Net/Evaluation/RecurrencePatternEvaluator.cs @@ -10,948 +10,947 @@ using Ical.Net.DataTypes; using Ical.Net.Utility; -namespace Ical.Net.Evaluation +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 { - /// - /// 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 + private const int _maxIncrementCount = 1000; + + protected RecurrencePattern Pattern { get; set; } + + public RecurrencePatternEvaluator(RecurrencePattern pattern) { - private const int _maxIncrementCount = 1000; + Pattern = pattern; + } - protected RecurrencePattern Pattern { get; set; } + private RecurrencePattern ProcessRecurrencePattern(IDateTime referenceDate) + { + var r = new RecurrencePattern(); + r.CopyFrom(Pattern); - public RecurrencePatternEvaluator(RecurrencePattern pattern) + // Convert the UNTIL value to one that matches the same time information as the reference date + if (r.Until != DateTime.MinValue) { - Pattern = pattern; + r.Until = DateUtil.MatchTimeZone(referenceDate, new CalDateTime(r.Until, referenceDate.TzId)).Value; } - private RecurrencePattern ProcessRecurrencePattern(IDateTime referenceDate) + if (referenceDate.HasTime) { - 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) + 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.Until = DateUtil.MatchTimeZone(referenceDate, new CalDateTime(r.Until, referenceDate.TzId)).Value; + r.BySecond.Add(referenceDate.Second); } - - if (referenceDate.HasTime) + 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 */) { - 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); - } + r.ByMinute.Add(referenceDate.Minute); } - else + 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 */) { - // The BYSECOND, BYMINUTE and BYHOUR rule parts MUST NOT be specified - // when the associated "DTSTART" property has a DATE value type. - // These rule parts MUST be ignored in RECUR value that violate the - // above requirement(e.g., generated by applications that pre - date - // this revision of iCalendar). - r.BySecond.Clear(); - r.BySecond.Add(0); - r.ByMinute.Clear(); - r.ByMinute.Add(0); - r.ByHour.Clear(); - r.ByHour.Add(0); - } - - // 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); - } + r.ByHour.Add(referenceDate.Hour); } - - return r; } -#pragma warning disable 0618 - private void EnforceEvaluationRestrictions(RecurrencePattern pattern) + else { - RecurrenceEvaluationModeType? evaluationMode = pattern.EvaluationMode; - RecurrenceRestrictionType? evaluationRestriction = pattern.RestrictionType; + // The BYSECOND, BYMINUTE and BYHOUR rule parts MUST NOT be specified + // when the associated "DTSTART" property has a DATE value type. + // These rule parts MUST be ignored in RECUR value that violate the + // above requirement(e.g., generated by applications that pre - date + // this revision of iCalendar). + r.BySecond.Clear(); + r.BySecond.Add(0); + r.ByMinute.Clear(); + r.ByMinute.Add(0); + r.ByHour.Clear(); + r.ByHour.Add(0); + } - if (evaluationRestriction != RecurrenceRestrictionType.NoRestriction) + // 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)) { - 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 ArgumentException(); - } - } - break; - case FrequencyType.Minutely: - { - switch (evaluationRestriction) - { - case RecurrenceRestrictionType.RestrictMinutely: - case RecurrenceRestrictionType.RestrictHourly: - throw new ArgumentException(); - } - } - break; - case FrequencyType.Hourly: - { - switch (evaluationRestriction) - { - case RecurrenceRestrictionType.RestrictHourly: - throw new ArgumentException(); - } - } - break; - } - break; - } - } - } -#pragma warning 0618 restore - /// - /// Returns a list of start dates in the specified period represented by this recurrence pattern. - /// This method includes a base date argument, which indicates the start of the first 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); - - // optimize the start time for selecting candidates - // (only applicable where a COUNT is not specified) - if (pattern.Count == int.MinValue) - { - var incremented = seedCopy; - while (incremented < periodStart) - { - seedCopy = incremented; - IncrementDate(ref incremented, pattern, pattern.Interval); - } + r.ByDay.Add(new WeekDay(referenceDate.DayOfWeek)); } - var expandBehavior = RecurrenceUtil.GetExpandBehaviorList(pattern); - - var noCandidateIncrementCount = 0; - var candidate = DateTime.MinValue; - while (maxCount < 0 || dates.Count < maxCount) + // 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) { - 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 >= pattern.Count) - { - break; - } + r.ByMonthDay.Add(referenceDate.Day); + } - //No need to continue if the seed is after the periodEnd - if (seedCopy > periodEnd) - { - break; - } + // 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); + } + } - var candidates = GetCandidates(seedCopy, pattern, expandBehavior); - if (candidates.Count > 0) - { - noCandidateIncrementCount = 0; + return r; + } +#pragma warning disable 0618 + private void EnforceEvaluationRestrictions(RecurrencePattern pattern) + { + RecurrenceEvaluationModeType? evaluationMode = pattern.EvaluationMode; + RecurrenceRestrictionType? evaluationRestriction = pattern.RestrictionType; - foreach (var t in candidates.OrderBy(c => c).Where(t => t >= originalDate)) + if (evaluationRestriction != RecurrenceRestrictionType.NoRestriction) + { + switch (evaluationMode) + { + case RecurrenceEvaluationModeType.AdjustAutomatically: + switch (pattern.Frequency) { - candidate = t; - - // candidates MAY occur before periodStart - // For example, FREQ=YEARLY;BYWEEKNO=1 could return dates - // from the previous year. - // - // exclude candidates that start at the same moment as periodEnd if the period is a range but keep them if targeting a specific moment - if (pattern.Count >= 1 && dates.Count >= pattern.Count) + case FrequencyType.Secondly: { - break; + 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; + } } - - if ((candidate >= periodEnd && periodStart != periodEnd) || candidate > periodEnd && periodStart == periodEnd) + break; + case FrequencyType.Minutely: { - continue; + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.RestrictMinutely: + pattern.Frequency = FrequencyType.Hourly; + break; + case RecurrenceRestrictionType.RestrictHourly: + pattern.Frequency = FrequencyType.Daily; + break; + } } - - if (pattern.Until == DateTime.MinValue || candidate <= pattern.Until) + break; + case FrequencyType.Hourly: { - dates.Add(candidate); + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.RestrictHourly: + pattern.Frequency = FrequencyType.Daily; + break; + } } + break; } - } - else - { - noCandidateIncrementCount++; - if (_maxIncrementCount > 0 && noCandidateIncrementCount > _maxIncrementCount) + break; + case RecurrenceEvaluationModeType.ThrowException: + case RecurrenceEvaluationModeType.Default: + switch (pattern.Frequency) { - break; + case FrequencyType.Secondly: + { + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.Default: + case RecurrenceRestrictionType.RestrictSecondly: + case RecurrenceRestrictionType.RestrictMinutely: + case RecurrenceRestrictionType.RestrictHourly: + throw new ArgumentException(); + } + } + break; + case FrequencyType.Minutely: + { + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.RestrictMinutely: + case RecurrenceRestrictionType.RestrictHourly: + throw new ArgumentException(); + } + } + break; + case FrequencyType.Hourly: + { + switch (evaluationRestriction) + { + case RecurrenceRestrictionType.RestrictHourly: + throw new ArgumentException(); + } + } + break; } - } - - IncrementDate(ref seedCopy, pattern, pattern.Interval); + break; } - - return dates; - } - - /// - /// Returns a list of possible dates generated from the applicable BY* rules, using the specified date as a seed. - /// - /// The seed date. - /// - /// - /// A list of possible dates. - private List GetCandidates(DateTime date, RecurrencePattern pattern, bool?[] expandBehaviors) - { - var dates = new List { 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; } + } +#pragma warning 0618 restore + /// + /// Returns a list of start dates in the specified period represented by this recurrence pattern. + /// This method includes a base date argument, which indicates the start of the first 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); - /// - /// Applies BYSETPOS rules to . Valid positions are from 1 to the size of the date list. Invalid - /// positions are ignored. - /// - /// The list of dates to which the BYSETPOS rules will be applied. - /// - private List ApplySetPosRules(List dates, RecurrencePattern pattern) + // optimize the start time for selecting candidates + // (only applicable where a COUNT is not specified) + if (pattern.Count == int.MinValue) { - // return if no SETPOS rules specified.. - if (pattern.BySetPosition.Count == 0) + var incremented = seedCopy; + while (incremented < periodStart) { - return dates; + seedCopy = incremented; + IncrementDate(ref incremented, pattern, pattern.Interval); } - - // sort the list before processing.. - dates.Sort(); - - var size = dates.Count; - var setPosDates = pattern.BySetPosition - .Where(p => p > 0 && p <= size || p < 0 && p >= -size) //Protect against out of range access - .Select(p => p > 0 && p <= size - ? dates[p - 1] - : dates[size + p]) - .ToList(); - 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. - /// - /// The list of dates to which the BYMONTH rules will be applied. - /// - /// - /// The modified list of dates after applying the BYMONTH rules. - private List GetMonthVariants(List dates, RecurrencePattern pattern, bool? expand) + var expandBehavior = RecurrenceUtil.GetExpandBehaviorList(pattern); + + var noCandidateIncrementCount = 0; + var candidate = DateTime.MinValue; + while (maxCount < 0 || dates.Count < maxCount) { - if (expand == null || pattern.ByMonth.Count == 0) + if (pattern.Until != DateTime.MinValue && candidate != DateTime.MinValue && candidate > pattern.Until) { - return dates; + break; } - if (expand.Value) + if (candidate != DateTime.MinValue && candidate > periodEnd) { - // Expand behavior - return dates - .SelectMany(d => pattern.ByMonth.Select(month => d.AddMonths(month - d.Month))) - .ToList(); + break; } - // Limit behavior - var dateSet = new HashSet(dates); - dateSet.ExceptWith(dates.Where(date => pattern.ByMonth.All(t => t != date.Month))); - return dateSet.ToList(); - } - - /// - /// 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. - /// - /// The list of dates to which the BYWEEKNO rules will be applied. - /// The modified list of dates after applying the BYWEEKNO rules. - private List GetWeekNoVariants(List dates, RecurrencePattern pattern, bool? expand) - { - if (expand == null || pattern.ByWeekNo.Count == 0) + if (pattern.Count >= 1 && dates.Count >= pattern.Count) { - return dates; + break; } - if (!expand.Value) + //No need to continue if the seed is after the periodEnd + if (seedCopy > periodEnd) { - return new List(); + break; } - // Expand behavior - var weekNoDates = new List(); - foreach (var t in dates) + var candidates = GetCandidates(seedCopy, pattern, expandBehavior); + if (candidates.Count > 0) { - foreach (var weekNo in pattern.ByWeekNo) + noCandidateIncrementCount = 0; + + foreach (var t in candidates.OrderBy(c => c).Where(t => t >= originalDate)) { - var date = t; - // Determine our current week number - var currWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); - while (currWeekNo > weekNo) + candidate = t; + + // candidates MAY occur before periodStart + // For example, FREQ=YEARLY;BYWEEKNO=1 could return dates + // from the previous year. + // + // exclude candidates that start at the same moment as periodEnd if the period is a range but keep them if targeting a specific moment + if (pattern.Count >= 1 && dates.Count >= pattern.Count) { - // 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.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); + break; } - // 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) + if ((candidate >= periodEnd && periodStart != periodEnd) || candidate > periodEnd && periodStart == periodEnd) { - date = date.AddDays(-1); + continue; } - for (var k = 0; k < 7; k++) + if (pattern.Until == DateTime.MinValue || candidate <= pattern.Until) { - weekNoDates.Add(date); - date = date.AddDays(1); + dates.Add(candidate); } } } - return weekNoDates; + else + { + noCandidateIncrementCount++; + if (_maxIncrementCount > 0 && noCandidateIncrementCount > _maxIncrementCount) + { + break; + } + } + + IncrementDate(ref seedCopy, pattern, pattern.Interval); + } + + return dates; + } + + /// + /// Returns a list of possible dates generated from the applicable BY* rules, using the specified date as a seed. + /// + /// The seed date. + /// + /// + /// A list of possible dates. + private List GetCandidates(DateTime date, RecurrencePattern pattern, bool?[] expandBehaviors) + { + var dates = new List { 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 . Valid positions are from 1 to the size of the date list. Invalid + /// positions are ignored. + /// + /// The list of dates to which the BYSETPOS rules will be applied. + /// + 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 size = dates.Count; + var setPosDates = pattern.BySetPosition + .Where(p => p > 0 && p <= size || p < 0 && p >= -size) //Protect against out of range access + .Select(p => p > 0 && p <= size + ? dates[p - 1] + : dates[size + p]) + .ToList(); + 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. + /// + /// The list of dates to which the BYMONTH rules will be applied. + /// + /// + /// The modified list of dates after applying the BYMONTH rules. + private List GetMonthVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByMonth.Count == 0) + { + return dates; } - /// - /// 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. - /// - /// The list of dates to which the BYYEARDAY rules will be applied. - /// The modified list of dates after applying the BYYEARDAY rules. - private List GetYearDayVariants(List dates, RecurrencePattern pattern, bool? expand) + if (expand.Value) { - if (expand == null || pattern.ByYearDay.Count == 0) - { - return dates; - } + // Expand behavior + return dates + .SelectMany(d => pattern.ByMonth.Select(month => d.AddMonths(month - d.Month))) + .ToList(); + } + + // Limit behavior + var dateSet = new HashSet(dates); + dateSet.ExceptWith(dates.Where(date => pattern.ByMonth.All(t => t != date.Month))); + return dateSet.ToList(); + } + + /// + /// 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. + /// + /// The list of dates to which the BYWEEKNO rules will be applied. + /// The modified list of dates after applying the BYWEEKNO rules. + private List GetWeekNoVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByWeekNo.Count == 0) + { + return dates; + } + + if (!expand.Value) + { + return new List(); + } - if (expand.Value) + // Expand behavior + var weekNoDates = new List(); + foreach (var t in dates) + { + foreach (var weekNo in pattern.ByWeekNo) { - var yearDayDates = new List(dates.Count); - foreach (var date in dates) + var date = t; + // Determine our current week number + var currWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); + while (currWeekNo > weekNo) { - 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)) - // Ignore the BY values that don't fit into the current year (i.e. +-366 in non-leap-years). - .Where(d => d.Year == date1.Year)); + // 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.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); } - 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); + // Move ahead to the correct week of the year + date = date.AddDays((weekNo - currWeekNo) * 7); - if (newDate.Date == date.Date) - { - goto Next; - } + // Step backward single days until we're at the correct DayOfWeek + while (date.DayOfWeek != pattern.FirstDayOfWeek) + { + date = date.AddDays(-1); } - dates.RemoveAt(i); - Next: - ; + 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. + /// + /// The list of dates to which the BYYEARDAY rules will be applied. + /// The modified list of dates after applying the BYYEARDAY rules. + private List GetYearDayVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByYearDay.Count == 0) + { 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. - /// - /// The list of dates to which the BYMONTHDAY rules will be applied. - /// The modified list of dates after applying the BYMONTHDAY rules. - private List GetMonthDayVariants(List dates, RecurrencePattern pattern, bool? expand) + if (expand.Value) { - if (expand == null || pattern.ByMonthDay.Count == 0) - { - return dates; - } - - if (expand.Value) + var yearDayDates = new List(dates.Count); + foreach (var date in dates) { - var monthDayDates = new List(); - foreach (var date in dates) - { - 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; + 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)) + // Ignore the BY values that don't fit into the current year (i.e. +-366 in non-leap-years). + .Where(d => d.Year == date1.Year)); } - // Limit behavior - for (var i = dates.Count - 1; i >= 0; i--) + 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 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 + ")"); - } + var yearDay = pattern.ByYearDay[j]; - // Account for positive or negative numbers - var newDate = monthDay > 0 - ? date.AddDays(-date.Day + monthDay) - : date.AddDays(-date.Day + 1).AddMonths(1).AddDays(monthDay); + var newDate = yearDay > 0 + ? date.AddDays(-date.DayOfYear + yearDay) + : date.AddDays(-date.DayOfYear + 1).AddYears(1).AddDays(yearDay); - if (newDate.Day.Equals(date.Day)) - { - goto Next; - } + if (newDate.Date == date.Date) + { + goto Next; } + } + dates.RemoveAt(i); Next: - dates.RemoveAt(i); - } + ; + } + + 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. + /// + /// The list of dates to which the BYMONTHDAY rules will be applied. + /// The modified list of dates after applying the BYMONTHDAY rules. + private List GetMonthDayVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByMonthDay.Count == 0) + { 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. - /// - /// The list of dates to which BYDAY rules will be applied. - /// The modified list of dates after applying BYDAY rules, or the original list if no BYDAY rules are specified. - private List GetDayVariants(List dates, RecurrencePattern pattern, bool? expand) + if (expand.Value) { - if (expand == null || pattern.ByDay.Count == 0) - { - return dates; + var monthDayDates = new List(); + foreach (var date in dates) + { + 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) + ); } - - if (expand.Value) + 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++) { - // Expand behavior - var weekDayDates = new List(); - foreach (var date in dates) + var monthDay = pattern.ByMonthDay[j]; + + var daysInMonth = Calendar.GetDaysInMonth(date.Year, date.Month); + if (Math.Abs(monthDay) > daysInMonth) { - foreach (var day in pattern.ByDay) - { - weekDayDates.AddRange(GetAbsWeekDays(date, day, pattern)); - } + throw new ArgumentException("Invalid day of month: " + date + " (day " + monthDay + ")"); } - return weekDayDates; - } + // Account for positive or negative numbers + var newDate = monthDay > 0 + ? date.AddDays(-date.Day + monthDay) + : date.AddDays(-date.Day + 1).AddMonths(1).AddDays(monthDay); - // Limit behavior - for (var i = dates.Count - 1; i >= 0; i--) - { - var date = dates[i]; - for (var j = 0; j < pattern.ByDay.Count; j++) + if (newDate.Day.Equals(date.Day)) { - 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; - } - } + goto Next; } - dates.RemoveAt(i); - Next: - ; } - return dates; + Next: + dates.RemoveAt(i); } - /// - /// Returns a list of applicable dates corresponding to the specified week day in accordance with the frequency - /// specified by this recurrence rule. - /// - /// The date to start the evaluation from. - /// The week day to evaluate. - /// A list of applicable dates. - private List GetAbsWeekDays(DateTime date, WeekDay weekDay, RecurrencePattern pattern) + 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. + /// + /// The list of dates to which BYDAY rules will be applied. + /// The modified list of dates after applying BYDAY rules, or the original list if no BYDAY rules are specified. + private List GetDayVariants(List dates, RecurrencePattern pattern, bool? expand) + { + if (expand == null || pattern.ByDay.Count == 0) { - var days = new List(); + return dates; + } - var dayOfWeek = weekDay.DayOfWeek; - if (pattern.Frequency == FrequencyType.Daily) + if (expand.Value) + { + // Expand behavior + var weekDayDates = new List(); + foreach (var date in dates) { - if (date.DayOfWeek == dayOfWeek) + foreach (var day in pattern.ByDay) { - days.Add(date); + weekDayDates.AddRange(GetAbsWeekDays(date, day, pattern)); } } - else if (pattern.Frequency == FrequencyType.Weekly || pattern.ByWeekNo.Count > 0) - { - var weekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); - // construct a list of possible week days.. - while (date.DayOfWeek != dayOfWeek) - { - date = date.AddDays(1); - } - - var nextWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); - var currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); + return weekDayDates; + } - //When we manage weekly recurring pattern and we have boundary case: - //Weekdays: Dec 31, Jan 1, Feb 1, Mar 1, Apr 1, May 1, June 1, Dec 31 - It's the 53th week of the year, but all another are 1st week number. - //So we need an EXRULE for this situation, but only for weekly events - while (currentWeekNo == weekNo || (nextWeekNo < weekNo && currentWeekNo == nextWeekNo && pattern.Frequency == FrequencyType.Weekly)) + // 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 ((pattern.ByWeekNo.Count == 0 || pattern.ByWeekNo.Contains(currentWeekNo)) - && (pattern.ByMonth.Count == 0 || pattern.ByMonth.Contains(date.Month))) + // If no offset is specified, simply test the day of week! + // FIXME: test with offset... + if (date.DayOfWeek.Equals(weekDay.DayOfWeek)) { - days.Add(date); + goto Next; } - - date = date.AddDays(7); - currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); } } - else if (pattern.Frequency == FrequencyType.Monthly || pattern.ByMonth.Count > 0) - { - var month = date.Month; + dates.RemoveAt(i); + Next: + ; + } - // construct a list of possible month days.. - date = date.AddDays(-date.Day + 1); - while (date.DayOfWeek != dayOfWeek) - { - date = date.AddDays(1); - } + return dates; + } - while (date.Month == month) - { - var currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); + /// + /// Returns a list of applicable dates corresponding to the specified week day in accordance with the frequency + /// specified by this recurrence rule. + /// + /// The date to start the evaluation from. + /// The week day to evaluate. + /// A list of applicable dates. + private List GetAbsWeekDays(DateTime date, WeekDay weekDay, RecurrencePattern pattern) + { + var days = new List(); - if ((pattern.ByWeekNo.Count == 0 || pattern.ByWeekNo.Contains(currentWeekNo)) - && (pattern.ByMonth.Count == 0 || pattern.ByMonth.Contains(date.Month))) - { - days.Add(date); - } - date = date.AddDays(7); - } + var dayOfWeek = weekDay.DayOfWeek; + if (pattern.Frequency == FrequencyType.Daily) + { + if (date.DayOfWeek == dayOfWeek) + { + days.Add(date); } - else if (pattern.Frequency == FrequencyType.Yearly) + } + else if (pattern.Frequency == FrequencyType.Weekly || pattern.ByWeekNo.Count > 0) + { + var weekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); + + // construct a list of possible week days.. + while (date.DayOfWeek != dayOfWeek) { - var year = date.Year; + date = date.AddDays(1); + } - // construct a list of possible year days.. - date = date.AddDays(-date.DayOfYear + 1); - while (date.DayOfWeek != dayOfWeek) - { - date = date.AddDays(1); - } + var nextWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); + var currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); - while (date.Year == year) + //When we manage weekly recurring pattern and we have boundary case: + //Weekdays: Dec 31, Jan 1, Feb 1, Mar 1, Apr 1, May 1, June 1, Dec 31 - It's the 53th week of the year, but all another are 1st week number. + //So we need an EXRULE for this situation, but only for weekly events + while (currentWeekNo == weekNo || (nextWeekNo < weekNo && currentWeekNo == nextWeekNo && pattern.Frequency == FrequencyType.Weekly)) + { + if ((pattern.ByWeekNo.Count == 0 || pattern.ByWeekNo.Contains(currentWeekNo)) + && (pattern.ByMonth.Count == 0 || pattern.ByMonth.Contains(date.Month))) { days.Add(date); - date = date.AddDays(7); } + + date = date.AddDays(7); + currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); } - return GetOffsetDates(days, weekDay.Offset); } - - /// - /// Returns a single-element sublist containing the element of at . - /// Valid offsets are from 1 to the size of the list. If an invalid offset is supplied, all elements from - /// are added to result. - /// - /// The list from which to extract the element. - /// The position of the element to extract. - private List GetOffsetDates(List dates, int offset) + else if (pattern.Frequency == FrequencyType.Monthly || pattern.ByMonth.Count > 0) { - if (offset == int.MinValue) - { - return dates; - } + var month = date.Month; - var offsetDates = new List(); - var size = dates.Count; - if (offset < 0 && offset >= -size) + // construct a list of possible month days.. + date = date.AddDays(-date.Day + 1); + while (date.DayOfWeek != dayOfWeek) { - offsetDates.Add(dates[size + offset]); + date = date.AddDays(1); } - else if (offset > 0 && offset <= size) + + while (date.Month == month) { - offsetDates.Add(dates[offset - 1]); + var currentWeekNo = Calendar.GetIso8601WeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, pattern.FirstDayOfWeek); + + if ((pattern.ByWeekNo.Count == 0 || pattern.ByWeekNo.Contains(currentWeekNo)) + && (pattern.ByMonth.Count == 0 || pattern.ByMonth.Contains(date.Month))) + { + days.Add(date); + } + date = date.AddDays(7); } - 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. - /// - /// The list of dates to which the BYHOUR rules will be applied. - /// - /// - /// The modified list of dates after applying the BYHOUR rules. - private List GetHourVariants(List dates, RecurrencePattern pattern, bool? expand) + else if (pattern.Frequency == FrequencyType.Yearly) { - if (expand == null || pattern.ByHour.Count == 0) + var year = date.Year; + + // construct a list of possible year days.. + date = date.AddDays(-date.DayOfYear + 1); + while (date.DayOfWeek != dayOfWeek) { - return dates; + date = date.AddDays(1); } - if (expand.Value) + while (date.Year == year) { - // Expand behavior - var hourlyDates = new List(); - 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; + days.Add(date); + date = date.AddDays(7); } - // Limit behavior - for (var i = dates.Count - 1; i >= 0; i--) + } + return GetOffsetDates(days, weekDay.Offset); + } + + /// + /// Returns a single-element sublist containing the element of at . + /// Valid offsets are from 1 to the size of the list. If an invalid offset is supplied, all elements from + /// are added to result. + /// + /// The list from which to extract the element. + /// The position of the element to extract. + private List GetOffsetDates(List dates, int offset) + { + if (offset == int.MinValue) + { + return dates; + } + + var offsetDates = new List(); + 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. + /// + /// The list of dates to which the BYHOUR rules will be applied. + /// + /// + /// The modified list of dates after applying the BYHOUR rules. + 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(); + 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]; - if (date.Hour == hour) - { - goto Next; - } + date = date.AddHours(-date.Hour + hour); + hourlyDates.Add(date); } - // Remove unmatched dates - dates.RemoveAt(i); - Next: - ; } - return dates; + return hourlyDates; } - - /// - /// 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. - /// - /// The list of dates to which the BYMINUTE rules will be applied. - /// - /// - /// The modified list of dates after applying the BYMINUTE rules. - private List GetMinuteVariants(List dates, RecurrencePattern pattern, bool? expand) + // Limit behavior + for (var i = dates.Count - 1; i >= 0; i--) { - if (expand == null || pattern.ByMinute.Count == 0) + var date = dates[i]; + for (var j = 0; j < pattern.ByHour.Count; j++) { - return dates; - } - - if (expand.Value) - { - // Expand behavior - var minutelyDates = new List(); - for (var i = 0; i < dates.Count; i++) + var hour = pattern.ByHour[j]; + if (date.Hour == hour) { - 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); - } + goto Next; } - return minutelyDates; } - // Limit behavior - for (var i = dates.Count - 1; i >= 0; i--) + // 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. + /// + /// The list of dates to which the BYMINUTE rules will be applied. + /// + /// + /// The modified list of dates after applying the BYMINUTE rules. + 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(); + 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]; - if (date.Minute == minute) - { - goto Next; - } + date = date.AddMinutes(-date.Minute + minute); + minutelyDates.Add(date); } - // Remove unmatched dates - dates.RemoveAt(i); - Next: - ; } - return dates; + return minutelyDates; } - - /// - /// 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. - /// - /// The list of dates to which the BYSECOND rules will be applied. - /// - /// - /// The modified list of dates after applying the BYSECOND rules. - private List GetSecondVariants(List dates, RecurrencePattern pattern, bool? expand) + // Limit behavior + for (var i = dates.Count - 1; i >= 0; i--) { - if (expand == null || pattern.BySecond.Count == 0) + var date = dates[i]; + for (var j = 0; j < pattern.ByMinute.Count; j++) { - return dates; - } - - if (expand.Value) - { - // Expand behavior - var secondlyDates = new List(); - for (var i = 0; i < dates.Count; i++) + var minute = pattern.ByMinute[j]; + if (date.Minute == minute) { - 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); - } + goto Next; } - return secondlyDates; } - // Limit behavior - for (var i = dates.Count - 1; i >= 0; i--) + // 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. + /// + /// The list of dates to which the BYSECOND rules will be applied. + /// + /// + /// The modified list of dates after applying the BYSECOND rules. + 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(); + 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]; - if (date.Second == second) - { - goto Next; - } + date = date.AddSeconds(-date.Second + second); + secondlyDates.Add(date); } - // Remove unmatched dates - dates.RemoveAt(i); - Next: - ; } - return dates; + return secondlyDates; } - - private Period CreatePeriod(DateTime dt, IDateTime referenceDate) + // Limit behavior + for (var i = dates.Count - 1; i >= 0; i--) { - // Turn each resulting date/time into an IDateTime and associate it - // with the reference date. - IDateTime newDt = new CalDateTime(dt, referenceDate.TzId); + 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; + } - // NOTE: fixes bug #2938007 - hasTime missing - newDt.HasTime = referenceDate.HasTime; + 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); - newDt.AssociateWith(referenceDate); + // NOTE: fixes bug #2938007 - hasTime missing + newDt.HasTime = referenceDate.HasTime; - // Create a period from the new date/time. - return new Period(newDt); - } + newDt.AssociateWith(referenceDate); - /// - /// Evaluate the occurrences of this recurrence pattern. - /// - /// The reference date, i.e. DTSTART. - /// Start (incl.) of the period occurrences are generated for. - /// End (excl.) of the period occurrences are generated for. - /// Whether the referenceDate itself should be returned. Ignored as the reference data MUST equal the first occurrence of an RRULE. - /// - public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + // Create a period from the new date/time. + return new Period(newDt); + } + + /// + /// Evaluate the occurrences of this recurrence pattern. + /// + /// The reference date, i.e. DTSTART. + /// Start (incl.) of the period occurrences are generated for. + /// End (excl.) of the period occurrences are generated for. + /// Whether the referenceDate itself should be returned. Ignored as the reference data MUST equal the first occurrence of an RRULE. + /// + public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + { + if (Pattern.Frequency != FrequencyType.None && Pattern.Frequency < FrequencyType.Daily && !referenceDate.HasTime) { - if (Pattern.Frequency != FrequencyType.None && Pattern.Frequency < FrequencyType.Daily && !referenceDate.HasTime) - { - // This case is not defined by RFC 5545. We handle it by evaluating the rule - // as if referenceDate had a time (i.e. set to midnight). + // This case is not defined by RFC 5545. We handle it by evaluating the rule + // as if referenceDate had a time (i.e. set to midnight). - referenceDate = referenceDate.Copy(); - referenceDate.HasTime = true; - } + referenceDate = referenceDate.Copy(); + referenceDate.HasTime = true; + } - // Create a recurrence pattern suitable for use during evaluation. - var pattern = ProcessRecurrencePattern(referenceDate); + // Create a recurrence pattern suitable for use during evaluation. + var pattern = ProcessRecurrencePattern(referenceDate); - // Enforce evaluation restrictions on the pattern. - EnforceEvaluationRestrictions(pattern); - Periods.Clear(); + // Enforce evaluation restrictions on the pattern. + EnforceEvaluationRestrictions(pattern); + Periods.Clear(); - var periodQuery = GetDates(referenceDate, periodStart, periodEnd, -1, pattern, includeReferenceDateInResults) - .Select(dt => CreatePeriod(dt, referenceDate)); + var periodQuery = GetDates(referenceDate, periodStart, periodEnd, -1, pattern, includeReferenceDateInResults) + .Select(dt => CreatePeriod(dt, referenceDate)); - Periods.UnionWith(periodQuery); + Periods.UnionWith(periodQuery); - return Periods; - } + return Periods; } -} +} \ No newline at end of file diff --git a/Ical.Net/Evaluation/RecurrenceUtil.cs b/Ical.Net/Evaluation/RecurrenceUtil.cs index a51920a21..2423389b2 100644 --- a/Ical.Net/Evaluation/RecurrenceUtil.cs +++ b/Ical.Net/Evaluation/RecurrenceUtil.cs @@ -9,95 +9,94 @@ using Ical.Net.DataTypes; using Ical.Net.Utility; -namespace Ical.Net.Evaluation +namespace Ical.Net.Evaluation; + +internal class RecurrenceUtil { - internal class RecurrenceUtil + public static void ClearEvaluation(IRecurrable recurrable) { - public static void ClearEvaluation(IRecurrable recurrable) - { - var evaluator = recurrable.GetService(typeof(IEvaluator)) as IEvaluator; - evaluator?.Clear(); - } + var evaluator = recurrable.GetService(typeof(IEvaluator)) as IEvaluator; + evaluator?.Clear(); + } - public static HashSet GetOccurrences(IRecurrable recurrable, IDateTime dt, bool includeReferenceDateInResults) => GetOccurrences(recurrable, - new CalDateTime(dt.Date), new CalDateTime(dt.Date.AddDays(1).AddSeconds(-1)), includeReferenceDateInResults); + public static HashSet GetOccurrences(IRecurrable recurrable, IDateTime dt, bool includeReferenceDateInResults) => GetOccurrences(recurrable, + new CalDateTime(dt.Date), new CalDateTime(dt.Date.AddDays(1).AddSeconds(-1)), includeReferenceDateInResults); - public static HashSet GetOccurrences(IRecurrable recurrable, IDateTime periodStart, IDateTime periodEnd, bool includeReferenceDateInResults) + public static HashSet GetOccurrences(IRecurrable recurrable, IDateTime periodStart, IDateTime periodEnd, bool includeReferenceDateInResults) + { + var evaluator = recurrable.GetService(typeof(IEvaluator)) as IEvaluator; + if (evaluator == null || recurrable.Start == null) { - var evaluator = recurrable.GetService(typeof(IEvaluator)) as IEvaluator; - if (evaluator == null || recurrable.Start == null) - { - return new HashSet(); - } + return new HashSet(); + } - // Ensure the start time is associated with the object being queried - var start = recurrable.Start; - start.AssociatedObject = recurrable as ICalendarObject; + // Ensure the start time is associated with the object being queried + var start = recurrable.Start; + start.AssociatedObject = recurrable as ICalendarObject; - // Change the time zone of periodStart/periodEnd as needed - // so they can be used during the evaluation process. + // Change the time zone of periodStart/periodEnd as needed + // so they can be used during the evaluation process. - periodStart.TzId = start.TzId; - periodEnd.TzId = start.TzId; + periodStart.TzId = start.TzId; + periodEnd.TzId = start.TzId; - var periods = evaluator.Evaluate(start, DateUtil.GetSimpleDateTimeData(periodStart), DateUtil.GetSimpleDateTimeData(periodEnd), - includeReferenceDateInResults); + var periods = evaluator.Evaluate(start, DateUtil.GetSimpleDateTimeData(periodStart), DateUtil.GetSimpleDateTimeData(periodEnd), + includeReferenceDateInResults); - var otherOccurrences = from p in periods - let endTime = p.EndTime ?? p.StartTime - where - (endTime.GreaterThan(periodStart) && p.StartTime.LessThan(periodEnd) || - (periodStart.Equals(periodEnd) && p.StartTime.LessThanOrEqual(periodStart) && endTime.GreaterThan(periodEnd))) || //A period that starts at the same time it ends - (p.StartTime.Equals(endTime) && periodStart.Equals(p.StartTime)) //An event that starts at the same time it ends - select new Occurrence(recurrable, p); + var otherOccurrences = from p in periods + let endTime = p.EndTime ?? p.StartTime + where + (endTime.GreaterThan(periodStart) && p.StartTime.LessThan(periodEnd) || + (periodStart.Equals(periodEnd) && p.StartTime.LessThanOrEqual(periodStart) && endTime.GreaterThan(periodEnd))) || //A period that starts at the same time it ends + (p.StartTime.Equals(endTime) && periodStart.Equals(p.StartTime)) //An event that starts at the same time it ends + select new Occurrence(recurrable, p); - var occurrences = new HashSet(otherOccurrences); - return occurrences; - } + var occurrences = new HashSet(otherOccurrences); + return occurrences; + } - public static bool?[] GetExpandBehaviorList(RecurrencePattern p) + public static bool?[] GetExpandBehaviorList(RecurrencePattern p) + { + // See the table in RFC 5545 Section 3.3.10 (Page 43). + switch (p.Frequency) { - // See the table in RFC 5545 Section 3.3.10 (Page 43). - switch (p.Frequency) + case FrequencyType.Minutely: + return new bool?[] { false, null, false, false, false, false, false, true, false }; + case FrequencyType.Hourly: + return new bool?[] { false, null, false, false, false, false, true, true, false }; + case FrequencyType.Daily: + return new bool?[] { false, null, null, false, false, true, true, true, false }; + case FrequencyType.Weekly: + return new bool?[] { false, null, null, null, true, true, true, true, false }; + case FrequencyType.Monthly: + { + var row = new bool?[] { false, null, null, true, true, true, true, true, false }; + + // Limit if BYMONTHDAY is present; otherwise, special expand for MONTHLY. + if (p.ByMonthDay.Count > 0) + { + row[4] = false; + } + + return row; + } + case FrequencyType.Yearly: { - case FrequencyType.Minutely: - return new bool?[] { false, null, false, false, false, false, false, true, false }; - case FrequencyType.Hourly: - return new bool?[] { false, null, false, false, false, false, true, true, false }; - case FrequencyType.Daily: - return new bool?[] { false, null, null, false, false, true, true, true, false }; - case FrequencyType.Weekly: - return new bool?[] { false, null, null, null, true, true, true, true, false }; - case FrequencyType.Monthly: - { - var row = new bool?[] { false, null, null, true, true, true, true, true, false }; - - // Limit if BYMONTHDAY is present; otherwise, special expand for MONTHLY. - if (p.ByMonthDay.Count > 0) - { - row[4] = false; - } - - return row; - } - case FrequencyType.Yearly: - { - var row = new bool?[] { true, true, true, true, true, true, true, true, false }; - - // Limit if BYYEARDAY or BYMONTHDAY is present; otherwise, - // special expand for WEEKLY if BYWEEKNO present; otherwise, - // special expand for MONTHLY if BYMONTH present; otherwise, - // special expand for YEARLY. - if (p.ByYearDay.Count > 0 || p.ByMonthDay.Count > 0) - { - row[4] = false; - } - - return row; - } - default: - return new bool?[] { false, null, false, false, false, false, false, false, false }; + var row = new bool?[] { true, true, true, true, true, true, true, true, false }; + + // Limit if BYYEARDAY or BYMONTHDAY is present; otherwise, + // special expand for WEEKLY if BYWEEKNO present; otherwise, + // special expand for MONTHLY if BYMONTH present; otherwise, + // special expand for YEARLY. + if (p.ByYearDay.Count > 0 || p.ByMonthDay.Count > 0) + { + row[4] = false; + } + + return row; } + default: + return new bool?[] { false, null, false, false, false, false, false, false, false }; } } -} +} \ No newline at end of file diff --git a/Ical.Net/Evaluation/RecurringEvaluator.cs b/Ical.Net/Evaluation/RecurringEvaluator.cs index 2e2400a60..0604346a6 100644 --- a/Ical.Net/Evaluation/RecurringEvaluator.cs +++ b/Ical.Net/Evaluation/RecurringEvaluator.cs @@ -9,162 +9,161 @@ using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; -namespace Ical.Net.Evaluation +namespace Ical.Net.Evaluation; + +public class RecurringEvaluator : Evaluator { - public class RecurringEvaluator : Evaluator + protected IRecurrable Recurrable { get; set; } + + public RecurringEvaluator(IRecurrable obj) { - protected IRecurrable Recurrable { get; set; } + Recurrable = obj; - public RecurringEvaluator(IRecurrable 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) { - 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; - } + AssociatedObject = (ICalendarObject) obj; } - - /// - /// Evaluates 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 (obj is ICalendarDataType) { - if (Recurrable.RecurrenceRules == null || !Recurrable.RecurrenceRules.Any()) - { - return new HashSet(); - } + var dt = (ICalendarDataType) obj; + AssociatedObject = dt.AssociatedObject; + } + } - var periodsQuery = Recurrable.RecurrenceRules.SelectMany(rule => - { - var ruleEvaluator = rule.GetService(typeof(IEvaluator)) as IEvaluator; - if (ruleEvaluator == null) - { - return Enumerable.Empty(); - } - return ruleEvaluator.Evaluate(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); - }); - - var periods = new HashSet(periodsQuery); - - //Only add referenceDate if there are no RecurrenceRules defined - if (includeReferenceDateInResults && (Recurrable.RecurrenceRules == null || !Recurrable.RecurrenceRules.Any())) - { - periods.UnionWith(new[] { new Period(referenceDate) }); - } - return periods; + /// + /// Evaluates 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(); } - /// Evaluates the RDate component, and adds each specified DateTime or Period to the Periods collection. - protected HashSet EvaluateRDate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd) + var periodsQuery = Recurrable.RecurrenceRules.SelectMany(rule => { - if (Recurrable.RecurrenceDates == null || !Recurrable.RecurrenceDates.Any()) + var ruleEvaluator = rule.GetService(typeof(IEvaluator)) as IEvaluator; + if (ruleEvaluator == null) { - return new HashSet(); + return Enumerable.Empty(); } + return ruleEvaluator.Evaluate(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); + }); + + var periods = new HashSet(periodsQuery); - var recurrences = new HashSet(Recurrable.RecurrenceDates.SelectMany(rdate => rdate)); - return recurrences; + //Only add referenceDate if there are no RecurrenceRules defined + if (includeReferenceDateInResults && (Recurrable.RecurrenceRules == null || !Recurrable.RecurrenceRules.Any())) + { + periods.UnionWith(new[] { new Period(referenceDate) }); } + return periods; + } - /// - /// Evaluates 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) + /// Evaluates 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()) { - if (Recurrable.ExceptionRules == null || !Recurrable.ExceptionRules.Any()) - { - return new HashSet(); - } + return new HashSet(); + } - var exRuleEvaluatorQuery = Recurrable.ExceptionRules.SelectMany(exRule => - { - var exRuleEvaluator = exRule.GetService(typeof(IEvaluator)) as IEvaluator; - if (exRuleEvaluator == null) - { - return Enumerable.Empty(); - } - return exRuleEvaluator.Evaluate(referenceDate, periodStart, periodEnd, false); - }); - - var exRuleExclusions = new HashSet(exRuleEvaluatorQuery); - return exRuleExclusions; + var recurrences = new HashSet(Recurrable.RecurrenceDates.SelectMany(rdate => rdate)); + return recurrences; + } + + /// + /// Evaluates 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(); } - /// - /// Evaluates 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) + var exRuleEvaluatorQuery = Recurrable.ExceptionRules.SelectMany(exRule => { - if (Recurrable.ExceptionDates == null || !Recurrable.ExceptionDates.Any()) + var exRuleEvaluator = exRule.GetService(typeof(IEvaluator)) as IEvaluator; + if (exRuleEvaluator == null) { - return new HashSet(); + return Enumerable.Empty(); } + return exRuleEvaluator.Evaluate(referenceDate, periodStart, periodEnd, false); + }); - var exDates = new HashSet(Recurrable.ExceptionDates.SelectMany(exDate => exDate)); - return exDates; - } + var exRuleExclusions = new HashSet(exRuleEvaluatorQuery); + return exRuleExclusions; + } - public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + /// + /// Evaluates 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()) { - Periods.Clear(); + return new HashSet(); + } - var rruleOccurrences = EvaluateRRule(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); - //Only add referenceDate if there are no RecurrenceRules defined - if (includeReferenceDateInResults && (Recurrable.RecurrenceRules == null || !Recurrable.RecurrenceRules.Any())) - { - rruleOccurrences.UnionWith(new[] { new Period(referenceDate), }); - } + var exDates = new HashSet(Recurrable.ExceptionDates.SelectMany(exDate => exDate)); + return exDates; + } - var rdateOccurrences = EvaluateRDate(referenceDate, periodStart, periodEnd); + public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + { + Periods.Clear(); - var exRuleExclusions = EvaluateExRule(referenceDate, periodStart, periodEnd); - var exDateExclusions = EvaluateExDate(referenceDate, periodStart, periodEnd); + var rruleOccurrences = EvaluateRRule(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); + //Only add referenceDate if there are no RecurrenceRules defined + if (includeReferenceDateInResults && (Recurrable.RecurrenceRules == null || !Recurrable.RecurrenceRules.Any())) + { + rruleOccurrences.UnionWith(new[] { new Period(referenceDate), }); + } - //Exclusions trump inclusions - Periods.UnionWith(rruleOccurrences); - Periods.UnionWith(rdateOccurrences); - Periods.ExceptWith(exRuleExclusions); - Periods.ExceptWith(exDateExclusions); + var rdateOccurrences = EvaluateRDate(referenceDate, periodStart, periodEnd); - var dateOverlaps = FindDateOverlaps(exDateExclusions); - Periods.ExceptWith(dateOverlaps); + var exRuleExclusions = EvaluateExRule(referenceDate, periodStart, periodEnd); + var exDateExclusions = EvaluateExDate(referenceDate, periodStart, periodEnd); - if (EvaluationStartBounds == DateTime.MaxValue || EvaluationStartBounds > periodStart) - { - EvaluationStartBounds = periodStart; - } - if (EvaluationEndBounds == DateTime.MinValue || EvaluationEndBounds < periodEnd) - { - EvaluationEndBounds = periodEnd; - } + //Exclusions trump inclusions + Periods.UnionWith(rruleOccurrences); + Periods.UnionWith(rdateOccurrences); + Periods.ExceptWith(exRuleExclusions); + Periods.ExceptWith(exDateExclusions); - return Periods; - } + var dateOverlaps = FindDateOverlaps(exDateExclusions); + Periods.ExceptWith(dateOverlaps); - private HashSet FindDateOverlaps(HashSet dates) + if (EvaluationStartBounds == DateTime.MaxValue || EvaluationStartBounds > periodStart) { - 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; + 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/Ical.Net/Evaluation/TimeZoneEvaluator.cs b/Ical.Net/Evaluation/TimeZoneEvaluator.cs index ae94815cc..bf117039e 100644 --- a/Ical.Net/Evaluation/TimeZoneEvaluator.cs +++ b/Ical.Net/Evaluation/TimeZoneEvaluator.cs @@ -9,133 +9,132 @@ using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; -namespace Ical.Net.Evaluation +namespace Ical.Net.Evaluation; + +public class TimeZoneEvaluator : Evaluator { - public class TimeZoneEvaluator : Evaluator - { - protected VTimeZone TimeZone { get; set; } + protected VTimeZone TimeZone { get; set; } - private List _occurrences; - public virtual List Occurrences - { - get => _occurrences; - set => _occurrences = value; - } + private List _occurrences; + public virtual List Occurrences + { + get => _occurrences; + set => _occurrences = value; + } - public TimeZoneEvaluator(VTimeZone tz) - { - TimeZone = tz; - _occurrences = new List(); - } + public TimeZoneEvaluator(VTimeZone tz) + { + TimeZone = tz; + _occurrences = new List(); + } - private void ProcessOccurrences(IDateTime referenceDate) - { - // Sort the occurrences by start time - _occurrences.Sort( - delegate (Occurrence o1, Occurrence o2) { - if (o1.Period?.StartTime == null) - { - return -1; - } - return o2.Period?.StartTime == null - ? 1 - : o1.Period.StartTime.CompareTo(o2.Period.StartTime); + private void ProcessOccurrences(IDateTime referenceDate) + { + // Sort the occurrences by start time + _occurrences.Sort( + delegate (Occurrence o1, Occurrence o2) { + if (o1.Period?.StartTime == null) + { + return -1; } - ); - - for (var i = 0; i < _occurrences.Count; i++) - { - var curr = _occurrences[i]; - var next = i < _occurrences.Count - 1 ? _occurrences[i + 1] : null; - - // Determine end times for our periods, overwriting previously calculated end times. - // This is important because we don't want to overcalculate our time zone information, - // but simply calculate enough to be accurate. When date/time ranges that are out of - // normal working bounds are encountered, then occurrences are processed again, and - // new end times are determined. - curr.Period.EndTime = next != null - ? next.Period.StartTime.AddTicks(-1) - : ConvertToIDateTime(EvaluationEndBounds, referenceDate); + return o2.Period?.StartTime == null + ? 1 + : o1.Period.StartTime.CompareTo(o2.Period.StartTime); } - } + ); - public override void Clear() + for (var i = 0; i < _occurrences.Count; i++) { - base.Clear(); - _occurrences.Clear(); + var curr = _occurrences[i]; + var next = i < _occurrences.Count - 1 ? _occurrences[i + 1] : null; + + // Determine end times for our periods, overwriting previously calculated end times. + // This is important because we don't want to overcalculate our time zone information, + // but simply calculate enough to be accurate. When date/time ranges that are out of + // normal working bounds are encountered, then occurrences are processed again, and + // new end times are determined. + curr.Period.EndTime = next != null + ? next.Period.StartTime.AddTicks(-1) + : ConvertToIDateTime(EvaluationEndBounds, referenceDate); } + } - public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) - { - // Ensure the reference date is associated with the time zone - if (referenceDate.AssociatedObject == null) - referenceDate.AssociatedObject = TimeZone; + public override void Clear() + { + base.Clear(); + _occurrences.Clear(); + } - var infos = new List(TimeZone.TimeZoneInfos); + public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + { + // Ensure the reference date is associated with the time zone + if (referenceDate.AssociatedObject == null) + referenceDate.AssociatedObject = TimeZone; + + var infos = new List(TimeZone.TimeZoneInfos); - // Evaluate extra time periods, without re-evaluating ones that were already evaluated - if ((EvaluationStartBounds == DateTime.MaxValue && EvaluationEndBounds == DateTime.MinValue) - || periodEnd.Equals(EvaluationStartBounds) - || periodStart.Equals(EvaluationEndBounds)) + // Evaluate extra time periods, without re-evaluating ones that were already evaluated + if ((EvaluationStartBounds == DateTime.MaxValue && EvaluationEndBounds == DateTime.MinValue) + || periodEnd.Equals(EvaluationStartBounds) + || periodStart.Equals(EvaluationEndBounds)) + { + foreach (var curr in infos) { - foreach (var curr in infos) + var evaluator = curr.GetService(typeof(IEvaluator)) as IEvaluator; + Debug.Assert(curr.Start != null, "TimeZoneInfo.Start must not be null."); + Debug.Assert(curr.Start.TzId == null, "TimeZoneInfo.Start must not have a time zone reference."); + Debug.Assert(evaluator != null, "TimeZoneInfo.GetService(typeof(IEvaluator)) must not be null."); + + // Time zones must include an effective start date/time + // and must provide an evaluator. + if (evaluator == null) { - var evaluator = curr.GetService(typeof(IEvaluator)) as IEvaluator; - Debug.Assert(curr.Start != null, "TimeZoneInfo.Start must not be null."); - Debug.Assert(curr.Start.TzId == null, "TimeZoneInfo.Start must not have a time zone reference."); - Debug.Assert(evaluator != null, "TimeZoneInfo.GetService(typeof(IEvaluator)) must not be null."); - - // Time zones must include an effective start date/time - // and must provide an evaluator. - if (evaluator == null) - { - continue; - } + continue; + } - // Set the start bounds - if (EvaluationStartBounds > periodStart) - { - EvaluationStartBounds = periodStart; - } + // Set the start bounds + if (EvaluationStartBounds > periodStart) + { + EvaluationStartBounds = periodStart; + } - // FIXME: 5 years is an arbitrary number, to eliminate the need - // to recalculate time zone information as much as possible. - var offsetEnd = periodEnd.AddYears(5); + // FIXME: 5 years is an arbitrary number, to eliminate the need + // to recalculate time zone information as much as possible. + var offsetEnd = periodEnd.AddYears(5); - // Determine the UTC occurrences of the Time Zone observances - var periods = evaluator.Evaluate( - referenceDate, - periodStart, - offsetEnd, - includeReferenceDateInResults); + // Determine the UTC occurrences of the Time Zone observances + var periods = evaluator.Evaluate( + referenceDate, + periodStart, + offsetEnd, + includeReferenceDateInResults); - foreach (var period in periods) - { - Periods.Add(period); - var o = new Occurrence(curr, period); - if (!_occurrences.Contains(o)) - { - _occurrences.Add(o); - } - } - - if (EvaluationEndBounds == DateTime.MinValue || EvaluationEndBounds < offsetEnd) + foreach (var period in periods) + { + Periods.Add(period); + var o = new Occurrence(curr, period); + if (!_occurrences.Contains(o)) { - EvaluationEndBounds = offsetEnd; + _occurrences.Add(o); } } - ProcessOccurrences(referenceDate); - } - else - { - if (EvaluationEndBounds != DateTime.MinValue && periodEnd > EvaluationEndBounds) + if (EvaluationEndBounds == DateTime.MinValue || EvaluationEndBounds < offsetEnd) { - Evaluate(referenceDate, EvaluationEndBounds, periodEnd, includeReferenceDateInResults); + EvaluationEndBounds = offsetEnd; } } - return Periods; + ProcessOccurrences(referenceDate); } + else + { + if (EvaluationEndBounds != DateTime.MinValue && periodEnd > EvaluationEndBounds) + { + Evaluate(referenceDate, EvaluationEndBounds, periodEnd, includeReferenceDateInResults); + } + } + + return Periods; } -} +} \ No newline at end of file diff --git a/Ical.Net/Evaluation/TimeZoneInfoEvaluator.cs b/Ical.Net/Evaluation/TimeZoneInfoEvaluator.cs index 4f7606d89..651c0f7a0 100644 --- a/Ical.Net/Evaluation/TimeZoneInfoEvaluator.cs +++ b/Ical.Net/Evaluation/TimeZoneInfoEvaluator.cs @@ -8,30 +8,29 @@ using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; -namespace Ical.Net.Evaluation +namespace Ical.Net.Evaluation; + +public class TimeZoneInfoEvaluator : RecurringEvaluator { - public class TimeZoneInfoEvaluator : RecurringEvaluator + protected VTimeZoneInfo TimeZoneInfo { - protected VTimeZoneInfo TimeZoneInfo - { - get => Recurrable as VTimeZoneInfo; - set => Recurrable = value; - } + get => Recurrable as VTimeZoneInfo; + set => Recurrable = value; + } - public TimeZoneInfoEvaluator(IRecurrable tzi) : base(tzi) { } + public TimeZoneInfoEvaluator(IRecurrable tzi) : base(tzi) { } - public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + { + // Time zones must include an effective start date/time + // and must provide an evaluator. + if (TimeZoneInfo == null) { - // Time zones must include an effective start date/time - // and must provide an evaluator. - if (TimeZoneInfo == null) - { - return new HashSet(); - } - - // Always include the reference date in the results - var periods = base.Evaluate(referenceDate, periodStart, periodEnd, true); - return periods; + return new HashSet(); } + + // Always include the reference date in the results + var periods = base.Evaluate(referenceDate, periodStart, periodEnd, true); + return periods; } -} +} \ No newline at end of file diff --git a/Ical.Net/Evaluation/TodoEvaluator.cs b/Ical.Net/Evaluation/TodoEvaluator.cs index 3953d712f..db63d2a8b 100644 --- a/Ical.Net/Evaluation/TodoEvaluator.cs +++ b/Ical.Net/Evaluation/TodoEvaluator.cs @@ -10,99 +10,98 @@ using Ical.Net.DataTypes; using Ical.Net.Utility; -namespace Ical.Net.Evaluation +namespace Ical.Net.Evaluation; + +public class TodoEvaluator : RecurringEvaluator { - public class TodoEvaluator : RecurringEvaluator - { - protected Todo Todo => Recurrable as Todo; + protected Todo Todo => Recurrable as Todo; - public TodoEvaluator(Todo todo) : base(todo) { } + public TodoEvaluator(Todo todo) : base(todo) { } - public void EvaluateToPreviousOccurrence(IDateTime completedDate, IDateTime currDt) - { - var beginningDate = completedDate.Copy(); + public void EvaluateToPreviousOccurrence(IDateTime completedDate, IDateTime currDt) + { + var beginningDate = completedDate.Copy(); - if (Todo.RecurrenceRules != null) + if (Todo.RecurrenceRules != null) + { + foreach (var rrule in Todo.RecurrenceRules) { - foreach (var rrule in Todo.RecurrenceRules) - { - DetermineStartingRecurrence(rrule, ref beginningDate); - } + DetermineStartingRecurrence(rrule, ref beginningDate); } - if (Todo.RecurrenceDates != null) + } + if (Todo.RecurrenceDates != null) + { + foreach (var rdate in Todo.RecurrenceDates) { - foreach (var rdate in Todo.RecurrenceDates) - { - DetermineStartingRecurrence(rdate, ref beginningDate); - } + DetermineStartingRecurrence(rdate, ref beginningDate); } - if (Todo.ExceptionRules != null) + } + if (Todo.ExceptionRules != null) + { + foreach (var exrule in Todo.ExceptionRules) { - foreach (var exrule in Todo.ExceptionRules) - { - DetermineStartingRecurrence(exrule, ref beginningDate); - } + DetermineStartingRecurrence(exrule, ref beginningDate); } - if (Todo.ExceptionDates != null) + } + if (Todo.ExceptionDates != null) + { + foreach (var exdate in Todo.ExceptionDates) { - foreach (var exdate in Todo.ExceptionDates) - { - DetermineStartingRecurrence(exdate, ref beginningDate); - } + DetermineStartingRecurrence(exdate, ref beginningDate); } - - Evaluate(Todo.Start, DateUtil.GetSimpleDateTimeData(beginningDate), DateUtil.GetSimpleDateTimeData(currDt).AddTicks(1), true); } - public void DetermineStartingRecurrence(PeriodList rdate, ref IDateTime referenceDateTime) + 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))) { - var evaluator = rdate.GetService(); + referenceDateTime = p.StartTime; + } + } - 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 void DetermineStartingRecurrence(RecurrencePattern recur, ref IDateTime referenceDateTime) + 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) { - if (recur.Count != int.MinValue) - { - referenceDateTime = Todo.Start.Copy(); - } - else - { - var dtVal = referenceDateTime.Value; - IncrementDate(ref dtVal, recur, -recur.Interval); - referenceDateTime.Value = dtVal; - } + return new HashSet(); } - public override HashSet Evaluate(IDateTime referenceDate, DateTime periodStart, DateTime periodEnd, bool includeReferenceDateInResults) + base.Evaluate(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); + + // Ensure each period has a duration + foreach (var period in Periods.Where(period => period.EndTime == null)) { - // TODO items can only recur if a start date is specified - if (Todo.Start == null) + period.Duration = Todo.Duration; + if (period.Duration != default) { - return new HashSet(); + period.EndTime = period.StartTime.Add(Todo.Duration); } - - base.Evaluate(referenceDate, periodStart, periodEnd, includeReferenceDateInResults); - - // Ensure each period has a duration - foreach (var period in Periods.Where(period => period.EndTime == null)) + else { period.Duration = Todo.Duration; - if (period.Duration != default) - { - period.EndTime = period.StartTime.Add(Todo.Duration); - } - else - { - period.Duration = Todo.Duration; - } } - return Periods; } + return Periods; } -} +} \ No newline at end of file diff --git a/Ical.Net/ICalendarObject.cs b/Ical.Net/ICalendarObject.cs index 3172485e3..b82686169 100644 --- a/Ical.Net/ICalendarObject.cs +++ b/Ical.Net/ICalendarObject.cs @@ -5,43 +5,42 @@ using Ical.Net.Collections; -namespace Ical.Net +namespace Ical.Net; + +public interface ICalendarObject : IGroupedObject, ILoadable, ICopyable, IServiceProvider { - 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; } - } -} + /// + /// 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/Ical.Net/ICalendarObjectList.cs b/Ical.Net/ICalendarObjectList.cs index 0dedaf1b8..26f775a5c 100644 --- a/Ical.Net/ICalendarObjectList.cs +++ b/Ical.Net/ICalendarObjectList.cs @@ -5,11 +5,10 @@ using Ical.Net.Collections; -namespace Ical.Net +namespace Ical.Net; + +public interface ICalendarObjectList : + IGroupedCollection where TType : class, ICalendarObject { - public interface ICalendarObjectList : - IGroupedCollection where TType : class, ICalendarObject - { - TType this[int index] { get; } - } -} + TType this[int index] { get; } +} \ No newline at end of file diff --git a/Ical.Net/ICalendarProperty.cs b/Ical.Net/ICalendarProperty.cs index a84e7b13f..968962d6f 100644 --- a/Ical.Net/ICalendarProperty.cs +++ b/Ical.Net/ICalendarProperty.cs @@ -6,10 +6,9 @@ using Ical.Net.Collections.Interfaces; using Ical.Net.DataTypes; -namespace Ical.Net +namespace Ical.Net; + +public interface ICalendarProperty : ICalendarParameterCollectionContainer, ICalendarObject, IValueObject { - public interface ICalendarProperty : ICalendarParameterCollectionContainer, ICalendarObject, IValueObject - { - object Value { get; set; } - } -} + object Value { get; set; } +} \ No newline at end of file diff --git a/Ical.Net/ICalendarPropertyListContainer.cs b/Ical.Net/ICalendarPropertyListContainer.cs index f40b5111b..cfa4e6f15 100644 --- a/Ical.Net/ICalendarPropertyListContainer.cs +++ b/Ical.Net/ICalendarPropertyListContainer.cs @@ -3,10 +3,9 @@ // Licensed under the MIT license. // -namespace Ical.Net +namespace Ical.Net; + +public interface ICalendarPropertyListContainer : ICalendarObject { - public interface ICalendarPropertyListContainer : ICalendarObject - { - CalendarPropertyList Properties { get; } - } -} + CalendarPropertyList Properties { get; } +} \ No newline at end of file diff --git a/Ical.Net/ICopyable.cs b/Ical.Net/ICopyable.cs index 51c273ddc..f93dc3661 100644 --- a/Ical.Net/ICopyable.cs +++ b/Ical.Net/ICopyable.cs @@ -3,24 +3,23 @@ // Licensed under the MIT license. // -namespace Ical.Net +namespace Ical.Net; + +public interface ICopyable { - public interface ICopyable - { - /// - /// (Deep) copies all relevant members from - /// the source object to the current one. - /// - /// If an object cannot use the implementation of a base class, - /// it must override this method and implement the copy logic itself. - /// - void CopyFrom(ICopyable obj); + /// + /// (Deep) copies all relevant members from + /// the source object to the current one. + /// + /// If an object cannot use the implementation of a base class, + /// it must override this method and implement the copy logic itself. + /// + void CopyFrom(ICopyable obj); - /// - /// Returns a deep copy of the current object, mostly by using the method - /// of the object when it is overridden, otherwise is used the implementation of the base class. - /// This is necessary when working with mutable reference types. - /// - T Copy(); - } -} + /// + /// Returns a deep copy of the current object, mostly by using the method + /// of the object when it is overridden, otherwise is used the implementation of the base class. + /// This is necessary when working with mutable reference types. + /// + T Copy(); +} \ No newline at end of file diff --git a/Ical.Net/IGetFreeBusy.cs b/Ical.Net/IGetFreeBusy.cs index d36b3f9ca..1d4fc711d 100644 --- a/Ical.Net/IGetFreeBusy.cs +++ b/Ical.Net/IGetFreeBusy.cs @@ -7,12 +7,11 @@ using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; -namespace Ical.Net +namespace Ical.Net; + +public interface IGetFreeBusy { - public interface IGetFreeBusy - { - FreeBusy GetFreeBusy(FreeBusy freeBusyRequest); - FreeBusy GetFreeBusy(IDateTime fromInclusive, IDateTime toExclusive); - FreeBusy GetFreeBusy(Organizer organizer, IEnumerable contacts, IDateTime fromInclusive, IDateTime toExclusive); - } -} + 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/Ical.Net/IGetOccurrences.cs b/Ical.Net/IGetOccurrences.cs index c2f6906e5..9207612fb 100644 --- a/Ical.Net/IGetOccurrences.cs +++ b/Ical.Net/IGetOccurrences.cs @@ -8,71 +8,70 @@ using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; -namespace Ical.Net +namespace Ical.Net; + +public interface IGetOccurrences { - 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(); + /// + /// 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); + /// + /// 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); + 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); + /// + /// 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); - } + 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; +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; + 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; + /// + /// 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; - } -} + HashSet GetOccurrences(DateTime startTime, DateTime endTime) where T : IRecurringComponent; +} \ No newline at end of file diff --git a/Ical.Net/ILoadable.cs b/Ical.Net/ILoadable.cs index 824cc46ca..37b90ac2f 100644 --- a/Ical.Net/ILoadable.cs +++ b/Ical.Net/ILoadable.cs @@ -5,23 +5,22 @@ using System; -namespace Ical.Net +namespace Ical.Net; + +public interface ILoadable { - public interface ILoadable - { - /// - /// Gets whether or not the object has been loaded. - /// - bool IsLoaded { get; } + /// + /// 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; + /// + /// An event that fires when the object has been loaded. + /// + event EventHandler Loaded; - /// - /// Fires the Loaded event. - /// - void OnLoaded(); - } -} + /// + /// Fires the Loaded event. + /// + void OnLoaded(); +} \ No newline at end of file diff --git a/Ical.Net/IMergeable.cs b/Ical.Net/IMergeable.cs index 27bdedcba..61aab5beb 100644 --- a/Ical.Net/IMergeable.cs +++ b/Ical.Net/IMergeable.cs @@ -3,13 +3,12 @@ // Licensed under the MIT license. // -namespace Ical.Net +namespace Ical.Net; + +public interface IMergeable { - public interface IMergeable - { - /// - /// Merges this object with another. - /// - void MergeWith(IMergeable obj); - } -} + /// + /// Merges this object with another. + /// + void MergeWith(IMergeable obj); +} \ No newline at end of file diff --git a/Ical.Net/IParameterCollection.cs b/Ical.Net/IParameterCollection.cs index 7c1590e30..f23edc942 100644 --- a/Ical.Net/IParameterCollection.cs +++ b/Ical.Net/IParameterCollection.cs @@ -6,15 +6,14 @@ using System.Collections.Generic; using Ical.Net.Collections; -namespace Ical.Net +namespace Ical.Net; + +public interface IParameterCollection : IGroupedList { - 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); - } -} + 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/Ical.Net/IServiceProvider.cs b/Ical.Net/IServiceProvider.cs index 983958e84..03ee1db78 100644 --- a/Ical.Net/IServiceProvider.cs +++ b/Ical.Net/IServiceProvider.cs @@ -5,17 +5,16 @@ using System; -namespace Ical.Net +namespace Ical.Net; + +public interface IServiceProvider { - 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); - } -} + 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/Ical.Net/ParameterList.cs b/Ical.Net/ParameterList.cs index a02d17ac1..b9afb90d3 100644 --- a/Ical.Net/ParameterList.cs +++ b/Ical.Net/ParameterList.cs @@ -6,25 +6,24 @@ using System.Collections.Generic; using Ical.Net.Collections; -namespace Ical.Net +namespace Ical.Net; + +public class ParameterList : GroupedValueList, IParameterCollection { - public class ParameterList : GroupedValueList, IParameterCollection + public virtual void SetParent(ICalendarObject parent) { - public virtual void SetParent(ICalendarObject parent) + foreach (var parameter in this) { - foreach (var parameter in this) - { - parameter.Parent = parent; - } + parameter.Parent = parent; } + } - public virtual void Add(string name, string value) - { - Add(new CalendarParameter(name, value)); - } + public virtual void Add(string name, string value) + { + Add(new CalendarParameter(name, value)); + } - public virtual string Get(string name) => Get(name); + public virtual string Get(string name) => Get(name); - public virtual IList GetMany(string name) => GetMany(name); - } -} + public virtual IList GetMany(string name) => GetMany(name); +} \ No newline at end of file diff --git a/Ical.Net/Proxies/CalendarObjectListProxy.cs b/Ical.Net/Proxies/CalendarObjectListProxy.cs index f100934c0..be7e35f69 100644 --- a/Ical.Net/Proxies/CalendarObjectListProxy.cs +++ b/Ical.Net/Proxies/CalendarObjectListProxy.cs @@ -7,13 +7,12 @@ using Ical.Net.Collections; using Ical.Net.Collections.Proxies; -namespace Ical.Net.Proxies +namespace Ical.Net.Proxies; + +public class CalendarObjectListProxy : GroupedCollectionProxy, ICalendarObjectList + where TType : class, ICalendarObject { - public class CalendarObjectListProxy : GroupedCollectionProxy, ICalendarObjectList - where TType : class, ICalendarObject - { - public CalendarObjectListProxy(IGroupedCollection list) : base(list) { } + public CalendarObjectListProxy(IGroupedCollection list) : base(list) { } - public virtual TType this[int index] => this.Skip(index).FirstOrDefault(); - } -} + public virtual TType this[int index] => this.Skip(index).FirstOrDefault(); +} \ No newline at end of file diff --git a/Ical.Net/Proxies/IUniqueComponentList.cs b/Ical.Net/Proxies/IUniqueComponentList.cs index c5a1773bb..5b9ed832a 100644 --- a/Ical.Net/Proxies/IUniqueComponentList.cs +++ b/Ical.Net/Proxies/IUniqueComponentList.cs @@ -6,12 +6,11 @@ using System.Collections.Generic; using Ical.Net.CalendarComponents; -namespace Ical.Net.Proxies +namespace Ical.Net.Proxies; + +public interface IUniqueComponentList : + ICalendarObjectList where TComponentType : class, IUniqueComponent { - public interface IUniqueComponentList : - ICalendarObjectList where TComponentType : class, IUniqueComponent - { - TComponentType this[string uid] { get; set; } - void AddRange(IEnumerable collection); - } -} + TComponentType this[string uid] { get; set; } + void AddRange(IEnumerable collection); +} \ No newline at end of file diff --git a/Ical.Net/Proxies/ParameterCollectionProxy.cs b/Ical.Net/Proxies/ParameterCollectionProxy.cs index bccd7f47b..5bb553fea 100644 --- a/Ical.Net/Proxies/ParameterCollectionProxy.cs +++ b/Ical.Net/Proxies/ParameterCollectionProxy.cs @@ -9,75 +9,74 @@ using Ical.Net.Collections; using Ical.Net.Collections.Proxies; -namespace Ical.Net.Proxies +namespace Ical.Net.Proxies; + +public class ParameterCollectionProxy : GroupedCollectionProxy, IParameterCollection { - public class ParameterCollectionProxy : GroupedCollectionProxy, IParameterCollection - { - protected GroupedValueList Parameters - => RealObject as GroupedValueList; + protected GroupedValueList Parameters + => RealObject as GroupedValueList; - public ParameterCollectionProxy(IGroupedList realObject) : base(realObject) { } + public ParameterCollectionProxy(IGroupedList realObject) : base(realObject) { } - public virtual void SetParent(ICalendarObject parent) + public virtual void SetParent(ICalendarObject parent) + { + foreach (var parameter in this) { - foreach (var parameter in this) - { - parameter.Parent = parent; - } + 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 => string.Equals(o.Name, name, StringComparison.Ordinal)); + + return parameter?.Value; + } + + public virtual IList GetMany(string name) => new GroupedValueListProxy(Parameters, name); - public virtual void Add(string name, string value) + public virtual void Set(string name, string value) + { + var parameter = RealObject.FirstOrDefault(o => string.Equals(o.Name, name, StringComparison.Ordinal)); + + if (parameter == null) { RealObject.Add(new CalendarParameter(name, value)); } - - public virtual string Get(string name) + else { - var parameter = RealObject.FirstOrDefault(o => string.Equals(o.Name, name, StringComparison.Ordinal)); - - return parameter?.Value; + parameter.SetValue(value); } + } - public virtual IList GetMany(string name) => new GroupedValueListProxy(Parameters, name); + public virtual void Set(string name, IEnumerable values) + { + var parameter = RealObject.FirstOrDefault(o => string.Equals(o.Name, name, StringComparison.Ordinal)); - public virtual void Set(string name, string value) + if (parameter == null) { - var parameter = RealObject.FirstOrDefault(o => string.Equals(o.Name, name, StringComparison.Ordinal)); - - if (parameter == null) - { - RealObject.Add(new CalendarParameter(name, value)); - } - else - { - parameter.SetValue(value); - } + RealObject.Add(new CalendarParameter(name, values)); } - - public virtual void Set(string name, IEnumerable values) + else { - var parameter = RealObject.FirstOrDefault(o => string.Equals(o.Name, name, StringComparison.Ordinal)); - - if (parameter == null) - { - RealObject.Add(new CalendarParameter(name, values)); - } - else - { - parameter.SetValue(values); - } + parameter.SetValue(values); } + } - public virtual int IndexOf(CalendarParameter obj) => 0; + public virtual int IndexOf(CalendarParameter obj) => 0; - public virtual void Insert(int index, CalendarParameter item) { } + public virtual void Insert(int index, CalendarParameter item) { } - public virtual void RemoveAt(int index) { } + public virtual void RemoveAt(int index) { } - public virtual CalendarParameter this[int index] - { - get { return Parameters[index]; } - set { } - } + public virtual CalendarParameter this[int index] + { + get { return Parameters[index]; } + set { } } -} +} \ No newline at end of file diff --git a/Ical.Net/Proxies/UniqueComponentListProxy.cs b/Ical.Net/Proxies/UniqueComponentListProxy.cs index 207f30aa8..4805de742 100644 --- a/Ical.Net/Proxies/UniqueComponentListProxy.cs +++ b/Ical.Net/Proxies/UniqueComponentListProxy.cs @@ -9,64 +9,63 @@ using Ical.Net.CalendarComponents; using Ical.Net.Collections; -namespace Ical.Net.Proxies +namespace Ical.Net.Proxies; + +public class UniqueComponentListProxy : + CalendarObjectListProxy, + IUniqueComponentList + where TComponentType : class, IUniqueComponent { - public class UniqueComponentListProxy : - CalendarObjectListProxy, - IUniqueComponentList - where TComponentType : class, IUniqueComponent + private readonly Dictionary _lookup; + + public UniqueComponentListProxy(IGroupedCollection children) : base(children) { - private readonly Dictionary _lookup; + _lookup = new Dictionary(); + } - public UniqueComponentListProxy(IGroupedCollection children) : base(children) + private TComponentType Search(string uid) + { + if (_lookup.TryGetValue(uid, out var componentType)) { - _lookup = new Dictionary(); + return componentType; } - private TComponentType Search(string uid) + var item = this.FirstOrDefault(c => string.Equals(c.Uid, uid, StringComparison.OrdinalIgnoreCase)); + + if (item == null) { - if (_lookup.TryGetValue(uid, out var componentType)) - { - return componentType; - } + return default(TComponentType); + } - var item = this.FirstOrDefault(c => string.Equals(c.Uid, uid, StringComparison.OrdinalIgnoreCase)); + _lookup[uid] = item; + return item; + } - if (item == null) + public virtual TComponentType this[string uid] + { + get => Search(uid); + set + { + // Find the item matching the UID + var item = Search(uid); + + if (item != null) { - return default(TComponentType); + Remove(item); } - _lookup[uid] = item; - return item; - } - - public virtual TComponentType this[string uid] - { - get => Search(uid); - set + if (value != null) { - // Find the item matching the UID - var item = Search(uid); - - if (item != null) - { - Remove(item); - } - - if (value != null) - { - Add(value); - } + Add(value); } } + } - public void AddRange(IEnumerable collection) + public void AddRange(IEnumerable collection) + { + foreach (var element in collection) { - foreach (var element in collection) - { - Add(element); - } + Add(element); } } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/CalendarComponentFactory.cs b/Ical.Net/Serialization/CalendarComponentFactory.cs index 66c1111c6..a399a90fd 100644 --- a/Ical.Net/Serialization/CalendarComponentFactory.cs +++ b/Ical.Net/Serialization/CalendarComponentFactory.cs @@ -5,48 +5,47 @@ using Ical.Net.CalendarComponents; -namespace Ical.Net.Serialization +namespace Ical.Net.Serialization; + +public class CalendarComponentFactory { - public class CalendarComponentFactory + public virtual ICalendarComponent Build(string objectName) { - public virtual ICalendarComponent Build(string objectName) - { - ICalendarComponent c; - var name = objectName.ToUpper(); + ICalendarComponent c; + var name = objectName.ToUpper(); - switch (name) - { - case Components.Alarm: - c = new Alarm(); - break; - case EventStatus.Name: - c = new CalendarEvent(); - break; - case Components.Freebusy: - c = new FreeBusy(); - break; - case JournalStatus.Name: - c = new Journal(); - break; - case Components.Timezone: - c = new VTimeZone(); - break; - case TodoStatus.Name: - c = new Todo(); - break; - case Components.Calendar: - c = new Calendar(); - break; - case Components.Daylight: - case Components.Standard: - c = new VTimeZoneInfo(); - break; - default: - c = new CalendarComponent(); - break; - } - c.Name = name; - return c; + switch (name) + { + case Components.Alarm: + c = new Alarm(); + break; + case EventStatus.Name: + c = new CalendarEvent(); + break; + case Components.Freebusy: + c = new FreeBusy(); + break; + case JournalStatus.Name: + c = new Journal(); + break; + case Components.Timezone: + c = new VTimeZone(); + break; + case TodoStatus.Name: + c = new Todo(); + break; + case Components.Calendar: + c = new Calendar(); + break; + case Components.Daylight: + case Components.Standard: + c = new VTimeZoneInfo(); + break; + default: + c = new CalendarComponent(); + break; } + c.Name = name; + return c; } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/CalendarSerializer.cs b/Ical.Net/Serialization/CalendarSerializer.cs index 69d4e3a66..53d105578 100644 --- a/Ical.Net/Serialization/CalendarSerializer.cs +++ b/Ical.Net/Serialization/CalendarSerializer.cs @@ -7,68 +7,67 @@ using System.Collections.Generic; using System.IO; -namespace Ical.Net.Serialization +namespace Ical.Net.Serialization; + +public class CalendarSerializer : ComponentSerializer { - public class CalendarSerializer : ComponentSerializer - { - private readonly Calendar _calendar; + private readonly Calendar _calendar; - public CalendarSerializer() - : this(new SerializationContext()) { } + public CalendarSerializer() + : this(new SerializationContext()) { } - public CalendarSerializer(Calendar cal) - { - _calendar = cal; - } + public CalendarSerializer(Calendar cal) + { + _calendar = cal; + } - public CalendarSerializer(SerializationContext ctx) : base(ctx) { } + public CalendarSerializer(SerializationContext ctx) : base(ctx) { } - public virtual string SerializeToString() => SerializeToString(_calendar); + public virtual string SerializeToString() => SerializeToString(_calendar); - protected override IComparer PropertySorter => new CalendarPropertySorter(); + protected override IComparer PropertySorter => new CalendarPropertySorter(); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + if (obj is Calendar) { - if (obj is Calendar) - { - // If we're serializing a calendar, we should indicate that we're using ical.net to do the work - var calendar = (Calendar) obj; - calendar.Version = LibraryMetadata.Version; - calendar.ProductId = LibraryMetadata.ProdId; - - return base.SerializeToString(calendar); - } + // If we're serializing a calendar, we should indicate that we're using ical.net to do the work + var calendar = (Calendar) obj; + calendar.Version = LibraryMetadata.Version; + calendar.ProductId = LibraryMetadata.ProdId; - return base.SerializeToString(obj); + return base.SerializeToString(calendar); } - public override object Deserialize(TextReader tr) => null; + return base.SerializeToString(obj); + } + + public override object Deserialize(TextReader tr) => null; - private class CalendarPropertySorter : IComparer + private class CalendarPropertySorter : IComparer + { + public int Compare(ICalendarProperty x, ICalendarProperty y) { - public int Compare(ICalendarProperty x, ICalendarProperty y) + if (x == y) + { + 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)) { - if (x == y) - { - 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; - } - return string.Equals("VERSION", y.Name, StringComparison.OrdinalIgnoreCase) - ? 1 - : string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); + return -1; } + return string.Equals("VERSION", y.Name, StringComparison.OrdinalIgnoreCase) + ? 1 + : string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); } } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/ComponentSerializer.cs b/Ical.Net/Serialization/ComponentSerializer.cs index 3680915b1..e41af4322 100644 --- a/Ical.Net/Serialization/ComponentSerializer.cs +++ b/Ical.Net/Serialization/ComponentSerializer.cs @@ -11,73 +11,72 @@ using Ical.Net.CalendarComponents; using Ical.Net.Utility; -namespace Ical.Net.Serialization +namespace Ical.Net.Serialization; + +public class ComponentSerializer : SerializerBase { - public class ComponentSerializer : SerializerBase - { - protected virtual IComparer PropertySorter => new PropertyAlphabetizer(); + protected virtual IComparer PropertySorter => new PropertyAlphabetizer(); - public ComponentSerializer() { } + public ComponentSerializer() { } - public ComponentSerializer(SerializationContext ctx) : base(ctx) { } + public ComponentSerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(CalendarComponent); + public override Type TargetType => typeof(CalendarComponent); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + if (!(obj is ICalendarComponent c)) { - if (!(obj is ICalendarComponent c)) - { - return null; - } + return null; + } - var sb = new StringBuilder(); - var upperName = c.Name.ToUpperInvariant(); - sb.Append(TextUtil.FoldLines($"BEGIN:{upperName}")); + var sb = new StringBuilder(); + var upperName = c.Name.ToUpperInvariant(); + sb.Append(TextUtil.FoldLines($"BEGIN:{upperName}")); - // Get a serializer factory - var sf = GetService(); + // 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(); + // 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)); - } + // 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)); + } - sb.Append(TextUtil.FoldLines($"END:{upperName}")); - return sb.ToString(); + // 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)); } - public override object Deserialize(TextReader tr) => null; + sb.Append(TextUtil.FoldLines($"END:{upperName}")); + return sb.ToString(); + } - public class PropertyAlphabetizer : IComparer + public override object Deserialize(TextReader tr) => null; + + public class PropertyAlphabetizer : IComparer + { + public int Compare(ICalendarProperty x, ICalendarProperty y) { - public int Compare(ICalendarProperty x, ICalendarProperty y) + if (x == y) + { + return 0; + } + if (x == null) { - if (x == y) - { - return 0; - } - if (x == null) - { - return -1; - } - return y == null - ? 1 - : string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); + return -1; } + return y == null + ? 1 + : string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); } } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataMapSerializer.cs b/Ical.Net/Serialization/DataMapSerializer.cs index a6025adab..05824ea81 100644 --- a/Ical.Net/Serialization/DataMapSerializer.cs +++ b/Ical.Net/Serialization/DataMapSerializer.cs @@ -7,65 +7,64 @@ using System.IO; using Ical.Net.Serialization.DataTypes; -namespace Ical.Net.Serialization +namespace Ical.Net.Serialization; + +public class DataMapSerializer : SerializerBase { - public class DataMapSerializer : SerializerBase - { - public DataMapSerializer() { } + public DataMapSerializer() { } - public DataMapSerializer(SerializationContext ctx) : base(ctx) { } + public DataMapSerializer(SerializationContext ctx) : base(ctx) { } - protected IStringSerializer GetMappedSerializer() + protected IStringSerializer GetMappedSerializer() + { + var sf = GetService(); + var mapper = GetService(); + if (sf == null || mapper == null) { - var sf = GetService(); - var mapper = GetService(); - if (sf == null || mapper == null) - { - return null; - } + return null; + } - var obj = SerializationContext.Peek(); + var obj = SerializationContext.Peek(); - // Get the data type for this object - var type = mapper.GetPropertyMapping(obj); + // Get the data type for this object + var type = mapper.GetPropertyMapping(obj); - return type == null - ? new StringSerializer(SerializationContext) - : sf.Build(type, SerializationContext) as IStringSerializer; - } + return type == null + ? new StringSerializer(SerializationContext) + : sf.Build(type, SerializationContext) as IStringSerializer; + } - public override Type TargetType + public override Type TargetType + { + get { - get - { - ISerializer serializer = GetMappedSerializer(); - return serializer?.TargetType; - } + ISerializer serializer = GetMappedSerializer(); + return serializer?.TargetType; } + } - public override string SerializeToString(object obj) - { - var serializer = GetMappedSerializer(); - return serializer?.SerializeToString(obj); - } + public override string SerializeToString(object obj) + { + var serializer = GetMappedSerializer(); + return serializer?.SerializeToString(obj); + } - public override object Deserialize(TextReader tr) + public override object Deserialize(TextReader tr) + { + var serializer = GetMappedSerializer(); + if (serializer == null) { - var serializer = GetMappedSerializer(); - if (serializer == null) - { - return null; - } + return null; + } - var value = tr.ReadToEnd(); - var returnValue = serializer.Deserialize(new StringReader(value)); + 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; - } + // 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; } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypeMapper.cs b/Ical.Net/Serialization/DataTypeMapper.cs index 1a78d97c8..872faedc7 100644 --- a/Ical.Net/Serialization/DataTypeMapper.cs +++ b/Ical.Net/Serialization/DataTypeMapper.cs @@ -8,146 +8,145 @@ using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization +namespace Ical.Net.Serialization; + +public delegate Type TypeResolverDelegate(object context); + +internal class DataTypeMapper { - public delegate Type TypeResolverDelegate(object context); + 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(AlarmAction.Name, 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(TriggerRelation.Name, 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); + } - internal class DataTypeMapper + protected Type ResolveStatusProperty(object context) { - private class PropertyMapping + if (!(context is ICalendarObject obj)) { - public Type ObjectType { get; set; } - public TypeResolverDelegate Resolver { get; set; } - public bool AllowsMultipleValuesPerProperty { get; set; } + return null; } - private readonly IDictionary _propertyMap = new Dictionary(StringComparer.OrdinalIgnoreCase); + switch (obj.Parent) + { + case CalendarEvent _: + return typeof(EventStatus); + case Todo _: + return typeof(TodoStatus); + case Journal _: + return typeof(JournalStatus); + } - public DataTypeMapper() + return null; + } + + public void AddPropertyMapping(string name, Type objectType, bool allowsMultipleValues) + { + if (name == null || objectType == null) { - AddPropertyMapping(AlarmAction.Name, 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(TriggerRelation.Name, 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); + return; } - protected Type ResolveStatusProperty(object context) + var m = new PropertyMapping { - if (!(context is ICalendarObject obj)) - { - return null; - } - - switch (obj.Parent) - { - case CalendarEvent _: - return typeof(EventStatus); - case Todo _: - return typeof(TodoStatus); - case Journal _: - return typeof(JournalStatus); - } + ObjectType = objectType, + AllowsMultipleValuesPerProperty = allowsMultipleValues + }; - return null; - } + _propertyMap[name] = m; + } - public void AddPropertyMapping(string name, Type objectType, bool allowsMultipleValues) + public void AddPropertyMapping(string name, TypeResolverDelegate resolver, bool allowsMultipleValues) + { + if (name == null || resolver == null) { - if (name == null || objectType == null) - { - return; - } - - var m = new PropertyMapping - { - ObjectType = objectType, - AllowsMultipleValuesPerProperty = allowsMultipleValues - }; - - _propertyMap[name] = m; + return; } - public void AddPropertyMapping(string name, TypeResolverDelegate resolver, bool allowsMultipleValues) + var m = new PropertyMapping { - if (name == null || resolver == null) - { - return; - } - - var m = new PropertyMapping - { - Resolver = resolver, - AllowsMultipleValuesPerProperty = allowsMultipleValues - }; - - _propertyMap[name] = m; - } + Resolver = resolver, + AllowsMultipleValuesPerProperty = allowsMultipleValues + }; - public void RemovePropertyMapping(string name) + _propertyMap[name] = m; + } + + public void RemovePropertyMapping(string name) + { + if (name != null && _propertyMap.ContainsKey(name)) { - if (name != null && _propertyMap.ContainsKey(name)) - { - _propertyMap.Remove(name); - } + _propertyMap.Remove(name); } + } - public virtual bool GetPropertyAllowsMultipleValues(object obj) + public virtual bool GetPropertyAllowsMultipleValues(object obj) + { + var p = obj as ICalendarProperty; + return !string.IsNullOrWhiteSpace(p?.Name) + && _propertyMap.TryGetValue(p.Name, out var m) + && m.AllowsMultipleValuesPerProperty; + } + + public virtual Type GetPropertyMapping(object obj) + { + var p = obj as ICalendarProperty; + if (p?.Name == null) { - var p = obj as ICalendarProperty; - return !string.IsNullOrWhiteSpace(p?.Name) - && _propertyMap.TryGetValue(p.Name, out var m) - && m.AllowsMultipleValuesPerProperty; + return null; } - public virtual Type GetPropertyMapping(object obj) + if (!_propertyMap.TryGetValue(p.Name, out var m)) { - var p = obj as ICalendarProperty; - if (p?.Name == null) - { - return null; - } - - if (!_propertyMap.TryGetValue(p.Name, out var m)) - { - return null; - } - - return m.Resolver == null - ? m.ObjectType - : m.Resolver(p); + return null; } + + return m.Resolver == null + ? m.ObjectType + : m.Resolver(p); } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypeSerializerFactory.cs b/Ical.Net/Serialization/DataTypeSerializerFactory.cs index c9cfe347b..8fdcc9267 100644 --- a/Ical.Net/Serialization/DataTypeSerializerFactory.cs +++ b/Ical.Net/Serialization/DataTypeSerializerFactory.cs @@ -7,91 +7,90 @@ using Ical.Net.DataTypes; using Ical.Net.Serialization.DataTypes; -namespace Ical.Net.Serialization +namespace Ical.Net.Serialization; + +public class DataTypeSerializerFactory : ISerializerFactory { - 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) { - /// - /// 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) { - if (objectType != null) - { - ISerializer s; - - if (typeof(Attachment).IsAssignableFrom(objectType)) - { - s = new AttachmentSerializer(ctx); - } - else if (typeof(Attendee).IsAssignableFrom(objectType)) - { - s = new AttendeeSerializer(ctx); - } - else if (typeof(IDateTime).IsAssignableFrom(objectType)) - { - s = new DateTimeSerializer(ctx); - } - else if (typeof(FreeBusyEntry).IsAssignableFrom(objectType)) - { - s = new FreeBusyEntrySerializer(ctx); - } - else if (typeof(GeographicLocation).IsAssignableFrom(objectType)) - { - s = new GeographicLocationSerializer(ctx); - } - else if (typeof(Organizer).IsAssignableFrom(objectType)) - { - s = new OrganizerSerializer(ctx); - } - else if (typeof(Period).IsAssignableFrom(objectType)) - { - s = new PeriodSerializer(ctx); - } - else if (typeof(PeriodList).IsAssignableFrom(objectType)) - { - s = new PeriodListSerializer(ctx); - } - else if (typeof(RecurrencePattern).IsAssignableFrom(objectType)) - { - s = new RecurrencePatternSerializer(ctx); - } - else if (typeof(RequestStatus).IsAssignableFrom(objectType)) - { - s = new RequestStatusSerializer(ctx); - } - else if (typeof(StatusCode).IsAssignableFrom(objectType)) - { - s = new StatusCodeSerializer(ctx); - } - else if (typeof(Trigger).IsAssignableFrom(objectType)) - { - s = new TriggerSerializer(ctx); - } - else if (typeof(UtcOffset).IsAssignableFrom(objectType)) - { - s = new UtcOffsetSerializer(ctx); - } - else if (typeof(WeekDay).IsAssignableFrom(objectType)) - { - s = new WeekDaySerializer(ctx); - } - // Default to a string serializer, which simply calls - // ToString() on the value to serialize it. - else - { - s = new StringSerializer(ctx); - } + ISerializer s; - return s; + if (typeof(Attachment).IsAssignableFrom(objectType)) + { + s = new AttachmentSerializer(ctx); } - return null; + else if (typeof(Attendee).IsAssignableFrom(objectType)) + { + s = new AttendeeSerializer(ctx); + } + else if (typeof(IDateTime).IsAssignableFrom(objectType)) + { + s = new DateTimeSerializer(ctx); + } + else if (typeof(FreeBusyEntry).IsAssignableFrom(objectType)) + { + s = new FreeBusyEntrySerializer(ctx); + } + else if (typeof(GeographicLocation).IsAssignableFrom(objectType)) + { + s = new GeographicLocationSerializer(ctx); + } + else if (typeof(Organizer).IsAssignableFrom(objectType)) + { + s = new OrganizerSerializer(ctx); + } + else if (typeof(Period).IsAssignableFrom(objectType)) + { + s = new PeriodSerializer(ctx); + } + else if (typeof(PeriodList).IsAssignableFrom(objectType)) + { + s = new PeriodListSerializer(ctx); + } + else if (typeof(RecurrencePattern).IsAssignableFrom(objectType)) + { + s = new RecurrencePatternSerializer(ctx); + } + else if (typeof(RequestStatus).IsAssignableFrom(objectType)) + { + s = new RequestStatusSerializer(ctx); + } + else if (typeof(StatusCode).IsAssignableFrom(objectType)) + { + s = new StatusCodeSerializer(ctx); + } + else if (typeof(Trigger).IsAssignableFrom(objectType)) + { + s = new TriggerSerializer(ctx); + } + else if (typeof(UtcOffset).IsAssignableFrom(objectType)) + { + s = new UtcOffsetSerializer(ctx); + } + else if (typeof(WeekDay).IsAssignableFrom(objectType)) + { + s = new WeekDaySerializer(ctx); + } + // Default to a string serializer, which simply calls + // ToString() on the value to serialize it. + else + { + s = new StringSerializer(ctx); + } + + return s; } + return null; } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs b/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs index 211548d62..49c5d2e5b 100644 --- a/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/AttachmentSerializer.cs @@ -7,89 +7,88 @@ using System.IO; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class AttachmentSerializer : EncodableDataTypeSerializer { - public class AttachmentSerializer : EncodableDataTypeSerializer - { - public AttachmentSerializer() { } + public AttachmentSerializer() { } - public AttachmentSerializer(SerializationContext ctx) : base(ctx) { } + public AttachmentSerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(Attachment); + public override Type TargetType => typeof(Attachment); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + var a = obj as Attachment; + if (a == null) { - 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 null; + } - return Encode(a, a.Uri.OriginalString); - } - if (a.Data == null) + if (a.Uri != null) + { + if (a.Parameters.ContainsKey("VALUE")) { - return null; + // Ensure no VALUE type is provided + a.Parameters.Remove("VALUE"); } - // Ensure the VALUE type is set to BINARY - a.SetValueType("BINARY"); + 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"); + // BASE64 encoding for BINARY inline attachments. + a.Parameters.Set("ENCODING", "BASE64"); - return Encode(a, a.Data); - } + return Encode(a, a.Data); + } - public Attachment Deserialize(string attachment) + public Attachment Deserialize(string attachment) + { + try { - try - { - var a = CreateAndAssociate() as Attachment; - // Decode the value, if necessary - var data = DecodeData(a, attachment); + 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 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, - AssociatedObject = a.AssociatedObject, - }; - } - - // 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, UriKind.RelativeOrAbsolute); - - - return a; - } - catch + // Get the format of the attachment + var valueType = a.GetValueType(); + if (valueType == typeof(byte[])) { - // ignored + // If the VALUE type is specifically set to BINARY, + // then set the Data property instead. + return new Attachment(data) + { + ValueEncoding = a.ValueEncoding, + AssociatedObject = a.AssociatedObject, + }; } - return null; + // 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, UriKind.RelativeOrAbsolute); + + + return a; + } + catch + { + // ignored } - public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); + return null; } -} + + public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs b/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs index 03ab196ca..780d13c9e 100644 --- a/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/AttendeeSerializer.cs @@ -7,48 +7,47 @@ using System.IO; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class AttendeeSerializer : StringSerializer { - public class AttendeeSerializer : StringSerializer - { - public AttendeeSerializer() { } + public AttendeeSerializer() { } - public AttendeeSerializer(SerializationContext ctx) : base(ctx) { } + public AttendeeSerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(Attendee); + 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 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) + public Attendee Deserialize(string attendee) + { + try { - 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; - } + var a = CreateAndAssociate() as Attendee; + var uriString = Unescape(Decode(a, attendee)); - a.Value = new Uri(uriString); - return a; - } - catch + // Prepend "mailto:" if necessary + if (!uriString.StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) { - // ignored + uriString = "mailto:" + uriString; } - return null; + a.Value = new Uri(uriString); + return a; + } + catch + { + // ignored } - public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); + return null; } -} + + public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs b/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs index ed3e10b87..d82e51830 100644 --- a/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/DataTypeSerializer.cs @@ -6,28 +6,27 @@ using System; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public abstract class DataTypeSerializer : SerializerBase { - public abstract class DataTypeSerializer : SerializerBase - { - protected DataTypeSerializer() { } + protected DataTypeSerializer() { } - protected DataTypeSerializer(SerializationContext ctx) : base(ctx) { } + protected DataTypeSerializer(SerializationContext ctx) : base(ctx) { } - protected virtual ICalendarDataType CreateAndAssociate() + protected virtual ICalendarDataType CreateAndAssociate() + { + // Create an instance of the object + if (!(Activator.CreateInstance(TargetType) is ICalendarDataType dt)) { - // Create an instance of the object - if (!(Activator.CreateInstance(TargetType) is ICalendarDataType dt)) - { - return null; - } - - if (SerializationContext.Peek() is ICalendarObject associatedObject) - { - dt.AssociatedObject = associatedObject; - } + return null; + } - return dt; + if (SerializationContext.Peek() is ICalendarObject associatedObject) + { + dt.AssociatedObject = associatedObject; } + + return dt; } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs b/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs index caf12d4e7..20e67dc24 100644 --- a/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/DateTimeSerializer.cs @@ -9,157 +9,156 @@ using System.Text.RegularExpressions; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class DateTimeSerializer : EncodableDataTypeSerializer { - public class DateTimeSerializer : EncodableDataTypeSerializer - { - public DateTimeSerializer() { } + public DateTimeSerializer() { } - public DateTimeSerializer(SerializationContext ctx) : base(ctx) { } + public DateTimeSerializer(SerializationContext ctx) : base(ctx) { } - private DateTime CoerceDateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + 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 { - 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) { - if (year > 9999) - { - dt = DateTime.MaxValue; - } - else if (year > 0) - { - dt = new DateTime(year, month, day, hour, minute, second, kind); - } + dt = DateTime.MaxValue; + } + else if (year > 0) + { + dt = new DateTime(year, month, day, hour, minute, second, kind); } - catch { } - - return dt; } + catch { } + + return dt; + } - public override Type TargetType => typeof(CalDateTime); + public override Type TargetType => typeof(CalDateTime); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + var dt = obj as IDateTime; + if (dt == null) { - var dt = obj as IDateTime; - if (dt == null) - { - return null; - } + return null; + } - // RFC 5545 3.3.5: - // The date with UTC time, or absolute time, is identified by a LATIN - // CAPITAL LETTER Z suffix character, the UTC designator, appended to - // the time value. The "TZID" property parameter MUST NOT be applied to DATE-TIME - // properties whose time values are specified in UTC. + // RFC 5545 3.3.5: + // The date with UTC time, or absolute time, is identified by a LATIN + // CAPITAL LETTER Z suffix character, the UTC designator, appended to + // the time value. The "TZID" property parameter MUST NOT be applied to DATE-TIME + // properties whose time values are specified in UTC. - var kind = dt.IsUtc - ? DateTimeKind.Utc - : DateTimeKind.Local; + var kind = dt.IsUtc + ? DateTimeKind.Utc + : DateTimeKind.Local; - if (dt.IsUtc) - { - dt.Parameters.Remove("TZID"); - } - else if (!string.IsNullOrWhiteSpace(dt.TzId)) - { - dt.Parameters.Set("TZID", dt.TzId); - } + if (dt.IsUtc) + { + dt.Parameters.Remove("TZID"); + } + else if (!string.IsNullOrWhiteSpace(dt.TzId)) + { + dt.Parameters.Set("TZID", dt.TzId); + } - DateTime.SpecifyKind(dt.Value, kind); + DateTime.SpecifyKind(dt.Value, kind); - // 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"); - } + // 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(); - value.Append($"{dt.Year:0000}{dt.Month:00}{dt.Day:00}"); - if (dt.HasTime) + var value = new StringBuilder(); + 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.IsUtc) { - value.Append($"T{dt.Hour:00}{dt.Minute:00}{dt.Second:00}"); - if (dt.IsUtc) - { - value.Append("Z"); - } + 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, RegexDefaults.Timeout); - internal static readonly Regex FullDateTimePatternMatch = new Regex(@"^((\d{4})(\d{2})(\d{2}))T((\d{2})(\d{2})(\d{2})(Z)?)$", _ciCompiled, RegexDefaults.Timeout); + // Encode the value as necessary + return Encode(dt, value.ToString()); + } - public override object Deserialize(TextReader tr) - { - var value = tr.ReadToEnd(); + private const RegexOptions _ciCompiled = RegexOptions.Compiled | RegexOptions.IgnoreCase; + internal static readonly Regex DateOnlyMatch = new Regex(@"^((\d{4})(\d{2})(\d{2}))?$", _ciCompiled, RegexDefaults.Timeout); + internal static readonly Regex FullDateTimePatternMatch = new Regex(@"^((\d{4})(\d{2})(\d{2}))T((\d{2})(\d{2})(\d{2})(Z)?)$", _ciCompiled, RegexDefaults.Timeout); - var dt = CreateAndAssociate() as IDateTime; - if (dt == null) - { - return null; - } + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); - // Decode the value as necessary - value = Decode(dt, value); + var dt = CreateAndAssociate() as IDateTime; + if (dt == null) + { + return null; + } - var match = FullDateTimePatternMatch.Match(value); - if (!match.Success) - { - match = DateOnlyMatch.Match(value); - } + // Decode the value as necessary + value = Decode(dt, value); - if (!match.Success) - { - return null; - } - var now = DateTime.Now; + var match = FullDateTimePatternMatch.Match(value); + if (!match.Success) + { + match = DateOnlyMatch.Match(value); + } - var year = now.Year; - var month = now.Month; - var date = now.Day; - var hour = 0; - var minute = 0; - var second = 0; + if (!match.Success) + { + return null; + } + var now = DateTime.Now; - 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); - } + var year = now.Year; + var month = now.Month; + var date = now.Day; + var hour = 0; + var minute = 0; + var second = 0; - var isUtc = match.Groups[9].Success; - var kind = isUtc - ? DateTimeKind.Utc - : DateTimeKind.Local; + 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 (isUtc) - { - dt.TzId = "UTC"; - } + var isUtc = match.Groups[9].Success; + var kind = isUtc + ? DateTimeKind.Utc + : DateTimeKind.Local; - dt.Value = CoerceDateTime(year, month, date, hour, minute, second, kind); - return dt; + if (isUtc) + { + dt.TzId = "UTC"; } + + dt.Value = CoerceDateTime(year, month, date, hour, minute, second, kind); + return dt; } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs b/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs index 794713924..4edae16d6 100644 --- a/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/EncodableDataTypeSerializer.cs @@ -5,83 +5,82 @@ using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public abstract class EncodableDataTypeSerializer : DataTypeSerializer { - public abstract class EncodableDataTypeSerializer : DataTypeSerializer - { - protected EncodableDataTypeSerializer() { } + protected EncodableDataTypeSerializer() { } - protected EncodableDataTypeSerializer(SerializationContext ctx) : base(ctx) { } + protected EncodableDataTypeSerializer(SerializationContext ctx) : base(ctx) { } - protected string Encode(IEncodableDataType dt, string value) + protected string Encode(IEncodableDataType dt, string value) + { + if (value == null) { - if (value == null) - { - return null; - } + return null; + } + + if (dt?.Encoding == null) + { + return value; + } - if (dt?.Encoding == null) - { - return value; - } + // Return the value in the current encoding + var encodingStack = GetService(); + return Encode(dt, encodingStack.Current.GetBytes(value)); + } - // Return the value in the current encoding + 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 Encode(dt, encodingStack.Current.GetBytes(value)); + return encodingStack.Current.GetString(data); } - protected string Encode(IEncodableDataType dt, byte[] data) + var encodingProvider = GetService(); + return encodingProvider?.Encode(dt.Encoding, data); + } + + protected string Decode(IEncodableDataType dt, string value) + { + if (dt?.Encoding == null) { - 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); + return value; } - protected string Decode(IEncodableDataType dt, string value) + var data = DecodeData(dt, value); + if (data == null) { - if (dt?.Encoding == null) - { - return value; - } + return null; + } - var data = DecodeData(dt, value); - if (data == null) - { - return null; - } + // Default to the current encoding + var encodingStack = GetService(); + return encodingStack.Current.GetString(data); + } - // 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; } - protected byte[] DecodeData(IEncodableDataType dt, string value) + if (dt?.Encoding == null) { - 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); + // 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/Ical.Net/Serialization/DataTypes/EnumSerializer.cs b/Ical.Net/Serialization/DataTypes/EnumSerializer.cs index 93e918fd9..d4ee20809 100644 --- a/Ical.Net/Serialization/DataTypes/EnumSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/EnumSerializer.cs @@ -7,69 +7,68 @@ using System.IO; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class EnumSerializer : EncodableDataTypeSerializer { - public class EnumSerializer : EncodableDataTypeSerializer - { - private readonly Type _mEnumType; + private readonly Type _mEnumType; - public EnumSerializer(Type enumType) - { - _mEnumType = enumType; - } + public EnumSerializer(Type enumType) + { + _mEnumType = enumType; + } - public EnumSerializer(Type enumType, SerializationContext ctx) : base(ctx) - { - _mEnumType = enumType; - } + public EnumSerializer(Type enumType, SerializationContext ctx) : base(ctx) + { + _mEnumType = enumType; + } - public override Type TargetType => _mEnumType; + public override Type TargetType => _mEnumType; - public override string SerializeToString(object enumValue) + public override string SerializeToString(object enumValue) + { + try { - try + var obj = SerializationContext.Peek() as ICalendarObject; + if (obj != null) { - var obj = SerializationContext.Peek() as ICalendarObject; - if (obj != null) + // Encode the value as needed. + var dt = new EncodableDataType { - // Encode the value as needed. - var dt = new EncodableDataType - { - AssociatedObject = obj - }; - return Encode(dt, enumValue.ToString()); - } - return enumValue.ToString(); - } - catch - { - return null; + AssociatedObject = obj + }; + return Encode(dt, enumValue.ToString()); } + return enumValue.ToString(); } - - public override object Deserialize(TextReader tr) + catch { - var value = tr.ReadToEnd(); + return null; + } + } - try + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); + + try + { + var obj = SerializationContext.Peek() as ICalendarObject; + if (obj != null) { - var obj = SerializationContext.Peek() as ICalendarObject; - if (obj != null) + // Decode the value, if necessary! + var dt = new EncodableDataType { - // 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); + AssociatedObject = obj + }; + value = Decode(dt, value); } - catch { } - return 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/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs b/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs index dbe86a6a0..4bf6fea6c 100644 --- a/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs +++ b/Ical.Net/Serialization/DataTypes/FreeBusyEntrySerializer.cs @@ -7,79 +7,78 @@ using System.IO; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class FreeBusyEntrySerializer : PeriodSerializer { - public class FreeBusyEntrySerializer : PeriodSerializer - { - public FreeBusyEntrySerializer() { } + public FreeBusyEntrySerializer() { } - public FreeBusyEntrySerializer(SerializationContext ctx) : base(ctx) { } + public FreeBusyEntrySerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(FreeBusyEntry); + public override Type TargetType => typeof(FreeBusyEntry); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + var entry = obj as FreeBusyEntry; + if (entry == null) { - var entry = obj as FreeBusyEntry; - if (entry == null) - { - return base.SerializeToString(obj); - } - - 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) + switch (entry.Status) { - var entry = base.Deserialize(tr) as FreeBusyEntry; - if (entry == null) - { - return entry; - } + 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; + } - if (!entry.Parameters.ContainsKey("FBTYPE")) - { - return entry; - } + return base.SerializeToString(obj); + } - var value = entry.Parameters.Get("FBTYPE"); - if (value == null) - { - return entry; - } + public override object Deserialize(TextReader tr) + { + var entry = base.Deserialize(tr) as FreeBusyEntry; + if (entry == 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; - } + if (!entry.Parameters.ContainsKey("FBTYPE")) + { + return entry; + } + 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/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs b/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs index e02709655..f2f36b665 100644 --- a/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/GeographicLocationSerializer.cs @@ -8,61 +8,60 @@ using System.IO; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class GeographicLocationSerializer : EncodableDataTypeSerializer { - public class GeographicLocationSerializer : EncodableDataTypeSerializer - { - public GeographicLocationSerializer() { } + public GeographicLocationSerializer() { } - public GeographicLocationSerializer(SerializationContext ctx) : base(ctx) { } + public GeographicLocationSerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(GeographicLocation); + public override Type TargetType => typeof(GeographicLocation); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + var g = obj as GeographicLocation; + if (g == null) { - 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); + return null; } - public GeographicLocation Deserialize(string value) - { - if (string.IsNullOrWhiteSpace(value)) - { - return null; - } - - var g = CreateAndAssociate() 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); + } - // Decode the value, if necessary! - value = Decode(g, value); + public GeographicLocation Deserialize(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + return null; + } - var values = value.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - if (values.Length != 2) - { - return null; - } + var g = CreateAndAssociate() as GeographicLocation; + if (g == null) + { + 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; + // Decode the value, if necessary! + value = Decode(g, value); - return g; + var values = value.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + if (values.Length != 2) + { + return null; } - public override object Deserialize(TextReader tr) => Deserialize(tr.ReadToEnd()); + 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/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs b/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs index b71c81383..a63aafc0a 100644 --- a/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/IntegerSerializer.cs @@ -7,66 +7,65 @@ using System.IO; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class IntegerSerializer : EncodableDataTypeSerializer { - public class IntegerSerializer : EncodableDataTypeSerializer - { - public IntegerSerializer() { } + public IntegerSerializer() { } - public IntegerSerializer(SerializationContext ctx) : base(ctx) { } + public IntegerSerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(int); + public override Type TargetType => typeof(int); - public override string SerializeToString(object integer) + public override string SerializeToString(object integer) + { + try { - try - { - var i = Convert.ToInt32(integer); + 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 + var obj = SerializationContext.Peek() as ICalendarObject; + if (obj != null) { - return null; + // Encode the value as needed. + var dt = new EncodableDataType + { + AssociatedObject = obj + }; + return Encode(dt, i.ToString()); } + return i.ToString(); } - - public override object Deserialize(TextReader tr) + catch { - var value = tr.ReadToEnd(); + return null; + } + } - 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); - } + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); - int i; - if (Int32.TryParse(value, out i)) + try + { + var obj = SerializationContext.Peek() as ICalendarObject; + if (obj != null) + { + // Decode the value, if necessary! + var dt = new EncodableDataType { - return i; - } + AssociatedObject = obj + }; + value = Decode(dt, value); } - catch { } - return value; + int i; + if (Int32.TryParse(value, out i)) + { + return i; + } } + catch { } + + return value; } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs b/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs index 0d543a300..a41f8bc70 100644 --- a/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/OrganizerSerializer.cs @@ -7,55 +7,54 @@ using System.IO; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class OrganizerSerializer : StringSerializer { - public class OrganizerSerializer : StringSerializer - { - public OrganizerSerializer() { } + public OrganizerSerializer() { } - public OrganizerSerializer(SerializationContext ctx) : base(ctx) { } + public OrganizerSerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(Organizer); + public override Type TargetType => typeof(Organizer); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + try { - try - { - var o = obj as Organizer; - return o?.Value == null - ? null - : Encode(o, Escape(o.Value.OriginalString)); - } - catch - { - return null; - } + var o = obj as Organizer; + return o?.Value == null + ? null + : Encode(o, Escape(o.Value.OriginalString)); } - - public override object Deserialize(TextReader tr) + catch { - var value = tr.ReadToEnd(); + return null; + } + } - Organizer o = null; - try - { - o = CreateAndAssociate() as Organizer; - if (o != null) - { - var uriString = Unescape(Decode(o, value)); + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); - // Prepend "mailto:" if necessary - if (!uriString.StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) - { - uriString = "mailto:" + uriString; - } + Organizer o = null; + try + { + o = CreateAndAssociate() as Organizer; + if (o != null) + { + var uriString = Unescape(Decode(o, value)); - o.Value = new Uri(uriString); + // Prepend "mailto:" if necessary + if (!uriString.StartsWith("mailto:", StringComparison.OrdinalIgnoreCase)) + { + uriString = "mailto:" + uriString; } - } - catch { } - return o; + o.Value = new Uri(uriString); + } } + catch { } + + return o; } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs b/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs index c5c9c496b..7dd3f6993 100644 --- a/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/PeriodListSerializer.cs @@ -8,89 +8,88 @@ using System.IO; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class PeriodListSerializer : EncodableDataTypeSerializer { - public class PeriodListSerializer : EncodableDataTypeSerializer + public PeriodListSerializer() { } + + public PeriodListSerializer(SerializationContext ctx) : base(ctx) { } + + public override Type TargetType => typeof(PeriodList); + + public override string SerializeToString(object obj) { - public PeriodListSerializer() { } + var periodList = obj as PeriodList; + var factory = GetService(); + if (periodList == null || factory == null) + { + return null; + } - public PeriodListSerializer(SerializationContext ctx) : base(ctx) { } + 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; + } - public override Type TargetType => typeof(PeriodList); + var parts = new List(periodList.Count); - public override string SerializeToString(object obj) + foreach (var p in periodList) { - var periodList = obj as PeriodList; - var factory = GetService(); - if (periodList == null || factory == null) + if (p.EndTime != null) { - return null; + parts.Add(periodSerializer.SerializeToString(p)); } - - var dtSerializer = factory.Build(typeof(IDateTime), SerializationContext) as IStringSerializer; - var periodSerializer = factory.Build(typeof(Period), SerializationContext) as IStringSerializer; - if (dtSerializer == null || periodSerializer == null) + else if (p.StartTime != null) { - return null; + parts.Add(dtSerializer.SerializeToString(p.StartTime)); } + } - var parts = new List(periodList.Count); + return Encode(periodList, string.Join(",", parts)); + } - foreach (var p in periodList) - { - if (p.EndTime != null) - { - parts.Add(periodSerializer.SerializeToString(p)); - } - else if (p.StartTime != null) - { - parts.Add(dtSerializer.SerializeToString(p.StartTime)); - } - } + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); - return Encode(periodList, string.Join(",", parts)); + // 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; } - public override object Deserialize(TextReader tr) - { - var value = tr.ReadToEnd(); + // Decode the value, if necessary + value = Decode(rdt, value); - // 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; - } + 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; + } - // Decode the value, if necessary - value = Decode(rdt, value); + 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; - var dtSerializer = factory.Build(typeof(IDateTime), SerializationContext) as IStringSerializer; - var periodSerializer = factory.Build(typeof(Period), SerializationContext) as IStringSerializer; - if (dtSerializer == null || periodSerializer == null) + if (dt != null) { - return null; + dt.AssociatedObject = rdt.AssociatedObject; + rdt.Add(dt); } - - var values = value.Split(','); - foreach (var v in values) + else if (p != null) { - 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); - } + p.AssociatedObject = rdt.AssociatedObject; + rdt.Add(p); } - return rdt; } + return rdt; } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs b/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs index 49940c703..a0d828b76 100644 --- a/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/PeriodSerializer.cs @@ -8,107 +8,106 @@ using System.Text; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class PeriodSerializer : EncodableDataTypeSerializer { - public class PeriodSerializer : EncodableDataTypeSerializer - { - public PeriodSerializer() { } + public PeriodSerializer() { } - public PeriodSerializer(SerializationContext ctx) : base(ctx) { } + public PeriodSerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(Period); + public override Type TargetType => typeof(Period); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + var p = obj as Period; + var factory = GetService(); + + if (p == null || factory == null) { - var p = obj as Period; - var factory = GetService(); + return null; + } + + // Push the period onto the serialization context stack + SerializationContext.Push(p); - if (p == null || factory == null) + try + { + var dtSerializer = factory.Build(typeof(IDateTime), SerializationContext) as IStringSerializer; + var timeSpanSerializer = factory.Build(typeof(TimeSpan), SerializationContext) as IStringSerializer; + if (dtSerializer == null || timeSpanSerializer == null) { return null; } + var sb = new StringBuilder(); - // Push the period onto the serialization context stack - SerializationContext.Push(p); + // Serialize the start time + sb.Append(dtSerializer.SerializeToString(p.StartTime)); - try + // Serialize the duration or end time + if (p.EndTime.HasTime) { - var dtSerializer = factory.Build(typeof(IDateTime), SerializationContext) as IStringSerializer; - var timeSpanSerializer = factory.Build(typeof(TimeSpan), SerializationContext) as IStringSerializer; - if (dtSerializer == null || timeSpanSerializer == null) - { - return null; - } - var sb = new StringBuilder(); - - // Serialize the start time - sb.Append(dtSerializer.SerializeToString(p.StartTime)); - - // Serialize the duration or end time - if (p.EndTime.HasTime) - { - // serialize the end time - sb.Append("/"); - sb.Append(dtSerializer.SerializeToString(p.EndTime)); - } - else - { - // Serialize the duration - sb.Append("/"); - sb.Append(timeSpanSerializer.SerializeToString(p.Duration)); - } - - // Encode the value as necessary - return Encode(p, sb.ToString()); + // serialize the end time + sb.Append("/"); + sb.Append(dtSerializer.SerializeToString(p.EndTime)); } - finally + else { - // Pop the period off the serialization context stack - SerializationContext.Pop(); + // Serialize the duration + sb.Append("/"); + sb.Append(timeSpanSerializer.SerializeToString(p.Duration)); } - } - public override object Deserialize(TextReader tr) + // Encode the value as necessary + return Encode(p, sb.ToString()); + } + finally { - var value = tr.ReadToEnd(); + // Pop the period off the serialization context stack + SerializationContext.Pop(); + } + } - var p = CreateAndAssociate() as Period; - var factory = GetService(); - if (p == null || factory == null) - { - return null; - } + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); - var dtSerializer = factory.Build(typeof(IDateTime), SerializationContext) as IStringSerializer; - var durationSerializer = factory.Build(typeof(TimeSpan), SerializationContext) as IStringSerializer; - if (dtSerializer == null || durationSerializer == null) - { - return null; - } + var p = CreateAndAssociate() as Period; + var factory = GetService(); + if (p == null || factory == null) + { + return null; + } - // Decode the value as necessary - value = Decode(p, value); + var dtSerializer = factory.Build(typeof(IDateTime), SerializationContext) as IStringSerializer; + var durationSerializer = factory.Build(typeof(TimeSpan), SerializationContext) as IStringSerializer; + if (dtSerializer == null || durationSerializer == null) + { + return null; + } - var values = value.Split('/'); - if (values.Length != 2) - { - return false; - } + // Decode the value as necessary + value = Decode(p, value); - 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])); - } + var values = value.Split('/'); + if (values.Length != 2) + { + return false; + } - // Only return an object if it has been deserialized correctly. - if (p.StartTime != null && p.Duration != null) - { - return p; - } + 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])); + } - return null; + // 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/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs b/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs index 598012648..0d7e95971 100644 --- a/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs @@ -10,488 +10,487 @@ using System.Text.RegularExpressions; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class RecurrencePatternSerializer : EncodableDataTypeSerializer { - public class RecurrencePatternSerializer : EncodableDataTypeSerializer - { - public RecurrencePatternSerializer() { } + public RecurrencePatternSerializer() { } - public RecurrencePatternSerializer(SerializationContext ctx) : base(ctx) { } + public RecurrencePatternSerializer(SerializationContext ctx) : base(ctx) { } - public static DayOfWeek GetDayOfWeek(string value) + public static DayOfWeek GetDayOfWeek(string value) + { + switch (value.ToUpper()) { - 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."); + 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) + protected static void AddInt32Values(IList list, string value) + { + var values = value.Split(','); + foreach (var v in values) { - var values = value.Split(','); - foreach (var v in values) - { - list.Add(Convert.ToInt32(v)); - } + list.Add(Convert.ToInt32(v)); } + } - public virtual void CheckRange(string name, IList values, int min, int max) + public virtual void CheckRange(string name, IList values, int min, int max) + { + var allowZero = (min == 0 || max == 0); + foreach (var value in values) { - var allowZero = (min == 0 || max == 0); - foreach (var value in values) - { - CheckRange(name, value, min, max, allowZero); - } + CheckRange(name, value, min, max, allowZero); } + } - public virtual void CheckRange(string name, int value, int min, int max) + 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))) { - var allowZero = min == 0 || max == 0; - CheckRange(name, value, min, max, allowZero); + throw new ArgumentException(name + " value " + value + " is out of range. Valid values are between " + min + " and " + max + + (allowZero ? "" : ", excluding zero (0)") + "."); } + } - public virtual void CheckRange(string name, int value, int min, int max, bool allowZero) + public virtual void CheckMutuallyExclusive(string name1, string name2, T obj1, TU obj2) + { + if (Equals(obj1, default(T)) || Equals(obj2, default(TU))) { - 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)") + "."); - } + return; } + // If the object is MinValue instead of its default, consider + // that to be unassigned. - 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 t1 = obj1.GetType(); + var fi1 = t1.GetField("MinValue"); + var fi2 = t1.GetField("MinValue"); - 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; + } - 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."); + } - 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()))); } + } - private void SerializeByValue(List aggregate, IList byValue, string name) + 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) { - if (byValue.Any()) - { - aggregate.Add(name + "=" + string.Join(",", byValue.Select(i => i.ToString()))); - } + return null; } - public override Type TargetType => typeof(RecurrencePattern); + // Push the recurrence pattern onto the serialization stack + SerializationContext.Push(recur); + var values = new List() + { + "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; + } - public override string SerializeToString(object obj) + if (interval != 1) { - var recur = obj as RecurrencePattern; - var factory = GetService(); - if (recur == null || factory == null) - { - return null; - } + values.Add("INTERVAL=" + interval); + } - // Push the recurrence pattern onto the serialization stack - SerializationContext.Push(recur); - var values = new List() - { - "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) + if (recur.Until != DateTime.MinValue) + { + var serializer = factory.Build(typeof(IDateTime), SerializationContext) as IStringSerializer; + if (serializer != null) { - interval = 1; + IDateTime until = new CalDateTime(recur.Until); + until.HasTime = true; + values.Add("UNTIL=" + serializer.SerializeToString(until)); } + } - if (interval != 1) - { - values.Add("INTERVAL=" + interval); - } + if (recur.FirstDayOfWeek != DayOfWeek.Monday) + { + values.Add("WKST=" + Enum.GetName(typeof(DayOfWeek), recur.FirstDayOfWeek).ToUpper().Substring(0, 2)); + } - 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.Count != int.MinValue) + { + values.Add("COUNT=" + recur.Count); + } - if (recur.FirstDayOfWeek != DayOfWeek.Monday) - { - values.Add("WKST=" + Enum.GetName(typeof(DayOfWeek), recur.FirstDayOfWeek).ToUpper().Substring(0, 2)); - } + if (recur.ByDay.Count > 0) + { + var bydayValues = new List(recur.ByDay.Count); - if (recur.Count != int.MinValue) + var serializer = factory.Build(typeof(WeekDay), SerializationContext) as IStringSerializer; + if (serializer != null) { - values.Add("COUNT=" + recur.Count); + bydayValues.AddRange(recur.ByDay.Select(byday => serializer.SerializeToString(byday))); } - if (recur.ByDay.Count > 0) - { - var bydayValues = new List(recur.ByDay.Count); + values.Add("BYDAY=" + string.Join(",", bydayValues)); + } - var serializer = factory.Build(typeof(WeekDay), SerializationContext) as IStringSerializer; - if (serializer != null) - { - bydayValues.AddRange(recur.ByDay.Select(byday => serializer.SerializeToString(byday))); - } + 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"); - values.Add("BYDAY=" + string.Join(",", bydayValues)); - } + // Pop the recurrence pattern off the serialization stack + SerializationContext.Pop(); - 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"); + return Encode(recur, string.Join(";", values)); + } - // Pop the recurrence pattern off the serialization stack - SerializationContext.Pop(); + //Compiling these is a one-time penalty of about 80ms + private const RegexOptions _ciCompiled = RegexOptions.IgnoreCase | RegexOptions.Compiled; - return Encode(recur, string.Join(";", values)); - } + 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, RegexDefaults.Timeout); - //Compiling these is a one-time penalty of about 80ms - private const RegexOptions _ciCompiled = RegexOptions.IgnoreCase | RegexOptions.Compiled; + internal static readonly Regex AdverbFrequencies = new Regex(@"FREQ=(SECONDLY|MINUTELY|HOURLY|DAILY|WEEKLY|MONTHLY|YEARLY);?(.*)", _ciCompiled, RegexDefaults.Timeout); - 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, RegexDefaults.Timeout); + internal static readonly Regex NumericTemporalUnits = new Regex(@"(?\d+)\w\w\s+(?second|minute|hour|day|week|month)", _ciCompiled, RegexDefaults.Timeout); - internal static readonly Regex AdverbFrequencies = new Regex(@"FREQ=(SECONDLY|MINUTELY|HOURLY|DAILY|WEEKLY|MONTHLY|YEARLY);?(.*)", _ciCompiled, RegexDefaults.Timeout); + internal static readonly Regex TemporalUnitType = new Regex(@"(?second|minute|hour|day|week|month)\s+(?\d+)", _ciCompiled, RegexDefaults.Timeout); - internal static readonly Regex NumericTemporalUnits = new Regex(@"(?\d+)\w\w\s+(?second|minute|hour|day|week|month)", _ciCompiled, RegexDefaults.Timeout); - - internal static readonly Regex TemporalUnitType = new Regex(@"(?second|minute|hour|day|week|month)\s+(?\d+)", _ciCompiled, RegexDefaults.Timeout); + 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, RegexDefaults.Timeout); - 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, RegexDefaults.Timeout); + internal static readonly Regex Time = new Regex(@"at\s+(?\d{1,2})(:(?\d{2})((:|\.)(?\d{2}))?)?\s*(?(a|p)m?)?", + _ciCompiled, RegexDefaults.Timeout); - internal static readonly Regex Time = new Regex(@"at\s+(?\d{1,2})(:(?\d{2})((:|\.)(?\d{2}))?)?\s*(?(a|p)m?)?", - _ciCompiled, RegexDefaults.Timeout); + internal static readonly Regex RecurUntil = new Regex(@"^\s*until\s+(?.+)$", _ciCompiled, RegexDefaults.Timeout); - internal static readonly Regex RecurUntil = new Regex(@"^\s*until\s+(?.+)$", _ciCompiled, RegexDefaults.Timeout); + internal static readonly Regex SpecificRecurrenceCount = new Regex(@"^\s*for\s+(?\d+)\s+occurrences\s*$", _ciCompiled, RegexDefaults.Timeout); - internal static readonly Regex SpecificRecurrenceCount = new Regex(@"^\s*for\s+(?\d+)\s+occurrences\s*$", _ciCompiled, RegexDefaults.Timeout); + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); - public override object Deserialize(TextReader tr) + // Instantiate the data type + var r = CreateAndAssociate() as RecurrencePattern; + var factory = GetService(); + if (r == null || factory == null) { - var value = tr.ReadToEnd(); + return r; + } - // 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); - // 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); - var match = AdverbFrequencies.Match(value); - if (match.Success) + // 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) { - // 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 keywordPairs = match.Groups[2].Value.Split(';'); - foreach (var keywordPair in keywordPairs) - { - var keyValues = keywordPair.Split('='); - var keyword = keyValues[0]; - var keyValue = keyValues[1]; + var keyValues = keywordPair.Split('='); + var keyword = keyValues[0]; + var keyValue = keyValues[1]; - switch (keyword.ToUpper()) + 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": { - 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; + 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) + // + // This matches strings such as: + // + // "Every 6 minutes" + // "Every 3 days" + // + else if ((match = OtherInterval.Match(value)).Success) + { + if (match.Groups["Interval"].Success) { - if (match.Groups["Interval"].Success) - { - int interval; - r.Interval = !int.TryParse(match.Groups["Interval"].Value, out interval) - ? 2 - : interval; - } - else - { - r.Interval = 1; - } + 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; - } + 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) + var values = match.Groups["More"].Value.Split(','); + foreach (var item in values) + { + if ((match = NumericTemporalUnits.Match(item)).Success || (match = TemporalUnitType.Match(item)).Success) { - if ((match = NumericTemporalUnits.Match(item)).Success || (match = TemporalUnitType.Match(item)).Success) + int num; + if (!int.TryParse(match.Groups["Num"].Value, out num)) { - int num; - if (!int.TryParse(match.Groups["Num"].Value, out num)) - { - continue; - } + 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; - } + 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) + } + else if ((match = RelativeDaysOfWeek.Match(item)).Success) + { + var num = int.MinValue; + if (match.Groups["Num"].Success) { - var num = int.MinValue; - if (match.Groups["Num"].Success) + if (int.TryParse(match.Groups["Num"].Value, out num)) { - if (int.TryParse(match.Groups["Num"].Value, out num)) + if (match.Groups["Last"].Success) { - if (match.Groups["Last"].Success) - { - // Make number negative - num *= -1; - } + // 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) + else if (match.Groups["Last"].Success) { - int hour; - - if (!int.TryParse(match.Groups["Hour"].Value, out hour)) - { - continue; - } + num = -1; + } + else if (match.Groups["First"].Success) + { + num = 1; + } - // Adjust for PM - if (match.Groups["Meridian"].Success && match.Groups["Meridian"].Value.ToUpper().StartsWith("P")) - { - hour += 12; - } + 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.ByHour.Add(hour); + r.ByDay.AddRange(dayOfWeekQuery); + } + else if ((match = Time.Match(item)).Success) + { + int 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) + if (!int.TryParse(match.Groups["Hour"].Value, out hour)) { - var dt = DateTime.Parse(match.Groups["DateTime"].Value); - DateTime.SpecifyKind(dt, DateTimeKind.Utc); + continue; + } - r.Until = dt; + // Adjust for PM + if (match.Groups["Meridian"].Success && match.Groups["Meridian"].Value.ToUpper().StartsWith("P")) + { + hour += 12; } - else if ((match = SpecificRecurrenceCount.Match(item)).Success) + + r.ByHour.Add(hour); + + int minute; + if (match.Groups["Minute"].Success && int.TryParse(match.Groups["Minute"].Value, out minute)) { - int count; - if (!int.TryParse(match.Groups["Count"].Value, out count)) + r.ByMinute.Add(minute); + int second; + if (match.Groups["Second"].Success && int.TryParse(match.Groups["Second"].Value, out second)) { - return false; + r.BySecond.Add(second); } - r.Count = count; } } - } - else - { - // Couldn't parse the object, return null! - r = null; - } + else if ((match = RecurUntil.Match(item)).Success) + { + var dt = DateTime.Parse(match.Groups["DateTime"].Value); + DateTime.SpecifyKind(dt, DateTimeKind.Utc); - if (r == null) - { - return r; + 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; + } - 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); - + 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/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs b/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs index 4bbdd16b2..429131bf3 100644 --- a/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/RequestStatusSerializer.cs @@ -9,118 +9,117 @@ using System.Text.RegularExpressions; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class RequestStatusSerializer : StringSerializer { - public class RequestStatusSerializer : StringSerializer - { - public RequestStatusSerializer() { } + public RequestStatusSerializer() { } - public RequestStatusSerializer(SerializationContext ctx) : base(ctx) { } + public RequestStatusSerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(RequestStatus); + public override Type TargetType => typeof(RequestStatus); - public override string SerializeToString(object obj) + 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 rs = obj as RequestStatus; - if (rs == null) + var factory = GetService(); + var serializer = factory?.Build(typeof(StatusCode), SerializationContext) as IStringSerializer; + if (serializer == null) { return null; } - // Push the object onto the serialization stack - SerializationContext.Push(rs); - - try + var builder = new StringBuilder(); + builder.Append(Escape(serializer.SerializeToString(rs.StatusCode))); + builder.Append(";"); + builder.Append(Escape(rs.Description)); + if (!string.IsNullOrWhiteSpace(rs.ExtraData)) { - var factory = GetService(); - var serializer = factory?.Build(typeof(StatusCode), SerializationContext) as IStringSerializer; - if (serializer == null) - { - return null; - } - - var builder = new StringBuilder(); - 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(); + builder.Append(Escape(rs.ExtraData)); } + return Encode(rs, builder.ToString()); } - catch + finally { - return null; + // Pop the object off the serialization stack + SerializationContext.Pop(); } } + catch + { + return null; + } + } + + internal static readonly Regex NarrowRequestMatch = new Regex(@"(.*?[^\\]);(.*?[^\\]);(.+)", RegexOptions.Compiled, RegexDefaults.Timeout); + internal static readonly Regex BroadRequestMatch = new Regex(@"(.*?[^\\]);(.+)", RegexOptions.Compiled, RegexDefaults.Timeout); - internal static readonly Regex NarrowRequestMatch = new Regex(@"(.*?[^\\]);(.*?[^\\]);(.+)", RegexOptions.Compiled, RegexDefaults.Timeout); - internal static readonly Regex BroadRequestMatch = new Regex(@"(.*?[^\\]);(.+)", RegexOptions.Compiled, RegexDefaults.Timeout); + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); - public override object Deserialize(TextReader tr) + var rs = CreateAndAssociate() as RequestStatus; + if (rs == null) { - var value = tr.ReadToEnd(); + return null; + } - var rs = CreateAndAssociate() as RequestStatus; - if (rs == 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; } - // Decode the value as needed - value = Decode(rs, value); - - // Push the object onto the serialization stack - SerializationContext.Push(rs); + var match = NarrowRequestMatch.Match(value); + if (!match.Success) + { + match = BroadRequestMatch.Match(value); + } - try + if (match.Success) { - var factory = GetService(); - if (factory == null) + var serializer = factory.Build(typeof(StatusCode), SerializationContext) as IStringSerializer; + if (serializer == null) { return null; } - var match = NarrowRequestMatch.Match(value); - if (!match.Success) + 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) { - match = BroadRequestMatch.Match(value); + rs.ExtraData = Unescape(match.Groups[3].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 rs; } - return null; } + finally + { + // Pop the object off the serialization stack + SerializationContext.Pop(); + } + return null; } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs b/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs index 345857ff5..634e5c9f6 100644 --- a/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/StatusCodeSerializer.cs @@ -8,66 +8,65 @@ using System.Text.RegularExpressions; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class StatusCodeSerializer : StringSerializer { - public class StatusCodeSerializer : StringSerializer - { - public StatusCodeSerializer() { } + public StatusCodeSerializer() { } - public StatusCodeSerializer(SerializationContext ctx) : base(ctx) { } + public StatusCodeSerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(StatusCode); + public override Type TargetType => typeof(StatusCode); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + var sc = obj as StatusCode; + if (sc == null) { - var sc = obj as StatusCode; - if (sc == null) - { - return 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))); + 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, RegexDefaults.Timeout); + internal static readonly Regex StatusCode = new Regex(@"\d(\.\d+)*", RegexOptions.Compiled | RegexOptions.CultureInvariant, RegexDefaults.Timeout); - public override object Deserialize(TextReader tr) - { - var value = tr.ReadToEnd(); + public override object Deserialize(TextReader tr) + { + var value = tr.ReadToEnd(); - var sc = CreateAndAssociate() as StatusCode; - if (sc == null) - { - return null; - } + var sc = CreateAndAssociate() as StatusCode; + if (sc == null) + { + return null; + } - // Decode the value as needed - value = Decode(sc, value); + // Decode the value as needed + value = Decode(sc, value); - var match = StatusCode.Match(value); - if (!match.Success) - { - return null; - } + 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++) + 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)) { - int num; - if (!int.TryParse(parts[i], out num)) - { - return false; - } - iparts[i] = num; + return false; } - - return new StatusCode(iparts); + iparts[i] = num; } + + return new StatusCode(iparts); } -} +} \ No newline at end of file diff --git a/Ical.Net/Serialization/DataTypes/StringSerializer.cs b/Ical.Net/Serialization/DataTypes/StringSerializer.cs index 192b4fcc2..132426c49 100644 --- a/Ical.Net/Serialization/DataTypes/StringSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/StringSerializer.cs @@ -11,148 +11,147 @@ using System.Text.RegularExpressions; using Ical.Net.DataTypes; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class StringSerializer : EncodableDataTypeSerializer { - public class StringSerializer : EncodableDataTypeSerializer - { - public StringSerializer() { } + public StringSerializer() { } - public StringSerializer(SerializationContext ctx) : base(ctx) { } + public StringSerializer(SerializationContext ctx) : base(ctx) { } - internal static readonly Regex SingleBackslashMatch = new Regex(@"(? typeof(string); + // NOTE: fixed a bug that caused text parsing to fail on + // programmatically entered strings. + // SEE unit test SERIALIZE25(). + value = value.Replace(SerializationConstants.LineBreak, @"\n"); + value = value.Replace("\r", @"\n"); + value = value.Replace("\n", @"\n"); + value = value.Replace(";", @"\;"); + value = value.Replace(",", @"\,"); + return value; + } + + public override Type TargetType => typeof(string); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + if (obj == null) { - if (obj == null) - { - return null; - } + return null; + } - var values = new List(); - 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 values = new List(); + 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) + var co = SerializationContext.Peek() as ICalendarObject; + if (co != null) + { + // Encode the string as needed. + var dt = new EncodableDataType { - // 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); - } - + AssociatedObject = co + }; for (var i = 0; i < values.Count; i++) { - values[i] = Escape(values[i]); + values[i] = Encode(dt, Escape(values[i])); } + return string.Join(",", values); } - internal static readonly Regex UnescapedCommas = new Regex(@"(? 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 value = tr.ReadToEnd(); - var serializeAsList = false; + // 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. - // 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); - } + var serializeAsList = false; - // Try to decode the string - EncodableDataType dt = null; - if (co != null) + // 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); + } + + // Try to decode the string + EncodableDataType dt = null; + if (co != null) + { + dt = new EncodableDataType { - dt = new EncodableDataType - { - AssociatedObject = co - }; - } + AssociatedObject = co + }; + } - var encodedValues = serializeAsList ? UnescapedCommas.Split(value) : new[] { value }; - var escapedValues = encodedValues.Select(v => Decode(dt, v)).ToList(); - var values = escapedValues.Select(Unescape).ToList(); + var encodedValues = serializeAsList ? UnescapedCommas.Split(value) : new[] { value }; + var escapedValues = encodedValues.Select(v => Decode(dt, v)).ToList(); + var values = escapedValues.Select(Unescape).ToList(); - if (co is ICalendarProperty) - { - // Is this necessary? - co.SetService("EscapedValue", escapedValues.Count == 1 ? escapedValues[0] : (object) escapedValues); - } + if (co is ICalendarProperty) + { + // Is this necessary? + 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; + // 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/Ical.Net/Serialization/DataTypes/TimeSpanSerializer.cs b/Ical.Net/Serialization/DataTypes/TimeSpanSerializer.cs index f1571eace..29db47a91 100644 --- a/Ical.Net/Serialization/DataTypes/TimeSpanSerializer.cs +++ b/Ical.Net/Serialization/DataTypes/TimeSpanSerializer.cs @@ -8,126 +8,125 @@ using System.Text; using System.Text.RegularExpressions; -namespace Ical.Net.Serialization.DataTypes +namespace Ical.Net.Serialization.DataTypes; + +public class TimeSpanSerializer : SerializerBase { - public class TimeSpanSerializer : SerializerBase - { - public TimeSpanSerializer() { } + public TimeSpanSerializer() { } - public TimeSpanSerializer(SerializationContext ctx) : base(ctx) { } + public TimeSpanSerializer(SerializationContext ctx) : base(ctx) { } - public override Type TargetType => typeof(TimeSpan); + public override Type TargetType => typeof(TimeSpan); - public override string SerializeToString(object obj) + public override string SerializeToString(object obj) + { + if (!(obj is TimeSpan)) { - if (!(obj is TimeSpan)) - { - return null; - } + return null; + } - var ts = (TimeSpan) obj; + var ts = (TimeSpan) obj; - if (ts == TimeSpan.Zero) - { - return "P0D"; - } + if (ts == TimeSpan.Zero) + { + return "P0D"; + } - var sb = new StringBuilder(); + var sb = new StringBuilder(); - if (ts < TimeSpan.Zero) - { - sb.Append("-"); - } + 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("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.Round(Math.Abs((double) ts.Days) / 7) + "W"); + sb.Append(Math.Abs(ts.Days) + "D"); } - else + if (ts.Hours != 0 || ts.Minutes != 0 || ts.Seconds != 0) { - if (ts.Days != 0) + sb.Append("T"); + if (ts.Hours != 0) { - sb.Append(Math.Abs(ts.Days) + "D"); + sb.Append(Math.Abs(ts.Hours) + "H"); } - if (ts.Hours != 0 || ts.Minutes != 0 || ts.Seconds != 0) + if (ts.Minutes != 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"); - } + 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)?(?