From 73c430a80a1eb60fa942f304b76335584616f4e6 Mon Sep 17 00:00:00 2001 From: Ojus M S Date: Mon, 22 Sep 2025 17:08:58 -0500 Subject: [PATCH] SHDR: ensure UTC-safe timestamps via ToUnixUTCTime to fix #113 --- libraries/MTConnect.NET-Common/UnixTime.cs | 33 +++++++++++++++++++ .../Adapters/ShdrAdapter.cs | 12 +++---- .../MTConnect.NET-SHDR/Shdr/ShdrDataItem.cs | 4 +-- .../MTConnect.NET-SHDR/Shdr/ShdrMessage.cs | 4 +-- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/libraries/MTConnect.NET-Common/UnixTime.cs b/libraries/MTConnect.NET-Common/UnixTime.cs index 4ff7de81..b2434173 100644 --- a/libraries/MTConnect.NET-Common/UnixTime.cs +++ b/libraries/MTConnect.NET-Common/UnixTime.cs @@ -35,6 +35,39 @@ public static long ToUnixTime(this DateTime d) } + /// + /// Convert a DateTime to Unix ticks (1/10,000 of a millisecond) ensuring the value is in UTC. + /// If the DateTime.Kind is Local, it will be converted to UTC. If Unspecified, the value will be + /// treated as UTC by default (for backwards compatibility) or as the specified kind, then converted to UTC. + /// + /// The DateTime to convert. + /// The kind to assume when DateTime.Kind is Unspecified. Defaults to Utc. + /// Unix ticks since epoch in UTC. + public static long ToUnixUtcTime(this DateTime d, DateTimeKind unspecifiedAssume = DateTimeKind.Utc) + { + var x = d; + if (x.Kind == DateTimeKind.Local) + { + x = x.ToUniversalTime(); + } + else if (x.Kind == DateTimeKind.Unspecified) + { + // Specify the assumed kind, then convert to UTC if necessary + x = DateTime.SpecifyKind(x, unspecifiedAssume); + if (x.Kind == DateTimeKind.Local) x = x.ToUniversalTime(); + } + + var duration = x - EpochTime; + return duration.Ticks; + } + + /// + /// Alias to to match requested API name. + /// + public static long ToUnixUTCTime(this DateTime d, DateTimeKind unspecifiedAssume = DateTimeKind.Utc) + => ToUnixUtcTime(d, unspecifiedAssume); + + public static DateTime ToDateTime(this long unixTicks) { return FromUnixTime(unixTicks); diff --git a/libraries/MTConnect.NET-SHDR/Adapters/ShdrAdapter.cs b/libraries/MTConnect.NET-SHDR/Adapters/ShdrAdapter.cs index da4e54a4..08c5ea70 100644 --- a/libraries/MTConnect.NET-SHDR/Adapters/ShdrAdapter.cs +++ b/libraries/MTConnect.NET-SHDR/Adapters/ShdrAdapter.cs @@ -698,7 +698,7 @@ public void AddDataItem(string dataItemKey, object value) public void AddDataItem(string dataItemKey, object value, DateTime timestamp) { - AddDataItem(dataItemKey, value, timestamp.ToUnixTime()); + AddDataItem(dataItemKey, value, timestamp.ToUnixUtcTime()); } public void AddDataItem(string dataItemKey, object value, long timestamp) @@ -735,7 +735,7 @@ public bool SendDataItem(string dataItemKey, object value) public bool SendDataItem(string dataItemKey, object value, DateTime timestamp) { - return SendDataItem(dataItemKey, value, timestamp.ToUnixTime()); + return SendDataItem(dataItemKey, value, timestamp.ToUnixUtcTime()); } public bool SendDataItem(string dataItemKey, object value, long timestamp) @@ -784,7 +784,7 @@ public void AddMessage(string messageId, string value) public void AddMessage(string messageId, string value, DateTime timestamp) { - AddMessage(messageId, value, timestamp.ToUnixTime()); + AddMessage(messageId, value, timestamp.ToUnixUtcTime()); } public void AddMessage(string messageId, string value, long timestamp) @@ -799,7 +799,7 @@ public void AddMessage(string messageId, string value, string nativeCode) public void AddMessage(string messageId, string value, string nativeCode, DateTime timestamp) { - AddMessage(messageId, value, nativeCode, timestamp.ToUnixTime()); + AddMessage(messageId, value, nativeCode, timestamp.ToUnixUtcTime()); } public void AddMessage(string messageId, string value, string nativeCode, long timestamp) @@ -831,7 +831,7 @@ public bool SendMessage(string dataItemId, string value) public bool SendMessage(string dataItemId, string value, DateTime timestamp) { - return SendMessage(dataItemId, value, timestamp.ToUnixTime()); + return SendMessage(dataItemId, value, timestamp.ToUnixUtcTime()); } public bool SendMessage(string dataItemId, string value, long timestamp) @@ -846,7 +846,7 @@ public bool SendMessage(string dataItemId, string value, string nativeCode) public bool SendMessage(string dataItemId, string value, string nativeCode, DateTime timestamp) { - return SendMessage(dataItemId, value, nativeCode, timestamp.ToUnixTime()); + return SendMessage(dataItemId, value, nativeCode, timestamp.ToUnixUtcTime()); } public bool SendMessage(string dataItemId, string value, string nativeCode, long timestamp) diff --git a/libraries/MTConnect.NET-SHDR/Shdr/ShdrDataItem.cs b/libraries/MTConnect.NET-SHDR/Shdr/ShdrDataItem.cs index 340a4ff9..ee902a91 100644 --- a/libraries/MTConnect.NET-SHDR/Shdr/ShdrDataItem.cs +++ b/libraries/MTConnect.NET-SHDR/Shdr/ShdrDataItem.cs @@ -66,7 +66,7 @@ public ShdrDataItem(string dataItemKey, object value, DateTime timestamp) { new ObservationValue(ValueKeys.Result, value != null ? value.ToString() : string.Empty) }; - Timestamp = timestamp.ToUnixTime(); + Timestamp = timestamp.ToUnixUtcTime(); } @@ -350,7 +350,7 @@ public static IEnumerable FromString(string input, bool uppercaseV if (timestamp.HasValue) { var y = ShdrLine.GetNextSegment(input); - return FromKeyValuePairs(y, timestamp.Value.ToUnixTime(), duration.HasValue ? duration.Value : 0, uppercaseValue); + return FromKeyValuePairs(y, timestamp.Value.ToUnixUtcTime(), duration.HasValue ? duration.Value : 0, uppercaseValue); } else { diff --git a/libraries/MTConnect.NET-SHDR/Shdr/ShdrMessage.cs b/libraries/MTConnect.NET-SHDR/Shdr/ShdrMessage.cs index 603c12d5..33a22b53 100644 --- a/libraries/MTConnect.NET-SHDR/Shdr/ShdrMessage.cs +++ b/libraries/MTConnect.NET-SHDR/Shdr/ShdrMessage.cs @@ -82,7 +82,7 @@ public ShdrMessage(string dataItemKey, string value, DateTime timestamp) { new ObservationValue(ValueKeys.Result, value != null ? value.ToString() : string.Empty) }; - Timestamp = timestamp.ToUnixTime(); + Timestamp = timestamp.ToUnixUtcTime(); } public ShdrMessage(string dataItemKey, string value, string nativeCode, DateTime timestamp) @@ -92,7 +92,7 @@ public ShdrMessage(string dataItemKey, string value, string nativeCode, DateTime values.Add(new ObservationValue(ValueKeys.Result, value != null ? value.ToString() : string.Empty)); if (!string.IsNullOrEmpty(nativeCode)) values.Add(new ObservationValue(ValueKeys.NativeCode, nativeCode)); Values = values; - Timestamp = timestamp.ToUnixTime(); + Timestamp = timestamp.ToUnixUtcTime(); } public ShdrMessage(IObservationInput observation)