[corlib] Improve file:// url handling in AppDomainSetup. (#16161)
[mono-project.git] / mcs / class / corlib / System / TimeZoneInfo.Serialization.cs
blobb78b5536b8e4522a1992eacf85c1affea0975326
1 /*
2 * System.TimeZoneInfo.Serialization
4 * Author(s)
5 * Sasha Kotlyar <sasha@arktronic.com>
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:
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
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.
27 using System.Collections.Generic;
28 using System.Globalization;
29 using System.Runtime.Serialization;
30 using System.Text;
32 namespace System
34 public
35 partial class TimeZoneInfo
37 public static TimeZoneInfo FromSerializedString (string source)
39 var input = new StringBuilder (source);
40 var tzId = DeserializeString (ref input);
41 var offset = DeserializeInt (ref input);
42 var displayName = DeserializeString (ref input);
43 var standardName = DeserializeString (ref input);
44 var daylightName = DeserializeString (ref input);
45 List<TimeZoneInfo.AdjustmentRule> rules = null;
46 while (input [0] != ';') {
47 if (rules == null)
48 rules = new List<TimeZoneInfo.AdjustmentRule> ();
49 rules.Add (DeserializeAdjustmentRule (ref input));
51 var offsetSpan = TimeSpan.FromMinutes (offset);
52 return TimeZoneInfo.CreateCustomTimeZone (tzId, offsetSpan, displayName, standardName, daylightName, rules?.ToArray ());
55 public string ToSerializedString ()
57 var stb = new StringBuilder ();
58 var daylightName = (string.IsNullOrEmpty(this.DaylightName) ? this.StandardName : this.DaylightName);
59 stb.AppendFormat ("{0};{1};{2};{3};{4};", EscapeForSerialization (this.Id), (int)this.BaseUtcOffset.TotalMinutes,
60 EscapeForSerialization (this.DisplayName), EscapeForSerialization (this.StandardName), EscapeForSerialization (daylightName));
62 if (this.SupportsDaylightSavingTime) {
63 foreach (var rule in this.GetAdjustmentRules()) {
64 var start = rule.DateStart.ToString ("MM:dd:yyyy", CultureInfo.InvariantCulture);
65 var end = rule.DateEnd.ToString ("MM:dd:yyyy", CultureInfo.InvariantCulture);
66 var delta = (int)rule.DaylightDelta.TotalMinutes;
67 var transitionStart = SerializeTransitionTime (rule.DaylightTransitionStart);
68 var transitionEnd = SerializeTransitionTime (rule.DaylightTransitionEnd);
69 stb.AppendFormat ("[{0};{1};{2};{3};{4};]", start, end, delta,
70 transitionStart, transitionEnd);
74 stb.Append (";");
75 return stb.ToString ();
78 private static TimeZoneInfo.AdjustmentRule DeserializeAdjustmentRule (ref StringBuilder input)
80 // Similar to: [01:01:0001;12:31:9999;60;[0;01:00:00;3;5;0;];[0;02:00:00;10;5;0;];]
81 if (input [0] != '[')
82 throw new SerializationException ();
83 input.Remove (0, 1); // [
84 var dateStart = DeserializeDate (ref input);
85 var dateEnd = DeserializeDate (ref input);
86 var delta = DeserializeInt (ref input);
87 var transitionStart = DeserializeTransitionTime (ref input);
88 var transitionEnd = DeserializeTransitionTime (ref input);
89 input.Remove (0, 1); // ]
90 var deltaSpan = TimeSpan.FromMinutes (delta);
91 return TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule (dateStart, dateEnd, deltaSpan,
92 transitionStart, transitionEnd);
95 private static TimeZoneInfo.TransitionTime DeserializeTransitionTime (ref StringBuilder input)
97 if (input [0] != '[' || (input [1] != '0' && input [1] != '1') || input [2] != ';')
98 throw new SerializationException ();
99 var rule = input [1];
100 input.Remove (0, 3); // [#;
101 var timeOfDay = DeserializeTime (ref input);
102 var month = DeserializeInt (ref input);
103 if (rule == '0') {
104 // Floating rule such as: [0;01:00:00;3;5;0;];
105 var week = DeserializeInt (ref input);
106 var dayOfWeek = DeserializeInt (ref input);
107 input.Remove (0, 2); // ];
108 return TimeZoneInfo.TransitionTime.CreateFloatingDateRule (timeOfDay, month, week, (DayOfWeek)dayOfWeek);
111 // Fixed rule such as: [1;02:15:59.999;6;2;];
112 var day = DeserializeInt (ref input);
113 input.Remove (0, 2); // ];
114 return TimeZoneInfo.TransitionTime.CreateFixedDateRule (timeOfDay, month, day);
117 private static string DeserializeString (ref StringBuilder input)
119 var stb = new StringBuilder ();
120 var isEscaped = false;
121 int charCount;
122 for (charCount = 0; charCount < input.Length; charCount++) {
123 var inChar = input [charCount];
124 if (isEscaped) {
125 isEscaped = false;
126 stb.Append (inChar);
127 } else if (inChar == '\\') {
128 isEscaped = true;
129 continue;
130 } else if (inChar == ';') {
131 break;
132 } else {
133 stb.Append (inChar);
136 input.Remove (0, charCount + 1);
137 return stb.ToString ();
140 private static int DeserializeInt(ref StringBuilder input)
142 int charCount = 0;
143 while(charCount++ < input.Length)
145 if (input[charCount] == ';')
146 break;
148 int result;
149 if(!int.TryParse(input.ToString(0, charCount), NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
150 throw new SerializationException();
151 input.Remove(0, charCount + 1);
152 return result;
155 private static DateTime DeserializeDate (ref StringBuilder input)
157 var inChars = new char[11];
158 input.CopyTo (0, inChars, 0, inChars.Length);
159 DateTime result;
160 if (!DateTime.TryParseExact (new string (inChars), "MM:dd:yyyy;", CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
161 throw new SerializationException ();
162 input.Remove (0, inChars.Length);
163 return result;
166 private static DateTime DeserializeTime (ref StringBuilder input)
168 if (input [8] == ';') {
169 // Without milliseconds
170 var inChars = new char[9];
171 input.CopyTo (0, inChars, 0, inChars.Length);
172 DateTime result;
173 if (!DateTime.TryParseExact (new string (inChars), "HH:mm:ss;", CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result))
174 throw new SerializationException ();
175 input.Remove (0, inChars.Length);
176 return result;
177 } else if (input [12] == ';') {
178 // With milliseconds
179 char[] inChars = new char[13];
180 input.CopyTo (0, inChars, 0, inChars.Length);
181 var inString = new string (inChars);
182 DateTime result;
183 if (!DateTime.TryParseExact (inString, "HH:mm:ss.fff;", CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out result))
184 throw new SerializationException ();
185 input.Remove (0, inChars.Length);
186 return result;
188 throw new SerializationException ();
191 private static string EscapeForSerialization (string unescaped)
193 return unescaped.Replace (@"\", @"\\").Replace (";", "\\;");
196 private static string SerializeTransitionTime (TimeZoneInfo.TransitionTime transition)
198 string timeOfDay;
199 if (transition.TimeOfDay.Millisecond > 0)
200 timeOfDay = transition.TimeOfDay.ToString ("HH:mm:ss.fff");
201 else
202 timeOfDay = transition.TimeOfDay.ToString ("HH:mm:ss");
204 if (transition.IsFixedDateRule) {
205 return string.Format ("[1;{0};{1};{2};]", timeOfDay, transition.Month, transition.Day);
208 return string.Format ("[0;{0};{1};{2};{3};]", timeOfDay, transition.Month,
209 transition.Week, (int)transition.DayOfWeek);