DateTime.Windows.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Runtime.CompilerServices;
  5. using System.Runtime.InteropServices;
  6. namespace System
  7. {
  8. public readonly partial struct DateTime
  9. {
  10. internal static readonly bool s_systemSupportsLeapSeconds = SystemSupportsLeapSeconds();
  11. public static unsafe DateTime UtcNow
  12. {
  13. get
  14. {
  15. if (s_systemSupportsLeapSeconds)
  16. {
  17. FullSystemTime time;
  18. GetSystemTimeWithLeapSecondsHandling(&time);
  19. return CreateDateTimeFromSystemTime(in time);
  20. }
  21. return new DateTime(((ulong)(GetSystemTimeAsFileTime() + FileTimeOffset)) | KindUtc);
  22. }
  23. }
  24. internal static unsafe bool IsValidTimeWithLeapSeconds(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
  25. {
  26. DateTime dt = new DateTime(year, month, day);
  27. FullSystemTime time = new FullSystemTime(year, month, dt.DayOfWeek, day, hour, minute, second);
  28. return kind switch
  29. {
  30. DateTimeKind.Local => ValidateSystemTime(&time.systemTime, localTime: true),
  31. DateTimeKind.Utc => ValidateSystemTime(&time.systemTime, localTime: false),
  32. _ => ValidateSystemTime(&time.systemTime, localTime: true) || ValidateSystemTime(&time.systemTime, localTime: false),
  33. };
  34. }
  35. private static unsafe DateTime FromFileTimeLeapSecondsAware(long fileTime)
  36. {
  37. FullSystemTime time;
  38. if (FileTimeToSystemTime(fileTime, &time))
  39. {
  40. return CreateDateTimeFromSystemTime(in time);
  41. }
  42. throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_DateTimeBadTicks);
  43. }
  44. private static unsafe long ToFileTimeLeapSecondsAware(long ticks)
  45. {
  46. FullSystemTime time = new FullSystemTime(ticks);
  47. long fileTime;
  48. if (SystemTimeToFileTime(&time.systemTime, &fileTime))
  49. {
  50. return fileTime + ticks % TicksPerMillisecond;
  51. }
  52. throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid);
  53. }
  54. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  55. private static DateTime CreateDateTimeFromSystemTime(in FullSystemTime time)
  56. {
  57. long ticks = DateToTicks(time.systemTime.Year, time.systemTime.Month, time.systemTime.Day);
  58. ticks += TimeToTicks(time.systemTime.Hour, time.systemTime.Minute, time.systemTime.Second);
  59. ticks += time.systemTime.Milliseconds * TicksPerMillisecond;
  60. ticks += time.hundredNanoSecond;
  61. return new DateTime(((ulong)(ticks)) | KindUtc);
  62. }
  63. // FullSystemTime struct is the SYSTEMTIME struct with extra hundredNanoSecond field to store more precise time.
  64. [StructLayout(LayoutKind.Sequential)]
  65. private struct FullSystemTime
  66. {
  67. internal Interop.Kernel32.SYSTEMTIME systemTime;
  68. internal long hundredNanoSecond;
  69. internal FullSystemTime(int year, int month, DayOfWeek dayOfWeek, int day, int hour, int minute, int second)
  70. {
  71. systemTime.Year = (ushort)year;
  72. systemTime.Month = (ushort)month;
  73. systemTime.DayOfWeek = (ushort)dayOfWeek;
  74. systemTime.Day = (ushort)day;
  75. systemTime.Hour = (ushort)hour;
  76. systemTime.Minute = (ushort)minute;
  77. systemTime.Second = (ushort)second;
  78. systemTime.Milliseconds = 0;
  79. hundredNanoSecond = 0;
  80. }
  81. internal FullSystemTime(long ticks)
  82. {
  83. DateTime dt = new DateTime(ticks);
  84. int year, month, day;
  85. dt.GetDatePart(out year, out month, out day);
  86. systemTime.Year = (ushort)year;
  87. systemTime.Month = (ushort)month;
  88. systemTime.DayOfWeek = (ushort)dt.DayOfWeek;
  89. systemTime.Day = (ushort)day;
  90. systemTime.Hour = (ushort)dt.Hour;
  91. systemTime.Minute = (ushort)dt.Minute;
  92. systemTime.Second = (ushort)dt.Second;
  93. systemTime.Milliseconds = (ushort)dt.Millisecond;
  94. hundredNanoSecond = 0;
  95. }
  96. }
  97. #if !CORECLR
  98. internal static readonly bool s_systemSupportsPreciseSystemTime = SystemSupportsPreciseSystemTime();
  99. private static unsafe bool SystemSupportsPreciseSystemTime()
  100. {
  101. if (Environment.IsWindows8OrAbove)
  102. {
  103. // GetSystemTimePreciseAsFileTime exists and we'd like to use it. However, on
  104. // misconfigured systems, it's possible for the "precise" time to be inaccurate:
  105. // https://github.com/dotnet/coreclr/issues/14187
  106. // If it's inaccurate, though, we expect it to be wildly inaccurate, so as a
  107. // workaround/heuristic, we get both the "normal" and "precise" times, and as
  108. // long as they're close, we use the precise one. This workaround can be removed
  109. // when we better understand what's causing the drift and the issue is no longer
  110. // a problem or can be better worked around on all targeted OSes.
  111. long systemTimeResult;
  112. Interop.Kernel32.GetSystemTimeAsFileTime(&systemTimeResult);
  113. long preciseSystemTimeResult;
  114. Interop.Kernel32.GetSystemTimePreciseAsFileTime(&preciseSystemTimeResult);
  115. return Math.Abs(preciseSystemTimeResult - systemTimeResult) <= 100 * TicksPerMillisecond;
  116. }
  117. return false;
  118. }
  119. private static unsafe bool ValidateSystemTime(Interop.Kernel32.SYSTEMTIME* time, bool localTime)
  120. {
  121. if (localTime)
  122. {
  123. Interop.Kernel32.SYSTEMTIME st;
  124. return Interop.Kernel32.TzSpecificLocalTimeToSystemTime(IntPtr.Zero, time, &st) != Interop.BOOL.FALSE;
  125. }
  126. else
  127. {
  128. long timestamp;
  129. return Interop.Kernel32.SystemTimeToFileTime(time, &timestamp) != Interop.BOOL.FALSE;
  130. }
  131. }
  132. private static unsafe bool FileTimeToSystemTime(long fileTime, FullSystemTime* time)
  133. {
  134. if (Interop.Kernel32.FileTimeToSystemTime(&fileTime, &time->systemTime) != Interop.BOOL.FALSE)
  135. {
  136. // to keep the time precision
  137. time->hundredNanoSecond = fileTime % TicksPerMillisecond;
  138. if (time->systemTime.Second > 59)
  139. {
  140. // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation.
  141. // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds
  142. time->systemTime.Second = 59;
  143. time->systemTime.Milliseconds = 999;
  144. time->hundredNanoSecond = 9999;
  145. }
  146. return true;
  147. }
  148. return false;
  149. }
  150. private static unsafe void GetSystemTimeWithLeapSecondsHandling(FullSystemTime* time)
  151. {
  152. if (!FileTimeToSystemTime(GetSystemTimeAsFileTime(), time))
  153. {
  154. Interop.Kernel32.GetSystemTime(&time->systemTime);
  155. time->hundredNanoSecond = 0;
  156. if (time->systemTime.Second > 59)
  157. {
  158. // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation.
  159. // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds
  160. time->systemTime.Second = 59;
  161. time->systemTime.Milliseconds = 999;
  162. time->hundredNanoSecond = 9999;
  163. }
  164. }
  165. }
  166. private static unsafe bool SystemTimeToFileTime(Interop.Kernel32.SYSTEMTIME* time, long* fileTime)
  167. {
  168. return Interop.Kernel32.SystemTimeToFileTime(time, fileTime) != Interop.BOOL.FALSE;
  169. }
  170. private static unsafe long GetSystemTimeAsFileTime()
  171. {
  172. long timestamp;
  173. if (s_systemSupportsPreciseSystemTime)
  174. {
  175. Interop.Kernel32.GetSystemTimePreciseAsFileTime(&timestamp);
  176. }
  177. else
  178. {
  179. Interop.Kernel32.GetSystemTimeAsFileTime(&timestamp);
  180. }
  181. return timestamp;
  182. }
  183. #endif
  184. }
  185. }