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.
5 // Inspired by various parts of CoreRT, most notably TimeZoneInfo.WinRT.cs.
7 #if !FULL_AOT_DESKTOP || WIN_PLATFORM
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;
20 using System.Threading;
24 partial class TimeZoneInfo
27 internal struct SYSTEMTIME
29 internal ushort wYear;
30 internal ushort wMonth;
31 internal ushort wDayOfWeek;
33 internal ushort wHour;
34 internal ushort wMinute;
35 internal ushort wSecond;
36 internal ushort wMilliseconds;
39 [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
40 internal struct TIME_ZONE_INFORMATION
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;
53 [StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
54 internal struct DYNAMIC_TIME_ZONE_INFORMATION
56 internal TIME_ZONE_INFORMATION TZI;
57 [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
58 internal string TimeZoneKeyName;
59 internal byte DynamicDaylightTimeDisabled;
62 internal const uint TIME_ZONE_ID_INVALID = 0xffffffff;
63 internal const uint ERROR_NO_MORE_ITEMS = 259;
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);
74 internal static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation (ref DYNAMIC_TIME_ZONE_INFORMATION timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset)
76 bool supportsDst = (timeZoneInformation.TZI.StandardDate.wMonth != 0);
79 if (timeZoneInformation.TZI.Bias == defaultBaseUtcOffset) {
80 // this rule will not contain any information to be used to adjust dates. just ignore it
84 return AdjustmentRule.CreateAdjustmentRule (
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
94 // Create an AdjustmentRule with TransitionTime objects
96 TransitionTime daylightTransitionStart;
97 if (!TransitionTimeFromTimeZoneInformation (timeZoneInformation, out daylightTransitionStart, true /* start date */)) {
101 TransitionTime daylightTransitionEnd;
102 if (!TransitionTimeFromTimeZoneInformation (timeZoneInformation, out daylightTransitionEnd, false /* end date */)) {
106 if (daylightTransitionStart.Equals(daylightTransitionEnd)) {
107 // this happens when the time zone does support DST but the OS has DST disabled
111 return AdjustmentRule.CreateAdjustmentRule (
114 new TimeSpan (0, -timeZoneInformation.TZI.DaylightBias, 0),
115 (TransitionTime) daylightTransitionStart,
116 (TransitionTime) daylightTransitionEnd,
117 new TimeSpan (0, defaultBaseUtcOffset - timeZoneInformation.TZI.Bias, 0));
121 // TransitionTimeFromTimeZoneInformation -
123 // Converts a TimeZoneInformation (REG_TZI_FORMAT struct) to a TransitionTime
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
128 private static bool TransitionTimeFromTimeZoneInformation (DYNAMIC_TIME_ZONE_INFORMATION timeZoneInformation, out TransitionTime transitionTime, bool readStartDate)
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.
139 bool supportsDst = (timeZoneInformation.TZI.StandardDate.wMonth != 0);
142 transitionTime = default (TransitionTime);
150 // If the Year member is not zero, the transition date is absolute; it will only occur one time
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).
157 // Using this notation, specify the 2:00a.m. on the first Sunday in April as follows:
163 // Specify 2:00a.m. on the last Thursday in October as follows:
171 // read the "daylightTransitionStart"
173 if (timeZoneInformation.TZI.DaylightDate.wYear == 0) {
174 transitionTime = TransitionTime.CreateFloatingDateRule (
175 new DateTime (1, /* year */
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);
186 transitionTime = TransitionTime.CreateFixedDateRule (
187 new DateTime (1, /* year */
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);
199 // read the "daylightTransitionEnd"
201 if (timeZoneInformation.TZI.StandardDate.wYear == 0) {
202 transitionTime = TransitionTime.CreateFloatingDateRule (
203 new DateTime (1, /* year */
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);
214 transitionTime = TransitionTime.CreateFixedDateRule (
215 new DateTime (1, /* year */
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);
230 internal static TimeZoneInfo TryCreateTimeZone (DYNAMIC_TIME_ZONE_INFORMATION timeZoneInformation)
232 uint firstYear = 0, lastYear = 0;
234 AdjustmentRule[] zoneRules = null;
235 int defaultBaseUtcOffset = timeZoneInformation.TZI.Bias;
237 if (String.IsNullOrEmpty (timeZoneInformation.TimeZoneKeyName))
241 // First get the adjustment rules
245 if (GetDynamicTimeZoneInformationEffectiveYears (ref timeZoneInformation, out firstYear, out lastYear) == 0) {
246 firstYear = lastYear = 0;
249 // If we don't have GetDynamicTimeZoneInformationEffectiveYears()
250 firstYear = lastYear = 0;
253 if (firstYear == lastYear) {
254 rule = CreateAdjustmentRuleFromTimeZoneInformation (ref timeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
256 zoneRules = new AdjustmentRule [1] { rule };
258 DYNAMIC_TIME_ZONE_INFORMATION dtzi = default (DYNAMIC_TIME_ZONE_INFORMATION);
259 List<AdjustmentRule> rules = new List<AdjustmentRule> ();
264 if (!GetTimeZoneInformationForYear ((ushort) firstYear, ref timeZoneInformation, out dtzi.TZI))
266 rule = CreateAdjustmentRuleFromTimeZoneInformation (ref dtzi, DateTime.MinValue.Date, new DateTime ((int) firstYear, 12, 31), defaultBaseUtcOffset);
270 for (uint i = firstYear + 1; i < lastYear; i++) {
271 if (!GetTimeZoneInformationForYear ((ushort) i, ref timeZoneInformation, out dtzi.TZI))
273 rule = CreateAdjustmentRuleFromTimeZoneInformation (ref dtzi, new DateTime ((int) i, 1, 1), new DateTime ((int) i, 12, 31), defaultBaseUtcOffset);
282 if (!GetTimeZoneInformationForYear ((ushort) lastYear, ref timeZoneInformation, out dtzi.TZI))
284 rule = CreateAdjustmentRuleFromTimeZoneInformation (ref dtzi, new DateTime ((int) lastYear, 1, 1), DateTime.MaxValue.Date, defaultBaseUtcOffset);
289 zoneRules = rules.ToArray ();
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,
302 internal static TimeZoneInfo GetLocalTimeZoneInfoWinRTFallback ()
305 DYNAMIC_TIME_ZONE_INFORMATION dtzi;
306 var result = GetDynamicTimeZoneInformation (out dtzi);
307 if (result == TIME_ZONE_ID_INVALID)
309 TimeZoneInfo timeZoneInfo = TryCreateTimeZone (dtzi);
310 return timeZoneInfo != null ? timeZoneInfo : Utc;
316 internal static TimeZoneInfo FindSystemTimeZoneByIdWinRTFallback (string id)
318 foreach (var tzi in GetSystemTimeZones ()) {
319 if (String.Compare (id, tzi.Id, StringComparison.Ordinal) == 0)
323 throw new TimeZoneNotFoundException ();
326 internal static List<TimeZoneInfo> GetSystemTimeZonesWinRTFallback ()
328 var result = new List<TimeZoneInfo> ();
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);
338 // EnumDynamicTimeZoneInformation() might not be available.
341 if (result.Count == 0)
348 #endif // !FULL_AOT_DESKTOP || WIN_PLATFORM