Ignition Perspective Power Chart Time Zone Offset Fix

Daniel Price10 min read
B&R AutomationHMI / SCADATroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Where can a UTC timestamp pick up an offset between MySQL and the Power Chart?

Follow the packet. A Power Chart pen request starts in the browser, travels to the Perspective session on the gateway, becomes a historian query to MySQL over JDBC, and comes back as a dataset that the browser renders against a time axis. Only some of those hops convert a timestamp to wall-clock time, and the offset you see on the trend was applied at one of them.

The setup behind this reference is an Ignition gateway on an AWS EC2 Linux instance running Perspective. Tag history goes to a separate MySQL server. The gateway is set to UTC, and direct reads of the database (outside Ignition) confirm UTC timestamps. Power Charts viewed from New Zealand showed history shifted to NZ local time. The goal was for every session to display UTC, matching the gateway.

Hop How time is carried Zone applied here Where to read it
MySQL storage (tag historian) Epoch milliseconds, integer column None. Epoch values are zone-free Direct SQL query with an explicit session zone
MySQL storage (DATETIME columns: transaction groups, report queries) Wall-clock value with no zone attached Whatever zone the writer used Direct SQL query plus knowledge of the writer
JDBC link, gateway to MySQL DATETIME converted to Java time objects Driver and JVM default zone Database connection settings, gateway zone
Gateway JVM Default zone, read once at startup Server-side formatting, report rendering Gateway web interface zone display (hover shows the zone ID)
Perspective session session.props.timeZoneId, session.props.locale Taken from the project time zone setting Label bound to the session props
Browser render Power Chart axis formatting; Date Time Input Session zone for components; browser OS zone for Date Time Input offsets timeAxis > tick > label > format

A shift appears only where something turns a zone-free instant into a wall-clock string, or turns a wall-clock value back into an instant using the wrong zone. The checks below find which side of the gateway does that: server or client.

Check 1: Which zone does the session itself report?

Reading: Put two labels on the view. Bind one to session.props.timeZoneId and the other to session.props.locale. Then edit the Power Chart timeAxis > tick > label > format property to include a zone token, so the axis prints the zone it is actually using. Look up the exact token syntax in the Power Chart format documentation for your Ignition version.

  • The session reports the browser's zone (for example, the NZ zone): The project setting is not reaching the session. Go to Check 4.
  • The session reports UTC, but the trend is still shifted: Rendering is working from the correct zone, so the offset was applied upstream. Go to Check 2.

In this installation the session props matched the project properties, yet history still arrived shifted. The browser zone setting was therefore not the cause.

Check 2: Does a server-rendered output show the same shift?

Reading: Run a report from the Reporting module over the same time window. A report runs its own SQL query and renders on the gateway, with no browser zone involved.

  • The report shows the same local-time shift: The conversion happens on the gateway, in the JVM default zone or the JDBC handling. Go to Check 3.
  • The report is correct and only the Power Chart is shifted: Look at client-side formatting: the axis format and any Date Time Input that feeds the chart's start and end range.

Here the report showed the same shift, which moved the fault to the gateway side.

Symptom Where the offset is applied Next action
Session zone equals the browser zone, and the trend is shifted Project or session time zone setting Check 4, then restart the session
Session zone is UTC, the trend is shifted, and the report is correct Component formatting or date inputs Inspect the axis format and Date Time Input bindings
Session zone is UTC, and both the trend and the report are shifted Gateway JVM zone or JDBC conversion Check 3
The trend is right, but the selected date range is off by the local offset Date Time Input using the browser zone Adjust the value with a script transform
The shift clears unexpectedly and a gap appears in history The zone in effect changed at a gateway restart Check 3 uptime, then the gap analysis

Check 3: Which zone is the gateway JVM actually running?

Reading: Hover over the time zone indicator in the gateway web interface. This gateway displayed Etc/UTC. Then compare that with the operating system. On the EC2 Linux host, timedatectl shows the OS zone and the NTP sync state.

The JVM reads its default time zone once, at startup. Ignition does not notice a change to the gateway OS time zone until the gateway restarts. If the OS zone was set or changed after the gateway service came up, the JVM keeps converting with the old zone. Because conversions use the zone captured at startup, the displayed zone and the zone used for conversion can disagree until the service restarts.

  • The OS zone changed after the last gateway start: Restart the gateway. Closing and reopening browser sessions will not help, because the stale zone lives in the gateway process.
  • The OS zone and the gateway zone agree, and the gateway started after the last zone change: The JVM is not the source. Go to Check 5 and verify the stored data.

To correlate, compare the host boot time (uptime, last reboot) and the EC2 instance event history with the time the symptom stopped. In this case nobody restarted the gateway on purpose, and closing the browser did not fix it. The problem then cleared without any deliberate action. The explanation that fits is an unplanned host restart on the AWS side, which restarted the Ignition service and reloaded the zone.

Check 4: Does forcing the project time zone to UTC change anything?

Reading: In the Perspective project properties, switch the project time zone from the Gateway time zone to an explicit UTC. Save the project, open a fresh session, and re-read session.props.timeZoneId.

This setting controls only the zone the session reports and that components use for formatting. It cannot undo a conversion already applied in the gateway's query or JDBC path. When the gateway already reports UTC, "Gateway" and "UTC" resolve to the same zone, so switching between them changes nothing. That matches what happened here: no change after switching to UTC. Later the project zone was toggled from Gateway to UTC and back, and the trend came right around the same time. The toggle was coincidental. The restart was the fix.

  • The session zone changes but the trend does not: The offset is on the gateway side. Go back to Check 3.
  • The session zone does not change: Many session settings take effect only on a new session. Close every tab for the project and start a new session before re-reading.

Set the project zone to explicit UTC anyway. It separates what the client displays from whatever the gateway OS zone happens to be.

Check 5: Are the stored rows really UTC?

Reading: Query MySQL directly, and control the MySQL session zone. renders in the MySQL session's zone, so a query without an explicit zone can create a shift that is not really there:


Tag historian rows store epoch milliseconds, which carry no zone. DATETIME columns written by transaction groups or scripts store a bare wall-clock value, and its meaning depends on the zone of whoever wrote it. Change a tag at a UTC time you have noted, then find that row.

  • The row shows the correct UTC time and the chart does not: The fault is in the read or display path (Checks 1 to 3).
  • The row itself is off by the offset: The shift happened at write time. For epoch columns, check the host clock and NTP sync. For DATETIME columns, check the zone the gateway JVM was running when the rows were written.

What does a gap in the history trend mean?

Once the trend came right, it showed a 12-hour gap. A gap whose length matches a UTC offset marks the point where the effective offset changed. Measure the gap exactly and compare it with the client location's UTC offset on that date. Standard time and daylight time give different values.

  1. Query the raw rows on both sides of the gap, using the explicit-zone query from Check 5.
  2. If the raw values are continuous across the gap while the process ran continuously, the gap is a display artifact of mixed conversion. It disappears after the gateway restart and a fresh session.
  3. If the raw values jump by the offset while the process ran continuously, the writer stamped rows with a shifted clock or zone before the change. Either correct the older rows with a one-time UPDATE that subtracts the measured offset over the affected range, or document the seam as a known data boundary.
  4. Match the seam against the host boot time from Check 3. A seam that lines up with a reboot confirms the zone-at-startup mechanism.

How do I apply and verify the fix?

Procedure (for the gateway-side branch):

  1. On the EC2 host, set the OS zone to UTC (timedatectl set-timezone UTC). Confirm with timedatectl that the clock is NTP-synchronized.
  2. Restart the Ignition gateway service in a planned window. The restart drops all sessions and restarts tag providers.
  3. In the gateway web interface, confirm the zone indicator shows Etc/UTC after the restart.
  4. In the Perspective project properties, set the project time zone explicitly to UTC instead of the Gateway time zone, and save.
  5. Close every open session for the project and start a fresh one.
  6. Keep the session.props.timeZoneId and session.props.locale readouts on a diagnostic view. Leave a zone token in the Power Chart timeAxis > tick > label > format so the axis states its zone.
  7. If Date Time Input components drive chart start and end times, run their values through an adjusted-value script transform. Follow the Time Zone Script Transform Example in Inductive Automation's "Time Zones in Perspective" help document (written for Ignition 8.1.3). A Date Time Input applies the browser's zone as the offset, whatever the session zone is.

Verification:

  1. A new session reads UTC in the session.props.timeZoneId label, and the axis label prints UTC.
  2. Write a known tag value at a noted UTC time. After the historian stores it, run the explicit-zone MySQL query. The row time, the Power Chart hover time, and the noted time must all agree.
  3. Run the report over the same window. The report time must equal the direct-query time.
  4. Open the same view from a client whose OS is set to a non-UTC zone. The chart must still show UTC, and date-range selection must land on the intended UTC window.
  5. After the next planned gateway restart, repeat step 2. Timestamps must still match, with no new seam in the trend.

FAQ

Does changing the Perspective project time zone to UTC fix a shifted Power Chart?

Only when the session was running in the browser's zone. If the gateway already reports UTC, the "Gateway" and "UTC" options resolve to the same zone and nothing changes. A gateway-side offset needs a gateway restart. Open a new session after any project zone change.

Does Ignition pick up a Linux OS time zone change without a restart?

No. The gateway JVM reads its default zone at startup, so it keeps using the old zone until the Ignition service restarts. Closing browser sessions does not clear it. Check timedatectl on the host against the zone shown in the gateway web interface, then restart the gateway.

Can I force every Perspective session to display UTC regardless of the client's location?

Yes. Set the project time zone to UTC so session.props.timeZoneId reports UTC in every session, then start fresh sessions. Date Time Input components still apply the browser's zone as the offset, so pass their values through a script transform to get UTC.

Can I show which time zone the Power Chart axis is using?

Yes. Add a zone token to the timeAxis > tick > label > format property, using the format syntax listed in the Power Chart documentation for your version. Pair it with a label bound to session.props.timeZoneId so you can compare what the axis uses with what the session reports.

Back to blog