Aman-Mittal opened a new pull request, #515: URL: https://github.com/apache/fineract-backoffice-ui/pull/515
Closes #496. ## What it does `formatDateToFineract` handed every string to `new Date()`. ECMAScript parses a **date-only** string as UTC midnight, while the getters that build the output read **local** time, so at any negative offset the two disagree: ``` TZ=America/New_York '2026-01-15' -> 14 January 2026 TZ=America/New_York '2026-01-01' -> 31 December 2025 ``` A string carrying a time or an explicit zone is still left to `new Date()`, which reads both correctly. Only the bare `YYYY-MM-DD` form changes. ## Proof it reached the ledger Closing an Active group from a Chromium context pinned to `America/New_York`, at **07:31 local** — nowhere near midnight, so this is not an edge-of-day artifact. The dialog's own default date was left untouched, which is the case the issue describes. The dialog on the pre-fix run:  | | browser's local date | `closureDate` sent | platform recorded | |---|---|---|---| | `main` @ `e6b31e4d` | 2026-09-06 | `05 September 2026` | `[2026, 9, 5]` | | this branch | 2026-09-06 | `06 September 2026` | `[2026, 9, 6]` | The user saw "Sep 6, 2026" on screen and the group was closed on the 5th. Recordings: [before](https://github.com/Aman-Mittal/fineract-backoffice-ui/raw/assets/issue-screenshots/date-utc-shift-before.webm) · [after](https://github.com/Aman-Mittal/fineract-backoffice-ui/raw/assets/issue-screenshots/date-utc-shift-after.webm). ## The helper fix alone was not enough Working through the 55 call sites turned up three more ways a date crossed the UTC boundary before or after the helper saw it. Each is in the second commit: - **Five call sites wrapped the value in `new Date()` first** — `group-view` ×2, `loan-view` ×2, `client-view` ×1. The dialogs behind them return `toIsoDate(new Date())`, a bare `YYYY-MM-DD`, so that wrapper re-parsed it as UTC before the new branch could apply. This is the path the evidence above exercises. - **Two dialogs seeded their picker from `new Date().toISOString()`** — `center-action-dialog` and `center-meeting-dialog`. Confirming without touching the picker sent a different calendar day than the one displayed. Both now use `toIsoDate(new Date())`, which the group and client action dialogs already did. - **The audit-log filter built its range with `toISOString().split('T')`**, which `date-formatter`'s own doc comment warns against — "from today" read as tomorrow east of Greenwich and yesterday west of it. ## Three hand-rolled workarounds are collapsed The defect was already known locally. `deposit-account-view` and `share-account-view` each split the ISO string into parts before formatting, both carrying a comment explaining why, and `loan-view` had a private `isoToFineractDate` doing the same. All three are the helper's own behaviour now. ## Range checking `new Date(2026, 12, 45)` does not fail — it rolls forward to 14 February 2027. Since the caller sends the result to the platform as the user's chosen date, a malformed value is refused rather than silently becoming a real one. Previously `'2026-13-45'` returned `''`; it still does. ## The regression guard, and why it is a CI step Nothing in this project tested west of Greenwich: the runner's clock is UTC and `playwright.config.ts` pins `Asia/Kolkata`. Every offset the suite exercises is ≥ 0, which is exactly why this shipped — and why no assertion running only at UTC can catch its return. The three new cases pass at UTC either way. So `ci.yml` runs the unit suite a second time under `TZ=America/New_York`. Reverting only the helper and running that pass: ``` × is read as the calendar date it spells, not as UTC midnight × does not roll back across a month or a year boundary × agrees with the array form of the same date AssertionError: expected '14 January 2026' to be '15 January 2026' AssertionError: expected '31 December 2025' to be '01 January 2026' ``` It costs about a minute per PR and covers the whole application rather than the one helper. Happy to drop it to a single-file run if that trade is not wanted — say so and I will. The pass also found four `...T00:00:00.000Z` fixtures in `center-view.component.test.ts` that no dialog in this application produces; they passed only because fixture and formatter agreed to read the value as UTC. They now carry the calendar date the dialogs actually return. ## Verification | | | |---|---| | `npm run test:unit` | 236 files / 1419 tests, exit 0 | | `TZ=America/New_York npm run test:unit` | 236 files / 1419 tests, exit 0 | | `npm run build` | clean | | `npm run lint` | clean — `date-formatter.ts` no longer needs its `unicorn/prefer-number-properties` suppression, pruned via `lint:prune` | Under the negative offset the suite went 9 failures → 3 → 0 as each layer was fixed; the last three were the fixtures above. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
