// Adapted from http://msmvps.com/blogs/jon_skeet/archive/2012/05/02/more-fun-with-datetime.aspx
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Explicit, Size = 8)]
struct DateTimeUnion
{
[FieldOffset(0)]
public DateTime datetime;
[FieldOffset(0)]
public ulong value;
}
public class DateTimesDeepDarkSecret
{
static string DumpDate(DateTime date)
{
DateTimeUnion foo = new DateTimeUnion();
foo.datetime = date;
ulong flags = foo.value >> 62;
ulong ticks = foo.value & 0x3FFFFFFFFFFFFFFF;
Debug.Assert((long)ticks == date.Ticks);
return String.Format("{{flags:{0:x}, ticks:{1:x}, datetime:{2}}}", flags, ticks, date);
}
public static void Main()
{
// Start with different UTC values around a DST transition
var original1 = new DateTime(2012, 10, 28, 0, 20, 0, DateTimeKind.Utc);
var original2 = new DateTime(2012, 10, 28, 1, 20, 0, DateTimeKind.Utc);
// Convert to local time
var local1 = original1.ToLocalTime();
var local2 = original2.ToLocalTime();
// Result is the same for both values. Information loss?
var expected = new DateTime(2012, 10, 28, 1, 20, 0, DateTimeKind.Local);
Console.WriteLine(local1 == expected); // True
Console.WriteLine(local2 == expected); // True
Console.WriteLine(local1 == local2); // True
// Convert back to UTC
var roundTrip1 = local1.ToUniversalTime();
var roundTrip2 = local2.ToUniversalTime();
// Values round-trip correctly! Information has been recovered...
Console.WriteLine(roundTrip1 == original1); // True
Console.WriteLine(roundTrip2 == original2); // True
Console.WriteLine(roundTrip1 == roundTrip2); // False
var local3 = local1.AddYears(-1).AddDays(2);
var local4 = local2.AddYears(-1).AddDays(2);
Console.WriteLine(local3.ToUniversalTime().Hour); // 0
Console.WriteLine(local4.ToUniversalTime().Hour); // 1
// Should be a no-op?
var local5 = DateTime.SpecifyKind(local1, local1.Kind);
Console.WriteLine(local5.ToUniversalTime().Hour); // 1
Console.WriteLine("original1: {0}", DumpDate(original1));
Console.WriteLine("original2: {0}", DumpDate(original2));
Console.WriteLine("local1: {0}", DumpDate(local1));
Console.WriteLine("local2: {0}", DumpDate(local2));
Console.WriteLine("expected: {0}", DumpDate(expected));
Console.WriteLine("roundTrip1: {0}", DumpDate(roundTrip1));
Console.WriteLine("roundTrip2: {0}", DumpDate(roundTrip2));
Console.WriteLine("local3: {0}", DumpDate(local3));
Console.WriteLine("local4: {0}", DumpDate(local4));
Console.WriteLine("local5: {0}", DumpDate(local5));
}
}