// 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)); } }