Which clock stamps a Perspective property write?
The clock that stamps a write belongs to the node that builds the qualified value (QV). A QV carries three fields: value, quality, and a UTC timestamp. Whichever node wraps a bare value into a QV writes its own clock into that timestamp.
Follow the packet. When a browser, the Designer, or Workstation writes to a session, view, or component property, the client builds the full QV locally and sends it to the gateway. The timestamp is the client's UTC clock at that moment. page.props.urlParams takes a different path. URL parameters arrive at the gateway as bare values, because they come from the URL during page load and navigation. The gateway wraps them into QVs, so they get the gateway's UTC clock. That still holds when you later write urlParams from a client. The property keeps its gateway-side handling, even though the design assumes it is read-only.
| Property scope | Written from | Where the QV is built | Timestamp clock |
|---|---|---|---|
page.props.urlParams |
URL / navigation | Gateway | Gateway UTC |
page.props.urlParams |
Browser / Designer client | Gateway | Gateway UTC |
session.* props |
Browser / Designer client | Client | Client UTC |
view.* props |
Browser / Designer client | Client | Client UTC |
| Component props | Browser / Designer client | Client | Client UTC |
Workstation clients are expected to follow the browser path. Run the check in the third section below to confirm this on your own installation.
If the clocks agree in UTC, the two paths produce timestamps that are effectively identical and nobody notices. The mismatch only becomes visible when the client clock has drifted from the gateway clock.
Check: confirm that the mismatch you see is on urlParams only. If view and component props written in the same client event also disagree with each other, you have a different problem.
Is the client clock actually off from the gateway?
Layer one first. For timestamps, that means the time source. A difference in time zone does not matter here, because QV timestamps are UTC. The only thing that matters is true skew from real time.
- Read the gateway host's UTC time and its time sync status from the OS. On Windows,
w32tm /query /statusshows the configured source and the last successful sync. - Read the same values on the Designer workstation or the browser client machine.
- Compare both machines against the same reference, not against each other by eye.
- Record the offset in seconds. This offset is the error you will see between
urlParamstimestamps and client-stamped prop timestamps.
Check: if the offset is close to zero, the timestamp difference between scopes should also be close to zero. If the difference is large while the clocks agree, go to the next section before you touch NTP.
How do you prove which node stamped a value?
Build a controlled write that sends the same value down both paths from a single client event.
- On a test view, add a button with an action that writes one value to
page.props.urlParams(use a test key) and to a custom property on the view. - Trigger the action from the Designer in preview mode, then again from a browser session.
- Read the QV timestamp of each property and log both. A gateway-side reference, such as a gateway-scoped script logging the gateway's current time, gives you a third timestamp to compare against.
- Compare the results:
- The
urlParamstimestamp should track the gateway reference. - The view prop timestamp should track the client clock.
- The
| Observation | Meaning | Next action |
|---|---|---|
| urlParams matches the gateway reference; view prop is offset by the measured skew | Normal stamping behavior with a skewed client clock | Fix time sync and decouple logic (next section) |
| Both props match each other and the gateway | Clocks in sync; no stamping issue | Look elsewhere (binding order, script timing) |
| Both props offset from the gateway by the same amount | Both were stamped on the client | Recheck which property the action actually writes |
| Offset changes between the Designer and a browser on the same machine | Different clients or hosts involved | Measure each host's clock separately |
Check: the measured timestamp delta between the two props should equal the clock offset from the previous section. Once it does, the mechanism is confirmed for your system.
How do you remove the dependency on which clock stamped the value?
There are two separate fixes. Apply both.
1. Synchronize the clocks. Point the gateway and every engineering and operator client to the same time source. Engineering laptops that run the Designer are the usual offenders, because they sit off the plant domain or sleep for days. After sync, the client-stamped and gateway-stamped timestamps converge.
2. Stop comparing QV timestamps across scopes. Sync reduces the error but does not remove it, because any client can drift again. Treat the QV timestamp as "when this node last wrapped the value," not as "when the event happened." Where the logic depends on event time, stamp it explicitly from one clock.
- Add a dedicated timestamp property (for example, a custom view or session property) that holds the event time.
- Populate it from one authority. For audit and cross-client ordering, use the gateway, for example by sending a message to a gateway-scoped handler that returns or writes gateway time.
- Drive change scripts, sorting, and "which is newer" comparisons from that property, not from
urlParamsor component QV timestamps. - Keep QV timestamps for diagnostics only, such as "when did this binding last update on this node."
Check: skew the client clock deliberately on a test machine. Logic that uses the explicit timestamp property should behave the same. Logic that still reads QV timestamps will shift by the size of the skew.
Which recurring pitfalls reintroduce the mismatch?
| Pitfall | Effect | Countermeasure |
|---|---|---|
Treating urlParams as a normal writable client prop |
Timestamps written with the gateway clock mix with client-stamped siblings | Use a view or session custom prop for client-driven state; leave urlParams for navigation input |
| Blaming the time zone | Time spent chasing offset settings | QV timestamps are UTC; measure true skew instead |
| Testing only in the Designer | The Designer host clock may differ from the runtime clients | Repeat the probe from a production browser |
| Sorting events from several clients by QV timestamp | Order breaks whenever any client drifts | Sort by a gateway-issued timestamp |
| Fixing NTP once and assuming it stays fixed | Drift returns on laptops and isolated HMIs | Monitor the offset periodically on each client host |
Check: search the project's scripts and bindings for any read of property timestamps. List each one and the scope it reads from.
How do you verify the whole path end to end?
- Confirm the gateway and all client hosts report a near-zero UTC offset against the common time source.
- Re-run the probe action from the Designer, a browser, and Workstation. The
urlParamstimestamp and the view prop timestamp should now agree within the residual sync error. - Trigger the real workflow that exposed the problem. Confirm that ordering and elapsed-time logic read from the explicit gateway-stamped property.
- Offset one test client's clock by a known amount and repeat step 3. The workflow result must not change. The only difference should be the raw QV timestamp on client-stamped props, and it should differ by exactly the offset you applied.
FAQ
Does Perspective stamp page.props.urlParams with the client clock when I write it from a browser?
No. urlParams values are wrapped into qualified values at the gateway, so they carry the gateway's UTC time even when a browser or the Designer performs the write.
Does a time zone difference between the Designer and the gateway cause the timestamp mismatch?
No. Qualified value timestamps are UTC. Only real clock skew between the hosts produces a visible difference, so measure each host against a common time source.
Can I rely on QV timestamps to order events from multiple clients?
Not safely. Client-built QVs use each client's own clock. Stamp events explicitly from one authority, such as a gateway-scoped handler, and sort on that property.
Can I keep writing to urlParams for client state?
You can, but it mixes gateway-stamped and client-stamped values in the same view. Move client-driven state to a view or session custom property and keep urlParams for navigation input.
How do I confirm the fix actually worked?
Deliberately offset a test client's clock and re-run the workflow. The logic result should not change, and only the raw client-stamped QV timestamps should shift by the applied offset.