DateTime.Windows.cs 8.9 KB

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