Move DateTime Windows implementation to shared CoreLib (#23888)
authorFilip Navara <filip.navara@gmail.com>
Sat, 13 Apr 2019 15:17:04 +0000 (17:17 +0200)
committerJan Kotas <jkotas@microsoft.com>
Sat, 13 Apr 2019 15:17:04 +0000 (08:17 -0700)
* 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

16 files changed:
src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.FileTimeToSystemTime.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetProcessInformation.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTime.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimeAsFileTime.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.GetSystemTimePreciseAsFileTime.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.SystemTimeToFileTime.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.TzSpecificLocalTimeToSystemTime.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/Interop/Windows/NtDll/Interop.NtQueryInformationFile.cs [moved from src/System.Private.CoreLib/shared/Interop/Windows/NtDll/NtQueryInformationFile.cs with 100% similarity]
src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
src/System.Private.CoreLib/shared/System/DateTime.Unix.cs
src/System.Private.CoreLib/shared/System/DateTime.Win32.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/System/DateTime.WinRT.cs [new file with mode: 0644]
src/System.Private.CoreLib/shared/System/DateTime.Windows.cs [new file with mode: 0644]
src/System.Private.CoreLib/src/System/DateTime.Windows.CoreCLR.cs [new file with mode: 0644]
src/System.Private.CoreLib/src/System/DateTime.Windows.cs [deleted file]

index 2339297..f7df61d 100644 (file)
   </ItemGroup>
   <ItemGroup Condition="'$(TargetsWindows)' == 'true'">
     <Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\InMemoryAssemblyLoader.cs" />
-    <Compile Include="$(BclSourcesRoot)\System\DateTime.Windows.cs" />
+    <Compile Include="$(BclSourcesRoot)\System\DateTime.Windows.CoreCLR.cs" />
     <Compile Include="$(BclSourcesRoot)\Interop\Windows\OleAut32\Interop.VariantClear.cs" />
     <Compile Include="$(BclSourcesRoot)\System\ApplicationModel.Windows.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Globalization\GlobalizationMode.Windows.cs" />
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 (file)
index 0000000..067ee71
--- /dev/null
@@ -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 (file)
index 0000000..22e0569
--- /dev/null
@@ -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 (file)
index 0000000..710db5e
--- /dev/null
@@ -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 (file)
index 0000000..e2dcd90
--- /dev/null
@@ -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 (file)
index 0000000..e326279
--- /dev/null
@@ -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 (file)
index 0000000..43db7b4
--- /dev/null
@@ -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 (file)
index 0000000..2cca7fa
--- /dev/null
@@ -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);
+    }
+}
index 02656f5..cb7fec8 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.ExpandEnvironmentStrings.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileAttributes.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FILE_INFO_BY_HANDLE_CLASS.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileTimeToSystemTime.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FileTypes.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FindClose.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.FindFirstFileEx.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetProcessTimes.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemDirectoryW.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemInfo.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemTime.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemTimeAsFileTime.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemTimePreciseAsFileTime.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetSystemTimes.cs" Condition="'$(FeaturePerfTracing)' == 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempFileNameW.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetTempPathW.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetEndOfFile.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetThreadErrorMode.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SetFilePointerEx.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SystemTimeToFileTime.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.SYSTEM_INFO.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.TimeZone.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.TzSpecificLocalTimeToSystemTime.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VirtualAlloc.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VirtualFree.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VirtualQuery.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.WriteFile_SafeHandle_NativeOverlapped.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Normaliz\Interop.Idna.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Normaliz\Interop.Normalization.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\NtDll\Interop.NtQuerySystemInformation.cs" Condition="'$(EnableWinRT)' != 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Ole32\Interop.CoCreateGuid.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Secur32\Interop.GetUserNameExW.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Shell32\Interop.SHGetKnownFolderPath.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFindHandle.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Buffer.Windows.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\DateTime.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\DebugProvider.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureInfo.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CompareInfo.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CultureData.Windows.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Win32.cs" Condition="'$(EnableWinRT)' != 'true'" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\IdnMapping.Windows.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.Win32.cs" Condition="'$(EnableWinRT)' != 'true'" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.WinRT.cs" Condition="'$(EnableWinRT)' == 'true'" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\Normalization.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\TextInfo.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Guid.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.TimeZone.Registry.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VerifyVersionExW.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.VerSetConditionMask.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\NtDll\NtQueryInformationFile.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\NtDll\Interop.NtQuerySystemInformation.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\NtDll\Interop.NtQueryInformationFile.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.Constants.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.LoadString.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.SendMessageTimeout.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeLibraryHandle.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\DateTime.Win32.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Win32.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.Win32.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.Win32.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.Win32.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Win32.cs" />
   </ItemGroup>
   <ItemGroup Condition="$(TargetsWindows) and '$(EnableWinRT)' == 'true'">
+    <Compile Include="$(MSBuildThisFileDirectory)System\DateTime.WinRT.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.WinRT.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\HijriCalendar.WinRT.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Globalization\JapaneseCalendar.WinRT.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IO\FileStream.WinRT.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CreateFile2.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.CREATEFILE2_EXTENDED_PARAMETERS.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Kernel32\Interop.GetProcessInformation.cs" />
   </ItemGroup>
   <ItemGroup Condition="$(TargetsWindows) or '$(FeaturePal)'=='true'">
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeWaitHandle.Windows.cs" />
index 6cf0181..2c4de3e 100644 (file)
@@ -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 (file)
index 0000000..d742c89
--- /dev/null
@@ -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 (file)
index 0000000..30a9a61
--- /dev/null
@@ -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 (file)
index 0000000..ba9df5c
--- /dev/null
@@ -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, &timestamp) != 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(&timestamp);
+            }
+            else
+            {
+                Interop.Kernel32.GetSystemTimeAsFileTime(&timestamp);
+            }
+
+            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 (file)
index 0000000..5597c13
--- /dev/null
@@ -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 (file)
index 9d0d0cd..0000000
+++ /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();
-    }
-}