Add autoconf checks for platforms without IPv6
[mono.git] / mcs / class / corlib / System / TimeZoneInfo.WinRT.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //
5 // Inspired by various parts of CoreRT, most notably TimeZoneInfo.WinRT.cs.
6
7 #if !FULL_AOT_DESKTOP || WIN_PLATFORM
8
9 using Microsoft.Win32;
10 using System;
11 using System.Collections;
12 using System.Diagnostics;
13 using System.Collections.Generic;
14 using System.Collections.ObjectModel;
15 using System.Globalization;
16 using System.Diagnostics.Contracts;
17 using System.Runtime.CompilerServices;
18 using System.Runtime.InteropServices;
19 using System.Text;
20 using System.Threading;
21
22 namespace System
23 {
24         partial class TimeZoneInfo
25         {
26
27                 internal struct SYSTEMTIME
28                 {
29                         internal ushort wYear;
30                         internal ushort wMonth;
31                         internal ushort wDayOfWeek;
32                         internal ushort wDay;
33                         internal ushort wHour;
34                         internal ushort wMinute;
35                         internal ushort wSecond;
36                         internal ushort wMilliseconds;
37                 }
38
39                 [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
40                 internal struct TIME_ZONE_INFORMATION
41                 {
42                         internal int Bias;
43                         [MarshalAs (UnmanagedType.ByValTStr, SizeConst=32)]
44                         internal string StandardName;
45                         internal SYSTEMTIME StandardDate;
46                         internal int StandardBias;
47                         [MarshalAs (UnmanagedType.ByValTStr, SizeConst=32)]
48                         internal string DaylightName;
49                         internal SYSTEMTIME DaylightDate;
50                         internal int DaylightBias;
51                 }
52
53                 [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
54                 internal struct DYNAMIC_TIME_ZONE_INFORMATION
55                 {
56                         internal TIME_ZONE_INFORMATION TZI;
57                         [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
58                         internal string TimeZoneKeyName;
59                         internal byte DynamicDaylightTimeDisabled;
60                 }
61
62                 internal const uint TIME_ZONE_ID_INVALID = 0xffffffff;
63                 internal const uint ERROR_NO_MORE_ITEMS = 259;
64
65                 [DllImport ("api-ms-win-core-timezone-l1-1-0.dll")]
66                 internal extern static uint EnumDynamicTimeZoneInformation (uint dwIndex, out DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation);
67                 [DllImport ("api-ms-win-core-timezone-l1-1-0.dll")]
68                 internal extern static uint GetDynamicTimeZoneInformation (out DYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation);
69                 [DllImport ("api-ms-win-core-timezone-l1-1-0.dll")]
70                 internal extern static uint GetDynamicTimeZoneInformationEffectiveYears(ref DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation, out uint FirstYear, out uint LastYear);
71                 [DllImport ("api-ms-win-core-timezone-l1-1-0.dll")]
72                 internal extern static bool GetTimeZoneInformationForYear(ushort wYear, ref DYNAMIC_TIME_ZONE_INFORMATION pdtzi, out TIME_ZONE_INFORMATION ptzi);
73
74                 internal static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation (ref DYNAMIC_TIME_ZONE_INFORMATION timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset)
75                 {
76                         bool supportsDst = (timeZoneInformation.TZI.StandardDate.wMonth != 0);
77
78                         if (!supportsDst) {
79                                 if (timeZoneInformation.TZI.Bias == defaultBaseUtcOffset) {
80                                         // this rule will not contain any information to be used to adjust dates. just ignore it
81                                         return null;
82                                 }
83
84                                 return AdjustmentRule.CreateAdjustmentRule (
85                                         startDate,
86                                         endDate,
87                                         TimeSpan.Zero, // no daylight saving transition
88                                         TransitionTime.CreateFixedDateRule (DateTime.MinValue, 1, 1),
89                                         TransitionTime.CreateFixedDateRule (DateTime.MinValue.AddMilliseconds(1), 1, 1),
90                                         new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.TZI.Bias, 0));  // Bias delta is all what we need from this rule
91                         }
92
93                         //
94                         // Create an AdjustmentRule with TransitionTime objects
95                         //
96                         TransitionTime daylightTransitionStart;
97                         if (!TransitionTimeFromTimeZoneInformation (timeZoneInformation, out daylightTransitionStart, true /* start date */)) {
98                                 return null;
99                         }
100
101                         TransitionTime daylightTransitionEnd;
102                         if (!TransitionTimeFromTimeZoneInformation (timeZoneInformation, out daylightTransitionEnd, false /* end date */)) {
103                                 return null;
104                         }
105
106                         if (daylightTransitionStart.Equals(daylightTransitionEnd)) {
107                                 // this happens when the time zone does support DST but the OS has DST disabled
108                                 return null;
109                         }
110
111                         return AdjustmentRule.CreateAdjustmentRule (
112                                 startDate,
113                                 endDate,
114                                 new TimeSpan (0, -timeZoneInformation.TZI.DaylightBias, 0),
115                                 (TransitionTime) daylightTransitionStart,
116                                 (TransitionTime) daylightTransitionEnd,
117                                 new TimeSpan (0, defaultBaseUtcOffset - timeZoneInformation.TZI.Bias, 0));
118                 }
119
120                 //
121                 // TransitionTimeFromTimeZoneInformation -
122                 //
123                 // Converts a TimeZoneInformation (REG_TZI_FORMAT struct) to a TransitionTime
124                 //
125                 // * when the argument 'readStart' is true the corresponding daylightTransitionTimeStart field is read
126                 // * when the argument 'readStart' is false the corresponding dayightTransitionTimeEnd field is read
127                 //
128                 private static bool TransitionTimeFromTimeZoneInformation (DYNAMIC_TIME_ZONE_INFORMATION timeZoneInformation, out TransitionTime transitionTime, bool readStartDate)
129                 {
130                         //
131                         // SYSTEMTIME - 
132                         //
133                         // If the time zone does not support daylight saving time or if the caller needs
134                         // to disable daylight saving time, the wMonth member in the SYSTEMTIME structure
135                         // must be zero. If this date is specified, the DaylightDate value in the 
136                         // TIME_ZONE_INFORMATION structure must also be specified. Otherwise, the system 
137                         // assumes the time zone data is invalid and no changes will be applied.
138                         //
139                         bool supportsDst = (timeZoneInformation.TZI.StandardDate.wMonth != 0);
140
141                         if (!supportsDst) {
142                                 transitionTime = default (TransitionTime);
143                                 return false;
144                         }
145
146                         //
147                         // SYSTEMTIME -
148                         //
149                         // * FixedDateRule -
150                         //   If the Year member is not zero, the transition date is absolute; it will only occur one time
151                         //
152                         // * FloatingDateRule -
153                         //   To select the correct day in the month, set the Year member to zero, the Hour and Minute 
154                         //   members to the transition time, the DayOfWeek member to the appropriate weekday, and the
155                         //   Day member to indicate the occurence of the day of the week within the month (first through fifth).
156                         //
157                         //   Using this notation, specify the 2:00a.m. on the first Sunday in April as follows: 
158                         //   Hour         = 2, 
159                         //   Month       = 4,
160                         //   DayOfWeek = 0,
161                         //   Day           = 1.
162                         //
163                         //   Specify 2:00a.m. on the last Thursday in October as follows:
164                         //   Hour         = 2,
165                         //   Month       = 10,
166                         //   DayOfWeek = 4,
167                         //   Day           = 5.
168                         //
169                         if (readStartDate) {
170                                 //
171                                 // read the "daylightTransitionStart"
172                                 //
173                                 if (timeZoneInformation.TZI.DaylightDate.wYear == 0) {
174                                         transitionTime = TransitionTime.CreateFloatingDateRule (
175                                                                          new DateTime (1,       /* year  */
176                                                                                                    1,   /* month */
177                                                                                                    1,   /* day   */
178                                                                                                    timeZoneInformation.TZI.DaylightDate.wHour,
179                                                                                                    timeZoneInformation.TZI.DaylightDate.wMinute,
180                                                                                                    timeZoneInformation.TZI.DaylightDate.wSecond,
181                                                                                                    timeZoneInformation.TZI.DaylightDate.wMilliseconds),
182                                                                          timeZoneInformation.TZI.DaylightDate.wMonth,
183                                                                          timeZoneInformation.TZI.DaylightDate.wDay,   /* Week 1-5 */
184                                                                          (DayOfWeek)timeZoneInformation.TZI.DaylightDate.wDayOfWeek);
185                                 } else {
186                                         transitionTime = TransitionTime.CreateFixedDateRule (
187                                                                          new DateTime (1,       /* year  */
188                                                                                                    1,   /* month */
189                                                                                                    1,   /* day   */
190                                                                                                    timeZoneInformation.TZI.DaylightDate.wHour,
191                                                                                                    timeZoneInformation.TZI.DaylightDate.wMinute,
192                                                                                                    timeZoneInformation.TZI.DaylightDate.wSecond,
193                                                                                                    timeZoneInformation.TZI.DaylightDate.wMilliseconds),
194                                                                          timeZoneInformation.TZI.DaylightDate.wMonth,
195                                                                          timeZoneInformation.TZI.DaylightDate.wDay);
196                                 }
197                         } else {
198                                 //
199                                 // read the "daylightTransitionEnd"
200                                 //
201                                 if (timeZoneInformation.TZI.StandardDate.wYear == 0) {
202                                         transitionTime = TransitionTime.CreateFloatingDateRule (
203                                                                          new DateTime (1,       /* year  */
204                                                                                                    1,   /* month */
205                                                                                                    1,   /* day   */
206                                                                                                    timeZoneInformation.TZI.StandardDate.wHour,
207                                                                                                    timeZoneInformation.TZI.StandardDate.wMinute,
208                                                                                                    timeZoneInformation.TZI.StandardDate.wSecond,
209                                                                                                    timeZoneInformation.TZI.StandardDate.wMilliseconds),
210                                                                          timeZoneInformation.TZI.StandardDate.wMonth,
211                                                                          timeZoneInformation.TZI.StandardDate.wDay,   /* Week 1-5 */
212                                                                          (DayOfWeek)timeZoneInformation.TZI.StandardDate.wDayOfWeek);
213                                 } else {
214                                         transitionTime = TransitionTime.CreateFixedDateRule (
215                                                                          new DateTime (1,       /* year  */
216                                                                                                    1,   /* month */
217                                                                                                    1,   /* day   */
218                                                                                                    timeZoneInformation.TZI.StandardDate.wHour,
219                                                                                                    timeZoneInformation.TZI.StandardDate.wMinute,
220                                                                                                    timeZoneInformation.TZI.StandardDate.wSecond,
221                                                                                                    timeZoneInformation.TZI.StandardDate.wMilliseconds),
222                                                                          timeZoneInformation.TZI.StandardDate.wMonth,
223                                                                          timeZoneInformation.TZI.StandardDate.wDay);
224                                 }
225                         }
226
227                         return true;
228                 }
229
230                 internal static TimeZoneInfo TryCreateTimeZone (DYNAMIC_TIME_ZONE_INFORMATION timeZoneInformation)
231                 {
232                         uint firstYear = 0, lastYear = 0;
233                         AdjustmentRule rule;
234                         AdjustmentRule[] zoneRules = null;
235                         int defaultBaseUtcOffset = timeZoneInformation.TZI.Bias;
236
237                         if (String.IsNullOrEmpty (timeZoneInformation.TimeZoneKeyName))
238                                 return null;
239
240                         //
241                         // First get the adjustment rules
242                         //
243
244                         try {
245                                 if (GetDynamicTimeZoneInformationEffectiveYears (ref timeZoneInformation, out firstYear, out lastYear) == 0) {
246                                         firstYear = lastYear = 0;
247                                 }
248                         } catch {
249                                 // If we don't have GetDynamicTimeZoneInformationEffectiveYears()
250                                 firstYear = lastYear = 0;
251                         }
252
253                         if (firstYear == lastYear) {
254                                 rule = CreateAdjustmentRuleFromTimeZoneInformation (ref timeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
255                                 if (rule != null)
256                                         zoneRules = new AdjustmentRule [1] { rule };
257                         } else {
258                                 DYNAMIC_TIME_ZONE_INFORMATION dtzi = default (DYNAMIC_TIME_ZONE_INFORMATION);
259                                 List<AdjustmentRule> rules = new List<AdjustmentRule> ();
260                                 //
261                                 // First rule
262                                 //
263
264                                 if (!GetTimeZoneInformationForYear ((ushort) firstYear, ref timeZoneInformation, out dtzi.TZI))
265                                         return null;
266                                 rule = CreateAdjustmentRuleFromTimeZoneInformation (ref dtzi, DateTime.MinValue.Date, new DateTime ((int) firstYear, 12, 31), defaultBaseUtcOffset);
267                                 if (rule != null)
268                                         rules.Add (rule);
269
270                                 for (uint i = firstYear + 1; i < lastYear; i++) {
271                                         if (!GetTimeZoneInformationForYear ((ushort) i, ref timeZoneInformation, out dtzi.TZI))
272                                                 return null;
273                                         rule = CreateAdjustmentRuleFromTimeZoneInformation (ref dtzi, new DateTime ((int) i, 1, 1), new DateTime ((int) i, 12, 31), defaultBaseUtcOffset);
274                                         if (rule != null)
275                                                 rules.Add (rule);
276                                 }
277
278                                 //
279                                 // Last rule
280                                 //
281
282                                 if (!GetTimeZoneInformationForYear ((ushort) lastYear, ref timeZoneInformation, out dtzi.TZI))
283                                         return null;
284                                 rule = CreateAdjustmentRuleFromTimeZoneInformation (ref dtzi, new DateTime ((int) lastYear, 1, 1), DateTime.MaxValue.Date, defaultBaseUtcOffset);
285                                 if (rule != null)
286                                         rules.Add (rule);
287
288                                 if (rules.Count > 0)
289                                         zoneRules = rules.ToArray ();
290                         }
291
292                         return new TimeZoneInfo (
293                                 timeZoneInformation.TimeZoneKeyName,
294                                 new TimeSpan (0, -(timeZoneInformation.TZI.Bias), 0),
295                                 timeZoneInformation.TZI.StandardName,   // we use the display name as the standared names
296                                 timeZoneInformation.TZI.StandardName,
297                                 timeZoneInformation.TZI.DaylightName,
298                                 zoneRules,
299                                 false);
300                 }
301
302                 internal static TimeZoneInfo GetLocalTimeZoneInfoWinRTFallback ()
303                 {
304                         try {
305                                 DYNAMIC_TIME_ZONE_INFORMATION dtzi;
306                                 var result = GetDynamicTimeZoneInformation (out dtzi);
307                                 if (result == TIME_ZONE_ID_INVALID)
308                                         return Utc;
309                                 TimeZoneInfo timeZoneInfo = TryCreateTimeZone (dtzi);
310                                 return timeZoneInfo != null ? timeZoneInfo : Utc;
311                         } catch {
312                                 return Utc;
313                         }
314                 }
315
316                 internal static TimeZoneInfo FindSystemTimeZoneByIdWinRTFallback (string id)
317                 {
318                         foreach (var tzi in GetSystemTimeZones ()) {
319                                 if (String.Compare (id, tzi.Id, StringComparison.Ordinal) == 0)
320                                         return tzi;
321                         }
322
323                         throw new TimeZoneNotFoundException ();
324                 }
325
326                 internal static List<TimeZoneInfo> GetSystemTimeZonesWinRTFallback ()
327                 {
328                         var result = new List<TimeZoneInfo> ();
329                         try {
330                                 uint index = 0;
331                                 DYNAMIC_TIME_ZONE_INFORMATION dtzi;
332                                 while (EnumDynamicTimeZoneInformation (index++, out dtzi) != ERROR_NO_MORE_ITEMS) {
333                                         var timeZoneInfo = TryCreateTimeZone (dtzi);
334                                         if (timeZoneInfo != null)
335                                                 result.Add (timeZoneInfo);
336                                 }
337                         } catch {
338                                 // EnumDynamicTimeZoneInformation() might not be available.
339                         }
340
341                         if (result.Count == 0)
342                                 result.Add (Local);
343                         return result;
344                 }
345         }
346 }
347
348 #endif // !FULL_AOT_DESKTOP || WIN_PLATFORM