-
-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add concept exercise: dates / library-fees #412
Draft
SaschaMann
wants to merge
1
commit into
main
Choose a base branch
from
exercises/concept/library-fees
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+269
−0
Draft
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Instructions | ||
|
||
Your librarian friend has asked you to extend her library software to automatically calculate late fees. | ||
Her current system stores the exact date and time of a book checkout as an [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) datetime string. | ||
She runs a local library in a small town in Ghana, which uses the GMT timezone (UTC +0), doesn't use daylight saving time, and doesn't need to worry about other timezones. | ||
|
||
## 1. Determine if a book was checked out before noon | ||
|
||
If a book was checked out before noon, the reader has 28 days to return it. | ||
If it was checked out at or after noon, it's 29 days. | ||
|
||
Implement the `before_noon` function. | ||
It should take a `DateTime` and return a boolean. | ||
|
||
```julia | ||
julia> before_noon(DateTime("2021-01-12T08:23:03")) | ||
true | ||
``` | ||
|
||
## 2. Calculate the return datetime | ||
|
||
Based on the checkout datetime, calculate the return date. | ||
|
||
Implement the `return_date` function. | ||
It should take a `DateTime` and return a `Date`, either 28 or 29 days later. | ||
|
||
```julia | ||
julia> return_date(DateTime("2020-11-28T15:55:33")) | ||
2020-12-27 | ||
``` | ||
|
||
## 3. Determine how late the return of the book was | ||
|
||
The library has a flat rate for late returns. | ||
To be able to calculate the fee, we need to know how many days after the return date the book was actually returned. | ||
|
||
Implement the `days_late` function. | ||
It should take two `DateTime`s, the checkout datetime and the datetime when the book was returned. | ||
If the planned return datetime is on an earlier or the same day as the actual return datetime, the function should return 0 days. | ||
Otherwise, the function should return the difference between those two datetimes in days. | ||
If the difference between those two datetimes is not an exact number of days, e.g. 3.5 days, round down to the nearest number of full days. | ||
|
||
```julia | ||
julia> days_late(DateTime("2020-12-27T15:55:33"), DateTime("2021-01-03T09:23:36")) | ||
6 days | ||
``` | ||
|
||
## 4. Determine if the book was returned on a monday or tuesday | ||
|
||
The library has a special offer for returning books on Mondays and Tuesdays. | ||
|
||
Implement the `fee_discount` function. | ||
It should take a `DateTime` and return a boolean. | ||
|
||
```julia | ||
julia> fee_discount(DateTime("2021-01-03T13:30:45")) | ||
false | ||
``` | ||
|
||
## 5. Calculate the late fee | ||
|
||
Implement the `late_fee` function. | ||
It should take three arguments: two ISO8601 datetime strings, checkout datetime and actual return datetime, and the late fee for one day. | ||
It should return the total late fee according to how late the actual return of the book was. | ||
|
||
Include the special Monday & Tuesday offer. If you return the book on Monday or Tuesday, your late fee is 50% off, rounded down to the nearest natural number. | ||
|
||
```elixir | ||
# Sunday, 7 days late | ||
julia> late_fee("2020-11-28T15:55:33", "2021-01-03T13:30:45", 100) | ||
700.0 | ||
|
||
# one day later, Monday, 8 days late | ||
julia> late_fee("2020-11-28T15:55:33", "2021-01-04T09:02:11", 100) | ||
400.0 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"blurb": "Learn how to handle dates in Julia by calculating library fees.", | ||
"language_versions": "≥1.0", | ||
"files": { | ||
"exemplar": [ | ||
".meta/exemplar.jl" | ||
], | ||
"test": [ | ||
"runtests.jl" | ||
], | ||
"solution": [ | ||
"library-fees.jl" | ||
] | ||
}, | ||
"contributors": [], | ||
"authors": [ | ||
"SaschaMann", | ||
"cmcaine" | ||
], | ||
"forked_from": [ | ||
"elixir/library-fees" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using Dates | ||
|
||
""" | ||
before_noon(dt) | ||
|
||
Return true if the given DateTime is in the forenoon. | ||
""" | ||
before_noon(dt) = Time(dt) < Time(Hour(12)) | ||
|
||
""" | ||
return_date(checkout_dt) | ||
|
||
Return the return date for a given checkout DateTime. | ||
""" | ||
function return_date(checkout_dt) | ||
# Make use of booleans being numbers. | ||
# before_noon(checkout_dt) will be treated as 0 if it's false and as 1 if it's true. | ||
Date(checkout_dt + Day(28) + Day(!before_noon(checkout_dt))) | ||
end | ||
|
||
""" | ||
days_late(return_dt, actual_return_dt) | ||
|
||
Return the number of days that the book was return too late. | ||
""" | ||
function days_late(return_dt, actual_return_dt) | ||
# If the book was returned before the return date, return 0 days | ||
actual_return_dt < return_dt && return Day(0) | ||
|
||
ms_to_d = 1000 * 60 * 60 * 24 | ||
Δdt = actual_return_dt - return_dt | ||
Day(Δdt - (Δdt % ms_to_d)) | ||
end | ||
|
||
""" | ||
fee_discount(return_dt) | ||
|
||
Return true if the Monday & Tuesday discount should be applied on the day of the given DateTime. | ||
""" | ||
fee_discount(return_dt) = Dates.ismonday(return_dt) || Dates.istuesday(return_dt) | ||
|
||
""" | ||
late_fee(checkout_time, actual_return_time, daily_fee) | ||
|
||
Return the total late fee. | ||
""" | ||
function late_fee(checkout_time, actual_return_time, daily_fee) | ||
return_dt = DateTime(return_date(DateTime(checkout_time))) | ||
actual_return_dt = DateTime(actual_return_time) | ||
|
||
delay = days_late(return_dt, actual_return_dt) | ||
|
||
floor(daily_fee * Dates.value(delay) * (1 - 0.5 * fee_discount(actual_return_dt))) | ||
end |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
using Test | ||
|
||
include("library-fees.jl") | ||
|
||
@testset "1. Determine if a book was checked out before noon" begin | ||
@testset "Return true if the given DateTime is before 12:00" begin | ||
@test before_noon(DateTime("2020-06-06T11:59:59")) | ||
end | ||
|
||
@testset "Return false if the given DateTime is after 12:00" begin | ||
@test !before_noon(DateTime("2021-01-03T12:01:01")) | ||
end | ||
|
||
@testset "Return false if the given DateTime is exactly 12:00" begin | ||
@test !before_noon(DateTime("2018-11-17T12:00:00")) | ||
end | ||
end | ||
|
||
@testset "2. Calculate the return Date" begin | ||
@testset "Add 28 days if the given DateTime is before 12:00" begin | ||
@test return_date(DateTime("2020-02-14T11:59:59")) == Date("2020-03-13") | ||
end | ||
|
||
@testset "Add 29 days if the given DateTime is after 12:00" begin | ||
@test return_date(DateTime("2021-01-03T12:01:01")) == Date("2021-02-01") | ||
end | ||
|
||
@testset "Add 29 days if the given DateTime is exactly at 12:00" begin | ||
@test return_date(DateTime("2018-12-01T12:00:00")) == Date("2018-12-30") | ||
end | ||
end | ||
|
||
@testset "3. Determine how late the return of the book was" begin | ||
@testset "Return 0 days for identical datetimes" begin | ||
@test days_late(DateTime("2021-02-01T12:00:00"), DateTime("2021-02-01T12:00:00")) == Day(0) | ||
end | ||
|
||
@testset "Return 0 days for identical dates, but different times" begin | ||
@test days_late(DateTime("2019-03-11T13:50:00"), DateTime("2019-03-11T12:00:00")) == Day(0) | ||
end | ||
|
||
@testset "Return 0 when planned return date is later than actual return date" begin | ||
@test days_late(DateTime("2020-12-03T09:50:00"), DateTime("2020-11-29T16:00:00")) == Day(0) | ||
end | ||
|
||
@testset "Return date difference in numbers of days when planned return date is earlier than actual return date" begin | ||
@test days_late(DateTime("2020-06-12T09:50:00"), DateTime("2020-06-21T16:00:00")) == Day(9) | ||
end | ||
end | ||
|
||
@testset "4. Determine if the book was returned on a monday or tuesday" begin | ||
@testset "Mondays" begin | ||
@test fee_discount(DateTime("2021-02-01T14:01:00")) | ||
@test fee_discount(DateTime("2020-03-16T09:23:52")) | ||
@test fee_discount(DateTime("2019-04-22T15:44:03")) | ||
end | ||
|
||
@testset "Tuesdays" begin | ||
@test fee_discount(DateTime("2021-02-02T15:07:00")) | ||
end | ||
|
||
@testset "Other days" begin | ||
# Friday | ||
@test !fee_discount(DateTime("2020-03-14T08:54:51")) | ||
|
||
# Sunday | ||
@test !fee_discount(DateTime("2019-04-28T11:37:12")) | ||
end | ||
end | ||
|
||
@testset "5. Calculate the late fee" begin | ||
@testset "Return 0 if the book was returned less than 28 days after a morning checkout" begin | ||
@test late_fee("2018-11-01T09:00:00", "2018-11-13T14:12:00", 123) == 0 | ||
end | ||
|
||
@testset "Return 0 if the book was returned exactly 28 days after a morning checkout" begin | ||
@test late_fee("2018-11-01T09:00:00", "2018-11-29T14:12:00", 123) == 0 | ||
end | ||
|
||
@testset "Return the rate for one day if the book was returned exactly 29 days after a morning checkout" begin | ||
@test late_fee("2018-11-01T09:00:00", "2018-11-30T14:12:00", 320) == 320 | ||
end | ||
|
||
@testset "Return 0 if the book was returned less than 29 days after an afternoon checkout" begin | ||
@test late_fee("2019-05-01T16:12:00", "2019-05-17T14:32:45", 400) == 0 | ||
end | ||
|
||
@testset "Return 0 if the book was returned exactly 29 days after an afternoon checkout" begin | ||
@test late_fee("2019-05-01T16:12:00", "2019-05-30T14:32:45", 313) == 0 | ||
end | ||
|
||
@testset "Return the rate for one day if the book was returned exactly 30 days after an afternoon checkout" begin | ||
@test late_fee("2019-05-01T16:12:00", "2019-05-31T14:32:45", 234) == 234 | ||
end | ||
|
||
@testset "Multiply the number of days late by the rate for one day" begin | ||
@test late_fee("2021-01-01T08:00:00", "2021-02-13T08:00:00", 111) == 111 * 15 | ||
end | ||
|
||
@testset "Late fee is 50% off (rounded down) when the book is returned on a Monday" begin | ||
@test late_fee("2021-01-01T08:00:00", "2021-02-15T08:00:00", 111) == floor(111 * 17 * 0.5) | ||
end | ||
end |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong language