[Mono-dev] Serialization TimeZoneInfo class

Andreas Auerswald andreas.auerswald at etit.tu-chemnitz.de
Mon Jun 17 07:50:26 UTC 2013


Hi,
I have implemented the serialization of the TimeZoneInfo class. It is 
compatible to the serialization in .Net so cross serialization shouldn't 
be a problem. I hope it will be included in the next releases of Mono.

The patch:

diff --git a/mcs/class/System.Core/System/TimeZoneInfo.AdjustmentRule.cs 
b/mcs/class/System.Core/System/TimeZoneInfo.AdjustmentRule.cs
index d3c16f4..4c413a8 100644
--- a/mcs/class/System.Core/System/TimeZoneInfo.AdjustmentRule.cs
+++ b/mcs/class/System.Core/System/TimeZoneInfo.AdjustmentRule.cs
@@ -74,6 +74,17 @@ namespace System
                  return new AdjustmentRule (dateStart, dateEnd, 
daylightDelta, daylightTransitionStart, daylightTransitionEnd);
              }

+            private AdjustmentRule (SerializationInfo info, 
StreamingContext context)
+            {
+                if (info == null)
+                    throw new ArgumentNullException ("info");
+                dateStart = (DateTime) info.GetValue ("DateStart", 
typeof (DateTime));
+                dateEnd = (DateTime) info.GetValue ("DateEnd", typeof 
(DateTime));
+                daylightDelta = (TimeSpan) info.GetValue 
("DaylightDelta", typeof (TimeSpan));
+                daylightTransitionStart = (TimeZoneInfo.TransitionTime) 
info.GetValue ("DaylightTransitionStart", typeof 
(TimeZoneInfo.TransitionTime));
+                daylightTransitionEnd = (TimeZoneInfo.TransitionTime) 
info.GetValue ("DaylightTransitionEnd", typeof 
(TimeZoneInfo.TransitionTime));
+            }
+
              private AdjustmentRule (
                  DateTime dateStart,
                  DateTime dateEnd,
@@ -130,7 +141,13 @@ namespace System
              public void GetObjectData (SerializationInfo info, 
StreamingContext context)
  #endif
              {
-                throw new NotImplementedException ();
+                if (info == null)
+                    throw new ArgumentNullException ("info");
+                info.AddValue ("DateStart", DateStart);
+                info.AddValue ("DateEnd", DateEnd);
+                info.AddValue ("DaylightDelta", DaylightDelta);
+                info.AddValue ("DaylightTransitionStart", 
DaylightTransitionStart);
+                info.AddValue ("DaylightTransitionEnd", 
DaylightTransitionEnd);
              }
  #if NET_4_0
              void IDeserializationCallback.OnDeserialization (object 
sender)
@@ -138,7 +155,38 @@ namespace System
              public void OnDeserialization (object sender)
  #endif
              {
-                throw new NotImplementedException ();
+                try {
+                    TimeZoneInfo.AdjustmentRule.Validate (dateStart, 
dateEnd, daylightDelta,
+ daylightTransitionStart, daylightTransitionEnd);
+                } catch (ArgumentException ex) {
+                    throw new SerializationException ("invalid 
serialization data", (Exception) ex);
+                }
+            }
+
+            private static void Validate (
+                DateTime dateStart,
+                DateTime dateEnd,
+                TimeSpan daylightDelta,
+                TransitionTime daylightTransitionStart,
+                TransitionTime daylightTransitionEnd)
+            {
+                if (dateStart.Kind != DateTimeKind.Unspecified || 
dateEnd.Kind != DateTimeKind.Unspecified)
+                    throw new ArgumentException ("the Kind property of 
dateStart or dateEnd parameter does not equal DateTimeKind.Unspecified");
+
+                if (daylightTransitionStart == daylightTransitionEnd)
+                    throw new ArgumentException 
("daylightTransitionStart parameter cannot equal daylightTransitionEnd 
parameter");
+
+                if (dateStart.Ticks % TimeSpan.TicksPerDay != 0 || 
dateEnd.Ticks % TimeSpan.TicksPerDay != 0)
+                    throw new ArgumentException ("dateStart or dateEnd 
parameter includes a time of day value");
+
+                if (dateEnd < dateStart)
+                    throw new ArgumentOutOfRangeException ("dateEnd is 
earlier than dateStart");
+
+                if (daylightDelta > new TimeSpan (14, 0, 0) || 
daylightDelta < new TimeSpan (-14, 0, 0))
+                    throw new ArgumentOutOfRangeException 
("daylightDelta is less than -14 or greater than 14 hours");
+
+                if (daylightDelta.Ticks % TimeSpan.TicksPerSecond != 0)
+                    throw new ArgumentOutOfRangeException 
("daylightDelta parameter does not represent a whole number of seconds");
              }
          }
      }
diff --git a/mcs/class/System.Core/System/TimeZoneInfo.TransitionTime.cs 
b/mcs/class/System.Core/System/TimeZoneInfo.TransitionTime.cs
index a914ed1..0a72ce2 100644
--- a/mcs/class/System.Core/System/TimeZoneInfo.TransitionTime.cs
+++ b/mcs/class/System.Core/System/TimeZoneInfo.TransitionTime.cs
@@ -108,6 +108,26 @@ namespace System
                  return new TransitionTime (timeOfDay, month, week, 
dayOfWeek);
              }

+            private TransitionTime (SerializationInfo info, 
StreamingContext context)
+            {
+                if (info == null)
+                    throw new ArgumentNullException ("info");
+                timeOfDay = (DateTime) info.GetValue ("TimeOfDay", 
typeof (DateTime));
+                month = (byte) info.GetValue ("Month", typeof (byte));
+                week = (byte) info.GetValue ("Week", typeof (byte));
+                day = (byte) info.GetValue ("Day", typeof (byte));
+                dayOfWeek = (DayOfWeek) info.GetValue ("DayOfWeek", 
typeof (DayOfWeek));
+                isFixedDateRule = (bool) info.GetValue 
("IsFixedDateRule", typeof (bool));
+
+                if (isFixedDateRule)
+                {
+                    week = -1;
+                    dayOfWeek = (DayOfWeek) (-1);
+                }
+                if (!isFixedDateRule)
+                    day = -1;
+            }
+
              private TransitionTime (
                  DateTime timeOfDay,
                  int month,
@@ -190,9 +210,25 @@ namespace System
              public void GetObjectData (SerializationInfo info, 
StreamingContext context)
  #endif
              {
-                throw new NotImplementedException ();
-            }
-
+                if (info == null)
+                    throw new ArgumentNullException ("info");
+                info.AddValue ("TimeOfDay", TimeOfDay);
+                info.AddValue ("Month", System.Convert.ToByte(Month));
+                if (week > -1)
+                    info.AddValue ("Week", System.Convert.ToByte(week));
+                else
+                    info.AddValue ("Week", (byte) 1);
+                if (day > -1)
+                    info.AddValue ("Day", System.Convert.ToByte(day));
+                else
+                    info.AddValue ("Day", (byte) 1);
+                if (dayOfWeek !=  ((System.DayOfWeek) (-1)))
+                    info.AddValue ("DayOfWeek", dayOfWeek);
+                else
+                    info.AddValue ("DayOfWeek", DayOfWeek.Sunday);
+                info.AddValue ("IsFixedDateRule", IsFixedDateRule);
+            }
+
              public override bool Equals (object obj)
              {
                  if (obj is TransitionTime)
@@ -216,7 +252,50 @@ namespace System
              public void OnDeserialization (object sender)
  #endif
              {
-                throw new NotImplementedException ();
+                try {
+                    TimeZoneInfo.TransitionTime.Validate (timeOfDay, 
month, week, day, dayOfWeek, isFixedDateRule);
+                } catch (ArgumentException ex) {
+                    throw new SerializationException ("invalid 
serialization data", (Exception) ex);
+                }
+            }
+
+            private static void Validate (DateTime timeOfDay, int 
month,int week, int day, DayOfWeek dayOfWeek, bool isFixedDateRule)
+            {
+                if (timeOfDay.Year != 1 || timeOfDay.Month != 1 || 
timeOfDay.Day != 1)
+                    throw new ArgumentException ("timeOfDay parameter 
has a non-default date component");
+
+                if (timeOfDay.Kind != DateTimeKind.Unspecified)
+                    throw new ArgumentException ("timeOfDay parameter 
Kind's property is not DateTimeKind.Unspecified");
+
+                if (timeOfDay.Ticks % TimeSpan.TicksPerMillisecond != 0)
+                    throw new ArgumentException ("timeOfDay parameter 
does not represent a whole number of milliseconds");
+
+                if (day < 1 || day > 31)
+                {
+                    if (!(!isFixedDateRule && day == -1))
+                        throw new ArgumentOutOfRangeException ("day 
parameter is less than 1 or greater than 31");
+                }
+
+                if (week < 1 || week > 5)
+                {
+                    if (!(isFixedDateRule && week == -1))
+                        throw new ArgumentOutOfRangeException ("week 
parameter is less than 1 or greater than 5");
+                }
+
+                if (month < 1 || month > 12)
+                    throw new ArgumentOutOfRangeException ("month 
parameter is less than 1 or greater than 12");
+
+                if (dayOfWeek != DayOfWeek.Sunday &&
+                        dayOfWeek != DayOfWeek.Monday &&
+                        dayOfWeek != DayOfWeek.Tuesday &&
+                        dayOfWeek != DayOfWeek.Wednesday &&
+                        dayOfWeek != DayOfWeek.Thursday &&
+                        dayOfWeek != DayOfWeek.Friday &&
+                        dayOfWeek != DayOfWeek.Saturday)
+                {
+                    if (!(isFixedDateRule && dayOfWeek == (DayOfWeek) 
(-1)))
+                        throw new ArgumentOutOfRangeException 
("dayOfWeek parameter is not a member od DayOfWeek enumeration");
+                }
              }
          }
      }
diff --git a/mcs/class/System.Core/System/TimeZoneInfo.cs 
b/mcs/class/System.Core/System/TimeZoneInfo.cs
index ee440a4..dd3c656 100644
--- a/mcs/class/System.Core/System/TimeZoneInfo.cs
+++ b/mcs/class/System.Core/System/TimeZoneInfo.cs
@@ -598,11 +531,19 @@ namespace System
          public void GetObjectData (SerializationInfo info, 
StreamingContext context)
  #endif
          {
-            throw new NotImplementedException ();
+            if (info == null)
+                throw new ArgumentNullException ("info");
+            info.AddValue ("Id", id);
+            info.AddValue ("DisplayName", displayName);
+            info.AddValue ("StandardName", standardDisplayName);
+            info.AddValue ("DaylightName", daylightDisplayName);
+            info.AddValue ("BaseUtcOffset", baseUtcOffset);
+            info.AddValue ("AdjustmentRules", adjustmentRules);
+            info.AddValue ("SupportsDaylightSavingTime", 
SupportsDaylightSavingTime);
          }

          //FIXME: change this to a generic Dictionary and allow caching 
for FindSystemTimeZoneById
@@ -786,9 +725,56 @@ namespace System
          public void OnDeserialization (object sender)
  #endif
          {
-            throw new NotImplementedException ();
+                try {
+                    TimeZoneInfo.Validate (id, baseUtcOffset, 
adjustmentRules);
+                } catch (ArgumentException ex) {
+                    throw new SerializationException ("invalid 
serialization data", (Exception) ex);
+                }
          }
-
+
+        private static void Validate (string id, TimeSpan 
baseUtcOffset, AdjustmentRule [] adjustmentRules)
+        {
+            if (id == null)
+                throw new ArgumentNullException ("id");
+
+            if (id == String.Empty)
+                throw new ArgumentException ("id parameter is an empty 
string");
+
+            if (baseUtcOffset.Ticks % TimeSpan.TicksPerMinute != 0)
+                throw new ArgumentException ("baseUtcOffset parameter 
does not represent a whole number of minutes");
+
+            if (baseUtcOffset > new TimeSpan (14, 0, 0) || 
baseUtcOffset < new TimeSpan (-14, 0, 0))
+                throw new ArgumentOutOfRangeException ("baseUtcOffset 
parameter is greater than 14 hours or less than -14 hours");
+
+#if STRICT
+            if (id.Length > 32)
+                throw new ArgumentException ("id parameter shouldn't be 
longer than 32 characters");
+#endif
+
+            if (adjustmentRules != null && adjustmentRules.Length != 0) {
+                AdjustmentRule prev = null;
+                foreach (AdjustmentRule current in adjustmentRules) {
+                    if (current == null)
+                        throw new InvalidTimeZoneException ("one or 
more elements in adjustmentRules are null");
+
+                    if ((baseUtcOffset + current.DaylightDelta < new 
TimeSpan (-14, 0, 0)) ||
+                            (baseUtcOffset + current.DaylightDelta > 
new TimeSpan (14, 0, 0)))
+                        throw new InvalidTimeZoneException ("Sum of 
baseUtcOffset and DaylightDelta of one or more object in adjustmentRules 
array is greater than 14 or less than -14 hours;");
+
+                    if (prev != null && prev.DateStart > current.DateStart)
+                        throw new InvalidTimeZoneException ("adjustment 
rules specified in adjustmentRules parameter are not in chronological 
order");
+
+                    if (prev != null && prev.DateEnd > current.DateStart)
+                        throw new InvalidTimeZoneException ("some 
adjustment rules in the adjustmentRules parameter overlap");
+
+                    if (prev != null && prev.DateEnd == current.DateStart)
+                        throw new InvalidTimeZoneException ("a date can 
have multiple adjustment rules applied to it");
+
+                    prev = current;
+                }
+            }
+        }
+
          public string ToSerializedString ()
          {
              throw new NotImplementedException ();
@@ -850,10 +832,24 @@ namespace System
              this.adjustmentRules = adjustmentRules;
          }

+        private TimeZoneInfo (SerializationInfo info, StreamingContext 
context)
+        {
+            if (info == null)
+                throw new ArgumentNullException ("info");
+            id = (string) info.GetValue ("Id", typeof (string));
+            displayName = (string) info.GetValue ("DisplayName", typeof 
(string));
+            standardDisplayName = (string) info.GetValue 
("StandardName", typeof (string));
+            daylightDisplayName = (string) info.GetValue 
("DaylightName", typeof (string));
+            baseUtcOffset = (TimeSpan) info.GetValue ("BaseUtcOffset", 
typeof (TimeSpan));
+            adjustmentRules = (TimeZoneInfo.AdjustmentRule []) 
info.GetValue ("AdjustmentRules", typeof (TimeZoneInfo.AdjustmentRule []));
+            supportsDaylightSavingTime = (bool) info.GetValue 
("SupportsDaylightSavingTime", typeof (bool));
+        }
+
          private AdjustmentRule GetApplicableRule (DateTime dateTime)
          {
              //Transitions are always in standard time
diff --git 
a/mcs/class/System.Core/Test/System/TimeZoneInfo.AdjustmentRuleTest.cs 
b/mcs/class/System.Core/Test/System/TimeZoneInfo.AdjustmentRuleTest.cs
index 6fd2412..e20c37d 100644
--- "a/mcs/class/System.Core/Test/System/TimeZoneInfo.AdjustmentRuleTest.cs"
+++ "b/mcs/class/System.Core/Test/System/TimeZoneInfo.AdjustmentRuleTest.cs"
@@ -1,4 +1,6 @@
  using System;
+using System.IO;
+using System.Runtime.Serialization.Formatters;
  using NUnit.Framework;

  #if NET_2_0
@@ -89,7 +91,33 @@ public void DeltaNotInSeconds ()
                  TimeZoneInfo.TransitionTime daylightTransitionEnd = 
TimeZoneInfo.TransitionTime.CreateFixedDateRule (new DateTime 
(1,1,1,2,0,0), 10, 11);
                  TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule 
(dateStart, dateEnd, new TimeSpan (55), daylightTransitionStart, 
daylightTransitionEnd);
              }
-        }
+        }
+
+       [TestFixture]
+        public class SerializationTests
+        {
+            TimeZoneInfo.AdjustmentRule rule;
+
+            [SetUp]
+            public void CreateRule ()
+            {
+                TimeZoneInfo.TransitionTime start = 
TimeZoneInfo.TransitionTime.CreateFloatingDateRule (new DateTime 
(1,1,1,1,0,0), 3, 5, DayOfWeek.Sunday);
+                TimeZoneInfo.TransitionTime end = 
TimeZoneInfo.TransitionTime.CreateFloatingDateRule (new DateTime 
(1,1,1,2,0,0), 10, 5, DayOfWeek.Sunday);
+                rule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule 
(DateTime.MinValue.Date, DateTime.MaxValue.Date, new TimeSpan (1,0,0), 
start, end);
+            }
+
+            [Test]
+            public void Serialization_Deserialization ()
+            {
+                MemoryStream stream = new MemoryStream ();
+                BinaryFormatter formatter = new BinaryFormatter ();
+                formatter.Serialize (stream, rule);
+                stream.Position = 0;
+                TimeZoneInfo.AdjustmentRule deserialized = 
(TimeZoneInfo.AdjustmentRule) formatter.Deserialize (stream);
+                stream.Close ();
+                stream.Dispose ();
+                Assert.AreEqual (rule, deserialized);
+            }
+        }
      }
  }
#endif
diff --git 
a/mcs/class/System.Core/Test/System/TimeZoneInfo.TransitionTimeTest.cs 
b/mcs/class/System.Core/Test/System/TimeZoneInfo.TransitionTimeTest.cs
index 8dd8263..2fd2e70 100644
--- "a/mcs/class/System.Core/Test/System/TimeZoneInfo.TransitionTimeTest.cs"
+++ "b/mcs/class/System.Core/Test/System/TimeZoneInfo.TransitionTimeTest.cs"
@@ -1,5 +1,7 @@

  using System;
+using System.IO;
+using System.Runtime.Serialization.Formatters;
  using NUnit.Framework;

  #if NET_2_0
@@ -107,6 +109,46 @@ public void EqualsObject ()
                  Assert.IsFalse (tt1.Equals (tt2), "2!=1");
              }
          }
+
+       [TestFixture]
+        public class SerializationTests
+        {
+            TimeZoneInfo.TransitionTime floatingDateRule;
+            TimeZoneInfo.TransitionTime fixedDateRule;
+
+            [SetUp]
+            public void CreateDateRules ()
+            {
+                floatingDateRule = 
TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 
1, 0, 0), 3, 5, DayOfWeek.Sunday);
+                fixedDateRule = 
TimeZoneInfo.TransitionTime.CreateFixedDateRule(new DateTime(1, 1, 1, 1, 
0, 0), 3, 12);
+            }
+
+            [Test]
+            public void Serialize_Deserialize_FloatingDateRule ()
+            {
+                MemoryStream stream = new MemoryStream ();
+                BinaryFormatter formatter = new BinaryFormatter ();
+                formatter.Serialize (stream, floatingDateRule);
+                stream.Position = 0;
+                TimeZoneInfo.TransitionTime deserialized = 
(TimeZoneInfo.TransitionTime) formatter.Deserialize (stream);
+                stream.Close ();
+                stream.Dispose ();
+                Assert.AreEqual (floatingDateRule, deserialized);
+            }
+
+            [Test]
+            public void Serialize_Deserialize_FixedDateRule ()
+            {
+                MemoryStream stream = new MemoryStream ();
+                BinaryFormatter formatter = new BinaryFormatter ();
+                formatter.Serialize (stream, fixedDateRule);
+                stream.Position = 0;
+                TimeZoneInfo.TransitionTime deserialized = 
(TimeZoneInfo.TransitionTime) formatter.Deserialize (stream);
+                stream.Close ();
+                stream.Dispose ();
+                Assert.AreEqual (fixedDateRule, deserialized);
+            }
+        }
      }
  }
  #endif
diff --git a/mcs/class/System.Core/Test/System/TimeZoneInfoTest.cs 
b/mcs/class/System.Core/Test/System/TimeZoneInfoTest.cs
index 06de33e..f678acf 100644
--- "a/mcs/class/System.Core/Test/System/TimeZoneInfoTest.cs"
+++ "b/mcs/class/System.Core/Test/System/TimeZoneInfoTest.cs"
@@ -28,6 +28,8 @@

  using System;
  using System.Collections;
+using System.IO;
+using System.Runtime.Serialization.Formatters;

  using NUnit.Framework;
  #if NET_2_0
@@ -657,6 +659,34 @@ public class HasSameRulesTests
                  Assert.IsTrue (utc.HasSameRules (custom));
              }
          }
+
+       [TestFixture]
+        public class SerializationTests
+        {
+            TimeZoneInfo london;
+
+            [SetUp]
+            public void CreateTimeZones ()
+            {
+                TimeZoneInfo.TransitionTime start = 
TimeZoneInfo.TransitionTime.CreateFloatingDateRule (new DateTime 
(1,1,1,1,0,0), 3, 5, DayOfWeek.Sunday);
+                TimeZoneInfo.TransitionTime end = 
TimeZoneInfo.TransitionTime.CreateFloatingDateRule (new DateTime 
(1,1,1,2,0,0), 10, 5, DayOfWeek.Sunday);
+                TimeZoneInfo.AdjustmentRule rule = 
TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule 
(DateTime.MinValue.Date, DateTime.MaxValue.Date, new TimeSpan (1,0,0), 
start, end);
+                london = TimeZoneInfo.CreateCustomTimeZone 
("Europe/London", new TimeSpan (0), "Europe/London", "British Standard 
Time", "British Summer Time", new TimeZoneInfo.AdjustmentRule [] {rule});
+            }
+
+            [Test]
+            public void Serialization_Deserialization ()
+            {
+                MemoryStream stream = new MemoryStream ();
+                BinaryFormatter formatter = new BinaryFormatter ();
+                formatter.Serialize (stream, london);
+                stream.Position = 0;
+                TimeZoneInfo deserialized = (TimeZoneInfo) 
formatter.Deserialize (stream);
+                stream.Close ();
+                stream.Dispose ();
+                Assert.AreEqual (london, deserialized);
+            }
+        }
      }
  }
  #endif

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ximian.com/pipermail/mono-devel-list/attachments/20130617/3573f627/attachment-0001.html>


More information about the Mono-devel-list mailing list