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
|