Skip to content

Fix invoice issue date shown one day early and inconsistent date format - #165

Open
Bouni wants to merge 5 commits into
kittendevv:mainfrom
Bouni:fix-149
Open

Bouni wants to merge 5 commits into
kittendevv:mainfrom
Bouni:fix-149

Conversation

@Bouni

@Bouni Bouni commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Fix invoice issue date shown one day early and inconsistent date format

Closes #149

Problem

The issue reports two bugs:

  1. The issue date on the invoice page is one day early. Users have to enter the next day to see the right date. The PDF shows the right date.
  2. The date format is wrong. Some views ignore the Date Format setting.

Root cause

Off-by-one: issue and due dates are date-only values, but the backend stores them as UTC midnight: new Date("2026-10-09") → 2026-10-09T00:00:00.000Z. The frontend then read that timestamp with local getters (getDate(), toLocaleDateString()). In any timezone west of UTC (e.g. the Americas), UTC midnight is still the previous evening, so the date showed as Oct 8.

The PDF looked right only because it's rendered on the server, which runs in UTC in Docker. Any server timezone west of UTC would have shifted it as well.

Format: the invoice page and the PDF already followed Settings → Localization → Date Format. The invoice list and the dashboard hard-coded toLocaleDateString(locale, { month: "short", … }) (e.g. Oct 9, 2026) and ignored the setting.

Changes

1. Read invoice dates in UTC on the backend

UTC getters for date-only values in:

  • utils/pdf.ts: PDF issue/due dates
  • utils/ubl.ts, utils/facturx.ts, utils/fatturapa.ts: e-invoice XML exports
  • controllers/invoices.ts: overdue check

Output now stays the same whatever timezone the server runs in.

2. Show the correct issue date on the invoice page

  • New shared helper frontend/src/lib/utils/date.ts: formatDate(date, format, utc = true).
    • Reads date-only values in UTC.
    • Applies the Date Format setting (YYYY-MM-DD / DD.MM.YYYY).
    • Takes utc = false for real timestamps.
  • The invoice page uses it for issue and due dates.
  • The overdue badge reads the due date in UTC.
  • History timestamps still show local time.
  • Unit tests in date.test.ts.

3. Use the Date Format setting in the invoice list and dashboard

The issue date in both views, and "last updated" in the list, now use formatDate(), so all dates follow the setting.

4. Default new invoices to today's local date

The editor defaulted to new Date().toISOString().slice(0, 10), which is the UTC date. In the evening west of UTC that's already tomorrow. It now uses the local date.

Before / after (browser in America/New_York, issue date 2026-10-09)

View Before After (YYYY-MM-DD) After (DD.MM.YYYY)
Invoice page 2026-10-08 ❌ 2026-10-09 09.10.2026
Invoice list Oct 8, 2026 ❌ 2026-10-09 09.10.2026
Dashboard Oct 8, 2026 ❌ 2026-10-09 09.10.2026
PDF 2026-10-09 2026-10-09 09.10.2026

Not in scope

  • Stored data is unchanged: dates are still saved as UTC midnight, so no migration is needed.
  • No new date formats: the existing setting (YYYY-MM-DD, DD.MM.YYYY) is now simply applied everywhere.
  • API edge case: invoices created through the API without an issue date get the current date and time. Near midnight, these may show the UTC date rather than the local one. Invoices created through the UI aren't affected.

Testing

  • bun test → 17/17 pass (4 new for formatDate)
  • svelte-check → 0 errors
  • Simulated with TZ=America/New_York: old code gives 2026-10-08, new code gives 2026-10-09
  • With the browser/OS timezone set west of UTC: create an invoice and check the date on the invoice page, list, dashboard and PDF
  • Switch Date Format to DD.MM.YYYY and check that all of the views above follow it
  • An invoice whose due date is today isn't marked overdue; one due yesterday is

🤖 Generated with Claude Code

Bouni and others added 5 commits October 9, 2026 09:28
Issue and due dates are stored as UTC midnight. Formatting them with
local getters shifts them by a day when the server runs in a timezone
west of UTC. Use UTC getters for the PDF, the UBL/Factur-X/FatturaPA
exports and the overdue check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The invoice page read the UTC-midnight issue/due dates in the browser's
local time, showing the previous day west of UTC. Add a shared
formatDate() helper that reads date-only values in UTC and honours the
Date Format setting, and use it on the invoice page. The overdue badge
gets the same UTC treatment; history timestamps stay in local time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#149)

Both views hard-coded toLocaleDateString() with a short month name,
ignoring Settings > Localization > Date Format, and were affected by the
same off-by-one. Use the shared formatDate() helper instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
toISOString() returns the UTC date, which is already tomorrow in the
evening for users west of UTC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

Invoice Issue Date and Date Format Incorrect

1 participant