Problem
The datetime concept covers LocalDate, LocalDateTime and formatting, but it does not cover amounts of time. Duration, Period and ChronoUnit.between() do not appear anywhere in the concept docs. The recently added beauty-salon-regulars exercise (#2944) also lists Duration and Period as out of scope.
As a result, students who meet date arithmetic in practice exercises (gigasecond, swift-scheduling, ledger) either do the arithmetic by hand or run into the classic pitfalls of the API:
LocalDate start = LocalDate.of(2024, 1, 15);
LocalDate end = LocalDate.of(2024, 3, 20);
Period.between(start, end); // P2M5D
Period.between(start, end).getDays(); // 5, not 65
ChronoUnit.DAYS.between(start, end); // 65
Duration.between(start, end); // throws UnsupportedTemporalTypeException
LocalDate.of(2025, 1, 31).plus(Period.ofMonths(1)); // 2025-02-28
The Period.getDays() mistake in particular comes up again and again when people compute āhow many days between two datesā.
Proposal
Add one concept, durations-and-periods (the name is open to discussion), together with a concept exercise.
Iād suggest one concept rather than two: what students most need to learn is the contrast between the two types.
Period |
Duration |
|
|---|---|---|
| Measures | years, months, days (calendar based) | seconds and nanoseconds (exact time) |
| Works with | LocalDate |
LocalTime, LocalDateTime, Instant |
| Typical use | āmembership expires in 1 monthā | āthe session lasted 90 minutesā |
Learning objectives
- Know the difference between
Period(date-based) andDuration(time-based). - Create amounts with
Period.ofX()/Duration.ofX()and add or subtract them withplus()/minus(). - Compute the amount between two temporals with
Period.between(),Duration.between()andChronoUnit.X.between(). - Understand that
Periodkeeps its units separately (P2M5D) and thatgetDays()is not a total; useChronoUnit.DAYS.between()ortoTotalMonths()when a total is needed. - Understand month-end clamping (
Jan 31 + 1 month = Feb 28/29). - Use
Durationaccessors such astoMinutes(),toHours()andtoMinutesPart().
Out of scope
- Time zones and
ZonedDateTime. The DST difference betweenPeriod.ofDays(1)andDuration.ofDays(1)can be shown as a short aside inabout.md, but the exercise itself would stay onLocal*types, since zones are not taught anywhere in the track yet. - Parsing and formatting ISO-8601 amounts (
P1M,PT90M), except for a short mention. - Custom
TemporalUnit/TemporalAmountimplementations.
Prerequisites
datetimenumbers
Concept exercise
The story should need both types, so students cannot get by with only one of them. One option is a gym or rental service:
- The membership end date is the start date plus a
Periodof N months. This tests month-end clamping. - The number of remaining days is computed with
ChronoUnit.DAYS.between(). This tests thegetDays()trap. - The length of a visit is a
Durationbetween check-in and check-outLocalDateTimes, billed per started 15 minutes. - An optional extra task checks whether a visit exceeded a time limit with
Duration.compareTo().
Iām happy to work on the story if the general direction is accepted.
Practice exercises that could reference the concept
gigasecond:plus(Duration.ofSeconds(1_000_000_000))swift-schedulingledger
Add the concept to their practices / prerequisites where it fits.
Deliverables
concepts/durations-and-periods/{about.md, introduction.md, links.json, .meta/config.json}- A concept exercise with
.docs, .meta/design.md, a reference solution and tests config.jsonupdates (concept, exercise, and links from the practice exercises above)- Optional: analyzer rules, e.g. flag
Period.between(...).getDays()used as a total day count
Iād be glad to open a PR for this, following the same structure as the temporal-adjusters concept.