[NTOS:EX] Various fixes for Daylight Saving Time transitions (#5868)

* [NTOS:EX] Various fixes for Daylight Saving Time transitions

* Fix automatic time changes when DST starts and ends.
* Currently it takes two cycles of the update provided by w32time to make the actual change.
* Convert Eric Kohl's TimeZoneID code contained in ExRefreshTimeZoneInformation to a new function.
* Use this new function in ExpSetTimeZoneInformation and ExRefreshTimeZoneInformation functions.

CORE-19290
This commit is contained in:
Doug Lyons 2024-05-05 21:32:04 -05:00 committed by GitHub
parent 28eb4e726a
commit 69ebfd671d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -30,6 +30,100 @@ ULONG ExpTimerResolutionCount = 0;
/* FUNCTIONS ****************************************************************/
/*++
* If successful, this function sets the following global variable:
* ExpTimeZoneInfo
*--*/
static
BOOLEAN
ExpGetTimeZoneId(
_In_ PLARGE_INTEGER TimeNow,
_Out_ PULONG TimeZoneId,
_Out_ PLARGE_INTEGER NewTimeZoneBias)
{
LARGE_INTEGER StandardTime;
LARGE_INTEGER DaylightTime;
LARGE_INTEGER LocalTimeNow = *TimeNow;
NTSTATUS Status;
DPRINT("ExpGetTimeZoneId\n");
/* Read time zone information from the registry */
Status = RtlQueryTimeZoneInformation(&ExpTimeZoneInfo);
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlQueryTimeZoneInformation failed (Status 0x%08lx)\n", Status);
return FALSE;
}
/* Get the default bias */
NewTimeZoneBias->QuadPart = (LONGLONG)ExpTimeZoneInfo.Bias * TICKSPERMINUTE;
if (ExpTimeZoneInfo.StandardDate.Month != 0 &&
ExpTimeZoneInfo.DaylightDate.Month != 0)
{
/* Get this year's standard start time */
if (!RtlCutoverTimeToSystemTime(&ExpTimeZoneInfo.StandardDate,
&StandardTime,
&LocalTimeNow,
TRUE))
{
DPRINT1("RtlCutoverTimeToSystemTime for StandardDate failed\n");
return FALSE;
}
/* Get this year's daylight start time */
if (!RtlCutoverTimeToSystemTime(&ExpTimeZoneInfo.DaylightDate,
&DaylightTime,
&LocalTimeNow,
TRUE))
{
DPRINT1("RtlCutoverTimeToSystemTime for DaylightDate failed\n");
return FALSE;
}
/* Determine the time zone id and update the time zone bias */
if (DaylightTime.QuadPart < StandardTime.QuadPart)
{
if ((LocalTimeNow.QuadPart >= DaylightTime.QuadPart) &&
(LocalTimeNow.QuadPart < StandardTime.QuadPart))
{
DPRINT("Daylight time\n");
*TimeZoneId = TIME_ZONE_ID_DAYLIGHT;
NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
}
else
{
DPRINT("Standard time\n");
*TimeZoneId = TIME_ZONE_ID_STANDARD;
NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.StandardBias * TICKSPERMINUTE;
}
}
else
{
if ((LocalTimeNow.QuadPart >= StandardTime.QuadPart) &&
(LocalTimeNow.QuadPart < DaylightTime.QuadPart))
{
DPRINT("Standard time\n");
*TimeZoneId = TIME_ZONE_ID_STANDARD;
NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.StandardBias * TICKSPERMINUTE;
}
else
{
DPRINT("Daylight time\n");
*TimeZoneId = TIME_ZONE_ID_DAYLIGHT;
NewTimeZoneBias->QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
}
}
}
else
{
*TimeZoneId = TIME_ZONE_ID_UNKNOWN;
}
return TRUE;
}
/*++
* @name ExAcquireTimeRefreshLock
*
@ -225,105 +319,39 @@ BOOLEAN
NTAPI
ExRefreshTimeZoneInformation(IN PLARGE_INTEGER CurrentBootTime)
{
LARGE_INTEGER StandardTime;
LARGE_INTEGER DaylightTime;
LARGE_INTEGER CurrentTime;
NTSTATUS Status;
LARGE_INTEGER CurrentTime, NewTimeZoneBias;
BOOLEAN Success;
/* Read time zone information from the registry */
Status = RtlQueryTimeZoneInformation(&ExpTimeZoneInfo);
if (!NT_SUCCESS(Status))
DPRINT("ExRefreshTimeZoneInformation\n");
/* Set the global data for ExpTimeZoneBias and the Time Zone ID */
Success = ExpGetTimeZoneId(CurrentBootTime, &ExpTimeZoneId, &NewTimeZoneBias);
if (!Success)
{
DPRINT1("RtlQueryTimeZoneInformation() failed (Status 0x%08lx)\n", Status);
DPRINT1("ExpGetTimeZoneId failed\n");
return FALSE;
}
DPRINT("ExpTimeZoneId is %lu\n", ExpTimeZoneId);
/* Get the default bias */
ExpTimeZoneBias.QuadPart = (LONGLONG)ExpTimeZoneInfo.Bias * TICKSPERMINUTE;
ExpTimeZoneBias = NewTimeZoneBias;
if (ExpTimeZoneInfo.StandardDate.Month != 0 &&
ExpTimeZoneInfo.DaylightDate.Month != 0)
{
/* Get this years standard start time */
if (!RtlCutoverTimeToSystemTime(&ExpTimeZoneInfo.StandardDate,
&StandardTime,
CurrentBootTime,
TRUE))
{
DPRINT1("RtlCutoverTimeToSystemTime() for StandardDate failed!\n");
return FALSE;
}
/* Get this years daylight start time */
if (!RtlCutoverTimeToSystemTime(&ExpTimeZoneInfo.DaylightDate,
&DaylightTime,
CurrentBootTime,
TRUE))
{
DPRINT1("RtlCutoverTimeToSystemTime() for DaylightDate failed!\n");
return FALSE;
}
/* Determine the time zone id and update the time zone bias */
if (DaylightTime.QuadPart < StandardTime.QuadPart)
{
if ((CurrentBootTime->QuadPart >= DaylightTime.QuadPart) &&
(CurrentBootTime->QuadPart < StandardTime.QuadPart))
{
DPRINT("Daylight time!\n");
ExpTimeZoneId = TIME_ZONE_ID_DAYLIGHT;
ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
}
else
{
DPRINT("Standard time!\n");
ExpTimeZoneId = TIME_ZONE_ID_STANDARD;
ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.StandardBias * TICKSPERMINUTE;
}
}
else
{
if ((CurrentBootTime->QuadPart >= StandardTime.QuadPart) &&
(CurrentBootTime->QuadPart < DaylightTime.QuadPart))
{
DPRINT("Standard time!\n");
ExpTimeZoneId = TIME_ZONE_ID_STANDARD;
ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.StandardBias * TICKSPERMINUTE;
}
else
{
DPRINT("Daylight time!\n");
ExpTimeZoneId = TIME_ZONE_ID_DAYLIGHT;
ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
}
}
}
else
{
ExpTimeZoneId = TIME_ZONE_ID_UNKNOWN;
}
/* Change it for user-mode applications */
SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.u.HighPart;
/* Change SharedUserData->TimeZoneBias for user-mode applications */
SharedUserData->TimeZoneBias.High2Time = ExpTimeZoneBias.u.HighPart;
SharedUserData->TimeZoneBias.LowPart = ExpTimeZoneBias.u.LowPart;
SharedUserData->TimeZoneBias.High1Time = ExpTimeZoneBias.u.HighPart;
SharedUserData->TimeZoneId = ExpTimeZoneId;
/* Convert boot time from local time to UTC */
KeBootTime.QuadPart += ExpTimeZoneBias.QuadPart;
/* Convert system time from local time to UTC */
do
{
CurrentTime.u.HighPart = SharedUserData->SystemTime.High1Time;
CurrentTime.u.LowPart = SharedUserData->SystemTime.LowPart;
} while (CurrentTime.u.HighPart != SharedUserData->SystemTime.High2Time);
KeQuerySystemTime(&CurrentTime);;
/* Change it for user-mode applications */
CurrentTime.QuadPart += ExpTimeZoneBias.QuadPart;
SharedUserData->SystemTime.High2Time = CurrentTime.u.HighPart;
SharedUserData->SystemTime.LowPart = CurrentTime.u.LowPart;
SharedUserData->SystemTime.High1Time = CurrentTime.u.HighPart;
SharedUserData->SystemTime.High2Time = CurrentTime.u.HighPart;
/* Return success */
return TRUE;
@ -332,9 +360,43 @@ ExRefreshTimeZoneInformation(IN PLARGE_INTEGER CurrentBootTime)
NTSTATUS
ExpSetTimeZoneInformation(PRTL_TIME_ZONE_INFORMATION TimeZoneInformation)
{
LARGE_INTEGER LocalTime, SystemTime, OldTime;
LARGE_INTEGER LocalTime, SystemTime, OldTime, NewTimeZoneBias;
TIME_FIELDS TimeFields;
DPRINT("ExpSetTimeZoneInformation() called\n");
BOOLEAN Success;
NTSTATUS Status;
LARGE_INTEGER LocalTimeNow;
DPRINT("ExpSetTimeZoneInformation called\n");
/* Read time zone information from the registry */
Status = RtlQueryTimeZoneInformation(&ExpTimeZoneInfo);
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlQueryTimeZoneInformation failed (Status 0x%08lx)\n", Status);
return FALSE;
}
/* Get the default bias */
NewTimeZoneBias.QuadPart = (LONGLONG)ExpTimeZoneInfo.Bias * TICKSPERMINUTE;
/* Get the Shared User Data System Time into the local SystemTime */
KeQuerySystemTime(&SystemTime);
LocalTimeNow = SystemTime;
/* Adjust LocalTimeNow for Time Zone Bias to use UTC for comparisons */
LocalTimeNow.QuadPart -= NewTimeZoneBias.QuadPart;
/* Set the Global Data for ExpTimeZoneBias and ExpTimeZoneId */
Success = ExpGetTimeZoneId(&LocalTimeNow, &ExpTimeZoneId, &NewTimeZoneBias);
if (!Success)
{
DPRINT1("ExpGetTimeZoneId failed\n");
return STATUS_UNSUCCESSFUL;
}
DPRINT("ExpTimeZoneId is %lu\n", ExpTimeZoneId);
ExpTimeZoneBias = NewTimeZoneBias;
DPRINT("Old time zone bias: %d minutes\n", ExpTimeZoneInfo.Bias);
DPRINT("Old time zone standard bias: %d minutes\n",
@ -347,13 +409,14 @@ ExpSetTimeZoneInformation(PRTL_TIME_ZONE_INFORMATION TimeZoneInformation)
HalQueryRealTimeClock(&TimeFields);
RtlTimeFieldsToTime(&TimeFields, &LocalTime);
/* FIXME: Calculate transition dates */
/* Calculate the bias and set the ID */
/* Calculate the bias */
ExpTimeZoneBias.QuadPart = ((LONGLONG)(TimeZoneInformation->Bias +
TimeZoneInformation->StandardBias)) *
TICKSPERMINUTE;
ExpTimeZoneId = TIME_ZONE_ID_STANDARD;
/* If Daylight Savings Time then add the DayLightBias to the Time Zone Bias */
if (ExpTimeZoneId == TIME_ZONE_ID_DAYLIGHT)
ExpTimeZoneBias.QuadPart += (LONGLONG)ExpTimeZoneInfo.DaylightBias * TICKSPERMINUTE;
/* Copy the timezone information */
RtlCopyMemory(&ExpTimeZoneInfo,
@ -377,7 +440,7 @@ ExpSetTimeZoneInformation(PRTL_TIME_ZONE_INFORMATION TimeZoneInformation)
PoNotifySystemTimeSet();
/* Return success */
DPRINT("ExpSetTimeZoneInformation() done\n");
DPRINT("ExpSetTimeZoneInformation done\n");
return STATUS_SUCCESS;
}
@ -401,6 +464,8 @@ NtSetSystemTime(IN PLARGE_INTEGER SystemTime,
TIME_FIELDS TimeFields;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status = STATUS_SUCCESS;
RTL_TIME_ZONE_INFORMATION TimeZoneInformation = { 0 };
ULONG TimeZoneIdSave;
PAGED_CODE();
@ -468,6 +533,27 @@ NtSetSystemTime(IN PLARGE_INTEGER SystemTime,
_SEH2_END;
}
/* Read time zone information from the registry and set the clock */
Status = RtlQueryTimeZoneInformation(&TimeZoneInformation);
if (!NT_SUCCESS(Status))
{
DPRINT1("RtlQueryTimeZoneInformation failed (Status 0x%08lx)\n", Status);
}
/* Test if we went from Daylight to Standard Time or vice versa */
TimeZoneIdSave = ExpTimeZoneId;
ExpSetTimeZoneInformation(&TimeZoneInformation);
if (ExpTimeZoneId != TimeZoneIdSave)
{
/* Going from DT to ST or vice versa we need to repeat this */
DPRINT("Daylight Time and Standard Time are switching\n");
/* Set the system time and notify the system */
KeSetSystemTime(&NewSystemTime, &OldSystemTime, FALSE, NULL);
PoNotifySystemTimeSet();
}
/* Return status */
return Status;
}