Reactos
1/*
2 * PROJECT: ReactOS TimeZone Utilities Library
3 * LICENSE: GPL-2.0 (https://spdx.org/licenses/GPL-2.0)
4 * PURPOSE: Provides time-zone utility wrappers around Win32 functions,
5 * that are used by different ReactOS modules such as
6 * timedate.cpl, syssetup.dll.
7 * COPYRIGHT: Copyright 2004-2005 Eric Kohl
8 * Copyright 2016 Carlo Bramini
9 * Copyright 2020 Hermes Belusca-Maito
10 */
11
12#include <stdlib.h>
13#include <windef.h>
14#include <winbase.h>
15#include <winreg.h>
16
17#include "tzlib.h"
18
19BOOL
20GetTimeZoneListIndex(
21 IN OUT PULONG pIndex)
22{
23 LONG lError;
24 HKEY hKey;
25 DWORD dwType;
26 DWORD dwValueSize;
27 DWORD Length;
28 LPWSTR Buffer;
29 LPWSTR Ptr, End;
30 BOOL bFound = FALSE;
31 unsigned long iLanguageID;
32 WCHAR szLanguageIdString[9];
33
34 if (*pIndex == -1)
35 {
36 *pIndex = 85; /* fallback to GMT time zone */
37
38 lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
39 L"SYSTEM\\CurrentControlSet\\Control\\NLS\\Language",
40 0,
41 KEY_QUERY_VALUE,
42 &hKey);
43 if (lError != ERROR_SUCCESS)
44 {
45 return FALSE;
46 }
47
48 dwValueSize = sizeof(szLanguageIdString);
49 lError = RegQueryValueExW(hKey,
50 L"Default",
51 NULL,
52 NULL,
53 (LPBYTE)szLanguageIdString,
54 &dwValueSize);
55 if (lError != ERROR_SUCCESS)
56 {
57 RegCloseKey(hKey);
58 return FALSE;
59 }
60
61 iLanguageID = wcstoul(szLanguageIdString, NULL, 16);
62 RegCloseKey(hKey);
63 }
64 else
65 {
66 iLanguageID = *pIndex;
67 }
68
69 lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
70 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
71 0,
72 KEY_QUERY_VALUE,
73 &hKey);
74 if (lError != ERROR_SUCCESS)
75 {
76 return FALSE;
77 }
78
79 dwValueSize = 0;
80 lError = RegQueryValueExW(hKey,
81 L"IndexMapping",
82 NULL,
83 &dwType,
84 NULL,
85 &dwValueSize);
86 if ((lError != ERROR_SUCCESS) || (dwType != REG_MULTI_SZ))
87 {
88 RegCloseKey(hKey);
89 return FALSE;
90 }
91
92 Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwValueSize);
93 if (Buffer == NULL)
94 {
95 RegCloseKey(hKey);
96 return FALSE;
97 }
98
99 lError = RegQueryValueExW(hKey,
100 L"IndexMapping",
101 NULL,
102 &dwType,
103 (LPBYTE)Buffer,
104 &dwValueSize);
105
106 RegCloseKey(hKey);
107
108 if ((lError != ERROR_SUCCESS) || (dwType != REG_MULTI_SZ))
109 {
110 HeapFree(GetProcessHeap(), 0, Buffer);
111 return FALSE;
112 }
113
114 Ptr = Buffer;
115 while (*Ptr != 0)
116 {
117 Length = wcslen(Ptr);
118 if (wcstoul(Ptr, NULL, 16) == iLanguageID)
119 bFound = TRUE;
120
121 Ptr = Ptr + Length + 1;
122 if (*Ptr == 0)
123 break;
124
125 if (bFound)
126 {
127 *pIndex = wcstoul(Ptr, &End, 10);
128 HeapFree(GetProcessHeap(), 0, Buffer);
129 return TRUE;
130 }
131
132 Length = wcslen(Ptr);
133 Ptr = Ptr + Length + 1;
134 }
135
136 HeapFree(GetProcessHeap(), 0, Buffer);
137 return FALSE;
138}
139
140LONG
141QueryTimeZoneData(
142 IN HKEY hZoneKey,
143 OUT PULONG Index OPTIONAL,
144 OUT PREG_TZI_FORMAT TimeZoneInfo,
145 OUT PWCHAR Description OPTIONAL,
146 IN OUT PULONG DescriptionSize OPTIONAL,
147 OUT PWCHAR StandardName OPTIONAL,
148 IN OUT PULONG StandardNameSize OPTIONAL,
149 OUT PWCHAR DaylightName OPTIONAL,
150 IN OUT PULONG DaylightNameSize OPTIONAL)
151{
152 LONG lError;
153 DWORD dwValueSize;
154
155 if (Index)
156 {
157 dwValueSize = sizeof(*Index);
158 lError = RegQueryValueExW(hZoneKey,
159 L"Index",
160 NULL,
161 NULL,
162 (LPBYTE)Index,
163 &dwValueSize);
164 if (lError != ERROR_SUCCESS)
165 *Index = 0;
166 }
167
168 /* The time zone information structure is mandatory for a valid time zone */
169 dwValueSize = sizeof(*TimeZoneInfo);
170 lError = RegQueryValueExW(hZoneKey,
171 L"TZI",
172 NULL,
173 NULL,
174 (LPBYTE)TimeZoneInfo,
175 &dwValueSize);
176 if (lError != ERROR_SUCCESS)
177 return lError;
178
179 if (Description && DescriptionSize && *DescriptionSize > 0)
180 {
181 lError = RegQueryValueExW(hZoneKey,
182 L"Display",
183 NULL,
184 NULL,
185 (LPBYTE)Description,
186 DescriptionSize);
187 if (lError != ERROR_SUCCESS)
188 *Description = 0;
189 }
190
191 if (StandardName && StandardNameSize && *StandardNameSize > 0)
192 {
193 lError = RegQueryValueExW(hZoneKey,
194 L"Std",
195 NULL,
196 NULL,
197 (LPBYTE)StandardName,
198 StandardNameSize);
199 if (lError != ERROR_SUCCESS)
200 *StandardName = 0;
201 }
202
203 if (DaylightName && DaylightNameSize && *DaylightNameSize > 0)
204 {
205 lError = RegQueryValueExW(hZoneKey,
206 L"Dlt",
207 NULL,
208 NULL,
209 (LPBYTE)DaylightName,
210 DaylightNameSize);
211 if (lError != ERROR_SUCCESS)
212 *DaylightName = 0;
213 }
214
215 return ERROR_SUCCESS;
216}
217
218//
219// NOTE: Very similar to the EnumDynamicTimeZoneInformation() function
220// introduced in Windows 8.
221//
222VOID
223EnumerateTimeZoneList(
224 IN PENUM_TIMEZONE_CALLBACK Callback,
225 IN PVOID Context OPTIONAL)
226{
227 LONG lError;
228 HKEY hZonesKey;
229 HKEY hZoneKey;
230 DWORD dwIndex;
231 DWORD dwNameSize;
232 WCHAR szKeyName[256];
233
234 /* Open the registry key containing the list of time zones */
235 lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
236 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
237 0,
238 KEY_ENUMERATE_SUB_KEYS,
239 &hZonesKey);
240 if (lError != ERROR_SUCCESS)
241 return;
242
243 /* Enumerate it */
244 for (dwIndex = 0; ; dwIndex++)
245 {
246 dwNameSize = sizeof(szKeyName);
247 lError = RegEnumKeyExW(hZonesKey,
248 dwIndex,
249 szKeyName,
250 &dwNameSize,
251 NULL,
252 NULL,
253 NULL,
254 NULL);
255 // if (lError != ERROR_SUCCESS && lError != ERROR_MORE_DATA)
256 if (lError == ERROR_NO_MORE_ITEMS)
257 break;
258
259 /* Open the time zone sub-key */
260 if (RegOpenKeyExW(hZonesKey,
261 szKeyName,
262 0,
263 KEY_QUERY_VALUE,
264 &hZoneKey))
265 {
266 /* We failed, continue with another sub-key */
267 continue;
268 }
269
270 /* Call the user-provided callback */
271 lError = Callback(hZoneKey, Context);
272 // lError = QueryTimeZoneData(hZoneKey, Context);
273
274 RegCloseKey(hZoneKey);
275 }
276
277 RegCloseKey(hZonesKey);
278}
279
280// Returns TRUE if AutoDaylight is ON.
281// Returns FALSE if AutoDaylight is OFF.
282BOOL
283GetAutoDaylight(VOID)
284{
285 LONG lError;
286 HKEY hKey;
287 DWORD dwType;
288 DWORD dwDisabled;
289 DWORD dwValueSize;
290
291 lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
292 L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
293 0,
294 KEY_QUERY_VALUE,
295 &hKey);
296 if (lError != ERROR_SUCCESS)
297 return FALSE;
298
299 // NOTE: On Vista+: REG_DWORD "DynamicDaylightTimeDisabled"
300 dwValueSize = sizeof(dwDisabled);
301 lError = RegQueryValueExW(hKey,
302 L"DisableAutoDaylightTimeSet",
303 NULL,
304 &dwType,
305 (LPBYTE)&dwDisabled,
306 &dwValueSize);
307
308 RegCloseKey(hKey);
309
310 if ((lError != ERROR_SUCCESS) || (dwType != REG_DWORD) || (dwValueSize != sizeof(dwDisabled)))
311 {
312 /*
313 * The call failed (non zero) because the registry value isn't available,
314 * which means auto-daylight shouldn't be disabled.
315 */
316 dwDisabled = FALSE;
317 }
318
319 return !dwDisabled;
320}
321
322VOID
323SetAutoDaylight(
324 IN BOOL EnableAutoDaylightTime)
325{
326 LONG lError;
327 HKEY hKey;
328 DWORD dwDisabled = TRUE;
329
330 lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
331 L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
332 0,
333 KEY_SET_VALUE,
334 &hKey);
335 if (lError != ERROR_SUCCESS)
336 return;
337
338 if (!EnableAutoDaylightTime)
339 {
340 /* Auto-Daylight disabled: set the value to TRUE */
341 // NOTE: On Vista+: REG_DWORD "DynamicDaylightTimeDisabled"
342 RegSetValueExW(hKey,
343 L"DisableAutoDaylightTimeSet",
344 0,
345 REG_DWORD,
346 (LPBYTE)&dwDisabled,
347 sizeof(dwDisabled));
348 }
349 else
350 {
351 /* Auto-Daylight enabled: just delete the value */
352 RegDeleteValueW(hKey, L"DisableAutoDaylightTimeSet");
353 }
354
355 RegCloseKey(hKey);
356}