我使用以下代码在 NTP 和 C# DateTime 之间进行转换。我认为前向翻转是正确的,向后翻转是错误的。
看下面的代码将8个字节转换成一个DatTime:
将 NTP 转换为日期时间
public static ulong GetMilliSeconds(byte[] ntpTime)
{
ulong intpart = 0, fractpart = 0;
for (var i = 0; i <= 3; i++)
intpart = 256 * intpart + ntpTime[i];
for (var i = 4; i <= 7; i++)
fractpart = 256 * fractpart + ntpTime[i];
var milliseconds = intpart * 1000 + ((fractpart * 1000) / 0x100000000L);
Debug.WriteLine("intpart: " + intpart);
Debug.WriteLine("fractpart: " + fractpart);
Debug.WriteLine("milliseconds: " + milliseconds);
return milliseconds;
}
public static DateTime ConvertToDateTime(byte[] ntpTime)
{
var span = TimeSpan.FromMilliseconds(GetMilliSeconds(ntpTime));
var time = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);
time += span;
return time;
}
将 DateTime 转换为 NTP
public static byte[] ConvertToNtp(ulong milliseconds)
{
ulong intpart = 0, fractpart = 0;
var ntpData = new byte[8];
intpart = milliseconds / 1000;
fractpart = ((milliseconds % 1000) * 0x100000000L) / 1000;
Debug.WriteLine("intpart: " + intpart);
Debug.WriteLine("fractpart: " + fractpart);
Debug.WriteLine("milliseconds: " + milliseconds);
var temp = intpart;
for (var i = 3; i >= 0; i--)
{
ntpData[i] = (byte)(temp % 256);
temp = temp / 256;
}
temp = fractpart;
for (var i = 7; i >= 4; i--)
{
ntpData[i] = (byte)(temp % 256);
temp = temp / 256;
}
return ntpData;
}
以下输入产生输出:
bytes = { 131, 170, 126, 128,
46, 197, 205, 234 }
var ms = GetMilliSeconds(bytes );
var ntp = ConvertToNtp(ms)
//GetMilliSeconds output
milliseconds: 2208988800182
intpart: 2208988800
fractpart: 784715242
//ConvertToNtp output
milliseconds: 2208988800182
intpart: 2208988800
fractpart: 781684047
注意从毫秒到小数部分的转换是错误的。为什么?
更新:
正如 Jonathan S. 指出的那样 - 这是分数的损失。因此,我不想来回转换,而是想直接使用 NTP 时间戳进行操作。更具体地说,加上毫秒。我假设以下函数可以做到这一点,但我很难验证它。我对小数部分非常不确定。
public static void AddMilliSeconds(ref byte[] ntpTime, ulong millis)
{
ulong intpart = 0, fractpart = 0;
for (var i = 0; i < 4; i++)
intpart = 256 * intpart + ntpTime[i];
for (var i = 4; i <= 7; i++)
fractpart = 256 * fractpart + ntpTime[i];
intpart += millis / 1000;
fractpart += millis % 1000;
var newIntpart = BitConverter.GetBytes(SwapEndianness(intpart));
var newFractpart = BitConverter.GetBytes(SwapEndianness(fractpart));
for (var i = 0; i < 8; i++)
{
if (i < 4)
ntpTime[i] = newIntpart[i];
if (i >= 4)
ntpTime[i] = newFractpart[i - 4];
}
}
最佳答案
您在这里遇到的是从 NTP 时间戳到毫秒的转换精度丢失。当您从 NTP 转换为毫秒时,您将丢弃部分分数。然后,当您获取该值并尝试转换回来时,您会得到一个略有不同的值。如果将 ulong
值更改为 decimal
值,您可以更清楚地看到这一点,如本测试所示:
public static decimal GetMilliSeconds(byte[] ntpTime)
{
decimal intpart = 0, fractpart = 0;
for (var i = 0; i <= 3; i++)
intpart = 256 * intpart + ntpTime[i];
for (var i = 4; i <= 7; i++)
fractpart = 256 * fractpart + ntpTime[i];
var milliseconds = intpart * 1000 + ((fractpart * 1000) / 0x100000000L);
Console.WriteLine("milliseconds: " + milliseconds);
Console.WriteLine("intpart: " + intpart);
Console.WriteLine("fractpart: " + fractpart);
return milliseconds;
}
public static byte[] ConvertToNtp(decimal milliseconds)
{
decimal intpart = 0, fractpart = 0;
var ntpData = new byte[8];
intpart = milliseconds / 1000;
fractpart = ((milliseconds % 1000) * 0x100000000L) / 1000m;
Console.WriteLine("milliseconds: " + milliseconds);
Console.WriteLine("intpart: " + intpart);
Console.WriteLine("fractpart: " + fractpart);
var temp = intpart;
for (var i = 3; i >= 0; i--)
{
ntpData[i] = (byte)(temp % 256);
temp = temp / 256;
}
temp = fractpart;
for (var i = 7; i >= 4; i--)
{
ntpData[i] = (byte)(temp % 256);
temp = temp / 256;
}
return ntpData;
}
public static void Main(string[] args)
{
byte[] bytes = { 131, 170, 126, 128,
46, 197, 205, 234 };
var ms = GetMilliSeconds(bytes);
Console.WriteLine();
var ntp = ConvertToNtp(ms);
}
这会产生以下结果:
milliseconds: 2208988800182.7057548798620701
intpart: 2208988800
fractpart: 784715242
milliseconds: 2208988800182.7057548798620701
intpart: 2208988800.1827057548798620701
fractpart: 784715242.0000000000703594496
是这 ~0.7 毫秒把事情搞砸了。
由于 NTP 时间戳包括 32 位小数秒 ( "a theoretical resolution of 2^-32 seconds or 233 picoseconds" ),转换为整数毫秒将导致精度损失。
响应更新:
将毫秒添加到 NTP 时间戳并不像添加整数部分和小数部分那么简单。考虑将小数点 1.75 和 2.75 相加。 0.75 + 0.75 = 1.5,您需要将 1 带入整数部分。此外,NTP 时间戳中的小数部分不是以 10 为底,因此您不能只添加毫秒。需要进行一些转换,使用类似 ms/1000 = ntpfrac/0x100000000
的比例。
这完全未经测试,但我认为您想要替换 AddMilliSeconds
中的 intpart +=
和 fracpart +=
行> 更像这样:
intpart += millis / 1000;
ulong fractsum = fractpart + (millis % 1000) / 1000 * 0x100000000L);
intpart += fractsum / 0x100000000L;
fractpart = fractsum % 0x100000000L;
关于c# - 在 NTP 和 C# DateTime 之间转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16763300/