简体   繁体   English

System.DateTime种类比特

[英]System.DateTime Kind Bits

Due to difficulties I experienced trying to call the DotNetOAuth CryptoKey constructor I started to investigate the .Net System.DateTime structure. 由于我遇到尝试调用DotNetOAuth CryptoKey构造函数的困难,我开始研究.Net System.DateTime结构。 According to what I've read, this object is actually represented by a 64 bit signed integer, with the "Ticks" encoded in the lower 62 bits and the Kind encoded in the upper 2 bits (IOW, it's a concatenation of the 2 bit Kind and 62 bit ticks). 根据我所读到的,这个对象实际上由一个64位有符号整数表示,其中“Ticks”编码在低62位,而Kind编码在高2位(IOW,它是2位的串联)种类和62位滴答)。

Now I wanted to actually "see" this so I constructed a small C# program that created three System.DateTime objects as so: 现在我想实际“看到”这个,所以我构建了一个小的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);

I then dumped the ticks property for each and, as expected, they were all equal. 然后我为每个倾倒了ticks属性,正如预期的那样,它们都是平等的。 Finally, I applied .ToBinary() 最后,我申请了.ToBinary()

    long bitUtc = dtUtc.ToBinary();
    long bitLocal = dtLocal.ToBinary();
    long bitU = dtU.ToBinary();

These longs were all different, again as expected. 这些多头都是不同的,再次如预期的那样。 HOWEVER, I then tried to "inspect" the upper two bits to see which state corresponded to what settings, and found that the upper two bits were set the same in all three. 然而,我接着试图“检查”上面两位以查看哪个状态对应于什么设置,并发现上面两位在所有三个中都设置相同。 I used the following routine to return the bit status: 我使用以下例程返回位状态:

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;
}

(I got this from another post on SO), and called it like this: (我从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);

Again, the first and second bits were set the same in all three (first was true, second false). 同样,第一和第二位在所有三个中都设置相同(第一个是真的,第二个是假的)。 I don't understand this, as I THOUGHT these would all be different, corresponding to the different SystemKind values. 我不明白这一点,因为我认为这些都会有所不同,对应于不同的SystemKind值。

Finally, I did some more reading and found (or at least it was said in one source) that MS doesn't serialize the Kind information in .ToBinary(). 最后,我做了一些阅读并发现(或者至少在一个来源中说过)MS没有在.ToBinary()中序列化Kind信息。 OK, but then why are the outputs of the .ToBinary() method all different? 好的,但是为什么.ToBinary()方法的输出都不同?

I would appreciate info from anyone who could point me in the direction of a resource that would help me understand where I've gone wrong. 我希望任何能够指出我能够帮助我了解我出错的资源方向的信息。

These longs were all different, again as expected. 这些多头都是不同的,再次如预期的那样。 HOWEVER, I then tried to "inspect" the upper two bits to see which state corresponded to what settings, and found that the upper two bits were set the same in all three. 然而,我接着试图“检查”上面两位以查看哪个状态对应于什么设置,并发现上面两位在所有三个中都设置相同。

I really don't think that's the case - not with the results of ToBinary . 我真的不认为是这种情况 - 不是ToBinary的结果。 Here's a short but complete program demonstrating the difference, using your source data, showing the results as hex (as if unsigned): 这是一个简短但完整的程序,使用源数据显示差异,将结果显示为十六进制(如无符号):

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"));
    }
}

Output: 输出:

48D131A200924700
88D131999ECDDF00
08D131A200924700

The top two bits are retrospectively 01, 10 and 00. The other bits change for the local case too, as per Marcin's post - but the top two bits really do indicate the kind. 前两位是回顾性的01,10和00.根据Marcin的帖子,其他位也改变了本地情况 - 但前两位确实表明了那种。

The IsBitSet method is broken because it's left-shifting an int literal rather than a long literal. IsBitSet方法被破坏,因为它左移一个int文字而不是一个long文字。 That means the shift will be mod 32, rather than mod 64 as intended. 这意味着移位将是mod 32,而不是预期的mod 64。 Try this instead: 试试这个:

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;
}

Finally, I did some more reading and found (or at least it was said in one source) that MS doesn't serialize the Kind information in .ToBinary(). 最后,我做了一些阅读并发现(或者至少在一个来源中说过)MS没有在.ToBinary()中序列化Kind信息。

It's easy to demonstrate that's not true: 很容易证明这不是真的:

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("===");
    }
}

ToBinary() works differently for different DateTimeKind . ToBinary()对不同的DateTimeKind工作方式不同。 You can see it on .NET source code : 你可以在.NET源代码上看到它:

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;
    }
}  

That's why you get different bits - local time is adjusted before transformed into bits, and so it does no longer match utc time. 这就是为什么你得到不同的位 - 在转换成位之前调整本地时间,因此它不再匹配utc时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM