2008-09-15 Andy Hume <andyhume32@yahoo.co.uk>
authorMiguel de Icaza <miguel@gnome.org>
Mon, 15 Sep 2008 22:09:52 +0000 (22:09 -0000)
committerMiguel de Icaza <miguel@gnome.org>
Mon, 15 Sep 2008 22:09:52 +0000 (22:09 -0000)
* DateTimeOffset.cs: DateTimeOffset currently has no
deserialization constructor, thus deserialization
fails ("SerializationException: The constructor to deserialize an
object of type System.DateTimeOffset was not found.")

Patch attached, implements GetObjectData and that constructor.
Also includes tests, which include round-tripping to/from MSFT.

svn path=/trunk/mcs/; revision=113098

mcs/class/corlib/System/ChangeLog
mcs/class/corlib/System/DateTimeOffset.cs
mcs/class/corlib/Test/System/DateTimeOffsetTest.cs

index c76357008e1b8e30a973e00dccf05d4cee5fa585..5aa20c34b7ab672e7ccee7664662257b461fd29b 100644 (file)
@@ -1,3 +1,13 @@
+2008-09-15  Andy Hume  <andyhume32@yahoo.co.uk>
+
+       * DateTimeOffset.cs: DateTimeOffset currently has no
+       deserialization constructor, thus deserialization
+       fails ("SerializationException: The constructor to deserialize an
+       object of type System.DateTimeOffset was not found.")
+
+       Patch attached, implements GetObjectData and that constructor.
+       Also includes tests, which include round-tripping to/from MSFT.
+
 2008-09-14  Zoltan Varga  <vargaz@gmail.com>
 
        * ConsoleDriver.cs: Remove obsolete GetTtySize icall.
index f335c8ee86d4e010222e85a7af03d71448bdb24d..a9fb2901b6b54793fb60c5193adc3385d1566ebb 100644 (file)
@@ -207,13 +207,34 @@ namespace System
                        return dt.GetHashCode () ^ utc_offset.GetHashCode ();
                }
 
+               [System.Security.Permissions.SecurityPermission (System.Security.Permissions.SecurityAction.LinkDemand, SerializationFormatter = true)]
                void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
                {
                        if (info == null)
                                throw new ArgumentNullException ("info");
-
-                       info.AddValue ("datetime", dt);
-                       info.AddValue ("offset", utc_offset);
+                       // An example SOAP serialization on MSFT is the following, so field 
+                       // names "DateTime" and "OffsetMinutes":
+                       //    <SOAP-ENV:Envelope ...>
+                       //    <SOAP-ENV:Body>
+                       //    <a1:DateTimeOffset id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/ns/System">
+                       //    <DateTime xsi:type="xsd:dateTime">2007-01-02T12:30:50.0000000+00:00</DateTime>
+                       //    <OffsetMinutes>0</OffsetMinutes>
+                       //    </a1:DateTimeOffset>
+                       //    </SOAP-ENV:Body>
+                       //    </SOAP-ENV:Envelope>
+                       DateTime dt0 = new DateTime (dt.Ticks).Subtract (utc_offset);
+                       info.AddValue ("DateTime", dt0);
+                       // MSFT BinaryFormatter output contains primitive code 6, i.e. Int16.
+                       info.AddValue ("OffsetMinutes", (Int16)utc_offset.TotalMinutes);
+               }
+
+               [System.Security.Permissions.SecurityPermission (System.Security.Permissions.SecurityAction.LinkDemand, SerializationFormatter = true)]
+               private DateTimeOffset(SerializationInfo info, StreamingContext context)
+               {
+                       DateTime dt0 = (DateTime)info.GetValue ("DateTime", typeof(DateTime));
+                       Int16 totalMinutes = info.GetInt16 ("OffsetMinutes");
+                       utc_offset = TimeSpan.FromMinutes(totalMinutes);
+                       dt = dt0.Add(utc_offset);
                }
 
                public static bool operator > (DateTimeOffset left, DateTimeOffset right)
index faa83e403131bcae946f36c600bcaad32464523f..7b96bf04ace3beec78fbfc6b19d55114335c4aed 100644 (file)
@@ -388,6 +388,148 @@ namespace MonoTests.System {
                        Assert.IsFalse (offset1.Equals (offset2), "1!=2");
                        Assert.IsFalse (offset2.Equals (offset1), "2!=1");
                }
+
+               [Test]
+               public void Serialization()
+               {
+                       global::System.IO.MemoryStream dst = new global::System.IO.MemoryStream ();
+                       global::System.Runtime.Serialization.IFormatter fmtr
+                               = new global::System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
+                       for (int i = 0; i < SerializationCases.Length; ++i) {
+                               dst.SetLength (0);
+                               DateTimeOffset cur = SerializationCases[i].Input;
+                               fmtr.Serialize (dst, cur);
+                               String result = BitConverter.ToString (dst.GetBuffer (), 0, (int)dst.Length);
+                               Assert.AreEqual (SerializationCases[i].ResultBinaryString, result, "resultToString #" + i);
+                       }//for
+               }
+
+               [Test]
+               public void Deserialization()
+               {
+                       global::System.Runtime.Serialization.IFormatter fmtr
+                               = new global::System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
+                       global::System.IO.MemoryStream src;
+                       for (int i = 0; i < SerializationCases.Length; ++i) {
+                               src = new global::System.IO.MemoryStream (
+                                       BitConverter_ByteArray_FromString (SerializationCases[i].ResultBinaryString));
+                               DateTimeOffset result = (DateTimeOffset)fmtr.Deserialize (src);
+#if false // DUMP_TO_CONSOLE
+                               Console.WriteLine ("#{0} input.o/s : {1}", i, SerializationCases[i].Input.Offset);
+                               Console.WriteLine ("#{0} result.o/s: {1}", i, result.Offset);
+                               Console.WriteLine ("#{0} input.dt : {1}={1:R}={1:u}", i, SerializationCases[i].Input.DateTime);
+                               Console.WriteLine ("#{0} result.dt: {1}={1:R}={1:u}", i, result.DateTime);
+                               Console.WriteLine ("#{0} input.dtK: {1}", i, SerializationCases[i].Input.DateTime.Kind);
+                               Console.WriteLine ("#{0} input : {1}={1:R}={1:u}", i, SerializationCases[i].Input);
+                               Console.WriteLine ("#{0} result: {1}={1:R}={1:u}", i, result);
+#endif
+                               Assert.AreEqual (SerializationCases[i].Input.Offset, result.Offset, ".Offset #" + i);
+                               Assert.AreEqual (SerializationCases[i].Input.DateTime, result.DateTime, ".DateTime #" + i);
+                               Assert.AreEqual (SerializationCases[i].Input, result, "equals #" + i);
+                               // DateTimeOffset always stores as Kind==Unspecified
+                               Assert.AreEqual (DateTimeKind.Unspecified, SerializationCases[i].Input.DateTime.Kind, ".DateTime.Kind==unspec #" + i);
+                               Assert.AreEqual (SerializationCases[i].Input.DateTime.Kind, result.DateTime.Kind, ".DateTime.Kind #" + i);
+                       }//for
+               }
+
+               public class TestRow
+               {
+                       public DateTimeOffset Input;
+                       public String ResultBinaryString;
+
+                       public TestRow(DateTimeOffset input, String resultBinaryString)
+                       {
+                               this.Input = input;
+                               this.ResultBinaryString = resultBinaryString;
+                       }
+               }
+               readonly TestRow[] SerializationCases = {
+                       // Multiple tests of Unspecified and different timezone offsets; one 
+                       // for UTC; and one for Local which is disabled as it suits only a machine
+                       // in GMT or similar.
+#if ___MACHINE_TIMEZONE_HAS_ZERO_OFFSET
+                       // The "new DateTimeOffset(new DateTime(...)))" expression results in a 
+                       // DTO that has an offset set to the machine timezone offset.  So, as 
+                       // with the test case at the bottom we can't test this around the world.
+                       new TestRow (new DateTimeOffset (new DateTime (2007, 01, 02, 12, 30, 50)),
+                               "00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00-00-"
+                               + "00-04-01-00-00-00-15-53-79-73-74-65-6D-2E-44-61-"
+                               + "74-65-54-69-6D-65-4F-66-66-73-65-74-02-00-00-00-"
+                               + "08-44-61-74-65-54-69-6D-65-0D-4F-66-66-73-65-74-"
+                               + "4D-69-6E-75-74-65-73-00-00-0D-07-00-39-75-F8-80-"
+                               + "FC-C8-08-00-00-0B"),
+#endif
+                       new TestRow (new DateTimeOffset (new DateTime (2007, 01, 02, 12, 30, 50), new TimeSpan (0, 0, 0)),
+                               "00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00-00-"
+                               + "00-04-01-00-00-00-15-53-79-73-74-65-6D-2E-44-61-"
+                               + "74-65-54-69-6D-65-4F-66-66-73-65-74-02-00-00-00-"
+                               + "08-44-61-74-65-54-69-6D-65-0D-4F-66-66-73-65-74-"
+                               + "4D-69-6E-75-74-65-73-00-00-0D-07-00-39-75-F8-80-"
+                               + "FC-C8-08-00-00-0B"),
+                       new TestRow (new DateTimeOffset (new DateTime (2007, 01, 02, 12, 30, 50), new TimeSpan (1, 0, 0)),
+                               "00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00-00-"
+                               + "00-04-01-00-00-00-15-53-79-73-74-65-6D-2E-44-61-"
+                               + "74-65-54-69-6D-65-4F-66-66-73-65-74-02-00-00-00-"
+                               + "08-44-61-74-65-54-69-6D-65-0D-4F-66-66-73-65-74-"
+                               + "4D-69-6E-75-74-65-73-00-00-0D-07-00-D1-B0-96-78-"
+                               + "FC-C8-08-3C-00-0B"),
+                       new TestRow (new DateTimeOffset (new DateTime (2007, 01, 02, 12, 30, 50), new TimeSpan (0, 30, 0)),
+                               "00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00-00-"
+                               + "00-04-01-00-00-00-15-53-79-73-74-65-6D-2E-44-61-"
+                               + "74-65-54-69-6D-65-4F-66-66-73-65-74-02-00-00-00-"
+                               + "08-44-61-74-65-54-69-6D-65-0D-4F-66-66-73-65-74-"
+                               + "4D-69-6E-75-74-65-73-00-00-0D-07-00-05-93-C7-7C-"
+                               + "FC-C8-08-1E-00-0B"),
+                       new TestRow (new DateTimeOffset (new DateTime (2007, 01, 02, 12, 30, 50), new TimeSpan (-5, 0, 0)),
+                               "00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00-00-"
+                               + "00-04-01-00-00-00-15-53-79-73-74-65-6D-2E-44-61-"
+                               + "74-65-54-69-6D-65-4F-66-66-73-65-74-02-00-00-00-"
+                               + "08-44-61-74-65-54-69-6D-65-0D-4F-66-66-73-65-74-"
+                               + "4D-69-6E-75-74-65-73-00-00-0D-07-00-41-4B-E1-AA-"
+                               + "FC-C8-08-D4-FE-0B"),
+                       new TestRow (new DateTimeOffset (new DateTime (2007, 01, 02, 12, 30, 50), new TimeSpan (-10, 30, 0)),
+                               "00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00-00-"
+                               + "00-04-01-00-00-00-15-53-79-73-74-65-6D-2E-44-61-"
+                               + "74-65-54-69-6D-65-4F-66-66-73-65-74-02-00-00-00-"
+                               + "08-44-61-74-65-54-69-6D-65-0D-4F-66-66-73-65-74-"
+                               + "4D-69-6E-75-74-65-73-00-00-0D-07-00-15-3F-99-D0-"
+                               + "FC-C8-08-C6-FD-0B"),
+                       // invalid case: .ctor ArgEx "non whole minutes"
+                       //      new DateTimeOffset(new DateTime(2007, 01, 02, 12, 30, 50), new TimeSpan(1, 2, 3));
+                       //----
+                       new TestRow (new DateTimeOffset (new DateTime (2007, 01, 02, 12, 30, 50, DateTimeKind.Utc)),
+                               "00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00-00-"
+                               + "00-04-01-00-00-00-15-53-79-73-74-65-6D-2E-44-61-"
+                               + "74-65-54-69-6D-65-4F-66-66-73-65-74-02-00-00-00-"
+                               + "08-44-61-74-65-54-69-6D-65-0D-4F-66-66-73-65-74-"
+                               + "4D-69-6E-75-74-65-73-00-00-0D-07-00-39-75-F8-80-"
+                               + "FC-C8-08-00-00-0B"),
+                       //----
+#if ___MACHINE_TIMEZONE_HAS_ZERO_OFFSET
+                       // Local needs offset to be the same as the machine timezone,
+                       // not easy to cope with the tests being run around the world!
+                       // This one works in UK, etc.
+                       new TestRow (new DateTimeOffset (new DateTime (2007, 01, 02, 12, 30, 50, DateTimeKind.Local), new TimeSpan (0,0,0)),
+                               "00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00-00-"
+                               + "00-04-01-00-00-00-15-53-79-73-74-65-6D-2E-44-61-"
+                               + "74-65-54-69-6D-65-4F-66-66-73-65-74-02-00-00-00-"
+                               + "08-44-61-74-65-54-69-6D-65-0D-4F-66-66-73-65-74-"
+                               + "4D-69-6E-75-74-65-73-00-00-0D-07-00-39-75-F8-80-"
+                               + "FC-C8-08-00-00-0B"),
+#endif
+               };
+
+               static byte[] BitConverter_ByteArray_FromString(String hexString)
+               {
+                       String[] numbers = hexString.Split('-');
+                       byte[] result = new byte[numbers.Length];
+                       for (int i = 0; i < numbers.Length; ++i) {
+                               byte x = Byte.Parse (numbers[i], NumberStyles.HexNumber, global::System.Globalization.CultureInfo.InvariantCulture);
+                               result[i] = x;
+                       }
+                       return result;
+               }
+
        }
 }
 #endif