From 3b2715e14a1a6a4726b557ac37246f91182517cd Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sat, 13 Apr 2019 17:17:04 +0200 Subject: [PATCH] Move DateTime Windows implementation to shared CoreLib (#23888) * Move DateTime Windows implementation to shared CoreLib * Make all the BOOL/struct marshalling explicit * internal -> private * Use unsafe code on all layers, remove unnecessary fixed blocks --- .../System.Private.CoreLib.csproj | 2 +- .../Kernel32/Interop.FileTimeToSystemTime.cs | 15 ++ .../Kernel32/Interop.GetProcessInformation.cs | 23 +++ .../Windows/Kernel32/Interop.GetSystemTime.cs | 15 ++ .../Kernel32/Interop.GetSystemTimeAsFileTime.cs | 15 ++ .../Interop.GetSystemTimePreciseAsFileTime.cs | 15 ++ .../Kernel32/Interop.SystemTimeToFileTime.cs | 14 ++ .../Interop.TzSpecificLocalTimeToSystemTime.cs | 18 ++ ...onFile.cs => Interop.NtQueryInformationFile.cs} | 0 .../shared/System.Private.CoreLib.Shared.projitems | 22 ++- .../shared/System/DateTime.Unix.cs | 4 +- .../shared/System/DateTime.Win32.cs | 23 +++ .../shared/System/DateTime.WinRT.cs | 27 +++ .../shared/System/DateTime.Windows.cs | 216 +++++++++++++++++++++ .../src/System/DateTime.Windows.CoreCLR.cs | 27 +++ .../src/System/DateTime.Windows.cs | 138 ------------- 16 files changed, 427 insertions(+), 147 deletions(-) create mode 100644 src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FileTimeToSystemTime.cs create mode 100644 src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetProcessInformation.cs create mode 100644 src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTime.cs create mode 100644 src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimeAsFileTime.cs create mode 100644 src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimePreciseAsFileTime.cs create mode 100644 src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.SystemTimeToFileTime.cs create mode 100644 src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TzSpecificLocalTimeToSystemTime.cs rename src/System.Private.CoreLib/shared/Interop/Windows/NtDll/{NtQueryInformationFile.cs => Interop.NtQueryInformationFile.cs} (100%) create mode 100644 src/System.Private.CoreLib/shared/System/DateTime.Win32.cs create mode 100644 src/System.Private.CoreLib/shared/System/DateTime.WinRT.cs create mode 100644 src/System.Private.CoreLib/shared/System/DateTime.Windows.cs create mode 100644 src/System.Private.CoreLib/src/System/DateTime.Windows.CoreCLR.cs delete mode 100644 src/System.Private.CoreLib/src/System/DateTime.Windows.cs diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 2339297..f7df61d 100644 --- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -369,7 +369,7 @@ - + diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FileTimeToSystemTime.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FileTimeToSystemTime.cs new file mode 100644 index 0000000..067ee71 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FileTimeToSystemTime.cs @@ -0,0 +1,15 @@ +// 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. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static unsafe extern Interop.BOOL FileTimeToSystemTime(long* lpFileTime, Interop.Kernel32.SYSTEMTIME* lpSystemTime); + } +} diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetProcessInformation.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetProcessInformation.cs new file mode 100644 index 0000000..22e0569 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetProcessInformation.cs @@ -0,0 +1,23 @@ +// 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. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + internal const int ProcessLeapSecondInfo = 8; + + internal struct PROCESS_LEAP_SECOND_INFO + { + public uint Flags; + public uint Reserved; + } + + [DllImport(Libraries.Kernel32)] + internal static unsafe extern Interop.BOOL GetProcessInformation(IntPtr hProcess, int ProcessInformationClass, void* ProcessInformation, int ProcessInformationSize); + } +} diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTime.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTime.cs new file mode 100644 index 0000000..710db5e --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTime.cs @@ -0,0 +1,15 @@ +// 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. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static unsafe extern void GetSystemTime(Interop.Kernel32.SYSTEMTIME* lpSystemTime); + } +} diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimeAsFileTime.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimeAsFileTime.cs new file mode 100644 index 0000000..e2dcd90 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimeAsFileTime.cs @@ -0,0 +1,15 @@ +// 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. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static unsafe extern void GetSystemTimeAsFileTime(long* lpSystemTimeAsFileTime); + } +} diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimePreciseAsFileTime.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimePreciseAsFileTime.cs new file mode 100644 index 0000000..e326279 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimePreciseAsFileTime.cs @@ -0,0 +1,15 @@ +// 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. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static unsafe extern void GetSystemTimePreciseAsFileTime(long* lpSystemTimeAsFileTime); + } +} diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.SystemTimeToFileTime.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.SystemTimeToFileTime.cs new file mode 100644 index 0000000..43db7b4 --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.SystemTimeToFileTime.cs @@ -0,0 +1,14 @@ +// 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. + +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static unsafe extern Interop.BOOL SystemTimeToFileTime(Interop.Kernel32.SYSTEMTIME* lpSystemTime, long* lpFileTime); + } +} diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TzSpecificLocalTimeToSystemTime.cs b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TzSpecificLocalTimeToSystemTime.cs new file mode 100644 index 0000000..2cca7fa --- /dev/null +++ b/src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TzSpecificLocalTimeToSystemTime.cs @@ -0,0 +1,18 @@ +// 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. + +using System; +using System.Runtime.InteropServices; + +internal partial class Interop +{ + internal partial class Kernel32 + { + [DllImport(Libraries.Kernel32)] + internal static unsafe extern Interop.BOOL TzSpecificLocalTimeToSystemTime( + IntPtr lpTimeZoneInformation, + Interop.Kernel32.SYSTEMTIME* lpLocalTime, + Interop.Kernel32.SYSTEMTIME* lpUniversalTime); + } +} diff --git a/src/System.Private.CoreLib/shared/Interop/Windows/NtDll/NtQueryInformationFile.cs b/src/System.Private.CoreLib/shared/Interop/Windows/NtDll/Interop.NtQueryInformationFile.cs similarity index 100% rename from src/System.Private.CoreLib/shared/Interop/Windows/NtDll/NtQueryInformationFile.cs rename to src/System.Private.CoreLib/shared/Interop/Windows/NtDll/Interop.NtQueryInformationFile.cs diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems index 02656f5..cb7fec8 100644 --- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems +++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems @@ -1005,6 +1005,7 @@ + @@ -1023,6 +1024,9 @@ + + + @@ -1048,8 +1052,10 @@ + + @@ -1057,7 +1063,6 @@ - @@ -1065,17 +1070,14 @@ + - - - - @@ -1125,20 +1127,28 @@ - + + + + + + + + + diff --git a/src/System.Private.CoreLib/shared/System/DateTime.Unix.cs b/src/System.Private.CoreLib/shared/System/DateTime.Unix.cs index 6cf0181..2c4de3e 100644 --- a/src/System.Private.CoreLib/shared/System/DateTime.Unix.cs +++ b/src/System.Private.CoreLib/shared/System/DateTime.Unix.cs @@ -18,8 +18,8 @@ namespace System } #endif - internal static DateTime FromFileTimeLeapSecondsAware(long fileTime) => default; - internal static long ToFileTimeLeapSecondsAware(long ticks) => default; + private static DateTime FromFileTimeLeapSecondsAware(long fileTime) => default; + private static long ToFileTimeLeapSecondsAware(long ticks) => default; // IsValidTimeWithLeapSeconds is not expected to be called at all for now on non-Windows platforms internal static bool IsValidTimeWithLeapSeconds(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) => false; diff --git a/src/System.Private.CoreLib/shared/System/DateTime.Win32.cs b/src/System.Private.CoreLib/shared/System/DateTime.Win32.cs new file mode 100644 index 0000000..d742c89 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/DateTime.Win32.cs @@ -0,0 +1,23 @@ +// 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. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public readonly partial struct DateTime + { + private static unsafe bool SystemSupportsLeapSeconds() + { + Interop.NtDll.SYSTEM_LEAP_SECOND_INFORMATION slsi; + + return Interop.NtDll.NtQuerySystemInformation( + Interop.NtDll.SystemLeapSecondInformation, + (void *) &slsi, + sizeof(Interop.NtDll.SYSTEM_LEAP_SECOND_INFORMATION), + null) == 0 && slsi.Enabled; + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/DateTime.WinRT.cs b/src/System.Private.CoreLib/shared/System/DateTime.WinRT.cs new file mode 100644 index 0000000..30a9a61 --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/DateTime.WinRT.cs @@ -0,0 +1,27 @@ +// 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. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public readonly partial struct DateTime + { + private static unsafe bool SystemSupportsLeapSeconds() + { + Interop.Kernel32.PROCESS_LEAP_SECOND_INFO info; + + // Store apps don't have access to an API that would let us find out whether leap seconds have been + // disabled by policy: this implementation will produce slightly different results from what + // we have for Win32. If GetProcessInformation succeeds, we have to act as if leap seconds existed. + // They could still have been disabled by policy, but we have no way to check for that. + return Interop.Kernel32.GetProcessInformation( + Interop.Kernel32.GetCurrentProcess(), + Interop.Kernel32.ProcessLeapSecondInfo, + &info, + sizeof(Interop.Kernel32.PROCESS_LEAP_SECOND_INFO)) != Interop.BOOL.FALSE; + } + } +} diff --git a/src/System.Private.CoreLib/shared/System/DateTime.Windows.cs b/src/System.Private.CoreLib/shared/System/DateTime.Windows.cs new file mode 100644 index 0000000..ba9df5c --- /dev/null +++ b/src/System.Private.CoreLib/shared/System/DateTime.Windows.cs @@ -0,0 +1,216 @@ +// 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. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public readonly partial struct DateTime + { + internal static readonly bool s_systemSupportsLeapSeconds = SystemSupportsLeapSeconds(); + + public static unsafe DateTime UtcNow + { + get + { + if (s_systemSupportsLeapSeconds) + { + FullSystemTime time; + GetSystemTimeWithLeapSecondsHandling(&time); + return CreateDateTimeFromSystemTime(in time); + } + + return new DateTime(((ulong)(GetSystemTimeAsFileTime() + FileTimeOffset)) | KindUtc); + } + } + + internal static unsafe bool IsValidTimeWithLeapSeconds(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) + { + DateTime dt = new DateTime(year, month, day); + FullSystemTime time = new FullSystemTime(year, month, dt.DayOfWeek, day, hour, minute, second); + + switch (kind) + { + case DateTimeKind.Local: return ValidateSystemTime(&time.systemTime, localTime: true); + case DateTimeKind.Utc: return ValidateSystemTime(&time.systemTime, localTime: false); + default: + return ValidateSystemTime(&time.systemTime, localTime: true) || ValidateSystemTime(&time.systemTime, localTime: false); + } + } + + private static unsafe DateTime FromFileTimeLeapSecondsAware(long fileTime) + { + FullSystemTime time; + if (FileTimeToSystemTime(fileTime, &time)) + { + return CreateDateTimeFromSystemTime(in time); + } + + throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_DateTimeBadTicks); + } + + private static unsafe long ToFileTimeLeapSecondsAware(long ticks) + { + FullSystemTime time = new FullSystemTime(ticks); + long fileTime; + + if (SystemTimeToFileTime(&time.systemTime, &fileTime)) + { + return fileTime + ticks % TicksPerMillisecond; + } + + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static DateTime CreateDateTimeFromSystemTime(in FullSystemTime time) + { + long ticks = DateToTicks(time.systemTime.Year, time.systemTime.Month, time.systemTime.Day); + ticks += TimeToTicks(time.systemTime.Hour, time.systemTime.Minute, time.systemTime.Second); + ticks += time.systemTime.Milliseconds * TicksPerMillisecond; + ticks += time.hundredNanoSecond; + return new DateTime( ((UInt64)(ticks)) | KindUtc); + } + + // FullSystemTime struct is the SYSTEMTIME struct with extra hundredNanoSecond field to store more precise time. + [StructLayout(LayoutKind.Sequential)] + private struct FullSystemTime + { + internal Interop.Kernel32.SYSTEMTIME systemTime; + internal long hundredNanoSecond; + + internal FullSystemTime(int year, int month, DayOfWeek dayOfWeek, int day, int hour, int minute, int second) + { + systemTime.Year = (ushort) year; + systemTime.Month = (ushort) month; + systemTime.DayOfWeek = (ushort) dayOfWeek; + systemTime.Day = (ushort) day; + systemTime.Hour = (ushort) hour; + systemTime.Minute = (ushort) minute; + systemTime.Second = (ushort) second; + systemTime.Milliseconds = 0; + hundredNanoSecond = 0; + } + + internal FullSystemTime(long ticks) + { + DateTime dt = new DateTime(ticks); + + int year, month, day; + dt.GetDatePart(out year, out month, out day); + + systemTime.Year = (ushort) year; + systemTime.Month = (ushort) month; + systemTime.DayOfWeek = (ushort) dt.DayOfWeek; + systemTime.Day = (ushort) day; + systemTime.Hour = (ushort) dt.Hour; + systemTime.Minute = (ushort) dt.Minute; + systemTime.Second = (ushort) dt.Second; + systemTime.Milliseconds = (ushort) dt.Millisecond; + hundredNanoSecond = 0; + } + }; + +#if !CORECLR + internal static readonly bool s_systemSupportsPreciseSystemTime = SystemSupportsPreciseSystemTime(); + + private static unsafe bool SystemSupportsPreciseSystemTime() + { + if (Environment.IsWindows8OrAbove) + { + // GetSystemTimePreciseAsFileTime exists and we'd like to use it. However, on + // misconfigured systems, it's possible for the "precise" time to be inaccurate: + // https://github.com/dotnet/coreclr/issues/14187 + // If it's inaccurate, though, we expect it to be wildly inaccurate, so as a + // workaround/heuristic, we get both the "normal" and "precise" times, and as + // long as they're close, we use the precise one. This workaround can be removed + // when we better understand what's causing the drift and the issue is no longer + // a problem or can be better worked around on all targeted OSes. + + long systemTimeResult; + Interop.Kernel32.GetSystemTimeAsFileTime(&systemTimeResult); + + long preciseSystemTimeResult; + Interop.Kernel32.GetSystemTimePreciseAsFileTime(&preciseSystemTimeResult); + + return Math.Abs(preciseSystemTimeResult - systemTimeResult) <= 100 * TicksPerMillisecond; + } + + return false; + } + + private static unsafe bool ValidateSystemTime(Interop.Kernel32.SYSTEMTIME* time, bool localTime) + { + if (localTime) + { + Interop.Kernel32.SYSTEMTIME st; + return Interop.Kernel32.TzSpecificLocalTimeToSystemTime(IntPtr.Zero, time, &st) != Interop.BOOL.FALSE; + } + else + { + long timestamp; + return Interop.Kernel32.SystemTimeToFileTime(time, ×tamp) != Interop.BOOL.FALSE; + } + } + + private static unsafe bool FileTimeToSystemTime(long fileTime, FullSystemTime* time) + { + if (Interop.Kernel32.FileTimeToSystemTime(&fileTime, &time->systemTime) != Interop.BOOL.FALSE) + { + // to keep the time precision + time->hundredNanoSecond = fileTime % TicksPerMillisecond; + if (time->systemTime.Second > 59) + { + // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation. + // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds + time->systemTime.Second = 59; + time->systemTime.Milliseconds = 999; + time->hundredNanoSecond = 9999; + } + return true; + } + return false; + } + + private static unsafe void GetSystemTimeWithLeapSecondsHandling(FullSystemTime* time) + { + if (!FileTimeToSystemTime(GetSystemTimeAsFileTime(), time)) + { + Interop.Kernel32.GetSystemTime(&time->systemTime); + time->hundredNanoSecond = 0; + if (time->systemTime.Second > 59) + { + // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation. + // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds + time->systemTime.Second = 59; + time->systemTime.Milliseconds = 999; + time->hundredNanoSecond = 9999; + } + } + } + + private static unsafe bool SystemTimeToFileTime(Interop.Kernel32.SYSTEMTIME* time, long* fileTime) + { + return Interop.Kernel32.SystemTimeToFileTime(time, fileTime) != Interop.BOOL.FALSE; + } + + private static unsafe long GetSystemTimeAsFileTime() + { + long timestamp; + + if (s_systemSupportsPreciseSystemTime) + { + Interop.Kernel32.GetSystemTimePreciseAsFileTime(×tamp); + } + else + { + Interop.Kernel32.GetSystemTimeAsFileTime(×tamp); + } + + return timestamp; + } +#endif + } +} diff --git a/src/System.Private.CoreLib/src/System/DateTime.Windows.CoreCLR.cs b/src/System.Private.CoreLib/src/System/DateTime.Windows.CoreCLR.cs new file mode 100644 index 0000000..5597c13 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/DateTime.Windows.CoreCLR.cs @@ -0,0 +1,27 @@ +// 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. + +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public readonly partial struct DateTime + { + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static unsafe extern bool ValidateSystemTime(Interop.Kernel32.SYSTEMTIME* time, bool localTime); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static unsafe extern bool FileTimeToSystemTime(long fileTime, FullSystemTime* time); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static unsafe extern void GetSystemTimeWithLeapSecondsHandling(FullSystemTime* time); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static unsafe extern bool SystemTimeToFileTime(Interop.Kernel32.SYSTEMTIME* time, long* fileTime); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + private static unsafe extern long GetSystemTimeAsFileTime(); + } +} diff --git a/src/System.Private.CoreLib/src/System/DateTime.Windows.cs b/src/System.Private.CoreLib/src/System/DateTime.Windows.cs deleted file mode 100644 index 9d0d0cd..0000000 --- a/src/System.Private.CoreLib/src/System/DateTime.Windows.cs +++ /dev/null @@ -1,138 +0,0 @@ -// 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. - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace System -{ - public readonly partial struct DateTime - { - internal static readonly bool s_systemSupportsLeapSeconds = SystemSupportsLeapSeconds(); - - public static DateTime UtcNow - { - get - { - if (s_systemSupportsLeapSeconds) - { - GetSystemTimeWithLeapSecondsHandling(out FullSystemTime time); - return CreateDateTimeFromSystemTime(in time); - } - - return new DateTime(((ulong)(GetSystemTimeAsFileTime() + FileTimeOffset)) | KindUtc); - } - } - - internal static bool IsValidTimeWithLeapSeconds(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind) - { - DateTime dt = new DateTime(year, month, day); - FullSystemTime time = new FullSystemTime(year, month, dt.DayOfWeek, day, hour, minute, second); - - switch (kind) - { - case DateTimeKind.Local: return ValidateSystemTime(in time.systemTime, localTime: true); - case DateTimeKind.Utc: return ValidateSystemTime(in time.systemTime, localTime: false); - default: - return ValidateSystemTime(in time.systemTime, localTime: true) || ValidateSystemTime(in time.systemTime, localTime: false); - } - } - - internal static DateTime FromFileTimeLeapSecondsAware(long fileTime) - { - if (FileTimeToSystemTime(fileTime, out FullSystemTime time)) - { - return CreateDateTimeFromSystemTime(in time); - } - - throw new ArgumentOutOfRangeException("fileTime", SR.ArgumentOutOfRange_DateTimeBadTicks); - } - - internal static long ToFileTimeLeapSecondsAware(long ticks) - { - FullSystemTime time = new FullSystemTime(ticks); - if (SystemTimeToFileTime(in time.systemTime, out long fileTime)) - { - return fileTime + ticks % TicksPerMillisecond; - } - - throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static DateTime CreateDateTimeFromSystemTime(in FullSystemTime time) - { - long ticks = DateToTicks(time.systemTime.Year, time.systemTime.Month, time.systemTime.Day); - ticks += TimeToTicks(time.systemTime.Hour, time.systemTime.Minute, time.systemTime.Second); - ticks += time.systemTime.Milliseconds * TicksPerMillisecond; - ticks += time.hundredNanoSecond; - return new DateTime( ((UInt64)(ticks)) | KindUtc); - } - - private static unsafe bool SystemSupportsLeapSeconds() - { - Interop.NtDll.SYSTEM_LEAP_SECOND_INFORMATION slsi; - - return Interop.NtDll.NtQuerySystemInformation( - Interop.NtDll.SystemLeapSecondInformation, - (void *) &slsi, - sizeof(Interop.NtDll.SYSTEM_LEAP_SECOND_INFORMATION), - null) == 0 && slsi.Enabled; - } - - // FullSystemTime struct is the SYSTEMTIME struct with extra hundredNanoSecond field to store more precise time. - [StructLayout(LayoutKind.Sequential)] - internal struct FullSystemTime - { - internal Interop.Kernel32.SYSTEMTIME systemTime; - internal long hundredNanoSecond; - - internal FullSystemTime(int year, int month, DayOfWeek dayOfWeek, int day, int hour, int minute, int second) - { - systemTime.Year = (ushort) year; - systemTime.Month = (ushort) month; - systemTime.DayOfWeek = (ushort) dayOfWeek; - systemTime.Day = (ushort) day; - systemTime.Hour = (ushort) hour; - systemTime.Minute = (ushort) minute; - systemTime.Second = (ushort) second; - systemTime.Milliseconds = 0; - hundredNanoSecond = 0; - } - - internal FullSystemTime(long ticks) - { - DateTime dt = new DateTime(ticks); - - int year, month, day; - dt.GetDatePart(out year, out month, out day); - - systemTime.Year = (ushort) year; - systemTime.Month = (ushort) month; - systemTime.DayOfWeek = (ushort) dt.DayOfWeek; - systemTime.Day = (ushort) day; - systemTime.Hour = (ushort) dt.Hour; - systemTime.Minute = (ushort) dt.Minute; - systemTime.Second = (ushort) dt.Second; - systemTime.Milliseconds = (ushort) dt.Millisecond; - hundredNanoSecond = 0; - } - }; - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern bool ValidateSystemTime(in Interop.Kernel32.SYSTEMTIME time, bool localTime); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern bool FileTimeToSystemTime(long fileTime, out FullSystemTime time); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern void GetSystemTimeWithLeapSecondsHandling(out FullSystemTime time); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern bool SystemTimeToFileTime(in Interop.Kernel32.SYSTEMTIME time, out long fileTime); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern long GetSystemTimeAsFileTime(); - } -} -- 2.7.4