由于我遇到尝试调用DotNetOAuth CryptoKey构造函数的困难,我开始研究.Net System.DateTime结构。根据我读过的内容,这个对象实际上是由64位有符号整数表示的,其中" Ticks"以低62位编码并且编码在高2位中的种类(IOW,它是2位种类和62位刻度的串联)。
现在我想实际上"看"这样我就构建了一个小的C#程序,它创建了三个System.DateTime对象:
DateTime dtUtc = new System.DateTime(2014, 4, 29, 9, 10, 30, System.DateTimeKind.Utc);
DateTime dtLocal = new System.DateTime(2014, 4, 29, 9, 10, 30, System.DateTimeKind.Local);
DateTime dtU = new System.DateTime(2014, 4, 29, 9, 10, 30, System.DateTimeKind.Unspecified);
然后我为每个人倾倒了ticks属性,正如预期的那样,他们都是平等的。最后,我应用了.ToBinary()
long bitUtc = dtUtc.ToBinary();
long bitLocal = dtLocal.ToBinary();
long bitU = dtU.ToBinary();
这些多头都是不同的,再次如预期的那样。然而,我接着试图"检查"高两位用于查看哪个状态对应于什么设置,并发现上两位在所有三个中都设置相同。我使用以下例程返回位状态:
public static bool IsBitSet<T>(this T t, int pos) where T : struct, IConvertible
{
var value = t.ToInt64(CultureInfo.CurrentCulture);
return (value & (1 << pos)) != 0;
}
(我从另一篇关于SO的文章中得到了这个),并称之为:
Boolean firstUtc = Class1.IsBitSet<long>(bitUtc, 63);
Boolean secondUtc = Class1.IsBitSet<long>(bitUtc, 62);
Boolean firstLocal = Class1.IsBitSet<long>(bitLocal, 63);
Boolean secondLocal = Class1.IsBitSet<long>(bitLocal, 62);
Boolean firstU = Class1.IsBitSet<long>(bitU, 63);
Boolean secondU = Class1.IsBitSet<long>(bitU, 62);
同样,第一和第二位在所有三个中都设置相同(第一个为真,第二个为假)。我不明白这一点,因为我认为这些都会有所不同,对应于不同的SystemKind值。
最后,我做了一些阅读并发现(或者至少在一个来源中说过)MS没有在.ToBinary()中序列化Kind信息。好的,但是为什么.ToBinary()方法的输出都不同?
我希望任何能够指出我能够帮助我了解我出错的资源的人的信息。
答案 0 :(得分:4)
这些多头都是不同的,再次如预期的那样。然而,我接着试图“检查”上面两位以查看哪个状态对应于什么设置,并发现上面两位在所有三个中都设置相同。
我真的不认为是这种情况 - 而不是ToBinary
的结果。这是一个简短但完整的程序,使用源数据显示差异,将结果显示为十六进制(如无符号):
using System;
class Test
{
static void Main()
{
DateTime dtUtc = new System.DateTime(2014, 4, 29, 9, 10, 30, System.DateTimeKind.Utc);
DateTime dtLocal = new System.DateTime(2014, 4, 29, 9, 10, 30, System.DateTimeKind.Local);
DateTime dtU = new System.DateTime(2014, 4, 29, 9, 10, 30, System.DateTimeKind.Unspecified);
Console.WriteLine(dtUtc.ToBinary().ToString("X16"));
Console.WriteLine(dtLocal.ToBinary().ToString("X16"));
Console.WriteLine(dtU.ToBinary().ToString("X16"));
}
}
输出:
48D131A200924700
88D131999ECDDF00
08D131A200924700
前两位是回顾性的01,10和00.其他位也改变了本地情况,根据Marcin的帖子 - 但前两位确实表明了那种。
IsBitSet
方法已被破解,因为它左移int
字面值而不是long
字面值。这意味着移位将是mod 32,而不是预期的mod 64。试试这个:
public static bool IsBitSet<T>(this T t, int pos) where T : struct, IConvertible
{
var value = t.ToInt64(CultureInfo.CurrentCulture);
return (value & (1L << pos)) != 0;
}
最后,我做了一些阅读并发现(或者至少在一个来源中说过)MS没有在.ToBinary()中序列化Kind信息。
很容易证明这不是真的:
using System;
class Test
{
static void Main()
{
DateTime start = DateTime.UtcNow;
Show(DateTime.SpecifyKind(start, DateTimeKind.Utc));
Show(DateTime.SpecifyKind(start, DateTimeKind.Local));
Show(DateTime.SpecifyKind(start, DateTimeKind.Unspecified));
}
static void Show(DateTime dt)
{
Console.WriteLine(dt.Kind);
DateTime dt2 = DateTime.FromBinary(dt.ToBinary());
Console.WriteLine(dt2.Kind);
Console.WriteLine("===");
}
}
答案 1 :(得分:1)
ToBinary()
对不同的DateTimeKind
的工作方式不同。您可以在.NET source code上看到它:
public Int64 ToBinary() {
if (Kind == DateTimeKind.Local) {
// Local times need to be adjusted as you move from one time zone to another,
// just as they are when serializing in text. As such the format for local times
// changes to store the ticks of the UTC time, but with flags that look like a
// local date.
// To match serialization in text we need to be able to handle cases where
// the UTC value would be out of range. Unused parts of the ticks range are
// used for this, so that values just past max value are stored just past the
// end of the maximum range, and values just below minimum value are stored
// at the end of the ticks area, just below 2^62.
TimeSpan offset = TimeZoneInfo.GetLocalUtcOffset(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
Int64 ticks = Ticks;
Int64 storedTicks = ticks - offset.Ticks;
if (storedTicks < 0) {
storedTicks = TicksCeiling + storedTicks;
}
return storedTicks | (unchecked((Int64) LocalMask));
}
else {
return (Int64)dateData;
}
}
这就是为什么你得到不同的位 - 在转换成位之前调整本地时间,因此它不再匹配utc时间。