TimeZoneInfo.Serialization.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. public
  34. partial class TimeZoneInfo
  35. {
  36. public static TimeZoneInfo FromSerializedString (string source)
  37. {
  38. var input = new StringBuilder (source);
  39. var tzId = DeserializeString (ref input);
  40. var offset = DeserializeInt (ref input);
  41. var displayName = DeserializeString (ref input);
  42. var standardName = DeserializeString (ref input);
  43. var daylightName = DeserializeString (ref input);
  44. var rules = new List<TimeZoneInfo.AdjustmentRule> ();
  45. while (input [0] != ';') {
  46. rules.Add (DeserializeAdjustmentRule (ref input));
  47. }
  48. var offsetSpan = TimeSpan.FromMinutes (offset);
  49. return TimeZoneInfo.CreateCustomTimeZone (tzId, offsetSpan, displayName, standardName, daylightName, rules.ToArray ());
  50. }
  51. public string ToSerializedString ()
  52. {
  53. var stb = new StringBuilder ();
  54. var daylightName = (string.IsNullOrEmpty(this.DaylightName) ? this.StandardName : this.DaylightName);
  55. stb.AppendFormat ("{0};{1};{2};{3};{4};", EscapeForSerialization (this.Id), (int)this.BaseUtcOffset.TotalMinutes,
  56. EscapeForSerialization (this.DisplayName), EscapeForSerialization (this.StandardName), EscapeForSerialization (daylightName));
  57. if (this.SupportsDaylightSavingTime) {
  58. foreach (var rule in this.GetAdjustmentRules()) {
  59. var start = rule.DateStart.ToString ("MM:dd:yyyy", CultureInfo.InvariantCulture);
  60. var end = rule.DateEnd.ToString ("MM:dd:yyyy", CultureInfo.InvariantCulture);
  61. var delta = (int)rule.DaylightDelta.TotalMinutes;
  62. var transitionStart = SerializeTransitionTime (rule.DaylightTransitionStart);
  63. var transitionEnd = SerializeTransitionTime (rule.DaylightTransitionEnd);
  64. stb.AppendFormat ("[{0};{1};{2};{3};{4};]", start, end, delta,
  65. transitionStart, transitionEnd);
  66. }
  67. }
  68. stb.Append (";");
  69. return stb.ToString ();
  70. }
  71. private static TimeZoneInfo.AdjustmentRule DeserializeAdjustmentRule (ref StringBuilder input)
  72. {
  73. // Similar to: [01:01:0001;12:31:9999;60;[0;01:00:00;3;5;0;];[0;02:00:00;10;5;0;];]
  74. if (input [0] != '[')
  75. throw new SerializationException ();
  76. input.Remove (0, 1); // [
  77. var dateStart = DeserializeDate (ref input);
  78. var dateEnd = DeserializeDate (ref input);
  79. var delta = DeserializeInt (ref input);
  80. var transitionStart = DeserializeTransitionTime (ref input);
  81. var transitionEnd = DeserializeTransitionTime (ref input);
  82. input.Remove (0, 1); // ]
  83. var deltaSpan = TimeSpan.FromMinutes (delta);
  84. return TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, deltaSpan,
  85. transitionStart, transitionEnd);
  86. }
  87. private static TimeZoneInfo.TransitionTime DeserializeTransitionTime (ref StringBuilder input)
  88. {
  89. if (input [0] != '[' || (input [1] != '0' && input [1] != '1') || input [2] != ';')
  90. throw new SerializationException ();
  91. var rule = input [1];
  92. input.Remove (0, 3); // [#;
  93. var timeOfDay = DeserializeTime (ref input);
  94. var month = DeserializeInt (ref input);
  95. if (rule == '0') {
  96. // Floating rule such as: [0;01:00:00;3;5;0;];
  97. var week = DeserializeInt (ref input);
  98. var dayOfWeek = DeserializeInt (ref input);
  99. input.Remove (0, 2); // ];
  100. return TimeZoneInfo.TransitionTime.CreateFloatingDateRule (timeOfDay, month, week, (DayOfWeek)dayOfWeek);
  101. }
  102. // Fixed rule such as: [1;02:15:59.999;6;2;];
  103. var day = DeserializeInt (ref input);
  104. input.Remove (0, 2); // ];
  105. return TimeZoneInfo.TransitionTime.CreateFixedDateRule (timeOfDay, month, day);
  106. }
  107. private static string DeserializeString (ref StringBuilder input)
  108. {
  109. var stb = new StringBuilder ();
  110. var isEscaped = false;
  111. int charCount;
  112. for (charCount = 0; charCount < input.Length; charCount++) {
  113. var inChar = input [charCount];
  114. if (isEscaped) {
  115. isEscaped = false;
  116. stb.Append (inChar);
  117. } else if (inChar == '\\') {
  118. isEscaped = true;
  119. continue;
  120. } else if (inChar == ';') {
  121. break;
  122. } else {
  123. stb.Append (inChar);
  124. }
  125. }
  126. input.Remove (0, charCount + 1);
  127. return stb.ToString ();
  128. }
  129. private static int DeserializeInt(ref StringBuilder input)
  130. {
  131. int charCount = 0;
  132. while(charCount++ < input.Length)
  133. {
  134. if (input[charCount] == ';')
  135. break;
  136. }
  137. int result;
  138. if(!int.TryParse(input.ToString(0, charCount), NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
  139. throw new SerializationException();
  140. input.Remove(0, charCount + 1);
  141. return result;
  142. }
  143. private static DateTime DeserializeDate (ref StringBuilder input)
  144. {
  145. var inChars = new char[11];
  146. input.CopyTo (0, inChars, 0, inChars.Length);
  147. DateTime result;
  148. if (!DateTime.TryParseExact (new string (inChars), "MM:dd:yyyy;", CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
  149. throw new SerializationException ();
  150. input.Remove (0, inChars.Length);
  151. return result;
  152. }
  153. private static DateTime DeserializeTime (ref StringBuilder input)
  154. {
  155. if (input [8] == ';') {
  156. // Without milliseconds
  157. var inChars = new char[9];
  158. input.CopyTo (0, inChars, 0, inChars.Length);
  159. DateTime result;
  160. if (!DateTime.TryParseExact (new string (inChars), "HH:mm:ss;", CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result))
  161. throw new SerializationException ();
  162. input.Remove (0, inChars.Length);
  163. return result;
  164. } else if (input [12] == ';') {
  165. // With milliseconds
  166. char[] inChars = new char[13];
  167. input.CopyTo (0, inChars, 0, inChars.Length);
  168. var inString = new string (inChars);
  169. DateTime result;
  170. if (!DateTime.TryParseExact (inString, "HH:mm:ss.fff;", CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result))
  171. throw new SerializationException ();
  172. input.Remove (0, inChars.Length);
  173. return result;
  174. }
  175. throw new SerializationException ();
  176. }
  177. private static string EscapeForSerialization (string unescaped)
  178. {
  179. return unescaped.Replace (@"\", @"\\").Replace (";", "\\;");
  180. }
  181. private static string SerializeTransitionTime (TimeZoneInfo.TransitionTime transition)
  182. {
  183. string timeOfDay;
  184. if (transition.TimeOfDay.Millisecond > 0)
  185. timeOfDay = transition.TimeOfDay.ToString ("HH:mm:ss.fff");
  186. else
  187. timeOfDay = transition.TimeOfDay.ToString ("HH:mm:ss");
  188. if (transition.IsFixedDateRule) {
  189. return string.Format ("[1;{0};{1};{2};]", timeOfDay, transition.Month, transition.Day);
  190. }
  191. return string.Format ("[0;{0};{1};{2};{3};]", timeOfDay, transition.Month,
  192. transition.Week, (int)transition.DayOfWeek);
  193. }
  194. }
  195. }
  196. #endif