比较 DateTime 和解析的 DateTimeOffset 刻度的差异

Difference in comparing DateTime and parsed DateTimeOffset ticks

我有两个应用程序。一个客户端应用程序和一个服务器应用程序。

客户端应用程序向服务器发送 HTTP 请求,其中请求正文包含 DateTime.

在我收到 DateTime 的服务器上,将其转换为 Ticks 并将其保存在 SQLite 数据库中。 (你可能会争论为什么 Ticks,但这就是这个应用程序的构建方式..)

到目前为止一切顺利。

不久之后(几分钟),服务器应用程序对数据库进行查询,并尝试查询过去 30 分钟内的所有记录。

在代码中我做了这样的事情来获得当前 DateTime 减去 30 分钟:

DateTimeOffset.Now.AddMinutes(-30).Ticks;

由于某种原因,这个数字大于新创建记录的时间戳。

我可能遗漏了什么,但我就是没看到...

这是一段完美模仿我的问题的代码。 (Link 到 post 底部的工作演示)

public static void Main()
{
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Client app
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    var clientTimestamp = DateTimeOffset.Now.DateTime;
    var jsonDateTime = clientTimestamp.ToString("yyyy-MM-ddThh:mm:ss.fffZ");


    // Then, the client app sends data as JSON to the server app...


    ////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Server app
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // Mimick as if we received a json string with a DateTime in it. Parse that back to a real DateTime object.
    // This is also the value that will be stored in the database
    var serverDateTime = DateTime.Parse(jsonDateTime);
    Console.WriteLine("Ticks serverDateTime:             " + serverDateTime.Ticks.ToString());


    // A minute or so later, a recurring job checks the database for newly added records in the range of: 30 minutes ago till now.

    // Create a new DateTime Now minus 30 minutes... Both Ticks values below are greater than the jsonDateTime ticks?

    // DateTimeOffset:
    // This value is larger, even with the minus -10 minutes?
    var nowDateTimeOffset = DateTimeOffset.Now.AddMinutes(-10).Ticks.ToString();
    Console.WriteLine("Ticks nowDateTime -10 minutes:    " + nowDateTimeOffset);

    // DateTime
    // This value is larger, even with the minus -10 minutes?
    var nowDateTime = DateTimeOffset.Now.AddMinutes(-10).Ticks.ToString();
    Console.WriteLine("Ticks nowDateTime -10 minutes:    " + nowDateTime);
}

这是一个演示:https://dotnetfiddle.net/58Un0p

在这种情况下,问题似乎出在日期时间的格式上:

"yyyy-MM-ddThh:mm:ss.fffZ"  // lower hh

然而,它应该是:

"yyyy-MM-ddTHH:mm:ss.fffZ"  // upper HH

使用 12 小时 (hh) 格式而不是 24 小时 (HH) 格式会导致 50% 的时间出现 12 小时差异!


正如 Hans 在评论中提到的,您应该确保使用 UTC 来比较 DateTime 值。 DateTime.Parse 默认情况下将 DateTime 类型设置为 DateTimeKind.Local。客户端发送的是 UTC 日期时间吗?

更新客户端时间戳,以便它发送带有 ToUniversalTime:

的 UTC 日期时间
var clientTimestamp = DateTimeOffset.Now.ToUniversalTime();

而不是 DateTime.Parse 考虑 DateTime.ParseExactAdjustToUniversal:

// convert the date to UTC before saving to database
var utcServerDate = DateTime.ParseExact(jsonDateTime, "yyyy-MM-ddTHH:mm:ss.fffZ", 
                         CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);

并且在比较 DateTimeOffset.Now 值时使用 UtcTicks:

var nowDateTimeOffset = DateTimeOffset.Now.AddMinutes(-30).UtcTicks;

我在尝试比较两个 DateTimeOffset 时遇到了同样的问题,它们是用 0(零)毫秒创建的,并且都使用 UTC。

在我的研究和长期调试此源代码中:

// local variable
DateTimeOffset datetime = DateTimeOffset.Parse ("2021-03-02T02:10:15.000Z");

// method
DateTimeOffset getNowTruncated () {
  DateTimeOffset now = DateTimeOffset.UtcNow.AddMilliseconds;
  now.AddMilliseconds (-now.Milliseconds);
}

// inside Main program
while (true) {
  if (getNowTruncated () == datetime) {
    break;
  } else {
    Console.WriteLine (getNowTruncated ());
  }
}

两个日期时间都是相同的年、月、日、时、分、秒、毫秒和unixtimestamp(使用ToUnixTimeMilliseconds()方法)。

那么,为什么永远不会触发“中断”?

当我比较两者的TICKS时,我发现了差异,如此接近解决它......

搜索 TICKS 时发现了 NANOSECOND 这个词,所以我猜这是问题所在,当从 DateTime.UtcNow 创建日期时间时,它是用纳秒创建的,并且只有在调用 Ticks 方法时才可见,为了避免它,我创建了下一个方法:

public static DateTimeOffset GetNowTruncate ()
{
    DateTimeOffset now = DateTimeOffset.UtcNow;
    return new DateTimeOffset (now.Year, now.Month, now.Day,
        now.Hour, now.Minute, now.Second, TimeSpan.Zero);
}

当你看到时,不存在任何设置纳秒的参数,因此它将为零。