views:

48

answers:

1

Can anyone help explain the difference between DateTime.ToBinary() and DateTime.ToFileTime()? As far as I can tell they seem to always return the same value (when dealing with UTC times at least). The same applies to DateTime.FromBinary() and DateTime.FromFileTime().

I've tried using Reflector and I can see some differences, I just don't understand the relevance of the magic numbers:

public long ToBinary()
{
    if (this.Kind != DateTimeKind.Local)
    {
        return (long) this.dateData;
    }
    TimeSpan utcOffset = TimeZoneInfo.Local.GetUtcOffset(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
    long num2 = this.Ticks - utcOffset.Ticks;
    if (num2 < 0L)
    {
        num2 = 0x4000000000000000L + num2;
    }
    return (num2 | -9223372036854775808L);
}

public long ToFileTime()
{
    return this.ToUniversalTime().ToFileTimeUtc();
}

public long ToFileTimeUtc()
{
    long num = ((this.InternalKind & 9223372036854775808L) != 0L) ? this.ToUniversalTime().InternalTicks : this.InternalTicks;
    num -= 0x701ce1722770000L;
    if (num < 0L)
    {
        throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_FileTimeInvalid"));
    }
    return num;
}

public static DateTime FromFileTime(long fileTime)
{
    return FromFileTimeUtc(fileTime).ToLocalTime();
}

public static DateTime FromFileTimeUtc(long fileTime)
{
    if ((fileTime < 0L) || (fileTime > 0x24c85a5ed1c03fffL))
    {
        throw new ArgumentOutOfRangeException("fileTime", Environment.GetResourceString("ArgumentOutOfRange_FileTimeInvalid"));
    }
    return new DateTime(fileTime + 0x701ce1722770000L, DateTimeKind.Utc);
}

public static DateTime FromBinary(long dateData)
{
    long num2;
    if ((dateData & -9223372036854775808L) == 0L)
    {
        return FromBinaryRaw(dateData);
    }
    long ticks = dateData & 0x3fffffffffffffffL;
    if (ticks > 0x3fffff36d5964000L)
    {
        ticks -= 0x4000000000000000L;
    }
    bool isAmbiguousLocalDst = false;
    if (ticks < 0L)
    {
        num2 = TimeZoneInfo.Local.GetUtcOffset(MinValue, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks;
    }
    else if (ticks > 0x2bca2875f4373fffL)
    {
        num2 = TimeZoneInfo.Local.GetUtcOffset(MaxValue, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks;
    }
    else
    {
        DateTime time = new DateTime(ticks, DateTimeKind.Utc);
        bool isDaylightSavings = false;
        num2 = TimeZoneInfo.GetUtcOffsetFromUtc(time, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks;
    }
    ticks += num2;
    if (ticks < 0L)
    {
        ticks += 0xc92a69c000L;
    }
    if ((ticks < 0L) || (ticks > 0x2bca2875f4373fffL))
    {
        throw new ArgumentException(Environment.GetResourceString("Argument_DateTimeBadBinaryData"), "dateData");
    }
    return new DateTime(ticks, DateTimeKind.Local, isAmbiguousLocalDst);
}
A: 

ToBinary() and ToFileTimeUtc() do not return the same value. ToBinary provide a round-trip value, an Int64 that can preserve the properties of a DateTime. It uses the same time base, January 1st of the year 0. The value is always UTC. Bit 62 is set for the extreme corner case where a local time near the 1/1/00 would be negative when converted to UTC (paying attention to detail here :)). Bit 63 is set when the Kind is UTC. Convert the magic number to hex to see this.

ToFileTimeUtc() uses the same time base as Windows' FILETIME, January 1st of the year 1601. The magic number is the number of ticks for 12am, 1/1/1601.

Hans Passant