10#include <private/qnumeric_p.h>
14#include <private/qwinregistry_p.h>
26#define MAX_KEY_LENGTH 255
36static const wchar_t tzRegPath[] = LR
"(SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones)";
37static const wchar_t currTzRegPath[] = LR
"(SYSTEM\CurrentControlSet\Control\TimeZoneInformation)";
100bool equalSystemtime(
const SYSTEMTIME &
t1,
const SYSTEMTIME &
t2)
102 return (
t1.wYear ==
t2.wYear
103 &&
t1.wMonth ==
t2.wMonth
104 &&
t1.wDay ==
t2.wDay
105 &&
t1.wDayOfWeek ==
t2.wDayOfWeek
106 &&
t1.wHour ==
t2.wHour
107 &&
t1.wMinute ==
t2.wMinute
108 &&
t1.wSecond ==
t2.wSecond
109 &&
t1.wMilliseconds ==
t2.wMilliseconds);
112bool equalTzi(
const TIME_ZONE_INFORMATION &tzi1,
const TIME_ZONE_INFORMATION &tzi2)
114 return(tzi1.Bias == tzi2.Bias
115 && tzi1.StandardBias == tzi2.StandardBias
116 && equalSystemtime(tzi1.StandardDate, tzi2.StandardDate)
117 && wcscmp(tzi1.StandardName, tzi2.StandardName) == 0
118 && tzi1.DaylightBias == tzi2.DaylightBias
119 && equalSystemtime(tzi1.DaylightDate, tzi2.DaylightDate)
120 && wcscmp(tzi1.DaylightName, tzi2.DaylightName) == 0);
123QWinTimeZonePrivate::QWinTransitionRule readRegistryRule(
const HKEY &
key,
124 const wchar_t *
value,
bool *
ok)
127 QWinTimeZonePrivate::QWinTransitionRule
rule;
129 DWORD tziSize =
sizeof(tzi);
130 if (RegQueryValueEx(
key,
value,
nullptr,
nullptr,
reinterpret_cast<BYTE *
>(&tzi), &tziSize)
133 rule.standardTimeBias = tzi.Bias + tzi.StandardBias;
134 rule.daylightTimeBias = tzi.Bias + tzi.DaylightBias -
rule.standardTimeBias;
135 rule.standardTimeRule = tzi.StandardDate;
136 rule.daylightTimeRule = tzi.DaylightDate;
142TIME_ZONE_INFORMATION getRegistryTzi(
const QByteArray &windowsId,
bool *
ok)
145 TIME_ZONE_INFORMATION tzi;
147 DWORD regTziSize =
sizeof(regTzi);
153 DWORD
size =
sizeof(tzi.DaylightName);
154 RegQueryValueEx(
key, L
"Dlt",
nullptr,
nullptr,
reinterpret_cast<LPBYTE
>(tzi.DaylightName), &
size);
156 size =
sizeof(tzi.StandardName);
157 RegQueryValueEx(
key, L
"Std",
nullptr,
nullptr,
reinterpret_cast<LPBYTE
>(tzi.StandardName), &
size);
159 if (RegQueryValueEx(
key, L
"TZI",
nullptr,
nullptr,
reinterpret_cast<BYTE *
>(®Tzi), ®TziSize)
161 tzi.Bias = regTzi.Bias;
162 tzi.StandardBias = regTzi.StandardBias;
163 tzi.DaylightBias = regTzi.DaylightBias;
164 tzi.StandardDate = regTzi.StandardDate;
165 tzi.DaylightDate = regTzi.DaylightDate;
173bool isSameRule(
const QWinTimeZonePrivate::QWinTransitionRule &last,
174 const QWinTimeZonePrivate::QWinTransitionRule &
rule)
181 return equalSystemtime(last.standardTimeRule,
rule.standardTimeRule)
182 && equalSystemtime(last.daylightTimeRule,
rule.daylightTimeRule)
183 && last.standardTimeBias ==
rule.standardTimeBias
184 && last.daylightTimeBias ==
rule.daylightTimeBias;
187QList<QByteArray> availableWindowsIds()
189 static const QList<QByteArray>
cache = [] {
190 QList<QByteArray>
list;
194 if (RegQueryInfoKey(
key, 0, 0, 0, &idCount, 0, 0, 0, 0, 0, 0, 0) == ERROR_SUCCESS
196 for (DWORD
i = 0;
i < idCount; ++
i) {
199 if (RegEnumKeyEx(
key,
i,
buffer, &maxLen, 0, 0, 0, 0) == ERROR_SUCCESS)
219 TIME_ZONE_INFORMATION sysTzi;
220 GetTimeZoneInformation(&sysTzi);
222 const auto winIds = availableWindowsIds();
224 if (equalTzi(getRegistryTzi(winId, &
ok), sysTzi))
232QDate calculateTransitionLocalDate(
const SYSTEMTIME &
rule,
int year)
235 if (
rule.wMonth == 0)
246 const int dayOfWeek =
rule.wDayOfWeek == 0 ? 7 :
rule.wDayOfWeek;
255 adjust += (
rule.wDay < 1 ? 1 :
rule.wDay > 4 ? 5 :
rule.wDay) * 7;
274 if (daySinceEpoch < 0 && msInDay > 0) {
280 return qMulOverflow(daySinceEpoch, std::integral_constant<qint64, MSECS_PER_DAY>(), &dayms)
284qint64 calculateTransitionForYear(
const SYSTEMTIME &
rule,
int year,
int bias)
304bool isAtStartOfYear(
const SYSTEMTIME &transition,
int year)
315 return transition.wMonth == 1 && transition.wDay == 1
316 && (
QDate(year, 1, 1).
dayOfWeek() - transition.wDayOfWeek) % 7 == 0
317 && transition.wHour == 0 && transition.wMinute == 0 && transition.wSecond == 0;
320struct TransitionTimePair
326 bool fakesDst =
false;
328 TransitionTimePair(
const QWinTimeZonePrivate::QWinTransitionRule &
rule,
329 int year,
int oldYearOffset)
331 :
std(calculateTransitionForYear(
rule.standardTimeRule, year,
332 rule.standardTimeBias +
rule.daylightTimeBias)),
334 dst(calculateTransitionForYear(
rule.daylightTimeRule, year,
rule.standardTimeBias))
381 if (
rule.standardTimeBias +
rule.daylightTimeBias == oldYearOffset
382 && isAtStartOfYear(
rule.daylightTimeRule, year)) {
386 if (
rule.standardTimeBias == oldYearOffset
387 && isAtStartOfYear(
rule.standardTimeRule, year)) {
389 "Year with (DST bias zero and) both transitions fake !");
395 bool startsInDst()
const
407 bool beforeInitialDst(
int year,
qint64 millis)
const
413 const QWinTimeZonePrivate *tzp,
bool isDst)
const
415 const auto type = isDst ? QTimeZone::DaylightTime : QTimeZone::StandardTime;
425int yearEndOffset(
const QWinTimeZonePrivate::QWinTransitionRule &
rule,
int year)
433 if (pair.dst > pair.std)
440 const GEOID
id = GetUserGeoID(GEOCLASS_NATION);
442 const int size = GetGeoInfo(
id, GEO_ISO2, code, 3, 0);
448int ruleIndexForYear(
const QList<QWinTimeZonePrivate::QWinTransitionRule> &rules,
int year)
450 if (rules.last().startYear <= year)
451 return rules.count() - 1;
453 if (rules.first().startYear > year)
457 int lo = 0, hi = rules.count();
460 while (lo + 1 < hi) {
461 const int mid = (lo + hi) / 2;
464 const int midYear = rules.at(mid).startYear;
467 else if (midYear < year)
478QWinTimeZonePrivate::QWinTimeZonePrivate()
485QWinTimeZonePrivate::QWinTimeZonePrivate(
const QByteArray &ianaId)
491QWinTimeZonePrivate::QWinTimeZonePrivate(
const QWinTimeZonePrivate &
other)
493 m_displayName(
other.m_displayName), m_standardName(
other.m_standardName),
494 m_daylightName(
other.m_daylightName), m_tranRules(
other.m_tranRules)
498QWinTimeZonePrivate::~QWinTimeZonePrivate()
502QWinTimeZonePrivate *QWinTimeZonePrivate::clone()
const
504 return new QWinTimeZonePrivate(*
this);
507void QWinTimeZonePrivate::init(
const QByteArray &ianaId)
510 m_windowsId = windowsSystemZoneId();
511 m_id = systemTimeZoneId();
513 m_windowsId = ianaIdToWindowsId(ianaId);
516 const auto initialYear = [](
const QWinTransitionRule &
rule) {
521 return (
rule.standardTimeRule.wMonth > 0 ||
rule.daylightTimeRule.wMonth > 0
525 bool badMonth =
false;
526 if (!m_windowsId.isEmpty()) {
531 if (baseKey.isValid()) {
533 m_displayName = baseKey.stringValue(L
"Display");
534 m_standardName = baseKey.stringValue(L
"Std");
535 m_daylightName = baseKey.stringValue(L
"Dlt");
537 const QString dynamicKeyPath = baseKeyPath +
"\\Dynamic DST"_L1;
539 if (dynamicKey.isValid()) {
541 const auto startYear = dynamicKey.dwordValue(L
"FirstEntry");
542 const auto endYear = dynamicKey.dwordValue(L
"LastEntry");
543 for (
int year =
int(startYear.first); year <= int(endYear.first); ++year) {
545 QWinTransitionRule
rule =
546 readRegistryRule(dynamicKey,
551 && (m_tranRules.isEmpty()
552 || !isSameRule(m_tranRules.last(),
rule))) {
554 && (
rule.standardTimeRule.wMonth == 0)
555 != (
rule.daylightTimeRule.wMonth == 0)) {
557 qWarning(
"MS registry TZ API violated its wMonth constraint;"
558 "this may cause mistakes for %s from %d",
561 const TransitionTimePair pair(
rule, year,
rule.standardTimeBias);
564 = m_tranRules.size() || pair.fakesDst ? year : initialYear(
rule);
565 m_tranRules.append(
rule);
571 QWinTransitionRule
rule = readRegistryRule(baseKey, L
"TZI", &ruleOk);
574 m_tranRules.append(
rule);
581 if (m_tranRules.size() == 0) {
584 m_displayName.clear();
585 }
else if (m_id.isEmpty()) {
586 m_id = m_standardName.toUtf8();
590QString QWinTimeZonePrivate::comment()
const
592 return m_displayName;
595QString QWinTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
596 QTimeZone::NameType nameType,
602 if (nameType == QTimeZone::OffsetName) {
603 const QWinTransitionRule &
rule =
606 if (timeType == QTimeZone::DaylightTime)
608 return isoOffsetFormat(
offset * -60);
612 case QTimeZone::DaylightTime :
613 return m_daylightName;
614 case QTimeZone::GenericTime :
615 return m_displayName;
616 case QTimeZone::StandardTime :
617 return m_standardName;
619 return m_standardName;
622QString QWinTimeZonePrivate::abbreviation(
qint64 atMSecsSinceEpoch)
const
624 return data(atMSecsSinceEpoch).abbreviation;
627int QWinTimeZonePrivate::offsetFromUtc(
qint64 atMSecsSinceEpoch)
const
629 return data(atMSecsSinceEpoch).offsetFromUtc;
632int QWinTimeZonePrivate::standardTimeOffset(
qint64 atMSecsSinceEpoch)
const
634 return data(atMSecsSinceEpoch).standardTimeOffset;
637int QWinTimeZonePrivate::daylightTimeOffset(
qint64 atMSecsSinceEpoch)
const
639 return data(atMSecsSinceEpoch).daylightTimeOffset;
642bool QWinTimeZonePrivate::hasDaylightTime()
const
644 return hasTransitions();
647bool QWinTimeZonePrivate::isDaylightTime(
qint64 atMSecsSinceEpoch)
const
649 return (
data(atMSecsSinceEpoch).daylightTimeOffset != 0);
655 for (
int ruleIndex = ruleIndexForYear(m_tranRules, year);
656 ruleIndex >= 0; --ruleIndex) {
657 const QWinTransitionRule &
rule = m_tranRules.at(ruleIndex);
659 if (year <
rule.startYear
660 || !(
rule.standardTimeRule.wMonth > 0 ||
rule.daylightTimeRule.wMonth > 0)) {
662 return ruleToData(
rule, forMSecsSinceEpoch, QTimeZone::StandardTime);
665 int prior = year == 1 ? -1 : year - 1;
666 const int endYear =
qMax(
rule.startYear, prior);
667 while (year >= endYear) {
668 const int newYearOffset = (prior <
rule.startYear && ruleIndex > 0)
669 ? yearEndOffset(m_tranRules.at(ruleIndex - 1), prior)
670 : yearEndOffset(
rule, prior);
671 const TransitionTimePair pair(
rule, year, newYearOffset);
673 if (ruleIndex == 0 && pair.beforeInitialDst(year, forMSecsSinceEpoch)) {
677 }
else if (pair.std != invalidMSecs() && pair.std <= forMSecsSinceEpoch) {
678 isDst = pair.std < pair.dst && pair.dst <= forMSecsSinceEpoch;
679 }
else if (pair.dst != invalidMSecs() && pair.dst <= forMSecsSinceEpoch) {
683 prior = year == 1 ? -1 : year - 1;
686 return ruleToData(
rule, forMSecsSinceEpoch,
687 isDst ? QTimeZone::DaylightTime :
QTimeZone::StandardTime,
698bool QWinTimeZonePrivate::hasTransitions()
const
700 for (
const QWinTransitionRule &
rule : m_tranRules) {
701 if (
rule.standardTimeRule.wMonth > 0 &&
rule.daylightTimeRule.wMonth > 0)
709 int year =
msecsToDate(afterMSecsSinceEpoch).year();
710 int newYearOffset = invalidSeconds();
711 for (
int ruleIndex = ruleIndexForYear(m_tranRules, year);
712 ruleIndex < m_tranRules.count(); ++ruleIndex) {
713 const QWinTransitionRule &
rule = m_tranRules.at(ruleIndex);
715 if (
rule.standardTimeRule.wMonth > 0 ||
rule.daylightTimeRule.wMonth > 0) {
716 int prior = year == 1 ? -1 : year - 1;
717 if (newYearOffset == invalidSeconds()) {
720 newYearOffset = (prior <
rule.startYear && ruleIndex > 0)
721 ? yearEndOffset(m_tranRules.at(ruleIndex - 1), prior)
722 : yearEndOffset(
rule, prior);
724 if (year <
rule.startYear) {
728 TransitionTimePair pair(
rule,
rule.startYear, newYearOffset);
734 const int endYear = ruleIndex + 1 < m_tranRules.count()
735 ?
qMin(m_tranRules.at(ruleIndex + 1).startYear, year + 2) : (year + 2);
736 while (year < endYear) {
737 const TransitionTimePair pair(
rule, year, newYearOffset);
739 Q_ASSERT(invalidMSecs() <= afterMSecsSinceEpoch);
740 if (ruleIndex == 0 && pair.beforeInitialDst(year, afterMSecsSinceEpoch)) {
749 }
else if (pair.std > afterMSecsSinceEpoch) {
750 isDst = pair.std > pair.dst && pair.dst > afterMSecsSinceEpoch;
751 }
else if (pair.dst > afterMSecsSinceEpoch) {
754 newYearOffset =
rule.standardTimeBias;
755 if (pair.dst > pair.std)
756 newYearOffset +=
rule.daylightTimeBias;
759 year = year == -1 ? 1 : year + 1;
763 return pair.ruleToData(
rule,
this, isDst);
770 if (newYearOffset == invalidSeconds())
771 newYearOffset =
rule.standardTimeBias;
782 if (beforeMSecsSinceEpoch <= minMSecs())
785 int year =
msecsToDate(beforeMSecsSinceEpoch).year();
786 for (
int ruleIndex = ruleIndexForYear(m_tranRules, year);
787 ruleIndex >= 0; --ruleIndex) {
788 const QWinTransitionRule &
rule = m_tranRules.at(ruleIndex);
791 if (year >=
rule.startYear
792 && (
rule.standardTimeRule.wMonth > 0 ||
rule.daylightTimeRule.wMonth > 0)) {
793 int prior = year == 1 ? -1 : year - 1;
794 const int endYear =
qMax(
rule.startYear, prior);
795 while (year >= endYear) {
796 const int newYearOffset = (prior <
rule.startYear && ruleIndex > 0)
797 ? yearEndOffset(m_tranRules.at(ruleIndex - 1), prior)
798 : yearEndOffset(
rule, prior);
799 const TransitionTimePair pair(
rule, year, newYearOffset);
804 if (ruleIndex == 0 && pair.beforeInitialDst(year, beforeMSecsSinceEpoch - 1))
805 return ruleToData(
rule, minMSecs(), QTimeZone::StandardTime,
false);
808 if (pair.std != invalidMSecs() && pair.std < beforeMSecsSinceEpoch) {
809 isDst = pair.std < pair.dst && pair.dst < beforeMSecsSinceEpoch;
810 }
else if (pair.dst != invalidMSecs() && pair.dst < beforeMSecsSinceEpoch) {
814 prior = year == 1 ? -1 : year - 1;
817 return pair.ruleToData(
rule,
this, isDst);
820 }
else if (ruleIndex == 0) {
824 return ruleToData(
rule, minMSecs(), QTimeZone::StandardTime,
false);
826 if (year >=
rule.startYear) {
827 year =
rule.startYear - 1;
836QByteArray QWinTimeZonePrivate::systemTimeZoneId()
const
839 const QByteArray windowsId = windowsSystemZoneId();
843 ianaId = windowsIdToDefaultIanaId(windowsId, territory);
846 ianaId = windowsIdToDefaultIanaId(windowsId);
850QList<QByteArray> QWinTimeZonePrivate::availableTimeZoneIds()
const
852 static const QList<QByteArray>
cache = [] {
854 const auto winIds = availableWindowsIds();
856 result += windowsIdToIanaIds(winId);
866 QTimeZone::TimeType
type,
871 tran.standardTimeOffset =
rule.standardTimeBias * -60;
873 tran.daylightTimeOffset = 0;
874 tran.abbreviation = m_standardName;
876 if (
type == QTimeZone::DaylightTime)
877 tran.standardTimeOffset +=
rule.daylightTimeBias * -60;
878 }
else if (
type == QTimeZone::DaylightTime) {
879 tran.daylightTimeOffset =
rule.daylightTimeBias * -60;
880 tran.abbreviation = m_daylightName;
882 tran.daylightTimeOffset = 0;
883 tran.abbreviation = m_standardName;
885 tran.offsetFromUtc = tran.standardTimeOffset + tran.daylightTimeOffset;
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
\inmodule QtCore\reentrant
\inmodule QtCore \reentrant
constexpr bool isValid() const
Returns true if this date is valid; otherwise returns false.
constexpr qint64 toJulianDay() const
Converts the date to a Julian day.
int month() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
static constexpr QDate fromJulianDay(qint64 jd_)
Converts the Julian day jd to a QDate.
QDate addDays(qint64 days) const
Returns a QDate object containing a date ndays later than the date of this object (or earlier if nday...
static QDate currentDate()
Returns the system clock's current date.
int dayOfWeek() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void append(parameter_type t)
static QLocale::Territory codeToTerritory(QStringView code) noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QByteArray toUtf8() const &
static QByteArray utcQByteArray()
static constexpr qint64 invalidMSecs()
static constexpr qint64 minMSecs()
\inmodule QtCore \reentrant
bool isValid() const
Returns true if the time is valid; otherwise returns false.
constexpr int msecsSinceStartOfDay() const
Returns the number of msecs since the start of the day, i.e.
QString stringValue(QStringView subKey) const
QCache< int, Employee > cache
[0]
Combined button and popup list for selecting options.
int toUtf8(char16_t u, OutputPtr &dst, InputPtr &src, InputPtr end)
static qint64 timeToMSecs(QDate date, QTime time)
static QDate msecsToDate(qint64 msecs)
DBusConnection const char * rule
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr const T & qMin(const T &a, const T &b)
constexpr const T & qMax(const T &a, const T &b)
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
std::enable_if_t< std::is_unsigned_v< T >||std::is_signed_v< T >, bool > qMulOverflow(T v1, T v2, T *r)
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat t1
[4]
GLenum GLuint GLintptr offset
#define Q_ASSERT_X(cond, x, msg)
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
constexpr qint64 JULIAN_DAY_FOR_EPOCH
constexpr int FIRST_DST_YEAR
struct _REG_TZI_FORMAT REG_TZI_FORMAT
static const wchar_t tzRegPath[]
constexpr qint64 MSECS_PER_DAY
static const wchar_t currTzRegPath[]