C# TimeSpan 秒和毫秒不同步
C# TimeSpan seconds and milliseconds are out of sync
我正在更新我们的团队维护的数据包模拟软件,并使用它来测试我们正在处理的其他应用程序。这些模拟数据包包含的两个字段是 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));
当数据包在其目的地被捕获并显示内容时,秒值似乎滞后于纳秒值相当大的量。纳秒值将上升到阈值、滚动并再次开始上升,但秒值需要几个数据包才能滚动。我已经检查过在代码中的其他地方设置后秒值是否以某种方式更新,但事实并非如此。剩余的数据包值在上面的代码片段之后设置,然后使用套接字将数据包发送到目的地。
我知道一个 tick 发生的时间是 16 毫秒,更新可能在两个数据包之间进行。这可能会导致它们暂时不同步。在那种情况下,由于滴答率,我预计它们最多不会匹配一个数据包。在我的情况下,在每 100 毫秒生成数据时更新秒值之前,这种不匹配发生在 3 或 4 个数据包上。
重要的是要注意,由于工作环境,我在法律上不能将完整的源代码放在网上,所以不要问。在数据包中设置时间后,我还检查了所有逻辑,以确保没有任何内容被覆盖。我的主要问题是这是否只是 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,然后在达到新的秒数时翻转以重新开始。当您看到翻转时,请检查秒值,您会注意到尚未更新。我会 post 打印输出,但我的联网机器没有 IDE。
解决方案:
正如下面已接受的答案所指出的,我正在使用 convert.ToUInt64 将 TotalSeconds 值(双精度)转换为 Ulong。这样做是为了满足我们内部构建的库功能。此强制转换使秒值看起来不准确。我最终使用 Math.Floor(double) 函数删除了转换前几秒的分形部分,一切都保持同步。下面列出了我更新的代码。
TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
packet.SetTimeSeconds(Convert.ToUInt64(Math.Floor(ts.Totalseconds)));
packet.SetTimeNanoseconds(Convert.ToUInt64(1e6 * ts.Milliseconds));
我没有看到任何不一致之处。下面是我从你的代码中得到的一个 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));
你应该使用下面的代码来代替上面的代码,看看有没有不一致。您可以看到 TotalSeconds
与 Milliseconds
一起滚动,但 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);
我正在更新我们的团队维护的数据包模拟软件,并使用它来测试我们正在处理的其他应用程序。这些模拟数据包包含的两个字段是 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));
当数据包在其目的地被捕获并显示内容时,秒值似乎滞后于纳秒值相当大的量。纳秒值将上升到阈值、滚动并再次开始上升,但秒值需要几个数据包才能滚动。我已经检查过在代码中的其他地方设置后秒值是否以某种方式更新,但事实并非如此。剩余的数据包值在上面的代码片段之后设置,然后使用套接字将数据包发送到目的地。
我知道一个 tick 发生的时间是 16 毫秒,更新可能在两个数据包之间进行。这可能会导致它们暂时不同步。在那种情况下,由于滴答率,我预计它们最多不会匹配一个数据包。在我的情况下,在每 100 毫秒生成数据时更新秒值之前,这种不匹配发生在 3 或 4 个数据包上。
重要的是要注意,由于工作环境,我在法律上不能将完整的源代码放在网上,所以不要问。在数据包中设置时间后,我还检查了所有逻辑,以确保没有任何内容被覆盖。我的主要问题是这是否只是 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,然后在达到新的秒数时翻转以重新开始。当您看到翻转时,请检查秒值,您会注意到尚未更新。我会 post 打印输出,但我的联网机器没有 IDE。
解决方案:
正如下面已接受的答案所指出的,我正在使用 convert.ToUInt64 将 TotalSeconds 值(双精度)转换为 Ulong。这样做是为了满足我们内部构建的库功能。此强制转换使秒值看起来不准确。我最终使用 Math.Floor(double) 函数删除了转换前几秒的分形部分,一切都保持同步。下面列出了我更新的代码。
TimeSpan ts = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
packet.SetTimeSeconds(Convert.ToUInt64(Math.Floor(ts.Totalseconds)));
packet.SetTimeNanoseconds(Convert.ToUInt64(1e6 * ts.Milliseconds));
我没有看到任何不一致之处。下面是我从你的代码中得到的一个 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));
你应该使用下面的代码来代替上面的代码,看看有没有不一致。您可以看到 TotalSeconds
与 Milliseconds
一起滚动,但 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);