Skip to content

Use calendar days for "yesterday"/"tomorrow" rounding - #393

Open
gabrielcaires wants to merge 4 commits into
github:mainfrom
gabrielcaires:fix/yesterday-calendar-days
Open

gabrielcaires wants to merge 4 commits into
github:mainfrom
gabrielcaires:fix/yesterday-calendar-days

Conversation

@gabrielcaires

@gabrielcaires gabrielcaires commented Nov 3, 2026 •

Copy link
Copy Markdown

Fixes #313

Problem

roundToSingleUnit turned N days + H hours into days by rounding the hours (days += Math.round(hours / 24)). "Yesterday" is a calendar concept, so a date 36–37 hours ago that is still on the previous calendar day showed as "2 days ago", while 38h ago (also yesterday) showed "yesterday" again.

Example from the issue (local time): now is Apr 30 13:53, date is Apr 29 00:53 → -P1DT13H → was "2 days ago", now "yesterday".

Change

  • When a duration has days but no weeks/months/years, days is the number of calendar days between relativeTo and the target date (applyDuration(relativeTo, duration)), compared at local midnight and rounded so DST days of 23h/25h still count as one day. Other cases keep the existing rounding.
  • The element passes the now it used for elapsedTime as relativeTo, plus the exact date, to the relative and micro formatters. Calendar days are counted from the real date rather than one rebuilt from the precision-truncated duration (dropped milliseconds, precision="hour"/"day"), and both rounding passes in the micro path agree.
  • On a 25 hour DST day, a full day of elapsed time can stay on one calendar date; that case stays in hours ("24 hours ago") instead of rounding away.

The duration format without micro doesn't call roundToSingleUnit and is unaffected.

Tests

  • New roundToSingleUnit cases for the issue scenario (past and future), for the 2 days + under 1 hour window right after/before midnight, and a case that stays at 2 days.
  • P1DT20H / P1DT18H used the default relativeTo (midnight), where +1d20h is calendar "tomorrow". They now use noon, which gives P2D in both directions.
  • Cases for the exact date option (truncated milliseconds, precision="day") and the DST same-day case.
  • npm test passes (647 tests).

The rewrite proposed in #301 also counts calendar days and would cover this issue.

🤖 Generated with Claude Code

"yesterday" and "tomorrow" were decided by rounding the hours part, so a
date 36-37 hours ago (still yesterday on the calendar) showed as
"2 days ago". When a duration has days but no weeks/months/years, count
the calendar days between relativeTo and the target date instead.

The element now also passes the `now` it used for elapsedTime as
relativeTo, so both calculations agree near midnight.

Fixes github#313

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gabrielcaires
gabrielcaires requested a review from a team as a code owner November 3, 2026 12:43
Copilot AI auto review requested due to automatic review settings November 3, 2026 12:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved DST handling and fractional-second truncation defects produce incorrect relative times, including “now” for a 24-hour-old timestamp and off-by-one future day counts.

3 open issues
What changed in this PR

This PR addresses #313 by using local calendar-day differences for day-level relative-time rounding, so “yesterday” and “tomorrow” better match calendar dates.

Changes:

  • Replaces hour-based day rounding with local calendar-day counting for durations without weeks, months, or years.
  • Passes the same reference time through elapsed-time calculation and relative/micro formatting.
  • Adds regression cases for past and future dates near midnight.
File Description
test/​duration.ts Adds calendar-day regression cases and explicit reference times.
src/​relative-time-element.ts Shares the reference time with relative and micro formatters.
src/​duration.ts Adds local calendar-day counting to single-unit rounding.

Review effort: Balanced (auto)

Comment thread src/duration.ts Resolved
Comment thread src/duration.ts Resolved
Comment thread src/relative-time-element.ts Resolved
gabrielcaires and others added 2 commits November 3, 2026 12:53
Rebuilding the target date from a precision-truncated duration could
cross midnight (dropped milliseconds, precision="hour"/"day"), and
re-rounding an already rounded micro duration could do the same across
DST. roundToSingleUnit now accepts the exact `date` and the element
passes it along with relativeTo.

On a 25 hour DST day a full day of elapsed time can stay on one
calendar date; keep that in hours instead of rounding it to "now".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved calendar-day rounding issues produce incorrect durations, ignore the configured time zone, and leave labels stale across midnight, while existing tests have become clock-dependent.

4 open issues
3 resolved since last review

Review effort: Max

Comment thread src/duration.ts Resolved
Comment thread src/duration.ts Resolved
Comment thread src/relative-time-element.ts Resolved
Comment thread src/relative-time-element.ts Resolved
- Pass the resolved time zone into calendar-day rounding
- Pick the micro unit from the already-rounded duration instead of rounding twice
- Schedule an update at the next midnight for hour-scale dates
- Freeze the clock in micro day-format tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inconsistent use of "yesterday"

2 participants