Summary
clear_unit currently resets a field to its minimum value (day → 1, month → 1, hour → 0). This covers start_of_* semantics. However, the equally important end_of_* operations are missing entirely, and consumers are forced to reimplement non-trivial calendar logic — particularly end_of_month, which must handle varying month lengths and leap years correctly.
Proposed API
On Date
date.start_of_month()? -> Date // 2024-10-15 → 2024-10-01
date.end_of_month()? -> Date // 2024-10-15 → 2024-10-31
// 2024-02-10 → 2024-02-29 (leap year)
// 2023-02-10 → 2023-02-28
date.start_of_year()? -> Date // 2024-10-15 → 2024-01-01
date.end_of_year()? -> Date // 2024-10-15 → 2024-12-31
date.start_of_week()? -> Date // ISO: Monday of current week
date.end_of_week()? -> Date // ISO: Sunday of current week
date.start_of_quarter()? -> Date // 2024-11-15 → 2024-10-01 (Q4)
date.end_of_quarter()? -> Date // 2024-11-15 → 2024-12-31 (Q4)
On DateTime
Same methods, preserving or zeroing time components as appropriate:
datetime.start_of_month()? -> DateTime // sets day=1, time=00:00:00
datetime.end_of_month()? -> DateTime // sets day=last, time=23:59:59
datetime.start_of_day()? -> DateTime // sets time=00:00:00 (alias for clear_time)
datetime.end_of_day()? -> DateTime // sets time=23:59:59
Why Not Just Use clear_unit?
clear_unit is a low-level building block. These methods are higher-level semantic operations:
| Goal |
With clear_unit |
With new methods |
| First day of month |
date.clear_unit(DateUnit::Day)? |
date.start_of_month()? |
| Last day of month |
Manual: compute days in month, then set |
date.end_of_month()? |
| First day of quarter |
Manual: compute quarter start month, then set |
date.start_of_quarter()? |
| Last second of day |
Manual: update to 23:59:59 |
datetime.end_of_day()? |
end_of_month in particular is non-trivial: it requires knowing the number of days in the month, which depends on the year (leap year handling for February). This is exactly the kind of logic timeflow should own.
Implementation Notes
end_of_month should use chrono's NaiveDate::from_ymd_opt with the next month's first day minus one day — the idiomatic approach that handles all edge cases automatically.
start_of_week / end_of_week should follow ISO 8601 (Monday = start, Sunday = end).
- Quarter boundaries: Q1 = Jan–Mar, Q2 = Apr–Jun, Q3 = Jul–Sep, Q4 = Oct–Dec.
- All methods return
Result<Self, SpanError> for consistency with the existing API.
- These methods can be implemented in terms of existing primitives (
clear_unit, update, new) to minimise new code surface.
Acceptance Criteria
Summary
clear_unitcurrently resets a field to its minimum value (day → 1, month → 1, hour → 0). This coversstart_of_*semantics. However, the equally importantend_of_*operations are missing entirely, and consumers are forced to reimplement non-trivial calendar logic — particularlyend_of_month, which must handle varying month lengths and leap years correctly.Proposed API
On
DateOn
DateTimeSame methods, preserving or zeroing time components as appropriate:
Why Not Just Use
clear_unit?clear_unitis a low-level building block. These methods are higher-level semantic operations:clear_unitdate.clear_unit(DateUnit::Day)?date.start_of_month()?date.end_of_month()?date.start_of_quarter()?updateto23:59:59datetime.end_of_day()?end_of_monthin particular is non-trivial: it requires knowing the number of days in the month, which depends on the year (leap year handling for February). This is exactly the kind of logictimeflowshould own.Implementation Notes
end_of_monthshould usechrono'sNaiveDate::from_ymd_optwith the next month's first day minus one day — the idiomatic approach that handles all edge cases automatically.start_of_week/end_of_weekshould follow ISO 8601 (Monday = start, Sunday = end).Result<Self, SpanError>for consistency with the existing API.clear_unit,update,new) to minimise new code surface.Acceptance Criteria
start_of_month,end_of_month,start_of_year,end_of_yearimplemented onDateandDateTimestart_of_week,end_of_weekimplemented (ISO Monday–Sunday)start_of_quarter,end_of_quarterimplementedstart_of_day,end_of_dayimplemented onDateTimeend_of_monthcorrectly handles February in leap and non-leap yearsend_of_monthcorrectly handles all 28/29/30/31-day monthsrust,ignoreexample