2 * System.TimeZoneInfo.Serialization
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
;
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] != ';') {
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
);
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;];]
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 ();
100 input
.Remove (0, 3); // [#;
101 var timeOfDay
= DeserializeTime (ref input
);
102 var month
= DeserializeInt (ref input
);
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;
122 for (charCount
= 0; charCount
< input
.Length
; charCount
++) {
123 var inChar
= input
[charCount
];
127 } else if (inChar
== '\\') {
130 } else if (inChar
== ';') {
136 input
.Remove (0, charCount
+ 1);
137 return stb
.ToString ();
140 private static int DeserializeInt(ref StringBuilder input
)
143 while(charCount
++ < input
.Length
)
145 if (input
[charCount
] == ';')
149 if(!int.TryParse(input
.ToString(0, charCount
), NumberStyles
.Integer
, CultureInfo
.InvariantCulture
, out result
))
150 throw new SerializationException();
151 input
.Remove(0, charCount
+ 1);
155 private static DateTime
DeserializeDate (ref StringBuilder input
)
157 var inChars
= new char[11];
158 input
.CopyTo (0, inChars
, 0, inChars
.Length
);
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
);
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
);
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
);
177 } else if (input
[12] == ';') {
179 char[] inChars
= new char[13];
180 input
.CopyTo (0, inChars
, 0, inChars
.Length
);
181 var inString
= new string (inChars
);
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
);
188 throw new SerializationException ();
191 private static string EscapeForSerialization (string unescaped
)
193 return unescaped
.Replace (@"\", @"\\").Replace (";", "\\;");
196 private static string SerializeTransitionTime (TimeZoneInfo
.TransitionTime transition
)
199 if (transition
.TimeOfDay
.Millisecond
> 0)
200 timeOfDay
= transition
.TimeOfDay
.ToString ("HH:mm:ss.fff");
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
);