From 09d78c626be048f32d37b249e1cd0c0866afae1a Mon Sep 17 00:00:00 2001 From: Erik Schierboom Date: Wed, 23 Apr 2025 14:55:13 +0200 Subject: [PATCH] Add `swift-scheduling` exercise --- config.json | 14 ++ exercises/Exercises.sln | 18 ++ .../swift-scheduling/.docs/instructions.md | 50 ++++++ .../swift-scheduling/.docs/introduction.md | 6 + .../practice/swift-scheduling/.editorconfig | 141 +++++++++++++++ .../swift-scheduling/.meta/Example.cs | 41 +++++ .../swift-scheduling/.meta/Generator.tpl | 21 +++ .../swift-scheduling/.meta/config.json | 19 ++ .../swift-scheduling/.meta/tests.toml | 58 ++++++ .../swift-scheduling/SwiftScheduling.cs | 7 + .../swift-scheduling/SwiftScheduling.csproj | 18 ++ .../swift-scheduling/SwiftSchedulingTests.cs | 130 ++++++++++++++ .../swift-scheduling/packages.lock.json | 170 ++++++++++++++++++ 13 files changed, 693 insertions(+) create mode 100644 exercises/practice/swift-scheduling/.docs/instructions.md create mode 100644 exercises/practice/swift-scheduling/.docs/introduction.md create mode 100644 exercises/practice/swift-scheduling/.editorconfig create mode 100644 exercises/practice/swift-scheduling/.meta/Example.cs create mode 100644 exercises/practice/swift-scheduling/.meta/Generator.tpl create mode 100644 exercises/practice/swift-scheduling/.meta/config.json create mode 100644 exercises/practice/swift-scheduling/.meta/tests.toml create mode 100644 exercises/practice/swift-scheduling/SwiftScheduling.cs create mode 100644 exercises/practice/swift-scheduling/SwiftScheduling.csproj create mode 100644 exercises/practice/swift-scheduling/SwiftSchedulingTests.cs create mode 100644 exercises/practice/swift-scheduling/packages.lock.json diff --git a/config.json b/config.json index 2c741a627..58f17917d 100644 --- a/config.json +++ b/config.json @@ -2759,6 +2759,20 @@ "numbers" ], "difficulty": 6 + }, + { + "slug": "swift-scheduling", + "name": "Swift Scheduling", + "uuid": "c323aceb-70ae-4e2b-bdef-03ffcdd40a92", + "practices": [ + "datetimes", + "string-formatting" + ], + "prerequisites": [ + "datetimes", + "string-formatting" + ], + "difficulty": 6 } ], "foregone": [ diff --git a/exercises/Exercises.sln b/exercises/Exercises.sln index bb2050a70..75af82cce 100644 --- a/exercises/Exercises.sln +++ b/exercises/Exercises.sln @@ -363,6 +363,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SplitSecondStopwatch", "pra EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BafflingBirthdays", "practice\baffling-birthdays\BafflingBirthdays.csproj", "{1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "swift-scheduling", "swift-scheduling", "{FA726133-BC1C-D753-28C5-C9ACB44C0776}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SwiftScheduling", "practice\swift-scheduling\SwiftScheduling.csproj", "{9A073412-4B64-48EB-A346-A56DE6847CCC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -2509,6 +2513,18 @@ Global {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Release|x64.Build.0 = Release|Any CPU {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Release|x86.ActiveCfg = Release|Any CPU {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E}.Release|x86.Build.0 = Release|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Debug|x64.ActiveCfg = Debug|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Debug|x64.Build.0 = Debug|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Debug|x86.ActiveCfg = Debug|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Debug|x86.Build.0 = Debug|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Release|Any CPU.Build.0 = Release|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Release|x64.ActiveCfg = Release|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Release|x64.Build.0 = Release|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Release|x86.ActiveCfg = Release|Any CPU + {9A073412-4B64-48EB-A346-A56DE6847CCC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2692,6 +2708,8 @@ Global {E81F1BA3-1F99-4DCB-B875-78D1F4750BD5} = {E276EF69-669A-43E0-88AC-8ABB17A9C026} {0A6CD51A-2120-4A72-A17B-08CCC2E6D365} = {E276EF69-669A-43E0-88AC-8ABB17A9C026} {1BA5F72A-ED8E-48A8-8FD6-0F567BDCBD9E} = {E276EF69-669A-43E0-88AC-8ABB17A9C026} + {FA726133-BC1C-D753-28C5-C9ACB44C0776} = {E276EF69-669A-43E0-88AC-8ABB17A9C026} + {9A073412-4B64-48EB-A346-A56DE6847CCC} = {FA726133-BC1C-D753-28C5-C9ACB44C0776} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AB4EA6C9-5461-4024-BDC7-2AE0C3A85CD1} diff --git a/exercises/practice/swift-scheduling/.docs/instructions.md b/exercises/practice/swift-scheduling/.docs/instructions.md new file mode 100644 index 000000000..6423a1066 --- /dev/null +++ b/exercises/practice/swift-scheduling/.docs/instructions.md @@ -0,0 +1,50 @@ +# Instructions + +Your task is to convert delivery date descriptions to _actual_ delivery dates, based on when the meeting started. + +There are two types of delivery date descriptions: + +1. Fixed: a predefined set of words. +2. Variable: words that have a variable component, but follow a predefined set of patterns. + +## Fixed delivery date descriptions + +There are three fixed delivery date descriptions: + +- `"NOW"` +- `"ASAP"` (As Soon As Possible) +- `"EOW"` (End Of Week) + +The following table shows how to translate them: + +| Description | Meeting start | Delivery date | +| ----------- | ----------------------------- | ----------------------------------- | +| `"NOW"` | - | Two hours after the meeting started | +| `"ASAP"` | Before 13:00 | Today at 17:00 | +| `"ASAP"` | After or at 13:00 | Tomorrow at 13:00 | +| `"EOW"` | Monday, Tuesday, or Wednesday | Friday at 17:00 | +| `"EOW"` | Thursday or Friday | Sunday at 20:00 | + +## Variable delivery date descriptions + +There are two variable delivery date description patterns: + +- `"M"` (N-th month) +- `"Q"` (N-th quarter) + +| Description | Meeting start | Delivery date | +| ----------- | ------------------------- | --------------------------------------------------------- | +| `"M"` | Before N-th month | At 8:00 on the _first_ workday of this year's N-th month | +| `"M"` | After or in N-th month | At 8:00 on the _first_ workday of next year's N-th month | +| `"Q"` | Before or in N-th quarter | At 8:00 on the _last_ workday of this year's N-th quarter | +| `"Q"` | After N-th quarter | At 8:00 on the _last_ workday of next year's N-th quarter | + +~~~~exercism/note +A workday is a Monday, Tuesday, Wednesday, Thursday, or Friday. + +A year has four quarters, each with three months: +1. January/February/March +2. April/May/June +3. July/August/September +4. October/November/December. +~~~~ diff --git a/exercises/practice/swift-scheduling/.docs/introduction.md b/exercises/practice/swift-scheduling/.docs/introduction.md new file mode 100644 index 000000000..2322f813f --- /dev/null +++ b/exercises/practice/swift-scheduling/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +This week, it is your turn to take notes in the department's planning meeting. +In this meeting, your boss will set delivery dates for all open work items. +Annoyingly, instead of specifying the _actual_ delivery dates, your boss will only _describe them_ in an abbreviated format. +As many of your colleagues won't be familiar with this corporate lingo, you'll need to convert these delivery date descriptions to actual delivery dates. diff --git a/exercises/practice/swift-scheduling/.editorconfig b/exercises/practice/swift-scheduling/.editorconfig new file mode 100644 index 000000000..4b8753556 --- /dev/null +++ b/exercises/practice/swift-scheduling/.editorconfig @@ -0,0 +1,141 @@ +############################### +# Core EditorConfig Options # +############################### + +; This file is for unifying the coding style for different editors and IDEs. +; More information at: +; https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2017 +; https://docs.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options?view=vs-2017 + +root = true + +[*] +indent_style = space + +[SwiftScheduling.cs] +indent_size = 4 + +############################### +# .NET Coding Conventions # +############################### + +# Organize usings +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = true + +# this. preferences +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 + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = always:suggestion +dotnet_style_readonly_field = true:suggestion + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +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_auto_properties = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion + +############################### +# Naming Conventions # +############################### + +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const + +############################### +# C# Code Style Rules # +############################### + +# var preferences +csharp_style_var_for_built_in_types = true:none +csharp_style_var_when_type_is_apparent = true:none +csharp_style_var_elsewhere = true:none + +# Expression-bodied members +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_constructors = true:suggestion +csharp_style_expression_bodied_operators = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_accessors = true:suggestion + +# Pattern-matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion + +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion + +# Expression-level preferences +csharp_prefer_braces = true:none +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +############################### +# C# Formatting Rules # +############################### + +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = false +csharp_new_line_before_members_in_anonymous_types = false +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true diff --git a/exercises/practice/swift-scheduling/.meta/Example.cs b/exercises/practice/swift-scheduling/.meta/Example.cs new file mode 100644 index 000000000..251c33790 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/Example.cs @@ -0,0 +1,41 @@ +public static class SwiftScheduling +{ + public static DateTime DeliveryDate(DateTime meetingStart, string description) => + description switch + { + "NOW" => meetingStart.AddHours(2), + "ASAP" when meetingStart.Hour <= 12 => meetingStart.AtHour(17), + "ASAP" => meetingStart.AddDays(1).AtHour(13), + "EOW" => meetingStart.DayOfWeek switch + { + DayOfWeek.Thursday or DayOfWeek.Friday => meetingStart.AddDays(7 - (int)meetingStart.DayOfWeek).AtHour(20), + _ => meetingStart.AddDays(DayOfWeek.Friday - meetingStart.DayOfWeek).AtHour(17), + }, + _ when description.EndsWith('M') => int.Parse(description[..^1]) switch + { + var month when month > meetingStart.Month => meetingStart.NthMonth(month).FirstWorkDay().AtHour(8), + var month => meetingStart.AddYears(1).NthMonth(month).FirstWorkDay().AtHour(8) + }, + _ when description.StartsWith('Q') => int.Parse(description[1..]) switch + { + var quarter when quarter * 3 > meetingStart.Month => meetingStart.NthQuarter(quarter).LastWorkDay().AtHour(8), + var quarter => meetingStart.AddYears(1).NthQuarter(quarter).LastWorkDay().AtHour(8) + }, + _ => throw new ArgumentException("Invalid date"), + }; + + private static DateTime NthMonth(this DateTime date, int n) => new(date.Year, n, 1); + + private static DateTime NthQuarter(this DateTime date, int n) => new(date.Year, n * 3, 1); + + private static IEnumerable WorkDays(this DateTime date) => + Enumerable.Range(1, DateTime.DaysInMonth(date.Year, date.Month)) + .Select(day => new DateTime(date.Year, date.Month, day)) + .Where(d => d.DayOfWeek is not DayOfWeek.Saturday and not DayOfWeek.Sunday); + + private static DateTime FirstWorkDay(this DateTime date) => date.WorkDays().First(); + + private static DateTime LastWorkDay(this DateTime date) => date.WorkDays().Last(); + + private static DateTime AtHour(this DateTime date, int hour) => date.Date.AddHours(hour); +} diff --git a/exercises/practice/swift-scheduling/.meta/Generator.tpl b/exercises/practice/swift-scheduling/.meta/Generator.tpl new file mode 100644 index 000000000..49122fbe4 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/Generator.tpl @@ -0,0 +1,21 @@ +{{func to_datetime + d = date.parse $0 + if d.hour + d.minute + d.second == 0 + $"new DateTime({d.year}, {d.month}, {d.day})" + else + $"new DateTime({d.year}, {d.month}, {d.day}, {d.hour}, {d.minute}, {d.second})" + end +end}} + +public class {{ testClass }} +{ + {{- for test in tests }} + [Fact{{ if !for.first }}(Skip = "Remove this Skip property to run this test"){{ end }}] + public void {{ test.testMethod | string.replace "4_m" "Four_m" | string.replace "2_m" "Two_m" | string.replace "11_m" "Eleven_m" }}() + { + var meetingStart = {{ test.input.meetingStart | to_datetime }}; + var expected = {{ test.expected | to_datetime }}; + Assert.Equal(expected, {{ testedClass }}.{{ test.testedMethod }}(meetingStart, {{ test.input.description | string.literal }}), TimeSpan.FromSeconds(1)); + } + {{ end -}} +} diff --git a/exercises/practice/swift-scheduling/.meta/config.json b/exercises/practice/swift-scheduling/.meta/config.json new file mode 100644 index 000000000..f675d4ed2 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "erikschierboom" + ], + "files": { + "solution": [ + "SwiftScheduling.cs" + ], + "test": [ + "SwiftSchedulingTests.cs" + ], + "example": [ + ".meta/Example.cs" + ] + }, + "blurb": "Convert delivery date descriptions to actual delivery dates.", + "source": "Erik Schierboom", + "source_url": "https://github.com/exercism/problem-specifications/pull/2536" +} diff --git a/exercises/practice/swift-scheduling/.meta/tests.toml b/exercises/practice/swift-scheduling/.meta/tests.toml new file mode 100644 index 000000000..7cc3e4158 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/tests.toml @@ -0,0 +1,58 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[1d0e6e72-f370-408c-bc64-5dafa9c6da73] +description = "NOW translates to two hours later" + +[93325e7b-677d-4d96-b017-2582af879dc2] +description = "ASAP before one in the afternoon translates to today at five in the afternoon" + +[cb4252a3-c4c1-41f6-8b8c-e7269733cef8] +description = "ASAP at one in the afternoon translates to tomorrow at one in the afternoon" + +[6fddc1ea-2fe9-4c60-81f7-9220d2f45537] +description = "ASAP after one in the afternoon translates to tomorrow at one in the afternoon" + +[25f46bf9-6d2a-4e95-8edd-f62dd6bc8a6e] +description = "EOW on Monday translates to Friday at five in the afternoon" + +[0b375df5-d198-489e-acee-fd538a768616] +description = "EOW on Tuesday translates to Friday at five in the afternoon" + +[4afbb881-0b5c-46be-94e1-992cdc2a8ca4] +description = "EOW on Wednesday translates to Friday at five in the afternoon" + +[e1341c2b-5e1b-4702-a95c-a01e8e96e510] +description = "EOW on Thursday translates to Sunday at eight in the evening" + +[bbffccf7-97f7-4244-888d-bdd64348fa2e] +description = "EOW on Friday translates to Sunday at eight in the evening" + +[d651fcf4-290e-407c-8107-36b9076f39b2] +description = "EOW translates to leap day" + +[439bf09f-3a0e-44e7-bad5-b7b6d0c4505a] +description = "2M before the second month of this year translates to the first workday of the second month of this year" + +[86d82e83-c481-4fb4-9264-625de7521340] +description = "11M in the eleventh month translates to the first workday of the eleventh month of next year" + +[0d0b8f6a-1915-46f5-a630-1ff06af9da08] +description = "4M in the ninth month translates to the first workday of the fourth month of next year" + +[06d401e3-8461-438f-afae-8d26aa0289e0] +description = "Q1 in the first quarter translates to the last workday of the first quarter of this year" + +[eebd5f32-b16d-4ecd-91a0-584b0364b7ed] +description = "Q4 in the second quarter translates to the last workday of the fourth quarter of this year" + +[c920886c-44ad-4d34-a156-dc4176186581] +description = "Q3 in the fourth quarter translates to the last workday of the third quarter of next year" diff --git a/exercises/practice/swift-scheduling/SwiftScheduling.cs b/exercises/practice/swift-scheduling/SwiftScheduling.cs new file mode 100644 index 000000000..a2d0e4531 --- /dev/null +++ b/exercises/practice/swift-scheduling/SwiftScheduling.cs @@ -0,0 +1,7 @@ +public static class SwiftScheduling +{ + public static DateTime DeliveryDate(DateTime meetingStart, string description) => + { + throw new NotImplementedException("You need to implement this method."); + } +} diff --git a/exercises/practice/swift-scheduling/SwiftScheduling.csproj b/exercises/practice/swift-scheduling/SwiftScheduling.csproj new file mode 100644 index 000000000..a38815241 --- /dev/null +++ b/exercises/practice/swift-scheduling/SwiftScheduling.csproj @@ -0,0 +1,18 @@ + + + enable + enable + Exe + net9.0 + true + + + + + + + + + + + \ No newline at end of file diff --git a/exercises/practice/swift-scheduling/SwiftSchedulingTests.cs b/exercises/practice/swift-scheduling/SwiftSchedulingTests.cs new file mode 100644 index 000000000..93454340e --- /dev/null +++ b/exercises/practice/swift-scheduling/SwiftSchedulingTests.cs @@ -0,0 +1,130 @@ +public class SwiftSchedulingTests +{ + [Fact] + public void Now_translates_to_two_hours_later() + { + var meetingStart = new DateTime(2012, 2, 13, 9, 0, 0); + var expected = new DateTime(2012, 2, 13, 11, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "NOW"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Asap_before_one_in_the_afternoon_translates_to_today_at_five_in_the_afternoon() + { + var meetingStart = new DateTime(1999, 6, 3, 9, 45, 0); + var expected = new DateTime(1999, 6, 3, 17, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "ASAP"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Asap_at_one_in_the_afternoon_translates_to_tomorrow_at_one_in_the_afternoon() + { + var meetingStart = new DateTime(2008, 12, 21, 13, 0, 0); + var expected = new DateTime(2008, 12, 22, 13, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "ASAP"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Asap_after_one_in_the_afternoon_translates_to_tomorrow_at_one_in_the_afternoon() + { + var meetingStart = new DateTime(2008, 12, 21, 14, 50, 0); + var expected = new DateTime(2008, 12, 22, 13, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "ASAP"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Eow_on_monday_translates_to_friday_at_five_in_the_afternoon() + { + var meetingStart = new DateTime(2025, 2, 3, 16, 0, 0); + var expected = new DateTime(2025, 2, 7, 17, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "EOW"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Eow_on_tuesday_translates_to_friday_at_five_in_the_afternoon() + { + var meetingStart = new DateTime(1997, 4, 29, 10, 50, 0); + var expected = new DateTime(1997, 5, 2, 17, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "EOW"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Eow_on_wednesday_translates_to_friday_at_five_in_the_afternoon() + { + var meetingStart = new DateTime(2005, 9, 14, 11, 0, 0); + var expected = new DateTime(2005, 9, 16, 17, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "EOW"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Eow_on_thursday_translates_to_sunday_at_eight_in_the_evening() + { + var meetingStart = new DateTime(2011, 5, 19, 8, 30, 0); + var expected = new DateTime(2011, 5, 22, 20, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "EOW"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Eow_on_friday_translates_to_sunday_at_eight_in_the_evening() + { + var meetingStart = new DateTime(2022, 8, 5, 14, 0, 0); + var expected = new DateTime(2022, 8, 7, 20, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "EOW"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Eow_translates_to_leap_day() + { + var meetingStart = new DateTime(2008, 2, 25, 10, 30, 0); + var expected = new DateTime(2008, 2, 29, 17, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "EOW"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Two_m_before_the_second_month_of_this_year_translates_to_the_first_workday_of_the_second_month_of_this_year() + { + var meetingStart = new DateTime(2007, 1, 2, 14, 15, 0); + var expected = new DateTime(2007, 2, 1, 8, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "2M"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Eleven_m_in_the_eleventh_month_translates_to_the_first_workday_of_the_eleventh_month_of_next_year() + { + var meetingStart = new DateTime(2013, 11, 21, 15, 30, 0); + var expected = new DateTime(2014, 11, 3, 8, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "11M"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Four_m_in_the_ninth_month_translates_to_the_first_workday_of_the_fourth_month_of_next_year() + { + var meetingStart = new DateTime(2019, 11, 18, 15, 15, 0); + var expected = new DateTime(2020, 4, 1, 8, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "4M"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Q1_in_the_first_quarter_translates_to_the_last_workday_of_the_first_quarter_of_this_year() + { + var meetingStart = new DateTime(2003, 1, 1, 10, 45, 0); + var expected = new DateTime(2003, 3, 31, 8, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "Q1"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Q4_in_the_second_quarter_translates_to_the_last_workday_of_the_fourth_quarter_of_this_year() + { + var meetingStart = new DateTime(2001, 4, 9, 9, 0, 0); + var expected = new DateTime(2001, 12, 31, 8, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "Q4"), TimeSpan.FromSeconds(1)); + } + + [Fact(Skip = "Remove this Skip property to run this test")] + public void Q3_in_the_fourth_quarter_translates_to_the_last_workday_of_the_third_quarter_of_next_year() + { + var meetingStart = new DateTime(2022, 10, 6, 11, 0, 0); + var expected = new DateTime(2023, 9, 29, 8, 0, 0); + Assert.Equal(expected, SwiftScheduling.DeliveryDate(meetingStart, "Q3"), TimeSpan.FromSeconds(1)); + } +} diff --git a/exercises/practice/swift-scheduling/packages.lock.json b/exercises/practice/swift-scheduling/packages.lock.json new file mode 100644 index 000000000..6b3387771 --- /dev/null +++ b/exercises/practice/swift-scheduling/packages.lock.json @@ -0,0 +1,170 @@ +{ + "version": 1, + "dependencies": { + "net9.0": { + "Exercism.Tests.xunit.v3": { + "type": "Direct", + "requested": "[0.1.0-beta1, )", + "resolved": "0.1.0-beta1", + "contentHash": "XjVtQWWxmHDDj7UMdkPKpBFFKnsW0tkBhlyJSfFFh+fWwGemyyJwJYhdsvWhiKKCY7zItB+mI/o0OQtOKQxUhA==", + "dependencies": { + "xunit.v3.extensibility.core": "1.1.0" + } + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.12.0, )", + "resolved": "17.12.0", + "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==", + "dependencies": { + "Microsoft.CodeCoverage": "17.12.0", + "Microsoft.TestPlatform.TestHost": "17.12.0" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.0.1, )", + "resolved": "3.0.1", + "contentHash": "lbyYtsBxA8Pz8kaf5Xn/Mj1mL9z2nlBWdZhqFaj66nxXBa4JwiTDm4eGcpSMet6du9TOWI6bfha+gQR6+IHawg==" + }, + "xunit.v3": { + "type": "Direct", + "requested": "[1.1.0, )", + "resolved": "1.1.0", + "contentHash": "1ckSz5GVswlM9TCk5bGdHOjnYwqAWjkeqxckoHawQIA8sTeuN+RCBUypCi5A/Um0XlczRx5TjAK5W6BbN0HLcQ==", + "dependencies": { + "xunit.analyzers": "1.20.0", + "xunit.v3.assert": "[1.1.0]", + "xunit.v3.core": "[1.1.0]" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.12.0", + "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA==" + }, + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { + "type": "Transitive", + "resolved": "1.5.3", + "contentHash": "h34zKNpGyni66VH738mRHeXSnf3klSShUdavUWNhSfWICUUi5aXeI0LBvoX/ad93N0+9xBDU3Fyi6WfxrwKQGw==", + "dependencies": { + "Microsoft.Testing.Platform": "1.5.3" + } + }, + "Microsoft.Testing.Platform": { + "type": "Transitive", + "resolved": "1.5.3", + "contentHash": "WqJydnJ99dEKtquR9HwINz104ehWJKTXbQQrydGatlLRw14bmsx0pa8+E6KUXMYXZAimN0swWlDmcJGjjW4TIg==" + }, + "Microsoft.Testing.Platform.MSBuild": { + "type": "Transitive", + "resolved": "1.5.3", + "contentHash": "bOtpRMSPeT5YLQo+NNY8EtdNTphAUcmALjW4ABU7P0rb6yR2XAZau3TzNieLmR3lRuwudguWzzBhgcLRXwZh0A==", + "dependencies": { + "Microsoft.Testing.Platform": "1.5.3" + } + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.12.0", + "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==", + "dependencies": { + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.12.0", + "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.12.0", + "Newtonsoft.Json": "13.0.1" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "OEkbBQoklHngJ8UD8ez2AERSk2g+/qpAaSWWCBFbpH727HxDq5ydVkuncBaKcKfwRqXGWx64dS6G1SUScMsitg==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.20.0", + "contentHash": "HElev2E9vFbPxwKRQtpCSSzLOu8M/N9EWBCB37v7SRx6z4Lbj19FxfLEig3v9jiI6s4b0l2uena91nEsTWl9jA==" + }, + "xunit.v3.assert": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "4D+eM08ImfhA+zLbRzi8HA4qsT98zDxgaCD7vCg8yFesokKsgSsqWsAmImHFjVymGVhVS7WFGb19d6v1k9i0xQ==", + "dependencies": { + "System.Collections.Immutable": "8.0.0", + "System.Memory": "4.6.0" + } + }, + "xunit.v3.common": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "Cq55z8pC7fOkfj+3TB/YQ6OW96qWqxKiMd15CtkIl37VtV9EsiUL4B4HsR6VLJCzkk7cBiXQ1ABVIcp3TCm6HQ==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "6.0.0" + } + }, + "xunit.v3.core": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kXP/1d3jnQ2m4skcdM3gSMmubI6P747D6KVswzeedysgFkLj2xJlfo7p7slsmtEnp8BZb8X6D92Hssd/UtVPMw==", + "dependencies": { + "Microsoft.Testing.Platform.MSBuild": "1.5.3", + "xunit.v3.extensibility.core": "[1.1.0]", + "xunit.v3.runner.inproc.console": "[1.1.0]" + } + }, + "xunit.v3.extensibility.core": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "AeQbbYN001x0c+B9pqwml6jZPovHz8O/sOp7jmrjz90rUzz/QPal12SlHLKYszR44CMnW4MsDam3RYT5pkYUxw==", + "dependencies": { + "xunit.v3.common": "[1.1.0]" + } + }, + "xunit.v3.runner.common": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "Q81J0VPuu8fpF+/1CIjThqKKUjnqh0TQrLlD0iORkF75KdsOV+iGWT8c3AVuY96kDoxXxkTf0ZvJsK6o9osc1A==", + "dependencies": { + "xunit.v3.common": "[1.1.0]" + } + }, + "xunit.v3.runner.inproc.console": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "lX/4TwIJe9ysCd5dqLk/Doq8ieYaZGivgf95xR59wRuSV+nHzHnyhpjXfaPUp8nkncUH1rOmJ85o1KebipisXQ==", + "dependencies": { + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.5.3", + "Microsoft.Testing.Platform": "1.5.3", + "xunit.v3.extensibility.core": "[1.1.0]", + "xunit.v3.runner.common": "[1.1.0]" + } + } + } + } +} \ No newline at end of file