From 5918211cb3c1052a091c146928dd463b9ac2c9c7 Mon Sep 17 00:00:00 2001 From: Tarek Mahmoud Sayed Date: Sat, 8 Dec 2018 12:40:13 -0800 Subject: [PATCH] Leap Seconds Support (dotnet/coreclr#21420) * Leap Seconds Support * Address Feedback * More feedback addressing Commit migrated from https://github.com/dotnet/coreclr/commit/98e49952213ff29ee78cb9c33dd33927a88905c6 --- .../System.Private.CoreLib.csproj | 3 +- .../src/System/DateTime.CoreCLR.cs | 27 ---- .../src/System/DateTime.Unix.cs | 30 +++++ .../src/System/DateTime.Windows.cs | 137 +++++++++++++++++++++ src/coreclr/src/classlibnative/bcltype/system.cpp | 132 ++++++++++++++++---- src/coreclr/src/classlibnative/bcltype/system.h | 12 ++ src/coreclr/src/vm/ecalllist.h | 92 +++++++------- .../NtDll/Interop.NtQuerySystemInformation.cs | 24 ++++ .../src/System.Private.CoreLib.Shared.projitems | 1 + .../System.Private.CoreLib/src/System/DateTime.cs | 123 +++++++++++++++++- .../src/System/DateTimeOffset.cs | 60 +++++++-- 11 files changed, 531 insertions(+), 110 deletions(-) delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/DateTime.CoreCLR.cs create mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/DateTime.Unix.cs create mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/DateTime.Windows.cs create mode 100644 src/libraries/System.Private.CoreLib/src/Interop/Windows/NtDll/Interop.NtQuerySystemInformation.cs diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 3d53137..a4c9bec 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -141,7 +141,6 @@ - @@ -427,12 +426,14 @@ + + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/DateTime.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/DateTime.CoreCLR.cs deleted file mode 100644 index c9a0f23..0000000 --- a/src/coreclr/src/System.Private.CoreLib/src/System/DateTime.CoreCLR.cs +++ /dev/null @@ -1,27 +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; - -namespace System -{ - public readonly partial struct DateTime - { - public static DateTime UtcNow - { - get - { - // following code is tuned for speed. Don't change it without running benchmark. - long ticks = 0; - ticks = GetSystemTimeAsFileTime(); - - return new DateTime(((ulong)(ticks + FileTimeOffset)) | KindUtc); - } - } - - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern long GetSystemTimeAsFileTime(); - } -} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/DateTime.Unix.cs b/src/coreclr/src/System.Private.CoreLib/src/System/DateTime.Unix.cs new file mode 100644 index 0000000..f5982fa --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/DateTime.Unix.cs @@ -0,0 +1,30 @@ +// 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; + +namespace System +{ + public readonly partial struct DateTime + { + internal const bool s_systemSupportsLeapSeconds = false; + + public static DateTime UtcNow + { + get + { + return new DateTime(((ulong)(GetSystemTimeAsFileTime() + FileTimeOffset)) | KindUtc); + } + } + + internal static DateTime FromFileTimeLeapSecondsAware(long fileTime) => default(DateTime); + internal static long ToFileTimeLeapSecondsAware(long ticks) => default(long); + + // 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; + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern long GetSystemTimeAsFileTime(); + } +} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/DateTime.Windows.cs b/src/coreclr/src/System.Private.CoreLib/src/System/DateTime.Windows.cs new file mode 100644 index 0000000..f34a6e0 --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/DateTime.Windows.cs @@ -0,0 +1,137 @@ +// 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 = new Interop.NtDll.SYSTEM_LEAP_SECOND_INFORMATION(); + 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(); + } +} diff --git a/src/coreclr/src/classlibnative/bcltype/system.cpp b/src/coreclr/src/classlibnative/bcltype/system.cpp index d525f87..bd1db8f 100644 --- a/src/coreclr/src/classlibnative/bcltype/system.cpp +++ b/src/coreclr/src/classlibnative/bcltype/system.cpp @@ -76,7 +76,7 @@ void WINAPI InitializeGetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) { func = &::GetSystemTimeAsFileTime; } - + g_pfnGetSystemTimeAsFileTime = func; func(lpSystemTimeAsFileTime); } @@ -99,12 +99,90 @@ FCIMPL0(INT64, SystemNative::__GetSystemTimeAsFileTime) FCIMPLEND; +#ifndef FEATURE_PAL + +FCIMPL1(VOID, SystemNative::GetSystemTimeWithLeapSecondsHandling, FullSystemTime *time) +{ + FCALL_CONTRACT; + INT64 timestamp; + + g_pfnGetSystemTimeAsFileTime((FILETIME*)×tamp); + + if (::FileTimeToSystemTime((FILETIME*)×tamp, &(time->systemTime))) + { + // to keep the time precision + time->hundredNanoSecond = timestamp % 10000; // 10000 is the number of 100-nano seconds per Millisecond + } + else + { + ::GetSystemTime(&(time->systemTime)); + time->hundredNanoSecond = 0; + } + + if (time->systemTime.wSecond > 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.wSecond = 59; + time->systemTime.wMilliseconds = 999; + time->hundredNanoSecond = 9999; + } +} +FCIMPLEND; + +FCIMPL2(FC_BOOL_RET, SystemNative::FileTimeToSystemTime, INT64 fileTime, FullSystemTime *time) +{ + FCALL_CONTRACT; + if (::FileTimeToSystemTime((FILETIME*)&fileTime, (LPSYSTEMTIME) time)) + { + // to keep the time precision + time->hundredNanoSecond = fileTime % 10000; // 10000 is the number of 100-nano seconds per Millisecond + if (time->systemTime.wSecond > 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.wSecond = 59; + time->systemTime.wMilliseconds = 999; + time->hundredNanoSecond = 9999; + } + FC_RETURN_BOOL(TRUE); + } + FC_RETURN_BOOL(FALSE); +} +FCIMPLEND; + +FCIMPL2(FC_BOOL_RET, SystemNative::ValidateSystemTime, SYSTEMTIME *time, CLR_BOOL localTime) +{ + FCALL_CONTRACT; + + if (localTime) + { + SYSTEMTIME st; + FC_RETURN_BOOL(::TzSpecificLocalTimeToSystemTime(NULL, time, &st)); + } + else + { + FILETIME timestamp; + FC_RETURN_BOOL(::SystemTimeToFileTime(time, ×tamp)); + } +} +FCIMPLEND; + +FCIMPL2(FC_BOOL_RET, SystemNative::SystemTimeToFileTime, SYSTEMTIME *time, INT64 *pFileTime) +{ + FCALL_CONTRACT; + + BOOL ret = ::SystemTimeToFileTime(time, (LPFILETIME) pFileTime); + FC_RETURN_BOOL(ret); +} +FCIMPLEND; +#endif // FEATURE_PAL FCIMPL0(UINT32, SystemNative::GetTickCount) { FCALL_CONTRACT; - + return ::GetTickCount(); } FCIMPLEND; @@ -132,7 +210,7 @@ VOID QCALLTYPE SystemNative::Exit(INT32 exitcode) FCIMPL1(VOID,SystemNative::SetExitCode,INT32 exitcode) { FCALL_CONTRACT; - + // The exit code for the process is communicated in one of two ways. If the // entrypoint returns an 'int' we take that. Otherwise we take a latched // process exit code. This can be modified by the app via setting @@ -144,7 +222,7 @@ FCIMPLEND FCIMPL0(INT32, SystemNative::GetExitCode) { FCALL_CONTRACT; - + // Return whatever has been latched so far. This is uninitialized to 0. return GetLatchedExitCode(); } @@ -161,7 +239,7 @@ void QCALLTYPE SystemNative::_GetCommandLine(QCall::StringHandleOnStack retStrin commandLine = WszGetCommandLine(); if (commandLine==NULL) COMPlusThrowOM(); - + retString.Set(commandLine); END_QCALL; @@ -187,10 +265,10 @@ FCIMPL0(Object*, SystemNative::GetCommandLineArgs) COMPlusThrowOM(); _ASSERTE(numArgs > 0); - + strArray = (PTRARRAYREF) AllocateObjectArray(numArgs, g_pStringClass); // Copy each argument into new Strings. - for(unsigned int i=0; iGetDataPtr())) + i; @@ -200,7 +278,7 @@ FCIMPL0(Object*, SystemNative::GetCommandLineArgs) HELPER_METHOD_FRAME_END(); - return OBJECTREFToObject(strArray); + return OBJECTREFToObject(strArray); } FCIMPLEND @@ -208,7 +286,7 @@ FCIMPLEND FCIMPL1(ReflectMethodObject*, SystemNative::GetMethodFromStackTrace, ArrayBase* pStackTraceUNSAFE) { FCALL_CONTRACT; - + I1ARRAYREF pArray(static_cast(pStackTraceUNSAFE)); StackTraceArray stackArray(pArray); @@ -276,7 +354,7 @@ FCIMPL0(FC_BOOL_RET, SystemNative::HasShutdownStarted) { FCALL_CONTRACT; - // Return true if the EE has started to shutdown and is now going to + // Return true if the EE has started to shutdown and is now going to // aggressively finalize objects referred to by static variables OR // if someone is unloading the current AppDomain AND we have started // finalizing objects referred to by static variables. @@ -304,7 +382,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce SO_TOLERANT; }CONTRACTL_END; - struct + struct { STRINGREF refMesgString; EXCEPTIONREF refExceptionForWatsonBucketing; @@ -313,7 +391,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); - + gc.refMesgString = refMesgString; gc.refExceptionForWatsonBucketing = refExceptionForWatsonBucketing; gc.refErrorSourceString = refErrorSourceString; @@ -344,12 +422,12 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce WCHAR * errorSourceString = NULL; - if (gc.refErrorSourceString != NULL) + if (gc.refErrorSourceString != NULL) { DWORD cchErrorSource = gc.refErrorSourceString->GetStringLength(); errorSourceString = new (nothrow) WCHAR[cchErrorSource + 1]; - if (errorSourceString != NULL) + if (errorSourceString != NULL) { memcpyNoGCRefs(errorSourceString, gc.refErrorSourceString->GetBuffer(), cchErrorSource * sizeof(WCHAR)); errorSourceString[cchErrorSource] = W('\0'); @@ -372,7 +450,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce pszMessage = g_szFailFastBuffer; } } - + if (cchMessage > 0) memcpyNoGCRefs(pszMessage, gc.refMesgString->GetBuffer(), cchMessage * sizeof(WCHAR)); pszMessage[cchMessage] = W('\0'); @@ -396,7 +474,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce Thread *pThread = GetThread(); -#ifndef FEATURE_PAL +#ifndef FEATURE_PAL // If we have the exception object, then try to setup // the watson bucket if it has any details. // On CoreCLR, Watson may not be enabled. Thus, we should @@ -430,18 +508,18 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce } // Note: Do not merge this FCALL method with any other FailFast overloads. -// Watson uses the managed FailFast method with one String for crash dump bucketization. +// Watson uses the managed FailFast method with one String for crash dump bucketization. FCIMPL1(VOID, SystemNative::FailFast, StringObject* refMessageUNSAFE) -{ +{ FCALL_CONTRACT; STRINGREF refMessage = (STRINGREF)refMessageUNSAFE; - + HELPER_METHOD_FRAME_BEGIN_1(refMessage); // The HelperMethodFrame knows how to get the return address. UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); - + // Call the actual worker to perform failfast GenericFailFast(refMessage, NULL, retaddr, COR_E_FAILFAST, NULL); @@ -450,16 +528,16 @@ FCIMPL1(VOID, SystemNative::FailFast, StringObject* refMessageUNSAFE) FCIMPLEND FCIMPL2(VOID, SystemNative::FailFastWithExitCode, StringObject* refMessageUNSAFE, UINT exitCode) -{ +{ FCALL_CONTRACT; STRINGREF refMessage = (STRINGREF)refMessageUNSAFE; - + HELPER_METHOD_FRAME_BEGIN_1(refMessage); // The HelperMethodFrame knows how to get the return address. UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); - + // Call the actual worker to perform failfast GenericFailFast(refMessage, NULL, retaddr, exitCode, NULL); @@ -468,7 +546,7 @@ FCIMPL2(VOID, SystemNative::FailFastWithExitCode, StringObject* refMessageUNSAFE FCIMPLEND FCIMPL2(VOID, SystemNative::FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE) -{ +{ FCALL_CONTRACT; STRINGREF refMessage = (STRINGREF)refMessageUNSAFE; @@ -478,7 +556,7 @@ FCIMPL2(VOID, SystemNative::FailFastWithException, StringObject* refMessageUNSAF // The HelperMethodFrame knows how to get the return address. UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); - + // Call the actual worker to perform failfast GenericFailFast(refMessage, refException, retaddr, COR_E_FAILFAST, NULL); @@ -498,7 +576,7 @@ FCIMPL3(VOID, SystemNative::FailFastWithExceptionAndSource, StringObject* refMes // The HelperMethodFrame knows how to get the return address. UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS(); - + // Call the actual worker to perform failfast GenericFailFast(refMessage, refException, retaddr, COR_E_FAILFAST, errorSource); @@ -534,6 +612,6 @@ BOOL QCALLTYPE SystemNative::WinRTSupported() - + diff --git a/src/coreclr/src/classlibnative/bcltype/system.h b/src/coreclr/src/classlibnative/bcltype/system.h index 9480ee94..803f1df 100644 --- a/src/coreclr/src/classlibnative/bcltype/system.h +++ b/src/coreclr/src/classlibnative/bcltype/system.h @@ -15,6 +15,12 @@ #include "fcall.h" #include "qcall.h" +struct FullSystemTime +{ + SYSTEMTIME systemTime; + INT64 hundredNanoSecond; +}; + class SystemNative { friend class DebugStackTrace; @@ -38,6 +44,12 @@ private: public: // Functions on the System.Environment class +#ifndef FEATURE_PAL + static FCDECL1(VOID, GetSystemTimeWithLeapSecondsHandling, FullSystemTime *time); + static FCDECL2(FC_BOOL_RET, ValidateSystemTime, SYSTEMTIME *time, CLR_BOOL localTime); + static FCDECL2(FC_BOOL_RET, FileTimeToSystemTime, INT64 fileTime, FullSystemTime *time); + static FCDECL2(FC_BOOL_RET, SystemTimeToFileTime, SYSTEMTIME *time, INT64 *pFileTime); +#endif // FEATURE_PAL static FCDECL0(INT64, __GetSystemTimeAsFileTime); static FCDECL0(UINT32, GetTickCount); diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 04c9fba..2db7b70 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -80,7 +80,7 @@ FCFuncStart(gEnumFuncs) QCFuncElement("GetEnumValuesAndNames", ReflectionEnum::GetEnumValuesAndNames) FCFuncElement("InternalBoxEnum", ReflectionEnum::InternalBoxEnum) FCFuncElement("Equals", ReflectionEnum::InternalEquals) - FCFuncElement("InternalCompareTo", ReflectionEnum::InternalCompareTo) + FCFuncElement("InternalCompareTo", ReflectionEnum::InternalCompareTo) FCFuncElement("InternalHasFlag", ReflectionEnum::InternalHasFlag) FCFuncEnd() @@ -137,6 +137,12 @@ FCFuncStart(gDiagnosticsStackTrace) FCFuncEnd() FCFuncStart(gDateTimeFuncs) +#if !defined(FEATURE_PAL) + FCFuncElement("GetSystemTimeWithLeapSecondsHandling", SystemNative::GetSystemTimeWithLeapSecondsHandling) + FCFuncElement("ValidateSystemTime", SystemNative::ValidateSystemTime) + FCFuncElement("FileTimeToSystemTime", SystemNative::FileTimeToSystemTime) + FCFuncElement("SystemTimeToFileTime", SystemNative::SystemTimeToFileTime) +#endif // FEATURE_PAL FCFuncElement("GetSystemTimeAsFileTime", SystemNative::__GetSystemTimeAsFileTime) FCFuncEnd() @@ -211,7 +217,7 @@ FCFuncStart(gSystem_RuntimeType) FCFuncElement("IsTypeExportedToWindowsRuntime", RuntimeTypeHandle::IsTypeExportedToWindowsRuntime) #endif FCFuncElement("IsWindowsRuntimeObjectType", RuntimeTypeHandle::IsWindowsRuntimeObjectType) -#endif // defined(FEATURE_COMINTEROP) +#endif // defined(FEATURE_COMINTEROP) FCFuncEnd() FCFuncStart(gJitHelpers) @@ -279,31 +285,31 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncEnd() FCFuncStart(gMetaDataImport) - FCFuncElement("_GetDefaultValue", MetaDataImport::GetDefaultValue) - FCFuncElement("_GetName", MetaDataImport::GetName) - FCFuncElement("_GetUserString", MetaDataImport::GetUserString) - FCFuncElement("_GetScopeProps", MetaDataImport::GetScopeProps) - FCFuncElement("_GetClassLayout", MetaDataImport::GetClassLayout) - FCFuncElement("_GetSignatureFromToken", MetaDataImport::GetSignatureFromToken) - FCFuncElement("_GetNamespace", MetaDataImport::GetNamespace) + FCFuncElement("_GetDefaultValue", MetaDataImport::GetDefaultValue) + FCFuncElement("_GetName", MetaDataImport::GetName) + FCFuncElement("_GetUserString", MetaDataImport::GetUserString) + FCFuncElement("_GetScopeProps", MetaDataImport::GetScopeProps) + FCFuncElement("_GetClassLayout", MetaDataImport::GetClassLayout) + FCFuncElement("_GetSignatureFromToken", MetaDataImport::GetSignatureFromToken) + FCFuncElement("_GetNamespace", MetaDataImport::GetNamespace) FCFuncElement("_GetEventProps", MetaDataImport::GetEventProps) FCFuncElement("_GetFieldDefProps", MetaDataImport::GetFieldDefProps) - FCFuncElement("_GetPropertyProps", MetaDataImport::GetPropertyProps) - FCFuncElement("_GetParentToken", MetaDataImport::GetParentToken) - FCFuncElement("_GetParamDefProps", MetaDataImport::GetParamDefProps) - FCFuncElement("_GetGenericParamProps", MetaDataImport::GetGenericParamProps) - - FCFuncElement("_Enum", MetaDataImport::Enum) - FCFuncElement("_GetMemberRefProps", MetaDataImport::GetMemberRefProps) - FCFuncElement("_GetCustomAttributeProps", MetaDataImport::GetCustomAttributeProps) - FCFuncElement("_GetFieldOffset", MetaDataImport::GetFieldOffset) - - FCFuncElement("_GetSigOfFieldDef", MetaDataImport::GetSigOfFieldDef) - FCFuncElement("_GetSigOfMethodDef", MetaDataImport::GetSigOfMethodDef) - FCFuncElement("_GetFieldMarshal", MetaDataImport::GetFieldMarshal) - FCFuncElement("_GetPInvokeMap", MetaDataImport::GetPinvokeMap) - FCFuncElement("_IsValidToken", MetaDataImport::IsValidToken) - FCFuncElement("_GetMarshalAs", MetaDataImport::GetMarshalAs) + FCFuncElement("_GetPropertyProps", MetaDataImport::GetPropertyProps) + FCFuncElement("_GetParentToken", MetaDataImport::GetParentToken) + FCFuncElement("_GetParamDefProps", MetaDataImport::GetParamDefProps) + FCFuncElement("_GetGenericParamProps", MetaDataImport::GetGenericParamProps) + + FCFuncElement("_Enum", MetaDataImport::Enum) + FCFuncElement("_GetMemberRefProps", MetaDataImport::GetMemberRefProps) + FCFuncElement("_GetCustomAttributeProps", MetaDataImport::GetCustomAttributeProps) + FCFuncElement("_GetFieldOffset", MetaDataImport::GetFieldOffset) + + FCFuncElement("_GetSigOfFieldDef", MetaDataImport::GetSigOfFieldDef) + FCFuncElement("_GetSigOfMethodDef", MetaDataImport::GetSigOfMethodDef) + FCFuncElement("_GetFieldMarshal", MetaDataImport::GetFieldMarshal) + FCFuncElement("_GetPInvokeMap", MetaDataImport::GetPinvokeMap) + FCFuncElement("_IsValidToken", MetaDataImport::IsValidToken) + FCFuncElement("_GetMarshalAs", MetaDataImport::GetMarshalAs) FCFuncEnd() FCFuncStart(gSignatureNative) @@ -336,9 +342,9 @@ FCFuncStart(gRuntimeMethodHandle) FCFuncElement("GetStubIfNeeded", RuntimeMethodHandle::GetStubIfNeeded) FCFuncElement("GetMethodFromCanonical", RuntimeMethodHandle::GetMethodFromCanonical) FCFuncElement("IsDynamicMethod", RuntimeMethodHandle::IsDynamicMethod) - FCFuncElement("GetMethodBody", RuntimeMethodHandle::GetMethodBody) + FCFuncElement("GetMethodBody", RuntimeMethodHandle::GetMethodBody) QCFuncElement("IsCAVisibleFromDecoratedType", RuntimeMethodHandle::IsCAVisibleFromDecoratedType) - FCFuncElement("IsConstructor", RuntimeMethodHandle::IsConstructor) + FCFuncElement("IsConstructor", RuntimeMethodHandle::IsConstructor) QCFuncElement("Destroy", RuntimeMethodHandle::Destroy) FCFuncElement("GetResolver", RuntimeMethodHandle::GetResolver) FCFuncElement("GetLoaderAllocator", RuntimeMethodHandle::GetLoaderAllocator) @@ -372,7 +378,7 @@ FCFuncStart(gCOMModuleFuncs) FCFuncElement("GetTypes", COMModule::GetTypes) QCFuncElement("GetFullyQualifiedName", COMModule::GetFullyQualifiedName) QCFuncElement("nIsTransientInternal", COMModule::IsTransient) - FCFuncElement("IsResource", COMModule::IsResource) + FCFuncElement("IsResource", COMModule::IsResource) FCFuncEnd() FCFuncStart(gCOMModuleBuilderFuncs) @@ -551,7 +557,7 @@ FCFuncStart(gAssemblyBuilderFuncs) FCFuncElement("GetInMemoryAssemblyModule", AssemblyNative::GetInMemoryAssemblyModule) FCFuncEnd() -#ifdef MDA_SUPPORTED +#ifdef MDA_SUPPORTED FCFuncStart(gMda) FCFuncElement("MemberInfoCacheCreation", MdaManagedSupport::MemberInfoCacheCreation) FCFuncElement("DateTimeInvalidLocalFormat", MdaManagedSupport::DateTimeInvalidLocalFormat) @@ -701,7 +707,7 @@ FCFuncStart(gThreadPoolFuncs) FCFuncElement("NotifyWorkItemComplete", ThreadPoolNative::NotifyRequestComplete) FCFuncElement("NotifyWorkItemProgressNative", ThreadPoolNative::NotifyRequestProgress) QCFuncElement("InitializeVMTp", ThreadPoolNative::InitializeVMTp) - FCFuncElement("ReportThreadStatus", ThreadPoolNative::ReportThreadStatus) + FCFuncElement("ReportThreadStatus", ThreadPoolNative::ReportThreadStatus) QCFuncElement("RequestWorkerThread", ThreadPoolNative::RequestWorkerThread) FCFuncEnd() @@ -811,7 +817,7 @@ FCFuncStart(gGCInterfaceFuncs) FCFuncElement("_SuppressFinalize", GCInterface::SuppressFinalize) FCFuncElement("_ReRegisterForFinalize", GCInterface::ReRegisterForFinalize) - + FCFuncElement("_GetAllocatedBytesForCurrentThread", GCInterface::GetAllocatedBytesForCurrentThread) FCFuncEnd() @@ -866,7 +872,7 @@ FCFuncStart(gInteropMarshalFuncs) FCFuncElement("GetObjectsForNativeVariants", MarshalNative::GetObjectsForNativeVariants) FCFuncElement("GetStartComSlot", MarshalNative::GetStartComSlot) FCFuncElement("GetEndComSlot", MarshalNative::GetEndComSlot) - + FCFuncElement("InitializeManagedWinRTFactoryObject", MarshalNative::InitializeManagedWinRTFactoryObject) FCFuncElement("GetNativeActivationFactory", MarshalNative::GetNativeActivationFactory) @@ -914,7 +920,7 @@ FCFuncEnd() FCFuncStart(gInterlockedFuncs) FCIntrinsicSig("Exchange", &gsig_SM_RefInt_Int_RetInt, COMInterlocked::Exchange, CORINFO_INTRINSIC_InterlockedXchg32) - FCIntrinsicSig("Exchange", &gsig_SM_RefLong_Long_RetLong, COMInterlocked::Exchange64, CORINFO_INTRINSIC_InterlockedXchg64) + FCIntrinsicSig("Exchange", &gsig_SM_RefLong_Long_RetLong, COMInterlocked::Exchange64, CORINFO_INTRINSIC_InterlockedXchg64) FCFuncElementSig("Exchange", &gsig_SM_RefDbl_Dbl_RetDbl, COMInterlocked::ExchangeDouble) FCFuncElementSig("Exchange", &gsig_SM_RefFlt_Flt_RetFlt, COMInterlocked::ExchangeFloat) FCFuncElementSig("Exchange", &gsig_SM_RefObj_Obj_RetObj, COMInterlocked::ExchangeObject) @@ -1039,7 +1045,7 @@ FCFuncStart(gMngdHiddenLengthArrayMarshalerFuncs) FCFuncElement("ConvertContentsToManaged", MngdHiddenLengthArrayMarshaler::ConvertContentsToManaged) FCFuncElement("ClearNativeContents", MngdHiddenLengthArrayMarshaler::ClearNativeContents) FCFuncEnd() - + FCFuncStart(gWinRTTypeNameConverterFuncs) FCFuncElement("ConvertToWinRTTypeName", StubHelpers::WinRTTypeNameConverter__ConvertToWinRTTypeName) FCFuncElement("GetTypeFromWinRTTypeName", StubHelpers::WinRTTypeNameConverter__GetTypeFromWinRTTypeName) @@ -1085,16 +1091,16 @@ FCFuncStart(gStubHelperFuncs) #ifdef MDA_SUPPORTED FCFuncElement("CheckCollectedDelegateMDA", StubHelpers::CheckCollectedDelegateMDA) #endif // MDA_SUPPORTED -#ifdef PROFILING_SUPPORTED +#ifdef PROFILING_SUPPORTED FCFuncElement("ProfilerBeginTransitionCallback", StubHelpers::ProfilerBeginTransitionCallback) FCFuncElement("ProfilerEndTransitionCallback", StubHelpers::ProfilerEndTransitionCallback) -#endif +#endif FCFuncElement("CreateCustomMarshalerHelper", StubHelpers::CreateCustomMarshalerHelper) FCFuncElement("FmtClassUpdateNativeInternal", StubHelpers::FmtClassUpdateNativeInternal) FCFuncElement("FmtClassUpdateCLRInternal", StubHelpers::FmtClassUpdateCLRInternal) FCFuncElement("LayoutDestroyNativeInternal", StubHelpers::LayoutDestroyNativeInternal) FCFuncElement("AllocateInternal", StubHelpers::AllocateInternal) - FCFuncElement("strlen", StubHelpers::AnsiStrlen) + FCFuncElement("strlen", StubHelpers::AnsiStrlen) FCFuncElement("MarshalToUnmanagedVaListInternal", StubHelpers::MarshalToUnmanagedVaListInternal) FCFuncElement("MarshalToManagedVaListInternal", StubHelpers::MarshalToManagedVaListInternal) FCFuncElement("CalcVaListSize", StubHelpers::CalcVaListSize) @@ -1274,7 +1280,7 @@ FCClassElement("ManifestBasedResourceGroveler", "System.Resources", gManifestBa FCClassElement("Marshal", "System.Runtime.InteropServices", gInteropMarshalFuncs) FCClassElement("Math", "System", gMathFuncs) FCClassElement("MathF", "System", gMathFFuncs) -#ifdef MDA_SUPPORTED +#ifdef MDA_SUPPORTED FCClassElement("Mda", "System", gMda) #endif FCClassElement("MdUtf8String", "System", gMdUtf8String) @@ -1284,10 +1290,10 @@ FCClassElement("MissingMemberException", "System", gMissingMemberExceptionFuncs #ifdef FEATURE_COMINTEROP FCClassElement("MngdHiddenLengthArrayMarshaler", "System.StubHelpers", gMngdHiddenLengthArrayMarshalerFuncs) #endif // FEATURE_COMINTEROP -FCClassElement("MngdNativeArrayMarshaler", "System.StubHelpers", gMngdNativeArrayMarshalerFuncs) -FCClassElement("MngdRefCustomMarshaler", "System.StubHelpers", gMngdRefCustomMarshalerFuncs) +FCClassElement("MngdNativeArrayMarshaler", "System.StubHelpers", gMngdNativeArrayMarshalerFuncs) +FCClassElement("MngdRefCustomMarshaler", "System.StubHelpers", gMngdRefCustomMarshalerFuncs) #ifdef FEATURE_COMINTEROP -FCClassElement("MngdSafeArrayMarshaler", "System.StubHelpers", gMngdSafeArrayMarshalerFuncs) +FCClassElement("MngdSafeArrayMarshaler", "System.StubHelpers", gMngdSafeArrayMarshalerFuncs) #endif // FEATURE_COMINTEROP FCClassElement("ModuleBuilder", "System.Reflection.Emit", gCOMModuleBuilderFuncs) FCClassElement("ModuleHandle", "System", gCOMModuleHandleFuncs) @@ -1311,9 +1317,9 @@ FCClassElement("RegistrationServices", "System.Runtime.InteropServices", gRegist #endif // FEATURE_COMINTEROP FCClassElement("RuntimeAssembly", "System.Reflection", gRuntimeAssemblyFuncs) -#ifdef FEATURE_COMINTEROP +#ifdef FEATURE_COMINTEROP FCClassElement("RuntimeClass", "System.Runtime.InteropServices.WindowsRuntime", gRuntimeClassFuncs) -#endif // FEATURE_COMINTEROP +#endif // FEATURE_COMINTEROP FCClassElement("RuntimeFieldHandle", "System", gCOMFieldHandleNewFuncs) FCClassElement("RuntimeHelpers", "System.Runtime.CompilerServices", gCompilerFuncs) FCClassElement("RuntimeImports", "System.Runtime", gRuntimeImportsFuncs) diff --git a/src/libraries/System.Private.CoreLib/src/Interop/Windows/NtDll/Interop.NtQuerySystemInformation.cs b/src/libraries/System.Private.CoreLib/src/Interop/Windows/NtDll/Interop.NtQuerySystemInformation.cs new file mode 100644 index 0000000..16f05e33 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/Interop/Windows/NtDll/Interop.NtQuerySystemInformation.cs @@ -0,0 +1,24 @@ +// 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 NtDll + { + [DllImport(Libraries.NtDll)] + internal static unsafe extern int NtQuerySystemInformation(int SystemInformationClass, void* SystemInformation, int SystemInformationLength, uint* ReturnLength); + + [StructLayout(LayoutKind.Sequential)] + internal struct SYSTEM_LEAP_SECOND_INFORMATION + { + public bool Enabled; + public uint Flags; + } + + internal const int SystemLeapSecondInformation = 206; + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 56a0b13..0c40e26 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -859,6 +859,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTime.cs b/src/libraries/System.Private.CoreLib/src/System/DateTime.cs index 4cba9f0..1a33479 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTime.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTime.cs @@ -196,6 +196,14 @@ namespace System // public DateTime(int year, int month, int day, int hour, int minute, int second) { + if (second == 60 && s_systemSupportsLeapSeconds && IsValidTimeWithLeapSeconds(year, month, day, hour, minute, second, DateTimeKind.Unspecified)) + { + // if we have leap second (second = 60) then we'll need to check if it is valid time. + // if it is valid, then we adjust the second to 59 so DateTime will consider this second is last second + // in the specified minute. + // if it is not valid time, we'll eventually throw. + second = 59; + } _dateData = (ulong)(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second)); } @@ -205,6 +213,16 @@ namespace System { throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); } + + if (second == 60 && s_systemSupportsLeapSeconds && IsValidTimeWithLeapSeconds(year, month, day, hour, minute, second, kind)) + { + // if we have leap second (second = 60) then we'll need to check if it is valid time. + // if it is valid, then we adjust the second to 59 so DateTime will consider this second is last second + // in the specified minute. + // if it is not valid time, we'll eventually throw. + second = 59; + } + long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); _dateData = ((ulong)ticks | ((ulong)kind << KindShift)); } @@ -216,7 +234,24 @@ namespace System { if (calendar == null) throw new ArgumentNullException(nameof(calendar)); + + int originalSecond = second; + if (second == 60 && s_systemSupportsLeapSeconds) + { + // Reset the second value now and then we'll validate it later when we get the final Gregorian date. + second = 59; + } + _dateData = (ulong)calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; + + if (originalSecond == 60) + { + DateTime dt = new DateTime(_dateData); + if (!IsValidTimeWithLeapSeconds(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 60, DateTimeKind.Unspecified)) + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + } } // Constructs a DateTime from a given year, month, day, hour, @@ -228,6 +263,16 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); } + + if (second == 60 && s_systemSupportsLeapSeconds && IsValidTimeWithLeapSeconds(year, month, day, hour, minute, second, DateTimeKind.Unspecified)) + { + // if we have leap second (second = 60) then we'll need to check if it is valid time. + // if it is valid, then we adjust the second to 59 so DateTime will consider this second is last second + // in the specified minute. + // if it is not valid time, we'll eventually throw. + second = 59; + } + long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); ticks += millisecond * TicksPerMillisecond; if (ticks < MinTicks || ticks > MaxTicks) @@ -245,6 +290,16 @@ namespace System { throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); } + + if (second == 60 && s_systemSupportsLeapSeconds && IsValidTimeWithLeapSeconds(year, month, day, hour, minute, second, kind)) + { + // if we have leap second (second = 60) then we'll need to check if it is valid time. + // if it is valid, then we adjust the second to 59 so DateTime will consider this second is last second + // in the specified minute. + // if it is not valid time, we'll eventually throw. + second = 59; + } + long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); ticks += millisecond * TicksPerMillisecond; if (ticks < MinTicks || ticks > MaxTicks) @@ -263,11 +318,28 @@ namespace System { throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1)); } + + int originalSecond = second; + if (second == 60 && s_systemSupportsLeapSeconds) + { + // Reset the second value now and then we'll validate it later when we get the final Gregorian date. + second = 59; + } + long ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; ticks += millisecond * TicksPerMillisecond; if (ticks < MinTicks || ticks > MaxTicks) throw new ArgumentException(SR.Arg_DateTimeRange); _dateData = (ulong)ticks; + + if (originalSecond == 60) + { + DateTime dt = new DateTime(_dateData); + if (!IsValidTimeWithLeapSeconds(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 60, DateTimeKind.Unspecified)) + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + } } public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind) @@ -282,11 +354,28 @@ namespace System { throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind)); } + + int originalSecond = second; + if (second == 60 && s_systemSupportsLeapSeconds) + { + // Reset the second value now and then we'll validate it later when we get the final Gregorian date. + second = 59; + } + long ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks; ticks += millisecond * TicksPerMillisecond; if (ticks < MinTicks || ticks > MaxTicks) throw new ArgumentException(SR.Arg_DateTimeRange); _dateData = ((ulong)ticks | ((ulong)kind << KindShift)); + + if (originalSecond == 60) + { + DateTime dt = new DateTime(_dateData); + if (!IsValidTimeWithLeapSeconds(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 60, kind)) + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } + } } private DateTime(SerializationInfo info, StreamingContext context) @@ -338,8 +427,6 @@ namespace System } } - - internal long InternalTicks { get @@ -722,6 +809,11 @@ namespace System throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_FileTimeInvalid); } + if (s_systemSupportsLeapSeconds) + { + return FromFileTimeLeapSecondsAware(fileTime); + } + // This is the ticks in Universal time for this fileTime. long universalTicks = fileTime + FileTimeOffset; return new DateTime(universalTicks, DateTimeKind.Utc); @@ -1223,11 +1315,18 @@ namespace System { // Treats the input as universal if it is not specified long ticks = ((InternalKind & LocalMask) != 0) ? ToUniversalTime().InternalTicks : this.InternalTicks; + + if (s_systemSupportsLeapSeconds) + { + return ToFileTimeLeapSecondsAware(ticks); + } + ticks -= FileTimeOffset; if (ticks < 0) { throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid); } + return ticks; } @@ -1574,7 +1673,7 @@ namespace System { return false; } - if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 60) + if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second > 60) { return false; } @@ -1582,6 +1681,24 @@ namespace System { return false; } + + if (second == 60) + { + if (s_systemSupportsLeapSeconds && IsValidTimeWithLeapSeconds(year, month, day, hour, minute, second, DateTimeKind.Unspecified)) + { + // if we have leap second (second = 60) then we'll need to check if it is valid time. + // if it is valid, then we adjust the second to 59 so DateTime will consider this second is last second + // of this minute. + // if it is not valid time, we'll eventually throw. + // although this is unspecified datetime kind, we'll assume the passed time is UTC to check the leap seconds. + second = 59; + } + else + { + return false; + } + } + long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second); ticks += millisecond * TicksPerMillisecond; diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs b/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs index e89cbc6..6fc5dfc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs @@ -9,14 +9,14 @@ using System.Runtime.Serialization; namespace System { - // DateTimeOffset is a value type that consists of a DateTime and a time zone offset, + // DateTimeOffset is a value type that consists of a DateTime and a time zone offset, // ie. how far away the time is from GMT. The DateTime is stored whole, and the offset - // is stored as an Int16 internally to save space, but presented as a TimeSpan. + // is stored as an Int16 internally to save space, but presented as a TimeSpan. // // The range is constrained so that both the represented clock time and the represented // UTC time fit within the boundaries of MaxValue. This gives it the same range as DateTime // for actual UTC times, and a slightly constrained range on one end when an offset is - // present. + // present. // // This class should be substitutable for date time in most cases; so most operations // effectively work on the clock time. However, the underlying UTC time is what counts @@ -30,7 +30,7 @@ namespace System [StructLayout(LayoutKind.Auto)] [Serializable] - [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public readonly struct DateTimeOffset : IComparable, IFormattable, IComparable, IEquatable, ISerializable, IDeserializationCallback, ISpanFormattable { // Constants @@ -109,7 +109,21 @@ namespace System public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, TimeSpan offset) { _offsetMinutes = ValidateOffset(offset); + + int originalSecond = second; + if (second == 60 && DateTime.s_systemSupportsLeapSeconds) + { + // Reset the leap second to 59 for now and then we'll validate it after getting the final UTC time. + second = 59; + } + _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second), offset); + + if (originalSecond == 60 && + !DateTime.IsValidTimeWithLeapSeconds(_dateTime.Year, _dateTime.Month, _dateTime.Day, _dateTime.Hour, _dateTime.Minute, 60, DateTimeKind.Utc)) + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } } // Constructs a DateTimeOffset from a given year, month, day, hour, @@ -117,7 +131,21 @@ namespace System public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan offset) { _offsetMinutes = ValidateOffset(offset); + + int originalSecond = second; + if (second == 60 && DateTime.s_systemSupportsLeapSeconds) + { + // Reset the leap second to 59 for now and then we'll validate it after getting the final UTC time. + second = 59; + } + _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second, millisecond), offset); + + if (originalSecond == 60 && + !DateTime.IsValidTimeWithLeapSeconds(_dateTime.Year, _dateTime.Month, _dateTime.Day, _dateTime.Hour, _dateTime.Minute, 60, DateTimeKind.Utc)) + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } } // Constructs a DateTimeOffset from a given year, month, day, hour, @@ -125,7 +153,21 @@ namespace System public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, TimeSpan offset) { _offsetMinutes = ValidateOffset(offset); + + int originalSecond = second; + if (second == 60 && DateTime.s_systemSupportsLeapSeconds) + { + // Reset the leap second to 59 for now and then we'll validate it after getting the final UTC time. + second = 59; + } + _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second, millisecond, calendar), offset); + + if (originalSecond == 60 && + !DateTime.IsValidTimeWithLeapSeconds(_dateTime.Year, _dateTime.Month, _dateTime.Day, _dateTime.Hour, _dateTime.Minute, 60, DateTimeKind.Utc)) + { + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond); + } } // Returns a DateTimeOffset representing the current date and time. The @@ -596,7 +638,7 @@ namespace System // Constructs a DateTimeOffset from a string. The string must specify a // date and optionally a time in a culture-specific or universal format. // Leading and trailing whitespace characters are allowed. - // + // public static DateTimeOffset Parse(string input) { if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); @@ -612,7 +654,7 @@ namespace System // Constructs a DateTimeOffset from a string. The string must specify a // date and optionally a time in a culture-specific or universal format. // Leading and trailing whitespace characters are allowed. - // + // public static DateTimeOffset Parse(string input, IFormatProvider formatProvider) { if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); @@ -642,7 +684,7 @@ namespace System // Constructs a DateTimeOffset from a string. The string must specify a // date and optionally a time in a culture-specific or universal format. // Leading and trailing whitespace characters are allowed. - // + // public static DateTimeOffset ParseExact(string input, string format, IFormatProvider formatProvider) { if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input); @@ -653,7 +695,7 @@ namespace System // Constructs a DateTimeOffset from a string. The string must specify a // date and optionally a time in a culture-specific or universal format. // Leading and trailing whitespace characters are allowed. - // + // public static DateTimeOffset ParseExact(string input, string format, IFormatProvider formatProvider, DateTimeStyles styles) { styles = ValidateStyles(styles, nameof(styles)); @@ -943,7 +985,7 @@ namespace System // RoundtripKind does not make sense for DateTimeOffset; ignore this flag for backward compatibility with DateTime style &= ~DateTimeStyles.RoundtripKind; - // AssumeLocal is also ignored as that is what we do by default with DateTimeOffset.Parse + // AssumeLocal is also ignored as that is what we do by default with DateTimeOffset.Parse style &= ~DateTimeStyles.AssumeLocal; return style; -- 2.7.4