[winpr,timezone] fix transition date detection

This commit is contained in:
Armin Novak 2024-06-24 13:09:00 +02:00
parent 0810e7d944
commit 85078bf8c8
No known key found for this signature in database
GPG Key ID: 2CF4A2D2D3D72105

View File

@ -344,7 +344,7 @@ static SYSTEMTIME tm2systemtime(const struct tm* t)
if (t)
{
st.wYear = (WORD)1900 + t->tm_year;
st.wMonth = (WORD)t->tm_mon;
st.wMonth = (WORD)t->tm_mon + 1;
st.wDay = (WORD)t->tm_mday;
st.wDayOfWeek = (WORD)t->tm_wday;
st.wHour = (WORD)t->tm_hour;
@ -355,6 +355,24 @@ static SYSTEMTIME tm2systemtime(const struct tm* t)
return st;
}
static struct tm systemtime2tm(const SYSTEMTIME* st)
{
struct tm t = { 0 };
if (st)
{
if (st->wYear >= 1900)
t.tm_year = st->wYear - 1900;
if (st->wMonth > 0)
t.tm_mon = st->wMonth - 1;
t.tm_mday = st->wDay;
t.tm_wday = st->wDayOfWeek;
t.tm_hour = st->wHour;
t.tm_min = st->wMinute;
t.tm_sec = st->wSecond;
}
return t;
}
static LONG get_gmtoff_min(const struct tm* t)
{
WINPR_ASSERT(t);
@ -386,28 +404,74 @@ static struct tm adjust_time(const struct tm* start, int hour, int minute)
return cur;
}
/* [MS-RDPBCGR] 2.2.1.11.1.1.1.1.1 System Time (TS_SYSTEMTIME) */
static WORD get_transition_weekday_occurrence(const SYSTEMTIME* st)
{
WORD count = 0;
struct tm start = systemtime2tm(st);
WORD last = 0;
struct tm next = start;
next.tm_mday = 1;
next.tm_isdst = -1;
do
{
const time_t t = mktime(&next);
next.tm_mday++;
struct tm cur = { 0 };
localtime_r(&t, &cur);
if (cur.tm_mon + 1 != st->wMonth)
break;
if (cur.tm_wday == st->wDayOfWeek)
{
if (cur.tm_mday <= st->wDay)
count++;
last++;
}
} while (TRUE);
if (count == last)
count = 5;
return count;
}
static SYSTEMTIME tm2transitiontime(const struct tm* cur)
{
SYSTEMTIME t = tm2systemtime(cur);
if (cur)
{
t.wDay = get_transition_weekday_occurrence(&t);
t.wYear = 0;
}
return t;
}
static SYSTEMTIME get_transition_time(const struct tm* start, BOOL toDst)
{
BOOL toggled = FALSE;
struct tm first = adjust_time(start, 0, 0);
for (int hour = 0; hour < 24; hour++)
{
for (int minute = 0; minute < 60; minute++)
{
struct tm cur = adjust_time(start, hour, minute);
if (cur.tm_isdst != first.tm_isdst)
toggled = TRUE;
struct tm cur = adjust_time(start, hour, 0);
if (cur.tm_isdst != first.tm_isdst)
toggled = TRUE;
if (toggled)
{
if (toDst && (cur.tm_isdst > 0))
return tm2systemtime(&cur);
else if (!toDst && (cur.tm_isdst == 0))
return tm2systemtime(&cur);
}
if (toggled)
{
if (toDst && (cur.tm_isdst > 0))
return tm2transitiontime(&cur);
else if (!toDst && (cur.tm_isdst == 0))
return tm2transitiontime(&cur);
}
}
return tm2systemtime(start);
return tm2transitiontime(start);
}
static BOOL get_transition_date(const struct tm* start, BOOL toDst, SYSTEMTIME* pdate)
@ -415,7 +479,7 @@ static BOOL get_transition_date(const struct tm* start, BOOL toDst, SYSTEMTIME*
WINPR_ASSERT(start);
WINPR_ASSERT(pdate);
*pdate = tm2systemtime(NULL);
*pdate = tm2transitiontime(NULL);
if (start->tm_isdst < 0)
return FALSE;
@ -423,8 +487,10 @@ static BOOL get_transition_date(const struct tm* start, BOOL toDst, SYSTEMTIME*
BOOL val = start->tm_isdst > 0; // the year starts with DST or not
BOOL toggled = FALSE;
struct tm cur = *start;
struct tm last = cur;
for (int day = 1; day <= 365; day++)
{
last = cur;
cur = next_day(&cur);
const BOOL curDst = (cur.tm_isdst > 0);
if ((val != curDst) && !toggled)
@ -434,12 +500,12 @@ static BOOL get_transition_date(const struct tm* start, BOOL toDst, SYSTEMTIME*
{
if (toDst && curDst)
{
*pdate = get_transition_time(&cur, toDst);
*pdate = get_transition_time(&last, toDst);
return TRUE;
}
if (!toDst && !curDst)
{
*pdate = get_transition_time(&cur, toDst);
*pdate = get_transition_time(&last, toDst);
return TRUE;
}
}