Skip to content

The quarter ends where the reader is, not seven hours earlier - #30

Open
nguyenngothuong wants to merge 1 commit into
votrongdao:devfrom
diginno-net:fix/console-quarter-boundary-is-a-local-date
Open

The quarter ends where the reader is, not seven hours earlier#30
nguyenngothuong wants to merge 1 commit into
votrongdao:devfrom
diginno-net:fix/console-quarter-boundary-is-a-local-date

Conversation

@nguyenngothuong

Copy link
Copy Markdown

The sales console is the application’s landing page, and east of Greenwich it opens empty.

What happens

useConsole builds its horizon boundaries with new Date(y, m, 1).toISOString().slice(0, 10). That constructs local midnight and then prints it in UTC, so at any positive offset the string comes back a day early — at UTC+07 the third quarter of 2026 ends on 2026-09-30 instead of 2026-10-01.

Those boundaries are compared against close_date, which the server sends as a plain calendar date with no zone on it. An opportunity closing on the last day of the quarter therefore satisfies close >= quarterEnd and is filtered out. This quarter is the default horizon on the console, so the effect is not subtle:

open pipeline
dev TZ=UTC $184k
dev TZ=Asia/Bangkok $0
this branch TZ=Asia/Bangkok $184k

Same tenant, same rows, same seed — browser timezone the only variable. Measured against docker compose up, driven with Playwright.

today had the same fault by the same route: for the first seven hours of a local day it named the previous one, which is what closingThisMonth filters from.

The change

localDay formats a date’s local calendar day, and it is the only thing the boundaries now go through. They move into an exported horizonBounds(now) so they can be pinned at a date and a timezone — useConsole passes the real clock.

Tests

src/features/sales/__tests__/consoleHorizon.test.ts, written first, runs under TZ=Asia/Bangkok. Four of its five assertions fail against the previous implementation; all five pass against this one. It sets the zone deliberately: at UTC every one of them passes against the unfixed code, which is why the suite never caught this.

Not in this change

liveRecords.test.ts > renders a server timestamp as a day fails on dev east of UTC and still fails here. It is the same root cause, but it is a bug in the expectation — the test asserts 28 Aug 2026 for a T23:18:06Z timestamp — and this one is a bug in the application. Happy to fix it in a second PR; it seemed wrong to bundle a test correction with a behaviour fix.

npm run lint could not be run at all: the repository has no eslint.config.js and ESLint 9 requires one. Filing that separately.


Signed off under the DCO. Verified: tsc -b clean, npm run build clean, npm test 136 passed with the one pre-existing failure above.

`useConsole` built its horizon boundaries with
`new Date(y, m, 1).toISOString().slice(0, 10)`. That constructs local midnight and then
prints it in UTC, so at any positive offset the string comes back a day early: at UTC+07
the third quarter of 2026 ended on 2026-09-30 rather than 2026-10-01.

The boundaries are compared against `close_date`, which the server sends as a plain
calendar date with no zone on it, so an opportunity closing on the last day of the
quarter satisfied `close >= quarterEnd` and was filtered out. `This quarter` is the
default horizon on the sales console, which is the application's landing page — so every
reader in Asia, Australia or eastern Europe opened the CRM to an empty pipeline while a
reader in London opened the same tenant and saw it full. `today` had the same fault: for
the first seven hours of a local day it named the previous one.

Measured against the running sample, same tenant, same rows, browser timezone the only
variable:

    origin/dev      TZ=Asia/Bangkok   open pipeline = $0
    origin/dev      TZ=UTC            open pipeline = $184k
    this commit     TZ=Asia/Bangkok   open pipeline = $184k

The boundaries move into an exported `horizonBounds(now)` so they can be pinned at a date
*and* a timezone; `localDay` formats a date's local calendar day and is the only thing
either of them now goes through. `src/features/sales/__tests__/consoleHorizon.test.ts`
runs under `TZ=Asia/Bangkok` — at UTC every one of its assertions passes against the
unfixed code, which is why the suite never caught this. Four of its five fail against the
previous implementation.

This is the same root cause as the `liveRecords` day-formatting assertion that fails on
this commit's parent east of UTC; that one is left alone, because it is a bug in the
expectation and this is a bug in the application.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: nguyenngothuong <nguyenthuongtb12@gmail.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.

1 participant