在NTP和C#DateTime之间转换
我使用以下代码在NTP和C#DateTime之间进行转换。我认为前躯是正确的,但后躯是错误的 请参阅以下代码以将8个字节转换为DatTime: 将NTP转换为日期时间 以下输入产生输出:在NTP和C#DateTime之间转换,c#,ntp,C#,Ntp,我使用以下代码在NTP和C#DateTime之间进行转换。我认为前躯是正确的,但后躯是错误的 请参阅以下代码以将8个字节转换为DatTime: 将NTP转换为日期时间 以下输入产生输出: bytes = { 131, 170, 126, 128, 46, 197, 205, 234 } var ms = GetMilliSeconds(bytes ); var ntp = ConvertToNtp(ms) //GetMilliSeconds output millise
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
请注意,从毫秒到小数部分的转换是错误的。为什么?
更新:
正如乔纳森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];
}
}
public static void add毫秒(ref byte[]ntpTime,ulong millis)
{
ulong intpart=0,fractpart=0;
对于(变量i=0;i<4;i++)
intpart=256*intpart+ntpTime[i];
对于(变量i=4;i=4)
ntpTime[i]=新分形零件[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);
}
是0.7毫秒把事情搞砸了
由于NTP时间戳包含32位小数秒(),因此转换为整数毫秒将导致精度损失
对最新情况的答复:
将毫秒添加到NTP时间戳并不像添加整数部分和小数部分那么简单。考虑将小数1.75和2.75相加。0.75+0.75=1.5,你需要把这个带到整数部分。此外,NTP时间戳中的分数部分不是base-10,因此不能只添加毫秒。一些转换是必要的,使用类似于ms/1000=ntpfrac/0x100000000
的比例
这是完全未经测试的,但我认为您应该将intpart+=
和fracpart+=
中的addmillizes
行替换为更像这样:
intpart += millis / 1000;
ulong fractsum = fractpart + (millis % 1000) / 1000 * 0x100000000L);
intpart += fractsum / 0x100000000L;
fractpart = fractsum % 0x100000000L;
日期时间标记为NTP并返回
static long ntpEpoch = (new DateTime(1900, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).Ticks;
static public long Ntp2Ticks(UInt64 a)
{
var b = (decimal)a * 1e7m / (1UL << 32);
return (long)b + ntpEpoch;
}
static public UInt64 Ticks2Ntp(long a)
{
decimal b = a - ntpEpoch;
b = (decimal)b / 1e7m * (1UL << 32);
return (UInt64)b;
}
static long ntpEpoch=(新的DateTime(1900,1,1,0,0,0,0,DateTimeKind.Utc)).Ticks;
静态公共长Ntp2Ticks(UInt64 a)
{
var b=(十进制)a*1e7m/(1UL对Cameron解决方案的建议:
使用
确保您的本地时间计算结果与其他时间相同,但没有除法
return (ulong)(elapsedTime.Ticks * 1e-7 * 4294967296ul)
或
这很有道理,谢谢。现在,与其来回转换,不如直接用NTP进行操作。我更新了我的问题以反映这一点。我更新了下面的答案。祝你好运!朱利叶斯,试试你提供的DateTime.UtcNow.Ticks的第一个表达式,告诉我会发生什么。修复它,我会重新启动投票失败。卡梅隆请解释一下……你会使用Utc。现在,减去所需的历元以获得elapsedTime……从那里得到的答案是一样的。你想看完整的方法吗?朱利叶斯,我的错误,我没有减去历元。请编辑你的答案,这样我就可以把投票结果还给你。朱利叶斯,我相信有代码中仍然存在一个错误,您选择在1e-7中使用浮点表示51位尾数,而不是64位精度,这意味着您正在丢失信息和数字的尾端。参见示例:Julius,您可以使用此小提琴查看需要多少位来存储(现在是历元)delta:,55位,double无法精确存储。另一个提琴显示double/decimal之间的计算是如何不同的。请记住,2036年后,答案中包含的历元将溢出,因此RFC中已给出另一个历元,并且应包含在此答案中,以及检测使用哪个历元的逻辑。如果您在回购协议中计算NTP时间的代码是错误的,请查看此提琴以了解原因:
intpart += millis / 1000;
ulong fractsum = fractpart + (millis % 1000) / 1000 * 0x100000000L);
intpart += fractsum / 0x100000000L;
fractpart = fractsum % 0x100000000L;
static long ntpEpoch = (new DateTime(1900, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).Ticks;
static public long Ntp2Ticks(UInt64 a)
{
var b = (decimal)a * 1e7m / (1UL << 32);
return (long)b + ntpEpoch;
}
static public UInt64 Ticks2Ntp(long a)
{
decimal b = a - ntpEpoch;
b = (decimal)b / 1e7m * (1UL << 32);
return (UInt64)b;
}
ntpEpoch = (new DateTime(1900, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).Ticks;
return (ulong)(elapsedTime.Ticks * 1e-7 * 4294967296ul)
return (ulong)(((long)(elapsedTime.Ticks * 0.0000001) << 32) + (elapsedTime.TotalMilliseconds % 1000 * 4294967296 * 0.001));
//TicksPerPicosecond = 0.0000001m
//4294967296 = uint.MaxValue + 1
//0.001 == PicosecondsPerNanosecond
public static System.DateTime UtcEpoch2036 = new System.DateTime(2036, 2, 7, 6, 28, 16, System.DateTimeKind.Utc);
public static System.DateTime UtcEpoch1900 = new System.DateTime(1900, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
public static ulong DateTimeToNptTimestamp(ref System.DateTime value/*, bool randomize = false*/)
{
System.DateTime baseDate = value >= UtcEpoch2036 ? UtcEpoch2036 : UtcEpoch1900;
System.TimeSpan elapsedTime = value > baseDate ? value.ToUniversalTime() - baseDate.ToUniversalTime() : baseDate.ToUniversalTime() - value.ToUniversalTime();
//Media.Common.Extensions.TimeSpan.TimeSpanExtensions.MicrosecondsPerMillisecond = 1000
//TicksPerPicosecond = 0.0000001m = 1e-7
//4294967296 = uint.MaxValue + 1
//0.001 == PicosecondsPerNanosecond = 1e-3
//429496.7296 Picoseconds = 4.294967296e-7 Seconds
//4.294967296e-7 * 1000 Milliseconds per second = 0.0004294967296 * 1e+9 (PicosecondsPerMilisecond) = 429.4967296
//0.4294967296 nanoseconds * 100 nanoseconds = 1 tick = 42.94967296 * 10000 ticks per millisecond = 429496.7296 / 1000 = 429.49672960000004
unchecked
{
//return (ulong)((long)(elapsedTime.Ticks * 0.0000001m) << 32 | (long)((decimal)elapsedTime.TotalMilliseconds % 1000 * 4294967296m * 0.001m));
//return (ulong)(((long)(elapsedTime.Ticks * 0.0000001m) << 32) + (elapsedTime.TotalMilliseconds % 1000 * 4294967296ul * 0.001));
//return (ulong)(elapsedTime.Ticks * 1e-7 * 4294967296ul); //ie-7 * 4294967296ul = 429.4967296 has random diff which complies better? (In order to minimize bias and help make timestamps unpredictable to an intruder, the non - significant bits should be set to an unbiased random bit string.)
//return (ulong)(elapsedTime.Ticks * 429.4967296m);//decimal precision is better but we still lose precision because of the magnitude? 0.001 msec dif ((ulong)(elapsedTime.Ticks * 429.4967296000000000429m))
//429.49672960000004m has reliable 003 msec diff
//Has 0 diff but causes fraction to be different from examples...
//return (ulong)((elapsedTime.Ticks + 1) * 429.4967296m);
//Also adding + 429ul;
return (ulong)(elapsedTime.Ticks * 429.496729600000000000429m);
//var ticks = (ulong)(elapsedTime.Ticks * 429.496729600000000000429m); //Has 0 diff on .137 measures otherwise 0.001 msec or 1 tick, keeps the examples the same.
//if(randomize) ticks ^= (ulong)(Utility.Random.Next() & byte.MaxValue);
//return ticks;
}
public static System.DateTime NptTimestampToDateTime(ref uint seconds, ref uint fractions, System.DateTime? epoch = null)
{
//Convert to ticks
//ulong ticks = (ulong)((seconds * System.TimeSpan.TicksPerSecond) + ((fractions * System.TimeSpan.TicksPerSecond) / 0x100000000L)); //uint.MaxValue + 1
unchecked
{
//Convert to ticks,
//'UtcEpoch1900.AddTicks(seconds * System.TimeSpan.TicksPerSecond + ((long)(fractions * 1e+12))).Millisecond' threw an exception of type 'System.ArgumentOutOfRangeException'
//0.01 millisecond = 1e+7 picseconds = 10000 nanoseconds
//10000 nanoseconds = 10 micros = 10000000 pioseconds
//0.001 Centisecond = 10 Microsecond
//1 Tick = 0.1 Microsecond
//0.1 * 100 Nanos Per Tick = 100
//TenMicrosecondsPerPicosecond = 10000000 = TimeSpan.TicksPerSecond = 10000000
//System.TimeSpan.TicksPerSecond is fine here also...
long ticks = seconds * System.TimeSpan.TicksPerSecond + ((long)(fractions * Media.Common.Extensions.TimeSpan.TimeSpanExtensions.TenMicrosecondsPerPicosecond) >> Common.Binary.BitsPerInteger);
//Return the result of adding the ticks to the epoch
//If the epoch was given then use that value otherwise determine the epoch based on the highest bit.
return epoch.HasValue ? epoch.Value.AddTicks(ticks) :
(seconds & 0x80000000L) == 0 ?
UtcEpoch2036.AddTicks(ticks) :
UtcEpoch1900.AddTicks(ticks);
}
}