System.DateTime Kind Bits



由于在尝试调用DotNetOAuth CryptoKey构造函数时遇到困难,我开始研究.Net System.DateTime结构。根据我所读到的,这个对象实际上是由一个64位有符号整数表示的,"Ticks"编码在较低的62位,Kind编码在较高的2位(IOW,它是2位Kind和62位Ticks的级联(。

现在我想真正"看到"这一点,所以我构建了一个小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信息。好吧,但为什么.ToBinay((方法的输出都不同呢?

我非常感谢任何能为我指明资源方向的人提供的信息,帮助我了解自己哪里出了问题。

这些龙都是不同的,再次正如预期的那样。然而,然后我试图"检查"上面的两个位,看看哪个状态对应于什么设置,发现上面的两位在所有三个设置中都是相同的。

我真的不认为是这样——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("===");
    }
}

ToBinary()对不同的DateTimeKind的工作方式不同。你可以在.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;
    }
}  

这就是为什么你会得到不同的比特——本地时间在转换成比特之前会进行调整,因此它不再匹配utc时间。

相关内容

  • 没有找到相关文章

最新更新