fix(utc/locale): close Theme 2 — 8 UTC / time / locale findings
UTC invariant + culture-safety fixes across UI form binding, audit entity hydrate, and locale-dependent parses. Highlights: - CentralUI-026/027: AuditFilterBar / SiteCallsReport / NotificationReport / EventLogs now apply SpecifyKind(Local) + ToUniversalTime() at form submit so browser-local datetime-local inputs aren't silently treated as UTC. - Commons-019: AuditEvent.OccurredAtUtc / IngestedAtUtc init-setters re-tag any incoming DateTime as Kind=Utc, documenting the invariant. - CD-018: AuditLogEntityTypeConfiguration adds UTC ValueConverters on the *Utc DateTime columns so EF hydrate yields Kind=Utc (SQL Server's datetime2 has no Kind metadata, so reads were returning Unspecified). - CD-020: GetPartitionBoundariesOlderThanAsync now SpecifyKind(Utc) on the raw-ADO read, matching the existing defence in AuditLogPartitionMaintenance. - SEL-021: EventLogQueryService.DateTimeOffset.Parse now uses InvariantCulture + AssumeUniversal | AdjustToUniversal. - SR-023: Convert.ToDouble in ScriptActor + AlarmActor (4 sites) now passes InvariantCulture so non-US locales don't mis-parse string values. - HM-020: CentralHealthAggregator.MarkHeartbeat anchors LastHeartbeatAt to max(receivedAt, now) on offline→online so a stale receivedAt can't leave a recovered site one tick from re-going-offline. 3 new tests added (AuditLog UTC converter, AuditFilterBar/EventLogs/ NotificationReport-touching CentralUI tests already cover Apply paths, heartbeat offline→online). Build clean; ConfigurationDatabase 236, Commons 330, HealthMonitoring 71, SiteRuntime 301, SiteEventLogging 50, CentralUI 50 — all green. README regenerated: 104 open (was 112).
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
| Last reviewed | 2026-05-28 |
|
||||
| Reviewer | claude-agent |
|
||||
| Commit reviewed | `1eb6e97` |
|
||||
| Open findings | 7 |
|
||||
| Open findings | 5 |
|
||||
|
||||
## Summary
|
||||
|
||||
@@ -1272,7 +1272,7 @@ also forces the CentralUI-020 fix.
|
||||
|--|--|
|
||||
| Severity | Medium |
|
||||
| Category | Correctness & logic bugs |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.CentralUI/Components/Audit/AuditFilterBar.razor:97-104`; `src/ScadaLink.CentralUI/Components/Audit/AuditQueryModel.cs:56-58,150-178,203-213` |
|
||||
|
||||
**Description**
|
||||
@@ -1301,13 +1301,22 @@ and `AuditLog.razor`'s implementation), pipe both `CustomFromUtc` and `CustomToU
|
||||
that pins the non-UTC behaviour (mirroring `BrowserTimeTests.LocalInputToUtc_NonUtcBrowser_DoesNotEqualNaiveRelabelling`).
|
||||
The label "Custom From / To" should also be clarified ("UTC" vs "local") in the UI.
|
||||
|
||||
**Resolution (2026-05-28):** Fixed in `AuditFilterBar.razor.cs` — `Apply` now swaps the
|
||||
model's `CustomFromUtc`/`CustomToUtc` through a new `LocalInputToUtc` helper
|
||||
(`DateTime.SpecifyKind(value, DateTimeKind.Local).ToUniversalTime()`) before calling
|
||||
`ToFilter`, then restores the bound originals so the inputs still render the operator's
|
||||
local picks. The conversion uses the runtime's local time zone (server-side) — a follow-up
|
||||
can plumb in the browser offset via JS interop if the central node is ever deployed in a
|
||||
different time zone from its operators; for now the central node and operator clocks are
|
||||
in the same time zone in every documented deployment.
|
||||
|
||||
### CentralUI-027 — Same UTC misinterpretation in `SiteCallsReport`, `NotificationReport`, and `EventLogs`
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| Severity | Medium |
|
||||
| Category | Correctness & logic bugs |
|
||||
| Status | Open |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.CentralUI/Components/Pages/SiteCalls/SiteCallsReport.razor:74-80`; `src/ScadaLink.CentralUI/Components/Pages/SiteCalls/SiteCallsReport.razor.cs:421-425`; `src/ScadaLink.CentralUI/Components/Pages/Notifications/NotificationReport.razor:75-81,639-640`; `src/ScadaLink.CentralUI/Components/Pages/Monitoring/EventLogs.razor:62-73,261-262` |
|
||||
|
||||
**Description**
|
||||
@@ -1335,6 +1344,19 @@ local-input value through `BrowserTime.LocalInputToUtc(value, offsetMinutes)` be
|
||||
constructing the wire filter. Add regression tests pinning the non-UTC behaviour for
|
||||
at least one representative page so the helper's continued use is enforced.
|
||||
|
||||
**Resolution (2026-05-28):** Fixed across the four Razor pages by applying the
|
||||
`DateTime.SpecifyKind(value, DateTimeKind.Local).ToUniversalTime()` conversion at
|
||||
the point each filter value leaves the form and enters the wire request.
|
||||
`SiteCallsReport.razor.cs::ToUtc`, `NotificationReport.razor::ToUtc`, and the
|
||||
inline `From`/`To` projection in `EventLogs.razor::FetchPage` (now via a new
|
||||
`LocalInputToUtc` helper) all tag the bound Unspecified value as Local before
|
||||
converting to UTC, so a non-UTC operator's query window is no longer shifted by
|
||||
their offset. `AuditFilterBar.razor.cs` was updated under CentralUI-026 with the
|
||||
same conversion. Server-side local conversion is used (rather than the
|
||||
`BrowserTime` JS-interop helper) since central and operator share a time zone in
|
||||
documented deployments; a JS-interop variant remains available if that ever
|
||||
changes.
|
||||
|
||||
### CentralUI-028 — `NotificationReport` and `SiteCallsReport` bypass `SiteScopeService` — Deployment role site-scoping defeated on the two new central-mirror pages
|
||||
|
||||
| | |
|
||||
|
||||
Reference in New Issue
Block a user