From f47dc668bcd2619e3e7780ca4b50dba380ff7a8d Mon Sep 17 00:00:00 2001 From: David Hall Date: Fri, 19 Jul 2019 15:22:52 -0600 Subject: [PATCH] Completed unit tests and fixes for TimeZoneApi.h --- PInvoke/Kernel32/TimeZoneApi.cs | 457 +++++++++++++++++-------- UnitTests/PInvoke/Kernel32/Kernel32.csproj | 1 + UnitTests/PInvoke/Kernel32/Kernel32Tests.cs | 23 -- UnitTests/PInvoke/Kernel32/TimeZoneApiTests.cs | 124 +++++++ 4 files changed, 444 insertions(+), 161 deletions(-) create mode 100644 UnitTests/PInvoke/Kernel32/TimeZoneApiTests.cs diff --git a/PInvoke/Kernel32/TimeZoneApi.cs b/PInvoke/Kernel32/TimeZoneApi.cs index d5a7c397..b5dfbd9a 100644 --- a/PInvoke/Kernel32/TimeZoneApi.cs +++ b/PInvoke/Kernel32/TimeZoneApi.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Security; using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; @@ -11,14 +12,17 @@ namespace Vanara.PInvoke public enum TZID : uint { /// - /// Daylight saving time is not used in the current time zone, because there are no transition dates or automatic adjustment for daylight saving time - /// is disabled. + /// Daylight saving time is not used in the current time zone, because there are no transition dates or automatic adjustment for + /// daylight saving time is disabled. /// TIME_ZONE_ID_UNKNOWN = 0, + /// The system is operating in the range covered by the StandardDate member of the TIME_ZONE_INFORMATION structure. TIME_ZONE_ID_STANDARD = 1, + /// The system is operating in the range covered by the DaylightDate member of the TIME_ZONE_INFORMATION structure. TIME_ZONE_ID_DAYLIGHT = 2, + /// The time zone identifier is invalid. TIME_ZONE_ID_INVALID = 0xFFFFFFFF } @@ -39,11 +43,11 @@ namespace Vanara.PInvoke /// /// None /// - // https://docs.microsoft.com/en-us/windows/desktop/api/timezoneapi/nf-timezoneapi-enumdynamictimezoneinformation - // DWORD EnumDynamicTimeZoneInformation( const DWORD dwIndex, PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation ); - [DllImport(Lib.Kernel32, SetLastError = false, ExactSpelling = true)] + // https://docs.microsoft.com/en-us/windows/desktop/api/timezoneapi/nf-timezoneapi-enumdynamictimezoneinformation DWORD + // EnumDynamicTimeZoneInformation( const DWORD dwIndex, PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation ); + [DllImport(Lib.KernelBase, SetLastError = false, ExactSpelling = true)] [PInvokeData("timezoneapi.h", MSDNShortId = "EBB2366A-86FE-4764-B7F9-5D305993CE0A")] - public static extern uint EnumDynamicTimeZoneInformation(uint dwIndex, out DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation); + public static extern Win32Error EnumDynamicTimeZoneInformation(uint dwIndex, out DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation); /// /// Enumerates DYNAMIC_TIME_ZONE_INFORMATION entries stored in the registry. This information is used to support time zones that @@ -54,18 +58,22 @@ namespace Vanara.PInvoke public static IEnumerable EnumDynamicTimeZoneInformation() { var i = 0U; - while (EnumDynamicTimeZoneInformation(i++, out var tz) != 0) + Win32Error err; + while ((err = EnumDynamicTimeZoneInformation(i++, out var tz)).Succeeded) yield return tz; + if (err != Win32Error.ERROR_NO_MORE_ITEMS) + throw err.GetException(); } /// Converts a file time to system time format. System time is based on Coordinated Universal Time (UTC). /// - /// A pointer to a FILETIME structure containing the file time to be converted to system (UTC) date and time format. This value must be less than - /// 0x8000000000000000. Otherwise, the function fails. + /// A pointer to a FILETIME structure containing the file time to be converted to system (UTC) date and time format. This value must + /// be less than 0x8000000000000000. Otherwise, the function fails. /// /// A pointer to a SYSTEMTIME structure to receive the converted file time. /// - /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.To get extended error information, call GetLastError. + /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.To get extended error + /// information, call GetLastError. /// [PInvokeData("FileAPI.h", MSDNShortId = "ms724280")] [DllImport(Lib.Kernel32, ExactSpelling = true, SetLastError = true), SuppressUnmanagedCodeSecurity] @@ -73,8 +81,8 @@ namespace Vanara.PInvoke public static extern bool FileTimeToSystemTime(in FILETIME lpFileTime, out SYSTEMTIME lpSystemTime); /// - /// Retrieves the current time zone and dynamic daylight saving time settings. These settings control the translations between Coordinated Universal Time - /// (UTC) and local time. + /// Retrieves the current time zone and dynamic daylight saving time settings. These settings control the translations between + /// Coordinated Universal Time (UTC) and local time. /// /// A pointer to a DYNAMIC_TIME_ZONE_INFORMATION structure. /// @@ -104,10 +112,13 @@ namespace Vanara.PInvoke // DWORD WINAPI GetDynamicTimeZoneInformation( _Out_ PDYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation); https://msdn.microsoft.com/en-us/library/windows/desktop/ms724318(v=vs.85).aspx [DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)] [PInvokeData("Winbase.h", MSDNShortId = "ms724318")] - public static extern int GetDynamicTimeZoneInformation(out DYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation); + public static extern TZID GetDynamicTimeZoneInformation(out DYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation); /// - /// Retrieves the current time zone settings. These settings control the translations between Coordinated Universal Time (UTC) and local time. + /// + /// Retrieves the current time zone settings. These settings control the translations between Coordinated Universal Time (UTC) and + /// local time. + /// /// /// To support boundaries for daylight saving time that change from year to year, use the GetDynamicTimeZoneInformation or /// GetTimeZoneInformationForYear function. @@ -125,7 +136,8 @@ namespace Vanara.PInvoke /// /// TIME_ZONE_ID_UNKNOWN0 /// - /// Daylight saving time is not used in the current time zone, because there are no transition dates or automatic adjustment for daylight saving time is disabled. + /// Daylight saving time is not used in the current time zone, because there are no transition dates or automatic adjustment for + /// daylight saving time is disabled. /// /// /// @@ -139,7 +151,8 @@ namespace Vanara.PInvoke /// /// /// - /// If the function fails for other reasons, such as an out of memory error, it returns TIME_ZONE_ID_INVALID. To get extended error information, call GetLastError. + /// If the function fails for other reasons, such as an out of memory error, it returns TIME_ZONE_ID_INVALID. To get extended error + /// information, call GetLastError. /// /// // DWORD WINAPI GetTimeZoneInformation( _Out_ LPTIME_ZONE_INFORMATION lpTimeZoneInformation); https://msdn.microsoft.com/en-us/library/windows/desktop/ms724421(v=vs.85).aspx @@ -148,28 +161,78 @@ namespace Vanara.PInvoke public static extern TZID GetTimeZoneInformation(out TIME_ZONE_INFORMATION lpTimeZoneInformation); /// - /// Retrieves the time zone settings for the specified year and time zone. These settings control the translations between Coordinated Universal Time - /// (UTC) and local time. + /// Retrieves the time zone settings for the specified year and time zone. These settings control the translations between + /// Coordinated Universal Time (UTC) and local time. /// - /// The year for which the time zone settings are to be retrieved. The wYear parameter must be a local time value. - /// - /// A pointer to a DYNAMIC_TIME_ZONE_INFORMATION structure that specifies the time zone. To populate this parameter, call - /// EnumDynamicTimeZoneInformation with the index of the time zone you want. If this parameter is NULL, the current time zone is used. + /// + /// The year for which the time zone settings are to be retrieved. The wYear parameter must be a local time value. /// - /// A pointer to a TIME_ZONE_INFORMATION structure that receives the time zone settings. + /// + /// A pointer to a DYNAMIC_TIME_ZONE_INFORMATION structure that specifies the time zone. To populate this parameter, call + /// EnumDynamicTimeZoneInformation with the index of the time zone you want. If this parameter is NULL, the current time zone + /// is used. + /// + /// A pointer to a TIME_ZONE_INFORMATION structure that receives the time zone settings. /// /// If the function succeeds, the return value is nonzero. - /// If the function fails, the return value is zero. To get extended error information, call GetLastError. + /// If the function fails, the return value is zero. To get extended error information, call GetLastError. /// - // BOOL WINAPI GetTimeZoneInformationForYear( _In_ USHORT wYear, _In_opt_ PDYNAMIC_TIME_ZONE_INFORMATION pdtzi, _Out_ LPTIME_ZONE_INFORMATION ptzi); https://msdn.microsoft.com/en-us/library/windows/desktop/bb540851(v=vs.85).aspx + /// + /// + /// The wYear parameter is assumed to be a local time value. If the local time is close to the transition between the old year and + /// the new year (00:00:00 January 1), passing a UTC year to the GetTimeZoneInformationForYear function can cause the function + /// to return time zone settings for the wrong year. + /// + /// + /// The StandardName and DaylightName members of the resultant TIME_ZONE_INFORMATION structure are localized according + /// to the current user default UI language. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-gettimezoneinformationforyear BOOL + // GetTimeZoneInformationForYear( USHORT wYear, PDYNAMIC_TIME_ZONE_INFORMATION pdtzi, LPTIME_ZONE_INFORMATION ptzi ); [DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)] - [PInvokeData("Winbase.h", MSDNShortId = "bb540851")] + [PInvokeData("timezoneapi.h", MSDNShortId = "5bd29a25-98f0-439e-be88-8011bbf98926")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetTimeZoneInformationForYear(ushort wYear, in DYNAMIC_TIME_ZONE_INFORMATION pdtzi, out TIME_ZONE_INFORMATION ptzi); /// - /// Sets the current time zone and dynamic daylight saving time settings. These settings control translations from Coordinated Universal Time (UTC) to - /// local time. + /// Retrieves the time zone settings for the specified year and time zone. These settings control the translations between + /// Coordinated Universal Time (UTC) and local time. + /// + /// + /// The year for which the time zone settings are to be retrieved. The wYear parameter must be a local time value. + /// + /// + /// A pointer to a DYNAMIC_TIME_ZONE_INFORMATION structure that specifies the time zone. To populate this parameter, call + /// EnumDynamicTimeZoneInformation with the index of the time zone you want. If this parameter is NULL, the current time zone + /// is used. + /// + /// A pointer to a TIME_ZONE_INFORMATION structure that receives the time zone settings. + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. To get extended error information, call GetLastError. + /// + /// + /// + /// The wYear parameter is assumed to be a local time value. If the local time is close to the transition between the old year and + /// the new year (00:00:00 January 1), passing a UTC year to the GetTimeZoneInformationForYear function can cause the function + /// to return time zone settings for the wrong year. + /// + /// + /// The StandardName and DaylightName members of the resultant TIME_ZONE_INFORMATION structure are localized according + /// to the current user default UI language. + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-gettimezoneinformationforyear BOOL + // GetTimeZoneInformationForYear( USHORT wYear, PDYNAMIC_TIME_ZONE_INFORMATION pdtzi, LPTIME_ZONE_INFORMATION ptzi ); + [DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)] + [PInvokeData("timezoneapi.h", MSDNShortId = "5bd29a25-98f0-439e-be88-8011bbf98926")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetTimeZoneInformationForYear(ushort wYear, [Optional] IntPtr pdtzi, out TIME_ZONE_INFORMATION ptzi); + + /// + /// Sets the current time zone and dynamic daylight saving time settings. These settings control translations from Coordinated + /// Universal Time (UTC) to local time. /// /// A pointer to a DYNAMIC_TIME_ZONE_INFORMATION structure. /// @@ -203,12 +266,13 @@ namespace Vanara.PInvoke /// Converts a system time to file time format. System time is based on Coordinated Universal Time (UTC). /// - /// A pointer to a SYSTEMTIME structure that contains the system time to be converted from UTC to file time format. The wDayOfWeek member of the - /// SYSTEMTIME structure is ignored. + /// A pointer to a SYSTEMTIME structure that contains the system time to be converted from UTC to file time format. The wDayOfWeek + /// member of the SYSTEMTIME structure is ignored. /// /// A pointer to a FILETIME structure to receive the converted system time. /// - /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.To get extended error information, call GetLastError. + /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.To get extended error + /// information, call GetLastError. /// [DllImport(Lib.Kernel32, ExactSpelling = true, SetLastError = true), SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] @@ -216,43 +280,104 @@ namespace Vanara.PInvoke public static extern bool SystemTimeToFileTime(in SYSTEMTIME lpSystemTime, out FILETIME lpFileTime); /// Converts a time in Coordinated Universal Time (UTC) to a specified time zone's corresponding local time. - /// - /// A pointer to a TIME_ZONE_INFORMATION structure that specifies the time zone of interest. + /// + /// A pointer to a TIME_ZONE_INFORMATION structure that specifies the time zone of interest. /// If lpTimeZone is NULL, the function uses the currently active time zone. /// /// - /// A pointer to a SYSTEMTIME structure that specifies the UTC time to be converted. The function converts this universal time to the specified - /// time zone's corresponding local time. + /// A pointer to a SYSTEMTIME structure that specifies the UTC time to be converted. The function converts this universal time to the + /// specified time zone's corresponding local time. /// - /// A pointer to a SYSTEMTIME structure that receives the local time. + /// A pointer to a SYSTEMTIME structure that receives the local time. /// /// - /// If the function succeeds, the return value is nonzero, and the function sets the members of the SYSTEMTIME structure pointed to by lpLocalTime - /// to the appropriate local time values. + /// If the function succeeds, the return value is nonzero, and the function sets the members of the SYSTEMTIME structure pointed to + /// by lpLocalTime to the appropriate local time values. /// - /// If the function fails, the return value is zero. To get extended error information, call GetLastError. + /// If the function fails, the return value is zero. To get extended error information, call GetLastError. /// - // BOOL WINAPI SystemTimeToTzSpecificLocalTime( _In_opt_ LPTIME_ZONE_INFORMATION lpTimeZone, _In_ LPSYSTEMTIME lpUniversalTime, _Out_ LPSYSTEMTIME lpLocalTime); + /// + /// + /// The SystemTimeToTzSpecificLocalTime function takes into account whether daylight saving time (DST) is in effect for the + /// local time to which the system time is to be converted. + /// + /// The SystemTimeToTzSpecificLocalTime function may calculate the local time incorrectly under the following conditions: + /// + /// + /// The time zone uses a different UTC offset for the old and new years. + /// + /// + /// The UTC time to be converted and the calculated local time are in different years. + /// + /// + /// Examples + /// For an example, see Retrieving the Last-Write Time. + /// + // https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetotzspecificlocaltime BOOL + // SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION *lpTimeZoneInformation, const SYSTEMTIME *lpUniversalTime, + // LPSYSTEMTIME lpLocalTime ); [DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)] - [PInvokeData("Winbase.h", MSDNShortId = "ms724949")] + [PInvokeData("timezoneapi.h", MSDNShortId = "f3a87ec2-67a0-418f-af6e-6c0b5547cffb")] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool SystemTimeToTzSpecificLocalTime(in TIME_ZONE_INFORMATION lpTimeZone, - in SYSTEMTIME lpUniversalTime, [Out] out SYSTEMTIME lpLocalTime); + public static extern bool SystemTimeToTzSpecificLocalTime(in TIME_ZONE_INFORMATION lpTimeZone, in SYSTEMTIME lpUniversalTime, [Out] out SYSTEMTIME lpLocalTime); + + /// Converts a time in Coordinated Universal Time (UTC) to a specified time zone's corresponding local time. + /// + /// A pointer to a TIME_ZONE_INFORMATION structure that specifies the time zone of interest. + /// If lpTimeZone is NULL, the function uses the currently active time zone. + /// + /// + /// A pointer to a SYSTEMTIME structure that specifies the UTC time to be converted. The function converts this universal time to the + /// specified time zone's corresponding local time. + /// + /// A pointer to a SYSTEMTIME structure that receives the local time. + /// + /// + /// If the function succeeds, the return value is nonzero, and the function sets the members of the SYSTEMTIME structure pointed to + /// by lpLocalTime to the appropriate local time values. + /// + /// If the function fails, the return value is zero. To get extended error information, call GetLastError. + /// + /// + /// + /// The SystemTimeToTzSpecificLocalTime function takes into account whether daylight saving time (DST) is in effect for the + /// local time to which the system time is to be converted. + /// + /// The SystemTimeToTzSpecificLocalTime function may calculate the local time incorrectly under the following conditions: + /// + /// + /// The time zone uses a different UTC offset for the old and new years. + /// + /// + /// The UTC time to be converted and the calculated local time are in different years. + /// + /// + /// Examples + /// For an example, see Retrieving the Last-Write Time. + /// + // https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetotzspecificlocaltime BOOL + // SystemTimeToTzSpecificLocalTime( const TIME_ZONE_INFORMATION *lpTimeZoneInformation, const SYSTEMTIME *lpUniversalTime, + // LPSYSTEMTIME lpLocalTime ); + [DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)] + [PInvokeData("timezoneapi.h", MSDNShortId = "f3a87ec2-67a0-418f-af6e-6c0b5547cffb")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SystemTimeToTzSpecificLocalTime([Optional] IntPtr lpTimeZone, in SYSTEMTIME lpUniversalTime, [Out] out SYSTEMTIME lpLocalTime); /// - /// Converts a time in Coordinated Universal Time (UTC) with dynamic daylight saving time settings to a specified time zone's corresponding local time. + /// Converts a time in Coordinated Universal Time (UTC) with dynamic daylight saving time settings to a specified time zone's + /// corresponding local time. /// /// /// A pointer to a DYNAMIC_TIME_ZONE_INFORMATION structure that specifies the time zone and dynamic daylight saving time. /// /// - /// A pointer to a SYSTEMTIME structure that specifies the UTC time to be converted. The function converts this universal time to the specified - /// time zone's corresponding local time. + /// A pointer to a SYSTEMTIME structure that specifies the UTC time to be converted. The function converts this universal time + /// to the specified time zone's corresponding local time. /// /// A pointer to a SYSTEMTIME structure that receives the local time. /// If the function fails, the return value is zero. To get extended error information, call GetLastError. - // BOOL WINAPI SystemTimeToTzSpecificLocalTimeEx( _In_opt_ const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, _In_ const SYSTEMTIME* - // lpUniversalTime, _Out_ LPSYSTEMTIME lpLocalTime ); https://msdn.microsoft.com/en-us/library/windows/desktop/jj206642(v=vs.85).aspx + // BOOL WINAPI SystemTimeToTzSpecificLocalTimeEx( _In_opt_ const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, _In_ const + // SYSTEMTIME* lpUniversalTime, _Out_ LPSYSTEMTIME lpLocalTime ); https://msdn.microsoft.com/en-us/library/windows/desktop/jj206642(v=vs.85).aspx [DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)] [PInvokeData("Winbase.h", MSDNShortId = "jj206642")] [return: MarshalAs(UnmanagedType.Bool)] @@ -267,7 +392,8 @@ namespace Vanara.PInvoke /// /// /// - /// A pointer to a SYSTEMTIME structure that specifies the local time to be converted. The function converts this time to the corresponding UTC time. + /// A pointer to a SYSTEMTIME structure that specifies the local time to be converted. The function converts this time to the + /// corresponding UTC time. /// /// /// @@ -275,23 +401,55 @@ namespace Vanara.PInvoke /// /// /// - /// If the function succeeds, the return value is nonzero, and the function sets the members of the SYSTEMTIME structure pointed to by - /// lpUniversalTime to the appropriate values. + /// If the function succeeds, the return value is nonzero, and the function sets the members of the SYSTEMTIME structure + /// pointed to by lpUniversalTime to the appropriate values. /// /// If the function fails, the return value is zero. To get extended error information, call GetLastError. /// - // BOOL WINAPI TzSpecificLocalTimeToSystemTime( _In_opt_ LPTIME_ZONE_INFORMATION lpTimeZoneInformation, _In_ LPSYSTEMTIME lpLocalTime, _Out_ LPSYSTEMTIME lpUniversalTime); + // BOOL WINAPI TzSpecificLocalTimeToSystemTime( _In_opt_ LPTIME_ZONE_INFORMATION lpTimeZoneInformation, _In_ LPSYSTEMTIME + // lpLocalTime, _Out_ LPSYSTEMTIME lpUniversalTime); [DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)] [PInvokeData("Winbase.h", MSDNShortId = "ms725485")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool TzSpecificLocalTimeToSystemTime(in TIME_ZONE_INFORMATION lpTimeZoneInformation, in SYSTEMTIME lpLocalTime, [Out] out SYSTEMTIME lpUniversalTime); + /// + /// Converts a local time to a time in Coordinated Universal Time (UTC). + /// + /// + /// A pointer to a TIME_ZONE_INFORMATION structure that specifies the time zone for the time specified in lpLocalTime. + /// If lpTimeZoneInformation is NULL, the function uses the currently active time zone. + /// + /// + /// + /// A pointer to a SYSTEMTIME structure that specifies the local time to be converted. The function converts this time to the + /// corresponding UTC time. + /// + /// + /// + /// A pointer to a SYSTEMTIME structure that receives the UTC time. + /// + /// + /// + /// If the function succeeds, the return value is nonzero, and the function sets the members of the SYSTEMTIME structure + /// pointed to by lpUniversalTime to the appropriate values. + /// + /// If the function fails, the return value is zero. To get extended error information, call GetLastError. + /// + // BOOL WINAPI TzSpecificLocalTimeToSystemTime( _In_opt_ LPTIME_ZONE_INFORMATION lpTimeZoneInformation, _In_ LPSYSTEMTIME + // lpLocalTime, _Out_ LPSYSTEMTIME lpUniversalTime); + [DllImport(Lib.Kernel32, SetLastError = true, ExactSpelling = true)] + [PInvokeData("Winbase.h", MSDNShortId = "ms725485")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool TzSpecificLocalTimeToSystemTime([Optional] IntPtr lpTimeZoneInformation, in SYSTEMTIME lpLocalTime, [Out] out SYSTEMTIME lpUniversalTime); + /// Converts a local time to a time with dynamic daylight saving time settings to Coordinated Universal Time (UTC). /// /// A pointer to a DYNAMIC_TIME_ZONE_INFORMATION structure that specifies the time zone and dynamic daylight saving time. /// /// - /// A pointer to a SYSTEMTIME structure that specifies the local time to be converted. The function converts this time to the corresponding UTC time. + /// A pointer to a SYSTEMTIME structure that specifies the local time to be converted. The function converts this time to the + /// corresponding UTC time. /// /// A pointer to a SYSTEMTIME structure that receives the UTC time. /// If the function fails, the return value is zero. To get extended error information, call GetLastError. @@ -303,8 +461,9 @@ namespace Vanara.PInvoke public static extern bool TzSpecificLocalTimeToSystemTimeEx(in DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation, in SYSTEMTIME lpLocalTime, out SYSTEMTIME lpUniversalTime); /// Specifies settings for a time zone and dynamic daylight saving time. - // typedef struct _TIME_DYNAMIC_ZONE_INFORMATION { LONG Bias; WCHAR StandardName[32]; SYSTEMTIME StandardDate; LONG StandardBias; WCHAR DaylightName[32]; - // SYSTEMTIME DaylightDate; LONG DaylightBias; WCHAR TimeZoneKeyName[128]; BOOLEAN DynamicDaylightTimeDisabled;} DYNAMIC_TIME_ZONE_INFORMATION, + // typedef struct _TIME_DYNAMIC_ZONE_INFORMATION { LONG Bias; WCHAR StandardName[32]; SYSTEMTIME StandardDate; LONG StandardBias; + // WCHAR DaylightName[32]; SYSTEMTIME DaylightDate; LONG DaylightBias; WCHAR TimeZoneKeyName[128]; BOOLEAN + // DynamicDaylightTimeDisabled;} DYNAMIC_TIME_ZONE_INFORMATION, // *PDYNAMIC_TIME_ZONE_INFORMATION; https://msdn.microsoft.com/en-us/library/windows/desktop/ms724253(v=vs.85).aspx [PInvokeData("WinBase.h", MSDNShortId = "ms724253")] [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)] @@ -312,110 +471,122 @@ namespace Vanara.PInvoke { /// /// - /// The current bias for local time translation on this computer, in minutes. The bias is the difference, in minutes, between Coordinated Universal - /// Time (UTC) and local time. All translations between UTC and local time are based on the following formula: + /// The current bias for local time translation on this computer, in minutes. The bias is the difference, in minutes, between + /// Coordinated Universal Time (UTC) and local time. All translations between UTC and local time are based on the following formula: /// /// UTC = local time + bias /// This member is required. /// public int Bias; + /// - /// A description for standard time. For example, "EST" could indicate Eastern Standard Time. The string will be returned unchanged by the - /// GetDynamicTimeZoneInformation function. This string can be empty. + /// A description for standard time. For example, "EST" could indicate Eastern Standard Time. The string will be returned + /// unchanged by the GetDynamicTimeZoneInformation function. This string can be empty. /// [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string StandardName; + /// /// - /// A SYSTEMTIME structure that contains a date and local time when the transition from daylight saving time to standard time occurs on this - /// operating system. If the time zone does not support daylight saving time or if the caller needs to disable daylight saving time, the - /// wMonth member in the SYSTEMTIME structure must be zero. If this date is specified, the DaylightDate member of this structure - /// must also be specified. Otherwise, the system assumes the time zone data is invalid and no changes will be applied. + /// A SYSTEMTIME structure that contains a date and local time when the transition from daylight saving time to standard + /// time occurs on this operating system. If the time zone does not support daylight saving time or if the caller needs to + /// disable daylight saving time, the wMonth member in the SYSTEMTIME structure must be zero. If this date is + /// specified, the DaylightDate member of this structure must also be specified. Otherwise, the system assumes the time + /// zone data is invalid and no changes will be applied. /// /// - /// To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, - /// the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurrence of the day of the week within the - /// month (1 to 5, where 5 indicates the final occurrence during the month if that day of the week does not occur 5 times). + /// To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to + /// the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the + /// occurrence of the day of the week within the month (1 to 5, where 5 indicates the final occurrence during the month if that + /// day of the week does not occur 5 times). /// /// - /// Using this notation, specify 02:00 on the first Sunday in April as follows: wHour = 2, wMonth = 4, wDayOfWeek = 0, - /// wDay = 1. Specify 02:00 on the last Thursday in October as follows: wHour = 2, wMonth = 10, wDayOfWeek = 4, - /// wDay = 5. + /// Using this notation, specify 02:00 on the first Sunday in April as follows: wHour = 2, wMonth = 4, + /// wDayOfWeek = 0, wDay = 1. Specify 02:00 on the last Thursday in October as follows: wHour = 2, + /// wMonth = 10, wDayOfWeek = 4, wDay = 5. /// /// - /// If the wYear member is not zero, the transition date is absolute; it will only occur one time. Otherwise, it is a relative date that - /// occurs yearly. + /// If the wYear member is not zero, the transition date is absolute; it will only occur one time. Otherwise, it is a + /// relative date that occurs yearly. /// /// public SYSTEMTIME StandardDate; + /// /// - /// The bias value to be used during local time translations that occur during standard time. This member is ignored if a value for the - /// StandardDate member is not supplied. + /// The bias value to be used during local time translations that occur during standard time. This member is ignored if a value + /// for the StandardDate member is not supplied. /// /// - /// This value is added to the value of the Bias member to form the bias used during standard time. In most time zones, the value of this - /// member is zero. + /// This value is added to the value of the Bias member to form the bias used during standard time. In most time zones, + /// the value of this member is zero. /// /// public int StandardBias; + /// - /// A description for daylight saving time (DST). For example, "PDT" could indicate Pacific Daylight Time. The string will be returned unchanged by - /// the GetDynamicTimeZoneInformation function. This string can be empty. + /// A description for daylight saving time (DST). For example, "PDT" could indicate Pacific Daylight Time. The string will be + /// returned unchanged by the GetDynamicTimeZoneInformation function. This string can be empty. /// [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string DaylightName; + /// /// - /// A SYSTEMTIME structure that contains a date and local time when the transition from standard time to daylight saving time occurs on this - /// operating system. If the time zone does not support daylight saving time or if the caller needs to disable daylight saving time, the - /// wMonth member in the SYSTEMTIME structure must be zero. If this date is specified, the StandardDate member in this structure - /// must also be specified. Otherwise, the system assumes the time zone data is invalid and no changes will be applied. + /// A SYSTEMTIME structure that contains a date and local time when the transition from standard time to daylight saving + /// time occurs on this operating system. If the time zone does not support daylight saving time or if the caller needs to + /// disable daylight saving time, the wMonth member in the SYSTEMTIME structure must be zero. If this date is + /// specified, the StandardDate member in this structure must also be specified. Otherwise, the system assumes the time + /// zone data is invalid and no changes will be applied. /// /// - /// To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, - /// the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurrence of the day of the week within the - /// month (1 to 5, where 5 indicates the final occurrence during the month if that day of the week does not occur 5 times). + /// To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to + /// the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the + /// occurrence of the day of the week within the month (1 to 5, where 5 indicates the final occurrence during the month if that + /// day of the week does not occur 5 times). /// /// - /// If the wYear member is not zero, the transition date is absolute; it will only occur one time. Otherwise, it is a relative date that - /// occurs yearly. + /// If the wYear member is not zero, the transition date is absolute; it will only occur one time. Otherwise, it is a + /// relative date that occurs yearly. /// /// public SYSTEMTIME DaylightDate; + /// /// - /// The bias value to be used during local time translations that occur during daylight saving time. This member is ignored if a value for the - /// DaylightDate member is not supplied. + /// The bias value to be used during local time translations that occur during daylight saving time. This member is ignored if a + /// value for the DaylightDate member is not supplied. /// /// - /// This value is added to the value of the Bias member to form the bias used during daylight saving time. In most time zones, the value of - /// this member is –60. + /// This value is added to the value of the Bias member to form the bias used during daylight saving time. In most time + /// zones, the value of this member is –60. /// /// public int DaylightBias; + /// The name of the time zone registry key on the local computer. For more information, see Remarks. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string TimeZoneKeyName; + /// /// - /// Indicates whether dynamic daylight saving time is disabled. Setting this member to TRUE disables dynamic daylight saving time, causing the - /// system to use a fixed set of transition dates. + /// Indicates whether dynamic daylight saving time is disabled. Setting this member to TRUE disables dynamic daylight + /// saving time, causing the system to use a fixed set of transition dates. /// /// - /// To restore dynamic daylight saving time, call the SetDynamicTimeZoneInformation function with DynamicDaylightTimeDisabled set to - /// FALSE. The system will read the transition dates for the current year at the next time update, the next system reboot, or the end of the - /// calendar year (whichever comes first.) + /// To restore dynamic daylight saving time, call the SetDynamicTimeZoneInformation function with + /// DynamicDaylightTimeDisabled set to FALSE. The system will read the transition dates for the current year at the + /// next time update, the next system reboot, or the end of the calendar year (whichever comes first.) /// /// /// When calling the GetDynamicTimeZoneInformation function, this member is TRUE if the time zone was set using the - /// SetTimeZoneInformation function instead of SetDynamicTimeZoneInformation or if the user has disabled this feature using the Date - /// and Time application in Control Panel. + /// SetTimeZoneInformation function instead of SetDynamicTimeZoneInformation or if the user has disabled this + /// feature using the Date and Time application in Control Panel. /// /// - /// To disable daylight saving time, set this member to TRUE, clear the StandardDate and DaylightDate members, and call - /// SetDynamicTimeZoneInformation. To restore daylight saving time, call SetDynamicTimeZoneInformation with - /// DynamicDaylightTimeDisabled set to FALSE. + /// To disable daylight saving time, set this member to TRUE, clear the StandardDate and DaylightDate + /// members, and call SetDynamicTimeZoneInformation. To restore daylight saving time, call + /// SetDynamicTimeZoneInformation with DynamicDaylightTimeDisabled set to FALSE. /// /// [MarshalAs(UnmanagedType.U1)] @@ -425,97 +596,107 @@ namespace Vanara.PInvoke /// /// Specifies settings for a time zone. /// - // typedef struct _TIME_ZONE_INFORMATION { LONG Bias; WCHAR StandardName[32]; SYSTEMTIME StandardDate; LONG StandardBias; WCHAR DaylightName[32]; - // SYSTEMTIME DaylightDate; LONG DaylightBias;} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION; + // typedef struct _TIME_ZONE_INFORMATION { LONG Bias; WCHAR StandardName[32]; SYSTEMTIME StandardDate; LONG StandardBias; WCHAR + // DaylightName[32]; SYSTEMTIME DaylightDate; LONG DaylightBias;} TIME_ZONE_INFORMATION, *PTIME_ZONE_INFORMATION; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] [PInvokeData("Winbase.h", MSDNShortId = "ms725481")] public struct TIME_ZONE_INFORMATION { /// /// - /// The current bias for local time translation on this computer, in minutes. The bias is the difference, in minutes, between Coordinated Universal - /// Time (UTC) and local time. All translations between UTC and local time are based on the following formula: + /// The current bias for local time translation on this computer, in minutes. The bias is the difference, in minutes, between + /// Coordinated Universal Time (UTC) and local time. All translations between UTC and local time are based on the following formula: /// /// UTC = local time + bias /// This member is required. /// public int Bias; + /// /// - /// A description for standard time. For example, "EST" could indicate Eastern Standard Time. The string will be returned unchanged by the - /// GetTimeZoneInformation function. This string can be empty. + /// A description for standard time. For example, "EST" could indicate Eastern Standard Time. The string will be returned + /// unchanged by the GetTimeZoneInformation function. This string can be empty. /// /// [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string StandardName; + /// /// - /// A SYSTEMTIME structure that contains a date and local time when the transition from daylight saving time to standard time occurs on this - /// operating system. If the time zone does not support daylight saving time or if the caller needs to disable daylight saving time, the - /// wMonth member in the SYSTEMTIME structure must be zero. If this date is specified, the DaylightDate member of this structure - /// must also be specified. Otherwise, the system assumes the time zone data is invalid and no changes will be applied. + /// A SYSTEMTIME structure that contains a date and local time when the transition from daylight saving time to standard + /// time occurs on this operating system. If the time zone does not support daylight saving time or if the caller needs to + /// disable daylight saving time, the wMonth member in the SYSTEMTIME structure must be zero. If this date is + /// specified, the DaylightDate member of this structure must also be specified. Otherwise, the system assumes the time + /// zone data is invalid and no changes will be applied. /// /// - /// To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, - /// the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurrence of the day of the week within the - /// month (1 to 5, where 5 indicates the final occurrence during the month if that day of the week does not occur 5 times). + /// To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to + /// the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the + /// occurrence of the day of the week within the month (1 to 5, where 5 indicates the final occurrence during the month if that + /// day of the week does not occur 5 times). /// /// - /// Using this notation, specify 02:00 on the first Sunday in April as follows: wHour = 2, wMonth = 4, wDayOfWeek = 0, - /// wDay = 1. Specify 02:00 on the last Thursday in October as follows: wHour = 2, wMonth = 10, wDayOfWeek = 4, - /// wDay = 5. + /// Using this notation, specify 02:00 on the first Sunday in April as follows: wHour = 2, wMonth = 4, + /// wDayOfWeek = 0, wDay = 1. Specify 02:00 on the last Thursday in October as follows: wHour = 2, + /// wMonth = 10, wDayOfWeek = 4, wDay = 5. /// /// - /// If the wYear member is not zero, the transition date is absolute; it will only occur one time. Otherwise, it is a relative date that - /// occurs yearly. + /// If the wYear member is not zero, the transition date is absolute; it will only occur one time. Otherwise, it is a + /// relative date that occurs yearly. /// /// public SYSTEMTIME StandardDate; + /// /// - /// The bias value to be used during local time translations that occur during standard time. This member is ignored if a value for the - /// StandardDate member is not supplied. + /// The bias value to be used during local time translations that occur during standard time. This member is ignored if a value + /// for the StandardDate member is not supplied. /// /// - /// This value is added to the value of the Bias member to form the bias used during standard time. In most time zones, the value of this - /// member is zero. + /// This value is added to the value of the Bias member to form the bias used during standard time. In most time zones, + /// the value of this member is zero. /// /// public int StandardBias; + /// /// - /// A description for daylight saving time. For example, "PDT" could indicate Pacific Daylight Time. The string will be returned unchanged by the - /// GetTimeZoneInformation function. This string can be empty. + /// A description for daylight saving time. For example, "PDT" could indicate Pacific Daylight Time. The string will be returned + /// unchanged by the GetTimeZoneInformation function. This string can be empty. /// /// [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string DaylightName; + /// /// - /// A SYSTEMTIME structure that contains a date and local time when the transition from standard time to daylight saving time occurs on this - /// operating system. If the time zone does not support daylight saving time or if the caller needs to disable daylight saving time, the - /// wMonth member in the SYSTEMTIME structure must be zero. If this date is specified, the StandardDate member in this structure - /// must also be specified. Otherwise, the system assumes the time zone data is invalid and no changes will be applied. + /// A SYSTEMTIME structure that contains a date and local time when the transition from standard time to daylight saving + /// time occurs on this operating system. If the time zone does not support daylight saving time or if the caller needs to + /// disable daylight saving time, the wMonth member in the SYSTEMTIME structure must be zero. If this date is + /// specified, the StandardDate member in this structure must also be specified. Otherwise, the system assumes the time + /// zone data is invalid and no changes will be applied. /// /// - /// To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to the transition time, - /// the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the occurrence of the day of the week within the - /// month (1 to 5, where 5 indicates the final occurrence during the month if that day of the week does not occur 5 times). + /// To select the correct day in the month, set the wYear member to zero, the wHour and wMinute members to + /// the transition time, the wDayOfWeek member to the appropriate weekday, and the wDay member to indicate the + /// occurrence of the day of the week within the month (1 to 5, where 5 indicates the final occurrence during the month if that + /// day of the week does not occur 5 times). /// /// - /// If the wYear member is not zero, the transition date is absolute; it will only occur one time. Otherwise, it is a relative date that - /// occurs yearly. + /// If the wYear member is not zero, the transition date is absolute; it will only occur one time. Otherwise, it is a + /// relative date that occurs yearly. /// /// public SYSTEMTIME DaylightDate; + /// /// - /// The bias value to be used during local time translations that occur during daylight saving time. This member is ignored if a value for the - /// DaylightDate member is not supplied. + /// The bias value to be used during local time translations that occur during daylight saving time. This member is ignored if a + /// value for the DaylightDate member is not supplied. /// /// - /// This value is added to the value of the Bias member to form the bias used during daylight saving time. In most time zones, the value of - /// this member is –60. + /// This value is added to the value of the Bias member to form the bias used during daylight saving time. In most time + /// zones, the value of this member is –60. /// /// public int DaylightBias; diff --git a/UnitTests/PInvoke/Kernel32/Kernel32.csproj b/UnitTests/PInvoke/Kernel32/Kernel32.csproj index f2e8497f..a1e30b12 100644 --- a/UnitTests/PInvoke/Kernel32/Kernel32.csproj +++ b/UnitTests/PInvoke/Kernel32/Kernel32.csproj @@ -48,6 +48,7 @@ + diff --git a/UnitTests/PInvoke/Kernel32/Kernel32Tests.cs b/UnitTests/PInvoke/Kernel32/Kernel32Tests.cs index a4071fb1..c7e78444 100644 --- a/UnitTests/PInvoke/Kernel32/Kernel32Tests.cs +++ b/UnitTests/PInvoke/Kernel32/Kernel32Tests.cs @@ -69,18 +69,6 @@ namespace Vanara.PInvoke.Tests File.Delete(fn); } - [Test] - public void FileTimeToSystemTimeTest() - { - var dt = DateTime.Today; - var ft = dt.ToFileTimeStruct(); - var b = FileTimeToSystemTime(ft, out var st); - if (!b) TestContext.WriteLine($"FileTimeToSystemTime:{Win32Error.GetLastError()}"); - Assert.That(b); - Assert.That(dt.Year, Is.EqualTo(st.wYear)); - Assert.That(dt.Day, Is.EqualTo(st.wDay)); - } - [Test] public void GetCompressedFileSizeTest() { @@ -155,16 +143,5 @@ namespace Vanara.PInvoke.Tests SetLastError(Win32Error.ERROR_AUDIT_FAILED); Assert.That((int)Win32Error.GetLastError(), Is.EqualTo(Win32Error.ERROR_AUDIT_FAILED)); } - - [Test] - public void SystemTimeToFileTimeTest() - { - var dt = new DateTime(2000, 1, 1, 4, 4, 4, 444, DateTimeKind.Utc); - var st = new SYSTEMTIME(dt, DateTimeKind.Utc); - Assert.That(st.ToString(DateTimeKind.Utc, null, null), Is.EqualTo(dt.ToString())); - var b = SystemTimeToFileTime(st, out var ft); - Assert.That(b, Is.True); - Assert.That(FileTimeExtensions.Equals(ft, dt.ToFileTimeStruct())); - } } } \ No newline at end of file diff --git a/UnitTests/PInvoke/Kernel32/TimeZoneApiTests.cs b/UnitTests/PInvoke/Kernel32/TimeZoneApiTests.cs new file mode 100644 index 00000000..65711359 --- /dev/null +++ b/UnitTests/PInvoke/Kernel32/TimeZoneApiTests.cs @@ -0,0 +1,124 @@ +using NUnit.Framework; +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using Vanara.Extensions; +using Vanara.InteropServices; +using static Vanara.PInvoke.AdvApi32; +using static Vanara.PInvoke.Kernel32; + +namespace Vanara.PInvoke.Tests +{ + [TestFixture] + public class TimeZoneApiTests + { + [Test] + public void EnumDynamicTimeZoneInformationTest() + { + Assert.That(EnumDynamicTimeZoneInformation(), Is.Not.Empty); + EnumDynamicTimeZoneInformation().ToArray().WriteValues(); + } + + [Test] + public void FileTimeToSystemTimeTest() + { + var dt = DateTime.Today; + var ft = dt.ToFileTimeStruct(); + Assert.That(FileTimeToSystemTime(ft, out var st), ResultIs.Successful); + Assert.That(dt.Year, Is.EqualTo(st.wYear)); + Assert.That(dt.Day, Is.EqualTo(st.wDay)); + } + + [Test] + public void GetSetDynamicTimeZoneInformationTest() + { + // Get a random ref + var dtz = EnumDynamicTimeZoneInformation().First(); + + // Get current + Assert.That(GetDynamicTimeZoneInformation(out var tz), Is.Not.EqualTo(TZID.TIME_ZONE_ID_INVALID)); + Assert.That(tz.StandardName, Is.Not.Null.Or.Empty); + + using (new PrivBlock("SeTimeZonePrivilege")) + { + // Set to random + Assert.That(SetDynamicTimeZoneInformation(dtz), ResultIs.Successful); + + // Restore to current + Assert.That(SetDynamicTimeZoneInformation(tz), ResultIs.Successful); + } + } + + [Test] + public void GetSetTimeZoneInformationTest() + { + Assert.That(GetTimeZoneInformation(out var tziOld), Is.Not.EqualTo(TZID.TIME_ZONE_ID_INVALID)); + Assert.That(tziOld.StandardName, Is.Not.Null.Or.Empty); + + using (new PrivBlock("SeTimeZonePrivilege")) + { + // Build new test tz + var tziNew = new TIME_ZONE_INFORMATION + { + Bias = tziOld.Bias + 60, + StandardName = "Test Standard Zone", + StandardDate = new SYSTEMTIME(0, 10, 5, 2), + DaylightName = "Test Daylight Zone", + DaylightDate = new SYSTEMTIME(0, 4, 1, 2), + DaylightBias = -60, + }; + + // Set to new + Assert.That(SetTimeZoneInformation(tziNew), ResultIs.Successful); + + // Restore to current + Assert.That(SetTimeZoneInformation(tziOld), ResultIs.Successful); + } + } + + [Test] + public void GetTimeZoneInformationForYearTest() + { + Assert.That(GetTimeZoneInformationForYear(2016, IntPtr.Zero, out var tz), ResultIs.Successful); + Assert.That(tz.StandardName, Is.Not.Null.Or.Empty); + } + + [Test] + public void SystemTimeToFileTimeTest() + { + var dt = new DateTime(2000, 1, 1, 4, 4, 4, 444, DateTimeKind.Utc); + var st = new SYSTEMTIME(dt, DateTimeKind.Utc); + Assert.That(st.ToString(DateTimeKind.Utc, null, null), Is.EqualTo(dt.ToString())); + Assert.That(SystemTimeToFileTime(st, out var ft), ResultIs.Successful); + Assert.That(FileTimeExtensions.Equals(ft, dt.ToFileTimeStruct())); + } + + [Test] + public void SystemTimeToTzSpecificLocalTimeTest() + { + var udt = DateTime.UtcNow.AddHours(100); + var ut = new SYSTEMTIME(udt, DateTimeKind.Utc); + Assert.That(SystemTimeToTzSpecificLocalTime(default, ut, out var lt), ResultIs.Successful); + Assert.That(lt.wHour, Is.EqualTo(udt.ToLocalTime().Hour)); + + Assert.That(TzSpecificLocalTimeToSystemTime(default, lt, out var nut), ResultIs.Successful); + Assert.That(nut, Is.EqualTo(ut)); + } + + [Test] + public void SystemTimeToTzSpecificLocalTimeExTest() + { + // Get a random ref + var dtz = EnumDynamicTimeZoneInformation().First(); + + var udt = DateTime.UtcNow.AddHours(100); + var ut = new SYSTEMTIME(udt, DateTimeKind.Utc); + Assert.That(SystemTimeToTzSpecificLocalTimeEx(dtz, ut, out var lt), ResultIs.Successful); + Assert.That(lt.wYear, Is.EqualTo(udt.ToLocalTime().Year)); + + Assert.That(TzSpecificLocalTimeToSystemTimeEx(dtz, lt, out var nut), ResultIs.Successful); + Assert.That(nut, Is.EqualTo(ut)); + } + } +} \ No newline at end of file