Skip to content

Times in the repeated autumn hour show their UTC offset in grids, text and chart CSV exports, in Lite and the Darling viewer - #4855

Merged
erikdarlingdata merged 16 commits into
devfrom
fix/4766-repeated-hour-offset
Sep 30, 2026
Merged

erikdarlingdata merged 16 commits into
devfrom
fix/4766-repeated-hour-offset

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Follows #4766, #4841 and #4848 for the grids, the text lines and the chart CSV export, in Lite and the Darling viewer.

Why

On the day the clocks go back, one wall-clock hour happens twice. #4848 made the charts and time windows keep each exact instant through that hour. But the server tab grids, the alert history rows and the chart CSV export still wrote both occurrences the same way. On a US Eastern server clock, 05:30Z and 06:30Z on 2026-11-01 both read 2026-11-01 01:30:00. A row did not say which of the two it meant.

Behavior change

  • Grids and text that show one time: in the repeated hour only, the time is followed by a space and its UTC offset. The first 01:30 reads 2026-11-01 01:30:00 -04:00 and the second reads 2026-11-01 01:30:00 -05:00. Every other time reads as before, and UTC mode never changes. A fixed-offset server clock never shows an offset.
  • Axis and tick labels stay plain. The viewer's time-range caption now shows the offset in the repeated hour, as Lite's has since Charts and time windows keep exact instants across daylight saving changes, in Lite and the Darling viewer (#4766) #4848.
  • Times read from a stored server wall clock stay plain in the repeated hour: Default Trace events, job history and the next scheduled Agent run. A stored wall time does not say which of the two passes it was in, so its offset is unknown.
  • Time columns that show the offset sort by the row's UTC instant, so the two passes of the repeated hour stay in order. Collection health and collection log times, which sorted as text ("10/1" before "9/30"), now sort by time.
  • Lite FinOps First Seen and Last Seen, and Lite collection health times, used this machine's clock in every mode. They now follow the display mode, like the other grids.
  • A row whose server can differ from the tab's server now uses that server's clock. This covers Lite FinOps app connections (the selected server), collection health (each row's server) and the viewer FinOps inventory and app connections (each row's server).
  • The baseline discontinuity marker names the offset in the repeated hour: baseline discontinuity at 2026-11-01 01:30 -04:00 (restart).
  • Eight FinOps and Query Store columns that formatted a DateTime in XAML now show text. They take the offset, follow the current culture's separators like the other grids, and are 170 px wide. A grid CSV export writes the shown text.
  • A fixed-width time column cuts off text longer than its width. There the offset stays hidden until the column is widened.
  • Chart CSV export: every row gains a 4th column, UTC offset, so a script that checks the column count now sees 4. The first three cells of each row are unchanged. The offset is on every row, not only in the repeated hour, so the whole column reads one way:
DateTime (Eastern Standard Time),Series,Value,UTC offset
2026-11-01 01:30:00,CPU,12.5,-04:00
2026-11-01 01:30:00,CPU,12.5,-05:00

What changes

  • PerformanceMonitor.Ui/DisplayZone.cs: new UtcOffsetText, the UTC offset of any instant in a zone (+00:00 in UTC).
  • Lite ContextMenuHelper and viewer ViewerServerTab.ChartContextMenu: the chart CSV header ends in UTC offset, and each row ends in its instant's offset.
  • Lite ServerTimeHelper.FormatServerTime: grid times take the offset in the repeated hour, through one new method, FormatInstant. The format still uses the current culture. The alert history row, the History windows' summary range, collection health and FinOps rows use FormatInstant with their own clock.
  • Viewer ViewerTimeHelper.FormatForDisplay (new): the twin of FormatServerTime, with an overload that takes a zone and a nullable overload that returns an empty string. 46 text sites that wrote ForDisplay(x).ToString(format) now call it with the same format. Alert rows, Manage Servers, notification routes and the FinOps rows use it too.
  • BaselineDiscontinuities.Sentence takes an optional offset, and both products pass it for the chart's display zone.
  • FinOps and Query Store rows gained text properties, and the viewer rows gained ...Utc members. The Lite FinOps tab stamps the selected server's clock on its rows. The viewer uses ClockForServerOrMachine for rows that span servers.
  • SortMemberPath on 72 time columns (36 per product) points at the row's UTC DateTime. DataGridFilterManager restores a saved sort by a column's SortMemberPath when it has one.
  • The viewer's time-range caption makes the same call as Lite's.
  • SystemEventRowFormat.StoredWallClock (new, in both products) words a Default Trace time as the plain wall time, and the Default Trace row uses it. The other System Events rows hold real instants and take the offset. The viewer's job history and Agent status texts keep the plain formatter, and their doc comments say why. The wrapper census in ConsumedTimestampFrameDisciplineTests lists StoredWallClock as a plain renderer.

Not in this change

  • Other time columns that show no offset still sort by their text.
  • The viewer's eight System Events grids have no UTC member on their rows, so they keep their text sort.

Test plan

  • Chart CSV (ChartCsvExportDisplayZoneTests, ViewerContextMenuTests): the two instants of the repeated hour write the same time cell and different offsets. The header ends in UTC offset, every row carries an offset, and UTC rows read +00:00.
  • Text (ServerTimeHelperClockTests, ViewerServerClockTests, AlertHistoryRowClockTests, ViewerAlertRowClockTests): the two instants read 01:30 -04:00 and 01:30 -05:00 in server mode, and 05:30 and 06:30 in UTC mode. The offset does not depend on the culture, and every other time reads as before.
  • Own-server clocks (CollectionHealthDisplayZoneTests, FinOpsRowDisplayZoneTests, FinOpsSeenColumnsZoneTests, ViewerFinOpsRowClockTests): the row's server is in a different zone from the active tab's server. The row reads its own server's wall time, not the tab's and not this machine's.
  • Summary range, marker and slicer (HistoryGridZoneTests, BaselineDiscontinuityTests, ViewerSlicerRangeLabelTests, ViewerNotificationRouteDisplayTests, ViewerGridTimeTextZoneTests).
  • Sort (GridTimeColumnSortMemberTests, ViewerGridTimeColumnSortMemberTests): each changed column's SortMemberPath names a DateTime on its row type. Two rows at 05:45Z and 06:15Z sort first pass first on the UTC member, and not on the display DateTime or the text.
  • Stored wall clock (StoredServerClockRowTextTests, both products): a Default Trace event, a job step and a next scheduled run come from stored wall clocks. They print no offset in either pass of the repeated hour. A real instant in the same test still does.
  • Other zones (ServerTimeHelperClockTests, ViewerServerClockTests): a fixed-offset clock never shows an offset at the instants where a US zone repeats an hour. On a Berlin clock, 2026-10-25 00:30Z and 01:30Z read 02:30:00 +02:00 and 02:30:00 +01:00.
  • RED: with each product change put back, its tests fail. Examples: TheTwoPointsOfTheRepeatedHour_WriteTheSameTimeCell_AndAreToldApartByTheirOffsets, FormatForDisplay_InServerMode_GivesEachInstantOfTheRepeatedHourItsOwnOffset, CollectionHealthDisplayZoneTests.ServerMode_ARowReadsItsOwnServersWallTime_NotTheActiveTabsOrThisMachines, HistoryGridZoneTests.EachHistoryWindow_WordsItsSummaryRange_ThroughFormatInstant, TheRepeatedHour_SortsFirstPassBeforeSecond_OnTheUtcMember_ButNotOnTheDisplayDateTimeOrTheText and, with the offset put back on a stored wall-clock row, ADefaultTraceEventReadFromTheStoredWallClock_PrintsNoOffset_InBothPassesOfTheRepeatedHour_WhileARealInstantStillDoes and AJobStepReadFromTheStoredWallClock_PrintsNoOffset_InBothPassesOfTheRepeatedHour_WhileARealInstantStillDoes.
  • The full Lite.Tests and Darling.Tests suites pass in CI on this head.

CHANGELOG

SECTION: Fixed
ENTRY:

…s and chart CSV exports (#4766)

In a zone where a wall time happens twice (the repeated hour after a fall-back), the two rows that read 01:30 could not be told apart in a grid or in an exported chart file.

Grids: both FormatServerTime overloads and the alert row's TimeLocal now append a space and the instant's UTC offset when its wall time is ambiguous, "2026-11-01 01:30:00 -04:00" for 05:30Z and "2026-11-01 01:30:00 -05:00" for 06:30Z on a US Eastern clock. Every other time, and every time in UTC mode, keeps the bare text, and the format string still runs on the current culture. One internal function (FormatInstant) holds the rule for both.

Chart CSV: the time cell stays a plain date and time, so a spreadsheet reads the whole column one way. A fourth column, "UTC offset", is added to the header and to every row, from the new DisplayZone.UtcOffsetText ("-04:00", "-05:00", "+00:00" in UTC).

The Local-mode tests now word the expected text from the machine's own zone, so they hold on a desktop whose clock repeats an hour at those instants.
…an empty pattern, so the member scan reads it whole (#4766)
… grid times show their UTC offset in the repeated autumn hour (#4766)
… grid times show their UTC offset in the repeated autumn hour: keep the source pins and the member scan reading the new shapes (#4766)
…cer range show the UTC offset in the repeated autumn hour, as Lite does (#4766)

The viewer's alert row and Manage Servers "Last Collected" word their time through a new
ViewerTimeHelper.FormatForDisplay(DateTime, TimeZoneInfo, string), the twin of Lite's FormatInstant, which
FormatForDisplay(DateTime, string) now calls with the current zone, so there is one rule.

Lite's collection health rows and FinOps application connections, and the viewer's FinOps inventory and
application connections, read their own server's clock instead of this machine's or the active tab's.
The viewer's notification route Modified column follows the display mode, and the viewer slicer's range
caption is worded exactly as Lite's is.
…ne, so the wrapper census reads each body whole (#4766)
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 30, 2026 07:09
@erikdarlingdata
erikdarlingdata merged commit 9b76f44 into dev Sep 30, 2026
21 of 22 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4766-repeated-hour-offset branch September 30, 2026 07:13
erikdarlingdata added a commit that referenced this pull request Sep 30, 2026
…rling viewer (#4766) (#4858)

A grid column that shows a time as text sorted by that text when its header was clicked, so "10/1/2026" came before "9/30/2026". #4855 fixed the columns that show a UTC offset, and the other time columns still sorted as text. Six Postgres time columns in the Darling viewer also showed their raw UTC value in every display mode.

- Every time column in both products' grids now sorts by a time. The change covers 16 columns in Lite and 47 in the Darling viewer. A column sorts by the row's UTC instant where the row has one, and otherwise by the row's stored server wall clock. Inside the repeated autumn hour, a column sorted by a stored wall clock can still sort out of order. The stored time does not say which pass it was in.
- The viewer's eight System Events grids sort by each event's UTC time, as Lite's already did.
- "Never" in Mute Rules Expires and Job History Last Success sorts first in ascending order.
- The six viewer Postgres time columns now follow the display mode like the tab's other times, and sort by their UTC instant. They are Lock Stats Last Seen, Wait Sampling Last Seen, Replication Connected Since, Column Stats Captured, and Index Bloat Measured and Estimated.
- Tests scan every grid column in both products' XAML and fail when a time column sorts by its text.

Follow-up to #4766.
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