2026-05-23-import-duplicate-timezone

activetype/changelogdomain/transactionsdomain/import

2026-05-23 — Bulk import fuzzy duplicate detection uses user timezone

TL;DR

Bulk import preview (checkBulkImportDuplicates) now matches fuzzy duplicates on the user's civil calendar day (User.settings.timezone, default UTC) instead of UTC-only day slicing. Fixes false negatives/positives for operators in non-UTC zones when comparing import rows to existing ledger entries.

What

  • Extracted zonedDayBoundsFromEpoch into src/lib/utils/zonedDayBounds.ts (IANA tz via Intl, binary search for day bounds, UTC fallback on invalid tz).
  • checkBulkImportDuplicates loads settings.timezone once per probe batch and uses zoned day bounds for fuzzy duplicate queries (same day + amount + accounts, excluding exact bankImportRef match).
  • Ref duplicate detection unchanged (bankImportRef variants).

Why

Transaction dates in the UI are chosen as local calendar days, but fuzzy duplicate detection sliced days in UTC. A transaction near midnight in Asia/Jakarta could miss or spuriously match ledger rows on adjacent UTC days.

Impact

  • In-app bulk import (AI normalize flow): duplicate flags in step 1 preview align with how the user thinks about dates.
  • Integrations API push: unchanged — still relies on externalId / bankImportRef for idempotent reruns; fuzzy duplicate is preview-only in the modal.

Touches

  • src/lib/utils/zonedDayBounds.ts (new)
  • src/lib/actions/transaction.tscheckBulkImportDuplicates
  • Repo docs/features/01-transactions.md — bulk import duplicate behavior

Links

  • Hub: [[Projects/personal-finance-notion/personal-finance-notion]]