/src/FreeRDP/winpr/libwinpr/timezone/timezone.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * WinPR: Windows Portable Runtime |
3 | | * Time Zone |
4 | | * |
5 | | * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> |
6 | | * Copyright 2024 Armin Novak <anovak@thincast.com> |
7 | | * Copyright 2024 Thincast Technologies GmbH |
8 | | * |
9 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
10 | | * you may not use this file except in compliance with the License. |
11 | | * You may obtain a copy of the License at |
12 | | * |
13 | | * http://www.apache.org/licenses/LICENSE-2.0 |
14 | | * |
15 | | * Unless required by applicable law or agreed to in writing, software |
16 | | * distributed under the License is distributed on an "AS IS" BASIS, |
17 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
18 | | * See the License for the specific language governing permissions and |
19 | | * limitations under the License. |
20 | | */ |
21 | | |
22 | | #include <winpr/config.h> |
23 | | |
24 | | #include <winpr/environment.h> |
25 | | #include <winpr/wtypes.h> |
26 | | #include <winpr/timezone.h> |
27 | | #include <winpr/crt.h> |
28 | | #include <winpr/assert.h> |
29 | | #include <winpr/file.h> |
30 | | #include "../log.h" |
31 | | |
32 | | #define TAG WINPR_TAG("timezone") |
33 | | |
34 | | #ifndef MIN |
35 | 134k | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) |
36 | | #endif |
37 | | |
38 | | #include "TimeZoneNameMap.h" |
39 | | #include "TimeZoneIanaAbbrevMap.h" |
40 | | |
41 | | #ifndef _WIN32 |
42 | | |
43 | | #include <time.h> |
44 | | #include <unistd.h> |
45 | | |
46 | | #endif |
47 | | |
48 | | #if !defined(_WIN32) |
49 | | static char* winpr_read_unix_timezone_identifier_from_file(FILE* fp) |
50 | 89.8k | { |
51 | 89.8k | const INT CHUNK_SIZE = 32; |
52 | 89.8k | INT64 rc = 0; |
53 | 89.8k | INT64 read = 0; |
54 | 89.8k | INT64 length = CHUNK_SIZE; |
55 | 89.8k | char* tzid = NULL; |
56 | | |
57 | 89.8k | tzid = (char*)malloc((size_t)length); |
58 | 89.8k | if (!tzid) |
59 | 0 | return NULL; |
60 | | |
61 | 89.8k | do |
62 | 89.8k | { |
63 | 89.8k | rc = fread(tzid + read, 1, length - read - 1, fp); |
64 | 89.8k | if (rc > 0) |
65 | 89.8k | read += rc; |
66 | | |
67 | 89.8k | if (read < (length - 1)) |
68 | 89.8k | break; |
69 | | |
70 | 0 | length += CHUNK_SIZE; |
71 | 0 | char* tmp = (char*)realloc(tzid, length); |
72 | 0 | if (!tmp) |
73 | 0 | { |
74 | 0 | free(tzid); |
75 | 0 | return NULL; |
76 | 0 | } |
77 | | |
78 | 0 | tzid = tmp; |
79 | 0 | } while (rc > 0); |
80 | | |
81 | 89.8k | if (ferror(fp)) |
82 | 0 | { |
83 | 0 | free(tzid); |
84 | 0 | return NULL; |
85 | 0 | } |
86 | | |
87 | 89.8k | tzid[read] = '\0'; |
88 | 89.8k | if (read > 0) |
89 | 89.8k | { |
90 | 89.8k | if (tzid[read - 1] == '\n') |
91 | 89.8k | tzid[read - 1] = '\0'; |
92 | 89.8k | } |
93 | | |
94 | 89.8k | return tzid; |
95 | 89.8k | } |
96 | | |
97 | | static char* winpr_get_timezone_from_link(const char* links[], size_t count) |
98 | 89.8k | { |
99 | 89.8k | const char* _links[] = { "/etc/localtime", "/etc/TZ" }; |
100 | | |
101 | 89.8k | if (links == NULL) |
102 | 0 | { |
103 | 0 | links = _links; |
104 | 0 | count = ARRAYSIZE(_links); |
105 | 0 | } |
106 | | |
107 | | /* |
108 | | * On linux distros such as Redhat or Archlinux, a symlink at /etc/localtime |
109 | | * will point to /usr/share/zoneinfo/region/place where region/place could be |
110 | | * America/Montreal for example. |
111 | | * Some distributions do have to symlink at /etc/TZ. |
112 | | */ |
113 | | |
114 | 89.8k | for (size_t x = 0; x < count; x++) |
115 | 89.8k | { |
116 | 89.8k | char* tzid = NULL; |
117 | 89.8k | const char* link = links[x]; |
118 | 89.8k | char* buf = realpath(link, NULL); |
119 | | |
120 | 89.8k | if (buf) |
121 | 89.8k | { |
122 | 89.8k | size_t sep = 0; |
123 | 89.8k | size_t alloc = 0; |
124 | 89.8k | size_t pos = 0; |
125 | 89.8k | size_t len = pos = strlen(buf); |
126 | | |
127 | | /* find the position of the 2nd to last "/" */ |
128 | 718k | for (size_t i = 1; i <= len; i++) |
129 | 718k | { |
130 | 718k | const size_t curpos = len - i; |
131 | 718k | const char cur = buf[curpos]; |
132 | | |
133 | 718k | if (cur == '/') |
134 | 179k | sep++; |
135 | 718k | if (sep >= 2) |
136 | 89.8k | { |
137 | 89.8k | alloc = i; |
138 | 89.8k | pos = len - i + 1; |
139 | 89.8k | break; |
140 | 89.8k | } |
141 | 718k | } |
142 | | |
143 | 89.8k | if ((len == 0) || (sep != 2)) |
144 | 0 | goto end; |
145 | | |
146 | 89.8k | tzid = (char*)calloc(alloc + 1, sizeof(char)); |
147 | | |
148 | 89.8k | if (!tzid) |
149 | 0 | goto end; |
150 | | |
151 | 89.8k | strncpy(tzid, &buf[pos], alloc); |
152 | 89.8k | WLog_DBG(TAG, "tzid: %s", tzid); |
153 | 89.8k | goto end; |
154 | 89.8k | } |
155 | | |
156 | 89.8k | end: |
157 | 89.8k | free(buf); |
158 | 89.8k | if (tzid) |
159 | 89.8k | return tzid; |
160 | 89.8k | } |
161 | | |
162 | 0 | return NULL; |
163 | 89.8k | } |
164 | | |
165 | | #if defined(ANDROID) |
166 | | #include "../utils/android.h" |
167 | | |
168 | | static char* winpr_get_android_timezone_identifier(void) |
169 | | { |
170 | | char* tzid = NULL; |
171 | | JNIEnv* jniEnv; |
172 | | |
173 | | /* Preferred: Try to get identifier from java TimeZone class */ |
174 | | if (jniVm && ((*jniVm)->GetEnv(jniVm, (void**)&jniEnv, JNI_VERSION_1_6) == JNI_OK)) |
175 | | { |
176 | | const char* raw; |
177 | | jclass jObjClass; |
178 | | jobject jObj; |
179 | | jmethodID jDefaultTimezone; |
180 | | jmethodID jTimezoneIdentifier; |
181 | | jstring tzJId; |
182 | | jboolean attached = (*jniVm)->AttachCurrentThread(jniVm, &jniEnv, NULL); |
183 | | jObjClass = (*jniEnv)->FindClass(jniEnv, "java/util/TimeZone"); |
184 | | |
185 | | if (!jObjClass) |
186 | | goto fail; |
187 | | |
188 | | jDefaultTimezone = |
189 | | (*jniEnv)->GetStaticMethodID(jniEnv, jObjClass, "getDefault", "()Ljava/util/TimeZone;"); |
190 | | |
191 | | if (!jDefaultTimezone) |
192 | | goto fail; |
193 | | |
194 | | jObj = (*jniEnv)->CallStaticObjectMethod(jniEnv, jObjClass, jDefaultTimezone); |
195 | | |
196 | | if (!jObj) |
197 | | goto fail; |
198 | | |
199 | | jTimezoneIdentifier = |
200 | | (*jniEnv)->GetMethodID(jniEnv, jObjClass, "getID", "()Ljava/lang/String;"); |
201 | | |
202 | | if (!jTimezoneIdentifier) |
203 | | goto fail; |
204 | | |
205 | | tzJId = (*jniEnv)->CallObjectMethod(jniEnv, jObj, jTimezoneIdentifier); |
206 | | |
207 | | if (!tzJId) |
208 | | goto fail; |
209 | | |
210 | | raw = (*jniEnv)->GetStringUTFChars(jniEnv, tzJId, 0); |
211 | | |
212 | | if (raw) |
213 | | tzid = _strdup(raw); |
214 | | |
215 | | (*jniEnv)->ReleaseStringUTFChars(jniEnv, tzJId, raw); |
216 | | fail: |
217 | | |
218 | | if (attached) |
219 | | (*jniVm)->DetachCurrentThread(jniVm); |
220 | | } |
221 | | |
222 | | /* Fall back to property, might not be available. */ |
223 | | if (!tzid) |
224 | | { |
225 | | FILE* fp = popen("getprop persist.sys.timezone", "r"); |
226 | | |
227 | | if (fp) |
228 | | { |
229 | | tzid = winpr_read_unix_timezone_identifier_from_file(fp); |
230 | | pclose(fp); |
231 | | } |
232 | | } |
233 | | |
234 | | return tzid; |
235 | | } |
236 | | #endif |
237 | | |
238 | | static char* winpr_get_unix_timezone_identifier_from_file(void) |
239 | 89.8k | { |
240 | | #if defined(ANDROID) |
241 | | return winpr_get_android_timezone_identifier(); |
242 | | #else |
243 | 89.8k | FILE* fp = NULL; |
244 | 89.8k | char* tzid = NULL; |
245 | | #if !defined(WINPR_TIMEZONE_FILE) |
246 | | #error \ |
247 | | "Please define WINPR_TIMEZONE_FILE with the path to your timezone file (e.g. /etc/timezone or similar)" |
248 | | #else |
249 | 89.8k | fp = winpr_fopen(WINPR_TIMEZONE_FILE, "r"); |
250 | 89.8k | #endif |
251 | | |
252 | 89.8k | if (NULL == fp) |
253 | 0 | return NULL; |
254 | | |
255 | 89.8k | tzid = winpr_read_unix_timezone_identifier_from_file(fp); |
256 | 89.8k | fclose(fp); |
257 | 89.8k | if (tzid != NULL) |
258 | 89.8k | WLog_DBG(TAG, "tzid: %s", tzid); |
259 | 89.8k | return tzid; |
260 | 89.8k | #endif |
261 | 89.8k | } |
262 | | |
263 | | static char* winpr_time_zone_from_env(void) |
264 | 89.8k | { |
265 | 89.8k | LPCSTR tz = "TZ"; |
266 | 89.8k | char* tzid = NULL; |
267 | | |
268 | 89.8k | DWORD nSize = GetEnvironmentVariableA(tz, NULL, 0); |
269 | 89.8k | if (nSize > 0) |
270 | 0 | { |
271 | 0 | tzid = (char*)calloc(nSize, sizeof(char)); |
272 | 0 | if (!tzid) |
273 | 0 | goto fail; |
274 | 0 | if (!GetEnvironmentVariableA(tz, tzid, nSize)) |
275 | 0 | goto fail; |
276 | 0 | else if (tzid[0] == ':') |
277 | 0 | { |
278 | | /* Remove leading colon, see tzset(3) */ |
279 | 0 | memmove(tzid, tzid + 1, nSize - sizeof(char)); |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | 89.8k | return tzid; |
284 | | |
285 | 0 | fail: |
286 | 0 | free(tzid); |
287 | 0 | return NULL; |
288 | 89.8k | } |
289 | | |
290 | | static char* winpr_translate_time_zone(const char* tzid) |
291 | 89.8k | { |
292 | 89.8k | const char* zipath = "/usr/share/zoneinfo/"; |
293 | 89.8k | char* buf = NULL; |
294 | 89.8k | const char* links[] = { buf }; |
295 | | |
296 | 89.8k | if (!tzid) |
297 | 0 | return NULL; |
298 | | |
299 | 89.8k | if (tzid[0] == '/') |
300 | 0 | { |
301 | | /* Full path given in TZ */ |
302 | 0 | links[0] = tzid; |
303 | 0 | } |
304 | 89.8k | else |
305 | 89.8k | { |
306 | 89.8k | size_t bsize = 0; |
307 | 89.8k | winpr_asprintf(&buf, &bsize, "%s%s", zipath, tzid); |
308 | 89.8k | links[0] = buf; |
309 | 89.8k | } |
310 | | |
311 | 89.8k | char* ntzid = winpr_get_timezone_from_link(links, 1); |
312 | 89.8k | free(buf); |
313 | 89.8k | return ntzid; |
314 | 89.8k | } |
315 | | |
316 | | static char* winpr_guess_time_zone(void) |
317 | 89.8k | { |
318 | 89.8k | char* tzid = winpr_time_zone_from_env(); |
319 | 89.8k | if (tzid) |
320 | 0 | goto end; |
321 | 89.8k | tzid = winpr_get_unix_timezone_identifier_from_file(); |
322 | 89.8k | if (tzid) |
323 | 89.8k | goto end; |
324 | 0 | tzid = winpr_get_timezone_from_link(NULL, 0); |
325 | 0 | if (tzid) |
326 | 0 | goto end; |
327 | | |
328 | 89.8k | end: |
329 | 89.8k | { |
330 | 89.8k | char* ntzid = winpr_translate_time_zone(tzid); |
331 | 89.8k | if (ntzid) |
332 | 89.8k | { |
333 | 89.8k | free(tzid); |
334 | 89.8k | return ntzid; |
335 | 89.8k | } |
336 | 0 | return tzid; |
337 | 89.8k | } |
338 | 89.8k | } |
339 | | |
340 | | static SYSTEMTIME tm2systemtime(const struct tm* t) |
341 | 179k | { |
342 | 179k | SYSTEMTIME st = { 0 }; |
343 | | |
344 | 179k | if (t) |
345 | 0 | { |
346 | 0 | st.wYear = (WORD)1900 + t->tm_year; |
347 | 0 | st.wMonth = (WORD)t->tm_mon; |
348 | 0 | st.wDay = (WORD)t->tm_mday; |
349 | 0 | st.wDayOfWeek = (WORD)t->tm_wday; |
350 | 0 | st.wHour = (WORD)t->tm_hour; |
351 | 0 | st.wMinute = (WORD)t->tm_min; |
352 | 0 | st.wSecond = (WORD)t->tm_sec; |
353 | 0 | st.wMilliseconds = 0; |
354 | 0 | } |
355 | 179k | return st; |
356 | 179k | } |
357 | | |
358 | | static LONG get_gmtoff_min(const struct tm* t) |
359 | 89.8k | { |
360 | 89.8k | WINPR_ASSERT(t); |
361 | 89.8k | return -(LONG)(t->tm_gmtoff / 60l); |
362 | 89.8k | } |
363 | | |
364 | | static struct tm next_day(const struct tm* start) |
365 | 98.3M | { |
366 | 98.3M | struct tm cur = *start; |
367 | 98.3M | cur.tm_hour = 0; |
368 | 98.3M | cur.tm_min = 0; |
369 | 98.3M | cur.tm_sec = 0; |
370 | 98.3M | cur.tm_isdst = -1; |
371 | 98.3M | cur.tm_mday++; |
372 | 98.3M | const time_t t = mktime(&cur); |
373 | 98.3M | localtime_r(&t, &cur); |
374 | 98.3M | return cur; |
375 | 98.3M | } |
376 | | |
377 | | static struct tm adjust_time(const struct tm* start, int hour, int minute) |
378 | 0 | { |
379 | 0 | struct tm cur = *start; |
380 | 0 | cur.tm_hour = hour; |
381 | 0 | cur.tm_min = minute; |
382 | 0 | cur.tm_sec = 0; |
383 | 0 | cur.tm_isdst = -1; |
384 | 0 | const time_t t = mktime(&cur); |
385 | 0 | localtime_r(&t, &cur); |
386 | 0 | return cur; |
387 | 0 | } |
388 | | |
389 | | static SYSTEMTIME get_transition_time(const struct tm* start, BOOL toDst) |
390 | 0 | { |
391 | 0 | BOOL toggled = FALSE; |
392 | 0 | struct tm first = adjust_time(start, 0, 0); |
393 | 0 | for (int hour = 0; hour < 24; hour++) |
394 | 0 | { |
395 | 0 | for (int minute = 0; minute < 60; minute++) |
396 | 0 | { |
397 | 0 | struct tm cur = adjust_time(start, hour, minute); |
398 | 0 | if (cur.tm_isdst != first.tm_isdst) |
399 | 0 | toggled = TRUE; |
400 | |
|
401 | 0 | if (toggled) |
402 | 0 | { |
403 | 0 | if (toDst && (cur.tm_isdst > 0)) |
404 | 0 | return tm2systemtime(&cur); |
405 | 0 | else if (!toDst && (cur.tm_isdst == 0)) |
406 | 0 | return tm2systemtime(&cur); |
407 | 0 | } |
408 | 0 | } |
409 | 0 | } |
410 | 0 | return tm2systemtime(start); |
411 | 0 | } |
412 | | |
413 | | static BOOL get_transition_date(const struct tm* start, BOOL toDst, SYSTEMTIME* pdate) |
414 | 179k | { |
415 | 179k | WINPR_ASSERT(start); |
416 | 179k | WINPR_ASSERT(pdate); |
417 | | |
418 | 179k | *pdate = tm2systemtime(NULL); |
419 | | |
420 | 179k | if (start->tm_isdst < 0) |
421 | 0 | return FALSE; |
422 | | |
423 | 179k | BOOL val = start->tm_isdst > 0; // the year starts with DST or not |
424 | 179k | BOOL toggled = FALSE; |
425 | 179k | struct tm cur = *start; |
426 | 65.7M | for (int day = 1; day <= 365; day++) |
427 | 65.5M | { |
428 | 65.5M | cur = next_day(&cur); |
429 | 65.5M | const BOOL curDst = (cur.tm_isdst > 0); |
430 | 65.5M | if ((val != curDst) && !toggled) |
431 | 0 | toggled = TRUE; |
432 | | |
433 | 65.5M | if (toggled) |
434 | 0 | { |
435 | 0 | if (toDst && curDst) |
436 | 0 | { |
437 | 0 | *pdate = get_transition_time(&cur, toDst); |
438 | 0 | return TRUE; |
439 | 0 | } |
440 | 0 | if (!toDst && !curDst) |
441 | 0 | { |
442 | 0 | *pdate = get_transition_time(&cur, toDst); |
443 | 0 | return TRUE; |
444 | 0 | } |
445 | 0 | } |
446 | 65.5M | } |
447 | 179k | return FALSE; |
448 | 179k | } |
449 | | |
450 | | static LONG get_bias(const struct tm* start, BOOL dstBias) |
451 | 179k | { |
452 | 179k | if ((start->tm_isdst > 0) && dstBias) |
453 | 0 | return get_gmtoff_min(start); |
454 | 179k | if ((start->tm_isdst == 0) && !dstBias) |
455 | 89.8k | return get_gmtoff_min(start); |
456 | 89.8k | if (start->tm_isdst < 0) |
457 | 0 | return get_gmtoff_min(start); |
458 | | |
459 | 89.8k | struct tm cur = *start; |
460 | 32.8M | for (int day = 1; day <= 365; day++) |
461 | 32.7M | { |
462 | 32.7M | cur = next_day(&cur); |
463 | 32.7M | if ((cur.tm_isdst > 0) && dstBias) |
464 | 0 | return get_gmtoff_min(&cur); |
465 | 32.7M | else if ((cur.tm_isdst == 0) && !dstBias) |
466 | 0 | return get_gmtoff_min(&cur); |
467 | 32.7M | } |
468 | 89.8k | return 0; |
469 | 89.8k | } |
470 | | |
471 | | static BOOL map_iana_id(const char* iana, LPTIME_ZONE_INFORMATION tz) |
472 | 89.8k | { |
473 | 89.8k | const char* winId = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_ID); |
474 | 89.8k | if (!winId) |
475 | 0 | return FALSE; |
476 | | |
477 | 89.8k | const char* winStd = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_STANDARD); |
478 | 89.8k | const char* winDst = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_DAYLIGHT); |
479 | | |
480 | 89.8k | ConvertUtf8ToWChar(winStd, tz->StandardName, ARRAYSIZE(tz->StandardName)); |
481 | 89.8k | ConvertUtf8ToWChar(winDst, tz->DaylightName, ARRAYSIZE(tz->DaylightName)); |
482 | | |
483 | 89.8k | return TRUE; |
484 | 89.8k | } |
485 | | |
486 | | DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation) |
487 | 89.8k | { |
488 | 89.8k | const char** list = NULL; |
489 | 89.8k | char* tzid = NULL; |
490 | 89.8k | const char* defaultName = "Client Local Time"; |
491 | 89.8k | DWORD res = TIME_ZONE_ID_UNKNOWN; |
492 | 89.8k | const TIME_ZONE_INFORMATION empty = { 0 }; |
493 | 89.8k | LPTIME_ZONE_INFORMATION tz = lpTimeZoneInformation; |
494 | | |
495 | 89.8k | WINPR_ASSERT(tz); |
496 | | |
497 | 89.8k | *tz = empty; |
498 | 89.8k | ConvertUtf8ToWChar(defaultName, tz->StandardName, ARRAYSIZE(tz->StandardName)); |
499 | | |
500 | 89.8k | const time_t t = time(NULL); |
501 | 89.8k | struct tm tres = { 0 }; |
502 | 89.8k | struct tm* local_time = localtime_r(&t, &tres); |
503 | 89.8k | if (!local_time) |
504 | 0 | goto out_error; |
505 | | |
506 | 89.8k | tz->Bias = get_bias(local_time, FALSE); |
507 | 89.8k | if (local_time->tm_isdst >= 0) |
508 | 89.8k | { |
509 | | /* DST bias is the difference between standard time and DST in minutes */ |
510 | 89.8k | const LONG d = get_bias(local_time, TRUE); |
511 | 89.8k | tz->DaylightBias = -1 * labs(tz->Bias - d); |
512 | 89.8k | get_transition_date(local_time, FALSE, &tz->StandardDate); |
513 | 89.8k | get_transition_date(local_time, TRUE, &tz->DaylightDate); |
514 | 89.8k | } |
515 | | |
516 | 89.8k | tzid = winpr_guess_time_zone(); |
517 | 89.8k | if (!map_iana_id(tzid, tz)) |
518 | 0 | { |
519 | 0 | const size_t len = TimeZoneIanaAbbrevGet(local_time->tm_zone, NULL, 0); |
520 | 0 | list = calloc(len, sizeof(char*)); |
521 | 0 | if (!list) |
522 | 0 | goto out_error; |
523 | 0 | const size_t size = TimeZoneIanaAbbrevGet(local_time->tm_zone, list, len); |
524 | 0 | for (size_t x = 0; x < size; x++) |
525 | 0 | { |
526 | 0 | const char* id = list[x]; |
527 | 0 | if (map_iana_id(id, tz)) |
528 | 0 | { |
529 | 0 | res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD; |
530 | 0 | break; |
531 | 0 | } |
532 | 0 | } |
533 | 0 | } |
534 | 89.8k | else |
535 | 89.8k | res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD; |
536 | | |
537 | 89.8k | out_error: |
538 | 89.8k | free(tzid); |
539 | 89.8k | free(list); |
540 | 89.8k | switch (res) |
541 | 89.8k | { |
542 | 0 | case TIME_ZONE_ID_DAYLIGHT: |
543 | 89.8k | case TIME_ZONE_ID_STANDARD: |
544 | 89.8k | WLog_DBG(TAG, "tz: Bias=%" PRId32 " sn='%s' dln='%s'", tz->Bias, tz->StandardName, |
545 | 89.8k | tz->DaylightName); |
546 | 89.8k | break; |
547 | 0 | default: |
548 | 0 | WLog_DBG(TAG, "tz not found, using computed bias %" PRId32 ".", tz->Bias); |
549 | 0 | break; |
550 | 89.8k | } |
551 | | |
552 | 89.8k | return res; |
553 | 89.8k | } |
554 | | |
555 | | BOOL SetTimeZoneInformation(const TIME_ZONE_INFORMATION* lpTimeZoneInformation) |
556 | 0 | { |
557 | 0 | WINPR_UNUSED(lpTimeZoneInformation); |
558 | 0 | return FALSE; |
559 | 0 | } |
560 | | |
561 | | BOOL SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime, LPFILETIME lpFileTime) |
562 | 0 | { |
563 | 0 | WINPR_UNUSED(lpSystemTime); |
564 | 0 | WINPR_UNUSED(lpFileTime); |
565 | 0 | return FALSE; |
566 | 0 | } |
567 | | |
568 | | BOOL FileTimeToSystemTime(const FILETIME* lpFileTime, LPSYSTEMTIME lpSystemTime) |
569 | 0 | { |
570 | 0 | WINPR_UNUSED(lpFileTime); |
571 | 0 | WINPR_UNUSED(lpSystemTime); |
572 | 0 | return FALSE; |
573 | 0 | } |
574 | | |
575 | | BOOL SystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION lpTimeZone, |
576 | | LPSYSTEMTIME lpUniversalTime, LPSYSTEMTIME lpLocalTime) |
577 | 0 | { |
578 | 0 | WINPR_UNUSED(lpTimeZone); |
579 | 0 | WINPR_UNUSED(lpUniversalTime); |
580 | 0 | WINPR_UNUSED(lpLocalTime); |
581 | 0 | return FALSE; |
582 | 0 | } |
583 | | |
584 | | BOOL TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation, |
585 | | LPSYSTEMTIME lpLocalTime, LPSYSTEMTIME lpUniversalTime) |
586 | 0 | { |
587 | 0 | WINPR_UNUSED(lpTimeZoneInformation); |
588 | 0 | WINPR_UNUSED(lpLocalTime); |
589 | 0 | WINPR_UNUSED(lpUniversalTime); |
590 | 0 | return FALSE; |
591 | 0 | } |
592 | | |
593 | | #endif |
594 | | |
595 | | /* |
596 | | * GetDynamicTimeZoneInformation is provided by the SDK if _WIN32_WINNT >= 0x0600 in SDKs above 7.1A |
597 | | * and incorrectly if _WIN32_WINNT >= 0x0501 in older SDKs |
598 | | */ |
599 | | #if !defined(_WIN32) || \ |
600 | | (defined(_WIN32) && (defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0600 || \ |
601 | | !defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0501)) /* Windows Vista */ |
602 | | |
603 | | DWORD GetDynamicTimeZoneInformation(PDYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation) |
604 | 44.9k | { |
605 | 44.9k | TIME_ZONE_INFORMATION tz = { 0 }; |
606 | 44.9k | const DWORD rc = GetTimeZoneInformation(&tz); |
607 | | |
608 | 44.9k | WINPR_ASSERT(pTimeZoneInformation); |
609 | 44.9k | pTimeZoneInformation->Bias = tz.Bias; |
610 | 44.9k | memcpy(pTimeZoneInformation->StandardName, tz.StandardName, |
611 | 44.9k | MIN(sizeof(tz.StandardName), sizeof(pTimeZoneInformation->StandardName))); |
612 | 44.9k | pTimeZoneInformation->StandardDate = tz.StandardDate; |
613 | 44.9k | pTimeZoneInformation->StandardBias = tz.StandardBias; |
614 | | |
615 | 44.9k | memcpy(pTimeZoneInformation->DaylightName, tz.DaylightName, |
616 | 44.9k | MIN(sizeof(tz.DaylightName), sizeof(pTimeZoneInformation->DaylightName))); |
617 | 44.9k | pTimeZoneInformation->DaylightDate = tz.DaylightDate; |
618 | 44.9k | pTimeZoneInformation->DaylightBias = tz.DaylightBias; |
619 | | |
620 | 44.9k | memcpy(pTimeZoneInformation->TimeZoneKeyName, tz.StandardName, |
621 | 44.9k | MIN(sizeof(tz.StandardName), sizeof(pTimeZoneInformation->TimeZoneKeyName))); |
622 | | |
623 | | /* https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-dynamic_time_zone_information |
624 | | */ |
625 | 44.9k | pTimeZoneInformation->DynamicDaylightTimeDisabled = FALSE; |
626 | 44.9k | return rc; |
627 | 44.9k | } |
628 | | |
629 | | BOOL SetDynamicTimeZoneInformation(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation) |
630 | 0 | { |
631 | 0 | WINPR_UNUSED(lpTimeZoneInformation); |
632 | 0 | return FALSE; |
633 | 0 | } |
634 | | |
635 | | BOOL GetTimeZoneInformationForYear(USHORT wYear, PDYNAMIC_TIME_ZONE_INFORMATION pdtzi, |
636 | | LPTIME_ZONE_INFORMATION ptzi) |
637 | 0 | { |
638 | 0 | WINPR_UNUSED(wYear); |
639 | 0 | WINPR_UNUSED(pdtzi); |
640 | 0 | WINPR_UNUSED(ptzi); |
641 | 0 | return FALSE; |
642 | 0 | } |
643 | | |
644 | | #endif |
645 | | |
646 | | #if !defined(_WIN32) || (defined(_WIN32) && (_WIN32_WINNT < 0x0601)) /* Windows 7 */ |
647 | | |
648 | | BOOL SystemTimeToTzSpecificLocalTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, |
649 | | const SYSTEMTIME* lpUniversalTime, LPSYSTEMTIME lpLocalTime) |
650 | 0 | { |
651 | 0 | WINPR_UNUSED(lpTimeZoneInformation); |
652 | 0 | WINPR_UNUSED(lpUniversalTime); |
653 | 0 | WINPR_UNUSED(lpLocalTime); |
654 | 0 | return FALSE; |
655 | 0 | } |
656 | | |
657 | | BOOL TzSpecificLocalTimeToSystemTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, |
658 | | const SYSTEMTIME* lpLocalTime, LPSYSTEMTIME lpUniversalTime) |
659 | 0 | { |
660 | 0 | WINPR_UNUSED(lpTimeZoneInformation); |
661 | 0 | WINPR_UNUSED(lpLocalTime); |
662 | 0 | WINPR_UNUSED(lpUniversalTime); |
663 | 0 | return FALSE; |
664 | 0 | } |
665 | | |
666 | | #endif |
667 | | |
668 | | #if !defined(_WIN32) || (defined(_WIN32) && (_WIN32_WINNT < 0x0602)) /* Windows 8 */ |
669 | | |
670 | | DWORD EnumDynamicTimeZoneInformation(const DWORD dwIndex, |
671 | | PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation) |
672 | 0 | { |
673 | 0 | WINPR_UNUSED(dwIndex); |
674 | 0 | WINPR_UNUSED(lpTimeZoneInformation); |
675 | 0 | return 0; |
676 | 0 | } |
677 | | |
678 | | DWORD GetDynamicTimeZoneInformationEffectiveYears( |
679 | | const PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation, LPDWORD FirstYear, LPDWORD LastYear) |
680 | 0 | { |
681 | 0 | WINPR_UNUSED(lpTimeZoneInformation); |
682 | 0 | WINPR_UNUSED(FirstYear); |
683 | 0 | WINPR_UNUSED(LastYear); |
684 | 0 | return 0; |
685 | 0 | } |
686 | | |
687 | | #endif |