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:
@@ -258,8 +258,12 @@
|
||||
var request = new EventLogQueryRequest(
|
||||
CorrelationId: Guid.NewGuid().ToString("N"),
|
||||
SiteId: _selectedSiteId,
|
||||
From: _filterFrom.HasValue ? new DateTimeOffset(_filterFrom.Value, TimeSpan.Zero) : null,
|
||||
To: _filterTo.HasValue ? new DateTimeOffset(_filterTo.Value, TimeSpan.Zero) : null,
|
||||
// CentralUI-027: <input type="datetime-local"> binds with DateTimeKind.Unspecified
|
||||
// — the value is the operator's browser-local wall-clock. Tag it Local and
|
||||
// convert to UTC; the prior code labelled the local value as UTC, silently
|
||||
// shifting the query window by the operator's UTC offset.
|
||||
From: LocalInputToUtc(_filterFrom),
|
||||
To: LocalInputToUtc(_filterTo),
|
||||
EventType: string.IsNullOrWhiteSpace(_filterEventType) ? null : _filterEventType.Trim(),
|
||||
Severity: string.IsNullOrWhiteSpace(_filterSeverity) ? null : _filterSeverity,
|
||||
InstanceId: string.IsNullOrWhiteSpace(_filterInstanceName) ? null : _filterInstanceName.Trim(),
|
||||
@@ -289,6 +293,18 @@
|
||||
_searching = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CentralUI-027: convert a value bound from <c><input type="datetime-local"></c>
|
||||
/// (DateTimeKind.Unspecified, operator's browser-local wall-clock) into UTC. Must tag
|
||||
/// the value Local before <see cref="DateTime.ToUniversalTime"/> can do anything.
|
||||
/// </summary>
|
||||
private static DateTimeOffset? LocalInputToUtc(DateTime? value) =>
|
||||
value.HasValue
|
||||
? new DateTimeOffset(
|
||||
DateTime.SpecifyKind(value.Value, DateTimeKind.Local).ToUniversalTime(),
|
||||
TimeSpan.Zero)
|
||||
: (DateTimeOffset?)null;
|
||||
|
||||
private static string GetSeverityBadge(string severity) => severity switch
|
||||
{
|
||||
"Error" => "bg-danger",
|
||||
|
||||
Reference in New Issue
Block a user