TimeZoneInfo.Serialization.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. * System.TimeZoneInfo.Serialization
  3. *
  4. * Author(s)
  5. * Sasha Kotlyar <[email protected]>
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining
  8. * a copy of this software and associated documentation files (the
  9. * "Software"), to deal in the Software without restriction, including
  10. * without limitation the rights to use, copy, modify, merge, publish,
  11. * distribute, sublicense, and/or sell copies of the Software, and to
  12. * permit persons to whom the Software is furnished to do so, subject to
  13. * the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be
  16. * included in all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  19. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  20. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  21. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  22. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  23. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  24. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  25. */
  26. #if INSIDE_CORLIB || (NET_3_5 && !NET_4_0 && !MOBILE)
  27. using System.Collections.Generic;
  28. using System.Globalization;
  29. using System.Runtime.Serialization;
  30. using System.Text;
  31. namespace System
  32. {
  33. #if NET_4_0 || !INSIDE_CORLIB
  34. public
  35. #endif
  36. partial class TimeZoneInfo
  37. {
  38. public static TimeZoneInfo FromSerializedString (string source)
  39. {
  40. var input = new StringBuilder (source);
  41. var tzId = DeserializeString (ref input);
  42. var offset = DeserializeInt (ref input);
  43. var displayName = DeserializeString (ref input);
  44. var standardName = DeserializeString (ref input);
  45. var daylightName = DeserializeString (ref input);
  46. var rules = new List<TimeZoneInfo.AdjustmentRule> ();
  47. while (input [0] != ';') {
  48. rules.Add (DeserializeAdjustmentRule (ref input));
  49. }
  50. var offsetSpan = TimeSpan.FromMinutes (offset);
  51. return TimeZoneInfo.CreateCustomTimeZone (tzId, offsetSpan, displayName, standardName, daylightName, rules.ToArray ());
  52. }
  53. public string ToSerializedString ()
  54. {
  55. var stb = new StringBuilder ();
  56. var daylightName = (string.IsNullOrEmpty(this.DaylightName) ? this.StandardName : this.DaylightName);
  57. stb.AppendFormat ("{0};{1};{2};{3};{4};", EscapeForSerialization (this.Id), (int)this.BaseUtcOffset.TotalMinutes,
  58. EscapeForSerialization (this.DisplayName), EscapeForSerialization (this.StandardName), EscapeForSerialization (daylightName));
  59. if (this.SupportsDaylightSavingTime) {
  60. foreach (var rule in this.GetAdjustmentRules()) {
  61. var start = rule.DateStart.ToString ("MM:dd:yyyy", CultureInfo.InvariantCulture);
  62. var end = rule.DateEnd.ToString ("MM:dd:yyyy", CultureInfo.InvariantCulture);
  63. var delta = (int)rule.DaylightDelta.TotalMinutes;
  64. var transitionStart = SerializeTransitionTime (rule.DaylightTransitionStart);
  65. var transitionEnd = SerializeTransitionTime (rule.DaylightTransitionEnd);
  66. stb.AppendFormat ("[{0};{1};{2};{3};{4};]", start, end, delta,
  67. transitionStart, transitionEnd);
  68. }
  69. }
  70. stb.Append (";");
  71. return stb.ToString ();
  72. }
  73. private static TimeZoneInfo.AdjustmentRule DeserializeAdjustmentRule (ref StringBuilder input)
  74. {
  75. // Similar to: [01:01:0001;12:31:9999;60;[0;01:00:00;3;5;0;];[0;02:00:00;10;5;0;];]
  76. if (input [0] != '[')
  77. throw new SerializationException ();
  78. input.Remove (0, 1); // [
  79. var dateStart = DeserializeDate (ref input);
  80. var dateEnd = DeserializeDate (ref input);
  81. var delta = DeserializeInt (ref input);
  82. var transitionStart = DeserializeTransitionTime (ref input);
  83. var transitionEnd = DeserializeTransitionTime (ref input);
  84. input.Remove (0, 1); // ]
  85. var deltaSpan = TimeSpan.FromMinutes (delta);
  86. return TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, deltaSpan,
  87. transitionStart, transitionEnd);
  88. }
  89. private static TimeZoneInfo.TransitionTime DeserializeTransitionTime (ref StringBuilder input)
  90. {
  91. if (input [0] != '[' || (input [1] != '0' && input [1] != '1') || input [2] != ';')
  92. throw new SerializationException ();
  93. var rule = input [1];
  94. input.Remove (0, 3); // [#;
  95. var timeOfDay = DeserializeTime (ref input);
  96. var month = DeserializeInt (ref input);
  97. if (rule == '0') {
  98. // Floating rule such as: [0;01:00:00;3;5;0;];
  99. var week = DeserializeInt (ref input);
  100. var dayOfWeek = DeserializeInt (ref input);
  101. input.Remove (0, 2); // ];
  102. return TimeZoneInfo.TransitionTime.CreateFloatingDateRule (timeOfDay, month, week, (DayOfWeek)dayOfWeek);
  103. }
  104. // Fixed rule such as: [1;02:15:59.999;6;2;];
  105. var day = DeserializeInt (ref input);
  106. input.Remove (0, 2); // ];
  107. return TimeZoneInfo.TransitionTime.CreateFixedDateRule (timeOfDay, month, day);
  108. }
  109. private static string DeserializeString (ref StringBuilder input)
  110. {
  111. var stb = new StringBuilder ();
  112. var isEscaped = false;
  113. int charCount;
  114. for (charCount = 0; charCount < input.Length; charCount++) {
  115. var inChar = input [charCount];
  116. if (isEscaped) {
  117. isEscaped = false;
  118. stb.Append (inChar);
  119. } else if (inChar == '\\') {
  120. isEscaped = true;
  121. continue;
  122. } else if (inChar == ';') {
  123. break;
  124. } else {
  125. stb.Append (inChar);
  126. }
  127. }
  128. input.Remove (0, charCount + 1);
  129. return stb.ToString ();
  130. }
  131. private static int DeserializeInt(ref StringBuilder input)
  132. {
  133. int charCount = 0;
  134. while(charCount++ < input.Length)
  135. {
  136. if (input[charCount] == ';')
  137. break;
  138. }
  139. int result;
  140. if(!int.TryParse(input.ToString(0, charCount), NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
  141. throw new SerializationException();
  142. input.Remove(0, charCount + 1);
  143. return result;
  144. }
  145. private static DateTime DeserializeDate (ref StringBuilder input)
  146. {
  147. var inChars = new char[11];
  148. input.CopyTo (0, inChars, 0, inChars.Length);
  149. DateTime result;
  150. if (!DateTime.TryParseExact (new string (inChars), "MM:dd:yyyy;", CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
  151. throw new SerializationException ();
  152. input.Remove (0, inChars.Length);
  153. return result;
  154. }
  155. private static DateTime DeserializeTime (ref StringBuilder input)
  156. {
  157. if (input [8] == ';') {
  158. // Without milliseconds
  159. var inChars = new char[9];
  160. input.CopyTo (0, inChars, 0, inChars.Length);
  161. DateTime result;
  162. if (!DateTime.TryParseExact (new string (inChars), "HH:mm:ss;", CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result))
  163. throw new SerializationException ();
  164. input.Remove (0, inChars.Length);
  165. return result;
  166. } else if (input [12] == ';') {
  167. // With milliseconds
  168. char[] inChars = new char[13];
  169. input.CopyTo (0, inChars, 0, inChars.Length);
  170. var inString = new string (inChars);
  171. DateTime result;
  172. if (!DateTime.TryParseExact (inString, "HH:mm:ss.fff;", CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result))
  173. throw new SerializationException ();
  174. input.Remove (0, inChars.Length);
  175. return result;
  176. }
  177. throw new SerializationException ();
  178. }
  179. private static string EscapeForSerialization (string unescaped)
  180. {
  181. return unescaped.Replace (@"\", @"\\").Replace (";", "\\;");
  182. }
  183. private static string SerializeTransitionTime (TimeZoneInfo.TransitionTime transition)
  184. {
  185. string timeOfDay;
  186. if (transition.TimeOfDay.Millisecond > 0)
  187. timeOfDay = transition.TimeOfDay.ToString ("HH:mm:ss.fff");
  188. else
  189. timeOfDay = transition.TimeOfDay.ToString ("HH:mm:ss");
  190. if (transition.IsFixedDateRule) {
  191. return string.Format ("[1;{0};{1};{2};]", timeOfDay, transition.Month, transition.Day);
  192. }
  193. return string.Format ("[0;{0};{1};{2};{3};]", timeOfDay, transition.Month,
  194. transition.Week, (int)transition.DayOfWeek);
  195. }
  196. }
  197. }
  198. #endif