File: TimeZoneInfo.WinRT.cs

package info (click to toggle)
mono 6.14.1%2Bds2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,282,732 kB
  • sloc: cs: 11,182,461; xml: 2,850,281; ansic: 699,123; cpp: 122,919; perl: 58,604; javascript: 30,841; asm: 21,845; makefile: 19,602; sh: 10,973; python: 4,772; pascal: 925; sql: 859; sed: 16; php: 1
file content (357 lines) | stat: -rwxr-xr-x 13,039 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// Inspired by various parts of CoreRT, most notably TimeZoneInfo.WinRT.cs.

#if WIN_PLATFORM

using Microsoft.Win32;
using System;
using System.Collections;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace System
{
	partial class TimeZoneInfo
	{

		internal struct SYSTEMTIME
		{
			internal ushort wYear;
			internal ushort wMonth;
			internal ushort wDayOfWeek;
			internal ushort wDay;
			internal ushort wHour;
			internal ushort wMinute;
			internal ushort wSecond;
			internal ushort wMilliseconds;
		}

		[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
		internal struct TIME_ZONE_INFORMATION
		{
			internal int Bias;
			[MarshalAs (UnmanagedType.ByValTStr, SizeConst=32)]
			internal string StandardName;
			internal SYSTEMTIME StandardDate;
			internal int StandardBias;
			[MarshalAs (UnmanagedType.ByValTStr, SizeConst=32)]
			internal string DaylightName;
			internal SYSTEMTIME DaylightDate;
			internal int DaylightBias;
		}

		[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Unicode)]
		internal struct DYNAMIC_TIME_ZONE_INFORMATION
		{
			internal TIME_ZONE_INFORMATION TZI;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
			internal string TimeZoneKeyName;
			internal byte DynamicDaylightTimeDisabled;
		}

		internal const uint TIME_ZONE_ID_INVALID = 0xffffffff;
		internal const uint ERROR_NO_MORE_ITEMS = 259;

		[DllImport ("api-ms-win-core-timezone-l1-1-0.dll")]
		internal extern static uint EnumDynamicTimeZoneInformation (uint dwIndex, out DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation);
		[DllImport ("api-ms-win-core-timezone-l1-1-0.dll")]
		internal extern static uint GetDynamicTimeZoneInformation (out DYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation);
		[DllImport ("api-ms-win-core-timezone-l1-1-0.dll")]
		internal extern static uint GetDynamicTimeZoneInformationEffectiveYears(ref DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation, out uint FirstYear, out uint LastYear);
		[DllImport ("api-ms-win-core-timezone-l1-1-0.dll")]
		internal extern static bool GetTimeZoneInformationForYear(ushort wYear, ref DYNAMIC_TIME_ZONE_INFORMATION pdtzi, out TIME_ZONE_INFORMATION ptzi);

		internal static AdjustmentRule CreateAdjustmentRuleFromTimeZoneInformation (ref DYNAMIC_TIME_ZONE_INFORMATION timeZoneInformation, DateTime startDate, DateTime endDate, int defaultBaseUtcOffset)
		{
			bool supportsDst = (timeZoneInformation.TZI.StandardDate.wMonth != 0);

			if (!supportsDst) {
				if (timeZoneInformation.TZI.Bias == defaultBaseUtcOffset) {
					// this rule will not contain any information to be used to adjust dates. just ignore it
					return null;
				}

				return AdjustmentRule.CreateAdjustmentRule (
					startDate,
					endDate,
					TimeSpan.Zero, // no daylight saving transition
					TransitionTime.CreateFixedDateRule (DateTime.MinValue, 1, 1),
					TransitionTime.CreateFixedDateRule (DateTime.MinValue.AddMilliseconds(1), 1, 1),
					new TimeSpan(0, defaultBaseUtcOffset - timeZoneInformation.TZI.Bias, 0));  // Bias delta is all what we need from this rule
			}

			//
			// Create an AdjustmentRule with TransitionTime objects
			//
			TransitionTime daylightTransitionStart;
			if (!TransitionTimeFromTimeZoneInformation (timeZoneInformation, out daylightTransitionStart, true /* start date */)) {
				return null;
			}

			TransitionTime daylightTransitionEnd;
			if (!TransitionTimeFromTimeZoneInformation (timeZoneInformation, out daylightTransitionEnd, false /* end date */)) {
				return null;
			}

			if (daylightTransitionStart.Equals(daylightTransitionEnd)) {
				// this happens when the time zone does support DST but the OS has DST disabled
				return null;
			}

			return AdjustmentRule.CreateAdjustmentRule (
				startDate,
				endDate,
				new TimeSpan (0, -timeZoneInformation.TZI.DaylightBias, 0),
				(TransitionTime) daylightTransitionStart,
				(TransitionTime) daylightTransitionEnd,
				new TimeSpan (0, defaultBaseUtcOffset - timeZoneInformation.TZI.Bias, 0));
		}

		//
		// TransitionTimeFromTimeZoneInformation -
		//
		// Converts a TimeZoneInformation (REG_TZI_FORMAT struct) to a TransitionTime
		//
		// * when the argument 'readStart' is true the corresponding daylightTransitionTimeStart field is read
		// * when the argument 'readStart' is false the corresponding dayightTransitionTimeEnd field is read
		//
		private static bool TransitionTimeFromTimeZoneInformation (DYNAMIC_TIME_ZONE_INFORMATION timeZoneInformation, out TransitionTime transitionTime, bool readStartDate)
		{
			//
			// SYSTEMTIME - 
			//
			// If the time zone does not support daylight saving time or if the caller needs
			// to disable daylight saving time, the wMonth member in the SYSTEMTIME structure
			// must be zero. If this date is specified, the DaylightDate value in the 
			// TIME_ZONE_INFORMATION structure must also be specified. Otherwise, the system 
			// assumes the time zone data is invalid and no changes will be applied.
			//
			bool supportsDst = (timeZoneInformation.TZI.StandardDate.wMonth != 0);

			if (!supportsDst) {
				transitionTime = default (TransitionTime);
				return false;
			}

			//
			// SYSTEMTIME -
			//
			// * FixedDateRule -
			//   If the Year member is not zero, the transition date is absolute; it will only occur one time
			//
			// * FloatingDateRule -
			//   To select the correct day in the month, set the Year member to zero, the Hour and Minute 
			//   members to the transition time, the DayOfWeek member to the appropriate weekday, and the
			//   Day member to indicate the occurence of the day of the week within the month (first through fifth).
			//
			//   Using this notation, specify the 2:00a.m. on the first Sunday in April as follows: 
			//   Hour	  = 2, 
			//   Month	 = 4,
			//   DayOfWeek = 0,
			//   Day	   = 1.
			//
			//   Specify 2:00a.m. on the last Thursday in October as follows:
			//   Hour	  = 2,
			//   Month	 = 10,
			//   DayOfWeek = 4,
			//   Day	   = 5.
			//
			if (readStartDate) {
				//
				// read the "daylightTransitionStart"
				//
				if (timeZoneInformation.TZI.DaylightDate.wYear == 0) {
					transitionTime = TransitionTime.CreateFloatingDateRule (
									 new DateTime (1,	/* year  */
												   1,	/* month */
												   1,	/* day   */
												   timeZoneInformation.TZI.DaylightDate.wHour,
												   timeZoneInformation.TZI.DaylightDate.wMinute,
												   timeZoneInformation.TZI.DaylightDate.wSecond,
												   timeZoneInformation.TZI.DaylightDate.wMilliseconds),
									 timeZoneInformation.TZI.DaylightDate.wMonth,
									 timeZoneInformation.TZI.DaylightDate.wDay,   /* Week 1-5 */
									 (DayOfWeek)timeZoneInformation.TZI.DaylightDate.wDayOfWeek);
				} else {
					transitionTime = TransitionTime.CreateFixedDateRule (
									 new DateTime (1,	/* year  */
												   1,	/* month */
												   1,	/* day   */
												   timeZoneInformation.TZI.DaylightDate.wHour,
												   timeZoneInformation.TZI.DaylightDate.wMinute,
												   timeZoneInformation.TZI.DaylightDate.wSecond,
												   timeZoneInformation.TZI.DaylightDate.wMilliseconds),
									 timeZoneInformation.TZI.DaylightDate.wMonth,
									 timeZoneInformation.TZI.DaylightDate.wDay);
				}
			} else {
				//
				// read the "daylightTransitionEnd"
				//
				if (timeZoneInformation.TZI.StandardDate.wYear == 0) {
					transitionTime = TransitionTime.CreateFloatingDateRule (
									 new DateTime (1,	/* year  */
												   1,	/* month */
												   1,	/* day   */
												   timeZoneInformation.TZI.StandardDate.wHour,
												   timeZoneInformation.TZI.StandardDate.wMinute,
												   timeZoneInformation.TZI.StandardDate.wSecond,
												   timeZoneInformation.TZI.StandardDate.wMilliseconds),
									 timeZoneInformation.TZI.StandardDate.wMonth,
									 timeZoneInformation.TZI.StandardDate.wDay,   /* Week 1-5 */
									 (DayOfWeek)timeZoneInformation.TZI.StandardDate.wDayOfWeek);
				} else {
					transitionTime = TransitionTime.CreateFixedDateRule (
									 new DateTime (1,	/* year  */
												   1,	/* month */
												   1,	/* day   */
												   timeZoneInformation.TZI.StandardDate.wHour,
												   timeZoneInformation.TZI.StandardDate.wMinute,
												   timeZoneInformation.TZI.StandardDate.wSecond,
												   timeZoneInformation.TZI.StandardDate.wMilliseconds),
									 timeZoneInformation.TZI.StandardDate.wMonth,
									 timeZoneInformation.TZI.StandardDate.wDay);
				}
			}

			return true;
		}

		internal static TimeZoneInfo TryCreateTimeZone (DYNAMIC_TIME_ZONE_INFORMATION timeZoneInformation)
		{
			uint firstYear = 0, lastYear = 0;
			AdjustmentRule rule;
			AdjustmentRule[] zoneRules = null;
			int defaultBaseUtcOffset = timeZoneInformation.TZI.Bias;

			if (String.IsNullOrEmpty (timeZoneInformation.TimeZoneKeyName))
				return null;

			//
			// First get the adjustment rules
			//

			try {
				if (GetDynamicTimeZoneInformationEffectiveYears (ref timeZoneInformation, out firstYear, out lastYear) != 0) {
					firstYear = lastYear = 0;
				}
			} catch {
				// If we don't have GetDynamicTimeZoneInformationEffectiveYears()
				firstYear = lastYear = 0;
			}

			if (firstYear == lastYear) {
				rule = CreateAdjustmentRuleFromTimeZoneInformation (ref timeZoneInformation, DateTime.MinValue.Date, DateTime.MaxValue.Date, defaultBaseUtcOffset);
				if (rule != null)
					zoneRules = new AdjustmentRule [1] { rule };
			} else {
				DYNAMIC_TIME_ZONE_INFORMATION dtzi = default (DYNAMIC_TIME_ZONE_INFORMATION);
				List<AdjustmentRule> rules = new List<AdjustmentRule> ();
				//
				// First rule
				//

				if (!GetTimeZoneInformationForYear ((ushort) firstYear, ref timeZoneInformation, out dtzi.TZI))
					return null;
				rule = CreateAdjustmentRuleFromTimeZoneInformation (ref dtzi, DateTime.MinValue.Date, new DateTime ((int) firstYear, 12, 31), defaultBaseUtcOffset);
				if (rule != null)
					rules.Add (rule);

				for (uint i = firstYear + 1; i < lastYear; i++) {
					if (!GetTimeZoneInformationForYear ((ushort) i, ref timeZoneInformation, out dtzi.TZI))
						return null;
					rule = CreateAdjustmentRuleFromTimeZoneInformation (ref dtzi, new DateTime ((int) i, 1, 1), new DateTime ((int) i, 12, 31), defaultBaseUtcOffset);
					if (rule != null)
						rules.Add (rule);
				}

				//
				// Last rule
				//

				if (!GetTimeZoneInformationForYear ((ushort) lastYear, ref timeZoneInformation, out dtzi.TZI))
					return null;
				rule = CreateAdjustmentRuleFromTimeZoneInformation (ref dtzi, new DateTime ((int) lastYear, 1, 1), DateTime.MaxValue.Date, defaultBaseUtcOffset);
				if (rule != null)
					rules.Add (rule);

				if (rules.Count > 0)
					zoneRules = rules.ToArray ();
			}

			return new TimeZoneInfo (
				timeZoneInformation.TimeZoneKeyName,
				new TimeSpan (0, -(timeZoneInformation.TZI.Bias), 0),
				timeZoneInformation.TZI.StandardName,   // we use the display name as the standared names
				timeZoneInformation.TZI.StandardName,
				timeZoneInformation.TZI.DaylightName,
				zoneRules,
				false);
		}

		internal static TimeZoneInfo GetLocalTimeZoneInfoWinRTFallback ()
		{
			try {
				DYNAMIC_TIME_ZONE_INFORMATION dtzi;
				var result = GetDynamicTimeZoneInformation (out dtzi);
				if (result == TIME_ZONE_ID_INVALID)
					return Utc;
				TimeZoneInfo timeZoneInfo = TryCreateTimeZone (dtzi);
				return timeZoneInfo != null ? timeZoneInfo : Utc;
			} catch {
				return Utc;
			}
		}

		internal static TimeZoneInfo FindSystemTimeZoneByIdWinRTFallback (string id)
		{
			foreach (var tzi in GetSystemTimeZones ()) {
				if (String.Compare (id, tzi.Id, StringComparison.Ordinal) == 0)
					return tzi;
			}

			throw new TimeZoneNotFoundException ();
		}

		internal static List<TimeZoneInfo> GetSystemTimeZonesWinRTFallback ()
		{
			var result = new List<TimeZoneInfo> ();
			try {
				uint index = 0;
				DYNAMIC_TIME_ZONE_INFORMATION dtzi;
				while (EnumDynamicTimeZoneInformation (index++, out dtzi) != ERROR_NO_MORE_ITEMS) {
					var timeZoneInfo = TryCreateTimeZone (dtzi);
					if (timeZoneInfo != null)
						result.Add (timeZoneInfo);
				}
			} catch {
				// EnumDynamicTimeZoneInformation() might not be available.
			}

			if (result.Count == 0) {
				result.Add (Local);
				result.Add (Utc);
			}

			result.Sort ((x, y) =>
			{
				int comparison = x.BaseUtcOffset.CompareTo(y.BaseUtcOffset);
				return comparison == 0 ? string.CompareOrdinal(x.DisplayName, y.DisplayName) : comparison;
			});

			return result;
		}
	}
}

#endif // !FULL_AOT_DESKTOP || WIN_PLATFORM