Promote: staging -> develop - #922
Open
github-actions[bot] wants to merge 1 commit into
Open
Conversation
…tory filter (#907) EN: The transaction-history filter compared the end bound against the picked day at local midnight, so every transaction timestamped later on that day was dropped (#779). The bound is now the start of the following local day, compared exclusively, which makes the selected end day fully inclusive. Three regression tests cover the same-day-afternoon case, the day after the bound, and a UTC-flagged timestamp falling on the local end day. DE: Der Transaktions-Filter verglich die obere Grenze gegen den gewählten Tag um lokale Mitternacht, wodurch jede später an diesem Tag datierte Transaktion herausfiel (#779). Die Grenze ist jetzt der Beginn des Folgetags in lokaler Zeit, exklusiv verglichen, womit der gewählte Endtag vollständig enthalten ist. Drei Regressionstests decken den Nachmittagsfall, den Tag nach der Grenze und einen UTC-markierten Zeitstempel am lokalen Endtag ab. <details> <summary>Details</summary> ## Root cause `DatePickerField` → `DatePicker.pickDate` returns the selected day as `DateTime(y, m, d)`, i.e. **local midnight**. `TransactionHistoryFilterCubit._applyFilter` treated that value as the inclusive upper bound: ```dart final beforeEnd = endDate == null || !transactionDate.isAfter(endDate); ``` For an end date of `2026-04-01` that bound is `2026-04-01 00:00:00.000` local, so a transaction at `2026-04-01 14:00` satisfies `isAfter(bound)` and is excluded. Only transactions at exactly 00:00:00.000 on the selected end day survived — an off-by-one day on the upper edge of every range the user picks. The start bound is not affected: local midnight is already the correct *start* of the picked day, so a picked start date behaves as intended. ## Fix ```dart final endBound = endDate == null ? null : _startOfNextLocalDay(endDate); ... final beforeEnd = endBound == null || transactionDate.isBefore(endBound); ``` with ```dart DateTime _startOfNextLocalDay(DateTime date) { final local = date.toLocal(); return DateTime(local.year, local.month, local.day + 1); } ``` Two deliberate details: - **Exclusive next-day bound rather than `23:59:59.999`.** `DateTime` carries microsecond resolution, so an end-of-day sentinel would still leave a sub-millisecond hole. `isBefore(startOfNextDay)` has no gap. - **`DateTime(y, m, d + 1)` rather than `.add(const Duration(days: 1))`.** `Duration` adds 24 absolute hours; on a DST-transition day that lands at 23:00 or 01:00 local instead of the next local midnight. The constructor normalises overflowing day values and resolves the wall-clock instant correctly, including across month and year ends. ## The zone concern raised in the issue Checked and **not a defect**: `DateTime.isBefore` / `isAfter` compare `microsecondsSinceEpoch`, i.e. absolute instants, so a UTC-flagged `timestamp` (from `DateTime.parse` on an API `...Z` string) is compared correctly against a local bound — no cross-boundary drift. The trap the team has hit before is `DateTime.==` / equality, which additionally compares the `isUtc` flag; this comparison path never uses it. What *does* matter for zone consistency is which calendar day the bound denotes. `TransactionHistoryRow` renders `transaction.timestamp.toLocal()`, so the user reads local days; the bound is therefore normalised via `toLocal()` before the day components are taken, so the filtered range matches the days shown in the list. The added `toLocal()` is a no-op for the values the picker produces and guards the bound against a caller passing a UTC-flagged `DateTime`. ## Blast radius `state.filtered` also feeds `TransactionHistoryDownloadButton`, so the exported statement inherits the same correction — a statement generated with an end date of "today" previously omitted everything from today. ## Tests Added to `test/screens/transaction_history/cubits/transaction_history_filter_cubit_test.dart`: 1. `changeFilter keeps transactions timestamped later on the selected end day (issue #779 regression)` — 00:00, 14:00 and 23:59:59 on the end day; expects all three. Fails on the old code with 1 of 3. 2. `changeFilter still excludes the day after the selected end date` — guards against over-correcting the bound by a day. 3. `changeFilter includes a UTC-flagged timestamp falling on the local end day` — pins the instant-vs-flag semantics described above. One existing test name (`changeFilter includes the boundaries (isBefore / isAfter, not isAtSameMoment)`) was shortened to `changeFilter includes the boundary days`, since it no longer describes the implementation it names. Its assertions are unchanged, and all four pre-existing filter tests keep passing under the new bound. **Not executed locally:** no Flutter toolchain was available in the environment this change was prepared in, so `flutter test` and `flutter analyze` have not been run against it — CI on this PR is the first execution. The diff was reviewed by hand against the existing test file's structure and the repo's `page_width: 100` formatter setting. </details>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automatic Staging PR
This PR was automatically created after changes were pushed to staging.
Commits: 1 new commit(s)
Checklist