diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs index bf9cb5ab..4dec9564 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriver.cs @@ -1751,8 +1751,12 @@ public sealed class OpcUaClientDriver : IDriver, ITagDiscovery, IReadable, IWrit _ => value.ToString(), }; + // Missing-field sentinel is UTC-kinded so a downstream .ToUniversalTime() can't shift it. + private static readonly DateTime MissingTimeSentinel = + DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); + private static DateTime CoerceDateTime(object? value) - => value is DateTime dt ? dt : DateTime.MinValue; + => value is DateTime dt ? dt : MissingTimeSentinel; private static ushort CoerceSeverity(object? value) { diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientHistoryTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientHistoryTests.cs index 4207fa74..69eadae4 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientHistoryTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientHistoryTests.cs @@ -112,6 +112,7 @@ public sealed class OpcUaClientHistoryTests clause.AttributeId.ShouldBe(Attributes.Value); clause.BrowsePath.Count.ShouldBe(1); clause.BrowsePath[0].Name.ShouldBe(expected[i]); + clause.BrowsePath[0].NamespaceIndex.ShouldBe((ushort)0); // BaseEventType fields live in ns 0 } } @@ -165,6 +166,7 @@ public sealed class OpcUaClientHistoryTests mapped[0].EventId.ShouldBe(Convert.ToBase64String(new byte[] { 9 })); mapped[0].SourceName.ShouldBeNull(); mapped[0].EventTimeUtc.ShouldBe(DateTime.MinValue); + mapped[0].EventTimeUtc.Kind.ShouldBe(DateTimeKind.Utc); // sentinel is UTC-kinded, not Unspecified mapped[0].Severity.ShouldBe((ushort)0); } }