C#TimeSpan秒和毫秒不同步

时间:2019-02-25 17:19:01

标签: c# datetime timespan

我正在研究包仿真软件的更新,我们的团队维护该包并使用它来测试我们正在研究的其他应用程序。这些模拟数据包保留的两个字段是unix时间(秒)和纳秒。这是自1970年1月1日以来经过的时间,以秒为单位,以纳秒为单位。我们正在为此特定应用程序使用c#,并使用TimeSpan对象来获取经过的时间。

TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

packet.SetTimeSeconds(Convert.ToUInt64(ts.Totalseconds));

packet.SetTimeNanoseconds(Convert.ToUInt64(1e6 * ts.Milliseconds));

当数据包在其目的地被捕获并显示内容时,似乎秒值比纳秒值要低很多。纳秒值将滴答至阈值,翻转,然后再次开始滴答,但秒值需要几个数据包才能翻转。我检查了是否在代码中的其他位置设置了秒值后是否以某种方式更新了它,但是没有。在上面的代码段之后设置剩余的数据包值,然后使用套接字将数据包发送到目的地。

我知道,发生一滴答声的时间是16毫秒,更新可能会落在两个数据包之间。这可能会导致它们暂时不同步。在那种情况下,由于滴答频率,我希望它们最多不匹配一个数据包。在我的情况下,这种不匹配会发生3或4个数据包,然后每100毫秒产生一次数据时,秒值就会更新。

重要的是要注意,由于工作环境的原因,我在法律上不能将完整的源代码放在网上,因此请不要提出。另外,我检查了数据包中的时间设置后的所有逻辑,以确保没有任何内容被覆盖。我的主要问题是,这仅仅是DateTime.UtcNow的问题还是TimeSpan可能会获得准确的经过时间。这不是一个成败的问题,因为它只是一个模拟器,但是在我们的测试数据包中具有准确的模拟时间会很好。

编辑:

有关其他信息,即使我不使用数据包结构并将值发送到接收器,也会遇到此问题。我编写了以下循环来测试TimeSpan / DateTime值,而不会妨碍其他逻辑。

for (int i = 0; i < 150; i++)
{

     TimeSpan temp = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

     Console.WriteLine("The seconds elapsed is: " + Convert.ToUInt64(temp.TotalSeconds) + " The nanoseconds portion: " + Convert.ToUInt64(1e6 * temp.Milliseconds);

}

请记住,跟随循环将打印出具有相同值的多个实例,因为处理器可以在时钟滴答之间迭代几次循环(每个滴答需要16毫秒)。如果运行循环,您将看到纳秒值按预期增加,直到999000000,然后在到达新的秒数时翻转以重新开始。当您看到该翻转时,请检查秒数值,您会注意到该值尚未更新。我可以打印输出,但是我的具有互联网连接的机器没有IDE。

解决方案:

正如以下公认的答案所指出的,我正在利用convert.ToUInt64将TotalSeconds值(双精度)转换为Ulong。这样做是为了满足我们内部构建的库功能。此强制转换将秒值四舍五入,使该值看起来不准确。我最终使用Math.Floor(double)函数删除了强制转换前几秒钟的分形部分,并使所有内容保持同步。我的更新代码如下。

TimeSpan ts =(DateTime.UtcNow-新的DateTime(1970,1,1,0,0,0,DateTimeKind.Utc);

packet.SetTimeSeconds(Convert.ToUInt64(Math.Floor(ts.Totalseconds)));

packet.SetTimeNanoseconds(Convert.ToUInt64(1e6 * ts.Milliseconds));

1 个答案:

答案 0 :(得分:2)

我没有发现任何不一致之处。以下是我从您的代码中获得的TimeSpan对象。

TimeSpan.TicksPerMillisecond    : 10,000
TimeSpan.TicksPerDay            : 864,000,000,000

Ticks               : 15511159080437769         1551115908043.7769  milliseconds

Days                : 17952                     1551052800000       milliseconds
Hours               : 17                             61200000       milliseconds
Minutes             : 31                              1860000       milliseconds
Seconds             : 48                                48000       milliseconds
Milliseconds        : 43                                   43       milliseconds
Sum                 :                           1551115908043       milliseconds (NOTE: we don't have microsecond or nanosecond value bearing properties)

TotalDays           : 17952.730417173341        15511159080437769.0 / TimeSpan.TicksPerDay
TotalHours          : 430865.53001216019        15511159080437769.0 / TimeSpan.TicksPerHour
TotalMinutes        : 25851931.800729614        15511159080437769.0 / TimeSpan.TicksPerMinute
TotalSeconds        : 1551115908.0437768        15511159080437769.0 / TimeSpan.TicksPerSecond
TotalMilliseconds   : 1551115908043.7769        15511159080437769.0 / TimeSpan.TicksPerMillisecond

如您所见,所有值都是根据TimeSpan.Ticks级别计算的。您可以计算

更新:

使用以下代码获取纳秒部分或纳秒总时间跨度的正确值

public static class TimeSpanExtension {
    const decimal TicksPerNanosecond = TimeSpan.TicksPerMillisecond / 1000000m;
    public static decimal GetTotalNanoSeconds(this TimeSpan ts) => ts.Ticks / TicksPerNanosecond;
    public static decimal GetMicroAndNanoSeconds(this TimeSpan ts) => ts.Ticks % TimeSpan.TicksPerMillisecond / TicksPerNanosecond;
}

更新2:

从问题的代码更新中,我注意到您正在使用Convert.ToUInt64来舍入秒数,这使您感到困惑。

Console.WriteLine("The seconds elapsed is: " + Convert.ToUInt64(temp.TotalSeconds) +
                    " The nanoseconds portion: " + Convert.ToUInt64(1e6 * temp.Milliseconds));

您应该使用下面的代码来代替上面的代码,以查看没有不一致之处。您可以看到TotalSecondsMilliseconds一起滚动,但是Convert.ToUInt64(temp.TotalSeconds)不会滚动

// C# 6.0 string interpolation syntax
Console.WriteLine($"{temp.TotalSeconds}, {Convert.ToUInt64(temp.TotalSeconds)}, {temp.Milliseconds,000}");

// similar statement without string interpolation
Console.WriteLine(temp.TotalSeconds + ", " + Convert.ToUInt64(temp.TotalSeconds) + ", " + temp.Milliseconds);