Skip to content

Conversation

ojusms
Copy link

@ojusms ojusms commented Sep 22, 2025

[SHDR] Ensure UTC-safe timestamps (fixes #113)

Summary

SHDR timestamps were offset from local time (e.g., +5 hours) due to ambiguous DateTime kinds and conversions. This PR introduces a UTC-safe conversion method and updates SHDR code paths to use it, ensuring consistent UTC timestamps across environments.

Fixes: #113

Problem

  • SHDR streams showed timestamps shifted by the local timezone.
  • Root cause: conversions using DateTime with Kind=Unspecified or Local led to non-UTC epoch tick calculations.

Example from the issue:

case DataItemCategory.SAMPLE:
    _client.AddDataItem(
        new ShdrDataItem(
            message.Path,
            message.Data,
            DateTimeOffset.FromUnixTimeMilliseconds(message.Timestamp).LocalDateTime
        )
    );

Using LocalDateTime (or an Unspecified time) will shift the UTC reference.

Changes

  • Added UTC-safe conversion helpers in libraries/MTConnect.NET-Common/UnixTime.cs:

    • DateTime.ToUnixUtcTime(DateTimeKind unspecifiedAssume = DateTimeKind.Utc)
    • DateTime.ToUnixUTCTime(...) as an alias (requested name)
    • Behavior:
      • Local times are converted to UTC.
      • Unspecified times default to UTC (configurable via parameter), then converted to UTC.
  • Updated SHDR code to use UTC-safe conversions:

    • libraries/MTConnect.NET-SHDR/Shdr/ShdrDataItem.cs
      • ShdrDataItem(string, object, DateTime) now uses timestamp.ToUnixUtcTime().
      • FromString(...) now converts parsed DateTime with ToUnixUtcTime() to avoid local ambiguity.
    • libraries/MTConnect.NET-SHDR/Shdr/ShdrMessage.cs
      • ShdrMessage(..., DateTime) overloads now use timestamp.ToUnixUtcTime().
    • libraries/MTConnect.NET-SHDR/Adapters/ShdrAdapter.cs
      • All DateTime overloads for AddDataItem, SendDataItem, AddMessage, and SendMessage now use ToUnixUtcTime().

No changes were made to formatting of outbound SHDR strings, which continue to use ISO 8601 with a UTC basis via ToString("o") on UTC DateTime.

Why this works

  • SHDR protocol expects timestamps representing a universal (UTC) time.
  • By normalizing all inbound DateTime instances to UTC before computing Unix ticks, the resulting timestamps are consistent regardless of host machine timezone or DateTime.Kind.

Backwards compatibility

  • Existing APIs remain intact.
  • New methods are additive:
    • ToUnixUtcTime(...)
    • ToUnixUTCTime(...) (alias)
  • Default behavior assumes Unspecified is UTC. If a caller truly intends local time, they can call ToUnixUtcTime(DateTimeKind.Local).

Testing

  • Built solution in Debug:
    • 0 errors; warnings unrelated to these changes.
  • Manual verification:
    • Constructed ShdrDataItem/ShdrMessage with DateTime inputs in Local/Unspecified kinds and verified UTC normalization in the resulting SHDR line.
  • Suggested tests (can be added):
    • Unit tests for UnixTimeExtensions verifying conversions for Utc, Local, and Unspecified.
    • Integration tests for SHDR lines round-tripping timestamps without offset when system timezone varies.

Affected areas

  • SHDR generation paths:
    • ShdrDataItem, ShdrMessage, ShdrAdapter
  • Common time utilities:
    • UnixTime.cs (extensions, no breaking changes)

Risks

  • If any downstream code relied on treating Unspecified as Local implicitly, behavior will now be UTC by default. This is mitigated by allowing the caller to specify unspecifiedAssume explicitly if needed.

Checklist

Example usage

// Before (could shift by local timezone)
var ts = someLocalOrUnspecifiedDateTime.ToUnixTime();

// After (UTC normalized, consistent everywhere)
var tsUtc = someLocalOrUnspecifiedDateTime.ToUnixUTCTime();

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.

[SHDR Adapter] Local date/time
1 participant