由于在尝试调用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时间。