From 7632b1d591b4dc7383fadbfbbf1a30855d536459 Mon Sep 17 00:00:00 2001 From: resu-xuniL Date: Tue, 9 Jun 2026 10:42:35 +0200 Subject: [PATCH 1/2] Add `swift-scheduling` exercice --- config.json | 8 + .../swift-scheduling/.docs/instructions.md | 50 ++++ .../swift-scheduling/.docs/introduction.md | 6 + .../swift-scheduling/.meta/config.json | 19 ++ .../swift-scheduling/.meta/example.php | 104 ++++++++ .../swift-scheduling/.meta/tests.toml | 58 +++++ .../swift-scheduling/SwiftScheduling.php | 33 +++ .../swift-scheduling/SwiftSchedulingTest.php | 238 ++++++++++++++++++ 8 files changed, 516 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/.meta/config.json create mode 100644 exercises/practice/swift-scheduling/.meta/example.php create mode 100644 exercises/practice/swift-scheduling/.meta/tests.toml create mode 100644 exercises/practice/swift-scheduling/SwiftScheduling.php create mode 100644 exercises/practice/swift-scheduling/SwiftSchedulingTest.php diff --git a/config.json b/config.json index 3268f8fc..99303ee9 100644 --- a/config.json +++ b/config.json @@ -489,6 +489,14 @@ "floating_point_numbers" ] }, + { + "slug": "swift-scheduling", + "name": "Swift Scheduling", + "uuid": "d080d214-1d41-4347-a494-1e05db9f614a", + "practices": [], + "prerequisites": [], + "difficulty": 1 + }, { "slug": "transpose", "name": "Transpose", diff --git a/exercises/practice/swift-scheduling/.docs/instructions.md b/exercises/practice/swift-scheduling/.docs/instructions.md new file mode 100644 index 00000000..6423a106 --- /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 00000000..2322f813 --- /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/.meta/config.json b/exercises/practice/swift-scheduling/.meta/config.json new file mode 100644 index 00000000..d98e5685 --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "resu-xuniL" + ], + "files": { + "solution": [ + "SwiftScheduling.php" + ], + "test": [ + "SwiftSchedulingTest.php" + ], + "example": [ + ".meta/example.php" + ] + }, + "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/example.php b/exercises/practice/swift-scheduling/.meta/example.php new file mode 100644 index 00000000..d5cb436d --- /dev/null +++ b/exercises/practice/swift-scheduling/.meta/example.php @@ -0,0 +1,104 @@ + 4, + 2 => 7, + 3 => 10, + 4 => 13 + ]; + + public function deliveryDate(DateTime $meetingStart, string $description): ?DateTime + { + switch ($description) { + case 'NOW': + return $this->convertNow($meetingStart); + case 'ASAP': + return $this->convertAsap($meetingStart); + case 'EOW': + return $this->convertEow($meetingStart); + default: + if (str_ends_with($description, "M")) { + return $this->convertVariableM($meetingStart, (int) substr($description, 0, -1)); + } elseif (str_starts_with($description, "Q")) { + return $this->convertVariableQ($meetingStart, (int) substr($description, 1,)); + } else { + return null; + } + } + } + + private function convertNow(DateTime $meetingStart): DateTime + { + return $meetingStart->modify('+2 hours'); + } + + private function convertAsap(DateTime $meetingStart): DateTime + { + $meetingHour = (int) $meetingStart->format('H'); + + return $meetingHour < 13 + ? $meetingStart->setTime(17, 0) + : $meetingStart->setTime(13, 0) + ->modify('+1 day'); + } + + private function convertEow(DateTime $meetingStart): DateTime + { + $meetingDay = (int) $meetingStart->format('w'); + + return $meetingDay === 1 || $meetingDay === 2 || $meetingDay === 3 + ? $meetingStart->modify("friday") + ->setTime(17, 0) + : $meetingStart->modify("sunday") + ->setTime(20, 0); + } + + private function convertVariableM(DateTime $meetingStart, int $dueMonth): DateTime + { + $meetingMonth = (int) $meetingStart->format('m'); + + $meetingMonth < $dueMonth + ? $meetingStart->setDate( + (int) $meetingStart->format('Y'), + $dueMonth, + 1 + ) + : $meetingStart->setDate( + (int) $meetingStart->format('Y') + 1, + $dueMonth, + 1 + ); + + $meetingDay = (int) $meetingStart->format('w'); + + return $meetingDay === 0 || $meetingDay === 6 + ? $meetingStart->modify("first weekday") + ->setTime(8, 0) + : $meetingStart->setTime(8, 0); + } + + private function convertVariableQ(DateTime $meetingStart, int $dueQuarter): DateTime + { + $meetingMonth = (int) $meetingStart->format('m'); + $meetingQuarter = round(($meetingMonth + 2) / 3); + + $meetingStart->setDate( + (int) $meetingStart->format('Y'), + self::QUARTER[$dueQuarter], + 1 + ); + + if ($meetingQuarter > $dueQuarter) { + $meetingStart->modify('+1 year'); + } + + $meetingStart->modify("last weekday") + ->setTime(8, 0); + + return $meetingStart; + } +} diff --git a/exercises/practice/swift-scheduling/.meta/tests.toml b/exercises/practice/swift-scheduling/.meta/tests.toml new file mode 100644 index 00000000..7cc3e415 --- /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.php b/exercises/practice/swift-scheduling/SwiftScheduling.php new file mode 100644 index 00000000..f4b4d054 --- /dev/null +++ b/exercises/practice/swift-scheduling/SwiftScheduling.php @@ -0,0 +1,33 @@ +. + * + * To disable strict typing, comment out the directive below. + */ + +declare(strict_types=1); + +class SwiftScheduling +{ + public function deliveryDate(DateTime $start, string $description): DateTime + { + throw new \BadMethodCallException("Implement the deliveryDate method"); + } +} diff --git a/exercises/practice/swift-scheduling/SwiftSchedulingTest.php b/exercises/practice/swift-scheduling/SwiftSchedulingTest.php new file mode 100644 index 00000000..92b7c666 --- /dev/null +++ b/exercises/practice/swift-scheduling/SwiftSchedulingTest.php @@ -0,0 +1,238 @@ +assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: 93325e7b-677d-4d96-b017-2582af879dc2 + */ + #[TestDox('ASAP before one in the afternoon translates to today at five in the afternoon')] + public function testAsapBeforeOneInTheAfternoonTranslatesToTodayAtFiveInTheAfternoon(): void + { + $description = "ASAP"; + $meetingStart = new DateTime("1999-06-03T09:45:00"); + $expected = new DateTime("1999-06-03T17:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: cb4252a3-c4c1-41f6-8b8c-e7269733cef8 + */ + #[TestDox('ASAP at one in the afternoon translates to tomorrow at one in the afternoon')] + public function testAsapAtOneInTheAfternoonTranslatesToTomorrowAtOneInTheAfternoon(): void + { + $description = "ASAP"; + $meetingStart = new DateTime("2008-12-21T13:00:00"); + $expected = new DateTime("2008-12-22T13:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: 6fddc1ea-2fe9-4c60-81f7-9220d2f45537 + */ + #[TestDox('ASAP after one in the afternoon translates to tomorrow at one in the afternoon')] + public function testAsapAfterOneInTheAfternoonTranslatesToTomorrowAtOneInTheAfternoon(): void + { + $description = "ASAP"; + $meetingStart = new DateTime("2008-12-21T14:50:00"); + $expected = new DateTime("2008-12-22T13:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: 25f46bf9-6d2a-4e95-8edd-f62dd6bc8a6e + */ + #[TestDox('EOW on Monday translates to Friday at five in the afternoon')] + public function testEowOnMondayTranslatesToFridayAtFiveInTheAfternoon(): void + { + $description = "EOW"; + $meetingStart = new DateTime("2025-02-03T16:00:00"); + $expected = new DateTime("2025-02-07T17:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: 0b375df5-d198-489e-acee-fd538a768616 + */ + #[TestDox('EOW on Tuesday translates to Friday at five in the afternoon')] + public function testEowOnTuesdayTranslatesToFridayAtFiveInTheAfternoon(): void + { + $description = "EOW"; + $meetingStart = new DateTime("1997-04-29T10:50:00"); + $expected = new DateTime("1997-05-02T17:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: 4afbb881-0b5c-46be-94e1-992cdc2a8ca4 + */ + #[TestDox('EOW on Wednesday translates to Friday at five in the afternoon')] + public function testEowOnWednesdayTranslatesToFridayAtFiveInTheAfternoon(): void + { + $description = "EOW"; + $meetingStart = new DateTime("2005-09-14T11:00:00"); + $expected = new DateTime("2005-09-16T17:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: e1341c2b-5e1b-4702-a95c-a01e8e96e510 + */ + #[TestDox('EOW on Thursday translates to Sunday at eight in the evening')] + public function testEowOnThursdayTranslatesToSundayAtEightInTheEvening(): void + { + $description = "EOW"; + $meetingStart = new DateTime("2011-05-19T08:30:00"); + $expected = new DateTime("2011-05-22T20:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: bbffccf7-97f7-4244-888d-bdd64348fa2e + */ + #[TestDox('EOW on Friday translates to Sunday at eight in the evening')] + public function testEowOnFridayTranslatesToSundayAtEightInTheEvening(): void + { + $description = "EOW"; + $meetingStart = new DateTime("2022-08-05T14:00:00"); + $expected = new DateTime("2022-08-07T20:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: d651fcf4-290e-407c-8107-36b9076f39b2 + */ + #[TestDox('EOW translates to leap day')] + public function testEowTranslatesToLeapDay(): void + { + $description = "EOW"; + $meetingStart = new DateTime("2008-02-25T10:30:00"); + $expected = new DateTime("2008-02-29T17:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: 439bf09f-3a0e-44e7-bad5-b7b6d0c4505a + */ + #[TestDox('2M before the second month of this year translates to the first workday of the second month of this year')] + public function testTwoMBeforeTheSecondMonthOfThisYearTranslatesToTheFirstWorkdayOfTheSecondMonthOfThisYear(): void + { + $description = "2M"; + $meetingStart = new DateTime("2007-01-02T14:15:00"); + $expected = new DateTime("2007-02-01T08:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: 86d82e83-c481-4fb4-9264-625de7521340 + */ + #[TestDox('11M in the eleventh month translates to the first workday of the eleventh month of next year')] + public function testElevenMInTheEleventhMonthTranslatesToTheFirstWorkdayOfTheEleventhMonthOfNextYear(): void + { + $description = "11M"; + $meetingStart = new DateTime("2013-11-21T15:30:00"); + $expected = new DateTime("2014-11-03T08:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: 0d0b8f6a-1915-46f5-a630-1ff06af9da08 + */ + #[TestDox('4M in the ninth month translates to the first workday of the fourth month of next year')] + public function testFourMInTheNinthMonthTranslatesToTheFirstWorkdayOfTheFourthMonthOfNextYear(): void + { + $description = "4M"; + $meetingStart = new DateTime("2019-11-18T15:15:00"); + $expected = new DateTime("2020-04-01T08:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: 06d401e3-8461-438f-afae-8d26aa0289e0 + */ + #[TestDox('Q1 in the first quarter translates to the last workday of the first quarter of this year')] + public function testQOneInTheFirstQuarterTranslatesToTheLastWorkdayOfTheFirstQuarterOfThisYear(): void + { + $description = "Q1"; + $meetingStart = new DateTime("2003-01-01T10:45:00"); + $expected = new DateTime("2003-03-31T08:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: eebd5f32-b16d-4ecd-91a0-584b0364b7ed + */ + #[TestDox('Q4 in the second quarter translates to the last workday of the fourth quarter of this year')] + public function testQFourInTheSecondQuarterTranslatesToTheLastWorkdayOfTheFourthQuarterOfThisYear(): void + { + $description = "Q4"; + $meetingStart = new DateTime("2001-04-09T09:00:00"); + $expected = new DateTime("2001-12-31T08:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } + + /** + * uuid: c920886c-44ad-4d34-a156-dc4176186581 + */ + #[TestDox('Q3 in the fourth quarter translates to the last workday of the third quarter of next year')] + public function testQThreeInTheFourthQuarterTranslatesToTheLastWorkdayOfTheThirdQuarterOfNextYear(): void + { + $description = "Q3"; + $meetingStart = new DateTime("2022-10-06T11:00:00"); + $expected = new DateTime("2023-09-29T08:00:00"); + $swiftScheduling = new SwiftScheduling(); + + $this->assertEquals($expected, $swiftScheduling->deliveryDate($meetingStart, $description)); + } +} From 401d67258467d9e01b5ecee29befa1946f0c2b1a Mon Sep 17 00:00:00 2001 From: resu-xuniL Date: Tue, 9 Jun 2026 10:45:28 +0200 Subject: [PATCH 2/2] Add exercice to `auto-sync.txt` --- bin/auto-sync.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/auto-sync.txt b/bin/auto-sync.txt index 16e02841..458182c8 100644 --- a/bin/auto-sync.txt +++ b/bin/auto-sync.txt @@ -82,6 +82,7 @@ spiral-matrix state-of-tic-tac-toe strain sublist +swift-scheduling twelve-days two-bucket two-fer