Skip to content

fix(i18n): use Intl.DateTimeFormat for locale-aware date/time display#894

Merged
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/locale-date-formatting
Jul 3, 2026
Merged

fix(i18n): use Intl.DateTimeFormat for locale-aware date/time display#894
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/locale-date-formatting

Conversation

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

Summary

Fixes inconsistent date/time display that hardcoded English (moment format strings) instead of respecting the browser's locale. Reported in ActivityWatch/aw-server-rust#602.

Changed files:

  • src/util/time.ts — adds three new locale-aware helpers using Intl.DateTimeFormat
  • src/views/Trends.vue — range labels ("Jun 15 – Jul 2") and busiest-day label ("Mon, Jun 15") now use the browser locale
  • src/visualizations/timeline.ts — event clocktime popup now respects locale time format

New helpers in time.ts:

  • format_date_short(date, locale?) — replaces moment.format('MMM D'), produces e.g. "15 jun" in sv-SE
  • format_date_with_weekday(date, locale?) — replaces moment.format('ddd, MMM D'), produces e.g. "mån 15 jun" in sv-SE
  • format_time_of_day(date, locale?) — replaces moment.format('HH:mm:ss'), produces 12h or 24h per locale

Out of scope (for follow-ups):

  • Clock face labels in Timespiral/sunburst visualizations (decorative, lower priority)
  • periodReadable() week labels in timeperiod.ts

The existing format_weekday_short, format_day_of_month, and get_short_month_labels in time.ts already used Intl.DateTimeFormat — this PR extends that pattern to the remaining display-facing call sites.

Test plan

  • Build passes: npm ci && make build
  • Lint passes: make lint
  • Visual check: Trends view date range labels render in correct locale
  • Visual check: Timeline event popup shows locale-appropriate time format

…d timeline

Replaces hardcoded moment().format('MMM D'), moment().format('ddd, MMM D'),
and moment().format('HH:mm:ss') calls with locale-aware Intl.DateTimeFormat
helpers, so dates and times respect the browser's locale instead of always
rendering in English.

Adds three new helpers to src/util/time.ts:
- format_date_short(): "Jun 15" → "15 jun" in sv-SE
- format_date_with_weekday(): "Mon, Jun 15" → "mån 15 jun" in sv-SE
- format_time_of_day(): "14:30:05" or "2:30:05 PM" per locale

Fixes the date labels in Trends.vue (range labels, busiest-day label)
and the event clocktime in the timeline visualization popup.
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 27.27273% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 35.58%. Comparing base (f24f6e9) to head (b02e226).

Files with missing lines Patch % Lines
src/util/time.ts 33.33% 6 Missing ⚠️
src/visualizations/timeline.ts 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #894      +/-   ##
==========================================
- Coverage   35.59%   35.58%   -0.01%     
==========================================
  Files          36       36              
  Lines        2152     2161       +9     
  Branches      398      401       +3     
==========================================
+ Hits          766      769       +3     
- Misses       1365     1371       +6     
  Partials       21       21              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces hardcoded moment format strings with three new Intl.DateTimeFormat helpers (format_date_short, format_date_with_weekday, format_time_of_day) that adapt to the browser's locale, fixing the English-only date/time display reported in the upstream issue.

  • src/util/time.ts: Adds three locale-aware helpers following the existing Intl.DateTimeFormat pattern already used by format_weekday_short and format_day_of_month.
  • src/views/Trends.vue: Range labels and busiest-day label now use the new helpers instead of moment.format('MMM D') / moment.format('ddd, MMM D').
  • src/visualizations/timeline.ts: Timeline event popup clocktime replaces moment(t.timestamp).format('HH:mm:ss') with format_time_of_day, using hour: '2-digit' for consistent zero-padding across 12h and 24h locales.

Confidence Score: 5/5

Safe to merge — the three new helpers are straightforward Intl.DateTimeFormat wrappers that match the timezone handling of the moment calls they replace.

All three call sites pass valid Date objects or ISO strings, timezone handling is equivalent to the replaced moment.format() calls (both convert to local time), and the hour: '2-digit' fix already landed in the head commit ensures consistent zero-padding in the timeline popup.

No files require special attention.

Important Files Changed

Filename Overview
src/util/time.ts Adds three locale-aware Intl.DateTimeFormat helpers; logic is correct and consistent with existing format_weekday_short / format_day_of_month pattern.
src/views/Trends.vue Range and busiest-day labels migrated from moment.format() to the new helpers; moment.toDate() correctly bridges the two APIs.
src/visualizations/timeline.ts Timeline clocktime migrated from moment HH:mm:ss to format_time_of_day; timezone handling is equivalent to the old moment() local-time conversion.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Event timestamp / moment.Moment / Date string] --> B{format helper}
    B --> C[format_date_short\nIntl month+day]
    B --> D[format_date_with_weekday\nIntl weekday+month+day]
    B --> E[format_time_of_day\nIntl hour+minute+second]

    C --> F[Trends.vue\ncurrentRangeLabel\npreviousRangeLabel]
    D --> G[Trends.vue\nbusiestDay.label]
    E --> H[timeline.ts\nclocktime popup td]

    style C fill:#d4edda
    style D fill:#d4edda
    style E fill:#d4edda
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Event timestamp / moment.Moment / Date string] --> B{format helper}
    B --> C[format_date_short\nIntl month+day]
    B --> D[format_date_with_weekday\nIntl weekday+month+day]
    B --> E[format_time_of_day\nIntl hour+minute+second]

    C --> F[Trends.vue\ncurrentRangeLabel\npreviousRangeLabel]
    D --> G[Trends.vue\nbusiestDay.label]
    E --> H[timeline.ts\nclocktime popup td]

    style C fill:#d4edda
    style D fill:#d4edda
    style E fill:#d4edda
Loading

Reviews (4): Last reviewed commit: "fix(time): use 2-digit hour to ensure co..." | Re-trigger Greptile

Comment thread src/util/time.ts
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

1 similar comment
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

All checks passing and ready for merge:

  • ✅ Build (webpack & vite)
  • ✅ Tests (3 variants)
  • ✅ Lint
  • ✅ CodeQL
  • ✅ Greptile Review (5/5 confidence)

The new Intl.DateTimeFormat helpers are straightforward and safe. Waiting for maintainer approval.

@ErikBjare ErikBjare merged commit 17d4a4b into ActivityWatch:master Jul 3, 2026
9 checks passed
TimeToBuildBob added a commit to TimeToBuildBob/aw-webui that referenced this pull request Jul 3, 2026
…ActivityWatch#894)

* fix(i18n): use Intl.DateTimeFormat for date/time display in Trends and timeline

Replaces hardcoded moment().format('MMM D'), moment().format('ddd, MMM D'),
and moment().format('HH:mm:ss') calls with locale-aware Intl.DateTimeFormat
helpers, so dates and times respect the browser's locale instead of always
rendering in English.

Adds three new helpers to src/util/time.ts:
- format_date_short(): "Jun 15" → "15 jun" in sv-SE
- format_date_with_weekday(): "Mon, Jun 15" → "mån 15 jun" in sv-SE
- format_time_of_day(): "14:30:05" or "2:30:05 PM" per locale

Fixes the date labels in Trends.vue (range labels, busiest-day label)
and the event clocktime in the timeline visualization popup.

* fix(lint): format options objects per prettier style

* fix(time): use 2-digit hour to ensure consistent zero-padding in timeline

---------

Co-authored-by: Bob <bob@gptme.org>
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.

2 participants