System.DateTime种类比特

时间:2014-04-30 19:42:05

标签: c# .net datetime

由于我遇到尝试调用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()方法的输出都不同?

我希望任何能够指出我能够帮助我了解我出错的资源的人的信息。

2 个答案:

答案 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时间。