在将 UNIX 时间戳转换为 .Net DateTime 并返回时保持 date/time 值一致
Keeping date/time values consistent when converting UNIX timestamps to .Net DateTime and back
我需要将 unix TimeStamp 转换为 .Net DateTIme 值,反之亦然。
这没问题,但我需要将转换后的值存储为 Int64(这意味着我丢失了小数位)。
丢失小数位意味着我可以获得原始值和转换值之间的差异。
如果可以的话,我会更改实现并将值存储为 Double 而不是 Int64……但遗憾的是我不能(规范等)。
编辑:
我需要将该值存储为 Int64,表示自 1970 年以来的秒数(unix 时间戳)
我想我已经找到解决这个问题的方法,但我不确定它是否 100% 有效。 (它在我的测试中 100% 有效)。在转换为 int 接缝之前将 Double 值四舍五入以解决问题。
这是否解决了问题?
这是我的代码:
[Test]
public void ConvertTest()
{
const Int64 orignalUnixValue = 252000596321;
var referenceTimeZero = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
//Test 1
var convertedDateTime = referenceTimeZero.AddSeconds(orignalUnixValue);
var convertedUnixValue = (convertedDateTime - referenceTimeZero).TotalSeconds;
var convertedDateTime2 = referenceTimeZero.AddSeconds(convertedUnixValue);
//This works
convertedDateTime2.Should()
.Be( convertedDateTime );
//Test 2
convertedDateTime = referenceTimeZero.AddSeconds(orignalUnixValue);
var convertedUnixValueCasted = (Int64)(convertedDateTime - referenceTimeZero).TotalSeconds;
convertedDateTime2 = referenceTimeZero.AddSeconds(convertedUnixValueCasted);
//Difference of 1 second
convertedDateTime2.Should()
.Be(convertedDateTime);
//Test 3
convertedDateTime = referenceTimeZero.AddSeconds(orignalUnixValue);
convertedUnixValueCasted = (Int64)Math.Ceiling((convertedDateTime - referenceTimeZero).TotalSeconds);
convertedDateTime2 = referenceTimeZero.AddSeconds(convertedUnixValueCasted);
//Possible workaround - works for this example
convertedDateTime2.Should()
.Be(convertedDateTime);
}
第一个测试显示了转换值的正确方法,第二个测试显示了当前的实现,第三个包含我的解决方法。
编辑
如果您绝对必须像使用 UNIX 时间戳一样使用这些值,那么我建议使用 Math.Round
而不是 Math.Ceiling
。这样您就可以确保数字四舍五入到 最接近的 整数。
不过,正如 Jon 所建议的那样,使用 TimeSpan.Ticks
而不是 TimeSpan.TotalSeconds
更有意义,因为报价开始时是 Int64
。这样你就不会出现转换错误了。
说明
您看到的问题似乎是由 double
的存储方式以及将 double
转换为 long
时发生的情况引起的。
我稍微修改了你的代码:
const Int64 orignalUnixValue = 252000596321;
DateTime referenceTimeZero = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
DateTime convertedDateTime = referenceTimeZero.AddSeconds(orignalUnixValue);
double convertedUnixValue = (convertedDateTime - referenceTimeZero).TotalSeconds;
Console.WriteLine(convertedUnixValue); // 252000596321
Console.WriteLine((long)convertedUnixValue); // 252000596320
如您所见,double
变量 似乎 存储了正确的值。但是在转换为 long
.
时会出现某种转换错误
通过使用 Jon Skeet 的 DoubleConverter,可以看出变量的值实际上小于 252000596321
,这是转换错误的原因。
Console.WriteLine(DoubleConverter.ToExactString(convertedUnixValue));
// 252000596320.999969482421875
我需要将 unix TimeStamp 转换为 .Net DateTIme 值,反之亦然。 这没问题,但我需要将转换后的值存储为 Int64(这意味着我丢失了小数位)。
丢失小数位意味着我可以获得原始值和转换值之间的差异。 如果可以的话,我会更改实现并将值存储为 Double 而不是 Int64……但遗憾的是我不能(规范等)。
编辑: 我需要将该值存储为 Int64,表示自 1970 年以来的秒数(unix 时间戳)
我想我已经找到解决这个问题的方法,但我不确定它是否 100% 有效。 (它在我的测试中 100% 有效)。在转换为 int 接缝之前将 Double 值四舍五入以解决问题。
这是否解决了问题?
这是我的代码:
[Test]
public void ConvertTest()
{
const Int64 orignalUnixValue = 252000596321;
var referenceTimeZero = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
//Test 1
var convertedDateTime = referenceTimeZero.AddSeconds(orignalUnixValue);
var convertedUnixValue = (convertedDateTime - referenceTimeZero).TotalSeconds;
var convertedDateTime2 = referenceTimeZero.AddSeconds(convertedUnixValue);
//This works
convertedDateTime2.Should()
.Be( convertedDateTime );
//Test 2
convertedDateTime = referenceTimeZero.AddSeconds(orignalUnixValue);
var convertedUnixValueCasted = (Int64)(convertedDateTime - referenceTimeZero).TotalSeconds;
convertedDateTime2 = referenceTimeZero.AddSeconds(convertedUnixValueCasted);
//Difference of 1 second
convertedDateTime2.Should()
.Be(convertedDateTime);
//Test 3
convertedDateTime = referenceTimeZero.AddSeconds(orignalUnixValue);
convertedUnixValueCasted = (Int64)Math.Ceiling((convertedDateTime - referenceTimeZero).TotalSeconds);
convertedDateTime2 = referenceTimeZero.AddSeconds(convertedUnixValueCasted);
//Possible workaround - works for this example
convertedDateTime2.Should()
.Be(convertedDateTime);
}
第一个测试显示了转换值的正确方法,第二个测试显示了当前的实现,第三个包含我的解决方法。
编辑
如果您绝对必须像使用 UNIX 时间戳一样使用这些值,那么我建议使用 Math.Round
而不是 Math.Ceiling
。这样您就可以确保数字四舍五入到 最接近的 整数。
不过,正如 Jon 所建议的那样,使用 TimeSpan.Ticks
而不是 TimeSpan.TotalSeconds
更有意义,因为报价开始时是 Int64
。这样你就不会出现转换错误了。
说明
您看到的问题似乎是由 double
的存储方式以及将 double
转换为 long
时发生的情况引起的。
我稍微修改了你的代码:
const Int64 orignalUnixValue = 252000596321;
DateTime referenceTimeZero = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
DateTime convertedDateTime = referenceTimeZero.AddSeconds(orignalUnixValue);
double convertedUnixValue = (convertedDateTime - referenceTimeZero).TotalSeconds;
Console.WriteLine(convertedUnixValue); // 252000596321
Console.WriteLine((long)convertedUnixValue); // 252000596320
如您所见,double
变量 似乎 存储了正确的值。但是在转换为 long
.
通过使用 Jon Skeet 的 DoubleConverter,可以看出变量的值实际上小于 252000596321
,这是转换错误的原因。
Console.WriteLine(DoubleConverter.ToExactString(convertedUnixValue));
// 252000596320.999969482421875