FreeRDP
Loading...
Searching...
No Matches
winpr/libwinpr/timezone/timezone.c
1
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#include "timezone.h"
32
33#define TAG WINPR_TAG("timezone")
34
35#include "TimeZoneNameMap.h"
36#include "TimeZoneIanaAbbrevMap.h"
37
38#ifndef _WIN32
39
40#include <time.h>
41#include <unistd.h>
42
43#endif
44
45#if !defined(_WIN32)
46static char* winpr_read_unix_timezone_identifier_from_file(FILE* fp)
47{
48 const INT CHUNK_SIZE = 32;
49 size_t rc = 0;
50 size_t read = 0;
51 size_t length = CHUNK_SIZE;
52
53 char* tzid = malloc(length);
54 if (!tzid)
55 return nullptr;
56
57 do
58 {
59 rc = fread(tzid + read, 1, length - read - 1UL, fp);
60 if (rc > 0)
61 read += rc;
62
63 if (read < (length - 1UL))
64 break;
65
66 if (read > length - 1UL)
67 {
68 free(tzid);
69 return nullptr;
70 }
71
72 length += CHUNK_SIZE;
73 char* tmp = (char*)realloc(tzid, length);
74 if (!tmp)
75 {
76 free(tzid);
77 return nullptr;
78 }
79
80 tzid = tmp;
81 } while (rc > 0);
82
83 if (ferror(fp))
84 {
85 free(tzid);
86 return nullptr;
87 }
88
89 tzid[read] = '\0';
90 if (read > 0)
91 {
92 if (tzid[read - 1] == '\n')
93 tzid[read - 1] = '\0';
94 }
95
96 return tzid;
97}
98
99static char* winpr_get_timezone_from_link(const char* links[], size_t count)
100{
101 const char* _links[] = { "/etc/localtime", "/etc/TZ" };
102
103 if (links == nullptr)
104 {
105 links = _links;
106 count = ARRAYSIZE(_links);
107 }
108
109 /*
110 * On linux distros such as Redhat or Archlinux, a symlink at /etc/localtime
111 * will point to /usr/share/zoneinfo/region/place where region/place could be
112 * America/Montreal for example.
113 * Some distributions do have to symlink at /etc/TZ.
114 */
115
116 for (size_t x = 0; x < count; x++)
117 {
118 char* tzid = nullptr;
119 const char* link = links[x];
120 char* buf = realpath(link, nullptr);
121
122 if (buf)
123 {
124 size_t sep = 0;
125 size_t alloc = 0;
126 size_t pos = 0;
127 size_t len = pos = strlen(buf);
128
129 /* find the position of the 2nd to last "/" */
130 for (size_t i = 1; i <= len; i++)
131 {
132 const size_t curpos = len - i;
133 const char cur = buf[curpos];
134
135 if (cur == '/')
136 sep++;
137 if (sep >= 2)
138 {
139 alloc = i;
140 pos = len - i + 1;
141 break;
142 }
143 }
144
145 if ((len == 0) || (sep != 2))
146 goto end;
147
148 tzid = (char*)calloc(alloc + 1, sizeof(char));
149
150 if (!tzid)
151 goto end;
152
153 strncpy(tzid, &buf[pos], alloc);
154 WLog_DBG(TAG, "tzid: %s", tzid);
155 goto end;
156 }
157
158 end:
159 free(buf);
160 if (tzid)
161 return tzid;
162 }
163
164 return nullptr;
165}
166
167#if defined(ANDROID)
168#include "../utils/android.h"
169
170static char* winpr_get_android_timezone_identifier(void)
171{
172 char* tzid = nullptr;
173 JNIEnv* jniEnv;
174
175 /* Preferred: Try to get identifier from java TimeZone class */
176 if (jniVm && ((*jniVm)->GetEnv(jniVm, (void**)&jniEnv, JNI_VERSION_1_6) == JNI_OK))
177 {
178 const char* raw;
179 jclass jObjClass;
180 jobject jObj;
181 jmethodID jDefaultTimezone;
182 jmethodID jTimezoneIdentifier;
183 jstring tzJId;
184 jboolean attached = (*jniVm)->AttachCurrentThread(jniVm, &jniEnv, nullptr);
185 jObjClass = (*jniEnv)->FindClass(jniEnv, "java/util/TimeZone");
186
187 if (!jObjClass)
188 goto fail;
189
190 jDefaultTimezone =
191 (*jniEnv)->GetStaticMethodID(jniEnv, jObjClass, "getDefault", "()Ljava/util/TimeZone;");
192
193 if (!jDefaultTimezone)
194 goto fail;
195
196 jObj = (*jniEnv)->CallStaticObjectMethod(jniEnv, jObjClass, jDefaultTimezone);
197
198 if (!jObj)
199 goto fail;
200
201 jTimezoneIdentifier =
202 (*jniEnv)->GetMethodID(jniEnv, jObjClass, "getID", "()Ljava/lang/String;");
203
204 if (!jTimezoneIdentifier)
205 goto fail;
206
207 tzJId = (*jniEnv)->CallObjectMethod(jniEnv, jObj, jTimezoneIdentifier);
208
209 if (!tzJId)
210 goto fail;
211
212 raw = (*jniEnv)->GetStringUTFChars(jniEnv, tzJId, 0);
213
214 if (raw)
215 tzid = _strdup(raw);
216
217 (*jniEnv)->ReleaseStringUTFChars(jniEnv, tzJId, raw);
218 fail:
219
220 if (attached)
221 (*jniVm)->DetachCurrentThread(jniVm);
222 }
223
224 /* Fall back to property, might not be available. */
225 if (!tzid)
226 {
227 FILE* fp = popen("getprop persist.sys.timezone", "r");
228
229 if (fp)
230 {
231 tzid = winpr_read_unix_timezone_identifier_from_file(fp);
232 pclose(fp);
233 }
234 }
235
236 return tzid;
237}
238#endif
239
240static char* winpr_get_unix_timezone_identifier_from_file(void)
241{
242#if defined(ANDROID)
243 return winpr_get_android_timezone_identifier();
244#else
245 FILE* fp = nullptr;
246 char* tzid = nullptr;
247#if !defined(WINPR_TIMEZONE_FILE)
248#error \
249 "Please define WINPR_TIMEZONE_FILE with the path to your timezone file (e.g. /etc/timezone or similar)"
250#else
251 fp = winpr_fopen(WINPR_TIMEZONE_FILE, "r");
252#endif
253
254 if (nullptr == fp)
255 return nullptr;
256
257 tzid = winpr_read_unix_timezone_identifier_from_file(fp);
258 (void)fclose(fp);
259 if (tzid != nullptr)
260 WLog_DBG(TAG, "tzid: %s", tzid);
261 return tzid;
262#endif
263}
264
265static char* winpr_time_zone_from_env(void)
266{
267 LPCSTR tz = "TZ";
268 char* tzid = nullptr;
269
270 DWORD nSize = GetEnvironmentVariableA(tz, nullptr, 0);
271 if (nSize > 0)
272 {
273 tzid = (char*)calloc(nSize, sizeof(char));
274 if (!tzid)
275 goto fail;
276 if (!GetEnvironmentVariableA(tz, tzid, nSize))
277 goto fail;
278 else if (tzid[0] == ':')
279 {
280 /* Remove leading colon, see tzset(3) */
281 memmove(tzid, tzid + 1, nSize - sizeof(char));
282 }
283 }
284
285 return tzid;
286
287fail:
288 free(tzid);
289 return nullptr;
290}
291
292static char* winpr_translate_time_zone(const char* tzid)
293{
294 const char* zipath = "/usr/share/zoneinfo/";
295 char* buf = nullptr;
296 const char* links[] = { buf };
297
298 if (!tzid)
299 return nullptr;
300
301 if (tzid[0] == '/')
302 {
303 /* Full path given in TZ */
304 links[0] = tzid;
305 }
306 else
307 {
308 size_t bsize = 0;
309 winpr_asprintf(&buf, &bsize, "%s%s", zipath, tzid);
310 links[0] = buf;
311 }
312
313 char* ntzid = winpr_get_timezone_from_link(links, 1);
314 free(buf);
315 return ntzid;
316}
317
318static char* winpr_guess_time_zone(void)
319{
320 char* tzid = winpr_time_zone_from_env();
321 if (tzid)
322 goto end;
323 tzid = winpr_get_unix_timezone_identifier_from_file();
324 if (tzid)
325 goto end;
326 tzid = winpr_get_timezone_from_link(nullptr, 0);
327 if (tzid)
328 goto end;
329
330end:
331{
332 char* ntzid = winpr_translate_time_zone(tzid);
333 if (ntzid)
334 {
335 free(tzid);
336 return ntzid;
337 }
338 return tzid;
339}
340}
341
342static SYSTEMTIME tm2systemtime(const struct tm* t)
343{
344 SYSTEMTIME st = WINPR_C_ARRAY_INIT;
345
346 if (t)
347 {
348 st.wYear = (WORD)(1900 + t->tm_year);
349 st.wMonth = (WORD)t->tm_mon + 1;
350 st.wDay = (WORD)t->tm_mday;
351 st.wDayOfWeek = (WORD)t->tm_wday;
352 st.wHour = (WORD)t->tm_hour;
353 st.wMinute = (WORD)t->tm_min;
354 st.wSecond = (WORD)t->tm_sec;
355 st.wMilliseconds = 0;
356 }
357 return st;
358}
359
360static struct tm systemtime2tm(const SYSTEMTIME* st)
361{
362 struct tm t = WINPR_C_ARRAY_INIT;
363 if (st)
364 {
365 if (st->wYear >= 1900)
366 t.tm_year = st->wYear - 1900;
367 if (st->wMonth > 0)
368 t.tm_mon = st->wMonth - 1;
369 t.tm_mday = st->wDay;
370 t.tm_wday = st->wDayOfWeek;
371 t.tm_hour = st->wHour;
372 t.tm_min = st->wMinute;
373 t.tm_sec = st->wSecond;
374 }
375 return t;
376}
377
378static LONG get_gmtoff_min(const struct tm* t)
379{
380 WINPR_ASSERT(t);
381 return -(LONG)(t->tm_gmtoff / 60l);
382}
383
384static struct tm update_tm(const struct tm* start)
385{
386 WINPR_ASSERT(start);
387 struct tm cur = *start;
388 const time_t t = mktime(&cur);
389 struct tm next = WINPR_C_ARRAY_INIT;
390 (void)localtime_r(&t, &next);
391 return next;
392}
393
394static struct tm next_day(const struct tm* start)
395{
396 struct tm cur = *start;
397 cur.tm_hour = 0;
398 cur.tm_min = 0;
399 cur.tm_sec = 0;
400 cur.tm_isdst = -1;
401 cur.tm_mday++;
402 return update_tm(&cur);
403}
404
405static struct tm adjust_time(const struct tm* start, int hour, int minute)
406{
407 struct tm cur = *start;
408 cur.tm_hour = hour;
409 cur.tm_min = minute;
410 cur.tm_sec = 0;
411 cur.tm_isdst = -1;
412 return update_tm(&cur);
413}
414
415/* [MS-RDPBCGR] 2.2.1.11.1.1.1.1.1 System Time (TS_SYSTEMTIME) */
416static WORD get_transition_weekday_occurrence(const SYSTEMTIME* st)
417{
418 WORD count = 0;
419 struct tm start = systemtime2tm(st);
420
421 WORD last = 0;
422 struct tm next = start;
423 next.tm_mday = 1;
424 next.tm_isdst = -1;
425 do
426 {
427
428 const time_t t = mktime(&next);
429 next.tm_mday++;
430
431 struct tm cur = WINPR_C_ARRAY_INIT;
432 (void)localtime_r(&t, &cur);
433
434 if (cur.tm_mon + 1 != st->wMonth)
435 break;
436
437 if (cur.tm_wday == st->wDayOfWeek)
438 {
439 if (cur.tm_mday <= st->wDay)
440 count++;
441 last++;
442 }
443
444 } while (TRUE);
445
446 if (count == last)
447 count = 5;
448
449 return count;
450}
451
452static SYSTEMTIME tm2transitiontime(const struct tm* cur)
453{
454 SYSTEMTIME t = tm2systemtime(cur);
455 if (cur)
456 {
457 t.wDay = get_transition_weekday_occurrence(&t);
458 t.wYear = 0;
459 }
460
461 return t;
462}
463
464static SYSTEMTIME get_transition_time(const struct tm* start, BOOL toDst)
465{
466 BOOL toggled = FALSE;
467 struct tm first = adjust_time(start, 0, 0);
468 for (int hour = 0; hour < 24; hour++)
469 {
470 struct tm cur = adjust_time(start, hour, 0);
471 if (cur.tm_isdst != first.tm_isdst)
472 toggled = TRUE;
473
474 if (toggled)
475 {
476 if (toDst && (cur.tm_isdst > 0))
477 return tm2transitiontime(&cur);
478 else if (!toDst && (cur.tm_isdst == 0))
479 return tm2transitiontime(&cur);
480 }
481 }
482 return tm2transitiontime(start);
483}
484
485static BOOL get_transition_date(const struct tm* pstart, BOOL toDst, SYSTEMTIME* pdate)
486{
487 WINPR_ASSERT(pstart);
488 WINPR_ASSERT(pdate);
489
490 *pdate = tm2transitiontime(nullptr);
491
492 struct tm start = update_tm(pstart);
493 if (start.tm_isdst < 0)
494 return FALSE;
495
496 BOOL val = start.tm_isdst > 0; // the year starts with DST or not
497 BOOL toggled = FALSE;
498 struct tm cur = start;
499 struct tm last = cur;
500 for (int day = 1; day <= 365; day++)
501 {
502 last = cur;
503 cur = next_day(&cur);
504 const BOOL curDst = (cur.tm_isdst > 0);
505 if ((val != curDst) && !toggled)
506 toggled = TRUE;
507
508 if (toggled)
509 {
510 if (toDst && curDst)
511 {
512 *pdate = get_transition_time(&last, toDst);
513 return TRUE;
514 }
515 if (!toDst && !curDst)
516 {
517 *pdate = get_transition_time(&last, toDst);
518 return TRUE;
519 }
520 }
521 }
522 return FALSE;
523}
524
525static LONG get_bias(const struct tm* start, BOOL dstBias)
526{
527 if ((start->tm_isdst > 0) && dstBias)
528 return get_gmtoff_min(start);
529 if ((start->tm_isdst == 0) && !dstBias)
530 return get_gmtoff_min(start);
531 if (start->tm_isdst < 0)
532 return get_gmtoff_min(start);
533
534 struct tm cur = *start;
535 for (int day = 1; day <= 365; day++)
536 {
537 cur = next_day(&cur);
538 if ((cur.tm_isdst > 0) && dstBias)
539 return get_gmtoff_min(&cur);
540 else if ((cur.tm_isdst == 0) && !dstBias)
541 return get_gmtoff_min(&cur);
542 }
543 return 0;
544}
545
546static BOOL map_iana_id(const char* iana, LPDYNAMIC_TIME_ZONE_INFORMATION tz)
547{
548 const char* winId = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_ID);
549 const char* winStd = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_STANDARD);
550 const char* winDst = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_DAYLIGHT);
551
552 if (winStd)
553 (void)ConvertUtf8ToWChar(winStd, tz->StandardName, ARRAYSIZE(tz->StandardName));
554 if (winDst)
555 (void)ConvertUtf8ToWChar(winDst, tz->DaylightName, ARRAYSIZE(tz->DaylightName));
556 if (winId)
557 (void)ConvertUtf8ToWChar(winId, tz->TimeZoneKeyName, ARRAYSIZE(tz->TimeZoneKeyName));
558
559 return winId != nullptr;
560}
561
562static const char* weekday2str(WORD wDayOfWeek)
563{
564 switch (wDayOfWeek)
565 {
566 case 0:
567 return "SUNDAY";
568 case 1:
569 return "MONDAY";
570 case 2:
571 return "TUESDAY";
572 case 3:
573 return "WEDNESDAY";
574 case 4:
575 return "THURSDAY";
576 case 5:
577 return "FRIDAY";
578 case 6:
579 return "SATURDAY";
580 default:
581 return "DAY-OF-MAGIC";
582 }
583}
584
585static char* systemtime2str(const SYSTEMTIME* t, char* buffer, size_t len)
586{
587 const SYSTEMTIME empty = WINPR_C_ARRAY_INIT;
588
589 if (memcmp(t, &empty, sizeof(SYSTEMTIME)) == 0)
590 (void)_snprintf(buffer, len, "{ not set }");
591 else
592 {
593 (void)_snprintf(buffer, len,
594 "{ %" PRIu16 "-%" PRIu16 "-%" PRIu16 " [%s] %" PRIu16 ":%" PRIu16
595 ":%" PRIu16 ".%" PRIu16 "}",
596 t->wYear, t->wMonth, t->wDay, weekday2str(t->wDayOfWeek), t->wHour,
597 t->wMinute, t->wSecond, t->wMilliseconds);
598 }
599 return buffer;
600}
601
602WINPR_ATTR_FORMAT_ARG(6, 7)
603static void log_print(wLog* log, DWORD level, const char* file, const char* fkt, size_t line,
604 WINPR_FORMAT_ARG const char* fmt, ...)
605{
606 if (!WLog_IsLevelActive(log, level))
607 return;
608
609 va_list ap = WINPR_C_ARRAY_INIT;
610 va_start(ap, fmt);
611 WLog_PrintTextMessageVA(log, level, line, file, fkt, fmt, ap);
612 va_end(ap);
613}
614
615#define log_timezone(tzif, result) log_timezone_((tzif), (result), __FILE__, __func__, __LINE__)
616static void log_timezone_(const DYNAMIC_TIME_ZONE_INFORMATION* tzif, DWORD result, const char* file,
617 const char* fkt, size_t line)
618{
619 WINPR_ASSERT(tzif);
620
621 char buffer[130] = WINPR_C_ARRAY_INIT;
622 DWORD level = WLOG_TRACE;
623 wLog* log = WLog_Get(TAG);
624 log_print(log, level, file, fkt, line, "DYNAMIC_TIME_ZONE_INFORMATION {");
625
626 log_print(log, level, file, fkt, line, " Bias=%" PRId32, tzif->Bias);
627 (void)ConvertWCharNToUtf8(tzif->StandardName, ARRAYSIZE(tzif->StandardName), buffer,
628 ARRAYSIZE(buffer));
629 log_print(log, level, file, fkt, line, " StandardName=%s", buffer);
630 log_print(log, level, file, fkt, line, " StandardDate=%s",
631 systemtime2str(&tzif->StandardDate, buffer, sizeof(buffer)));
632 log_print(log, level, file, fkt, line, " StandardBias=%" PRId32, tzif->StandardBias);
633
634 (void)ConvertWCharNToUtf8(tzif->DaylightName, ARRAYSIZE(tzif->DaylightName), buffer,
635 ARRAYSIZE(buffer));
636 log_print(log, level, file, fkt, line, " DaylightName=%s", buffer);
637 log_print(log, level, file, fkt, line, " DaylightDate=%s",
638 systemtime2str(&tzif->DaylightDate, buffer, sizeof(buffer)));
639 log_print(log, level, file, fkt, line, " DaylightBias=%" PRId32, tzif->DaylightBias);
640 (void)ConvertWCharNToUtf8(tzif->TimeZoneKeyName, ARRAYSIZE(tzif->TimeZoneKeyName), buffer,
641 ARRAYSIZE(buffer));
642 log_print(log, level, file, fkt, line, " TimeZoneKeyName=%s", buffer);
643 log_print(log, level, file, fkt, line, " DynamicDaylightTimeDisabled=DST-%s",
644 tzif->DynamicDaylightTimeDisabled ? "disabled" : "enabled");
645 switch (result)
646 {
647 case TIME_ZONE_ID_DAYLIGHT:
648 log_print(log, level, file, fkt, line, " DaylightDate in use");
649 break;
650 case TIME_ZONE_ID_STANDARD:
651 log_print(log, level, file, fkt, line, " StandardDate in use");
652 break;
653 default:
654 log_print(log, level, file, fkt, line, " UnknownDate in use");
655 break;
656 }
657 log_print(log, level, file, fkt, line, "}");
658}
659
660DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation)
661{
662 DYNAMIC_TIME_ZONE_INFORMATION dyn = WINPR_C_ARRAY_INIT;
663 DWORD rc = GetDynamicTimeZoneInformation(&dyn);
664 lpTimeZoneInformation->Bias = dyn.Bias;
665 lpTimeZoneInformation->DaylightBias = dyn.DaylightBias;
666 lpTimeZoneInformation->DaylightDate = dyn.DaylightDate;
667 lpTimeZoneInformation->StandardBias = dyn.StandardBias;
668 lpTimeZoneInformation->StandardDate = dyn.StandardDate;
669 memcpy(lpTimeZoneInformation->StandardName, dyn.StandardName,
670 sizeof(lpTimeZoneInformation->StandardName));
671 memcpy(lpTimeZoneInformation->DaylightName, dyn.DaylightName,
672 sizeof(lpTimeZoneInformation->DaylightName));
673 return rc;
674}
675
676BOOL SetTimeZoneInformation(const TIME_ZONE_INFORMATION* lpTimeZoneInformation)
677{
678 WINPR_UNUSED(lpTimeZoneInformation);
679 return FALSE;
680}
681
682BOOL SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime, LPFILETIME lpFileTime)
683{
684 WINPR_UNUSED(lpSystemTime);
685 WINPR_UNUSED(lpFileTime);
686 return FALSE;
687}
688
689BOOL FileTimeToSystemTime(const FILETIME* lpFileTime, LPSYSTEMTIME lpSystemTime)
690{
691 WINPR_UNUSED(lpFileTime);
692 WINPR_UNUSED(lpSystemTime);
693 return FALSE;
694}
695
696BOOL SystemTimeToTzSpecificLocalTime(LPTIME_ZONE_INFORMATION lpTimeZone,
697 LPSYSTEMTIME lpUniversalTime, LPSYSTEMTIME lpLocalTime)
698{
699 WINPR_UNUSED(lpTimeZone);
700 WINPR_UNUSED(lpUniversalTime);
701 WINPR_UNUSED(lpLocalTime);
702 return FALSE;
703}
704
705BOOL TzSpecificLocalTimeToSystemTime(LPTIME_ZONE_INFORMATION lpTimeZoneInformation,
706 LPSYSTEMTIME lpLocalTime, LPSYSTEMTIME lpUniversalTime)
707{
708 WINPR_UNUSED(lpTimeZoneInformation);
709 WINPR_UNUSED(lpLocalTime);
710 WINPR_UNUSED(lpUniversalTime);
711 return FALSE;
712}
713
714#endif
715
716/*
717 * GetDynamicTimeZoneInformation is provided by the SDK if _WIN32_WINNT >= 0x0600 in SDKs above 7.1A
718 * and incorrectly if _WIN32_WINNT >= 0x0501 in older SDKs
719 */
720#if !defined(_WIN32) || \
721 (defined(_WIN32) && (defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0600 || \
722 !defined(NTDDI_WIN8) && _WIN32_WINNT < 0x0501)) /* Windows Vista */
723
724typedef enum
725{
726 HAVE_TRANSITION_DATES = 0,
727 HAVE_NO_STANDARD_TRANSITION_DATE = 1,
728 HAVE_NO_DAYLIGHT_TRANSITION_DATE = 2
729} dyn_transition_result;
730
731static int dynamic_time_zone_from_localtime(const struct tm* local_time,
733{
734 WINPR_ASSERT(local_time);
735 WINPR_ASSERT(tz);
736 int rc = HAVE_TRANSITION_DATES;
737
738 tz->Bias = get_bias(local_time, FALSE);
739
740 /* If the current time has (or had) DST */
741 if (local_time->tm_isdst >= 0)
742 {
743 /* DST bias is the difference between standard time and DST in minutes */
744 const LONG d = get_bias(local_time, TRUE);
745 tz->DaylightBias = -1 * (tz->Bias - d);
746 struct tm newyear = *local_time;
747 newyear.tm_mday = 0;
748 newyear.tm_yday = 0;
749 newyear.tm_wday = 0;
750 newyear.tm_min = 0;
751 newyear.tm_sec = 0;
752 newyear.tm_mon = 0;
753
754 /* Searching for transition dates.
755 *
756 * For the current DST setting, search from the local_time, for the other one
757 * search from beginning of the year.
758 * We want the dates in the same year, so searching both from local_time might lead to
759 * issues.
760 */
761 const struct tm* stdtransition = local_time;
762 const struct tm* dsttransition = &newyear;
763 if (local_time->tm_isdst == 0)
764 {
765 stdtransition = &newyear;
766 dsttransition = local_time;
767 }
768 if (!get_transition_date(stdtransition, FALSE, &tz->StandardDate))
769 {
770 rc |= HAVE_NO_STANDARD_TRANSITION_DATE;
771 tz->StandardBias = 0;
772 }
773 if (!get_transition_date(dsttransition, TRUE, &tz->DaylightDate))
774 {
775 rc |= HAVE_NO_DAYLIGHT_TRANSITION_DATE;
776 tz->DaylightBias = 0;
777 }
778 }
779 return rc;
780}
781
782DWORD GetDynamicTimeZoneInformation(PDYNAMIC_TIME_ZONE_INFORMATION pTimeZoneInformation)
783{
784 const char** list = nullptr;
785 char* tzid = nullptr;
786 const char* defaultName = "Client Local Time";
787 DWORD res = TIME_ZONE_ID_UNKNOWN;
788 const DYNAMIC_TIME_ZONE_INFORMATION empty = WINPR_C_ARRAY_INIT;
789
790 WINPR_ASSERT(pTimeZoneInformation);
791
792 *pTimeZoneInformation = empty;
793 (void)ConvertUtf8ToWChar(defaultName, pTimeZoneInformation->StandardName,
794 ARRAYSIZE(pTimeZoneInformation->StandardName));
795
796 const time_t t = time(nullptr);
797 struct tm tres = WINPR_C_ARRAY_INIT;
798 struct tm* local_time = localtime_r(&t, &tres);
799 if (!local_time)
800 goto out_error;
801
802 pTimeZoneInformation->Bias = get_bias(local_time, FALSE);
803 if (local_time->tm_isdst >= 0)
804 dynamic_time_zone_from_localtime(local_time, pTimeZoneInformation);
805
806 tzid = winpr_guess_time_zone();
807 if (!map_iana_id(tzid, pTimeZoneInformation))
808 {
809 const size_t len = TimeZoneIanaAbbrevGet(local_time->tm_zone, nullptr, 0);
810 list = (const char**)calloc(len, sizeof(const char*));
811 if (!list)
812 goto out_error;
813 const size_t size = TimeZoneIanaAbbrevGet(local_time->tm_zone, list, len);
814 for (size_t x = 0; x < size; x++)
815 {
816 const char* id = list[x];
817 if (map_iana_id(id, pTimeZoneInformation))
818 {
819 res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
820 break;
821 }
822 }
823 }
824 else
825 res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
826
827out_error:
828 free(tzid);
829 free((void*)list);
830
831 log_timezone(pTimeZoneInformation, res);
832 return res;
833}
834
835BOOL SetDynamicTimeZoneInformation(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation)
836{
837 WINPR_UNUSED(lpTimeZoneInformation);
838 return FALSE;
839}
840
841BOOL GetTimeZoneInformationForYear(USHORT wYear, PDYNAMIC_TIME_ZONE_INFORMATION pdtzi,
843{
844 WINPR_UNUSED(wYear);
845 WINPR_UNUSED(pdtzi);
846 WINPR_UNUSED(ptzi);
847 return FALSE;
848}
849
850#endif
851
852#if !defined(_WIN32) || (defined(_WIN32) && (_WIN32_WINNT < 0x0601)) /* Windows 7 */
853
854BOOL SystemTimeToTzSpecificLocalTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation,
855 const SYSTEMTIME* lpUniversalTime, LPSYSTEMTIME lpLocalTime)
856{
857 WINPR_UNUSED(lpTimeZoneInformation);
858 WINPR_UNUSED(lpUniversalTime);
859 WINPR_UNUSED(lpLocalTime);
860 return FALSE;
861}
862
863BOOL TzSpecificLocalTimeToSystemTimeEx(const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation,
864 const SYSTEMTIME* lpLocalTime, LPSYSTEMTIME lpUniversalTime)
865{
866 WINPR_UNUSED(lpTimeZoneInformation);
867 WINPR_UNUSED(lpLocalTime);
868 WINPR_UNUSED(lpUniversalTime);
869 return FALSE;
870}
871
872#endif
873
874#if !defined(_WIN32)
875
876DWORD EnumDynamicTimeZoneInformation(DWORD dwIndex,
877 PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation)
878{
879 if (!lpTimeZoneInformation)
880 return ERROR_INVALID_PARAMETER;
881 const DYNAMIC_TIME_ZONE_INFORMATION empty = WINPR_C_ARRAY_INIT;
882 *lpTimeZoneInformation = empty;
883
884 const TimeZoneNameMapEntry* entry = TimeZoneGetAt(dwIndex);
885 if (!entry)
886 return ERROR_NO_MORE_ITEMS;
887
888 if (entry->DaylightName)
889 (void)ConvertUtf8ToWChar(entry->DaylightName, lpTimeZoneInformation->DaylightName,
890 ARRAYSIZE(lpTimeZoneInformation->DaylightName));
891 if (entry->StandardName)
892 (void)ConvertUtf8ToWChar(entry->StandardName, lpTimeZoneInformation->StandardName,
893 ARRAYSIZE(lpTimeZoneInformation->StandardName));
894 if (entry->Id)
895 (void)ConvertUtf8ToWChar(entry->Id, lpTimeZoneInformation->TimeZoneKeyName,
896 ARRAYSIZE(lpTimeZoneInformation->TimeZoneKeyName));
897
898 const time_t t = time(nullptr);
899 struct tm tres = WINPR_C_ARRAY_INIT;
900
901 char* tzcopy = entry->Iana ? setNewAndSaveOldTZ(entry->Iana) : nullptr;
902
903 struct tm* local_time = localtime_r(&t, &tres);
904
905 if (local_time)
906 dynamic_time_zone_from_localtime(local_time, lpTimeZoneInformation);
907
908 if (entry->Iana)
909 restoreSavedTZ(tzcopy);
910
911 return ERROR_SUCCESS;
912}
913
914// NOLINTBEGIN(readability-non-const-parameter)
915DWORD GetDynamicTimeZoneInformationEffectiveYears(
916 const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, LPDWORD FirstYear, LPDWORD LastYear)
917// NOLINTEND(readability-non-const-parameter)
918{
919 WINPR_UNUSED(lpTimeZoneInformation);
920 WINPR_UNUSED(FirstYear);
921 WINPR_UNUSED(LastYear);
922 return ERROR_FILE_NOT_FOUND;
923}
924
925#elif _WIN32_WINNT < 0x0602 /* Windows 8 */
926DWORD EnumDynamicTimeZoneInformation(DWORD dwIndex,
927 PDYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation)
928{
929 WINPR_UNUSED(dwIndex);
930 WINPR_UNUSED(lpTimeZoneInformation);
931 return ERROR_NO_MORE_ITEMS;
932}
933
934DWORD GetDynamicTimeZoneInformationEffectiveYears(
935 const DYNAMIC_TIME_ZONE_INFORMATION* lpTimeZoneInformation, LPDWORD FirstYear, LPDWORD LastYear)
936{
937 WINPR_UNUSED(lpTimeZoneInformation);
938 WINPR_UNUSED(FirstYear);
939 WINPR_UNUSED(LastYear);
940 return ERROR_FILE_NOT_FOUND;
941}
942#endif
943
944#if !defined(_WIN32)
945char* setNewAndSaveOldTZ(const char* val)
946{
947 // NOLINTBEGIN(concurrency-mt-unsafe)
948 const char* otz = getenv("TZ");
949 char* oldtz = nullptr;
950 if (otz)
951 oldtz = _strdup(otz);
952 setenv("TZ", val, 1);
953 tzset();
954 // NOLINTEND(concurrency-mt-unsafe)
955 return oldtz;
956}
957
958void restoreSavedTZ(char* saved)
959{
960 // NOLINTBEGIN(concurrency-mt-unsafe)
961 if (saved)
962 {
963 setenv("TZ", saved, 1);
964 free(saved);
965 }
966 else
967 unsetenv("TZ");
968 tzset();
969 // NOLINTEND(concurrency-mt-unsafe)
970}
971#endif