| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- // 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
- }
- }
|