XINCGer/Unity3DTraining · error · ArgumentException

Conversion from DateTime to Timestamp requires the DateTime…

Error message

Conversion from DateTime to Timestamp requires the DateTime kind to be Utc

What it means

Timestamp.FromDateTime only accepts DateTime values whose Kind is DateTimeKind.Utc; anything Local or Unspecified is rejected with ArgumentException because the conversion to Unix-epoch seconds would silently misinterpret the wall-clock time. The library forces callers to convert explicitly rather than guessing a time zone.

Solutions

  1. Use DateTime.UtcNow or call dateTime.ToUniversalTime() before FromDateTime
  2. For Unspecified values you know are UTC, use DateTime.SpecifyKind(dt, DateTimeKind.Utc)
  3. For local wall-clock times with a known zone, convert with TimeZoneInfo.ConvertTimeToUtc(dt, zone) first
  4. Prefer DateTimeOffset and Timestamp.FromDateTimeOffset when offsets are available

Example fix

// before
var ts = Timestamp.FromDateTime(DateTime.Now); // throws: Kind is Local
// after
var ts2 = Timestamp.FromDateTime(DateTime.UtcNow);
// or for a parsed value known to be UTC:
var parsed = DateTime.Parse("2024-01-01T12:00:00Z").ToUniversalTime();
var ts3 = Timestamp.FromDateTime(parsed);
Defensive patterns

Strategy: try-catch

Validate before calling

static DateTime EnsureUtc(DateTime dt) => dt.Kind == DateTimeKind.Utc ? dt : dt.ToUniversalTime();

Type guard

static bool IsUtc(DateTime dt) => dt.Kind == DateTimeKind.Utc;

Try / catch

try { return Timestamp.FromDateTime(dt); } catch (ArgumentException ex) { log.Warn(ex, "Non-UTC DateTime Kind={Kind}", dt.Kind); return Timestamp.FromDateTime(dt.ToUniversalTime()); }

Prevention

When it happens

Trigger: Calling FromDateTime (directly or via FromDateTimeOffset with a non-UTC offset) with dateTime.Kind == Local (e.g. DateTime.Now) or Unspecified (e.g. parsed from a string without offset info).

Common situations: Passing DateTime.Now instead of DateTime.UtcNow; strings parsed with DateTime.Parse producing Unspecified kind; values read from databases configured with local time; legacy code that stores local wall-clock times.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12). Data as JSON: /api/errors/c4888faf4122ae6e. Report an issue: GitHub.

Appendix: source

Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/WellKnownTypes/TimestampPartial.cs:152

        /// <returns>This timestamp as a <c>DateTimeOffset</c>.</returns>
        /// <exception cref="InvalidOperationException">The timestamp contains invalid values; either it is
        /// incorrectly normalized or is outside the valid range.</exception>
        public DateTimeOffset ToDateTimeOffset()
        {
            return new DateTimeOffset(ToDateTime(), TimeSpan.Zero);
        }

        /// <summary>
        /// Converts the specified <see cref="DateTime"/> to a <see cref="Timestamp"/>.
        /// </summary>
        /// <param name="dateTime"></param>
        /// <exception cref="ArgumentException">The <c>Kind</c> of <paramref name="dateTime"/> is not <c>DateTimeKind.Utc</c>.</exception>
        /// <returns>The converted timestamp.</returns>
        public static Timestamp FromDateTime(DateTime dateTime)
        {
            if (dateTime.Kind != DateTimeKind.Utc)
            {
                throw new ArgumentException("Conversion from DateTime to Timestamp requires the DateTime kind to be Utc", "dateTime");
            }
            // Do the arithmetic using DateTime.Ticks, which is always non-negative, making things simpler.
            long secondsSinceBclEpoch = dateTime.Ticks / TimeSpan.TicksPerSecond;
            int nanoseconds = (int)  (dateTime.Ticks % TimeSpan.TicksPerSecond) * Duration.NanosecondsPerTick;
            return new Timestamp { Seconds = secondsSinceBclEpoch - BclSecondsAtUnixEpoch, Nanos = nanoseconds };
        }

        /// <summary>
        /// Converts the given <see cref="DateTimeOffset"/> to a <see cref="Timestamp"/>
        /// </summary>
        /// <remarks>The offset is taken into consideration when converting the value (so the same instant in time
        /// is represented) but is not a separate part of the resulting value. In other words, there is no
        /// roundtrip operation to retrieve the original <c>DateTimeOffset</c>.</remarks>
        /// <param name="dateTimeOffset">The date and time (with UTC offset) to convert to a timestamp.</param>
        /// <returns>The converted timestamp.</returns>
        public static Timestamp FromDateTimeOffset(DateTimeOffset dateTimeOffset)
        {
            // We don't need to worry about this having negative ticks: DateTimeOffset is constrained to handle

View on GitHub (pinned to 016f98412e)