Skip to content

feat(changelog): extract and display published date for changelog entries#1465

Merged
arnestrickmann merged 1 commit intomainfrom
emdash/date-in-changelog-5xh
Mar 13, 2026
Merged

feat(changelog): extract and display published date for changelog entries#1465
arnestrickmann merged 1 commit intomainfrom
emdash/date-in-changelog-5xh

Conversation

@arnestrickmann
Copy link
Contributor

Summary

  • Extract published dates from changelog HTML when no <time> tag is present, supporting both human-readable (e.g. "March 13, 2026") and ISO date formats adjacent to version strings
  • When JSON API responses lack publishedAt, fall back to HTML scraping to resolve the date
  • Display the published date as a Badge in both the changelog modal and sidebar notification card
  • Extract shared formatChangelogPublishedAt helper into src/renderer/lib/changelogDate.ts for reuse

Changes

  • ChangelogService.ts — Added regex-based date extraction (extractPublishedAtForVersion, extractPublishedAtFromText), fetchHtml helper, and withResolvedHtmlPublishedAt fallback. JSON-sourced entries now attempt HTML enrichment when publishedAt is missing.
  • ChangelogModal.tsx — Replaced inline formatPublishedAt with shared helper; renders date in a Badge component
  • ChangelogNotificationCard.tsx — Added published date display as a Badge above the title
  • changelogDate.ts — New shared date formatting utility extracted from the modal
  • ChangelogService.test.ts — Added tests for date inference from rendered content and for avoiding incorrect cross-version date assignment

Test plan

  • Verify pnpm exec vitest run passes, including the two new parseChangelogHtml tests
  • Confirm the changelog modal shows a date badge for releases with and without <time> tags
  • Confirm the sidebar notification card shows the published date badge
  • Verify releases without any discoverable date do not show a badge

@vercel
Copy link

vercel bot commented Mar 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Mar 13, 2026 8:08pm

Request Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR adds published-date extraction and display to the changelog feature. When the JSON API response lacks a publishedAt field, the service falls back to scraping the HTML changelog page using regex patterns that look for human-readable dates ("March 13, 2026") and ISO dates adjacent to version strings. The extracted date is then shown as an outline Badge in both the changelog modal and the sidebar notification card, with a shared formatChangelogPublishedAt helper in src/renderer/lib/changelogDate.ts.

Key changes:

  • ChangelogService.ts adds extractPublishedAtForVersion, extractPublishedAtFromText, fetchHtml, and withResolvedHtmlPublishedAt; JSON-matched entries without a date now trigger an additional HTML fetch for enrichment
  • changelogDate.ts extracts the date formatting utility as a shared renderer helper
  • ChangelogModal.tsx and ChangelogNotificationCard.tsx adopt the shared helper and render the date as a Badge
  • Two new tests cover human-readable date inference and prevention of cross-version date leakage

Issues found:

  • ISO_DATE_REGEX uses [0-9:.+-Z] which creates an unintentional ASCII range (+ through Z); the hyphen must be escaped or repositioned — see inline comment on ChangelogService.ts
  • formatChangelogPublishedAt parses ISO date-only strings (e.g. "2026-03-13") via new Date(), which the ECMAScript spec treats as UTC midnight; users in any negative-UTC-offset timezone (the Americas) will see the badge date as one day earlier than authored — see inline comment on changelogDate.ts

Confidence Score: 3/5

  • Safe to merge with two targeted fixes: escape the regex hyphen and guard ISO date-only strings from UTC parsing in the formatter
  • The overall architecture is clean and the fallback strategy is well-designed. The two issues are concrete and user-visible: the ISO_DATE_REGEX character-class range is a latent correctness bug, and the UTC midnight parsing of ISO date-only strings will display the wrong day to users in US/Americas timezones. Neither is catastrophic but both are straightforward to fix before merging.
  • src/renderer/lib/changelogDate.ts (UTC parsing of ISO date-only strings) and src/main/services/ChangelogService.ts (ISO_DATE_REGEX character class)

Important Files Changed

Filename Overview
src/main/services/ChangelogService.ts Adds regex-based date extraction, fetchHtml helper, and withResolvedHtmlPublishedAt fallback; ISO_DATE_REGEX contains an unintentional character-class range ([0-9:.+-Z] should be [0-9:.\-+Z])
src/renderer/lib/changelogDate.ts New shared date-formatting helper extracted from ChangelogModal; ISO date-only strings (e.g. "2026-03-13") are parsed as UTC midnight, causing the formatted day to be off by one for users in negative-UTC-offset timezones
src/renderer/components/ChangelogModal.tsx Replaces the inline formatPublishedAt with the shared formatChangelogPublishedAt helper and wraps the date in a Badge component; no logic changes beyond the extraction
src/renderer/components/sidebar/ChangelogNotificationCard.tsx Adds the publishedAt badge above the title using the shared helper; straightforward addition with no logic issues
src/test/main/ChangelogService.test.ts Adds two tests covering date inference from rendered HTML and prevention of cross-version date assignment; both tests are well-scoped and use human-readable date strings (no ISO-only date coverage)

Sequence Diagram

sequenceDiagram
    participant App
    participant ChangelogService
    participant JsonAPI as JSON API
    participant HtmlPage as HTML Changelog Page
    participant Renderer

    App->>ChangelogService: getLatestEntry(version)
    loop For each API URL
        ChangelogService->>JsonAPI: fetchJson(apiUrl)
        JsonAPI-->>ChangelogService: payload (may lack publishedAt)
        alt match found with publishedAt
            ChangelogService-->>App: ChangelogEntry (complete)
        else match found, publishedAt missing
            ChangelogService->>HtmlPage: fetchHtml(EMDASH_CHANGELOG_URL)
            HtmlPage-->>ChangelogService: html
            ChangelogService->>ChangelogService: parseChangelogHtml(html, version)
            note over ChangelogService: extractTime OR extractPublishedAtForVersion
            ChangelogService-->>App: ChangelogEntry (publishedAt enriched)
        end
    end
    alt no JSON match at all
        ChangelogService->>HtmlPage: fetchHtml(EMDASH_CHANGELOG_URL)
        HtmlPage-->>ChangelogService: html
        ChangelogService->>ChangelogService: parseChangelogHtml(html, version)
        ChangelogService-->>App: ChangelogEntry (or null)
    end
    App->>Renderer: pass ChangelogEntry
    Renderer->>Renderer: formatChangelogPublishedAt(publishedAt)
    Renderer-->>App: Badge with formatted date
Loading

Last reviewed commit: fa7747a

const MONTH_NAME_PATTERN =
'(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)';
const HUMAN_DATE_REGEX = new RegExp(`\\b(${MONTH_NAME_PATTERN}\\s+\\d{1,2},\\s+\\d{4})\\b`, 'i');
const ISO_DATE_REGEX = /\b(\d{4}-\d{2}-\d{2}(?:[tT][0-9:.+-Z]*)?)\b/;
Copy link
Contributor

Choose a reason for hiding this comment

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

Unintentional character-class range in ISO_DATE_REGEX

The character class [0-9:.+-Z] contains an unintended ASCII range. In a regex character class, a bare - between two characters creates a range. Here + is ASCII 43 and Z is ASCII 90, so +-Z matches all characters from ASCII 43–90 — including ,, /, ;, <, =, >, ?, @, and all uppercase letters A–Y. The intent is clearly to match ISO 8601 time-segment characters (digits, colon, period, plus, hyphen, and the literal Z). The hyphen must either be escaped or placed at the start/end of the class to be treated as a literal.

Suggested change
const ISO_DATE_REGEX = /\b(\d{4}-\d{2}-\d{2}(?:[tT][0-9:.+-Z]*)?)\b/;
const ISO_DATE_REGEX = /\b(\d{4}-\d{2}-\d{2}(?:[tT][0-9:.\-+Z]*)?)\b/;

Comment on lines +4 to +5
const parsed = new Date(value);
if (Number.isNaN(parsed.getTime())) return value;
Copy link
Contributor

Choose a reason for hiding this comment

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

ISO date-only strings parsed as UTC midnight, causing off-by-one day for users in negative-offset timezones

When value is an ISO date-only string such as "2026-03-13" (without a time component), the ECMAScript specification mandates that new Date("2026-03-13") is parsed as UTC midnight. Intl.DateTimeFormat then formats this in the user's local timezone. For any user whose timezone is behind UTC (UTC-1 through UTC-12, covering all of the Americas), the displayed date will be one day earlier — March 12 instead of March 13. This is a user-visible regression that will affect the badge in both the changelog modal and the sidebar notification card.

This PR introduces new paths that can write ISO date-only strings into publishedAt via ISO_DATE_REGEX (e.g. an HTML heading like "2026-03-13 v0.4.32" extracts "2026-03-13"). The same bug pre-existed for <time datetime="2026-03-13">, but this PR widens the surface.

To prevent the timezone shift, parse date-only strings explicitly as local midnight instead of UTC:

Suggested change
const parsed = new Date(value);
if (Number.isNaN(parsed.getTime())) return value;
const parsed = /^\d{4}-\d{2}-\d{2}$/.test(value)
? new Date(`${value}T00:00:00`)
: new Date(value);

Adding a local-time suffix (T00:00:00, no Z) causes the engine to parse the date in local time, so the rendered day always matches what was authored.

@arnestrickmann arnestrickmann merged commit 7d3f9cb into main Mar 13, 2026
5 checks passed
@arnestrickmann arnestrickmann deleted the emdash/date-in-changelog-5xh branch March 13, 2026 21:38
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.

1 participant