Skip to content

Commit

Permalink
Optimize System.DateTime
Browse files Browse the repository at this point in the history
  • Loading branch information
pentp committed Jan 25, 2021
1 parent 1a25256 commit e31f845
Show file tree
Hide file tree
Showing 21 changed files with 421 additions and 607 deletions.
6 changes: 0 additions & 6 deletions src/coreclr/classlibnative/bcltype/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@
#include "fcall.h"
#include "qcall.h"

struct FullSystemTime
{
SYSTEMTIME systemTime;
INT64 hundredNanoSecond;
};

class SystemNative
{
friend class DebugStackTrace;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ internal static partial class Kernel32
{
[DllImport(Libraries.Kernel32)]
[SuppressGCTransition]
internal static extern unsafe Interop.BOOL FileTimeToSystemTime(long* lpFileTime, Interop.Kernel32.SYSTEMTIME* lpSystemTime);
internal static extern unsafe Interop.BOOL FileTimeToSystemTime(ulong* lpFileTime, Interop.Kernel32.SYSTEMTIME* lpSystemTime);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ internal static partial class Kernel32
{
[DllImport(Libraries.Kernel32)]
[SuppressGCTransition]
internal static extern unsafe Interop.BOOL SystemTimeToFileTime(Interop.Kernel32.SYSTEMTIME* lpSystemTime, long* lpFileTime);
internal static extern unsafe Interop.BOOL SystemTimeToFileTime(Interop.Kernel32.SYSTEMTIME* lpSystemTime, ulong* lpFileTime);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,6 @@
<data name="Arg_DataMisalignedException" xml:space="preserve">
<value>A datatype misalignment was detected in a load or store instruction.</value>
</data>
<data name="Arg_DateTimeRange" xml:space="preserve">
<value>Combination of arguments to the DateTime constructor is out of the legal range.</value>
</data>
<data name="Arg_DecBitCtor" xml:space="preserve">
<value>Decimal constructor requires an array or span of four valid decimal bytes.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ public static DateTime UtcNow
}
}

private static DateTime FromFileTimeLeapSecondsAware(long fileTime) => default;
private static long ToFileTimeLeapSecondsAware(long ticks) => default;
private static DateTime FromFileTimeLeapSecondsAware(ulong fileTime) => default;
private static ulong 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;
internal static bool IsValidTimeWithLeapSeconds(int year, int month, int day, int hour, int minute, DateTimeKind kind) => false;
}
}
70 changes: 33 additions & 37 deletions src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static unsafe DateTime UtcNow
{
get
{
long fileTime;
ulong fileTime;
s_pfnGetSystemTimeAsFileTime(&fileTime);

if (s_systemSupportsLeapSeconds)
Expand All @@ -25,36 +25,27 @@ public static unsafe DateTime UtcNow
if (Interop.Kernel32.FileTimeToSystemTime(&fileTime, &time.systemTime) != Interop.BOOL.FALSE)
{
// to keep the time precision
time.hundredNanoSecond = fileTime % 10000; // 10000 is the number of 100-nano seconds per Millisecond
time.hundredNanoSecond = (uint)(fileTime % 10000); // 10000 is the number of 100-nano seconds per Millisecond
}
else
{
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;
}

return CreateDateTimeFromSystemTime(in time);
}
else
{
return new DateTime(((ulong)(fileTime + FileTimeOffset)) | KindUtc);
return new DateTime(fileTime + FileTimeOffset | KindUtc);
}
}
}

internal static unsafe bool IsValidTimeWithLeapSeconds(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
internal static unsafe bool IsValidTimeWithLeapSeconds(int year, int month, int day, int hour, int minute, DateTimeKind kind)
{
DateTime dt = new DateTime(year, month, day);
FullSystemTime time = new FullSystemTime(year, month, dt.DayOfWeek, day, hour, minute, second);
FullSystemTime time = new FullSystemTime(year, month, dt.DayOfWeek, day, hour, minute, 60);

if (kind != DateTimeKind.Utc)
{
Expand All @@ -65,15 +56,15 @@ internal static unsafe bool IsValidTimeWithLeapSeconds(int year, int month, int

if (kind != DateTimeKind.Local)
{
long ft;
ulong ft;
if (Interop.Kernel32.SystemTimeToFileTime(&time.systemTime, &ft) != Interop.BOOL.FALSE)
return true;
}

return false;
}

private static unsafe DateTime FromFileTimeLeapSecondsAware(long fileTime)
private static unsafe DateTime FromFileTimeLeapSecondsAware(ulong fileTime)
{
FullSystemTime time;
if (Interop.Kernel32.FileTimeToSystemTime(&fileTime, &time.systemTime) == Interop.BOOL.FALSE)
Expand All @@ -82,47 +73,52 @@ private static unsafe DateTime FromFileTimeLeapSecondsAware(long fileTime)
}

// 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;
}
time.hundredNanoSecond = (uint)(fileTime % TicksPerMillisecond);
return CreateDateTimeFromSystemTime(in time);
}

private static unsafe long ToFileTimeLeapSecondsAware(long ticks)
private static unsafe ulong ToFileTimeLeapSecondsAware(long ticks)
{
FullSystemTime time = new FullSystemTime(ticks);
long fileTime;
ulong fileTime;

if (Interop.Kernel32.SystemTimeToFileTime(&time.systemTime, &fileTime) == Interop.BOOL.FALSE)
{
throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid);
}

return fileTime + ticks % TicksPerMillisecond;
return fileTime + (ulong)ticks % TicksPerMillisecond;
}

[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(((ulong)(ticks)) | KindUtc);
uint year = time.systemTime.Year;
uint[] days = IsLeapYear((int)year) ? s_daysToMonth366 : s_daysToMonth365;
uint n = DaysToYear(year) + days[time.systemTime.Month - 1] + time.systemTime.Day - 1;
ulong ticks = n * (ulong)TicksPerDay;

ticks += time.systemTime.Hour * (ulong)TicksPerHour;
ticks += time.systemTime.Minute * (ulong)TicksPerMinute;
uint second = time.systemTime.Second;
if (second <= 59)
{
ulong tmp = second * (uint)TicksPerSecond + time.systemTime.Milliseconds * (uint)TicksPerMillisecond + time.hundredNanoSecond;
return new DateTime(ticks + tmp | KindUtc);
}

// 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
ticks += TicksPerMinute - 1 | KindUtc;
return new DateTime(ticks);
}

// 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 uint hundredNanoSecond;

internal FullSystemTime(int year, int month, DayOfWeek dayOfWeek, int day, int hour, int minute, int second)
{
Expand Down Expand Up @@ -156,9 +152,9 @@ internal FullSystemTime(long ticks)
}
}

private static unsafe readonly delegate* unmanaged[SuppressGCTransition]<long*, void> s_pfnGetSystemTimeAsFileTime = GetGetSystemTimeAsFileTimeFnPtr();
private static unsafe readonly delegate* unmanaged[SuppressGCTransition]<ulong*, void> s_pfnGetSystemTimeAsFileTime = GetGetSystemTimeAsFileTimeFnPtr();

private static unsafe delegate* unmanaged[SuppressGCTransition]<long*, void> GetGetSystemTimeAsFileTimeFnPtr()
private static unsafe delegate* unmanaged[SuppressGCTransition]<ulong*, void> GetGetSystemTimeAsFileTimeFnPtr()
{
IntPtr kernel32Lib = Interop.Kernel32.LoadLibraryEx(Interop.Libraries.Kernel32, IntPtr.Zero, Interop.Kernel32.LOAD_LIBRARY_SEARCH_SYSTEM32);
Debug.Assert(kernel32Lib != IntPtr.Zero);
Expand Down Expand Up @@ -192,7 +188,7 @@ internal FullSystemTime(long ticks)
}
}

return (delegate* unmanaged[SuppressGCTransition]<long*, void>)pfnGetSystemTime;
return (delegate* unmanaged[SuppressGCTransition]<ulong*, void>)pfnGetSystemTime;
}
}
}
Loading

0 comments on commit e31f845

Please sign in to comment.