KDELibs4Support

ktimezone_win.cpp
1 /*
2  This file is part of the KDE libraries
3  Copyright (c) 2008 Marc Mutz <[email protected]>, Till Adam <[email protected]>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include "ktimezone_win.h"
22 
23 #include <QStringList>
24 #include <QLibrary>
25 
26 #include "kdebug.h"
27 
28 #include <windows.h>
29 
30 #include <memory>
31 #include <string>
32 #include <cassert>
33 
34 struct ZoneKey {
35  QString zoneOlson;
36  QString zoneWin;
37 };
38 
39 static const ZoneKey ZoneTbl[] = {
40  {QLatin1String("Australia/Darwin"), QLatin1String("AUS Central Standard Time")},
41  {QLatin1String("Australia/Sydney"), QLatin1String("AUS Eastern Standard Time")},
42  {QLatin1String("Asia/Kabul"), QLatin1String("Afghanistan Standard Time")},
43  {QLatin1String("America/Anchorage"), QLatin1String("Alaskan Standard Time")},
44  {QLatin1String("Asia/Riyadh"), QLatin1String("Arab Standard Time")},
45  {QLatin1String("Asia/Dubai"), QLatin1String("Arabian Standard Time")},
46  {QLatin1String("Asia/Baghdad"), QLatin1String("Arabic Standard Time")},
47  {QLatin1String("America/Buenos_Aires"), QLatin1String("Argentina Standard Time")},
48  {QLatin1String("Asia/Yerevan"), QLatin1String("Armenian Standard Time")},
49  {QLatin1String("America/Halifax"), QLatin1String("Atlantic Standard Time")},
50  {QLatin1String("Asia/Baku"), QLatin1String("Azerbaijan Standard Time")},
51  {QLatin1String("Atlantic/Azores"), QLatin1String("Azores Standard Time")},
52  {QLatin1String("America/Regina"), QLatin1String("Canada Central Standard Time")},
53  {QLatin1String("Atlantic/Cape_Verde"), QLatin1String("Cape Verde Standard Time")},
54  {QLatin1String("Asia/Yerevan"), QLatin1String("Caucasus Standard Time")},
55  {QLatin1String("Australia/Adelaide"), QLatin1String("Cen. Australia Standard Time")},
56  {QLatin1String("America/Guatemala"), QLatin1String("Central America Standard Time")},
57  {QLatin1String("Asia/Dhaka"), QLatin1String("Central Asia Standard Time")},
58  {QLatin1String("America/Manaus"), QLatin1String("Central Brazilian Standard Time")},
59  {QLatin1String("Europe/Budapest"), QLatin1String("Central Europe Standard Time")},
60  {QLatin1String("Europe/Warsaw"), QLatin1String("Central European Standard Time")},
61  {QLatin1String("Pacific/Guadalcanal"), QLatin1String("Central Pacific Standard Time")},
62  {QLatin1String("America/Chicago"), QLatin1String("Central Standard Time")},
63  {QLatin1String("America/Mexico_City"), QLatin1String("Central Standard Time (Mexico)")},
64  {QLatin1String("Asia/Shanghai"), QLatin1String("China Standard Time")},
65  {QLatin1String("Etc/GMT+12"), QLatin1String("Dateline Standard Time")},
66  {QLatin1String("Africa/Nairobi"), QLatin1String("E. Africa Standard Time")},
67  {QLatin1String("Australia/Brisbane"), QLatin1String("E. Australia Standard Time")},
68  {QLatin1String("Europe/Minsk"), QLatin1String("E. Europe Standard Time")},
69  {QLatin1String("America/Sao_Paulo"), QLatin1String("E. South America Standard Time")},
70  {QLatin1String("America/New_York"), QLatin1String("Eastern Standard Time")},
71  {QLatin1String("Africa/Cairo"), QLatin1String("Egypt Standard Time")},
72  {QLatin1String("Asia/Yekaterinburg"), QLatin1String("Ekaterinburg Standard Time")},
73  {QLatin1String("Europe/Kiev"), QLatin1String("FLE Standard Time")},
74  {QLatin1String("Pacific/Fiji"), QLatin1String("Fiji Standard Time")},
75  {QLatin1String("Europe/London"), QLatin1String("GMT Standard Time")},
76  {QLatin1String("Europe/Istanbul"), QLatin1String("GTB Standard Time")},
77  {QLatin1String("Etc/GMT-3"), QLatin1String("Georgian Standard Time")},
78  {QLatin1String("America/Godthab"), QLatin1String("Greenland Standard Time")},
79  {QLatin1String("Atlantic/Reykjavik"), QLatin1String("Greenwich Standard Time")},
80  {QLatin1String("Pacific/Honolulu"), QLatin1String("Hawaiian Standard Time")},
81  {QLatin1String("Asia/Calcutta"), QLatin1String("India Standard Time")},
82  {QLatin1String("Asia/Tehran"), QLatin1String("Iran Standard Time")},
83  {QLatin1String("Asia/Jerusalem"), QLatin1String("Israel Standard Time")},
84  {QLatin1String("Asia/Amman"), QLatin1String("Jordan Standard Time")},
85  {QLatin1String("Asia/Seoul"), QLatin1String("Korea Standard Time")},
86  {QLatin1String("Indian/Mauritius"), QLatin1String("Mauritius Standard Time")},
87  {QLatin1String("America/Mexico_City"), QLatin1String("Mexico Standard Time")},
88  {QLatin1String("America/Chihuahua"), QLatin1String("Mexico Standard Time 2")},
89  {QLatin1String("Atlantic/South_Georgia"), QLatin1String("Mid-Atlantic Standard Time")},
90  {QLatin1String("Asia/Beirut"), QLatin1String("Middle East Standard Time")},
91  {QLatin1String("America/Montevideo"), QLatin1String("Montevideo Standard Time")},
92  {QLatin1String("Africa/Casablanca"), QLatin1String("Morocco Standard Time")},
93  {QLatin1String("America/Denver"), QLatin1String("Mountain Standard Time")},
94  {QLatin1String("America/Chihuahua"), QLatin1String("Mountain Standard Time (Mexico)")},
95  {QLatin1String("Asia/Rangoon"), QLatin1String("Myanmar Standard Time")},
96  {QLatin1String("Asia/Novosibirsk"), QLatin1String("N. Central Asia Standard Time")},
97  {QLatin1String("Africa/Windhoek"), QLatin1String("Namibia Standard Time")},
98  {QLatin1String("Asia/Katmandu"), QLatin1String("Nepal Standard Time")},
99  {QLatin1String("Pacific/Auckland"), QLatin1String("New Zealand Standard Time")},
100  {QLatin1String("America/St_Johns"), QLatin1String("Newfoundland Standard Time")},
101  {QLatin1String("Asia/Irkutsk"), QLatin1String("North Asia East Standard Time")},
102  {QLatin1String("Asia/Krasnoyarsk"), QLatin1String("North Asia Standard Time")},
103  {QLatin1String("America/Santiago"), QLatin1String("Pacific SA Standard Time")},
104  {QLatin1String("America/Los_Angeles"), QLatin1String("Pacific Standard Time")},
105  {QLatin1String("America/Tijuana"), QLatin1String("Pacific Standard Time (Mexico)")},
106  {QLatin1String("Asia/Karachi"), QLatin1String("Pakistan Standard Time")},
107  {QLatin1String("Europe/Paris"), QLatin1String("Romance Standard Time")},
108  {QLatin1String("Europe/Moscow"), QLatin1String("Russian Standard Time")},
109  {QLatin1String("Etc/GMT+3"), QLatin1String("SA Eastern Standard Time")},
110  {QLatin1String("America/Bogota"), QLatin1String("SA Pacific Standard Time")},
111  {QLatin1String("America/La_Paz"), QLatin1String("SA Western Standard Time")},
112  {QLatin1String("Asia/Bangkok"), QLatin1String("SE Asia Standard Time")},
113  {QLatin1String("Pacific/Apia"), QLatin1String("Samoa Standard Time")},
114  {QLatin1String("Asia/Singapore"), QLatin1String("Singapore Standard Time")},
115  {QLatin1String("Africa/Johannesburg"), QLatin1String("South Africa Standard Time")},
116  {QLatin1String("Asia/Colombo"), QLatin1String("Sri Lanka Standard Time")},
117  {QLatin1String("Asia/Taipei"), QLatin1String("Taipei Standard Time")},
118  {QLatin1String("Australia/Hobart"), QLatin1String("Tasmania Standard Time")},
119  {QLatin1String("Asia/Tokyo"), QLatin1String("Tokyo Standard Time")},
120  {QLatin1String("Pacific/Tongatapu"), QLatin1String("Tonga Standard Time")},
121  {QLatin1String("Etc/GMT+5"), QLatin1String("US Eastern Standard Time")},
122  {QLatin1String("America/Phoenix"), QLatin1String("US Mountain Standard Time")},
123  {QLatin1String("America/Caracas"), QLatin1String("Venezuela Standard Time")},
124  {QLatin1String("Asia/Vladivostok"), QLatin1String("Vladivostok Standard Time")},
125  {QLatin1String("Australia/Perth"), QLatin1String("W. Australia Standard Time")},
126  {QLatin1String("Africa/Lagos"), QLatin1String("W. Central Africa Standard Time")},
127  {QLatin1String("Europe/Berlin"), QLatin1String("W. Europe Standard Time")},
128  {QLatin1String("Asia/Tashkent"), QLatin1String("West Asia Standard Time")},
129  {QLatin1String("Pacific/Port_Moresby"), QLatin1String("West Pacific Standard Time")},
130  {QLatin1String("Asia/Yakutsk"), QLatin1String("Yakutsk Standard Time")}
131 };
132 
133 static QString getWinZoneName(const QString &name)
134 {
135  for (int i = 0; i < sizeof(ZoneTbl) / sizeof(ZoneTbl[0]); ++i) {
136  if (ZoneTbl[i].zoneOlson == name) {
137  return ZoneTbl[i].zoneWin;
138  }
139  }
140 
141  return name;
142 }
143 
144 typedef BOOL (WINAPI *PtrTzSpecificLocalTimeToSystemTime)(LPTIME_ZONE_INFORMATION lpTimeZoneInformation,
145  LPSYSTEMTIME lpLocalTime,
146  LPSYSTEMTIME lpUniversalTime
147  );
148 static PtrTzSpecificLocalTimeToSystemTime pTzSpecificLocalTimeToSystemTime = 0;
149 
150 namespace
151 {
152 class HKeyCloser
153 {
154  const HKEY hkey;
155  Q_DISABLE_COPY(HKeyCloser)
156 public:
157  KDELIBS4SUPPORT_DEPRECATED explicit HKeyCloser(HKEY hk) : hkey(hk) {}
158  ~HKeyCloser()
159  {
160  RegCloseKey(hkey);
161  }
162 };
163 
164 struct TZI {
165  LONG Bias;
166  LONG StandardBias;
167  LONG DaylightBias;
168  SYSTEMTIME StandardDate;
169  SYSTEMTIME DaylightDate;
170 };
171 }
172 
173 // TCHAR can be either uchar, or wchar_t:
174 #ifdef UNICODE
175 
176 static inline QString tchar_to_qstring(const TCHAR *str)
177 {
178  return QString::fromUtf16(reinterpret_cast<const ushort *>(str));
179 }
180 
181 static inline const TCHAR *qstring_to_tchar(const QString &str)
182 {
183  return reinterpret_cast<const TCHAR *>(str.utf16());
184 }
185 
186 static inline std::basic_string<TCHAR> qstring_to_tcharstring(const QString &str)
187 {
188  return std::basic_string<TCHAR>(qstring_to_tchar(str));
189 }
190 
191 #else
192 
193 static inline QString tchar_to_qstring(const TCHAR *str)
194 {
195  return QString::fromLocal8Bit(str);
196 }
197 
198 static inline const TCHAR *qstring_to_tchar(const QString &str)
199 {
200  return str.toLocal8Bit().constData();
201 }
202 
203 static inline std::basic_string<TCHAR> qstring_to_tcharstring(const QString &str)
204 {
205  return std::basic_string<TCHAR>(qstring_to_tchar(str));
206 }
207 
208 #endif
209 
210 static const TCHAR timeZonesKey[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
211 static inline QDateTime systemtime_to_qdatetime(const SYSTEMTIME &st)
212 {
213  return QDateTime(QDate(st.wYear, st.wMonth, st.wDay),
214  QTime(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds));
215 }
216 
217 static SYSTEMTIME qdatetime_to_systemtime(const QDateTime &dt)
218 {
219  const QDate d = dt.date();
220  const QTime t = dt.time();
221  const SYSTEMTIME st = {
222  d.year(),
223  d.month(),
224  d.dayOfWeek() % 7, // 1..7 (Mon..Sun)->0..6(Sun..Sat)
225  d.day(),
226  t.hour(),
227  t.minute(),
228  t.second(),
229  t.msec(),
230  };
231  return st;
232 }
233 
234 static bool TzSpecificLocalTimeToSystemTime_Portable(TIME_ZONE_INFORMATION *tz,
235  SYSTEMTIME *i_stLocal,
236  SYSTEMTIME *o_stUniversal)
237 {
238 
239  // the method below was introduced in XP. If it's there, use it, otherwise
240  // fall back to doing things manually
241  if (!pTzSpecificLocalTimeToSystemTime) {
242  QLibrary kernelLib(QLatin1String("kernel32"));
243  pTzSpecificLocalTimeToSystemTime = (PtrTzSpecificLocalTimeToSystemTime)kernelLib.resolve("TzSpecificLocalTimeToSystemTime");
244  }
245 
246  if (pTzSpecificLocalTimeToSystemTime) {
247  return pTzSpecificLocalTimeToSystemTime(tz, i_stLocal, o_stUniversal) != 0;
248  }
249 
250  // the algorithm is:
251  // - switch to the desired timezone temporarily
252  // - convert system time to (local) file time in that timezone
253  // - convert local file time to utc file time
254  // - convert utc file time to system time
255  // - reset timezone
256  FILETIME ft, ft_utc;
257  int result = 1;
258  TIME_ZONE_INFORMATION currentTimeZone;
259  result = GetTimeZoneInformation(&currentTimeZone);
260  if (result == TIME_ZONE_ID_INVALID) {
261  kWarning(161) << "Getting time zone information failed";
262  return false;
263  }
264  result = SetTimeZoneInformation(tz);
265  if (result == 0) {
266  kWarning(161) << "Setting temporary time zone failed";
267  return false;
268  }
269  result = SystemTimeToFileTime(i_stLocal, &ft);
270  if (result == 0) {
271  kWarning(161) << "SysteTimeToFileTime failed";
272  return false;
273  }
274  result = LocalFileTimeToFileTime(&ft, &ft_utc);
275  if (result == 0) {
276  kWarning(161) << "LocalFileTimeToFileTime failed";
277  return false;
278  }
279  result = FileTimeToSystemTime(&ft_utc, o_stUniversal);
280  if (result == 0) {
281  kWarning(161) << "FileTimeToSystemTime failed";
282  return false;
283  }
284  result = SetTimeZoneInformation(&currentTimeZone);
285  if (result == 0) {
286  kWarning(161) << "Re-setting time zone information failed";
287  return false;
288  }
289  return true;
290 }
291 
292 static bool get_binary_value(HKEY key, const TCHAR *value, void *data, DWORD numData, DWORD *outNumData = 0)
293 {
294  DWORD size = numData;
295  DWORD type = REG_BINARY;
296  if (RegQueryValueEx(key, value, 0, &type, (LPBYTE)data, &size) != ERROR_SUCCESS) {
297  return false;
298  }
299  assert(type == REG_BINARY);
300  if (type != REG_BINARY) {
301  return false;
302  }
303  if (outNumData) {
304  *outNumData = size;
305  }
306  return true;
307 }
308 
309 static bool get_string_value(HKEY key, const TCHAR *value, TCHAR *dest, DWORD destSizeInBytes)
310 {
311  DWORD size = destSizeInBytes;
312  DWORD type = REG_SZ;
313  dest[0] = '\0';
314  if (RegQueryValueEx(key, value, 0, &type, (LPBYTE)dest, &size) != ERROR_SUCCESS) {
315  return false;
316  }
317  //dest[ qMin( size, destSizeInBytes - sizeof( WCHAR ) ) / sizeof( WCHAR ) ] = 0;
318  assert(type == REG_SZ);
319  if (type != REG_SZ) {
320  return false;
321  }
322  return true;
323 }
324 
325 //
326 //
327 // Backend interface impl:
328 //
329 //
330 
331 static bool check_prereq(const KTimeZone *caller, const QDateTime &dt, Qt::TimeSpec spec)
332 {
333  return caller && caller->isValid() && dt.isValid() && dt.timeSpec() == spec;
334 }
335 
336 static inline bool check_local(const KTimeZone *caller, const QDateTime &dt)
337 {
338  return check_prereq(caller, dt, Qt::LocalTime);
339 }
340 
341 static inline bool check_utc(const KTimeZone *caller, const QDateTime &dt)
342 {
343  return check_prereq(caller, dt, Qt::UTC);
344 }
345 
346 static bool has_transition(const TIME_ZONE_INFORMATION &tz)
347 {
348  return tz.StandardDate.wMonth != 0 && tz.DaylightDate.wMonth != 0;
349 }
350 
351 static int win_dayofweek_to_qt_dayofweek(int wdow)
352 {
353  // Sun(0)..Sat(6) -> Mon(1)...Sun(7)
354  return wdow ? wdow : 7;
355 }
356 
357 static int qt_dayofweek_to_win_dayofweek(int qdow)
358 {
359  // Mon(1)...Sun(7) -> Sub(0)...Sat(6)
360  return qdow % 7;
361 }
362 
363 static QDate find_nth_weekday_in_month_of_year(int nth, int dayOfWeek, int month, int year)
364 {
365  assert(nth >= 1);
366  assert(nth <= 5);
367 
368  const QDate first(year, month, 1);
369  const int actualDayOfWeek = first.dayOfWeek();
370  QDate candidate = first.addDays((nth - 1) * 7 + dayOfWeek - actualDayOfWeek);
371  assert(candidate.dayOfWeek() == dayOfWeek);
372  if (nth == 5)
373  if (candidate.month() != month) {
374  candidate = candidate.addDays(-7);
375  }
376  assert(candidate.month() == month);
377  return candidate;
378 }
379 
380 static QDateTime transition(const SYSTEMTIME &st, int year)
381 {
382  assert(st.wYear == 0);
383  assert(st.wMonth != 0);
384  return QDateTime(find_nth_weekday_in_month_of_year(st.wDay, win_dayofweek_to_qt_dayofweek(st.wDayOfWeek), st.wMonth, year),
385  QTime(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds));
386 }
387 
388 struct Transitions {
389  QDateTime stdStart, dstStart;
390 };
391 
392 Transitions transitions(const TIME_ZONE_INFORMATION &tz, int year)
393 {
394  const Transitions t = {
395  transition(tz.StandardDate, year), transition(tz.DaylightDate, year)
396  };
397  return t;
398 }
399 
400 static const int MAX_KEY_LENGTH = 255;
401 
402 static QStringList list_key(HKEY key)
403 {
404 
405  DWORD numSubKeys = 0;
406  QStringList result;
407 
408  if (RegQueryInfoKey(key, 0, 0, 0, &numSubKeys, 0, 0, 0, 0, 0, 0, 0) == ERROR_SUCCESS)
409  for (DWORD i = 0; i < numSubKeys; ++i) {
410  TCHAR name[MAX_KEY_LENGTH + 1];
411  DWORD nameLen = MAX_KEY_LENGTH;
412  if (RegEnumKeyEx(key, i, name, &nameLen, 0, 0, 0, 0) == ERROR_SUCCESS) {
413  result.push_back(tchar_to_qstring(name));
414  }
415  }
416 
417  return result;
418 }
419 
420 static QStringList list_standard_names()
421 {
422  QStringList standardNames;
423 
424  HKEY timeZones;
425  QStringList keys;
426  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, timeZonesKey, 0, KEY_READ, &timeZones) == ERROR_SUCCESS) {
427  keys = list_key(timeZones);
428  }
429 
430  std::basic_string<TCHAR> path(timeZonesKey);
431  path += TEXT("\\");
432 
433  const HKeyCloser closer(timeZones);
434  Q_FOREACH (const QString &keyname, keys) {
435 
436  std::basic_string<TCHAR> keypath(path);
437  keypath += qstring_to_tcharstring(keyname);
438  HKEY key;
439  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keypath.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS) {
440  return standardNames; // FIXME what's the right error handling here?
441  }
442 
443  const HKeyCloser closer(key);
444 
445  TIME_ZONE_INFORMATION tz;
446  get_string_value(key, L"Std", tz.StandardName, sizeof(tz.StandardName));
447 
448  standardNames << tchar_to_qstring(tz.StandardName);
449  }
450 
451  for (int i = 0; i < sizeof(ZoneTbl) / sizeof(ZoneTbl[0]); ++i) {
452  standardNames << ZoneTbl[i].zoneOlson;
453  }
454 
455  return standardNames;
456 }
457 
458 static std::basic_string<TCHAR> pathFromZoneName(const KTimeZone &zone)
459 {
460  std::basic_string<TCHAR> path(timeZonesKey);
461  path += TEXT("\\");
462 
463  QString name = zone.name();
464 
465  name = getWinZoneName(name);
466 
467  HKEY timeZones;
468  QStringList keys;
469  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, timeZonesKey, 0, KEY_READ, &timeZones) == ERROR_SUCCESS) {
470  keys = list_key(timeZones);
471  }
472 
473  const HKeyCloser closer(timeZones);
474  Q_FOREACH (const QString &keyname, keys) {
475 
476  std::basic_string<TCHAR> keypath(path);
477  keypath += qstring_to_tcharstring(keyname);
478  HKEY key;
479  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keypath.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS) {
480  return 0; // FIXME what's the right error handling here?
481  }
482 
483  const HKeyCloser closer(key);
484 
485  TIME_ZONE_INFORMATION tz;
486  get_string_value(key, L"Std", tz.StandardName, sizeof(tz.StandardName));
487 
488  if (tchar_to_qstring(tz.StandardName) == name) {
489  return keypath;
490  }
491  }
492  Q_ASSERT(false);
493 
494  return path;
495 }
496 
497 /******************************************************************************/
498 
499 class KSystemTimeZoneSourceWindowsPrivate
500 {
501 public:
502  KSystemTimeZoneSourceWindowsPrivate() {}
503  ~KSystemTimeZoneSourceWindowsPrivate() {}
504 };
505 
506 class KSystemTimeZoneBackendWindows : public KTimeZoneBackend
507 {
508 public:
509  KSystemTimeZoneBackendWindows(KTimeZoneSource *source, const QString &name)
510  : KTimeZoneBackend(source, name) {}
511 
512  ~KSystemTimeZoneBackendWindows() {}
513 
514  KSystemTimeZoneBackendWindows *clone() const;
515 
516  QByteArray type() const;
517 
518  int offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const;
519  int offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const;
520  int offset(const KTimeZone *caller, time_t t) const;
521  bool isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const;
522  bool isDst(const KTimeZone *caller, time_t t) const;
523 };
524 
525 class KSystemTimeZoneDataWindows : public KTimeZoneData
526 {
527 public:
528  KSystemTimeZoneDataWindows()
529  : KTimeZoneData()
530  {
531 
532  }
533  TIME_ZONE_INFORMATION _tzi;
534  QString displayName;
535 
536  const TIME_ZONE_INFORMATION &tzi(int year = 0) const
537  {
538  Q_UNUSED(year);
539  return _tzi;
540  }
541 };
542 
544  : d(new KSystemTimeZoneSourceWindowsPrivate)
545 {
546 }
547 
549 {
550  KSystemTimeZoneDataWindows *data = new KSystemTimeZoneDataWindows();
551 
552  std::basic_string<TCHAR> path = pathFromZoneName(zone);
553 
554  HKEY key;
555  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path.c_str(), 0, KEY_READ, &key) != ERROR_SUCCESS) {
556  delete data;
557  return 0; // FIXME what's the right error handling here?
558  }
559 
560  const HKeyCloser closer(key);
561 
562  TZI tzi = { 0 };
563 
564  if (!get_binary_value(key, TEXT("TZI"), &tzi, sizeof(TZI))) {
565  delete data;
566  return 0; // ?
567  }
568 
569  get_string_value(key, L"Std", data->_tzi.StandardName, sizeof(data->_tzi.StandardName));
570  get_string_value(key, L"Dlt", data->_tzi.DaylightName, sizeof(data->_tzi.DaylightName));
571 
572  TCHAR display[512];
573  get_string_value(key, L"Display", display, sizeof(display));
574  data->displayName = tchar_to_qstring(display);
575 
576 #define COPY( name ) data->_tzi.name = tzi.name
577  COPY(Bias);
578  COPY(StandardBias);
579  COPY(StandardDate);
580  COPY(DaylightBias);
581  COPY(DaylightDate);
582 #undef COPY
583 
584  return data;
585 }
586 
587 Transitions transitions(const KTimeZone *caller, int year)
588 {
589  return transitions(static_cast<const KSystemTimeZoneDataWindows *>(caller->data(true))->tzi(year), year);
590 }
591 
592 static bool is_dst(const TIME_ZONE_INFORMATION &tzi, const QDateTime &utc, int year)
593 {
594  if (!has_transition(tzi)) {
595  return false;
596  }
597  const Transitions trans = transitions(tzi, year);
598  if (trans.stdStart < trans.dstStart) {
599  return trans.dstStart <= utc || utc < trans.stdStart;
600  } else {
601  return trans.dstStart <= utc && utc < trans.stdStart;
602  }
603 }
604 
605 static bool is_dst(const KTimeZone *caller, const QDateTime &utc)
606 {
607  assert(caller);
608  assert(caller->isValid());
609  const int year = utc.date().year();
610  const TIME_ZONE_INFORMATION &tzi = static_cast<const KSystemTimeZoneDataWindows *>(caller->data(true))->tzi(year);
611  return is_dst(tzi, utc, year);
612 }
613 
614 static int effective_offset(const TIME_ZONE_INFORMATION &tz, bool isDst)
615 {
616  int bias = tz.Bias;
617  if (has_transition(tz))
618  if (isDst) {
619  bias += tz.DaylightBias;
620  } else {
621  bias += tz.StandardBias;
622  }
623  return bias * -60; // min -> secs
624 }
625 
626 static int offset_at_utc(const KTimeZone *caller, const QDateTime &utc)
627 {
628  assert(caller);
629  assert(caller->isValid());
630  const int year = utc.date().year();
631  const TIME_ZONE_INFORMATION &tz = static_cast<const KSystemTimeZoneDataWindows *>(caller->data(true))->tzi(year);
632  return effective_offset(tz, is_dst(tz, utc, year));
633 }
634 
635 static const int OneHour = 3600; //sec
636 
637 static int difference(const SYSTEMTIME &st1, const SYSTEMTIME &st2)
638 {
639  return systemtime_to_qdatetime(st1).secsTo(systemtime_to_qdatetime(st2));
640 }
641 
642 static int offset_at_zone_time(const KTimeZone *caller, const SYSTEMTIME &zone, int *secondOffset)
643 {
644  assert(caller);
645  assert(caller->isValid());
646  assert(caller->data(true));
647  const KSystemTimeZoneDataWindows *const data = static_cast<const KSystemTimeZoneDataWindows *>(caller->data(true));
648  const TIME_ZONE_INFORMATION &tz = data->tzi(zone.wYear);
649  SYSTEMTIME utc;
650  if (!TzSpecificLocalTimeToSystemTime_Portable(const_cast<LPTIME_ZONE_INFORMATION>(&tz), const_cast<LPSYSTEMTIME>(&zone), &utc)) {
651  return 0;
652  }
653  const bool isDst = is_dst(tz, systemtime_to_qdatetime(utc), utc.wYear);
654  int result = effective_offset(tz, isDst);
655 //FIXME: SystemTimeToTzSpecificLocalTime does not exsit on wince
656 #ifndef _WIN32_WCE
657  if (secondOffset) {
658  const SYSTEMTIME utcplus1 = qdatetime_to_systemtime(systemtime_to_qdatetime(utc).addSecs(OneHour));
659  const SYSTEMTIME utcminus1 = qdatetime_to_systemtime(systemtime_to_qdatetime(utc).addSecs(-OneHour));
660  SYSTEMTIME zoneplus1, zoneminus1;
661  if (!SystemTimeToTzSpecificLocalTime(const_cast<LPTIME_ZONE_INFORMATION>(&tz), const_cast<LPSYSTEMTIME>(&utcplus1), &zoneplus1) ||
662  !SystemTimeToTzSpecificLocalTime(const_cast<LPTIME_ZONE_INFORMATION>(&tz), const_cast<LPSYSTEMTIME>(&utcminus1), &zoneminus1)) {
663  return result;
664  }
665  if (difference(zoneminus1, zone) != OneHour ||
666  difference(zone, zoneplus1) != OneHour) {
667  *secondOffset = effective_offset(tz, !isDst);
668  if (result < *secondOffset) {
669  qSwap(result, *secondOffset);
670  }
671  }
672  }
673 #endif
674  return result;
675 }
676 
677 KSystemTimeZoneBackendWindows *KSystemTimeZoneBackendWindows::clone() const
678 {
679  return new KSystemTimeZoneBackendWindows(*this);
680 }
681 
682 QByteArray KSystemTimeZoneBackendWindows::type() const
683 {
684  return "KSystemTimeZoneWindows";
685 }
686 
687 int KSystemTimeZoneBackendWindows::offsetAtZoneTime(const KTimeZone *caller, const QDateTime &zoneDateTime, int *secondOffset) const
688 {
689  if (!caller->isValid() || !zoneDateTime.isValid() || zoneDateTime.timeSpec() != Qt::LocalTime) {
690  return 0;
691  }
692  if (!check_local(caller, zoneDateTime)) {
693  return 0;
694  }
695 
696  return offset_at_zone_time(caller, qdatetime_to_systemtime(zoneDateTime), secondOffset);
697 }
698 
699 int KSystemTimeZoneBackendWindows::offsetAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
700 {
701  if (!caller->isValid() || !utcDateTime.isValid()) {
702  return 0;
703  }
704  if (!check_utc(caller, utcDateTime)) {
705  return 0;
706  }
707  return offset_at_utc(caller, utcDateTime);
708 }
709 
710 int KSystemTimeZoneBackendWindows::offset(const KTimeZone *caller, time_t t) const
711 {
712  if (!caller->isValid() || t == KTimeZone::InvalidTime_t) {
713  return 0;
714  }
715  return offsetAtUtc(caller, KTimeZone::fromTime_t(t));
716 }
717 
718 bool KSystemTimeZoneBackendWindows::isDstAtUtc(const KTimeZone *caller, const QDateTime &utcDateTime) const
719 {
720  return check_utc(caller, utcDateTime) && is_dst(caller, utcDateTime);
721 }
722 
723 bool KSystemTimeZoneBackendWindows::isDst(const KTimeZone *caller, time_t t) const
724 {
725  return isDstAtUtc(caller, KTimeZone::fromTime_t(t));
726 }
727 
729  : KTimeZone(new KSystemTimeZoneBackendWindows(source, name))
730 {}
731 
733 {
734  return list_standard_names();
735 }
736 
int minute() const const
KSystemTimeZoneWindows(KTimeZoneSource *source, const QString &name)
Creates a time zone.
static QDateTime fromTime_t(time_t t)
Converts a UTC time, measured in seconds since 00:00:00 UTC 1st January 1970 (as returned by time(2))...
Definition: ktimezone.cpp:922
void push_back(const T &value)
Base class for the parsed data returned by a KTimeZoneSource class.
Definition: ktimezone.h:1302
bool isValid() const
Checks whether the instance is valid.
Definition: ktimezone.cpp:638
static QStringList listTimeZones()
Static helper method that lists all availalbe timezones on the system as per the information in the W...
QTime time() const const
int day() const const
int second() const const
TimeSpec
int dayOfWeek() const const
QString fromLocal8Bit(const char *str, int size)
const KTimeZoneData * data(bool create=false) const
Returns the detailed parsed data for the time zone.
Definition: ktimezone.cpp:757
QString fromUtf16(const ushort *unicode, int size)
Type type(const QSqlDatabase &db)
const char * constData() const const
static const time_t InvalidTime_t
Indicates an invalid time_t value.
Definition: ktimezone.h:1079
int hour() const const
QByteArray toLocal8Bit() const const
int msec() const const
Qt::TimeSpec timeSpec() const const
Base class representing a time zone.
Definition: ktimezone.h:415
KSystemTimeZoneSourceWindows()
Constructs a time zone source.
bool isValid() const const
const ushort * utf16() const const
QDate date() const const
qint64 secsTo(const QDateTime &other) const const
KTimeZoneData * parse(const KTimeZone &zone) const
Retrieves and parses the Windows registry information to extract detailed information for one time zo...
QString name() const
Returns the name of the time zone.
Definition: ktimezone.cpp:663
QDate addDays(qint64 ndays) const const
Base class representing a source of time zone information.
Definition: ktimezone.h:1231
int year() const const
int month() const const
Base backend class for KTimeZone classes.
Definition: ktimezone.h:1121
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Aug 12 2020 22:58:29 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.