linux-packaging-mono/mcs/class/corlib/System/TimeZoneInfo.WinRT.cs
Xamarin Public Jenkins (auto-signing) e46a49ecf1 Imported Upstream version 5.10.0.47
Former-commit-id: d0813289fa2d35e1f8ed77530acb4fb1df441bc0
2018-01-24 17:04:36 +00:00

349 lines
12 KiB
C#
Executable File

// 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);
return result;
}
}
}
#endif // !FULL_AOT_DESKTOP || WIN_PLATFORM